summaryrefslogtreecommitdiffstats
path: root/third_party/sqlite
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-06 22:39:41 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-06 22:39:41 +0000
commit586381f8db3497c24c11f96234f1879b34e74bc7 (patch)
tree99f7d18350289b135ef6dd5c161baba8bce668a3 /third_party/sqlite
parent6e3b12ff2cbbe8c481f986c8f0dd230bb50add2a (diff)
downloadchromium_src-586381f8db3497c24c11f96234f1879b34e74bc7.zip
chromium_src-586381f8db3497c24c11f96234f1879b34e74bc7.tar.gz
chromium_src-586381f8db3497c24c11f96234f1879b34e74bc7.tar.bz2
Upgrade our sqlite to 3.6.1, with the local changes made by Gears. I'm
checking in the full sqlite tree to make upstream merges easier. This means we'll have generated sources split out from the originals. One important change this makes is that "BEGIN" now defaults to "BEGIN IMMEDIATE" rather than "BEGIN DEFERRED". This doesn't affect us because we don't use unqualified BEGIN statements. The full CL is too big for Rietveld. I'm splitting it into 2. This one is reviewable. The other CL is just a fresh drop of: //depot/googleclient/gears/opensource/third_party/sqlite_google Review URL: http://codereview.chromium.org/15067 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7623 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/sqlite')
-rwxr-xr-xthird_party/sqlite/Makefile.arm-wince-mingw32ce-gcc138
-rwxr-xr-xthird_party/sqlite/Makefile.in766
-rwxr-xr-xthird_party/sqlite/Makefile.linux-gcc147
-rwxr-xr-xthird_party/sqlite/README34
-rw-r--r--third_party/sqlite/README.google164
-rw-r--r--third_party/sqlite/SConscript133
-rwxr-xr-xthird_party/sqlite/VERSION1
-rw-r--r--third_party/sqlite/VERSION_DATE1
-rwxr-xr-xthird_party/sqlite/aclocal.m47924
-rwxr-xr-xthird_party/sqlite/addopcodes.awk32
-rwxr-xr-xthird_party/sqlite/art/2005osaward.gifbin0 -> 3750 bytes
-rwxr-xr-xthird_party/sqlite/art/SQLite.epsbin0 -> 24155 bytes
-rwxr-xr-xthird_party/sqlite/art/SQLite.gifbin0 -> 3062 bytes
-rwxr-xr-xthird_party/sqlite/art/SQLiteLogo3.tiffbin0 -> 85156 bytes
-rwxr-xr-xthird_party/sqlite/art/SQLite_big.gifbin0 -> 7428 bytes
-rwxr-xr-xthird_party/sqlite/art/nocopy.gifbin0 -> 3449 bytes
-rwxr-xr-xthird_party/sqlite/art/powered_by_sqlite.gifbin0 -> 3391 bytes
-rwxr-xr-xthird_party/sqlite/config.guess1535
-rwxr-xr-xthird_party/sqlite/config.h.in95
-rwxr-xr-xthird_party/sqlite/config.sub1644
-rwxr-xr-xthird_party/sqlite/configure14347
-rwxr-xr-xthird_party/sqlite/configure.ac688
-rwxr-xr-xthird_party/sqlite/contrib/sqlitecon.tcl679
-rwxr-xr-xthird_party/sqlite/doc/lemon.html892
-rwxr-xr-xthird_party/sqlite/doc/report1.txt121
-rwxr-xr-xthird_party/sqlite/ext/README.txt2
-rwxr-xr-xthird_party/sqlite/ext/fts1/README.txt2
-rwxr-xr-xthird_party/sqlite/ext/fts1/ft_hash.c404
-rwxr-xr-xthird_party/sqlite/ext/fts1/ft_hash.h111
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1.c3341
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1.h11
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1_hash.c369
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1_hash.h112
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1_porter.c643
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1_tokenizer.h90
-rwxr-xr-xthird_party/sqlite/ext/fts1/fts1_tokenizer1.c221
-rwxr-xr-xthird_party/sqlite/ext/fts1/fulltext.c1496
-rwxr-xr-xthird_party/sqlite/ext/fts1/fulltext.h11
-rwxr-xr-xthird_party/sqlite/ext/fts1/simple_tokenizer.c174
-rwxr-xr-xthird_party/sqlite/ext/fts1/tokenizer.h89
-rwxr-xr-xthird_party/sqlite/ext/fts2/README.tokenizers133
-rwxr-xr-xthird_party/sqlite/ext/fts2/README.txt4
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2.c (renamed from third_party/sqlite/fts2.c)390
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2.h (renamed from third_party/sqlite/fts2.h)0
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_hash.c (renamed from third_party/sqlite/fts2_hash.c)35
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_hash.h (renamed from third_party/sqlite/fts2_hash.h)2
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_icu.c (renamed from third_party/sqlite/fts2_icu.c)7
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_porter.c (renamed from third_party/sqlite/fts2_porter.c)0
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_tokenizer.c (renamed from third_party/sqlite/fts2_tokenizer.c)4
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_tokenizer.h (renamed from third_party/sqlite/fts2_tokenizer.h)0
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/fts2/fts2_tokenizer1.c (renamed from third_party/sqlite/fts2_tokenizer1.c)0
-rwxr-xr-xthird_party/sqlite/ext/fts2/mkfts2amal.tcl116
-rwxr-xr-xthird_party/sqlite/ext/fts3/README.tokenizers133
-rwxr-xr-xthird_party/sqlite/ext/fts3/README.txt4
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3.c7216
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3.h26
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_hash.c374
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_hash.h110
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_icu.c258
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_porter.c642
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_tokenizer.c371
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_tokenizer.h145
-rwxr-xr-xthird_party/sqlite/ext/fts3/fts3_tokenizer1.c230
-rwxr-xr-xthird_party/sqlite/ext/fts3/mkfts3amal.tcl115
-rwxr-xr-xthird_party/sqlite/ext/icu/README.txt170
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/ext/icu/icu.c (renamed from third_party/sqlite/icu.c)6
-rwxr-xr-xthird_party/sqlite/ext/rtree/README120
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree.c2826
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree.h26
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree1.test364
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree2.test152
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree3.test74
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree4.test236
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree5.test80
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree_perf.tcl76
-rwxr-xr-xthird_party/sqlite/ext/rtree/rtree_util.tcl195
-rwxr-xr-xthird_party/sqlite/ext/rtree/viewrtree.tcl189
-rwxr-xr-xthird_party/sqlite/google_generate_preprocessed.sh26
-rwxr-xr-xthird_party/sqlite/google_update_sqlite.sh35
-rwxr-xr-xthird_party/sqlite/install-sh251
-rwxr-xr-xthird_party/sqlite/ltmain.sh7929
-rwxr-xr-xthird_party/sqlite/main.mk547
-rw-r--r--third_party/sqlite/malloc.c835
-rwxr-xr-xthird_party/sqlite/mkdll.sh48
-rwxr-xr-xthird_party/sqlite/mkextu.sh13
-rwxr-xr-xthird_party/sqlite/mkextw.sh22
-rwxr-xr-xthird_party/sqlite/mkopcodec.awk31
-rwxr-xr-xthird_party/sqlite/mkopcodeh.awk157
-rwxr-xr-xthird_party/sqlite/mkso.sh32
-rw-r--r--third_party/sqlite/opcodes.c148
-rw-r--r--third_party/sqlite/opcodes.h160
-rw-r--r--third_party/sqlite/os.c96
-rw-r--r--third_party/sqlite/os.h548
-rw-r--r--third_party/sqlite/os_common.h198
-rwxr-xr-xthird_party/sqlite/preprocessed/README10
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/preprocessed/keywordhash.h (renamed from third_party/sqlite/keywordhash.h)0
-rwxr-xr-xthird_party/sqlite/preprocessed/opcodes.c151
-rwxr-xr-xthird_party/sqlite/preprocessed/opcodes.h177
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/preprocessed/parse.c (renamed from third_party/sqlite/parse.c)2799
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/preprocessed/parse.h (renamed from third_party/sqlite/parse.h)0
-rwxr-xr-xthird_party/sqlite/preprocessed/sqlite3.h6294
-rwxr-xr-xthird_party/sqlite/publish.sh136
-rwxr-xr-xthird_party/sqlite/publish_osx.sh35
-rwxr-xr-xthird_party/sqlite/spec.template67
-rwxr-xr-xthird_party/sqlite/sqlite.pc.in13
-rw-r--r--third_party/sqlite/sqlite.vcproj201
-rwxr-xr-xthird_party/sqlite/sqlite3.1229
-rwxr-xr-xthird_party/sqlite/sqlite3.def150
-rw-r--r--third_party/sqlite/sqlite3.h2722
-rwxr-xr-xthird_party/sqlite/sqlite3.pc.in13
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/alter.c (renamed from third_party/sqlite/alter.c)136
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/analyze.c (renamed from third_party/sqlite/analyze.c)147
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/attach.c (renamed from third_party/sqlite/attach.c)90
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/auth.c (renamed from third_party/sqlite/auth.c)11
-rwxr-xr-xthird_party/sqlite/src/bitvec.c325
-rwxr-xr-xthird_party/sqlite/src/btmutex.c317
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/btree.c (renamed from third_party/sqlite/btree.c)2494
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/btree.h (renamed from third_party/sqlite/btree.h)86
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/btreeInt.h (renamed from third_party/sqlite/btreeInt.h)147
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/build.c (renamed from third_party/sqlite/build.c)692
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/callback.c (renamed from third_party/sqlite/callback.c)42
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/complete.c (renamed from third_party/sqlite/complete.c)24
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/date.c (renamed from third_party/sqlite/date.c)279
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/delete.c (renamed from third_party/sqlite/delete.c)293
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/expr.c (renamed from third_party/sqlite/expr.c)2035
-rwxr-xr-xthird_party/sqlite/src/fault.c71
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/func.c (renamed from third_party/sqlite/func.c)408
-rwxr-xr-xthird_party/sqlite/src/global.c77
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/hash.c (renamed from third_party/sqlite/hash.c)96
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/hash.h (renamed from third_party/sqlite/hash.h)7
-rwxr-xr-xthird_party/sqlite/src/hwtime.h87
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/insert.c (renamed from third_party/sqlite/insert.c)934
-rwxr-xr-xthird_party/sqlite/src/journal.c239
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/legacy.c (renamed from third_party/sqlite/legacy.c)38
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/loadext.c (renamed from third_party/sqlite/loadext.c)241
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/main.c (renamed from third_party/sqlite/main.c)1148
-rwxr-xr-xthird_party/sqlite/src/malloc.c742
-rwxr-xr-xthird_party/sqlite/src/md5.c389
-rwxr-xr-xthird_party/sqlite/src/mem1.c147
-rwxr-xr-xthird_party/sqlite/src/mem2.c443
-rwxr-xr-xthird_party/sqlite/src/mem3.c682
-rwxr-xr-xthird_party/sqlite/src/mem4.c393
-rwxr-xr-xthird_party/sqlite/src/mem5.c515
-rwxr-xr-xthird_party/sqlite/src/mem6.c496
-rwxr-xr-xthird_party/sqlite/src/mutex.c273
-rwxr-xr-xthird_party/sqlite/src/mutex.h85
-rwxr-xr-xthird_party/sqlite/src/mutex_os2.c273
-rwxr-xr-xthird_party/sqlite/src/mutex_unix.c325
-rwxr-xr-xthird_party/sqlite/src/mutex_w32.c244
-rwxr-xr-xthird_party/sqlite/src/os.c277
-rwxr-xr-xthird_party/sqlite/src/os.h279
-rwxr-xr-xthird_party/sqlite/src/os_common.h137
-rwxr-xr-xthird_party/sqlite/src/os_os2.c1119
-rwxr-xr-xthird_party/sqlite/src/os_symbian.cc579
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/os_unix.c (renamed from third_party/sqlite/os_unix.c)2303
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/os_win.c (renamed from third_party/sqlite/os_win.c)1516
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/pager.c (renamed from third_party/sqlite/pager.c)2621
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/pager.h (renamed from third_party/sqlite/pager.h)45
-rwxr-xr-xthird_party/sqlite/src/parse.y1122
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/pragma.c (renamed from third_party/sqlite/pragma.c)515
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/prepare.c (renamed from third_party/sqlite/prepare.c)316
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/printf.c (renamed from third_party/sqlite/printf.c)347
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/random.c (renamed from third_party/sqlite/random.c)82
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/select.c (renamed from third_party/sqlite/select.c)2943
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/shell.c (renamed from third_party/sqlite/shell.c)181
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/shell_icu.c (renamed from third_party/sqlite/shell_icu.c)11
-rwxr-xr-xthird_party/sqlite/src/sqlite.h.in6294
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/sqlite3ext.h (renamed from third_party/sqlite/sqlite3ext.h)92
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/sqliteInt.h (renamed from third_party/sqlite/sqliteInt.h)901
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/sqliteLimit.h (renamed from third_party/sqlite/sqliteLimit.h)55
-rwxr-xr-xthird_party/sqlite/src/status.c111
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/table.c (renamed from third_party/sqlite/table.c)48
-rwxr-xr-xthird_party/sqlite/src/tclsqlite.c2599
-rwxr-xr-xthird_party/sqlite/src/test1.c4784
-rwxr-xr-xthird_party/sqlite/src/test2.c659
-rwxr-xr-xthird_party/sqlite/src/test3.c1605
-rwxr-xr-xthird_party/sqlite/src/test4.c716
-rwxr-xr-xthird_party/sqlite/src/test5.c217
-rwxr-xr-xthird_party/sqlite/src/test6.c881
-rwxr-xr-xthird_party/sqlite/src/test7.c723
-rwxr-xr-xthird_party/sqlite/src/test8.c1344
-rwxr-xr-xthird_party/sqlite/src/test9.c193
-rwxr-xr-xthird_party/sqlite/src/test_async.c1717
-rwxr-xr-xthird_party/sqlite/src/test_autoext.c169
-rwxr-xr-xthird_party/sqlite/src/test_btree.c140
-rwxr-xr-xthird_party/sqlite/src/test_config.c488
-rwxr-xr-xthird_party/sqlite/src/test_devsym.c353
-rwxr-xr-xthird_party/sqlite/src/test_func.c431
-rwxr-xr-xthird_party/sqlite/src/test_hexio.c342
-rwxr-xr-xthird_party/sqlite/src/test_loadext.c124
-rwxr-xr-xthird_party/sqlite/src/test_malloc.c1339
-rwxr-xr-xthird_party/sqlite/src/test_md5.c392
-rwxr-xr-xthird_party/sqlite/src/test_mutex.c387
-rwxr-xr-xthird_party/sqlite/src/test_onefile.c822
-rwxr-xr-xthird_party/sqlite/src/test_osinst.c1069
-rwxr-xr-xthird_party/sqlite/src/test_schema.c361
-rwxr-xr-xthird_party/sqlite/src/test_server.c493
-rwxr-xr-xthird_party/sqlite/src/test_tclvar.c325
-rwxr-xr-xthird_party/sqlite/src/test_thread.c332
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/tokenize.c (renamed from third_party/sqlite/tokenize.c)84
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/trigger.c (renamed from third_party/sqlite/trigger.c)273
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/update.c (renamed from third_party/sqlite/update.c)451
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/utf.c (renamed from third_party/sqlite/utf.c)159
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/util.c (renamed from third_party/sqlite/util.c)534
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vacuum.c (renamed from third_party/sqlite/vacuum.c)61
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbe.c (renamed from third_party/sqlite/vdbe.c)4345
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbe.h (renamed from third_party/sqlite/vdbe.h)108
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbeInt.h (renamed from third_party/sqlite/vdbeInt.h)86
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbeapi.c (renamed from third_party/sqlite/vdbeapi.c)599
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbeaux.c (renamed from third_party/sqlite/vdbeaux.c)1286
-rwxr-xr-xthird_party/sqlite/src/vdbeblob.c348
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbefifo.c (renamed from third_party/sqlite/vdbefifo.c)36
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vdbemem.c (renamed from third_party/sqlite/vdbemem.c)593
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/vtab.c (renamed from third_party/sqlite/vtab.c)213
-rwxr-xr-x[-rw-r--r--]third_party/sqlite/src/where.c (renamed from third_party/sqlite/where.c)874
-rwxr-xr-xthird_party/sqlite/tclinstaller.tcl32
-rwxr-xr-xthird_party/sqlite/test/aggerror.test78
-rwxr-xr-xthird_party/sqlite/test/all.test149
-rwxr-xr-xthird_party/sqlite/test/alter.test814
-rwxr-xr-xthird_party/sqlite/test/alter2.test450
-rwxr-xr-xthird_party/sqlite/test/alter3.test395
-rwxr-xr-xthird_party/sqlite/test/altermalloc.test70
-rwxr-xr-xthird_party/sqlite/test/analyze.test296
-rwxr-xr-xthird_party/sqlite/test/async.test81
-rwxr-xr-xthird_party/sqlite/test/async2.test129
-rwxr-xr-xthird_party/sqlite/test/async3.test74
-rwxr-xr-xthird_party/sqlite/test/attach.test792
-rwxr-xr-xthird_party/sqlite/test/attach2.test394
-rwxr-xr-xthird_party/sqlite/test/attach3.test348
-rwxr-xr-xthird_party/sqlite/test/attachmalloc.test49
-rwxr-xr-xthird_party/sqlite/test/auth.test2324
-rwxr-xr-xthird_party/sqlite/test/auth2.test168
-rwxr-xr-xthird_party/sqlite/test/autoinc.test555
-rwxr-xr-xthird_party/sqlite/test/autovacuum.test665
-rwxr-xr-xthird_party/sqlite/test/autovacuum_ioerr2.test132
-rwxr-xr-xthird_party/sqlite/test/avtrans.test921
-rwxr-xr-xthird_party/sqlite/test/badutf.test143
-rwxr-xr-xthird_party/sqlite/test/between.test113
-rwxr-xr-xthird_party/sqlite/test/bigfile.test193
-rwxr-xr-xthird_party/sqlite/test/bigrow.test223
-rwxr-xr-xthird_party/sqlite/test/bind.test660
-rwxr-xr-xthird_party/sqlite/test/bindxfer.test84
-rwxr-xr-xthird_party/sqlite/test/bitvec.test191
-rwxr-xr-xthird_party/sqlite/test/blob.test147
-rwxr-xr-xthird_party/sqlite/test/busy.test61
-rwxr-xr-xthird_party/sqlite/test/cache.test63
-rwxr-xr-xthird_party/sqlite/test/capi2.test791
-rwxr-xr-xthird_party/sqlite/test/capi3.test1198
-rwxr-xr-xthird_party/sqlite/test/capi3b.test145
-rwxr-xr-xthird_party/sqlite/test/capi3c.test1301
-rwxr-xr-xthird_party/sqlite/test/capi3d.test93
-rwxr-xr-xthird_party/sqlite/test/cast.test322
-rwxr-xr-xthird_party/sqlite/test/check.test372
-rwxr-xr-xthird_party/sqlite/test/collate1.test307
-rwxr-xr-xthird_party/sqlite/test/collate2.test664
-rwxr-xr-xthird_party/sqlite/test/collate3.test428
-rwxr-xr-xthird_party/sqlite/test/collate4.test700
-rwxr-xr-xthird_party/sqlite/test/collate5.test270
-rwxr-xr-xthird_party/sqlite/test/collate6.test153
-rwxr-xr-xthird_party/sqlite/test/collate7.test72
-rwxr-xr-xthird_party/sqlite/test/collate8.test52
-rwxr-xr-xthird_party/sqlite/test/collate9.test178
-rwxr-xr-xthird_party/sqlite/test/collateA.test217
-rwxr-xr-xthird_party/sqlite/test/colmeta.test94
-rwxr-xr-xthird_party/sqlite/test/colname.test258
-rwxr-xr-xthird_party/sqlite/test/conflict.test792
-rwxr-xr-xthird_party/sqlite/test/corrupt.test178
-rwxr-xr-xthird_party/sqlite/test/corrupt2.test442
-rwxr-xr-xthird_party/sqlite/test/corrupt3.test109
-rwxr-xr-xthird_party/sqlite/test/corrupt4.test74
-rwxr-xr-xthird_party/sqlite/test/corrupt5.test44
-rwxr-xr-xthird_party/sqlite/test/corrupt6.test251
-rwxr-xr-xthird_party/sqlite/test/corrupt7.test102
-rwxr-xr-xthird_party/sqlite/test/corrupt8.test99
-rwxr-xr-xthird_party/sqlite/test/corrupt9.test131
-rwxr-xr-xthird_party/sqlite/test/corruptA.test72
-rwxr-xr-xthird_party/sqlite/test/crash.test411
-rwxr-xr-xthird_party/sqlite/test/crash2.test132
-rwxr-xr-xthird_party/sqlite/test/crash3.test190
-rwxr-xr-xthird_party/sqlite/test/crash4.test102
-rwxr-xr-xthird_party/sqlite/test/crash5.test110
-rwxr-xr-xthird_party/sqlite/test/crash6.test118
-rwxr-xr-xthird_party/sqlite/test/crash7.test82
-rwxr-xr-xthird_party/sqlite/test/crashtest1.c96
-rwxr-xr-xthird_party/sqlite/test/createtab.test146
-rwxr-xr-xthird_party/sqlite/test/cse.test160
-rwxr-xr-xthird_party/sqlite/test/date.test489
-rwxr-xr-xthird_party/sqlite/test/default.test52
-rwxr-xr-xthird_party/sqlite/test/delete.test320
-rwxr-xr-xthird_party/sqlite/test/delete2.test119
-rwxr-xr-xthird_party/sqlite/test/delete3.test57
-rwxr-xr-xthird_party/sqlite/test/descidx1.test359
-rwxr-xr-xthird_party/sqlite/test/descidx2.test179
-rwxr-xr-xthird_party/sqlite/test/descidx3.test149
-rwxr-xr-xthird_party/sqlite/test/diskfull.test115
-rwxr-xr-xthird_party/sqlite/test/distinctagg.test57
-rwxr-xr-xthird_party/sqlite/test/enc.test172
-rwxr-xr-xthird_party/sqlite/test/enc2.test555
-rwxr-xr-xthird_party/sqlite/test/enc3.test107
-rwxr-xr-xthird_party/sqlite/test/eval.test71
-rwxr-xr-xthird_party/sqlite/test/exclusive.test453
-rwxr-xr-xthird_party/sqlite/test/exclusive2.test297
-rwxr-xr-xthird_party/sqlite/test/exec.test37
-rwxr-xr-xthird_party/sqlite/test/expr.test747
-rwxr-xr-xthird_party/sqlite/test/filectrl.test31
-rwxr-xr-xthird_party/sqlite/test/filefmt.test115
-rwxr-xr-xthird_party/sqlite/test/fkey1.test77
-rwxr-xr-xthird_party/sqlite/test/format4.test65
-rwxr-xr-xthird_party/sqlite/test/fts.test61
-rwxr-xr-xthird_party/sqlite/test/fts1.test61
-rwxr-xr-xthird_party/sqlite/test/fts1a.test186
-rwxr-xr-xthird_party/sqlite/test/fts1b.test147
-rwxr-xr-xthird_party/sqlite/test/fts1c.test1213
-rwxr-xr-xthird_party/sqlite/test/fts1d.test65
-rwxr-xr-xthird_party/sqlite/test/fts1e.test85
-rwxr-xr-xthird_party/sqlite/test/fts1f.test90
-rwxr-xr-xthird_party/sqlite/test/fts1i.test88
-rwxr-xr-xthird_party/sqlite/test/fts1j.test89
-rwxr-xr-xthird_party/sqlite/test/fts1k.test69
-rwxr-xr-xthird_party/sqlite/test/fts1l.test65
-rwxr-xr-xthird_party/sqlite/test/fts1m.test50
-rwxr-xr-xthird_party/sqlite/test/fts1n.test45
-rwxr-xr-xthird_party/sqlite/test/fts1o.test138
-rwxr-xr-xthird_party/sqlite/test/fts1porter.test23590
-rwxr-xr-xthird_party/sqlite/test/fts2.test68
-rwxr-xr-xthird_party/sqlite/test/fts2a.test202
-rwxr-xr-xthird_party/sqlite/test/fts2b.test147
-rwxr-xr-xthird_party/sqlite/test/fts2c.test1213
-rwxr-xr-xthird_party/sqlite/test/fts2d.test65
-rwxr-xr-xthird_party/sqlite/test/fts2e.test85
-rwxr-xr-xthird_party/sqlite/test/fts2f.test90
-rwxr-xr-xthird_party/sqlite/test/fts2g.test93
-rwxr-xr-xthird_party/sqlite/test/fts2h.test76
-rwxr-xr-xthird_party/sqlite/test/fts2i.test87
-rwxr-xr-xthird_party/sqlite/test/fts2j.test89
-rwxr-xr-xthird_party/sqlite/test/fts2k.test105
-rwxr-xr-xthird_party/sqlite/test/fts2l.test69
-rwxr-xr-xthird_party/sqlite/test/fts2m.test65
-rwxr-xr-xthird_party/sqlite/test/fts2n.test196
-rwxr-xr-xthird_party/sqlite/test/fts2o.test169
-rwxr-xr-xthird_party/sqlite/test/fts2p.test357
-rwxr-xr-xthird_party/sqlite/test/fts2q.test346
-rwxr-xr-xthird_party/sqlite/test/fts2r.test121
-rwxr-xr-xthird_party/sqlite/test/fts2token.test174
-rwxr-xr-xthird_party/sqlite/test/fts3.test68
-rwxr-xr-xthird_party/sqlite/test/fts3aa.test202
-rwxr-xr-xthird_party/sqlite/test/fts3ab.test147
-rwxr-xr-xthird_party/sqlite/test/fts3ac.test1213
-rwxr-xr-xthird_party/sqlite/test/fts3ad.test65
-rwxr-xr-xthird_party/sqlite/test/fts3ae.test85
-rwxr-xr-xthird_party/sqlite/test/fts3af.test90
-rwxr-xr-xthird_party/sqlite/test/fts3ag.test93
-rwxr-xr-xthird_party/sqlite/test/fts3ah.test76
-rwxr-xr-xthird_party/sqlite/test/fts3ai.test87
-rwxr-xr-xthird_party/sqlite/test/fts3aj.test89
-rwxr-xr-xthird_party/sqlite/test/fts3ak.test105
-rwxr-xr-xthird_party/sqlite/test/fts3al.test69
-rwxr-xr-xthird_party/sqlite/test/fts3am.test65
-rwxr-xr-xthird_party/sqlite/test/fts3an.test196
-rwxr-xr-xthird_party/sqlite/test/fts3ao.test169
-rwxr-xr-xthird_party/sqlite/test/fts3atoken.test174
-rwxr-xr-xthird_party/sqlite/test/fts3b.test218
-rwxr-xr-xthird_party/sqlite/test/fts3c.test357
-rwxr-xr-xthird_party/sqlite/test/fts3d.test346
-rwxr-xr-xthird_party/sqlite/test/fts3e.test125
-rwxr-xr-xthird_party/sqlite/test/fts3near.test168
-rwxr-xr-xthird_party/sqlite/test/func.test1032
-rwxr-xr-xthird_party/sqlite/test/fuzz.test251
-rwxr-xr-xthird_party/sqlite/test/fuzz2.test102
-rwxr-xr-xthird_party/sqlite/test/fuzz_common.tcl392
-rwxr-xr-xthird_party/sqlite/test/fuzz_malloc.test93
-rwxr-xr-xthird_party/sqlite/test/hook.test317
-rwxr-xr-xthird_party/sqlite/test/icu.test117
-rwxr-xr-xthird_party/sqlite/test/in.test580
-rwxr-xr-xthird_party/sqlite/test/in2.test67
-rwxr-xr-xthird_party/sqlite/test/in3.test289
-rwxr-xr-xthird_party/sqlite/test/incrblob.test619
-rwxr-xr-xthird_party/sqlite/test/incrblob2.test409
-rwxr-xr-xthird_party/sqlite/test/incrblob_err.test137
-rwxr-xr-xthird_party/sqlite/test/incrvacuum.test737
-rwxr-xr-xthird_party/sqlite/test/incrvacuum2.test146
-rwxr-xr-xthird_party/sqlite/test/incrvacuum_ioerr.test181
-rwxr-xr-xthird_party/sqlite/test/index.test711
-rwxr-xr-xthird_party/sqlite/test/index2.test74
-rwxr-xr-xthird_party/sqlite/test/index3.test58
-rwxr-xr-xthird_party/sqlite/test/insert.test391
-rwxr-xr-xthird_party/sqlite/test/insert2.test278
-rwxr-xr-xthird_party/sqlite/test/insert3.test171
-rwxr-xr-xthird_party/sqlite/test/insert4.test306
-rwxr-xr-xthird_party/sqlite/test/insert5.test117
-rwxr-xr-xthird_party/sqlite/test/interrupt.test183
-rwxr-xr-xthird_party/sqlite/test/intpkey.test605
-rwxr-xr-xthird_party/sqlite/test/io.test549
-rwxr-xr-xthird_party/sqlite/test/ioerr.test407
-rwxr-xr-xthird_party/sqlite/test/ioerr2.test134
-rwxr-xr-xthird_party/sqlite/test/ioerr3.test42
-rwxr-xr-xthird_party/sqlite/test/ioerr4.test100
-rwxr-xr-xthird_party/sqlite/test/ioerr5.test207
-rwxr-xr-xthird_party/sqlite/test/join.test506
-rwxr-xr-xthird_party/sqlite/test/join2.test75
-rwxr-xr-xthird_party/sqlite/test/join3.test62
-rwxr-xr-xthird_party/sqlite/test/join4.test98
-rwxr-xr-xthird_party/sqlite/test/join5.test110
-rwxr-xr-xthird_party/sqlite/test/journal1.test67
-rwxr-xr-xthird_party/sqlite/test/jrnlmode.test387
-rwxr-xr-xthird_party/sqlite/test/lastinsert.test366
-rwxr-xr-xthird_party/sqlite/test/laststmtchanges.test281
-rwxr-xr-xthird_party/sqlite/test/like.test514
-rwxr-xr-xthird_party/sqlite/test/like2.test1009
-rwxr-xr-xthird_party/sqlite/test/limit.test472
-rwxr-xr-xthird_party/sqlite/test/loadext.test272
-rwxr-xr-xthird_party/sqlite/test/loadext2.test144
-rwxr-xr-xthird_party/sqlite/test/lock.test354
-rwxr-xr-xthird_party/sqlite/test/lock2.test169
-rwxr-xr-xthird_party/sqlite/test/lock3.test78
-rwxr-xr-xthird_party/sqlite/test/lock4.test115
-rwxr-xr-xthird_party/sqlite/test/lock5.test177
-rwxr-xr-xthird_party/sqlite/test/lookaside.test97
-rwxr-xr-xthird_party/sqlite/test/main.test486
-rwxr-xr-xthird_party/sqlite/test/malloc.test620
-rwxr-xr-xthird_party/sqlite/test/malloc3.test664
-rwxr-xr-xthird_party/sqlite/test/malloc4.test194
-rwxr-xr-xthird_party/sqlite/test/malloc5.test397
-rwxr-xr-xthird_party/sqlite/test/malloc6.test55
-rwxr-xr-xthird_party/sqlite/test/malloc7.test48
-rwxr-xr-xthird_party/sqlite/test/malloc8.test95
-rwxr-xr-xthird_party/sqlite/test/malloc9.test51
-rwxr-xr-xthird_party/sqlite/test/mallocA.test78
-rwxr-xr-xthird_party/sqlite/test/mallocAll.test67
-rwxr-xr-xthird_party/sqlite/test/mallocB.test51
-rwxr-xr-xthird_party/sqlite/test/mallocC.test107
-rwxr-xr-xthird_party/sqlite/test/mallocD.test65
-rwxr-xr-xthird_party/sqlite/test/mallocE.test48
-rwxr-xr-xthird_party/sqlite/test/mallocF.test76
-rwxr-xr-xthird_party/sqlite/test/mallocG.test68
-rwxr-xr-xthird_party/sqlite/test/mallocH.test73
-rwxr-xr-xthird_party/sqlite/test/mallocI.test43
-rwxr-xr-xthird_party/sqlite/test/malloc_common.tcl168
-rwxr-xr-xthird_party/sqlite/test/manydb.test91
-rwxr-xr-xthird_party/sqlite/test/memdb.test417
-rwxr-xr-xthird_party/sqlite/test/memleak.test98
-rwxr-xr-xthird_party/sqlite/test/memsubsys1.test232
-rwxr-xr-xthird_party/sqlite/test/memsubsys2.test157
-rwxr-xr-xthird_party/sqlite/test/minmax.test542
-rwxr-xr-xthird_party/sqlite/test/minmax2.test387
-rwxr-xr-xthird_party/sqlite/test/minmax3.test217
-rwxr-xr-xthird_party/sqlite/test/misc1.test585
-rwxr-xr-xthird_party/sqlite/test/misc2.test435
-rwxr-xr-xthird_party/sqlite/test/misc3.test341
-rwxr-xr-xthird_party/sqlite/test/misc4.test211
-rwxr-xr-xthird_party/sqlite/test/misc5.test1872
-rwxr-xr-xthird_party/sqlite/test/misc6.test48
-rwxr-xr-xthird_party/sqlite/test/misc7.test475
-rwxr-xr-xthird_party/sqlite/test/misuse.test207
-rwxr-xr-xthird_party/sqlite/test/mutex1.test163
-rwxr-xr-xthird_party/sqlite/test/mutex2.test101
-rwxr-xr-xthird_party/sqlite/test/nan.test228
-rwxr-xr-xthird_party/sqlite/test/notnull.test505
-rwxr-xr-xthird_party/sqlite/test/null.test283
-rwxr-xr-xthird_party/sqlite/test/openv2.test41
-rwxr-xr-xthird_party/sqlite/test/pager.test571
-rwxr-xr-xthird_party/sqlite/test/pager2.test408
-rwxr-xr-xthird_party/sqlite/test/pager3.test73
-rwxr-xr-xthird_party/sqlite/test/pageropt.test201
-rwxr-xr-xthird_party/sqlite/test/pagesize.test182
-rwxr-xr-xthird_party/sqlite/test/permutations.test547
-rwxr-xr-xthird_party/sqlite/test/pragma.test1206
-rwxr-xr-xthird_party/sqlite/test/pragma2.test119
-rwxr-xr-xthird_party/sqlite/test/printf.test3718
-rwxr-xr-xthird_party/sqlite/test/progress.test177
-rwxr-xr-xthird_party/sqlite/test/ptrchng.test223
-rwxr-xr-xthird_party/sqlite/test/quick.test134
-rwxr-xr-xthird_party/sqlite/test/quote.test89
-rwxr-xr-xthird_party/sqlite/test/rdonly.test78
-rwxr-xr-xthird_party/sqlite/test/reindex.test171
-rwxr-xr-xthird_party/sqlite/test/rollback.test82
-rwxr-xr-xthird_party/sqlite/test/rowid.test701
-rwxr-xr-xthird_party/sqlite/test/rtree.test40
-rwxr-xr-xthird_party/sqlite/test/safety.test93
-rwxr-xr-xthird_party/sqlite/test/schema.test367
-rwxr-xr-xthird_party/sqlite/test/schema2.test340
-rwxr-xr-xthird_party/sqlite/test/select1.test1044
-rwxr-xr-xthird_party/sqlite/test/select2.test185
-rwxr-xr-xthird_party/sqlite/test/select3.test264
-rwxr-xr-xthird_party/sqlite/test/select4.test796
-rwxr-xr-xthird_party/sqlite/test/select5.test201
-rwxr-xr-xthird_party/sqlite/test/select6.test506
-rwxr-xr-xthird_party/sqlite/test/select7.test159
-rwxr-xr-xthird_party/sqlite/test/select8.test62
-rwxr-xr-xthird_party/sqlite/test/select9.test421
-rwxr-xr-xthird_party/sqlite/test/selectA.test1278
-rwxr-xr-xthird_party/sqlite/test/selectB.test379
-rwxr-xr-xthird_party/sqlite/test/server1.test171
-rwxr-xr-xthird_party/sqlite/test/shared.test1005
-rwxr-xr-xthird_party/sqlite/test/shared2.test131
-rwxr-xr-xthird_party/sqlite/test/shared3.test105
-rwxr-xr-xthird_party/sqlite/test/shared4.test237
-rwxr-xr-xthird_party/sqlite/test/shared_err.test501
-rwxr-xr-xthird_party/sqlite/test/shortread1.test52
-rwxr-xr-xthird_party/sqlite/test/sidedelete.test92
-rwxr-xr-xthird_party/sqlite/test/soak.test89
-rwxr-xr-xthird_party/sqlite/test/softheap1.test50
-rwxr-xr-xthird_party/sqlite/test/sort.test467
-rwxr-xr-xthird_party/sqlite/test/speed1.test293
-rwxr-xr-xthird_party/sqlite/test/speed1p.explain366
-rwxr-xr-xthird_party/sqlite/test/speed1p.test342
-rwxr-xr-xthird_party/sqlite/test/speed2.test339
-rwxr-xr-xthird_party/sqlite/test/speed3.test186
-rwxr-xr-xthird_party/sqlite/test/speed4.test231
-rwxr-xr-xthird_party/sqlite/test/speed4p.explain283
-rwxr-xr-xthird_party/sqlite/test/speed4p.test292
-rwxr-xr-xthird_party/sqlite/test/sqllimits1.test841
-rwxr-xr-xthird_party/sqlite/test/subquery.test494
-rwxr-xr-xthird_party/sqlite/test/subselect.test210
-rwxr-xr-xthird_party/sqlite/test/substr.test130
-rwxr-xr-xthird_party/sqlite/test/sync.test98
-rwxr-xr-xthird_party/sqlite/test/table.test676
-rwxr-xr-xthird_party/sqlite/test/tableapi.test252
-rwxr-xr-xthird_party/sqlite/test/tclsqlite.test504
-rwxr-xr-xthird_party/sqlite/test/tempdb.test55
-rwxr-xr-xthird_party/sqlite/test/temptable.test422
-rwxr-xr-xthird_party/sqlite/test/tester.tcl940
-rwxr-xr-xthird_party/sqlite/test/thread001.test138
-rwxr-xr-xthird_party/sqlite/test/thread002.test109
-rwxr-xr-xthird_party/sqlite/test/thread1.test172
-rwxr-xr-xthird_party/sqlite/test/thread2.test246
-rwxr-xr-xthird_party/sqlite/test/thread_common.tcl88
-rwxr-xr-xthird_party/sqlite/test/threadtest1.c289
-rwxr-xr-xthird_party/sqlite/test/threadtest2.c133
-rwxr-xr-xthird_party/sqlite/test/tkt1435.test111
-rwxr-xr-xthird_party/sqlite/test/tkt1443.test180
-rwxr-xr-xthird_party/sqlite/test/tkt1444.test56
-rwxr-xr-xthird_party/sqlite/test/tkt1449.test262
-rwxr-xr-xthird_party/sqlite/test/tkt1473.test728
-rwxr-xr-xthird_party/sqlite/test/tkt1501.test36
-rwxr-xr-xthird_party/sqlite/test/tkt1512.test54
-rwxr-xr-xthird_party/sqlite/test/tkt1514.test27
-rwxr-xr-xthird_party/sqlite/test/tkt1536.test38
-rwxr-xr-xthird_party/sqlite/test/tkt1537.test122
-rwxr-xr-xthird_party/sqlite/test/tkt1567.test51
-rwxr-xr-xthird_party/sqlite/test/tkt1644.test111
-rwxr-xr-xthird_party/sqlite/test/tkt1667.test83
-rwxr-xr-xthird_party/sqlite/test/tkt1873.test72
-rwxr-xr-xthird_party/sqlite/test/tkt2141.test61
-rwxr-xr-xthird_party/sqlite/test/tkt2192.test140
-rwxr-xr-xthird_party/sqlite/test/tkt2213.test29
-rwxr-xr-xthird_party/sqlite/test/tkt2251.test108
-rwxr-xr-xthird_party/sqlite/test/tkt2285.test56
-rwxr-xr-xthird_party/sqlite/test/tkt2332.test67
-rwxr-xr-xthird_party/sqlite/test/tkt2339.test100
-rwxr-xr-xthird_party/sqlite/test/tkt2391.test49
-rwxr-xr-xthird_party/sqlite/test/tkt2409.test218
-rwxr-xr-xthird_party/sqlite/test/tkt2450.test48
-rwxr-xr-xthird_party/sqlite/test/tkt2640.test124
-rwxr-xr-xthird_party/sqlite/test/tkt2643.test39
-rwxr-xr-xthird_party/sqlite/test/tkt2686.test81
-rwxr-xr-xthird_party/sqlite/test/tkt2767.test88
-rwxr-xr-xthird_party/sqlite/test/tkt2817.test72
-rwxr-xr-xthird_party/sqlite/test/tkt2820.test94
-rwxr-xr-xthird_party/sqlite/test/tkt2822.test336
-rwxr-xr-xthird_party/sqlite/test/tkt2832.test65
-rwxr-xr-xthird_party/sqlite/test/tkt2854.test149
-rwxr-xr-xthird_party/sqlite/test/tkt2920.test78
-rwxr-xr-xthird_party/sqlite/test/tkt2927.test674
-rwxr-xr-xthird_party/sqlite/test/tkt2942.test62
-rwxr-xr-xthird_party/sqlite/test/tkt3080.test77
-rwxr-xr-xthird_party/sqlite/test/tkt3093.test86
-rwxr-xr-xthird_party/sqlite/test/tkt3121.test50
-rwxr-xr-xthird_party/sqlite/test/tkt3201.test74
-rwxr-xr-xthird_party/sqlite/test/tokenize.test65
-rwxr-xr-xthird_party/sqlite/test/trace.test169
-rwxr-xr-xthird_party/sqlite/test/trans.test937
-rwxr-xr-xthird_party/sqlite/test/trigger1.test631
-rwxr-xr-xthird_party/sqlite/test/trigger2.test742
-rwxr-xr-xthird_party/sqlite/test/trigger3.test190
-rwxr-xr-xthird_party/sqlite/test/trigger4.test200
-rwxr-xr-xthird_party/sqlite/test/trigger5.test43
-rwxr-xr-xthird_party/sqlite/test/trigger6.test82
-rwxr-xr-xthird_party/sqlite/test/trigger7.test121
-rwxr-xr-xthird_party/sqlite/test/trigger8.test42
-rwxr-xr-xthird_party/sqlite/test/trigger9.test223
-rwxr-xr-xthird_party/sqlite/test/triggerA.test230
-rwxr-xr-xthird_party/sqlite/test/triggerB.test44
-rwxr-xr-xthird_party/sqlite/test/types.test324
-rwxr-xr-xthird_party/sqlite/test/types2.test340
-rwxr-xr-xthird_party/sqlite/test/types3.test99
-rwxr-xr-xthird_party/sqlite/test/unique.test253
-rwxr-xr-xthird_party/sqlite/test/update.test608
-rwxr-xr-xthird_party/sqlite/test/utf16align.test84
-rwxr-xr-xthird_party/sqlite/test/vacuum.test340
-rwxr-xr-xthird_party/sqlite/test/vacuum2.test128
-rwxr-xr-xthird_party/sqlite/test/vacuum3.test317
-rwxr-xr-xthird_party/sqlite/test/varint.test32
-rwxr-xr-xthird_party/sqlite/test/veryquick.test15
-rwxr-xr-xthird_party/sqlite/test/view.test542
-rwxr-xr-xthird_party/sqlite/test/vtab1.test1165
-rwxr-xr-xthird_party/sqlite/test/vtab2.test108
-rwxr-xr-xthird_party/sqlite/test/vtab3.test140
-rwxr-xr-xthird_party/sqlite/test/vtab4.test193
-rwxr-xr-xthird_party/sqlite/test/vtab5.test152
-rwxr-xr-xthird_party/sqlite/test/vtab6.test574
-rwxr-xr-xthird_party/sqlite/test/vtab7.test207
-rwxr-xr-xthird_party/sqlite/test/vtab8.test78
-rwxr-xr-xthird_party/sqlite/test/vtab9.test70
-rwxr-xr-xthird_party/sqlite/test/vtabA.test134
-rwxr-xr-xthird_party/sqlite/test/vtabB.test79
-rwxr-xr-xthird_party/sqlite/test/vtabC.test114
-rwxr-xr-xthird_party/sqlite/test/vtab_alter.test103
-rwxr-xr-xthird_party/sqlite/test/vtab_err.test71
-rwxr-xr-xthird_party/sqlite/test/vtab_shared.test61
-rwxr-xr-xthird_party/sqlite/test/where.test1160
-rwxr-xr-xthird_party/sqlite/test/where2.test624
-rwxr-xr-xthird_party/sqlite/test/where3.test216
-rwxr-xr-xthird_party/sqlite/test/where4.test273
-rwxr-xr-xthird_party/sqlite/test/where5.test288
-rwxr-xr-xthird_party/sqlite/test/where6.test130
-rwxr-xr-xthird_party/sqlite/test/zeroblob.test230
-rwxr-xr-xthird_party/sqlite/tool/diffdb.c44
-rwxr-xr-xthird_party/sqlite/tool/fragck.tcl149
-rwxr-xr-xthird_party/sqlite/tool/lemon.c4866
-rwxr-xr-xthird_party/sqlite/tool/lempar.c813
-rwxr-xr-xthird_party/sqlite/tool/memleak.awk29
-rwxr-xr-xthird_party/sqlite/tool/memleak2.awk29
-rwxr-xr-xthird_party/sqlite/tool/memleak3.tcl233
-rwxr-xr-xthird_party/sqlite/tool/mkkeywordhash.c559
-rwxr-xr-xthird_party/sqlite/tool/mkopts.tcl51
-rwxr-xr-xthird_party/sqlite/tool/mksqlite3c.tcl288
-rwxr-xr-xthird_party/sqlite/tool/mksqlite3internalh.tcl146
-rwxr-xr-xthird_party/sqlite/tool/omittest.tcl215
-rwxr-xr-xthird_party/sqlite/tool/opcodeDoc.awk23
-rwxr-xr-xthird_party/sqlite/tool/report1.txt66
-rwxr-xr-xthird_party/sqlite/tool/showdb.c86
-rwxr-xr-xthird_party/sqlite/tool/showjournal.c76
-rwxr-xr-xthird_party/sqlite/tool/soak1.tcl103
-rwxr-xr-xthird_party/sqlite/tool/space_used.tcl111
-rwxr-xr-xthird_party/sqlite/tool/spaceanal.tcl863
-rwxr-xr-xthird_party/sqlite/tool/speedtest.tcl275
-rwxr-xr-xthird_party/sqlite/tool/speedtest16.c169
-rwxr-xr-xthird_party/sqlite/tool/speedtest2.tcl207
-rwxr-xr-xthird_party/sqlite/tool/speedtest8.c351
-rwxr-xr-xthird_party/sqlite/tool/speedtest8inst1.c216
-rwxr-xr-xthird_party/sqlite/www/capi3ref.tcl1882
642 files changed, 270036 insertions, 18508 deletions
diff --git a/third_party/sqlite/Makefile.arm-wince-mingw32ce-gcc b/third_party/sqlite/Makefile.arm-wince-mingw32ce-gcc
new file mode 100755
index 0000000..d1ca389
--- /dev/null
+++ b/third_party/sqlite/Makefile.arm-wince-mingw32ce-gcc
@@ -0,0 +1,138 @@
+#!/usr/make
+#
+# Makefile for SQLITE
+#
+# This is a template makefile for SQLite. Most people prefer to
+# use the autoconf generated "configure" script to generate the
+# makefile automatically. But that does not work for everybody
+# and in every situation. If you are having problems with the
+# "configure" script, you might want to try this makefile as an
+# alternative. Create a copy of this file, edit the parameters
+# below and type "make".
+#
+
+#### The directory where to find the mingw32ce tools
+MINGW32CE = /opt/mingw32ce/bin
+
+#### The target prefix of the mingw32ce tools
+TARGET = arm-wince-mingw32ce
+
+#### The toplevel directory of the source tree. This is the directory
+# that contains this "Makefile.in" and the "configure.in" script.
+#
+TOP = ../sqlite
+
+#### C Compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+BCC = gcc -g -O2
+#BCC = /opt/ancic/bin/c89 -0
+
+#### If the target operating system supports the "usleep()" system
+# call, then define the HAVE_USLEEP macro for all C modules.
+#
+USLEEP =
+#USLEEP = -DHAVE_USLEEP=1
+
+#### If you want the SQLite library to be safe for use within a
+# multi-threaded program, then define the following macro
+# appropriately:
+#
+THREADSAFE = -DTHREADSAFE=1
+#THREADSAFE = -DTHREADSAFE=0
+
+#### Specify any extra linker options needed to make the library
+# thread safe
+#
+#THREADLIB = -lpthread
+THREADLIB =
+
+#### Specify any extra libraries needed to access required functions.
+#
+#TLIBS = -lrt # fdatasync on Solaris 8
+TLIBS =
+
+#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1
+# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all
+# malloc()s and free()s in order to track down memory leaks.
+#
+# SQLite uses some expensive assert() statements in the inner loop.
+# You can make the library go almost twice as fast if you compile
+# with -DNDEBUG=1
+#
+#OPTS = -DSQLITE_DEBUG=2
+#OPTS = -DSQLITE_DEBUG=1
+#OPTS =
+OPTS = -DNDEBUG=1 -DSQLITE_OS_WIN=1 -D_WIN32_WCE=1
+#OPTS += -DHAVE_FDATASYNC=1
+
+#### The suffix to add to executable files. ".exe" for windows.
+# Nothing for unix.
+#
+EXE = .exe
+#EXE =
+
+#### C Compile and options for use in building executables that
+# will run on the target platform. This is usually the same
+# as BCC, unless you are cross-compiling.
+#
+#TCC = gcc -O6
+#TCC = gcc -g -O0 -Wall
+#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
+#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
+TCC = $(MINGW32CE)/$(TARGET)-gcc -O2
+#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive
+
+#### Tools used to build a static library.
+#
+#AR = ar cr
+#AR = /opt/mingw/bin/i386-mingw32-ar cr
+AR = $(MINGW32CE)/$(TARGET)-ar cr
+#RANLIB = ranlib
+#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib
+RANLIB = $(MINGW32CE)/$(TARGET)-ranlib
+
+#MKSHLIB = gcc -shared
+#SO = so
+#SHPREFIX = lib
+MKSHLIB = $(MINGW32CE)/$(TARGET)-gcc -shared
+SO = dll
+SHPREFIX =
+
+#### Extra compiler options needed for programs that use the TCL library.
+#
+#TCL_FLAGS =
+#TCL_FLAGS = -DSTATIC_BUILD=1
+TCL_FLAGS = -I/home/drh/tcltk/8.4linux
+#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1
+#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux
+
+#### Linker options needed to link against the TCL library.
+#
+#LIBTCL = -ltcl -lm -ldl
+LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl
+#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt
+#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc
+
+#### Additional objects for SQLite library when TCL support is enabled.
+TCLOBJ =
+#TCLOBJ = tclsqlite.o
+
+#### Compiler options needed for programs that use the readline() library.
+#
+READLINE_FLAGS =
+#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline
+
+#### Linker options needed by programs using readline() must link against.
+#
+LIBREADLINE =
+#LIBREADLINE = -static -lreadline -ltermcap
+
+#### Which "awk" program provides nawk compatibilty
+#
+# NAWK = nawk
+NAWK = awk
+
+# You should not have to change anything below this line
+###############################################################################
+include $(TOP)/main.mk
diff --git a/third_party/sqlite/Makefile.in b/third_party/sqlite/Makefile.in
new file mode 100755
index 0000000..ce94298
--- /dev/null
+++ b/third_party/sqlite/Makefile.in
@@ -0,0 +1,766 @@
+#!/usr/make
+#
+# Makefile for SQLITE
+#
+# This makefile is suppose to be configured automatically using the
+# autoconf. But if that does not work for you, you can configure
+# the makefile manually. Just set the parameters below to values that
+# work well for your system.
+#
+# If the configure script does not work out-of-the-box, you might
+# be able to get it to work by giving it some hints. See the comment
+# at the beginning of configure.in for additional information.
+#
+
+# The toplevel directory of the source tree. This is the directory
+# that contains this "Makefile.in" and the "configure.in" script.
+#
+TOP = @srcdir@
+
+# C Compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+BCC = @BUILD_CC@ @BUILD_CFLAGS@
+
+# C Compile and options for use in building executables that
+# will run on the target platform. (BCC and TCC are usually the
+# same unless your are cross-compiling.)
+#
+TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src
+
+# 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
+
+# Define -DNDEBUG to compile without debugging (i.e., for production usage)
+# Omitting the define will cause extra debugging code to be inserted and
+# includes extra comments when "EXPLAIN stmt" is used.
+#
+TCC += @TARGET_DEBUG@ @XTHREADCONNECT@
+
+# Compiler options needed for programs that use the TCL library.
+#
+TCC += @TCL_INCLUDE_SPEC@
+
+# The library that programs using TCL must link against.
+#
+LIBTCL = @TCL_LIB_SPEC@ @TCL_LIBS@
+
+# Compiler options needed for programs that use the readline() library.
+#
+READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
+
+# The library that programs using readline() must link against.
+#
+LIBREADLINE = @TARGET_READLINE_LIBS@
+
+# Should the database engine be compiled threadsafe
+#
+TCC += -DSQLITE_THREADSAFE=@SQLITE_THREADSAFE@
+
+# Do threads override each others locks by default (1), or do we test (-1)
+#
+TCC += -DSQLITE_THREAD_OVERRIDE_LOCK=@THREADSOVERRIDELOCKS@
+
+# Any target libraries which libsqlite must be linked against
+#
+TLIBS = @LIBS@
+
+# Flags controlling use of the in memory btree implementation
+#
+# SQLITE_TEMP_STORE is 0 to force temporary tables to be in a file, 1 to
+# default to file, 2 to default to memory, and 3 to force temporary
+# tables to always be in memory.
+#
+TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
+
+# Enable/disable loadable extensions, and other optional features
+# based on configuration. (-DSQLITE_OMIT*). The same set of OMIT
+# flags should be passed to the LEMON parser generator and the
+# mkkeywordhash tool as well.
+OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
+
+TCC += $(OPT_FEATURE_FLAGS)
+
+# Version numbers and release number for the SQLite being compiled.
+#
+VERSION = @VERSION@
+VERSION_NUMBER = @VERSION_NUMBER@
+RELEASE = @RELEASE@
+
+# Filename extensions
+#
+BEXE = @BUILD_EXEEXT@
+TEXE = @TARGET_EXEEXT@
+
+# The following variable is "1" if the configure script was able to locate
+# the tclConfig.sh file. It is an empty string otherwise. When this
+# variable is "1", the TCL extension library (libtclsqlite3.so) is built
+# and installed.
+#
+HAVE_TCL = @HAVE_TCL@
+
+# This is the command to use for tclsh - normally just "tclsh", but we may
+# know the specific version we want to use
+#
+TCLSH_CMD = @TCLSH_CMD@
+
+# The suffix used on shared libraries. Ex: ".dll", ".so", ".dylib"
+#
+SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
+
+# If gcov support was enabled by the configure script, add the appropriate
+# flags here. It's not always as easy as just having the user add the right
+# CFLAGS / LDFLAGS, because libtool wants to use CFLAGS when linking, which
+# causes build errors with -fprofile-arcs -ftest-coverage with some GCCs.
+# Supposedly GCC does the right thing if you use --coverage, but in
+# practice it still fails. See:
+#
+# http://www.mail-archive.com/debian-gcc@lists.debian.org/msg26197.html
+#
+# for more info.
+#
+GCOV_CFLAGS1 = -fprofile-arcs -ftest-coverage
+GCOV_LDFLAGS1 = -lgcov
+USE_GCOV = @USE_GCOV@
+LTCOMPILE_EXTRAS += $(GCOV_CFLAGS$(USE_GCOV))
+LTLINK_EXTRAS += $(GCOV_LDFLAGS$(USE_GCOV))
+
+
+# The directory into which to store package information for
+
+# Some standard variables and programs
+#
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+pkgconfigdir = $(libdir)/pkgconfig
+bindir = @bindir@
+includedir = @includedir@
+INSTALL = @INSTALL@
+LIBTOOL = ./libtool
+ALLOWRELEASE = @ALLOWRELEASE@
+
+# libtool compile/link/install
+LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(TCC) $(LTCOMPILE_EXTRAS)
+LTLINK = $(LIBTOOL) --mode=link $(TCC) @LDFLAGS@ $(LTLINK_EXTRAS)
+LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
+
+# nawk compatible awk.
+NAWK = @AWK@
+
+# You should not have to change anything below this line
+###############################################################################
+
+# Object files for the SQLite library (non-amalgamation).
+#
+OBJS0 = alter.lo analyze.lo attach.lo auth.lo bitvec.lo btmutex.lo \
+ btree.lo build.lo callback.lo complete.lo date.lo \
+ delete.lo expr.lo fault.lo func.lo global.lo \
+ hash.lo journal.lo insert.lo loadext.lo \
+ main.lo malloc.lo mem1.lo mem2.lo mem3.lo mem4.lo mem5.lo mem6.lo \
+ mutex.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
+ opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
+ pager.lo parse.lo pragma.lo prepare.lo printf.lo random.lo \
+ select.lo status.lo table.lo tokenize.lo trigger.lo update.lo \
+ util.lo vacuum.lo \
+ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbefifo.lo vdbemem.lo \
+ where.lo utf.lo legacy.lo vtab.lo
+
+# Object files for the amalgamation.
+#
+OBJS1 = sqlite3.lo
+
+# Determine the real value of LIBOBJ based on the 'configure' script
+#
+USE_AMALGAMATION = @USE_AMALGAMATION@
+LIBOBJ = $(OBJS$(USE_AMALGAMATION))
+
+
+# All of the source code files.
+#
+SRC = \
+ $(TOP)/src/alter.c \
+ $(TOP)/src/analyze.c \
+ $(TOP)/src/attach.c \
+ $(TOP)/src/auth.c \
+ $(TOP)/src/bitvec.c \
+ $(TOP)/src/btmutex.c \
+ $(TOP)/src/btree.c \
+ $(TOP)/src/btree.h \
+ $(TOP)/src/btreeInt.h \
+ $(TOP)/src/build.c \
+ $(TOP)/src/callback.c \
+ $(TOP)/src/complete.c \
+ $(TOP)/src/date.c \
+ $(TOP)/src/delete.c \
+ $(TOP)/src/expr.c \
+ $(TOP)/src/fault.c \
+ $(TOP)/src/func.c \
+ $(TOP)/src/global.c \
+ $(TOP)/src/hash.c \
+ $(TOP)/src/hash.h \
+ $(TOP)/src/hwtime.h \
+ $(TOP)/src/insert.c \
+ $(TOP)/src/journal.c \
+ $(TOP)/src/legacy.c \
+ $(TOP)/src/loadext.c \
+ $(TOP)/src/main.c \
+ $(TOP)/src/malloc.c \
+ $(TOP)/src/mem1.c \
+ $(TOP)/src/mem2.c \
+ $(TOP)/src/mem3.c \
+ $(TOP)/src/mem4.c \
+ $(TOP)/src/mem5.c \
+ $(TOP)/src/mem6.c \
+ $(TOP)/src/mutex.c \
+ $(TOP)/src/mutex.h \
+ $(TOP)/src/mutex_os2.c \
+ $(TOP)/src/mutex_unix.c \
+ $(TOP)/src/mutex_w32.c \
+ $(TOP)/src/os.c \
+ $(TOP)/src/os.h \
+ $(TOP)/src/os_common.h \
+ $(TOP)/src/os_unix.c \
+ $(TOP)/src/os_win.c \
+ $(TOP)/src/os_os2.c \
+ $(TOP)/src/pager.c \
+ $(TOP)/src/pager.h \
+ $(TOP)/src/parse.y \
+ $(TOP)/src/pragma.c \
+ $(TOP)/src/prepare.c \
+ $(TOP)/src/printf.c \
+ $(TOP)/src/random.c \
+ $(TOP)/src/select.c \
+ $(TOP)/src/status.c \
+ $(TOP)/src/shell.c \
+ $(TOP)/src/sqlite.h.in \
+ $(TOP)/src/sqlite3ext.h \
+ $(TOP)/src/sqliteInt.h \
+ $(TOP)/src/sqliteLimit.h \
+ $(TOP)/src/table.c \
+ $(TOP)/src/tclsqlite.c \
+ $(TOP)/src/tokenize.c \
+ $(TOP)/src/trigger.c \
+ $(TOP)/src/utf.c \
+ $(TOP)/src/update.c \
+ $(TOP)/src/util.c \
+ $(TOP)/src/vacuum.c \
+ $(TOP)/src/vdbe.c \
+ $(TOP)/src/vdbe.h \
+ $(TOP)/src/vdbeapi.c \
+ $(TOP)/src/vdbeaux.c \
+ $(TOP)/src/vdbeblob.c \
+ $(TOP)/src/vdbefifo.c \
+ $(TOP)/src/vdbemem.c \
+ $(TOP)/src/vdbeInt.h \
+ $(TOP)/src/vtab.c \
+ $(TOP)/src/where.c
+
+# Generated source code files
+#
+SRC += \
+ keywordhash.h \
+ opcodes.c \
+ opcodes.h \
+ parse.c \
+ parse.h \
+ config.h \
+ sqlite3.h
+
+# Source code for extensions
+#
+SRC += \
+ $(TOP)/ext/fts1/fts1.c \
+ $(TOP)/ext/fts1/fts1.h \
+ $(TOP)/ext/fts1/fts1_hash.c \
+ $(TOP)/ext/fts1/fts1_hash.h \
+ $(TOP)/ext/fts1/fts1_porter.c \
+ $(TOP)/ext/fts1/fts1_tokenizer.h \
+ $(TOP)/ext/fts1/fts1_tokenizer1.c
+SRC += \
+ $(TOP)/ext/fts2/fts2.c \
+ $(TOP)/ext/fts2/fts2.h \
+ $(TOP)/ext/fts2/fts2_hash.c \
+ $(TOP)/ext/fts2/fts2_hash.h \
+ $(TOP)/ext/fts2/fts2_icu.c \
+ $(TOP)/ext/fts2/fts2_porter.c \
+ $(TOP)/ext/fts2/fts2_tokenizer.h \
+ $(TOP)/ext/fts2/fts2_tokenizer.c \
+ $(TOP)/ext/fts2/fts2_tokenizer1.c
+SRC += \
+ $(TOP)/ext/fts3/fts3.c \
+ $(TOP)/ext/fts3/fts3.h \
+ $(TOP)/ext/fts3/fts3_hash.c \
+ $(TOP)/ext/fts3/fts3_hash.h \
+ $(TOP)/ext/fts3/fts3_icu.c \
+ $(TOP)/ext/fts3/fts3_porter.c \
+ $(TOP)/ext/fts3/fts3_tokenizer.h \
+ $(TOP)/ext/fts3/fts3_tokenizer.c \
+ $(TOP)/ext/fts3/fts3_tokenizer1.c
+SRC += \
+ $(TOP)/ext/icu/icu.c
+SRC += \
+ $(TOP)/ext/rtree/rtree.h \
+ $(TOP)/ext/rtree/rtree.c
+
+# Source code to the library files needed by the test fixture
+#
+TESTSRC2 = \
+ $(TOP)/src/attach.c \
+ $(TOP)/src/bitvec.c \
+ $(TOP)/src/btree.c \
+ $(TOP)/src/build.c \
+ $(TOP)/src/date.c \
+ $(TOP)/src/expr.c \
+ $(TOP)/src/func.c \
+ $(TOP)/src/insert.c \
+ $(TOP)/src/malloc.c \
+ $(TOP)/src/os.c \
+ $(TOP)/src/os_os2.c \
+ $(TOP)/src/os_unix.c \
+ $(TOP)/src/os_win.c \
+ $(TOP)/src/pager.c \
+ $(TOP)/src/pragma.c \
+ $(TOP)/src/prepare.c \
+ $(TOP)/src/printf.c \
+ $(TOP)/src/random.c \
+ $(TOP)/src/select.c \
+ $(TOP)/src/tokenize.c \
+ $(TOP)/src/utf.c \
+ $(TOP)/src/util.c \
+ $(TOP)/src/vdbe.c \
+ $(TOP)/src/vdbeapi.c \
+ $(TOP)/src/vdbeaux.c \
+ $(TOP)/src/vdbemem.c \
+ $(TOP)/src/where.c \
+ parse.c
+
+# Source code to the actual test files.
+#
+TESTSRC = \
+ $(TOP)/src/test1.c \
+ $(TOP)/src/test2.c \
+ $(TOP)/src/test3.c \
+ $(TOP)/src/test4.c \
+ $(TOP)/src/test5.c \
+ $(TOP)/src/test6.c \
+ $(TOP)/src/test7.c \
+ $(TOP)/src/test8.c \
+ $(TOP)/src/test9.c \
+ $(TOP)/src/test_autoext.c \
+ $(TOP)/src/test_async.c \
+ $(TOP)/src/test_btree.c \
+ $(TOP)/src/test_config.c \
+ $(TOP)/src/test_devsym.c \
+ $(TOP)/src/test_func.c \
+ $(TOP)/src/test_hexio.c \
+ $(TOP)/src/test_malloc.c \
+ $(TOP)/src/test_md5.c \
+ $(TOP)/src/test_mutex.c \
+ $(TOP)/src/test_onefile.c \
+ $(TOP)/src/test_osinst.c \
+ $(TOP)/src/test_schema.c \
+ $(TOP)/src/test_server.c \
+ $(TOP)/src/test_tclvar.c \
+ $(TOP)/src/test_thread.c
+
+# Header files used by all library source files.
+#
+HDR = \
+ sqlite3.h \
+ $(TOP)/src/btree.h \
+ $(TOP)/src/btreeInt.h \
+ $(TOP)/src/hash.h \
+ $(TOP)/src/hwtime.h \
+ $(TOP)/src/sqliteLimit.h \
+ $(TOP)/src/mutex.h \
+ opcodes.h \
+ $(TOP)/src/os.h \
+ $(TOP)/src/os_common.h \
+ $(TOP)/src/sqlite3ext.h \
+ $(TOP)/src/sqliteInt.h \
+ $(TOP)/src/vdbe.h \
+ $(TOP)/src/vdbeInt.h \
+ parse.h \
+ config.h
+
+# Header files used by extensions
+#
+HDR += \
+ $(TOP)/ext/fts1/fts1.h \
+ $(TOP)/ext/fts1/fts1_hash.h \
+ $(TOP)/ext/fts1/fts1_tokenizer.h
+HDR += \
+ $(TOP)/ext/fts2/fts2.h \
+ $(TOP)/ext/fts2/fts2_hash.h \
+ $(TOP)/ext/fts2/fts2_tokenizer.h
+HDR += \
+ $(TOP)/ext/fts3/fts3.h \
+ $(TOP)/ext/fts3/fts3_hash.h \
+ $(TOP)/ext/fts3/fts3_tokenizer.h
+HDR += \
+ $(TOP)/ext/rtree/rtree.h
+
+# If using the amalgamation, use sqlite3.c directly to build the test
+# fixture. Otherwise link against libsqlite3.la. (This distinction is
+# necessary because the test fixture requires non-API symbols which are
+# hidden when the library is built via the amalgamation).
+#
+TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
+TESTFIXTURE_SRC1 = sqlite3.c
+TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
+
+
+# This is the default Makefile target. The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all: sqlite3.h libsqlite3.la sqlite3$(TEXE) $(HAVE_TCL:1=libtclsqlite3.la)
+
+Makefile: $(TOP)/Makefile.in
+ ./config.status
+
+# Generate the file "last_change" which contains the date of change
+# of the most recently modified source code file
+#
+last_change: $(SRC)
+ cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \
+ | $(NAWK) '{print $$5,$$6}' >last_change
+
+libsqlite3.la: $(LIBOBJ)
+ $(LTLINK) -o $@ $(LIBOBJ) $(TLIBS) \
+ ${ALLOWRELEASE} -rpath "$(libdir)" -version-info "8:6:8"
+
+libtclsqlite3.la: tclsqlite.lo libsqlite3.la
+ $(LTLINK) -o $@ tclsqlite.lo \
+ libsqlite3.la @TCL_STUB_LIB_SPEC@ $(TLIBS) \
+ -rpath "$(libdir)/sqlite" \
+ -version-info "8:6:8"
+
+sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
+ $(LTLINK) $(READLINE_FLAGS) \
+ -o $@ $(TOP)/src/shell.c libsqlite3.la \
+ $(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
+
+# This target creates a directory named "tsrc" and fills it with
+# copies of all of the C source code and header files needed to
+# build on the target system. Some of the C source code and header
+# files are automatically generated. This target takes care of
+# all that automatic generation.
+#
+.target_source: $(SRC)
+ rm -rf tsrc
+ mkdir -p tsrc
+ cp $(SRC) tsrc
+ rm tsrc/sqlite.h.in tsrc/parse.y
+ touch .target_source
+
+sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl
+
+# Rules to build the LEMON compiler generator
+#
+lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
+ $(BCC) -o $@ $(TOP)/tool/lemon.c
+ cp $(TOP)/tool/lempar.c .
+
+
+# Rule to build the amalgamation
+#
+sqlite3.lo: sqlite3.c
+ $(LTCOMPILE) -c sqlite3.c
+
+# Rules to build individual files
+#
+alter.lo: $(TOP)/src/alter.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/alter.c
+
+analyze.lo: $(TOP)/src/analyze.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/analyze.c
+
+attach.lo: $(TOP)/src/attach.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/attach.c
+
+auth.lo: $(TOP)/src/auth.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/auth.c
+
+bitvec.lo: $(TOP)/src/bitvec.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/bitvec.c
+
+btmutex.lo: $(TOP)/src/btmutex.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/btmutex.c
+
+btree.lo: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
+ $(LTCOMPILE) -c $(TOP)/src/btree.c
+
+build.lo: $(TOP)/src/build.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/build.c
+
+callback.lo: $(TOP)/src/callback.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/callback.c
+
+complete.lo: $(TOP)/src/complete.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/complete.c
+
+date.lo: $(TOP)/src/date.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/date.c
+
+delete.lo: $(TOP)/src/delete.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/delete.c
+
+expr.lo: $(TOP)/src/expr.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/expr.c
+
+fault.lo: $(TOP)/src/fault.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/fault.c
+
+func.lo: $(TOP)/src/func.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/func.c
+
+global.lo: $(TOP)/src/global.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/global.c
+
+hash.lo: $(TOP)/src/hash.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/hash.c
+
+insert.lo: $(TOP)/src/insert.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/insert.c
+
+journal.lo: $(TOP)/src/journal.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/journal.c
+
+legacy.lo: $(TOP)/src/legacy.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/legacy.c
+
+loadext.lo: $(TOP)/src/loadext.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/loadext.c
+
+main.lo: $(TOP)/src/main.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/main.c
+
+malloc.lo: $(TOP)/src/malloc.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/malloc.c
+
+mem1.lo: $(TOP)/src/mem1.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem1.c
+
+mem2.lo: $(TOP)/src/mem2.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem2.c
+
+mem3.lo: $(TOP)/src/mem3.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem3.c
+
+mem4.lo: $(TOP)/src/mem4.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem4.c
+
+mem5.lo: $(TOP)/src/mem5.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem5.c
+
+mem6.lo: $(TOP)/src/mem6.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem6.c
+
+mutex.lo: $(TOP)/src/mutex.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex.c
+
+mutex_os2.lo: $(TOP)/src/mutex_os2.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_os2.c
+
+mutex_unix.lo: $(TOP)/src/mutex_unix.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_unix.c
+
+mutex_w32.lo: $(TOP)/src/mutex_w32.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_w32.c
+
+pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
+ $(LTCOMPILE) -c $(TOP)/src/pager.c
+
+opcodes.lo: opcodes.c
+ $(LTCOMPILE) -c opcodes.c
+
+opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
+ sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
+
+opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
+ cat parse.h $(TOP)/src/vdbe.c | $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
+
+os.lo: $(TOP)/src/os.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/os.c
+
+os_unix.lo: $(TOP)/src/os_unix.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/os_unix.c
+
+os_win.lo: $(TOP)/src/os_win.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/os_win.c
+
+os_os2.lo: $(TOP)/src/os_os2.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/os_os2.c
+
+parse.lo: parse.c $(HDR)
+ $(LTCOMPILE) -c parse.c
+
+parse.h: parse.c
+
+parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk
+ cp $(TOP)/src/parse.y .
+ ./lemon$(BEXE) $(OPTS) $(OPT_FEATURE_FLAGS) parse.y
+ mv parse.h parse.h.temp
+ $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
+
+pragma.lo: $(TOP)/src/pragma.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/pragma.c
+
+prepare.lo: $(TOP)/src/prepare.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/prepare.c
+
+printf.lo: $(TOP)/src/printf.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/printf.c
+
+random.lo: $(TOP)/src/random.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/random.c
+
+select.lo: $(TOP)/src/select.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/select.c
+
+status.lo: $(TOP)/src/status.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/status.c
+
+sqlite3.h: $(TOP)/src/sqlite.h.in
+ sed -e s/--VERS--/$(RELEASE)/ $(TOP)/src/sqlite.h.in | \
+ sed -e s/--VERSION-NUMBER--/$(VERSION_NUMBER)/ >sqlite3.h
+
+table.lo: $(TOP)/src/table.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/table.c
+
+tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR)
+ $(LTCOMPILE) -DUSE_TCL_STUBS=1 -c $(TOP)/src/tclsqlite.c
+
+tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/tokenize.c
+
+keywordhash.h: $(TOP)/tool/mkkeywordhash.c
+ $(BCC) -o mkkeywordhash$(BEXE) $(OPTS) $(OPT_FEATURE_FLAGS) $(TOP)/tool/mkkeywordhash.c
+ ./mkkeywordhash$(BEXE) >keywordhash.h
+
+trigger.lo: $(TOP)/src/trigger.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/trigger.c
+
+update.lo: $(TOP)/src/update.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/update.c
+
+utf.lo: $(TOP)/src/utf.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/utf.c
+
+util.lo: $(TOP)/src/util.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/util.c
+
+vacuum.lo: $(TOP)/src/vacuum.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vacuum.c
+
+vdbe.lo: $(TOP)/src/vdbe.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbe.c
+
+vdbeapi.lo: $(TOP)/src/vdbeapi.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbeapi.c
+
+vdbeaux.lo: $(TOP)/src/vdbeaux.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbeaux.c
+
+vdbeblob.lo: $(TOP)/src/vdbeblob.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbeblob.c
+
+vdbefifo.lo: $(TOP)/src/vdbefifo.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbefifo.c
+
+vdbemem.lo: $(TOP)/src/vdbemem.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vdbemem.c
+
+vtab.lo: $(TOP)/src/vtab.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/vtab.c
+
+where.lo: $(TOP)/src/where.c $(HDR)
+ $(LTCOMPILE) -c $(TOP)/src/where.c
+
+tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR)
+ $(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c
+
+tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR)
+ $(LTCOMPILE) -DTCL_USE_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c
+
+tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
+ $(LTLINK) -o $@ tclsqlite-shell.lo \
+ libsqlite3.la $(LIBTCL)
+
+testfixture$(TEXE): $(TESTFIXTURE_SRC)
+ $(LTLINK) -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_NO_SYNC=1\
+ -DSQLITE_CRASH_TEST=1 \
+ -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE $(TEMP_STORE) \
+ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
+
+
+fulltest: testfixture$(TEXE) sqlite3$(TEXE)
+ ./testfixture$(TEXE) $(TOP)/test/all.test
+
+test: testfixture$(TEXE) sqlite3$(TEXE)
+ ./testfixture$(TEXE) $(TOP)/test/veryquick.test
+
+sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl
+ sed \
+ -e '/^#/d' \
+ -e 's,\\,\\\\,g' \
+ -e 's,",\\",g' \
+ -e 's,^,",' \
+ -e 's,$$,\\n",' \
+ $(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h
+ $(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
+ -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \
+ $(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL)
+
+
+install: sqlite3$(BEXE) libsqlite3.la sqlite3.h ${HAVE_TCL:1=tcl_install}
+ $(INSTALL) -d $(DESTDIR)$(libdir)
+ $(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
+ $(INSTALL) -d $(DESTDIR)$(bindir)
+ $(LTINSTALL) sqlite3$(BEXE) $(DESTDIR)$(bindir)
+ $(INSTALL) -d $(DESTDIR)$(includedir)
+ $(INSTALL) -m 0644 sqlite3.h $(DESTDIR)$(includedir)
+ $(INSTALL) -m 0644 $(TOP)/src/sqlite3ext.h $(DESTDIR)$(includedir)
+ $(INSTALL) -d $(DESTDIR)$(pkgconfigdir)
+ $(INSTALL) -m 0644 sqlite3.pc $(DESTDIR)$(pkgconfigdir)
+
+tcl_install: libtclsqlite3.la
+ $(TCLSH_CMD) $(TOP)/tclinstaller.tcl $(VERSION)
+
+clean:
+ rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
+ rm -f sqlite3.h opcodes.*
+ rm -rf .libs .deps tsrc
+ rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz
+ rm -f mkkeywordhash$(BEXE) keywordhash.h
+ rm -f $(PUBLISH)
+ rm -f *.da *.bb *.bbg gmon.out
+ rm -f testfixture$(TEXE) test.db
+ rm -f common.tcl
+ rm -f sqlite3.dll sqlite3.lib sqlite3.def
+ rm -f sqlite3.c .target_source
+
+distclean: clean
+ rm -f config.log config.status libtool Makefile sqlite3.pc
+
+#
+# Windows section
+#
+dll: sqlite3.dll
+
+REAL_LIBOBJ = $(LIBOBJ:%.lo=.libs/%.o)
+
+$(REAL_LIBOBJ): $(LIBOBJ)
+
+sqlite3.def: $(REAL_LIBOBJ)
+ echo 'EXPORTS' >sqlite3.def
+ nm $(REAL_LIBOBJ) | grep ' T ' | grep ' _sqlite3_' \
+ | sed 's/^.* _//' >>sqlite3.def
+
+sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def
+ $(TCC) -shared -o $@ sqlite3.def \
+ -Wl,"--strip-all" $(REAL_LIBOBJ)
diff --git a/third_party/sqlite/Makefile.linux-gcc b/third_party/sqlite/Makefile.linux-gcc
new file mode 100755
index 0000000..ac607c4
--- /dev/null
+++ b/third_party/sqlite/Makefile.linux-gcc
@@ -0,0 +1,147 @@
+#!/usr/make
+#
+# Makefile for SQLITE
+#
+# This is a template makefile for SQLite. Most people prefer to
+# use the autoconf generated "configure" script to generate the
+# makefile automatically. But that does not work for everybody
+# and in every situation. If you are having problems with the
+# "configure" script, you might want to try this makefile as an
+# alternative. Create a copy of this file, edit the parameters
+# below and type "make".
+#
+
+#### The toplevel directory of the source tree. This is the directory
+# that contains this "Makefile.in" and the "configure.in" script.
+#
+TOP = ..
+
+#### C Compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+BCC = gcc -g -O2
+#BCC = /opt/ancic/bin/c89 -0
+
+#### If the target operating system supports the "usleep()" system
+# call, then define the HAVE_USLEEP macro for all C modules.
+#
+#USLEEP =
+USLEEP = -DHAVE_USLEEP=1
+
+#### If you want the SQLite library to be safe for use within a
+# multi-threaded program, then define the following macro
+# appropriately:
+#
+#THREADSAFE = -DTHREADSAFE=1
+THREADSAFE = -DTHREADSAFE=1
+
+#### Specify any extra linker options needed to make the library
+# thread safe
+#
+#THREADLIB = -lpthread
+THREADLIB = -lpthread
+
+#### Specify any extra libraries needed to access required functions.
+#
+#TLIBS = -lrt # fdatasync on Solaris 8
+TLIBS =
+
+#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1
+# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all
+# malloc()s and free()s in order to track down memory leaks.
+#
+# SQLite uses some expensive assert() statements in the inner loop.
+# You can make the library go almost twice as fast if you compile
+# with -DNDEBUG=1
+#
+#OPTS = -DSQLITE_DEBUG=2
+#OPTS = -DSQLITE_DEBUG=1
+#OPTS =
+
+# These flags match those for SQLITE_CFLAGS in config.mk.
+
+OPTS += -DNDEBUG
+OPTS += -DSQLITE_CORE
+OPTS += -DSQLITE_ENABLE_FTS1 -DSQLITE_ENABLE_BROKEN_FTS1
+OPTS += -DSQLITE_ENABLE_FTS2 -DSQLITE_ENABLE_BROKEN_FTS2
+OPTS += -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600
+OPTS += -DHAVE_USLEEP=1
+
+# Additional SQLite tests.
+OPTS += -DSQLITE_MEMDEBUG=1
+
+# Don't include these ones, they break the SQLite tests.
+# -DSQLITE_OMIT_ATTACH=1 \
+# -DSQLITE_OMIT_LOAD_EXTENSION=1 \
+# -DSQLITE_OMIT_VACUUM=1 \
+# -DSQLITE_TRANSACTION_DEFAULT_IMMEDIATE=1 \
+
+# TODO(shess) I can't see why I need this setting.
+OPTS += -DOS_UNIX=1
+
+#### The suffix to add to executable files. ".exe" for windows.
+# Nothing for unix.
+#
+#EXE = .exe
+EXE =
+
+#### C Compile and options for use in building executables that
+# will run on the target platform. This is usually the same
+# as BCC, unless you are cross-compiling.
+#
+TCC = gcc -O6
+#TCC = gcc -g -O0 -Wall
+#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
+#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
+#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive
+
+#### Tools used to build a static library.
+#
+AR = ar cr
+#AR = /opt/mingw/bin/i386-mingw32-ar cr
+RANLIB = ranlib
+#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib
+
+MKSHLIB = gcc -shared
+SO = so
+SHPREFIX = lib
+# SO = dll
+# SHPREFIX =
+
+#### Extra compiler options needed for programs that use the TCL library.
+#
+TCL_FLAGS = -I/usr/include/tcl8.4
+#TCL_FLAGS = -DSTATIC_BUILD=1
+#TCL_FLAGS = -I/home/drh/tcltk/8.4linux
+#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1
+#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux
+
+#### Linker options needed to link against the TCL library.
+#
+LIBTCL = -ltcl8.4 -lm -ldl
+#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl
+#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt
+#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc
+
+#### Additional objects for SQLite library when TCL support is enabled.
+#TCLOBJ =
+TCLOBJ = tclsqlite.o
+
+#### Compiler options needed for programs that use the readline() library.
+#
+READLINE_FLAGS =
+#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline
+
+#### Linker options needed by programs using readline() must link against.
+#
+LIBREADLINE =
+#LIBREADLINE = -static -lreadline -ltermcap
+
+#### Which "awk" program provides nawk compatibilty
+#
+# NAWK = nawk
+NAWK = awk
+
+# You should not have to change anything below this line
+###############################################################################
+include $(TOP)/main.mk
diff --git a/third_party/sqlite/README b/third_party/sqlite/README
new file mode 100755
index 0000000..5942a8f
--- /dev/null
+++ b/third_party/sqlite/README
@@ -0,0 +1,34 @@
+This directory contains source code to
+
+ SQLite: An Embeddable SQL Database Engine
+
+To compile the project, first create a directory in which to place
+the build products. It is recommended, but not required, that the
+build directory be separate from the source directory. Cd into the
+build directory and then from the build directory run the configure
+script found at the root of the source tree. Then run "make".
+
+For example:
+
+ tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
+ mkdir bld ;# Build will occur in a sibling directory
+ cd bld ;# Change to the build directory
+ ../sqlite/configure ;# Run the configure script
+ make ;# Run the makefile.
+ make install ;# (Optional) Install the build products
+
+The configure script uses autoconf 2.61 and libtool. If the configure
+script does not work out for you, there is a generic makefile named
+"Makefile.linux-gcc" in the top directory of the source tree that you
+can copy and edit to suit your needs. Comments on the generic makefile
+show what changes are needed.
+
+The linux binaries on the website are created using the generic makefile,
+not the configure script.
+The windows binaries on the website are created using MinGW32 configured
+as a cross-compiler running under Linux. For details, see the ./publish.sh
+script at the top-level of the source tree.
+
+Contacts:
+
+ http://www.sqlite.org/
diff --git a/third_party/sqlite/README.google b/third_party/sqlite/README.google
index abc7ee2..93cfdc2 100644
--- a/third_party/sqlite/README.google
+++ b/third_party/sqlite/README.google
@@ -1,59 +1,105 @@
-This directory contains a partial snapshot of the sqlite library from
-http://sqlite.org.
-
-Current version: 3.5.3, released 23-Nov-2007.
-
-(This was built as 3.4.2 [13-Aug-2007] as described below, with the four
-intervening patches 4315, 4427, 4546, and 4556 applied by hand.)
-
-To import a new snapshot of sqlite:
-
-- Visit http://sqlite.org/download.html and download the latest source
- distribution.
-- Unpack the source on a Linux machine.
-- Change to the source directory and run:
- $ ./configure --disable-tcl # build headers
- $ make # build some generated files
-- Copy the generated .c/.h files from the sqlite directory to this directory,
- as well as those in src/ and ext/fts2/. Omit files which have been omitted
- here. Here's an easy way to be sure you get everything:
- $ cp /path/to/source/*.[ch] . # don't forget subdirs, too
- $ gvn status | grep -v ^M # print the names of all new files
- $ mkdir new; mv each new file to new
- Then rebuild, and if any of the files in new/ are needed, move them
- back into this directory, add them to the project, and "gvn add" them.
-- Apply the preload-cache.diff (see below)
-- Update this README to reflect the new version number.
-
-Modifications for this release:
-- I marked all changes I made with "evanm", so you can find them with
- "grep evanm *".
-- Most files include sqlite3ext.h with SQLITE_CORE #defined, but two don't:
- fts2_tokenizer.c and icu.c. Without this #define, the calls in
- fts2_tokenizer.c try to go through some pointer to the sqlite API instead of
- calling the functions directly (to work as a loadable module), but then crash
- (because the other files never initialize that loadable module support). As
- a hack I #defined it in these files, but it'd be nice to figure out what
- really ought to happen here (perhaps this file is new and hasn't been tested
- to verify it works right).
-- shell_icu.c is a Chrome-specific file used to load our ICU data. shell.c
- has been modifed to call into shell_icu.c.
-- fts2_icu.c has a critical bug. U8_NEXT is used over a UTF-16 string. It's replaced
- by U16_NEXT (jungshik)
-
-Patch
------
-The file preload-cache.diff patch must be applied to add a new function we use
-to prime the database cache. It allows much faster performance by reading the
-file in one contiguous operation rather than bringing it in organically, which
-involves a lot of seeking.
-
-FTS2 modification
------------------
-In fts2.c, we added an additional check from fts3 to try to catch some
-additional problems. In buildTerms on line 3807, we replaced
- if( iPosition<0 ){
-with:
- if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){
-It is from this change to sqlite:
- http://www.sqlite.org/cvstrac/chngview?cn=4514
+Instructions for importing a new release of sqlite from sqlite.org.
+
+First, you need to be on Linux.
+
+Find the release you want at:
+ http://www.sqlite.org/cvstrac/timeline
+
+Search for "Milestone", and find the appropriate release. Click
+through, and snag the "Date" for use in DATE line below.
+Unfortunately, the actual displayed date string on that page will not
+work, you have to make it YYYY/MM/DD. [TODO(shess) Determine if the
+fact that sqlite.org thinks it's running under UTC is relevant.]
+
+DATE='2007/01/24 09:54:56'
+
+# Get to the third_party/sqlite directory in your Chromium client.
+cd ../third_party/sqlite
+
+# Run the super awesome automatic merge tool (requires kdiff3).
+# NOTE(shess): The following (which runs google_generate_preprocessed.sh) will
+# produce different output on grhat versus goobuntu; I've been running it on
+# goobuntu.
+./google_update_sqlite.sh "$DATE"
+
+# Resolve any conflicts. Figure out if we've got everything we should
+# have (see below), or if we can omit any changes we no longer need.
+
+# TODO(shess) If we don't want the CVS dirs.
+# find sqlite_vendor -name CVS -execdir rm -rf {} + -prune
+
+# Find a sucker. Send review.
+# TODO(shess) Describe an appropriate comment style. Seems like it
+# should include the DATE line, and the sqlite version number.
+
+
+--------------------------------------------
+
+Why all this convolution? The problem being solved is that we want to
+harvest changes from the sqlite CVS tree, and merge them with any
+local changes we make. In CVS, you would do this using a "vendor
+import", which is essentially a branch dedictated to the vendor
+version which is merged with local changes.
+
+third_party/sqlite_vendor/... is the CVS checkout for a particular
+build from sqlite.org. third_party/sqlite_google/... is the local
+version, with our local modifications. So we update the sqlite_vendor
+tree, then use perforce to downintegrate changes into our
+locally-modified tree. The downintegrate will call out any
+conflicting changes, but will otherwise just merge things together.
+Basically, sqlite_vendor is a gateway between CVS and perforce.
+
+Scott Hess <shess@google.com>, April 9, 2007.
+
+--------------------------------------------
+
+How to run the SQLite tests for the Gears version of SQLite on Linux.
+
+cd ../third_party/sqlite_google/
+mkdir build
+cd build
+make -f ../Makefile.linux-gcc testfixture
+make -f ../Makefile.linux-gcc test > /tmp/test.log
+egrep -v 'Ok$' /tmp/test.log
+# When run on a locally-mounted disk, my output ends with:
+# 0 errors out of 57887 tests
+
+Scott Hess <shess@google.com>, December 11, 2007
+
+--------------------------------------------
+
+As of September 12, 2008, these are our changes from sqlite_vendor:
+
+ - fts2.c disables fts2_tokenizer().
+ - sqlite3Poison() in src/btree.c.
+ - BEGIN defaults to BEGIN IMMEDIATE in parse.y.
+ - Tweak to SQLITE_EXTENSION_INIT* in sqlite3ext.h.
+ - That implied a change in src/test_autoext.c for testing.
+ - Added fts.test and fts1.test in tests, modified quick.test.
+ - src/os_symbian.cc.
+ - Modifications to Makefile.linux-gcc and main.mk for compiling
+ SQLite tests.
+ - Compile warning fixed in func.c (check if this is still needed)
+ - Fixed a typo bug in fts2_icu.c: "strlen(nInput)" (filed upstream as
+ http://www.sqlite.org/cvstrac/tktview?tn=3543)
+
+Changes from Chrome:
+ - I marked all changes I made with "evanm", so you can find them with
+ "grep evanm *".
+ - Most files include sqlite3ext.h with SQLITE_CORE #defined, but two don't:
+ fts2_tokenizer.c and icu.c. Without this #define, the calls in
+ fts2_tokenizer.c try to go through some pointer to the sqlite API instead
+ of calling the functions directly (to work as a loadable module), but then
+ crash (because the other files never initialize that loadable module
+ support). As a hack I #defined it in these files, but it'd be nice to
+ figure out what really ought to happen here (perhaps this file is new and
+ hasn't been tested to verify it works right). Update: Seems this is an
+ issue we get because we're using fts2 instead of fts3.
+ - shell_icu.c is a Chrome-specific file used to load our ICU data. shell.c
+ has been modifed to call into shell_icu.c.
+ - fts2_icu.c has a critical bug. U8_NEXT is used over a UTF-16 string. It's
+ rep$ by U16_NEXT (jungshik)
+ - Added a new function sqlite3Preload we use to prime the database cache. It
+ allows much faster performance by reading the file in one contiguous
+ operation rather than bringing it in organically, which involves a lot of
+ seeking.
diff --git a/third_party/sqlite/SConscript b/third_party/sqlite/SConscript
index eb69b44..16face9 100644
--- a/third_party/sqlite/SConscript
+++ b/third_party/sqlite/SConscript
@@ -39,56 +39,89 @@ if env.Bit('windows'):
)
input_files = [
- 'alter.c',
- 'analyze.c',
- 'attach.c',
- 'auth.c',
- 'btree.c',
- 'build.c',
- 'callback.c',
- 'complete.c',
- 'date.c',
- 'delete.c',
- 'expr.c',
- 'fts2.c',
- 'fts2_hash.c',
- 'fts2_icu.c',
- 'fts2_porter.c',
- 'fts2_tokenizer.c',
- 'fts2_tokenizer1.c',
- 'func.c',
- 'hash.c',
- 'icu.c',
- 'insert.c',
- 'legacy.c',
- 'loadext.c',
- 'main.c',
- 'malloc.c',
- 'opcodes.c',
- 'os.c',
- 'os_unix.c',
- 'os_win.c',
- 'pager.c',
- 'parse.c',
- 'pragma.c',
- 'prepare.c',
- 'printf.c',
- 'random.c',
- 'select.c',
- 'table.c',
- 'tokenize.c',
- 'trigger.c',
- 'update.c',
- 'utf.c',
- 'util.c',
- 'vacuum.c',
- 'vdbe.c',
- 'vdbeapi.c',
- 'vdbeaux.c',
- 'vdbefifo.c',
- 'vdbemem.c',
- 'vtab.c',
- 'where.c',
+ 'ext/fts2/fts2.c',
+ 'ext/fts2/fts2.h',
+ 'ext/fts2/fts2_hash.c',
+ 'ext/fts2/fts2_hash.h',
+ 'ext/fts2/fts2_icu.c',
+ 'ext/fts2/fts2_porter.c',
+ 'ext/fts2/fts2_tokenizer.c',
+ 'ext/fts2/fts2_tokenizer.h',
+ 'ext/fts2/fts2_tokenizer1.c',
+ 'ext/icu/icu.c',
+ 'preprocessed/keywordhash.h',
+ 'preprocessed/opcodes.c',
+ 'preprocessed/opcodes.h',
+ 'preprocessed/parse.c',
+ 'preprocessed/parse.h',
+ 'preprocessed/sqlite3.h',
+ 'src/alter.c',
+ 'src/analyze.c',
+ 'src/attach.c',
+ 'src/auth.c',
+ 'src/bitvec.c',
+ 'src/btmutex.c',
+ 'src/btree.c',
+ 'src/btree.h',
+ 'src/btreeInt.h',
+ 'src/build.c',
+ 'src/callback.c',
+ 'src/complete.c',
+ 'src/date.c',
+ 'src/delete.c',
+ 'src/expr.c',
+ 'src/fault.c',
+ 'src/func.c',
+ 'src/global.c',
+ 'src/hash.c',
+ 'src/hash.h',
+ 'src/insert.c',
+ 'src/legacy.c',
+ 'src/loadext.c',
+ 'src/main.c',
+ 'src/malloc.c',
+ 'src/mem1.c',
+ 'src/mem2.c',
+ 'src/mem3.c',
+ 'src/mem4.c',
+ 'src/mem5.c',
+ 'src/mem6.c',
+ 'src/mutex.c',
+ 'src/mutex.h',
+ 'src/mutex_w32.c',
+ 'src/os.c',
+ 'src/os.h',
+ 'src/os_common.h',
+ 'src/os_win.c',
+ 'src/pager.c',
+ 'src/pager.h',
+ 'src/pragma.c',
+ 'src/prepare.c',
+ 'src/printf.c',
+ 'src/random.c',
+ 'src/select.c',
+ 'src/shell_icu.c',
+ 'src/sqlite3ext.h',
+ 'src/sqliteInt.h',
+ 'src/sqliteLimit.h',
+ 'src/status.c',
+ 'src/table.c',
+ 'src/tokenize.c',
+ 'src/trigger.c',
+ 'src/update.c',
+ 'src/utf.c',
+ 'src/util.c',
+ 'src/vacuum.c',
+ 'src/vdbe.c',
+ 'src/vdbe.h',
+ 'src/vdbeInt.h',
+ 'src/vdbeapi.c',
+ 'src/vdbeaux.c',
+ 'src/vdbeblob.c',
+ 'src/vdbefifo.c',
+ 'src/vdbemem.c',
+ 'src/vtab.c',
+ 'src/where.c',
]
env.ChromeLibrary('sqlite', input_files)
diff --git a/third_party/sqlite/VERSION b/third_party/sqlite/VERSION
new file mode 100755
index 0000000..9575d51
--- /dev/null
+++ b/third_party/sqlite/VERSION
@@ -0,0 +1 @@
+3.6.1
diff --git a/third_party/sqlite/VERSION_DATE b/third_party/sqlite/VERSION_DATE
new file mode 100644
index 0000000..ceecd94
--- /dev/null
+++ b/third_party/sqlite/VERSION_DATE
@@ -0,0 +1 @@
+2008/12/16 18:16:00
diff --git a/third_party/sqlite/aclocal.m4 b/third_party/sqlite/aclocal.m4
new file mode 100755
index 0000000..659b9b8
--- /dev/null
+++ b/third_party/sqlite/aclocal.m4
@@ -0,0 +1,7924 @@
+# generated automatically by aclocal 1.10.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 56 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+_LT_PROG_ECHO_BACKSLASH
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_quote(m4_if([$2], [],
+ m4_quote(lt_decl_tag_varnames),
+ m4_quote(m4_shift($@)))),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS))))])
+m4_define([_lt_decl_varnames_tagged], [lt_combine([$1], [$2], [_], $3)])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "X$<var>" | $Xsed -e "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\[$]0 --fallback-echo"')dnl "
+ lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\`
+ ;;
+esac
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+ lt_cl_success=:
+ test "$silent" = yes &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+ exec AS_MESSAGE_LOG_FD>/dev/null
+ $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+ exec AS_MESSAGE_LOG_FD>>config.log
+ $lt_cl_success || AS_EXIT(1)
+fi
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ _LT_PROG_XSI_SHELLFNS
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS="$save_LDFLAGS"
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES
+# --------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+ if test "$GCC" = "yes"; then
+ output_verbose_link_cmd=echo
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ m4_if([$1], [CXX],
+[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX
+# -----------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+AC_LINK_IFELSE(AC_LANG_PROGRAM,[
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi],[])
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[ifdef([AC_DIVERSION_NOTICE],
+ [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+ [AC_DIVERT_PUSH(NOTICE)])
+$1
+AC_DIVERT_POP
+])# _LT_SHELL_INIT
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[_LT_SHELL_INIT([
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X[$]1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+[$]*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "[$]0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(lt_ECHO)
+])
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1],
+ [An echo program that does not interpret backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[AC_CHECK_TOOL(AR, ar, false)
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1])
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ exit (status);
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen="shl_load"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen="dlopen"],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test "$hard_links" = no; then
+ AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+ [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+ test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+ test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[123]]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ # Handle Gentoo/FreeBSD as it was Linux
+ case $host_vendor in
+ gentoo)
+ version_type=linux ;;
+ *)
+ version_type=freebsd-$objformat ;;
+ esac
+
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ linux)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ need_lib_prefix=no
+ need_version=no
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[[89]] | openbsd2.[[89]].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_name_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+ [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$1; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_DECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_DECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method == "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+ AC_SUBST([DUMPBIN])
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM="-lm")
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC*)
+ # IBM XL 8.0 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd*)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ icc* | ecc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+ ;;
+ cygwin* | mingw*)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ freebsd1*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ AC_LINK_IFELSE(int foo(void) {},
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ )
+ LDFLAGS="$save_LDFLAGS"
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ else
+ case $host_os in
+ openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)])
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
+ [[If ld is used when linking, flag to hardcode $libdir into a binary
+ during linking. This must work even if $libdir does not exist]])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [fix_srcfile_path], [1],
+ [Fix the shell variable $srcfile for the compiler])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report which library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_PROG_CXX
+# ------------
+# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++
+# compiler, we have our own version here.
+m4_defun([_LT_PROG_CXX],
+[
+pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes])
+AC_PROG_CXX
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+ ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+ (test "X$CXX" != "Xg++"))) ; then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_CXX
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_CXX], [])
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[AC_REQUIRE([_LT_PROG_CXX])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test "$GXX" = yes; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test "$with_gnu_ld" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='${wl}'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GXX" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared
+ # libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd[[12]]*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ gnu*)
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test $with_gnu_ld = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test $with_gnu_ld = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 will use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ xl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd2*)
+ # C++ shared libraries are fairly broken
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=echo
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require `-G' NOT `-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)="$GXX"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+])
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test $p = "-L" ||
+ test $p = "-R"; then
+ prev=$p
+ continue
+ else
+ prev=
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ case $p in
+ -L* | -R*)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+ fi
+ fi
+ ;;
+
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+
+linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+solaris*)
+ case $cc_basename in
+ CC*)
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ # Adding this requires a known-good setup of shared libraries for
+ # Sun compiler versions before 5.6, else PIC objects from an old
+ # archive will be linked into the output, leading to subtle bugs.
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_PROG_F77
+# ------------
+# Since AC_PROG_F77 is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_F77],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes])
+AC_PROG_F77
+if test -z "$F77" || test "X$F77" = "Xno"; then
+ _lt_disable_F77=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_F77
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_F77], [])
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_REQUIRE([_LT_PROG_F77])dnl
+AC_LANG_PUSH(Fortran 77)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${F77-"f77"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$G77"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_PROG_FC
+# -----------
+# Since AC_PROG_FC is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_FC],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes])
+AC_PROG_FC
+if test -z "$FC" || test "X$FC" = "Xno"; then
+ _lt_disable_FC=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_FC
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_FC], [])
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_REQUIRE([_LT_PROG_FC])dnl
+AC_LANG_PUSH(Fortran)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${FC-"f95"}
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC="$lt_save_CC"
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f $lt_ac_sed && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test $lt_ac_count -gt 10 && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test $lt_ac_count -gt $lt_ac_max; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_XSI_SHELLFNS
+# ---------------------
+# Bourne and XSI compatible variants of some useful shell functions.
+m4_defun([_LT_PROG_XSI_SHELLFNS],
+[case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $[*] ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+dnl func_dirname_and_basename
+dnl A portable version of this function is already defined in general.m4sh
+dnl so there is no need for it here.
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[[^=]]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$[@]"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]+=\$[2]"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]=\$$[1]\$[2]"
+}
+
+_LT_EOF
+ ;;
+ esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl `shared' nor `disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [0], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [pic_mode="$withval"],
+ [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+m4_define([lt_combine],
+[m4_if([$2], [], [],
+ [m4_if([$4], [], [],
+ [lt_join(m4_quote(m4_default([$1], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_prefix, [$2],
+ [m4_foreach(_Lt_suffix, lt_car([m4_shiftn(3, $@)]),
+ [_Lt_prefix[]$3[]_Lt_suffix ])])))))])])dnl
+])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Generated from ltversion.in.
+
+# serial 2976 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.2.4])
+m4_define([LT_PACKAGE_REVISION], [1.2976])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.2.4'
+macro_revision='1.2976'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 4 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+
diff --git a/third_party/sqlite/addopcodes.awk b/third_party/sqlite/addopcodes.awk
new file mode 100755
index 0000000..b806b1d
--- /dev/null
+++ b/third_party/sqlite/addopcodes.awk
@@ -0,0 +1,32 @@
+#!/usr/bin/awk
+#
+# This script appends additional token codes to the end of the
+# parse.h file that lemon generates. These extra token codes are
+# not used by the parser. But they are used by the tokenizer and/or
+# the code generator.
+#
+#
+BEGIN {
+ max = 0
+}
+/^#define TK_/ {
+ print $0
+ if( max<$3 ) max = $3
+}
+END {
+ printf "#define TK_%-29s %4d\n", "TO_TEXT", max+1
+ printf "#define TK_%-29s %4d\n", "TO_BLOB", max+2
+ printf "#define TK_%-29s %4d\n", "TO_NUMERIC", max+3
+ printf "#define TK_%-29s %4d\n", "TO_INT", max+4
+ printf "#define TK_%-29s %4d\n", "TO_REAL", max+5
+ printf "#define TK_%-29s %4d\n", "END_OF_FILE", max+6
+ printf "#define TK_%-29s %4d\n", "ILLEGAL", max+7
+ printf "#define TK_%-29s %4d\n", "SPACE", max+8
+ printf "#define TK_%-29s %4d\n", "UNCLOSED_STRING", max+9
+ printf "#define TK_%-29s %4d\n", "COMMENT", max+10
+ printf "#define TK_%-29s %4d\n", "FUNCTION", max+11
+ printf "#define TK_%-29s %4d\n", "COLUMN", max+12
+ printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", max+13
+ printf "#define TK_%-29s %4d\n", "AGG_COLUMN", max+14
+ printf "#define TK_%-29s %4d\n", "CONST_FUNC", max+15
+}
diff --git a/third_party/sqlite/art/2005osaward.gif b/third_party/sqlite/art/2005osaward.gif
new file mode 100755
index 0000000..fa6d7d7
--- /dev/null
+++ b/third_party/sqlite/art/2005osaward.gif
Binary files differ
diff --git a/third_party/sqlite/art/SQLite.eps b/third_party/sqlite/art/SQLite.eps
new file mode 100755
index 0000000..1f334ec
--- /dev/null
+++ b/third_party/sqlite/art/SQLite.eps
Binary files differ
diff --git a/third_party/sqlite/art/SQLite.gif b/third_party/sqlite/art/SQLite.gif
new file mode 100755
index 0000000..5ec05b0
--- /dev/null
+++ b/third_party/sqlite/art/SQLite.gif
Binary files differ
diff --git a/third_party/sqlite/art/SQLiteLogo3.tiff b/third_party/sqlite/art/SQLiteLogo3.tiff
new file mode 100755
index 0000000..70b88e7
--- /dev/null
+++ b/third_party/sqlite/art/SQLiteLogo3.tiff
Binary files differ
diff --git a/third_party/sqlite/art/SQLite_big.gif b/third_party/sqlite/art/SQLite_big.gif
new file mode 100755
index 0000000..dc9e6a0
--- /dev/null
+++ b/third_party/sqlite/art/SQLite_big.gif
Binary files differ
diff --git a/third_party/sqlite/art/nocopy.gif b/third_party/sqlite/art/nocopy.gif
new file mode 100755
index 0000000..cc4a59c
--- /dev/null
+++ b/third_party/sqlite/art/nocopy.gif
Binary files differ
diff --git a/third_party/sqlite/art/powered_by_sqlite.gif b/third_party/sqlite/art/powered_by_sqlite.gif
new file mode 100755
index 0000000..5bfed02
--- /dev/null
+++ b/third_party/sqlite/art/powered_by_sqlite.gif
Binary files differ
diff --git a/third_party/sqlite/config.guess b/third_party/sqlite/config.guess
new file mode 100755
index 0000000..34093cc
--- /dev/null
+++ b/third_party/sqlite/config.guess
@@ -0,0 +1,1535 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+# Inc.
+
+timestamp='2007-07-22'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+if [ "${UNAME_SYSTEM}" = "Linux" ] ; then
+ eval $set_cc_for_build
+ cat << EOF > $dummy.c
+ #include <features.h>
+ #ifdef __UCLIBC__
+ # ifdef __UCLIBC_CONFIG_VERSION__
+ LIBC=uclibc __UCLIBC_CONFIG_VERSION__
+ # else
+ LIBC=uclibc
+ # endif
+ #else
+ LIBC=gnu
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep LIBC= | sed -e 's: ::g'`
+fi
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:[3456]*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ EM64T | authenticamd)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-${LIBC}
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-${LIBC}
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-${LIBC}
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-${LIBC}
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-${LIBC}
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-${LIBC}
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+ PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+ *) echo hppa-unknown-linux-${LIBC} ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-${LIBC}
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-${LIBC}
+ exit ;;
+ xtensa:Linux:*:*)
+ echo xtensa-unknown-linux-${LIBC}
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-${LIBC}"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}aout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}coff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}oldld"
+ exit ;;
+ esac
+ # This should get integrated into the C code below, but now we hack
+ if [ "$LIBC" != "gnu" ] ; then echo "$TENTATIVE" && exit 0 ; fi
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^LIBC/{
+ s: ::g
+ p
+ }'`"
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/third_party/sqlite/config.h.in b/third_party/sqlite/config.h.in
new file mode 100755
index 0000000..5a2a8d1
--- /dev/null
+++ b/third_party/sqlite/config.h.in
@@ -0,0 +1,95 @@
+/*
+** 2008 March 6
+**
+** 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.
+**
+*************************************************************************
+** Configuration header template to be filled in by 'configure' script
+**
+** @(#) $Id: config.h.in,v 1.3 2008/07/22 05:05:02 shane Exp $
+*/
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+
+/*****************************
+** Data types
+*****************************/
+
+/* Define as 1 if you have the int8_t type */
+#undef HAVE_INT8_T
+
+/* Define as 1 if you have the int16_t type */
+#undef HAVE_INT16_T
+
+/* Define as 1 if you have the int32_t type */
+#undef HAVE_INT32_T
+
+/* Define as 1 if you have the int64_t type */
+#undef HAVE_INT64_T
+
+/* Define as 1 if you have the uint8_t type */
+#undef HAVE_UINT8_T
+
+/* Define as 1 if you have the uint16_t type */
+#undef HAVE_UINT16_T
+
+/* Define as 1 if you have the uint32_t type */
+#undef HAVE_UINT32_T
+
+/* Define as 1 if you have the uint64_t type */
+#undef HAVE_UINT64_T
+
+
+/*****************************
+** Header Files
+*****************************/
+
+/* Define as 1 if you have the sys/types.h header */
+#undef HAVE_SYS_TYPES_H
+
+/* Define as 1 if you have the stdlib.h header */
+#undef HAVE_STDLIB_H
+
+/* Define as 1 if you have the stdint.h header */
+#undef HAVE_STDINT_H
+
+/* Define as 1 if you have the inttypes.h header */
+#undef HAVE_INTTYPES_H
+
+
+/*****************************
+** Functions
+*****************************/
+
+/* Define as 1 if you have the usleep() function */
+#undef HAVE_USLEEP
+
+/* Define as 1 if you have the fdatasync() function */
+#undef HAVE_FDATASYNC
+
+/* Define as 1 if you have the gmtime_r() function */
+#undef HAVE_GMTIME_R
+
+/* Define as 1 if you have the localtime_r() function */
+#undef HAVE_LOCALTIME_R
+
+/* Define as 1 if you have the localtime_s() function */
+#undef HAVE_LOCALTIME_S
+
+
+/*****************************
+** Large file support
+*****************************/
+
+#undef _FILE_OFFSET_BITS
+#undef _LARGE_FILES
+
+
+/* End of header */
+#endif
diff --git a/third_party/sqlite/config.sub b/third_party/sqlite/config.sub
new file mode 100755
index 0000000..63cdd0a
--- /dev/null
+++ b/third_party/sqlite/config.sub
@@ -0,0 +1,1644 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+# Inc.
+
+timestamp='2007-06-28'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx | dvp \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]a*eb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]a*eb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsEE* | ee | ps2)
+ basic_machine=mips64r5900el-scei
+ case $os in
+ -linux*)
+ ;;
+ *)
+ os=-elf
+ ;;
+ esac
+ ;;
+ iop)
+ basic_machine=mipsel-scei
+ os=-irx
+ ;;
+ dvp)
+ basic_machine=dvp-scei
+ os=-elf
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -irx*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/third_party/sqlite/configure b/third_party/sqlite/configure
new file mode 100755
index 0000000..90df20c
--- /dev/null
+++ b/third_party/sqlite/configure
@@ -0,0 +1,14347 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for sqlite 3.6.1.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='sqlite'
+PACKAGE_TARNAME='sqlite'
+PACKAGE_VERSION='3.6.1'
+PACKAGE_STRING='sqlite 3.6.1'
+PACKAGE_BUGREPORT=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS LIBTOOL build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SED EGREP FGREP GREP LD DUMPBIN ac_ct_DUMPBIN NM LN_S AR ac_ct_AR STRIP ac_ct_STRIP RANLIB ac_ct_RANLIB lt_ECHO DSYMUTIL ac_ct_DSYMUTIL NMEDIT ac_ct_NMEDIT LIPO ac_ct_LIPO OTOOL ac_ct_OTOOL OTOOL64 ac_ct_OTOOL64 CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA AWK TCLSH_CMD program_prefix VERSION RELEASE VERSION_NUMBER BUILD_CC SQLITE_THREADSAFE XTHREADCONNECT THREADSOVERRIDELOCKS ALLOWRELEASE TEMP_STORE BUILD_EXEEXT SQLITE_OS_UNIX SQLITE_OS_WIN SQLITE_OS_OS2 TARGET_EXEEXT TCL_VERSION TCL_BIN_DIR TCL_SRC_DIR TCL_LIBS TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIB_FLAG TCL_LIB_SPEC TCL_STUB_LIB_FILE TCL_STUB_LIB_FLAG TCL_STUB_LIB_SPEC HAVE_TCL TARGET_READLINE_LIBS TARGET_READLINE_INC TARGET_HAVE_READLINE TARGET_DEBUG USE_AMALGAMATION OPT_FEATURE_FLAGS USE_GCOV BUILD_CFLAGS LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures sqlite 3.6.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of sqlite 3.6.1:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-shared[=PKGS]
+ build shared libraries [default=yes]
+ --enable-static[=PKGS]
+ build static libraries [default=yes]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-largefile omit support for large files
+ --enable-threadsafe Support threadsafe operation
+ --enable-cross-thread-connections
+ Allow connection sharing across threads
+ --enable-threads-override-locks
+ Threads can override each others locks
+ --enable-releasemode Support libtool link to release mode
+ --enable-tempstore Use an in-ram database for temporary tables
+ (never,no,yes,always)
+ --disable-tcl do not build TCL extension
+ --disable-readline disable readline support [default=detect]
+ --enable-debug enable debugging & verbose explain
+ --disable-amalgamation Disable the amalgamation and instead build all files
+ separately
+ --enable-load-extension Enable loading of external extensions
+ --enable-gcov Enable coverage testing using gcov
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-hints=FILE Read configuration options from FILE
+ --with-tcl=DIR directory containing tcl configuration
+ (tclConfig.sh)
+ --with-readline-lib specify readline library
+ --with-readline-inc specify readline include paths
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+sqlite configure 3.6.1
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by sqlite $as_me 3.6.1, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# The following RCS revision string applies to configure.in
+# $Revision: 1.64 $
+
+#########
+# Programs needed
+#
+case `pwd` in
+ *\ * | *\ *)
+ { echo "$as_me:$LINENO: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.4'
+macro_revision='1.2976'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+ { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+ ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+ { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+ { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+ ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+ { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std1. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6
+if test "${lt_cv_path_SED+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f $lt_ac_sed && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test $lt_ac_count -gt 10 && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test $lt_ac_count -gt $lt_ac_max; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+
+fi
+
+SED=$lt_cv_path_SED
+
+echo "$as_me:$LINENO: result: $SED" >&5
+echo "${ECHO_T}$SED" >&6
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for fgrep" >&5
+echo $ECHO_N "checking for fgrep... $ECHO_C" >&6
+if test "${ac_cv_prog_fgrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo 'ab*c' | (grep -F 'ab*c') >/dev/null 2>&1
+ then ac_cv_prog_fgrep='grep -F'
+ else ac_cv_prog_fgrep='fgrep'
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_fgrep" >&5
+echo "${ECHO_T}$ac_cv_prog_fgrep" >&6
+ FGREP=$ac_cv_prog_fgrep
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld or --without-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+ withval="$with_gnu_ld"
+ test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi;
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6
+else
+ echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking for BSD- or MS-compatible name lister (nm)" >&5
+echo $ECHO_N "checking for BSD- or MS-compatible name lister (nm)... $ECHO_C" >&6
+if test "${lt_cv_path_NM+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5
+echo "${ECHO_T}$lt_cv_path_NM" >&6
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_DUMPBIN+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ echo "$as_me:$LINENO: result: $DUMPBIN" >&5
+echo "${ECHO_T}$DUMPBIN" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ echo "$as_me:$LINENO: result: $ac_ct_DUMPBIN" >&5
+echo "${ECHO_T}$ac_ct_DUMPBIN" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+test -n "$ac_ct_DUMPBIN" || ac_ct_DUMPBIN=":"
+
+ DUMPBIN=$ac_ct_DUMPBIN
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking the name lister ($NM) interface" >&5
+echo $ECHO_N "checking the name lister ($NM) interface... $ECHO_C" >&6
+if test "${lt_cv_nm_interface+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:2927: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:2930: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:2933: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $lt_cv_nm_interface" >&5
+echo "${ECHO_T}$lt_cv_nm_interface" >&6
+
+echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+ echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6
+fi
+
+# find the maximum length of command line arguments
+echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5
+echo $ECHO_N "checking the maximum length of command line arguments... $ECHO_C" >&6
+if test "${lt_cv_sys_max_cmd_len+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5
+echo "${ECHO_T}$lt_cv_sys_max_cmd_len" >&6
+else
+ echo "$as_me:$LINENO: result: none" >&5
+echo "${ECHO_T}none" >&6
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+echo "$as_me:$LINENO: checking whether the shell understands some XSI constructs" >&5
+echo $ECHO_N "checking whether the shell understands some XSI constructs... $ECHO_C" >&6
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+echo "$as_me:$LINENO: result: $xsi_shell" >&5
+echo "${ECHO_T}$xsi_shell" >&6
+
+
+echo "$as_me:$LINENO: checking whether the shell understands \"+=\"" >&5
+echo $ECHO_N "checking whether the shell understands \"+=\"... $ECHO_C" >&6
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+echo "$as_me:$LINENO: result: $lt_shell_append" >&5
+echo "${ECHO_T}$lt_shell_append" >&6
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5
+echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6
+if test "${lt_cv_ld_reload_flag+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5
+echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking how to recognize dependent libraries" >&5
+echo $ECHO_N "checking how to recognize dependent libraries... $ECHO_C" >&6
+if test "${lt_cv_deplibs_check_method+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5
+echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="ar"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_AR" && ac_cv_prog_ac_ct_AR="false"
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ echo "$as_me:$LINENO: result: $ac_ct_AR" >&5
+echo "${ECHO_T}$ac_ct_AR" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ AR=$ac_ct_AR
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ STRIP=$ac_ct_STRIP
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ RANLIB=$ac_ct_RANLIB
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5
+echo $ECHO_N "checking command to parse $NM output from $compiler object... $ECHO_C" >&6
+if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ echo "$as_me:$LINENO: result: failed" >&5
+echo "${ECHO_T}failed" >&6
+else
+ echo "$as_me:$LINENO: result: ok" >&5
+echo "${ECHO_T}ok" >&6
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-libtool-lock or --disable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+ enableval="$enable_libtool_lock"
+
+fi;
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line 3996 "configure"' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5
+echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6
+if test "${lt_cv_cc_needs_belf+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ lt_cv_cc_needs_belf=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+lt_cv_cc_needs_belf=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5
+echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_DSYMUTIL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ echo "$as_me:$LINENO: result: $DSYMUTIL" >&5
+echo "${ECHO_T}$DSYMUTIL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_DSYMUTIL" && ac_cv_prog_ac_ct_DSYMUTIL=":"
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ echo "$as_me:$LINENO: result: $ac_ct_DSYMUTIL" >&5
+echo "${ECHO_T}$ac_ct_DSYMUTIL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ DSYMUTIL=$ac_ct_DSYMUTIL
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_NMEDIT+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ echo "$as_me:$LINENO: result: $NMEDIT" >&5
+echo "${ECHO_T}$NMEDIT" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_NMEDIT" && ac_cv_prog_ac_ct_NMEDIT=":"
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ echo "$as_me:$LINENO: result: $ac_ct_NMEDIT" >&5
+echo "${ECHO_T}$ac_ct_NMEDIT" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ NMEDIT=$ac_ct_NMEDIT
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_LIPO+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ echo "$as_me:$LINENO: result: $LIPO" >&5
+echo "${ECHO_T}$LIPO" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_LIPO" && ac_cv_prog_ac_ct_LIPO=":"
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ echo "$as_me:$LINENO: result: $ac_ct_LIPO" >&5
+echo "${ECHO_T}$ac_ct_LIPO" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ LIPO=$ac_ct_LIPO
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_OTOOL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ echo "$as_me:$LINENO: result: $OTOOL" >&5
+echo "${ECHO_T}$OTOOL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_OTOOL" && ac_cv_prog_ac_ct_OTOOL=":"
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ echo "$as_me:$LINENO: result: $ac_ct_OTOOL" >&5
+echo "${ECHO_T}$ac_ct_OTOOL" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ OTOOL=$ac_ct_OTOOL
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_OTOOL64+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ echo "$as_me:$LINENO: result: $OTOOL64" >&5
+echo "${ECHO_T}$OTOOL64" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_OTOOL64" && ac_cv_prog_ac_ct_OTOOL64=":"
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ echo "$as_me:$LINENO: result: $ac_ct_OTOOL64" >&5
+echo "${ECHO_T}$ac_ct_OTOOL64" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ OTOOL64=$ac_ct_OTOOL64
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking for -single_module linker flag" >&5
+echo $ECHO_N "checking for -single_module linker flag... $ECHO_C" >&6
+if test "${lt_cv_apple_cc_single_mod+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+echo "$as_me:$LINENO: result: $lt_cv_apple_cc_single_mod" >&5
+echo "${ECHO_T}$lt_cv_apple_cc_single_mod" >&6
+ echo "$as_me:$LINENO: checking for -exported_symbols_list linker flag" >&5
+echo $ECHO_N "checking for -exported_symbols_list linker flag... $ECHO_C" >&6
+if test "${lt_cv_ld_exported_symbols_list+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ lt_cv_ld_exported_symbols_list=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+lt_cv_ld_exported_symbols_list=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_ld_exported_symbols_list" >&5
+echo "${ECHO_T}$lt_cv_ld_exported_symbols_list" >&6
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ exit(2);
+ exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ # Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+ enableval="$enable_shared"
+ p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi;
+
+
+
+
+
+
+
+
+ # Check whether --enable-static or --disable-static was given.
+if test "${enable_static+set}" = set; then
+ enableval="$enable_static"
+ p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=yes
+fi;
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic or --without-pic was given.
+if test "${with_pic+set}" = set; then
+ withval="$with_pic"
+ pic_mode="$withval"
+else
+ pic_mode=default
+fi;
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install or --disable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then
+ enableval="$enable_fast_install"
+ p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi;
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+echo "$as_me:$LINENO: checking for objdir" >&5
+echo $ECHO_N "checking for objdir... $ECHO_C" >&6
+if test "${lt_cv_objdir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5
+echo "${ECHO_T}$lt_cv_objdir" >&6
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5
+echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ echo "$as_me:$LINENO: checking for file" >&5
+echo $ECHO_N "checking for file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:5771: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:5775: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ icc* | ecc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic" >&6
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic works... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_pic_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:6095: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:6099: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_pic_works" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_pic_works" >&6
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_static_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_static_works" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_static_works" >&6
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:6200: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:6204: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:6255: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:6259: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+ if test "$hard_links" = no; then
+ { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ if test "$GCC" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat >conftest.$ac_ext <<_ACEOF
+int foo(void) {}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+echo "$as_me:$LINENO: result: $ld_shlibs" >&5
+echo "${ECHO_T}$ld_shlibs" >&6
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\"") >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5
+echo "${ECHO_T}$archive_cmds_need_lc" >&6
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ # Handle Gentoo/FreeBSD as it was Linux
+ case $host_vendor in
+ gentoo)
+ version_type=linux ;;
+ *)
+ version_type=freebsd-$objformat ;;
+ esac
+
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ linux)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ need_lib_prefix=no
+ need_version=no
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then
+ shlibpath_overrides_runpath=yes
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_name_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action" >&5
+echo "${ECHO_T}$hardcode_action" >&6
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ echo "$as_me:$LINENO: checking for shl_load" >&5
+echo $ECHO_N "checking for shl_load... $ECHO_C" >&6
+if test "${ac_cv_func_shl_load+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shl_load (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+char (*f) () = shl_load;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shl_load;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_shl_load=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+echo "${ECHO_T}$ac_cv_func_shl_load" >&6
+if test $ac_cv_func_shl_load = yes; then
+ lt_cv_dlopen="shl_load"
+else
+ echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shl_load ();
+int
+main ()
+{
+shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dld_shl_load=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6
+if test $ac_cv_lib_dld_shl_load = yes; then
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ echo "$as_me:$LINENO: checking for dlopen" >&5
+echo $ECHO_N "checking for dlopen... $ECHO_C" >&6
+if test "${ac_cv_func_dlopen+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char dlopen (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+char (*f) () = dlopen;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != dlopen;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_dlopen=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+echo "${ECHO_T}$ac_cv_func_dlopen" >&6
+if test $ac_cv_func_dlopen = yes; then
+ lt_cv_dlopen="dlopen"
+else
+ echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_svld_dlopen=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_svld_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6
+if test $ac_cv_lib_svld_dlopen = yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dld_link ();
+int
+main ()
+{
+dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dld_dld_link=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_dld_link=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6
+if test $ac_cv_lib_dld_dld_link = yes; then
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 9057 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ exit (status);
+}
+_LT_EOF
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self" >&6
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 9157 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ exit (status);
+}
+_LT_EOF
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ fi
+ ;;
+ *)
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6
+ echo "$as_me:$LINENO: result: $can_build_shared" >&5
+echo "${ECHO_T}$can_build_shared" >&6
+
+ echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ echo "$as_me:$LINENO: result: $enable_shared" >&5
+echo "${ECHO_T}$enable_shared" >&6
+
+ echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ echo "$as_me:$LINENO: result: $enable_static" >&5
+echo "${ECHO_T}$enable_static" >&6
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$AWK" && break
+done
+
+
+#########
+# Enable large file support (if special flags are necessary)
+#
+# Check whether --enable-largefile or --disable-largefile was given.
+if test "${enable_largefile+set}" = set; then
+ enableval="$enable_largefile"
+
+fi;
+if test "$enable_largefile" != no; then
+
+ echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
+echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_largefile_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_sys_largefile_CC=no
+ if test "$GCC" != yes; then
+ ac_save_CC=$CC
+ while :; do
+ # IRIX 6.2 and later do not support large files by default,
+ # so use the C compiler's -n32 option if that helps.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+ CC="$CC -n32"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_largefile_CC=' -n32'; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+ break
+ done
+ CC=$ac_save_CC
+ rm -f conftest.$ac_ext
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6
+ if test "$ac_cv_sys_largefile_CC" != no; then
+ CC=$CC$ac_cv_sys_largefile_CC
+ fi
+
+ echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_file_offset_bits+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ ac_cv_sys_file_offset_bits=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_file_offset_bits=64; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ break
+done
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
+echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6
+if test "$ac_cv_sys_file_offset_bits" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
+_ACEOF
+
+fi
+rm -f conftest*
+ echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
+echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_large_files+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ ac_cv_sys_large_files=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_large_files=1; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ break
+done
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
+echo "${ECHO_T}$ac_cv_sys_large_files" >&6
+if test "$ac_cv_sys_large_files" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define _LARGE_FILES $ac_cv_sys_large_files
+_ACEOF
+
+fi
+rm -f conftest*
+fi
+
+
+#########
+# Check for needed/wanted data types
+echo "$as_me:$LINENO: checking for int8_t" >&5
+echo $ECHO_N "checking for int8_t... $ECHO_C" >&6
+if test "${ac_cv_type_int8_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((int8_t *) 0)
+ return 0;
+if (sizeof (int8_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_int8_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_int8_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_int8_t" >&5
+echo "${ECHO_T}$ac_cv_type_int8_t" >&6
+if test $ac_cv_type_int8_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT8_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for int16_t" >&5
+echo $ECHO_N "checking for int16_t... $ECHO_C" >&6
+if test "${ac_cv_type_int16_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((int16_t *) 0)
+ return 0;
+if (sizeof (int16_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_int16_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_int16_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_int16_t" >&5
+echo "${ECHO_T}$ac_cv_type_int16_t" >&6
+if test $ac_cv_type_int16_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT16_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for int32_t" >&5
+echo $ECHO_N "checking for int32_t... $ECHO_C" >&6
+if test "${ac_cv_type_int32_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((int32_t *) 0)
+ return 0;
+if (sizeof (int32_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_int32_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_int32_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_int32_t" >&5
+echo "${ECHO_T}$ac_cv_type_int32_t" >&6
+if test $ac_cv_type_int32_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT32_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for int64_t" >&5
+echo $ECHO_N "checking for int64_t... $ECHO_C" >&6
+if test "${ac_cv_type_int64_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((int64_t *) 0)
+ return 0;
+if (sizeof (int64_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_int64_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_int64_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5
+echo "${ECHO_T}$ac_cv_type_int64_t" >&6
+if test $ac_cv_type_int64_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT64_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for intptr_t" >&5
+echo $ECHO_N "checking for intptr_t... $ECHO_C" >&6
+if test "${ac_cv_type_intptr_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((intptr_t *) 0)
+ return 0;
+if (sizeof (intptr_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_intptr_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_intptr_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_intptr_t" >&5
+echo "${ECHO_T}$ac_cv_type_intptr_t" >&6
+if test $ac_cv_type_intptr_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INTPTR_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uint8_t" >&5
+echo $ECHO_N "checking for uint8_t... $ECHO_C" >&6
+if test "${ac_cv_type_uint8_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((uint8_t *) 0)
+ return 0;
+if (sizeof (uint8_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_uint8_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uint8_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uint8_t" >&5
+echo "${ECHO_T}$ac_cv_type_uint8_t" >&6
+if test $ac_cv_type_uint8_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT8_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uint16_t" >&5
+echo $ECHO_N "checking for uint16_t... $ECHO_C" >&6
+if test "${ac_cv_type_uint16_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((uint16_t *) 0)
+ return 0;
+if (sizeof (uint16_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_uint16_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uint16_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uint16_t" >&5
+echo "${ECHO_T}$ac_cv_type_uint16_t" >&6
+if test $ac_cv_type_uint16_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT16_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uint32_t" >&5
+echo $ECHO_N "checking for uint32_t... $ECHO_C" >&6
+if test "${ac_cv_type_uint32_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((uint32_t *) 0)
+ return 0;
+if (sizeof (uint32_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_uint32_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uint32_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uint32_t" >&5
+echo "${ECHO_T}$ac_cv_type_uint32_t" >&6
+if test $ac_cv_type_uint32_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT32_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uint64_t" >&5
+echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6
+if test "${ac_cv_type_uint64_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((uint64_t *) 0)
+ return 0;
+if (sizeof (uint64_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_uint64_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uint64_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5
+echo "${ECHO_T}$ac_cv_type_uint64_t" >&6
+if test $ac_cv_type_uint64_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT64_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uintptr_t" >&5
+echo $ECHO_N "checking for uintptr_t... $ECHO_C" >&6
+if test "${ac_cv_type_uintptr_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if ((uintptr_t *) 0)
+ return 0;
+if (sizeof (uintptr_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_type_uintptr_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uintptr_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uintptr_t" >&5
+echo "${ECHO_T}$ac_cv_type_uintptr_t" >&6
+if test $ac_cv_type_uintptr_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINTPTR_T 1
+_ACEOF
+
+
+fi
+
+
+#########
+# Check for needed/wanted headers
+
+
+
+
+for ac_header in sys/types.h stdlib.h stdint.h inttypes.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to the sqlite lists. ##
+## --------------------------------- ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+#########
+# Figure out whether or not we have these functions
+#
+
+
+
+
+
+for ac_func in usleep fdatasync localtime_r gmtime_r localtime_s
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+#########
+# By default, we use the amalgamation (this may be changed below...)
+#
+USE_AMALGAMATION=1
+
+#########
+# See whether we can run specific tclsh versions known to work well;
+# if not, then we fall back to plain tclsh.
+# TODO: try other versions before falling back?
+#
+for ac_prog in tclsh8.4 tclsh
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_TCLSH_CMD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$TCLSH_CMD"; then
+ ac_cv_prog_TCLSH_CMD="$TCLSH_CMD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_TCLSH_CMD="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+TCLSH_CMD=$ac_cv_prog_TCLSH_CMD
+if test -n "$TCLSH_CMD"; then
+ echo "$as_me:$LINENO: result: $TCLSH_CMD" >&5
+echo "${ECHO_T}$TCLSH_CMD" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$TCLSH_CMD" && break
+done
+test -n "$TCLSH_CMD" || TCLSH_CMD="none"
+
+if test "$TCLSH_CMD" = "none"; then
+ # If we can't find a local tclsh, then building the amalgamation will fail.
+ # We act as though --disable-amalgamation has been used.
+ echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
+ USE_AMALGAMATION=0
+ TCLSH_CMD="tclsh"
+fi
+
+
+
+#########
+# Set up an appropriate program prefix
+#
+if test "$program_prefix" = "NONE"; then
+ program_prefix=""
+fi
+
+
+VERSION=`cat $srcdir/VERSION | sed 's/^\([0-9]*\.*[0-9]*\).*/\1/'`
+echo "Version set to $VERSION"
+
+RELEASE=`cat $srcdir/VERSION`
+echo "Release set to $RELEASE"
+
+VERSION_NUMBER=`cat $srcdir/VERSION \
+ | sed 's/[^0-9]/ /g' \
+ | awk '{printf "%d%03d%03d",$1,$2,$3}'`
+echo "Version number set to $VERSION_NUMBER"
+
+
+#########
+# Check to see if the --with-hints=FILE option is used. If there is none,
+# then check for a files named "$host.hints" and ../$hosts.hints where
+# $host is the hostname of the build system. If still no hints are
+# found, try looking in $system.hints and ../$system.hints where
+# $system is the result of uname -s.
+#
+
+# Check whether --with-hints or --without-hints was given.
+if test "${with_hints+set}" = set; then
+ withval="$with_hints"
+ hints=$withval
+fi;
+if test "$hints" = ""; then
+ host=`hostname | sed 's/\..*//'`
+ if test -r $host.hints; then
+ hints=$host.hints
+ else
+ if test -r ../$host.hints; then
+ hints=../$host.hints
+ fi
+ fi
+fi
+if test "$hints" = ""; then
+ sys=`uname -s`
+ if test -r $sys.hints; then
+ hints=$sys.hints
+ else
+ if test -r ../$sys.hints; then
+ hints=../$sys.hints
+ fi
+ fi
+fi
+if test "$hints" != ""; then
+ echo "$as_me:$LINENO: result: reading hints from $hints" >&5
+echo "${ECHO_T}reading hints from $hints" >&6
+ . $hints
+fi
+
+#########
+# Locate a compiler for the build machine. This compiler should
+# generate command-line programs that run on the build machine.
+#
+if test x"$cross_compiling" = xno; then
+ BUILD_CC=$CC
+ BUILD_CFLAGS=$CFLAGS
+else
+ if test "${BUILD_CC+set}" != set; then
+ for ac_prog in gcc cc cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_BUILD_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$BUILD_CC"; then
+ ac_cv_prog_BUILD_CC="$BUILD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_BUILD_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+BUILD_CC=$ac_cv_prog_BUILD_CC
+if test -n "$BUILD_CC"; then
+ echo "$as_me:$LINENO: result: $BUILD_CC" >&5
+echo "${ECHO_T}$BUILD_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$BUILD_CC" && break
+done
+
+ fi
+ if test "${BUILD_CFLAGS+set}" != set; then
+ BUILD_CFLAGS="-g"
+ fi
+fi
+
+
+##########
+# Do we want to support multithreaded use of sqlite
+#
+# Check whether --enable-threadsafe or --disable-threadsafe was given.
+if test "${enable_threadsafe+set}" = set; then
+ enableval="$enable_threadsafe"
+
+else
+ enable_threadsafe=yes
+fi;
+echo "$as_me:$LINENO: checking whether to support threadsafe operation" >&5
+echo $ECHO_N "checking whether to support threadsafe operation... $ECHO_C" >&6
+if test "$enable_threadsafe" = "no"; then
+ SQLITE_THREADSAFE=0
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+else
+ SQLITE_THREADSAFE=1
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+fi
+
+
+if test "$SQLITE_THREADSAFE" = "1"; then
+ echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
+echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
+if test "${ac_cv_search_pthread_create+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_pthread_create=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char pthread_create ();
+int
+main ()
+{
+pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_pthread_create="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_pthread_create" = no; then
+ for ac_lib in pthread; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char pthread_create ();
+int
+main ()
+{
+pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_pthread_create="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
+echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
+if test "$ac_cv_search_pthread_create" != no; then
+ test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
+
+fi
+
+fi
+
+##########
+# Do we want to allow a connection created in one thread to be used
+# in another thread. This does not work on many Linux systems (ex: RedHat 9)
+# due to bugs in the threading implementations. This is thus off by default.
+#
+# Check whether --enable-cross-thread-connections or --disable-cross-thread-connections was given.
+if test "${enable_cross_thread_connections+set}" = set; then
+ enableval="$enable_cross_thread_connections"
+
+else
+ enable_xthreadconnect=no
+fi;
+echo "$as_me:$LINENO: checking whether to allow connections to be shared across threads" >&5
+echo $ECHO_N "checking whether to allow connections to be shared across threads... $ECHO_C" >&6
+if test "$enable_xthreadconnect" = "no"; then
+ XTHREADCONNECT=''
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+else
+ XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1'
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+fi
+
+
+##########
+# Do we want to set threadsOverrideEachOthersLocks variable to be 1 (true) by
+# default. Normally, a test at runtime is performed to determine the
+# appropriate value of this variable. Use this option only if you're sure that
+# threads can safely override each others locks in all runtime situations.
+#
+# Check whether --enable-threads-override-locks or --disable-threads-override-locks was given.
+if test "${enable_threads_override_locks+set}" = set; then
+ enableval="$enable_threads_override_locks"
+
+else
+ enable_threads_override_locks=no
+fi;
+echo "$as_me:$LINENO: checking whether threads can override each others locks" >&5
+echo $ECHO_N "checking whether threads can override each others locks... $ECHO_C" >&6
+if test "$enable_threads_override_locks" = "no"; then
+ THREADSOVERRIDELOCKS='-1'
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+else
+ THREADSOVERRIDELOCKS='1'
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+fi
+
+
+##########
+# Do we want to support release
+#
+# Check whether --enable-releasemode or --disable-releasemode was given.
+if test "${enable_releasemode+set}" = set; then
+ enableval="$enable_releasemode"
+
+else
+ enable_releasemode=no
+fi;
+echo "$as_me:$LINENO: checking whether to support shared library linked as release mode or not" >&5
+echo $ECHO_N "checking whether to support shared library linked as release mode or not... $ECHO_C" >&6
+if test "$enable_releasemode" = "no"; then
+ ALLOWRELEASE=""
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+else
+ ALLOWRELEASE="-release `cat $srcdir/VERSION`"
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+fi
+
+
+##########
+# Do we want temporary databases in memory
+#
+# Check whether --enable-tempstore or --disable-tempstore was given.
+if test "${enable_tempstore+set}" = set; then
+ enableval="$enable_tempstore"
+
+else
+ enable_tempstore=no
+fi;
+echo "$as_me:$LINENO: checking whether to use an in-ram database for temporary tables" >&5
+echo $ECHO_N "checking whether to use an in-ram database for temporary tables... $ECHO_C" >&6
+case "$enable_tempstore" in
+ never )
+ TEMP_STORE=0
+ echo "$as_me:$LINENO: result: never" >&5
+echo "${ECHO_T}never" >&6
+ ;;
+ no )
+ TEMP_STORE=1
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ ;;
+ always )
+ TEMP_STORE=3
+ echo "$as_me:$LINENO: result: always" >&5
+echo "${ECHO_T}always" >&6
+ ;;
+ yes )
+ TEMP_STORE=3
+ echo "$as_me:$LINENO: result: always" >&5
+echo "${ECHO_T}always" >&6
+ ;;
+ * )
+ TEMP_STORE=1
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ ;;
+esac
+
+
+
+###########
+# Lots of things are different if we are compiling for Windows using
+# the CYGWIN environment. So check for that special case and handle
+# things accordingly.
+#
+echo "$as_me:$LINENO: checking if executables have the .exe suffix" >&5
+echo $ECHO_N "checking if executables have the .exe suffix... $ECHO_C" >&6
+if test "$config_BUILD_EXEEXT" = ".exe"; then
+ CYGWIN=yes
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+ echo "$as_me:$LINENO: result: unknown" >&5
+echo "${ECHO_T}unknown" >&6
+fi
+if test "$CYGWIN" != "yes"; then
+
+case $host_os in
+ *cygwin* ) CYGWIN=yes;;
+ * ) CYGWIN=no;;
+esac
+
+fi
+if test "$CYGWIN" = "yes"; then
+ BUILD_EXEEXT=.exe
+else
+ BUILD_EXEEXT=$EXEEXT
+fi
+if test x"$cross_compiling" = xno; then
+ TARGET_EXEEXT=$BUILD_EXEEXT
+else
+ TARGET_EXEEXT=$config_TARGET_EXEEXT
+fi
+if test "$TARGET_EXEEXT" = ".exe"; then
+ if test $OS2_SHELL ; then
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=0
+ SQLITE_OS_OS2=1
+ CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1"
+ if test "$ac_compiler_gnu" = "yes" ; then
+ CFLAGS="$CFLAGS -Zomf -Zexe -Zmap"
+ BUILD_CFLAGS="$BUILD_CFLAGS -Zomf -Zexe"
+ fi
+ else
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=1
+ SQLITE_OS_OS2=0
+ CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
+ fi
+else
+ SQLITE_OS_UNIX=1
+ SQLITE_OS_WIN=0
+ SQLITE_OS_OS2=0
+ CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1"
+fi
+
+
+
+
+
+
+
+##########
+# Figure out all the parameters needed to compile against Tcl.
+#
+# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
+# macros in the in the tcl.m4 file of the standard TCL distribution.
+# Those macros could not be used directly since we have to make some
+# minor changes to accomodate systems that do not have TCL installed.
+#
+# Check whether --enable-tcl or --disable-tcl was given.
+if test "${enable_tcl+set}" = set; then
+ enableval="$enable_tcl"
+ use_tcl=$enableval
+else
+ use_tcl=yes
+fi;
+if test "${use_tcl}" = "yes" ; then
+
+# Check whether --with-tcl or --without-tcl was given.
+if test "${with_tcl+set}" = set; then
+ withval="$with_tcl"
+ with_tclconfig=${withval}
+fi;
+ echo "$as_me:$LINENO: checking for Tcl configuration" >&5
+echo $ECHO_N "checking for Tcl configuration... $ECHO_C" >&6
+ if test "${ac_cv_c_tclconfig+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+ # First check to see if --with-tcl was specified.
+ if test x"${with_tclconfig}" != x ; then
+ if test -f "${with_tclconfig}/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
+ else
+ { { echo "$as_me:$LINENO: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&5
+echo "$as_me: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+ # then check for a private Tcl installation
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ../tcl \
+ `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+ `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \
+ `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+ `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \
+ `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+ `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \
+ `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null`
+ do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ `ls -d ${libdir} 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` \
+ `ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/lib 2>/dev/null`
+ do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tcl \
+ `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null`
+ do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+fi
+
+
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ use_tcl=no
+ { echo "$as_me:$LINENO: WARNING: Can't find Tcl configuration definitions" >&5
+echo "$as_me: WARNING: Can't find Tcl configuration definitions" >&2;}
+ { echo "$as_me:$LINENO: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&5
+echo "$as_me: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&2;}
+ { echo "$as_me:$LINENO: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&5
+echo "$as_me: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&2;}
+ else
+ TCL_BIN_DIR=${ac_cv_c_tclconfig}
+ echo "$as_me:$LINENO: result: found $TCL_BIN_DIR/tclConfig.sh" >&5
+echo "${ECHO_T}found $TCL_BIN_DIR/tclConfig.sh" >&6
+
+ echo "$as_me:$LINENO: checking for existence of $TCL_BIN_DIR/tclConfig.sh" >&5
+echo $ECHO_N "checking for existence of $TCL_BIN_DIR/tclConfig.sh... $ECHO_C" >&6
+ if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
+ echo "$as_me:$LINENO: result: loading" >&5
+echo "${ECHO_T}loading" >&6
+ . $TCL_BIN_DIR/tclConfig.sh
+ else
+ echo "$as_me:$LINENO: result: file not found" >&5
+echo "${ECHO_T}file not found" >&6
+ fi
+
+ #
+ # If the TCL_BIN_DIR is the build directory (not the install directory),
+ # then set the common variable name to the value of the build variables.
+ # For example, the variable TCL_LIB_SPEC will be set to the value
+ # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
+ # instead of TCL_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ #
+
+ if test -f $TCL_BIN_DIR/Makefile ; then
+ TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
+ TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
+ TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
+ fi
+
+ #
+ # eval is required to do the TCL_DBGX substitution
+ #
+
+ eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+ eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+ eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+
+ eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+ eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
+ eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fi
+fi
+if test "${use_tcl}" = "no" ; then
+ HAVE_TCL=""
+else
+ HAVE_TCL=1
+fi
+
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "readline()" library.
+#
+TARGET_READLINE_LIBS=""
+TARGET_READLINE_INC=""
+TARGET_HAVE_READLINE=0
+# Check whether --enable-readline or --disable-readline was given.
+if test "${enable_readline+set}" = set; then
+ enableval="$enable_readline"
+ with_readline=$enableval
+else
+ with_readline=auto
+fi;
+
+if test x"$with_readline" != xno; then
+ found="yes"
+
+
+# Check whether --with-readline-lib or --without-readline-lib was given.
+if test "${with_readline_lib+set}" = set; then
+ withval="$with_readline_lib"
+ with_readline_lib=$withval
+else
+ with_readline_lib="auto"
+fi;
+ if test "x$with_readline_lib" = xauto; then
+ save_LIBS="$LIBS"
+ LIBS=""
+ echo "$as_me:$LINENO: checking for library containing tgetent" >&5
+echo $ECHO_N "checking for library containing tgetent... $ECHO_C" >&6
+if test "${ac_cv_search_tgetent+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_tgetent=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent ();
+int
+main ()
+{
+tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_tgetent="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_tgetent" = no; then
+ for ac_lib in readline ncurses curses termcap; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent ();
+int
+main ()
+{
+tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_tgetent="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_tgetent" >&5
+echo "${ECHO_T}$ac_cv_search_tgetent" >&6
+if test "$ac_cv_search_tgetent" != no; then
+ test "$ac_cv_search_tgetent" = "none required" || LIBS="$ac_cv_search_tgetent $LIBS"
+ term_LIBS="$LIBS"
+else
+ term_LIBS=""
+fi
+
+ echo "$as_me:$LINENO: checking for readline in -lreadline" >&5
+echo $ECHO_N "checking for readline in -lreadline... $ECHO_C" >&6
+if test "${ac_cv_lib_readline_readline+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char readline ();
+int
+main ()
+{
+readline ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_readline_readline=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_readline_readline=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_readline_readline" >&5
+echo "${ECHO_T}$ac_cv_lib_readline_readline" >&6
+if test $ac_cv_lib_readline_readline = yes; then
+ TARGET_READLINE_LIBS="-lreadline"
+else
+ found="no"
+fi
+
+ TARGET_READLINE_LIBS="$TARGET_READLINE_LIBS $term_LIBS"
+ LIBS="$save_LIBS"
+ else
+ TARGET_READLINE_LIBS="$with_readline_lib"
+ fi
+
+
+# Check whether --with-readline-inc or --without-readline-inc was given.
+if test "${with_readline_inc+set}" = set; then
+ withval="$with_readline_inc"
+ with_readline_inc=$withval
+else
+ with_readline_inc="auto"
+fi;
+ if test "x$with_readline_inc" = xauto; then
+ if test "${ac_cv_header_readline_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for readline.h" >&5
+echo $ECHO_N "checking for readline.h... $ECHO_C" >&6
+if test "${ac_cv_header_readline_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_readline_h" >&5
+echo "${ECHO_T}$ac_cv_header_readline_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking readline.h usability" >&5
+echo $ECHO_N "checking readline.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <readline.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking readline.h presence" >&5
+echo $ECHO_N "checking readline.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <readline.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: readline.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: readline.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: readline.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: readline.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: readline.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: readline.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: readline.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: readline.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: readline.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: readline.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: readline.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to the sqlite lists. ##
+## --------------------------------- ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for readline.h" >&5
+echo $ECHO_N "checking for readline.h... $ECHO_C" >&6
+if test "${ac_cv_header_readline_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_readline_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_readline_h" >&5
+echo "${ECHO_T}$ac_cv_header_readline_h" >&6
+
+fi
+if test $ac_cv_header_readline_h = yes; then
+ found="yes"
+else
+
+ found="no"
+ if test "$cross_compiling" != yes; then
+ for dir in /usr /usr/local /usr/local/readline /usr/contrib /mingw; do
+ for subdir in include include/readline; do
+ as_ac_File=`echo "ac_cv_file_$dir/$subdir/readline.h" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $dir/$subdir/readline.h" >&5
+echo $ECHO_N "checking for $dir/$subdir/readline.h... $ECHO_C" >&6
+if eval "test \"\${$as_ac_File+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ test "$cross_compiling" = yes &&
+ { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
+echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
+ { (exit 1); exit 1; }; }
+if test -r "$dir/$subdir/readline.h"; then
+ eval "$as_ac_File=yes"
+else
+ eval "$as_ac_File=no"
+fi
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_File'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_File'}'`" >&6
+if test `eval echo '${'$as_ac_File'}'` = yes; then
+ found=yes
+fi
+
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/$subdir"
+ break
+ fi
+ done
+ test "$found" = "yes" && break
+ done
+ fi
+
+fi
+
+
+ else
+ TARGET_READLINE_INC="$with_readline_inc"
+ fi
+
+ if test x"$found" = xno; then
+ TARGET_READLINE_LIBS=""
+ TARGET_READLINE_INC=""
+ TARGET_HAVE_READLINE=0
+ else
+ TARGET_HAVE_READLINE=1
+ fi
+fi
+
+
+
+
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "fdatasync()" function.
+#
+echo "$as_me:$LINENO: checking for library containing fdatasync" >&5
+echo $ECHO_N "checking for library containing fdatasync... $ECHO_C" >&6
+if test "${ac_cv_search_fdatasync+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_fdatasync=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char fdatasync ();
+int
+main ()
+{
+fdatasync ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_fdatasync="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_fdatasync" = no; then
+ for ac_lib in rt; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char fdatasync ();
+int
+main ()
+{
+fdatasync ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_fdatasync="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_fdatasync" >&5
+echo "${ECHO_T}$ac_cv_search_fdatasync" >&6
+if test "$ac_cv_search_fdatasync" != no; then
+ test "$ac_cv_search_fdatasync" = "none required" || LIBS="$ac_cv_search_fdatasync $LIBS"
+
+fi
+
+
+#########
+# check for debug enabled
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+ use_debug=$enableval
+else
+ use_debug=no
+fi;
+if test "${use_debug}" = "yes" ; then
+ TARGET_DEBUG="-DSQLITE_DEBUG=1"
+else
+ TARGET_DEBUG="-DNDEBUG"
+fi
+
+
+#########
+# See whether we should use the amalgamation to build
+# Check whether --enable-amalgamation or --disable-amalgamation was given.
+if test "${enable_amalgamation+set}" = set; then
+ enableval="$enable_amalgamation"
+ use_amalgamation=$enableval
+else
+ use_amalgamation=yes
+fi;
+if test "${use_amalgamation}" != "yes" ; then
+ USE_AMALGAMATION=0
+fi
+
+
+#########
+# See whether we should allow loadable extensions
+# Check whether --enable-load-extension or --disable-load-extension was given.
+if test "${enable_load_extension+set}" = set; then
+ enableval="$enable_load_extension"
+ use_loadextension=$enableval
+else
+ use_loadextension=no
+fi;
+if test "${use_loadextension}" = "yes" ; then
+ OPT_FEATURE_FLAGS=""
+else
+ OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
+fi
+
+#########
+# attempt to duplicate any OMITS into the $(OPT_FEATURE_FLAGS) parameter
+for option in $CFLAGS $CPPFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";;
+ esac
+done
+
+
+
+# attempt to remove any OMITS from the $(CFLAGS) parameter
+ac_temp_CFLAGS=""
+for option in $CFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_CFLAGS="$ac_temp_CFLAGS $option";;
+ esac
+done
+CFLAGS=$ac_temp_CFLAGS
+
+
+# attempt to remove any OMITS from the $(CPPFLAGS) parameter
+ac_temp_CPPFLAGS=""
+for option in $CPPFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_CPPFLAGS="$ac_temp_CPPFLAGS $option";;
+ esac
+done
+CPPFLAGS=$ac_temp_CPPFLAGS
+
+
+# attempt to remove any OMITS from the $(BUILD_CFLAGS) parameter
+ac_temp_BUILD_CFLAGS=""
+for option in $BUILD_CFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_BUILD_CFLAGS="$ac_temp_BUILD_CFLAGS $option";;
+ esac
+done
+BUILD_CFLAGS=$ac_temp_BUILD_CFLAGS
+
+
+#########
+# See whether we should use GCOV
+# Check whether --enable-gcov or --disable-gcov was given.
+if test "${enable_gcov+set}" = set; then
+ enableval="$enable_gcov"
+ use_gcov=$enableval
+else
+ use_gcov=no
+fi;
+if test "${use_gcov}" = "yes" ; then
+ USE_GCOV=1
+else
+ USE_GCOV=0
+fi
+
+
+
+#########
+# Output the config header
+ ac_config_headers="$ac_config_headers config.h"
+
+
+#########
+# Generate the output files.
+#
+
+ ac_config_files="$ac_config_files Makefile sqlite3.pc"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by sqlite $as_me 3.6.1, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+sqlite config.status 3.6.1
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+#
+# INIT-COMMANDS section.
+#
+
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+
+_ACEOF
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "sqlite3.pc" ) CONFIG_FILES="$CONFIG_FILES sqlite3.pc" ;;
+ "libtool" ) CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@LIBTOOL@,$LIBTOOL,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@SED@,$SED,;t t
+s,@EGREP@,$EGREP,;t t
+s,@FGREP@,$FGREP,;t t
+s,@GREP@,$GREP,;t t
+s,@LD@,$LD,;t t
+s,@DUMPBIN@,$DUMPBIN,;t t
+s,@ac_ct_DUMPBIN@,$ac_ct_DUMPBIN,;t t
+s,@NM@,$NM,;t t
+s,@LN_S@,$LN_S,;t t
+s,@AR@,$AR,;t t
+s,@ac_ct_AR@,$ac_ct_AR,;t t
+s,@STRIP@,$STRIP,;t t
+s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@lt_ECHO@,$lt_ECHO,;t t
+s,@DSYMUTIL@,$DSYMUTIL,;t t
+s,@ac_ct_DSYMUTIL@,$ac_ct_DSYMUTIL,;t t
+s,@NMEDIT@,$NMEDIT,;t t
+s,@ac_ct_NMEDIT@,$ac_ct_NMEDIT,;t t
+s,@LIPO@,$LIPO,;t t
+s,@ac_ct_LIPO@,$ac_ct_LIPO,;t t
+s,@OTOOL@,$OTOOL,;t t
+s,@ac_ct_OTOOL@,$ac_ct_OTOOL,;t t
+s,@OTOOL64@,$OTOOL64,;t t
+s,@ac_ct_OTOOL64@,$ac_ct_OTOOL64,;t t
+s,@CPP@,$CPP,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@AWK@,$AWK,;t t
+s,@TCLSH_CMD@,$TCLSH_CMD,;t t
+s,@program_prefix@,$program_prefix,;t t
+s,@VERSION@,$VERSION,;t t
+s,@RELEASE@,$RELEASE,;t t
+s,@VERSION_NUMBER@,$VERSION_NUMBER,;t t
+s,@BUILD_CC@,$BUILD_CC,;t t
+s,@SQLITE_THREADSAFE@,$SQLITE_THREADSAFE,;t t
+s,@XTHREADCONNECT@,$XTHREADCONNECT,;t t
+s,@THREADSOVERRIDELOCKS@,$THREADSOVERRIDELOCKS,;t t
+s,@ALLOWRELEASE@,$ALLOWRELEASE,;t t
+s,@TEMP_STORE@,$TEMP_STORE,;t t
+s,@BUILD_EXEEXT@,$BUILD_EXEEXT,;t t
+s,@SQLITE_OS_UNIX@,$SQLITE_OS_UNIX,;t t
+s,@SQLITE_OS_WIN@,$SQLITE_OS_WIN,;t t
+s,@SQLITE_OS_OS2@,$SQLITE_OS_OS2,;t t
+s,@TARGET_EXEEXT@,$TARGET_EXEEXT,;t t
+s,@TCL_VERSION@,$TCL_VERSION,;t t
+s,@TCL_BIN_DIR@,$TCL_BIN_DIR,;t t
+s,@TCL_SRC_DIR@,$TCL_SRC_DIR,;t t
+s,@TCL_LIBS@,$TCL_LIBS,;t t
+s,@TCL_INCLUDE_SPEC@,$TCL_INCLUDE_SPEC,;t t
+s,@TCL_LIB_FILE@,$TCL_LIB_FILE,;t t
+s,@TCL_LIB_FLAG@,$TCL_LIB_FLAG,;t t
+s,@TCL_LIB_SPEC@,$TCL_LIB_SPEC,;t t
+s,@TCL_STUB_LIB_FILE@,$TCL_STUB_LIB_FILE,;t t
+s,@TCL_STUB_LIB_FLAG@,$TCL_STUB_LIB_FLAG,;t t
+s,@TCL_STUB_LIB_SPEC@,$TCL_STUB_LIB_SPEC,;t t
+s,@HAVE_TCL@,$HAVE_TCL,;t t
+s,@TARGET_READLINE_LIBS@,$TARGET_READLINE_LIBS,;t t
+s,@TARGET_READLINE_INC@,$TARGET_READLINE_INC,;t t
+s,@TARGET_HAVE_READLINE@,$TARGET_HAVE_READLINE,;t t
+s,@TARGET_DEBUG@,$TARGET_DEBUG,;t t
+s,@USE_AMALGAMATION@,$USE_AMALGAMATION,;t t
+s,@OPT_FEATURE_FLAGS@,$OPT_FEATURE_FLAGS,;t t
+s,@USE_GCOV@,$USE_GCOV,;t t
+s,@BUILD_CFLAGS@,$BUILD_CFLAGS,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='[ ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ # Do quote $f, to prevent DOS paths from being IFS'd.
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+ # Remove the trailing spaces.
+ sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h. The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status. Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless. Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo ' :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+ # Write a limited-size here document to $tmp/defines.sed.
+ echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#define' lines.
+ echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/defines.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo ' fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+ # Write a limited-size here document to $tmp/undefs.sed.
+ echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#undef'
+ echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+ rm -f conftest.undefs
+ mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ echo "/* Generated by configure. */" >$tmp/config.h
+ else
+ echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
+ fi
+ cat $tmp/in >>$tmp/config.h
+ rm -f $tmp/in
+ if test x"$ac_file" != x-; then
+ if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ rm -f $ac_file
+ mv $tmp/config.h $ac_file
+ fi
+ else
+ cat $tmp/config.h
+ rm -f $tmp/config.h
+ fi
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_COMMANDS section.
+#
+for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
+ ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
+ ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
+$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_dest" : 'X\(//\)[^/]' \| \
+ X"$ac_dest" : 'X\(//\)$' \| \
+ X"$ac_dest" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_dest" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
+echo "$as_me: executing $ac_dest commands" >&6;}
+ case $ac_dest in
+ libtool )
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/third_party/sqlite/configure.ac b/third_party/sqlite/configure.ac
new file mode 100755
index 0000000..3af7567
--- /dev/null
+++ b/third_party/sqlite/configure.ac
@@ -0,0 +1,688 @@
+#
+# The build process allows for using a cross-compiler. But the default
+# action is to target the same platform that we are running on. The
+# configure script needs to discover the following properties of the
+# build and target systems:
+#
+# srcdir
+#
+# The is the name of the directory that contains the
+# "configure" shell script. All source files are
+# located relative to this directory.
+#
+# bindir
+#
+# The name of the directory where executables should be
+# written by the "install" target of the makefile.
+#
+# program_prefix
+#
+# Add this prefix to the names of all executables that run
+# on the target machine. Default: ""
+#
+# ENABLE_SHARED
+#
+# True if shared libraries should be generated.
+#
+# BUILD_CC
+#
+# The name of a command that is used to convert C
+# source files into executables that run on the build
+# platform.
+#
+# BUILD_CFLAGS
+#
+# Switches that the build compiler needs in order to construct
+# command-line programs.
+#
+# BUILD_LIBS
+#
+# Libraries that the build compiler needs in order to construct
+# command-line programs.
+#
+# BUILD_EXEEXT
+#
+# The filename extension for executables on the build
+# platform. "" for Unix and ".exe" for Windows.
+#
+# TCL_*
+#
+# Lots of values are read in from the tclConfig.sh script,
+# if that script is available. This values are used for
+# constructing and installing the TCL extension.
+#
+# TARGET_READLINE_LIBS
+#
+# This is the library directives passed to the target linker
+# that cause the executable to link against the readline library.
+# This might be a switch like "-lreadline" or pathnames of library
+# file like "../../src/libreadline.a".
+#
+# TARGET_READLINE_INC
+#
+# This variables define the directory that contain header
+# files for the readline library. If the compiler is able
+# to find <readline.h> on its own, then this can be blank.
+#
+# TARGET_EXEEXT
+#
+# The filename extension for executables on the
+# target platform. "" for Unix and ".exe" for windows.
+#
+# The generated configure script will make an attempt to guess
+# at all of the above parameters. You can override any of
+# the guesses by setting the environment variable named
+# "config_AAAA" where "AAAA" is the name of the parameter
+# described above. (Exception: srcdir cannot be set this way.)
+# If you have a file that sets one or more of these environment
+# variables, you can invoke configure as follows:
+#
+# configure --with-hints=FILE
+#
+# where FILE is the name of the file that sets the environment
+# variables. FILE should be an absolute pathname.
+#
+# This configure.in file is easy to reuse on other projects. Just
+# change the argument to AC_INIT(). And disable any features that
+# you don't need (for example BLT) by erasing or commenting out
+# the corresponding code.
+#
+AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n']))
+
+dnl Put the RCS revision string after AC_INIT so that it will also
+dnl show in in configure.
+# The following RCS revision string applies to configure.in
+# $Revision: 1.48 $
+
+#########
+# Programs needed
+#
+AC_PROG_LIBTOOL
+AC_PROG_INSTALL
+AC_PROG_AWK
+
+#########
+# Enable large file support (if special flags are necessary)
+#
+AC_SYS_LARGEFILE
+
+#########
+# Check for needed/wanted data types
+AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, intptr_t, uint8_t,
+ uint16_t, uint32_t, uint64_t, uintptr_t])
+
+#########
+# Check for needed/wanted headers
+AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h])
+
+#########
+# Figure out whether or not we have these functions
+#
+AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s])
+
+#########
+# By default, we use the amalgamation (this may be changed below...)
+#
+USE_AMALGAMATION=1
+
+#########
+# See whether we can run specific tclsh versions known to work well;
+# if not, then we fall back to plain tclsh.
+# TODO: try other versions before falling back?
+#
+AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.4 tclsh], none)
+if test "$TCLSH_CMD" = "none"; then
+ # If we can't find a local tclsh, then building the amalgamation will fail.
+ # We act as though --disable-amalgamation has been used.
+ echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
+ USE_AMALGAMATION=0
+ TCLSH_CMD="tclsh"
+fi
+AC_SUBST(TCLSH_CMD)
+
+
+#########
+# Set up an appropriate program prefix
+#
+if test "$program_prefix" = "NONE"; then
+ program_prefix=""
+fi
+AC_SUBST(program_prefix)
+
+VERSION=[`cat $srcdir/VERSION | sed 's/^\([0-9]*\.*[0-9]*\).*/\1/'`]
+echo "Version set to $VERSION"
+AC_SUBST(VERSION)
+RELEASE=`cat $srcdir/VERSION`
+echo "Release set to $RELEASE"
+AC_SUBST(RELEASE)
+VERSION_NUMBER=[`cat $srcdir/VERSION \
+ | sed 's/[^0-9]/ /g' \
+ | awk '{printf "%d%03d%03d",$1,$2,$3}'`]
+echo "Version number set to $VERSION_NUMBER"
+AC_SUBST(VERSION_NUMBER)
+
+#########
+# Check to see if the --with-hints=FILE option is used. If there is none,
+# then check for a files named "$host.hints" and ../$hosts.hints where
+# $host is the hostname of the build system. If still no hints are
+# found, try looking in $system.hints and ../$system.hints where
+# $system is the result of uname -s.
+#
+AC_ARG_WITH(hints,
+ AC_HELP_STRING([--with-hints=FILE],[Read configuration options from FILE]),
+ hints=$withval)
+if test "$hints" = ""; then
+ host=`hostname | sed 's/\..*//'`
+ if test -r $host.hints; then
+ hints=$host.hints
+ else
+ if test -r ../$host.hints; then
+ hints=../$host.hints
+ fi
+ fi
+fi
+if test "$hints" = ""; then
+ sys=`uname -s`
+ if test -r $sys.hints; then
+ hints=$sys.hints
+ else
+ if test -r ../$sys.hints; then
+ hints=../$sys.hints
+ fi
+ fi
+fi
+if test "$hints" != ""; then
+ AC_MSG_RESULT(reading hints from $hints)
+ . $hints
+fi
+
+#########
+# Locate a compiler for the build machine. This compiler should
+# generate command-line programs that run on the build machine.
+#
+if test x"$cross_compiling" = xno; then
+ BUILD_CC=$CC
+ BUILD_CFLAGS=$CFLAGS
+else
+ if test "${BUILD_CC+set}" != set; then
+ AC_CHECK_PROGS(BUILD_CC, gcc cc cl)
+ fi
+ if test "${BUILD_CFLAGS+set}" != set; then
+ BUILD_CFLAGS="-g"
+ fi
+fi
+AC_SUBST(BUILD_CC)
+
+##########
+# Do we want to support multithreaded use of sqlite
+#
+AC_ARG_ENABLE(threadsafe,
+AC_HELP_STRING([--enable-threadsafe],[Support threadsafe operation]),,enable_threadsafe=yes)
+AC_MSG_CHECKING([whether to support threadsafe operation])
+if test "$enable_threadsafe" = "no"; then
+ SQLITE_THREADSAFE=0
+ AC_MSG_RESULT([no])
+else
+ SQLITE_THREADSAFE=1
+ AC_MSG_RESULT([yes])
+fi
+AC_SUBST(SQLITE_THREADSAFE)
+
+if test "$SQLITE_THREADSAFE" = "1"; then
+ AC_SEARCH_LIBS(pthread_create, pthread)
+fi
+
+##########
+# Do we want to allow a connection created in one thread to be used
+# in another thread. This does not work on many Linux systems (ex: RedHat 9)
+# due to bugs in the threading implementations. This is thus off by default.
+#
+AC_ARG_ENABLE(cross-thread-connections,
+AC_HELP_STRING([--enable-cross-thread-connections],[Allow connection sharing across threads]),,enable_xthreadconnect=no)
+AC_MSG_CHECKING([whether to allow connections to be shared across threads])
+if test "$enable_xthreadconnect" = "no"; then
+ XTHREADCONNECT=''
+ AC_MSG_RESULT([no])
+else
+ XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1'
+ AC_MSG_RESULT([yes])
+fi
+AC_SUBST(XTHREADCONNECT)
+
+##########
+# Do we want to set threadsOverrideEachOthersLocks variable to be 1 (true) by
+# default. Normally, a test at runtime is performed to determine the
+# appropriate value of this variable. Use this option only if you're sure that
+# threads can safely override each others locks in all runtime situations.
+#
+AC_ARG_ENABLE(threads-override-locks,
+AC_HELP_STRING([--enable-threads-override-locks],[Threads can override each others locks]),,enable_threads_override_locks=no)
+AC_MSG_CHECKING([whether threads can override each others locks])
+if test "$enable_threads_override_locks" = "no"; then
+ THREADSOVERRIDELOCKS='-1'
+ AC_MSG_RESULT([no])
+else
+ THREADSOVERRIDELOCKS='1'
+ AC_MSG_RESULT([yes])
+fi
+AC_SUBST(THREADSOVERRIDELOCKS)
+
+##########
+# Do we want to support release
+#
+AC_ARG_ENABLE(releasemode,
+AC_HELP_STRING([--enable-releasemode],[Support libtool link to release mode]),,enable_releasemode=no)
+AC_MSG_CHECKING([whether to support shared library linked as release mode or not])
+if test "$enable_releasemode" = "no"; then
+ ALLOWRELEASE=""
+ AC_MSG_RESULT([no])
+else
+ ALLOWRELEASE="-release `cat $srcdir/VERSION`"
+ AC_MSG_RESULT([yes])
+fi
+AC_SUBST(ALLOWRELEASE)
+
+##########
+# Do we want temporary databases in memory
+#
+AC_ARG_ENABLE(tempstore,
+AC_HELP_STRING([--enable-tempstore],[Use an in-ram database for temporary tables (never,no,yes,always)]),,enable_tempstore=no)
+AC_MSG_CHECKING([whether to use an in-ram database for temporary tables])
+case "$enable_tempstore" in
+ never )
+ TEMP_STORE=0
+ AC_MSG_RESULT([never])
+ ;;
+ no )
+ TEMP_STORE=1
+ AC_MSG_RESULT([no])
+ ;;
+ always )
+ TEMP_STORE=3
+ AC_MSG_RESULT([always])
+ ;;
+ yes )
+ TEMP_STORE=3
+ AC_MSG_RESULT([always])
+ ;;
+ * )
+ TEMP_STORE=1
+ AC_MSG_RESULT([yes])
+ ;;
+esac
+
+AC_SUBST(TEMP_STORE)
+
+###########
+# Lots of things are different if we are compiling for Windows using
+# the CYGWIN environment. So check for that special case and handle
+# things accordingly.
+#
+AC_MSG_CHECKING([if executables have the .exe suffix])
+if test "$config_BUILD_EXEEXT" = ".exe"; then
+ CYGWIN=yes
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(unknown)
+fi
+if test "$CYGWIN" != "yes"; then
+ AC_CYGWIN
+fi
+if test "$CYGWIN" = "yes"; then
+ BUILD_EXEEXT=.exe
+else
+ BUILD_EXEEXT=$EXEEXT
+fi
+if test x"$cross_compiling" = xno; then
+ TARGET_EXEEXT=$BUILD_EXEEXT
+else
+ TARGET_EXEEXT=$config_TARGET_EXEEXT
+fi
+if test "$TARGET_EXEEXT" = ".exe"; then
+ if test $OS2_SHELL ; then
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=0
+ SQLITE_OS_OS2=1
+ CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1"
+ if test "$ac_compiler_gnu" = "yes" ; then
+ CFLAGS="$CFLAGS -Zomf -Zexe -Zmap"
+ BUILD_CFLAGS="$BUILD_CFLAGS -Zomf -Zexe"
+ fi
+ else
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=1
+ SQLITE_OS_OS2=0
+ CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
+ fi
+else
+ SQLITE_OS_UNIX=1
+ SQLITE_OS_WIN=0
+ SQLITE_OS_OS2=0
+ CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1"
+fi
+
+AC_SUBST(BUILD_EXEEXT)
+AC_SUBST(SQLITE_OS_UNIX)
+AC_SUBST(SQLITE_OS_WIN)
+AC_SUBST(SQLITE_OS_OS2)
+AC_SUBST(TARGET_EXEEXT)
+
+##########
+# Figure out all the parameters needed to compile against Tcl.
+#
+# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
+# macros in the in the tcl.m4 file of the standard TCL distribution.
+# Those macros could not be used directly since we have to make some
+# minor changes to accomodate systems that do not have TCL installed.
+#
+AC_ARG_ENABLE(tcl, AC_HELP_STRING([--disable-tcl],[do not build TCL extension]),
+ [use_tcl=$enableval],[use_tcl=yes])
+if test "${use_tcl}" = "yes" ; then
+ AC_ARG_WITH(tcl, AC_HELP_STRING([--with-tcl=DIR],[directory containing tcl configuration (tclConfig.sh)]), with_tclconfig=${withval})
+ AC_MSG_CHECKING([for Tcl configuration])
+ AC_CACHE_VAL(ac_cv_c_tclconfig,[
+ # First check to see if --with-tcl was specified.
+ if test x"${with_tclconfig}" != x ; then
+ if test -f "${with_tclconfig}/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
+ fi
+ fi
+ # then check for a private Tcl installation
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ../tcl \
+ `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null`
+ do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ `ls -d ${libdir} 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` \
+ `ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/lib 2>/dev/null`
+ do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tcl \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null`
+ do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ use_tcl=no
+ AC_MSG_WARN(Can't find Tcl configuration definitions)
+ AC_MSG_WARN(*** Without Tcl the regression tests cannot be executed ***)
+ AC_MSG_WARN(*** Consider using --with-tcl=... to define location of Tcl ***)
+ else
+ TCL_BIN_DIR=${ac_cv_c_tclconfig}
+ AC_MSG_RESULT(found $TCL_BIN_DIR/tclConfig.sh)
+
+ AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
+ if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
+ AC_MSG_RESULT([loading])
+ . $TCL_BIN_DIR/tclConfig.sh
+ else
+ AC_MSG_RESULT([file not found])
+ fi
+
+ #
+ # If the TCL_BIN_DIR is the build directory (not the install directory),
+ # then set the common variable name to the value of the build variables.
+ # For example, the variable TCL_LIB_SPEC will be set to the value
+ # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
+ # instead of TCL_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ #
+
+ if test -f $TCL_BIN_DIR/Makefile ; then
+ TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
+ TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
+ TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
+ fi
+
+ #
+ # eval is required to do the TCL_DBGX substitution
+ #
+
+ eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+ eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+ eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+
+ eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+ eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
+ eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
+
+ AC_SUBST(TCL_VERSION)
+ AC_SUBST(TCL_BIN_DIR)
+ AC_SUBST(TCL_SRC_DIR)
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(TCL_INCLUDE_SPEC)
+
+ AC_SUBST(TCL_LIB_FILE)
+ AC_SUBST(TCL_LIB_FLAG)
+ AC_SUBST(TCL_LIB_SPEC)
+
+ AC_SUBST(TCL_STUB_LIB_FILE)
+ AC_SUBST(TCL_STUB_LIB_FLAG)
+ AC_SUBST(TCL_STUB_LIB_SPEC)
+ fi
+fi
+if test "${use_tcl}" = "no" ; then
+ HAVE_TCL=""
+else
+ HAVE_TCL=1
+fi
+AC_SUBST(HAVE_TCL)
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "readline()" library.
+#
+TARGET_READLINE_LIBS=""
+TARGET_READLINE_INC=""
+TARGET_HAVE_READLINE=0
+AC_ARG_ENABLE([readline],
+ [AC_HELP_STRING([--disable-readline],[disable readline support [default=detect]])],
+ [with_readline=$enableval],
+ [with_readline=auto])
+
+if test x"$with_readline" != xno; then
+ found="yes"
+
+ AC_ARG_WITH([readline-lib],
+ [AC_HELP_STRING([--with-readline-lib],[specify readline library])],
+ [with_readline_lib=$withval],
+ [with_readline_lib="auto"])
+ if test "x$with_readline_lib" = xauto; then
+ save_LIBS="$LIBS"
+ LIBS=""
+ AC_SEARCH_LIBS(tgetent, [readline ncurses curses termcap], [term_LIBS="$LIBS"], [term_LIBS=""])
+ AC_CHECK_LIB([readline], [readline], [TARGET_READLINE_LIBS="-lreadline"], [found="no"])
+ TARGET_READLINE_LIBS="$TARGET_READLINE_LIBS $term_LIBS"
+ LIBS="$save_LIBS"
+ else
+ TARGET_READLINE_LIBS="$with_readline_lib"
+ fi
+
+ AC_ARG_WITH([readline-inc],
+ [AC_HELP_STRING([--with-readline-inc],[specify readline include paths])],
+ [with_readline_inc=$withval],
+ [with_readline_inc="auto"])
+ if test "x$with_readline_inc" = xauto; then
+ AC_CHECK_HEADER(readline.h, [found="yes"], [
+ found="no"
+ if test "$cross_compiling" != yes; then
+ for dir in /usr /usr/local /usr/local/readline /usr/contrib /mingw; do
+ for subdir in include include/readline; do
+ AC_CHECK_FILE($dir/$subdir/readline.h, found=yes)
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/$subdir"
+ break
+ fi
+ done
+ test "$found" = "yes" && break
+ done
+ fi
+ ])
+ else
+ TARGET_READLINE_INC="$with_readline_inc"
+ fi
+
+ if test x"$found" = xno; then
+ TARGET_READLINE_LIBS=""
+ TARGET_READLINE_INC=""
+ TARGET_HAVE_READLINE=0
+ else
+ TARGET_HAVE_READLINE=1
+ fi
+fi
+
+AC_SUBST(TARGET_READLINE_LIBS)
+AC_SUBST(TARGET_READLINE_INC)
+AC_SUBST(TARGET_HAVE_READLINE)
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "fdatasync()" function.
+#
+AC_SEARCH_LIBS(fdatasync, [rt])
+
+#########
+# check for debug enabled
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]),
+ [use_debug=$enableval],[use_debug=no])
+if test "${use_debug}" = "yes" ; then
+ TARGET_DEBUG="-DSQLITE_DEBUG=1"
+else
+ TARGET_DEBUG="-DNDEBUG"
+fi
+AC_SUBST(TARGET_DEBUG)
+
+#########
+# See whether we should use the amalgamation to build
+AC_ARG_ENABLE(amalgamation, AC_HELP_STRING([--disable-amalgamation],
+ [Disable the amalgamation and instead build all files separately]),
+ [use_amalgamation=$enableval],[use_amalgamation=yes])
+if test "${use_amalgamation}" != "yes" ; then
+ USE_AMALGAMATION=0
+fi
+AC_SUBST(USE_AMALGAMATION)
+
+#########
+# See whether we should allow loadable extensions
+AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--enable-load-extension],
+ [Enable loading of external extensions]),
+ [use_loadextension=$enableval],[use_loadextension=no])
+if test "${use_loadextension}" = "yes" ; then
+ OPT_FEATURE_FLAGS=""
+else
+ OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
+fi
+
+#########
+# attempt to duplicate any OMITS into the $(OPT_FEATURE_FLAGS) parameter
+for option in $CFLAGS $CPPFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";;
+ esac
+done
+AC_SUBST(OPT_FEATURE_FLAGS)
+
+
+# attempt to remove any OMITS from the $(CFLAGS) parameter
+ac_temp_CFLAGS=""
+for option in $CFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_CFLAGS="$ac_temp_CFLAGS $option";;
+ esac
+done
+CFLAGS=$ac_temp_CFLAGS
+
+
+# attempt to remove any OMITS from the $(CPPFLAGS) parameter
+ac_temp_CPPFLAGS=""
+for option in $CPPFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_CPPFLAGS="$ac_temp_CPPFLAGS $option";;
+ esac
+done
+CPPFLAGS=$ac_temp_CPPFLAGS
+
+
+# attempt to remove any OMITS from the $(BUILD_CFLAGS) parameter
+ac_temp_BUILD_CFLAGS=""
+for option in $BUILD_CFLAGS
+do
+ case $option in
+ -DSQLITE_OMIT*) ;;
+ *) ac_temp_BUILD_CFLAGS="$ac_temp_BUILD_CFLAGS $option";;
+ esac
+done
+BUILD_CFLAGS=$ac_temp_BUILD_CFLAGS
+
+
+#########
+# See whether we should use GCOV
+AC_ARG_ENABLE(gcov, AC_HELP_STRING([--enable-gcov],
+ [Enable coverage testing using gcov]),
+ [use_gcov=$enableval],[use_gcov=no])
+if test "${use_gcov}" = "yes" ; then
+ USE_GCOV=1
+else
+ USE_GCOV=0
+fi
+AC_SUBST(USE_GCOV)
+
+
+#########
+# Output the config header
+AC_CONFIG_HEADERS(config.h)
+
+#########
+# Generate the output files.
+#
+AC_SUBST(BUILD_CFLAGS)
+AC_OUTPUT([
+Makefile
+sqlite3.pc
+])
diff --git a/third_party/sqlite/contrib/sqlitecon.tcl b/third_party/sqlite/contrib/sqlitecon.tcl
new file mode 100755
index 0000000..b5dbcaf
--- /dev/null
+++ b/third_party/sqlite/contrib/sqlitecon.tcl
@@ -0,0 +1,679 @@
+# A Tk console widget for SQLite. Invoke sqlitecon::create with a window name,
+# a prompt string, a title to set a new top-level window, and the SQLite
+# database handle. For example:
+#
+# sqlitecon::create .sqlcon {sql:- } {SQL Console} db
+#
+# A toplevel window is created that allows you to type in SQL commands to
+# be processed on the spot.
+#
+# A limited set of dot-commands are supported:
+#
+# .table
+# .schema ?TABLE?
+# .mode list|column|multicolumn|line
+# .exit
+#
+# In addition, a new SQL function named "edit()" is created. This function
+# takes a single text argument and returns a text result. Whenever the
+# the function is called, it pops up a new toplevel window containing a
+# text editor screen initialized to the argument. When the "OK" button
+# is pressed, whatever revised text is in the text editor is returned as
+# the result of the edit() function. This allows text fields of SQL tables
+# to be edited quickly and easily as follows:
+#
+# UPDATE table1 SET dscr = edit(dscr) WHERE rowid=15;
+#
+
+
+# Create a namespace to work in
+#
+namespace eval ::sqlitecon {
+ # do nothing
+}
+
+# Create a console widget named $w. The prompt string is $prompt.
+# The title at the top of the window is $title. The database connection
+# object is $db
+#
+proc sqlitecon::create {w prompt title db} {
+ upvar #0 $w.t v
+ if {[winfo exists $w]} {destroy $w}
+ if {[info exists v]} {unset v}
+ toplevel $w
+ wm title $w $title
+ wm iconname $w $title
+ frame $w.mb -bd 2 -relief raised
+ pack $w.mb -side top -fill x
+ menubutton $w.mb.file -text File -menu $w.mb.file.m
+ menubutton $w.mb.edit -text Edit -menu $w.mb.edit.m
+ pack $w.mb.file $w.mb.edit -side left -padx 8 -pady 1
+ set m [menu $w.mb.file.m -tearoff 0]
+ $m add command -label {Close} -command "destroy $w"
+ sqlitecon::create_child $w $prompt $w.mb.edit.m
+ set v(db) $db
+ $db function edit ::sqlitecon::_edit
+}
+
+# This routine creates a console as a child window within a larger
+# window. It also creates an edit menu named "$editmenu" if $editmenu!="".
+# The calling function is responsible for posting the edit menu.
+#
+proc sqlitecon::create_child {w prompt editmenu} {
+ upvar #0 $w.t v
+ if {$editmenu!=""} {
+ set m [menu $editmenu -tearoff 0]
+ $m add command -label Cut -command "sqlitecon::Cut $w.t"
+ $m add command -label Copy -command "sqlitecon::Copy $w.t"
+ $m add command -label Paste -command "sqlitecon::Paste $w.t"
+ $m add command -label {Clear Screen} -command "sqlitecon::Clear $w.t"
+ $m add separator
+ $m add command -label {Save As...} -command "sqlitecon::SaveFile $w.t"
+ catch {$editmenu config -postcommand "sqlitecon::EnableEditMenu $w"}
+ }
+ scrollbar $w.sb -orient vertical -command "$w.t yview"
+ pack $w.sb -side right -fill y
+ text $w.t -font fixed -yscrollcommand "$w.sb set"
+ pack $w.t -side right -fill both -expand 1
+ bindtags $w.t Sqlitecon
+ set v(editmenu) $editmenu
+ set v(history) 0
+ set v(historycnt) 0
+ set v(current) -1
+ set v(prompt) $prompt
+ set v(prior) {}
+ set v(plength) [string length $v(prompt)]
+ set v(x) 0
+ set v(y) 0
+ set v(mode) column
+ set v(header) on
+ $w.t mark set insert end
+ $w.t tag config ok -foreground blue
+ $w.t tag config err -foreground red
+ $w.t insert end $v(prompt)
+ $w.t mark set out 1.0
+ after idle "focus $w.t"
+}
+
+bind Sqlitecon <1> {sqlitecon::Button1 %W %x %y}
+bind Sqlitecon <B1-Motion> {sqlitecon::B1Motion %W %x %y}
+bind Sqlitecon <B1-Leave> {sqlitecon::B1Leave %W %x %y}
+bind Sqlitecon <B1-Enter> {sqlitecon::cancelMotor %W}
+bind Sqlitecon <ButtonRelease-1> {sqlitecon::cancelMotor %W}
+bind Sqlitecon <KeyPress> {sqlitecon::Insert %W %A}
+bind Sqlitecon <Left> {sqlitecon::Left %W}
+bind Sqlitecon <Control-b> {sqlitecon::Left %W}
+bind Sqlitecon <Right> {sqlitecon::Right %W}
+bind Sqlitecon <Control-f> {sqlitecon::Right %W}
+bind Sqlitecon <BackSpace> {sqlitecon::Backspace %W}
+bind Sqlitecon <Control-h> {sqlitecon::Backspace %W}
+bind Sqlitecon <Delete> {sqlitecon::Delete %W}
+bind Sqlitecon <Control-d> {sqlitecon::Delete %W}
+bind Sqlitecon <Home> {sqlitecon::Home %W}
+bind Sqlitecon <Control-a> {sqlitecon::Home %W}
+bind Sqlitecon <End> {sqlitecon::End %W}
+bind Sqlitecon <Control-e> {sqlitecon::End %W}
+bind Sqlitecon <Return> {sqlitecon::Enter %W}
+bind Sqlitecon <KP_Enter> {sqlitecon::Enter %W}
+bind Sqlitecon <Up> {sqlitecon::Prior %W}
+bind Sqlitecon <Control-p> {sqlitecon::Prior %W}
+bind Sqlitecon <Down> {sqlitecon::Next %W}
+bind Sqlitecon <Control-n> {sqlitecon::Next %W}
+bind Sqlitecon <Control-k> {sqlitecon::EraseEOL %W}
+bind Sqlitecon <<Cut>> {sqlitecon::Cut %W}
+bind Sqlitecon <<Copy>> {sqlitecon::Copy %W}
+bind Sqlitecon <<Paste>> {sqlitecon::Paste %W}
+bind Sqlitecon <<Clear>> {sqlitecon::Clear %W}
+
+# Insert a single character at the insertion cursor
+#
+proc sqlitecon::Insert {w a} {
+ $w insert insert $a
+ $w yview insert
+}
+
+# Move the cursor one character to the left
+#
+proc sqlitecon::Left {w} {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ if {$col>$v(plength)} {
+ $w mark set insert "insert -1c"
+ }
+}
+
+# Erase the character to the left of the cursor
+#
+proc sqlitecon::Backspace {w} {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ if {$col>$v(plength)} {
+ $w delete {insert -1c}
+ }
+}
+
+# Erase to the end of the line
+#
+proc sqlitecon::EraseEOL {w} {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ if {$col>=$v(plength)} {
+ $w delete insert {insert lineend}
+ }
+}
+
+# Move the cursor one character to the right
+#
+proc sqlitecon::Right {w} {
+ $w mark set insert "insert +1c"
+}
+
+# Erase the character to the right of the cursor
+#
+proc sqlitecon::Delete w {
+ $w delete insert
+}
+
+# Move the cursor to the beginning of the current line
+#
+proc sqlitecon::Home w {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ $w mark set insert $row.$v(plength)
+}
+
+# Move the cursor to the end of the current line
+#
+proc sqlitecon::End w {
+ $w mark set insert {insert lineend}
+}
+
+# Add a line to the history
+#
+proc sqlitecon::addHistory {w line} {
+ upvar #0 $w v
+ if {$v(historycnt)>0} {
+ set last [lindex $v(history) [expr $v(historycnt)-1]]
+ if {[string compare $last $line]} {
+ lappend v(history) $line
+ incr v(historycnt)
+ }
+ } else {
+ set v(history) [list $line]
+ set v(historycnt) 1
+ }
+ set v(current) $v(historycnt)
+}
+
+# Called when "Enter" is pressed. Do something with the line
+# of text that was entered.
+#
+proc sqlitecon::Enter w {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ set start $row.$v(plength)
+ set line [$w get $start "$start lineend"]
+ $w insert end \n
+ $w mark set out end
+ if {$v(prior)==""} {
+ set cmd $line
+ } else {
+ set cmd $v(prior)\n$line
+ }
+ if {[string index $cmd 0]=="." || [$v(db) complete $cmd]} {
+ regsub -all {\n} [string trim $cmd] { } cmd2
+ addHistory $w $cmd2
+ set rc [catch {DoCommand $w $cmd} res]
+ if {![winfo exists $w]} return
+ if {$rc} {
+ $w insert end $res\n err
+ } elseif {[string length $res]>0} {
+ $w insert end $res\n ok
+ }
+ set v(prior) {}
+ $w insert end $v(prompt)
+ } else {
+ set v(prior) $cmd
+ regsub -all {[^ ]} $v(prompt) . x
+ $w insert end $x
+ }
+ $w mark set insert end
+ $w mark set out {insert linestart}
+ $w yview insert
+}
+
+# Execute a single SQL command. Pay special attention to control
+# directives that begin with "."
+#
+# The return value is the text output from the command, properly
+# formatted.
+#
+proc sqlitecon::DoCommand {w cmd} {
+ upvar #0 $w v
+ set mode $v(mode)
+ set header $v(header)
+ if {[regexp {^(\.[a-z]+)} $cmd all word]} {
+ if {$word==".mode"} {
+ regexp {^.[a-z]+ +([a-z]+)} $cmd all v(mode)
+ return {}
+ } elseif {$word==".exit"} {
+ destroy [winfo toplevel $w]
+ return {}
+ } elseif {$word==".header"} {
+ regexp {^.[a-z]+ +([a-z]+)} $cmd all v(header)
+ return {}
+ } elseif {$word==".tables"} {
+ set mode multicolumn
+ set cmd {SELECT name FROM sqlite_master WHERE type='table'
+ UNION ALL
+ SELECT name FROM sqlite_temp_master WHERE type='table'}
+ $v(db) eval {PRAGMA database_list} {
+ if {$name!="temp" && $name!="main"} {
+ append cmd "UNION ALL SELECT name FROM $name.sqlite_master\
+ WHERE type='table'"
+ }
+ }
+ append cmd { ORDER BY 1}
+ } elseif {$word==".fullschema"} {
+ set pattern %
+ regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
+ set mode list
+ set header 0
+ set cmd "SELECT sql FROM sqlite_master WHERE tbl_name LIKE '$pattern'
+ AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
+ WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
+ $v(db) eval {PRAGMA database_list} {
+ if {$name!="temp" && $name!="main"} {
+ append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
+ WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
+ }
+ }
+ } elseif {$word==".schema"} {
+ set pattern %
+ regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
+ set mode list
+ set header 0
+ set cmd "SELECT sql FROM sqlite_master WHERE name LIKE '$pattern'
+ AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
+ WHERE name LIKE '$pattern' AND sql NOT NULL"
+ $v(db) eval {PRAGMA database_list} {
+ if {$name!="temp" && $name!="main"} {
+ append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
+ WHERE name LIKE '$pattern' AND sql NOT NULL"
+ }
+ }
+ } else {
+ return \
+ ".exit\n.mode line|list|column\n.schema ?TABLENAME?\n.tables"
+ }
+ }
+ set res {}
+ if {$mode=="list"} {
+ $v(db) eval $cmd x {
+ set sep {}
+ foreach col $x(*) {
+ append res $sep$x($col)
+ set sep |
+ }
+ append res \n
+ }
+ if {[info exists x(*)] && $header} {
+ set sep {}
+ set hdr {}
+ foreach col $x(*) {
+ append hdr $sep$col
+ set sep |
+ }
+ set res $hdr\n$res
+ }
+ } elseif {[string range $mode 0 2]=="col"} {
+ set y {}
+ $v(db) eval $cmd x {
+ foreach col $x(*) {
+ if {![info exists cw($col)] || $cw($col)<[string length $x($col)]} {
+ set cw($col) [string length $x($col)]
+ }
+ lappend y $x($col)
+ }
+ }
+ if {[info exists x(*)] && $header} {
+ set hdr {}
+ set ln {}
+ set dash ---------------------------------------------------------------
+ append dash ------------------------------------------------------------
+ foreach col $x(*) {
+ if {![info exists cw($col)] || $cw($col)<[string length $col]} {
+ set cw($col) [string length $col]
+ }
+ lappend hdr $col
+ lappend ln [string range $dash 1 $cw($col)]
+ }
+ set y [concat $hdr $ln $y]
+ }
+ if {[info exists x(*)]} {
+ set format {}
+ set arglist {}
+ set arglist2 {}
+ set i 0
+ foreach col $x(*) {
+ lappend arglist x$i
+ append arglist2 " \$x$i"
+ incr i
+ append format " %-$cw($col)s"
+ }
+ set format [string trimleft $format]\n
+ if {[llength $arglist]>0} {
+ foreach $arglist $y "append res \[format [list $format] $arglist2\]"
+ }
+ }
+ } elseif {$mode=="multicolumn"} {
+ set y [$v(db) eval $cmd]
+ set max 0
+ foreach e $y {
+ if {$max<[string length $e]} {set max [string length $e]}
+ }
+ set ncol [expr {int(80/($max+2))}]
+ if {$ncol<1} {set ncol 1}
+ set nelem [llength $y]
+ set nrow [expr {($nelem+$ncol-1)/$ncol}]
+ set format "%-${max}s"
+ for {set i 0} {$i<$nrow} {incr i} {
+ set j $i
+ while 1 {
+ append res [format $format [lindex $y $j]]
+ incr j $nrow
+ if {$j>=$nelem} break
+ append res { }
+ }
+ append res \n
+ }
+ } else {
+ $v(db) eval $cmd x {
+ foreach col $x(*) {append res "$col = $x($col)\n"}
+ append res \n
+ }
+ }
+ return [string trimright $res]
+}
+
+# Change the line to the previous line
+#
+proc sqlitecon::Prior w {
+ upvar #0 $w v
+ if {$v(current)<=0} return
+ incr v(current) -1
+ set line [lindex $v(history) $v(current)]
+ sqlitecon::SetLine $w $line
+}
+
+# Change the line to the next line
+#
+proc sqlitecon::Next w {
+ upvar #0 $w v
+ if {$v(current)>=$v(historycnt)} return
+ incr v(current) 1
+ set line [lindex $v(history) $v(current)]
+ sqlitecon::SetLine $w $line
+}
+
+# Change the contents of the entry line
+#
+proc sqlitecon::SetLine {w line} {
+ upvar #0 $w v
+ scan [$w index insert] %d.%d row col
+ set start $row.$v(plength)
+ $w delete $start end
+ $w insert end $line
+ $w mark set insert end
+ $w yview insert
+}
+
+# Called when the mouse button is pressed at position $x,$y on
+# the console widget.
+#
+proc sqlitecon::Button1 {w x y} {
+ global tkPriv
+ upvar #0 $w v
+ set v(mouseMoved) 0
+ set v(pressX) $x
+ set p [sqlitecon::nearestBoundry $w $x $y]
+ scan [$w index insert] %d.%d ix iy
+ scan $p %d.%d px py
+ if {$px==$ix} {
+ $w mark set insert $p
+ }
+ $w mark set anchor $p
+ focus $w
+}
+
+# Find the boundry between characters that is nearest
+# to $x,$y
+#
+proc sqlitecon::nearestBoundry {w x y} {
+ set p [$w index @$x,$y]
+ set bb [$w bbox $p]
+ if {![string compare $bb ""]} {return $p}
+ if {($x-[lindex $bb 0])<([lindex $bb 2]/2)} {return $p}
+ $w index "$p + 1 char"
+}
+
+# This routine extends the selection to the point specified by $x,$y
+#
+proc sqlitecon::SelectTo {w x y} {
+ upvar #0 $w v
+ set cur [sqlitecon::nearestBoundry $w $x $y]
+ if {[catch {$w index anchor}]} {
+ $w mark set anchor $cur
+ }
+ set anchor [$w index anchor]
+ if {[$w compare $cur != $anchor] || (abs($v(pressX) - $x) >= 3)} {
+ if {$v(mouseMoved)==0} {
+ $w tag remove sel 0.0 end
+ }
+ set v(mouseMoved) 1
+ }
+ if {[$w compare $cur < anchor]} {
+ set first $cur
+ set last anchor
+ } else {
+ set first anchor
+ set last $cur
+ }
+ if {$v(mouseMoved)} {
+ $w tag remove sel 0.0 $first
+ $w tag add sel $first $last
+ $w tag remove sel $last end
+ update idletasks
+ }
+}
+
+# Called whenever the mouse moves while button-1 is held down.
+#
+proc sqlitecon::B1Motion {w x y} {
+ upvar #0 $w v
+ set v(y) $y
+ set v(x) $x
+ sqlitecon::SelectTo $w $x $y
+}
+
+# Called whenever the mouse leaves the boundries of the widget
+# while button 1 is held down.
+#
+proc sqlitecon::B1Leave {w x y} {
+ upvar #0 $w v
+ set v(y) $y
+ set v(x) $x
+ sqlitecon::motor $w
+}
+
+# This routine is called to automatically scroll the window when
+# the mouse drags offscreen.
+#
+proc sqlitecon::motor w {
+ upvar #0 $w v
+ if {![winfo exists $w]} return
+ if {$v(y)>=[winfo height $w]} {
+ $w yview scroll 1 units
+ } elseif {$v(y)<0} {
+ $w yview scroll -1 units
+ } else {
+ return
+ }
+ sqlitecon::SelectTo $w $v(x) $v(y)
+ set v(timer) [after 50 sqlitecon::motor $w]
+}
+
+# This routine cancels the scrolling motor if it is active
+#
+proc sqlitecon::cancelMotor w {
+ upvar #0 $w v
+ catch {after cancel $v(timer)}
+ catch {unset v(timer)}
+}
+
+# Do a Copy operation on the stuff currently selected.
+#
+proc sqlitecon::Copy w {
+ if {![catch {set text [$w get sel.first sel.last]}]} {
+ clipboard clear -displayof $w
+ clipboard append -displayof $w $text
+ }
+}
+
+# Return 1 if the selection exists and is contained
+# entirely on the input line. Return 2 if the selection
+# exists but is not entirely on the input line. Return 0
+# if the selection does not exist.
+#
+proc sqlitecon::canCut w {
+ set r [catch {
+ scan [$w index sel.first] %d.%d s1x s1y
+ scan [$w index sel.last] %d.%d s2x s2y
+ scan [$w index insert] %d.%d ix iy
+ }]
+ if {$r==1} {return 0}
+ if {$s1x==$ix && $s2x==$ix} {return 1}
+ return 2
+}
+
+# Do a Cut operation if possible. Cuts are only allowed
+# if the current selection is entirely contained on the
+# current input line.
+#
+proc sqlitecon::Cut w {
+ if {[sqlitecon::canCut $w]==1} {
+ sqlitecon::Copy $w
+ $w delete sel.first sel.last
+ }
+}
+
+# Do a paste opeation.
+#
+proc sqlitecon::Paste w {
+ if {[sqlitecon::canCut $w]==1} {
+ $w delete sel.first sel.last
+ }
+ if {[catch {selection get -displayof $w -selection CLIPBOARD} topaste]
+ && [catch {selection get -displayof $w -selection PRIMARY} topaste]} {
+ return
+ }
+ if {[info exists ::$w]} {
+ set prior 0
+ foreach line [split $topaste \n] {
+ if {$prior} {
+ sqlitecon::Enter $w
+ update
+ }
+ set prior 1
+ $w insert insert $line
+ }
+ } else {
+ $w insert insert $topaste
+ }
+}
+
+# Enable or disable entries in the Edit menu
+#
+proc sqlitecon::EnableEditMenu w {
+ upvar #0 $w.t v
+ set m $v(editmenu)
+ if {$m=="" || ![winfo exists $m]} return
+ switch [sqlitecon::canCut $w.t] {
+ 0 {
+ $m entryconf Copy -state disabled
+ $m entryconf Cut -state disabled
+ }
+ 1 {
+ $m entryconf Copy -state normal
+ $m entryconf Cut -state normal
+ }
+ 2 {
+ $m entryconf Copy -state normal
+ $m entryconf Cut -state disabled
+ }
+ }
+}
+
+# Prompt the user for the name of a writable file. Then write the
+# entire contents of the console screen to that file.
+#
+proc sqlitecon::SaveFile w {
+ set types {
+ {{Text Files} {.txt}}
+ {{All Files} *}
+ }
+ set f [tk_getSaveFile -filetypes $types -title "Write Screen To..."]
+ if {$f!=""} {
+ if {[catch {open $f w} fd]} {
+ tk_messageBox -type ok -icon error -message $fd
+ } else {
+ puts $fd [string trimright [$w get 1.0 end] \n]
+ close $fd
+ }
+ }
+}
+
+# Erase everything from the console above the insertion line.
+#
+proc sqlitecon::Clear w {
+ $w delete 1.0 {insert linestart}
+}
+
+# An in-line editor for SQL
+#
+proc sqlitecon::_edit {origtxt {title {}}} {
+ for {set i 0} {[winfo exists .ed$i]} {incr i} continue
+ set w .ed$i
+ toplevel $w
+ wm protocol $w WM_DELETE_WINDOW "$w.b.can invoke"
+ wm title $w {Inline SQL Editor}
+ frame $w.b
+ pack $w.b -side bottom -fill x
+ button $w.b.can -text Cancel -width 6 -command [list set ::$w 0]
+ button $w.b.ok -text OK -width 6 -command [list set ::$w 1]
+ button $w.b.cut -text Cut -width 6 -command [list ::sqlitecon::Cut $w.t]
+ button $w.b.copy -text Copy -width 6 -command [list ::sqlitecon::Copy $w.t]
+ button $w.b.paste -text Paste -width 6 -command [list ::sqlitecon::Paste $w.t]
+ set ::$w {}
+ pack $w.b.cut $w.b.copy $w.b.paste $w.b.can $w.b.ok\
+ -side left -padx 5 -pady 5 -expand 1
+ if {$title!=""} {
+ label $w.title -text $title
+ pack $w.title -side top -padx 5 -pady 5
+ }
+ text $w.t -bg white -fg black -yscrollcommand [list $w.sb set]
+ pack $w.t -side left -fill both -expand 1
+ scrollbar $w.sb -orient vertical -command [list $w.t yview]
+ pack $w.sb -side left -fill y
+ $w.t insert end $origtxt
+
+ vwait ::$w
+
+ if {[set ::$w]} {
+ set txt [string trimright [$w.t get 1.0 end]]
+ } else {
+ set txt $origtxt
+ }
+ destroy $w
+ return $txt
+}
diff --git a/third_party/sqlite/doc/lemon.html b/third_party/sqlite/doc/lemon.html
new file mode 100755
index 0000000..6a4d6db
--- /dev/null
+++ b/third_party/sqlite/doc/lemon.html
@@ -0,0 +1,892 @@
+<html>
+<head>
+<title>The Lemon Parser Generator</title>
+</head>
+<body bgcolor=white>
+<h1 align=center>The Lemon Parser Generator</h1>
+
+<p>Lemon is an LALR(1) parser generator for C or C++.
+It does the same job as ``bison'' and ``yacc''.
+But lemon is not another bison or yacc clone. It
+uses a different grammar syntax which is designed to
+reduce the number of coding errors. Lemon also uses a more
+sophisticated parsing engine that is faster than yacc and
+bison and which is both reentrant and thread-safe.
+Furthermore, Lemon implements features that can be used
+to eliminate resource leaks, making is suitable for use
+in long-running programs such as graphical user interfaces
+or embedded controllers.</p>
+
+<p>This document is an introduction to the Lemon
+parser generator.</p>
+
+<h2>Theory of Operation</h2>
+
+<p>The main goal of Lemon is to translate a context free grammar (CFG)
+for a particular language into C code that implements a parser for
+that language.
+The program has two inputs:
+<ul>
+<li>The grammar specification.
+<li>A parser template file.
+</ul>
+Typically, only the grammar specification is supplied by the programmer.
+Lemon comes with a default parser template which works fine for most
+applications. But the user is free to substitute a different parser
+template if desired.</p>
+
+<p>Depending on command-line options, Lemon will generate between
+one and three files of outputs.
+<ul>
+<li>C code to implement the parser.
+<li>A header file defining an integer ID for each terminal symbol.
+<li>An information file that describes the states of the generated parser
+ automaton.
+</ul>
+By default, all three of these output files are generated.
+The header file is suppressed if the ``-m'' command-line option is
+used and the report file is omitted when ``-q'' is selected.</p>
+
+<p>The grammar specification file uses a ``.y'' suffix, by convention.
+In the examples used in this document, we'll assume the name of the
+grammar file is ``gram.y''. A typical use of Lemon would be the
+following command:
+<pre>
+ lemon gram.y
+</pre>
+This command will generate three output files named ``gram.c'',
+``gram.h'' and ``gram.out''.
+The first is C code to implement the parser. The second
+is the header file that defines numerical values for all
+terminal symbols, and the last is the report that explains
+the states used by the parser automaton.</p>
+
+<h3>Command Line Options</h3>
+
+<p>The behavior of Lemon can be modified using command-line options.
+You can obtain a list of the available command-line options together
+with a brief explanation of what each does by typing
+<pre>
+ lemon -?
+</pre>
+As of this writing, the following command-line options are supported:
+<ul>
+<li><tt>-b</tt>
+<li><tt>-c</tt>
+<li><tt>-g</tt>
+<li><tt>-m</tt>
+<li><tt>-q</tt>
+<li><tt>-s</tt>
+<li><tt>-x</tt>
+</ul>
+The ``-b'' option reduces the amount of text in the report file by
+printing only the basis of each parser state, rather than the full
+configuration.
+The ``-c'' option suppresses action table compression. Using -c
+will make the parser a little larger and slower but it will detect
+syntax errors sooner.
+The ``-g'' option causes no output files to be generated at all.
+Instead, the input grammar file is printed on standard output but
+with all comments, actions and other extraneous text deleted. This
+is a useful way to get a quick summary of a grammar.
+The ``-m'' option causes the output C source file to be compatible
+with the ``makeheaders'' program.
+Makeheaders is a program that automatically generates header files
+from C source code. When the ``-m'' option is used, the header
+file is not output since the makeheaders program will take care
+of generated all header files automatically.
+The ``-q'' option suppresses the report file.
+Using ``-s'' causes a brief summary of parser statistics to be
+printed. Like this:
+<pre>
+ Parser statistics: 74 terminals, 70 nonterminals, 179 rules
+ 340 states, 2026 parser table entries, 0 conflicts
+</pre>
+Finally, the ``-x'' option causes Lemon to print its version number
+and then stops without attempting to read the grammar or generate a parser.</p>
+
+<h3>The Parser Interface</h3>
+
+<p>Lemon doesn't generate a complete, working program. It only generates
+a few subroutines that implement a parser. This section describes
+the interface to those subroutines. It is up to the programmer to
+call these subroutines in an appropriate way in order to produce a
+complete system.</p>
+
+<p>Before a program begins using a Lemon-generated parser, the program
+must first create the parser.
+A new parser is created as follows:
+<pre>
+ void *pParser = ParseAlloc( malloc );
+</pre>
+The ParseAlloc() routine allocates and initializes a new parser and
+returns a pointer to it.
+The actual data structure used to represent a parser is opaque --
+its internal structure is not visible or usable by the calling routine.
+For this reason, the ParseAlloc() routine returns a pointer to void
+rather than a pointer to some particular structure.
+The sole argument to the ParseAlloc() routine is a pointer to the
+subroutine used to allocate memory. Typically this means ``malloc()''.</p>
+
+<p>After a program is finished using a parser, it can reclaim all
+memory allocated by that parser by calling
+<pre>
+ ParseFree(pParser, free);
+</pre>
+The first argument is the same pointer returned by ParseAlloc(). The
+second argument is a pointer to the function used to release bulk
+memory back to the system.</p>
+
+<p>After a parser has been allocated using ParseAlloc(), the programmer
+must supply the parser with a sequence of tokens (terminal symbols) to
+be parsed. This is accomplished by calling the following function
+once for each token:
+<pre>
+ Parse(pParser, hTokenID, sTokenData, pArg);
+</pre>
+The first argument to the Parse() routine is the pointer returned by
+ParseAlloc().
+The second argument is a small positive integer that tells the parse the
+type of the next token in the data stream.
+There is one token type for each terminal symbol in the grammar.
+The gram.h file generated by Lemon contains #define statements that
+map symbolic terminal symbol names into appropriate integer values.
+(A value of 0 for the second argument is a special flag to the
+parser to indicate that the end of input has been reached.)
+The third argument is the value of the given token. By default,
+the type of the third argument is integer, but the grammar will
+usually redefine this type to be some kind of structure.
+Typically the second argument will be a broad category of tokens
+such as ``identifier'' or ``number'' and the third argument will
+be the name of the identifier or the value of the number.</p>
+
+<p>The Parse() function may have either three or four arguments,
+depending on the grammar. If the grammar specification file request
+it, the Parse() function will have a fourth parameter that can be
+of any type chosen by the programmer. The parser doesn't do anything
+with this argument except to pass it through to action routines.
+This is a convenient mechanism for passing state information down
+to the action routines without having to use global variables.</p>
+
+<p>A typical use of a Lemon parser might look something like the
+following:
+<pre>
+ 01 ParseTree *ParseFile(const char *zFilename){
+ 02 Tokenizer *pTokenizer;
+ 03 void *pParser;
+ 04 Token sToken;
+ 05 int hTokenId;
+ 06 ParserState sState;
+ 07
+ 08 pTokenizer = TokenizerCreate(zFilename);
+ 09 pParser = ParseAlloc( malloc );
+ 10 InitParserState(&sState);
+ 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
+ 12 Parse(pParser, hTokenId, sToken, &sState);
+ 13 }
+ 14 Parse(pParser, 0, sToken, &sState);
+ 15 ParseFree(pParser, free );
+ 16 TokenizerFree(pTokenizer);
+ 17 return sState.treeRoot;
+ 18 }
+</pre>
+This example shows a user-written routine that parses a file of
+text and returns a pointer to the parse tree.
+(We've omitted all error-handling from this example to keep it
+simple.)
+We assume the existence of some kind of tokenizer which is created
+using TokenizerCreate() on line 8 and deleted by TokenizerFree()
+on line 16. The GetNextToken() function on line 11 retrieves the
+next token from the input file and puts its type in the
+integer variable hTokenId. The sToken variable is assumed to be
+some kind of structure that contains details about each token,
+such as its complete text, what line it occurs on, etc. </p>
+
+<p>This example also assumes the existence of structure of type
+ParserState that holds state information about a particular parse.
+An instance of such a structure is created on line 6 and initialized
+on line 10. A pointer to this structure is passed into the Parse()
+routine as the optional 4th argument.
+The action routine specified by the grammar for the parser can use
+the ParserState structure to hold whatever information is useful and
+appropriate. In the example, we note that the treeRoot field of
+the ParserState structure is left pointing to the root of the parse
+tree.</p>
+
+<p>The core of this example as it relates to Lemon is as follows:
+<pre>
+ ParseFile(){
+ pParser = ParseAlloc( malloc );
+ while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
+ Parse(pParser, hTokenId, sToken);
+ }
+ Parse(pParser, 0, sToken);
+ ParseFree(pParser, free );
+ }
+</pre>
+Basically, what a program has to do to use a Lemon-generated parser
+is first create the parser, then send it lots of tokens obtained by
+tokenizing an input source. When the end of input is reached, the
+Parse() routine should be called one last time with a token type
+of 0. This step is necessary to inform the parser that the end of
+input has been reached. Finally, we reclaim memory used by the
+parser by calling ParseFree().</p>
+
+<p>There is one other interface routine that should be mentioned
+before we move on.
+The ParseTrace() function can be used to generate debugging output
+from the parser. A prototype for this routine is as follows:
+<pre>
+ ParseTrace(FILE *stream, char *zPrefix);
+</pre>
+After this routine is called, a short (one-line) message is written
+to the designated output stream every time the parser changes states
+or calls an action routine. Each such message is prefaced using
+the text given by zPrefix. This debugging output can be turned off
+by calling ParseTrace() again with a first argument of NULL (0).</p>
+
+<h3>Differences With YACC and BISON</h3>
+
+<p>Programmers who have previously used the yacc or bison parser
+generator will notice several important differences between yacc and/or
+bison and Lemon.
+<ul>
+<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
+ the tokenizer calls the parser.
+<li>Lemon uses no global variables. Yacc and bison use global variables
+ to pass information between the tokenizer and parser.
+<li>Lemon allows multiple parsers to be running simultaneously. Yacc
+ and bison do not.
+</ul>
+These differences may cause some initial confusion for programmers
+with prior yacc and bison experience.
+But after years of experience using Lemon, I firmly
+believe that the Lemon way of doing things is better.</p>
+
+<h2>Input File Syntax</h2>
+
+<p>The main purpose of the grammar specification file for Lemon is
+to define the grammar for the parser. But the input file also
+specifies additional information Lemon requires to do its job.
+Most of the work in using Lemon is in writing an appropriate
+grammar file.</p>
+
+<p>The grammar file for lemon is, for the most part, free format.
+It does not have sections or divisions like yacc or bison. Any
+declaration can occur at any point in the file.
+Lemon ignores whitespace (except where it is needed to separate
+tokens) and it honors the same commenting conventions as C and C++.</p>
+
+<h3>Terminals and Nonterminals</h3>
+
+<p>A terminal symbol (token) is any string of alphanumeric
+and underscore characters
+that begins with an upper case letter.
+A terminal can contain lower class letters after the first character,
+but the usual convention is to make terminals all upper case.
+A nonterminal, on the other hand, is any string of alphanumeric
+and underscore characters than begins with a lower case letter.
+Again, the usual convention is to make nonterminals use all lower
+case letters.</p>
+
+<p>In Lemon, terminal and nonterminal symbols do not need to
+be declared or identified in a separate section of the grammar file.
+Lemon is able to generate a list of all terminals and nonterminals
+by examining the grammar rules, and it can always distinguish a
+terminal from a nonterminal by checking the case of the first
+character of the name.</p>
+
+<p>Yacc and bison allow terminal symbols to have either alphanumeric
+names or to be individual characters included in single quotes, like
+this: ')' or '$'. Lemon does not allow this alternative form for
+terminal symbols. With Lemon, all symbols, terminals and nonterminals,
+must have alphanumeric names.</p>
+
+<h3>Grammar Rules</h3>
+
+<p>The main component of a Lemon grammar file is a sequence of grammar
+rules.
+Each grammar rule consists of a nonterminal symbol followed by
+the special symbol ``::='' and then a list of terminals and/or nonterminals.
+The rule is terminated by a period.
+The list of terminals and nonterminals on the right-hand side of the
+rule can be empty.
+Rules can occur in any order, except that the left-hand side of the
+first rule is assumed to be the start symbol for the grammar (unless
+specified otherwise using the <tt>%start</tt> directive described below.)
+A typical sequence of grammar rules might look something like this:
+<pre>
+ expr ::= expr PLUS expr.
+ expr ::= expr TIMES expr.
+ expr ::= LPAREN expr RPAREN.
+ expr ::= VALUE.
+</pre>
+</p>
+
+<p>There is one non-terminal in this example, ``expr'', and five
+terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
+``RPAREN'' and ``VALUE''.</p>
+
+<p>Like yacc and bison, Lemon allows the grammar to specify a block
+of C code that will be executed whenever a grammar rule is reduced
+by the parser.
+In Lemon, this action is specified by putting the C code (contained
+within curly braces <tt>{...}</tt>) immediately after the
+period that closes the rule.
+For example:
+<pre>
+ expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
+</pre>
+</p>
+
+<p>In order to be useful, grammar actions must normally be linked to
+their associated grammar rules.
+In yacc and bison, this is accomplished by embedding a ``$$'' in the
+action to stand for the value of the left-hand side of the rule and
+symbols ``$1'', ``$2'', and so forth to stand for the value of
+the terminal or nonterminal at position 1, 2 and so forth on the
+right-hand side of the rule.
+This idea is very powerful, but it is also very error-prone. The
+single most common source of errors in a yacc or bison grammar is
+to miscount the number of symbols on the right-hand side of a grammar
+rule and say ``$7'' when you really mean ``$8''.</p>
+
+<p>Lemon avoids the need to count grammar symbols by assigning symbolic
+names to each symbol in a grammar rule and then using those symbolic
+names in the action.
+In yacc or bison, one would write this:
+<pre>
+ expr -> expr PLUS expr { $$ = $1 + $3; };
+</pre>
+But in Lemon, the same rule becomes the following:
+<pre>
+ expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
+</pre>
+In the Lemon rule, any symbol in parentheses after a grammar rule
+symbol becomes a place holder for that symbol in the grammar rule.
+This place holder can then be used in the associated C action to
+stand for the value of that symbol.<p>
+
+<p>The Lemon notation for linking a grammar rule with its reduce
+action is superior to yacc/bison on several counts.
+First, as mentioned above, the Lemon method avoids the need to
+count grammar symbols.
+Secondly, if a terminal or nonterminal in a Lemon grammar rule
+includes a linking symbol in parentheses but that linking symbol
+is not actually used in the reduce action, then an error message
+is generated.
+For example, the rule
+<pre>
+ expr(A) ::= expr(B) PLUS expr(C). { A = B; }
+</pre>
+will generate an error because the linking symbol ``C'' is used
+in the grammar rule but not in the reduce action.</p>
+
+<p>The Lemon notation for linking grammar rules to reduce actions
+also facilitates the use of destructors for reclaiming memory
+allocated by the values of terminals and nonterminals on the
+right-hand side of a rule.</p>
+
+<h3>Precedence Rules</h3>
+
+<p>Lemon resolves parsing ambiguities in exactly the same way as
+yacc and bison. A shift-reduce conflict is resolved in favor
+of the shift, and a reduce-reduce conflict is resolved by reducing
+whichever rule comes first in the grammar file.</p>
+
+<p>Just like in
+yacc and bison, Lemon allows a measure of control
+over the resolution of paring conflicts using precedence rules.
+A precedence value can be assigned to any terminal symbol
+using the %left, %right or %nonassoc directives. Terminal symbols
+mentioned in earlier directives have a lower precedence that
+terminal symbols mentioned in later directives. For example:</p>
+
+<p><pre>
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+</pre></p>
+
+<p>In the preceding sequence of directives, the AND operator is
+defined to have the lowest precedence. The OR operator is one
+precedence level higher. And so forth. Hence, the grammar would
+attempt to group the ambiguous expression
+<pre>
+ a AND b OR c
+</pre>
+like this
+<pre>
+ a AND (b OR c).
+</pre>
+The associativity (left, right or nonassoc) is used to determine
+the grouping when the precedence is the same. AND is left-associative
+in our example, so
+<pre>
+ a AND b AND c
+</pre>
+is parsed like this
+<pre>
+ (a AND b) AND c.
+</pre>
+The EXP operator is right-associative, though, so
+<pre>
+ a EXP b EXP c
+</pre>
+is parsed like this
+<pre>
+ a EXP (b EXP c).
+</pre>
+The nonassoc precedence is used for non-associative operators.
+So
+<pre>
+ a EQ b EQ c
+</pre>
+is an error.</p>
+
+<p>The precedence of non-terminals is transferred to rules as follows:
+The precedence of a grammar rule is equal to the precedence of the
+left-most terminal symbol in the rule for which a precedence is
+defined. This is normally what you want, but in those cases where
+you want to precedence of a grammar rule to be something different,
+you can specify an alternative precedence symbol by putting the
+symbol in square braces after the period at the end of the rule and
+before any C-code. For example:</p>
+
+<p><pre>
+ expr = MINUS expr. [NOT]
+</pre></p>
+
+<p>This rule has a precedence equal to that of the NOT symbol, not the
+MINUS symbol as would have been the case by default.</p>
+
+<p>With the knowledge of how precedence is assigned to terminal
+symbols and individual
+grammar rules, we can now explain precisely how parsing conflicts
+are resolved in Lemon. Shift-reduce conflicts are resolved
+as follows:
+<ul>
+<li> If either the token to be shifted or the rule to be reduced
+ lacks precedence information, then resolve in favor of the
+ shift, but report a parsing conflict.
+<li> If the precedence of the token to be shifted is greater than
+ the precedence of the rule to reduce, then resolve in favor
+ of the shift. No parsing conflict is reported.
+<li> If the precedence of the token it be shifted is less than the
+ precedence of the rule to reduce, then resolve in favor of the
+ reduce action. No parsing conflict is reported.
+<li> If the precedences are the same and the shift token is
+ right-associative, then resolve in favor of the shift.
+ No parsing conflict is reported.
+<li> If the precedences are the same the the shift token is
+ left-associative, then resolve in favor of the reduce.
+ No parsing conflict is reported.
+<li> Otherwise, resolve the conflict by doing the shift and
+ report the parsing conflict.
+</ul>
+Reduce-reduce conflicts are resolved this way:
+<ul>
+<li> If either reduce rule
+ lacks precedence information, then resolve in favor of the
+ rule that appears first in the grammar and report a parsing
+ conflict.
+<li> If both rules have precedence and the precedence is different
+ then resolve the dispute in favor of the rule with the highest
+ precedence and do not report a conflict.
+<li> Otherwise, resolve the conflict by reducing by the rule that
+ appears first in the grammar and report a parsing conflict.
+</ul>
+
+<h3>Special Directives</h3>
+
+<p>The input grammar to Lemon consists of grammar rules and special
+directives. We've described all the grammar rules, so now we'll
+talk about the special directives.</p>
+
+<p>Directives in lemon can occur in any order. You can put them before
+the grammar rules, or after the grammar rules, or in the mist of the
+grammar rules. It doesn't matter. The relative order of
+directives used to assign precedence to terminals is important, but
+other than that, the order of directives in Lemon is arbitrary.</p>
+
+<p>Lemon supports the following special directives:
+<ul>
+<li><tt>%code</tt>
+<li><tt>%default_destructor</tt>
+<li><tt>%default_type</tt>
+<li><tt>%destructor</tt>
+<li><tt>%extra_argument</tt>
+<li><tt>%include</tt>
+<li><tt>%left</tt>
+<li><tt>%name</tt>
+<li><tt>%nonassoc</tt>
+<li><tt>%parse_accept</tt>
+<li><tt>%parse_failure </tt>
+<li><tt>%right</tt>
+<li><tt>%stack_overflow</tt>
+<li><tt>%stack_size</tt>
+<li><tt>%start_symbol</tt>
+<li><tt>%syntax_error</tt>
+<li><tt>%token_destructor</tt>
+<li><tt>%token_prefix</tt>
+<li><tt>%token_type</tt>
+<li><tt>%type</tt>
+</ul>
+Each of these directives will be described separately in the
+following sections:</p>
+
+<h4>The <tt>%code</tt> directive</h4>
+
+<p>The %code directive is used to specify addition C/C++ code that
+is added to the end of the main output file. This is similar to
+the %include directive except that %include is inserted at the
+beginning of the main output file.</p>
+
+<p>%code is typically used to include some action routines or perhaps
+a tokenizer as part of the output file.</p>
+
+<h4>The <tt>%default_destructor</tt> directive</h4>
+
+<p>The %default_destructor directive specifies a destructor to
+use for non-terminals that do not have their own destructor
+specified by a separate %destructor directive. See the documentation
+on the %destructor directive below for additional information.</p>
+
+<p>In some grammers, many different non-terminal symbols have the
+same datatype and hence the same destructor. This directive is
+a convenience way to specify the same destructor for all those
+non-terminals using a single statement.</p>
+
+<h4>The <tt>%default_type</tt> directive</h4>
+
+<p>The %default_type directive specifies the datatype of non-terminal
+symbols that do no have their own datatype defined using a separate
+%type directive. See the documentation on %type below for addition
+information.</p>
+
+<h4>The <tt>%destructor</tt> directive</h4>
+
+<p>The %destructor directive is used to specify a destructor for
+a non-terminal symbol.
+(See also the %token_destructor directive which is used to
+specify a destructor for terminal symbols.)</p>
+
+<p>A non-terminal's destructor is called to dispose of the
+non-terminal's value whenever the non-terminal is popped from
+the stack. This includes all of the following circumstances:
+<ul>
+<li> When a rule reduces and the value of a non-terminal on
+ the right-hand side is not linked to C code.
+<li> When the stack is popped during error processing.
+<li> When the ParseFree() function runs.
+</ul>
+The destructor can do whatever it wants with the value of
+the non-terminal, but its design is to deallocate memory
+or other resources held by that non-terminal.</p>
+
+<p>Consider an example:
+<pre>
+ %type nt {void*}
+ %destructor nt { free($$); }
+ nt(A) ::= ID NUM. { A = malloc( 100 ); }
+</pre>
+This example is a bit contrived but it serves to illustrate how
+destructors work. The example shows a non-terminal named
+``nt'' that holds values of type ``void*''. When the rule for
+an ``nt'' reduces, it sets the value of the non-terminal to
+space obtained from malloc(). Later, when the nt non-terminal
+is popped from the stack, the destructor will fire and call
+free() on this malloced space, thus avoiding a memory leak.
+(Note that the symbol ``$$'' in the destructor code is replaced
+by the value of the non-terminal.)</p>
+
+<p>It is important to note that the value of a non-terminal is passed
+to the destructor whenever the non-terminal is removed from the
+stack, unless the non-terminal is used in a C-code action. If
+the non-terminal is used by C-code, then it is assumed that the
+C-code will take care of destroying it if it should really
+be destroyed. More commonly, the value is used to build some
+larger structure and we don't want to destroy it, which is why
+the destructor is not called in this circumstance.</p>
+
+<p>By appropriate use of destructors, it is possible to
+build a parser using Lemon that can be used within a long-running
+program, such as a GUI, that will not leak memory or other resources.
+To do the same using yacc or bison is much more difficult.</p>
+
+<h4>The <tt>%extra_argument</tt> directive</h4>
+
+The %extra_argument directive instructs Lemon to add a 4th parameter
+to the parameter list of the Parse() function it generates. Lemon
+doesn't do anything itself with this extra argument, but it does
+make the argument available to C-code action routines, destructors,
+and so forth. For example, if the grammar file contains:</p>
+
+<p><pre>
+ %extra_argument { MyStruct *pAbc }
+</pre></p>
+
+<p>Then the Parse() function generated will have an 4th parameter
+of type ``MyStruct*'' and all action routines will have access to
+a variable named ``pAbc'' that is the value of the 4th parameter
+in the most recent call to Parse().</p>
+
+<h4>The <tt>%include</tt> directive</h4>
+
+<p>The %include directive specifies C code that is included at the
+top of the generated parser. You can include any text you want --
+the Lemon parser generator copies it blindly. If you have multiple
+%include directives in your grammar file the value of the last
+%include directive overwrites all the others.</p.
+
+<p>The %include directive is very handy for getting some extra #include
+preprocessor statements at the beginning of the generated parser.
+For example:</p>
+
+<p><pre>
+ %include {#include &lt;unistd.h&gt;}
+</pre></p>
+
+<p>This might be needed, for example, if some of the C actions in the
+grammar call functions that are prototyed in unistd.h.</p>
+
+<h4>The <tt>%left</tt> directive</h4>
+
+The %left directive is used (along with the %right and
+%nonassoc directives) to declare precedences of terminal
+symbols. Every terminal symbol whose name appears after
+a %left directive but before the next period (``.'') is
+given the same left-associative precedence value. Subsequent
+%left directives have higher precedence. For example:</p>
+
+<p><pre>
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+</pre></p>
+
+<p>Note the period that terminates each %left, %right or %nonassoc
+directive.</p>
+
+<p>LALR(1) grammars can get into a situation where they require
+a large amount of stack space if you make heavy use or right-associative
+operators. For this reason, it is recommended that you use %left
+rather than %right whenever possible.</p>
+
+<h4>The <tt>%name</tt> directive</h4>
+
+<p>By default, the functions generated by Lemon all begin with the
+five-character string ``Parse''. You can change this string to something
+different using the %name directive. For instance:</p>
+
+<p><pre>
+ %name Abcde
+</pre></p>
+
+<p>Putting this directive in the grammar file will cause Lemon to generate
+functions named
+<ul>
+<li> AbcdeAlloc(),
+<li> AbcdeFree(),
+<li> AbcdeTrace(), and
+<li> Abcde().
+</ul>
+The %name directive allows you to generator two or more different
+parsers and link them all into the same executable.
+</p>
+
+<h4>The <tt>%nonassoc</tt> directive</h4>
+
+<p>This directive is used to assign non-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.</p>
+
+<h4>The <tt>%parse_accept</tt> directive</h4>
+
+<p>The %parse_accept directive specifies a block of C code that is
+executed whenever the parser accepts its input string. To ``accept''
+an input string means that the parser was able to process all tokens
+without error.</p>
+
+<p>For example:</p>
+
+<p><pre>
+ %parse_accept {
+ printf("parsing complete!\n");
+ }
+</pre></p>
+
+
+<h4>The <tt>%parse_failure</tt> directive</h4>
+
+<p>The %parse_failure directive specifies a block of C code that
+is executed whenever the parser fails complete. This code is not
+executed until the parser has tried and failed to resolve an input
+error using is usual error recovery strategy. The routine is
+only invoked when parsing is unable to continue.</p>
+
+<p><pre>
+ %parse_failure {
+ fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
+ }
+</pre></p>
+
+<h4>The <tt>%right</tt> directive</h4>
+
+<p>This directive is used to assign right-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.</p>
+
+<h4>The <tt>%stack_overflow</tt> directive</h4>
+
+<p>The %stack_overflow directive specifies a block of C code that
+is executed if the parser's internal stack ever overflows. Typically
+this just prints an error message. After a stack overflow, the parser
+will be unable to continue and must be reset.</p>
+
+<p><pre>
+ %stack_overflow {
+ fprintf(stderr,"Giving up. Parser stack overflow\n");
+ }
+</pre></p>
+
+<p>You can help prevent parser stack overflows by avoiding the use
+of right recursion and right-precedence operators in your grammar.
+Use left recursion and and left-precedence operators instead, to
+encourage rules to reduce sooner and keep the stack size down.
+For example, do rules like this:
+<pre>
+ list ::= list element. // left-recursion. Good!
+ list ::= .
+</pre>
+Not like this:
+<pre>
+ list ::= element list. // right-recursion. Bad!
+ list ::= .
+</pre>
+
+<h4>The <tt>%stack_size</tt> directive</h4>
+
+<p>If stack overflow is a problem and you can't resolve the trouble
+by using left-recursion, then you might want to increase the size
+of the parser's stack using this directive. Put an positive integer
+after the %stack_size directive and Lemon will generate a parse
+with a stack of the requested size. The default value is 100.</p>
+
+<p><pre>
+ %stack_size 2000
+</pre></p>
+
+<h4>The <tt>%start_symbol</tt> directive</h4>
+
+<p>By default, the start-symbol for the grammar that Lemon generates
+is the first non-terminal that appears in the grammar file. But you
+can choose a different start-symbol using the %start_symbol directive.</p>
+
+<p><pre>
+ %start_symbol prog
+</pre></p>
+
+<h4>The <tt>%token_destructor</tt> directive</h4>
+
+<p>The %destructor directive assigns a destructor to a non-terminal
+symbol. (See the description of the %destructor directive above.)
+This directive does the same thing for all terminal symbols.</p>
+
+<p>Unlike non-terminal symbols which may each have a different data type
+for their values, terminals all use the same data type (defined by
+the %token_type directive) and so they use a common destructor. Other
+than that, the token destructor works just like the non-terminal
+destructors.</p>
+
+<h4>The <tt>%token_prefix</tt> directive</h4>
+
+<p>Lemon generates #defines that assign small integer constants
+to each terminal symbol in the grammar. If desired, Lemon will
+add a prefix specified by this directive
+to each of the #defines it generates.
+So if the default output of Lemon looked like this:
+<pre>
+ #define AND 1
+ #define MINUS 2
+ #define OR 3
+ #define PLUS 4
+</pre>
+You can insert a statement into the grammar like this:
+<pre>
+ %token_prefix TOKEN_
+</pre>
+to cause Lemon to produce these symbols instead:
+<pre>
+ #define TOKEN_AND 1
+ #define TOKEN_MINUS 2
+ #define TOKEN_OR 3
+ #define TOKEN_PLUS 4
+</pre>
+
+<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
+
+<p>These directives are used to specify the data types for values
+on the parser's stack associated with terminal and non-terminal
+symbols. The values of all terminal symbols must be of the same
+type. This turns out to be the same data type as the 3rd parameter
+to the Parse() function generated by Lemon. Typically, you will
+make the value of a terminal symbol by a pointer to some kind of
+token structure. Like this:</p>
+
+<p><pre>
+ %token_type {Token*}
+</pre></p>
+
+<p>If the data type of terminals is not specified, the default value
+is ``int''.</p>
+
+<p>Non-terminal symbols can each have their own data types. Typically
+the data type of a non-terminal is a pointer to the root of a parse-tree
+structure that contains all information about that non-terminal.
+For example:</p>
+
+<p><pre>
+ %type expr {Expr*}
+</pre></p>
+
+<p>Each entry on the parser's stack is actually a union containing
+instances of all data types for every non-terminal and terminal symbol.
+Lemon will automatically use the correct element of this union depending
+on what the corresponding non-terminal or terminal symbol is. But
+the grammar designer should keep in mind that the size of the union
+will be the size of its largest element. So if you have a single
+non-terminal whose data type requires 1K of storage, then your 100
+entry parser stack will require 100K of heap space. If you are willing
+and able to pay that price, fine. You just need to know.</p>
+
+<h3>Error Processing</h3>
+
+<p>After extensive experimentation over several years, it has been
+discovered that the error recovery strategy used by yacc is about
+as good as it gets. And so that is what Lemon uses.</p>
+
+<p>When a Lemon-generated parser encounters a syntax error, it
+first invokes the code specified by the %syntax_error directive, if
+any. It then enters its error recovery strategy. The error recovery
+strategy is to begin popping the parsers stack until it enters a
+state where it is permitted to shift a special non-terminal symbol
+named ``error''. It then shifts this non-terminal and continues
+parsing. But the %syntax_error routine will not be called again
+until at least three new tokens have been successfully shifted.</p>
+
+<p>If the parser pops its stack until the stack is empty, and it still
+is unable to shift the error symbol, then the %parse_failed routine
+is invoked and the parser resets itself to its start state, ready
+to begin parsing a new file. This is what will happen at the very
+first syntax error, of course, if there are no instances of the
+``error'' non-terminal in your grammar.</p>
+
+</body>
+</html>
diff --git a/third_party/sqlite/doc/report1.txt b/third_party/sqlite/doc/report1.txt
new file mode 100755
index 0000000..a236e7c
--- /dev/null
+++ b/third_party/sqlite/doc/report1.txt
@@ -0,0 +1,121 @@
+An SQLite (version 1.0) database was used in a large military application
+where the database contained 105 tables and indices. The following is
+a breakdown on the sizes of keys and data within these tables and indices:
+
+Entries: 967089
+Size: 45896104
+Avg Size: 48
+Key Size: 11112265
+Avg Key Size: 12
+Max Key Size: 99
+
+ 0..8 263 0%
+ 9..12 5560 0%
+ 13..16 71394 7%
+ 17..24 180717 26%
+ 25..32 215442 48%
+ 33..40 151118 64%
+ 41..48 77479 72%
+ 49..56 13983 74%
+ 57..64 14481 75%
+ 65..80 41342 79%
+ 81..96 127098 92%
+ 97..112 38054 96%
+ 113..128 14197 98%
+ 129..144 8208 99%
+ 145..160 3326 99%
+ 161..176 1242 99%
+ 177..192 604 99%
+ 193..208 222 99%
+ 209..224 213 99%
+ 225..240 132 99%
+ 241..256 58 99%
+ 257..288 515 99%
+ 289..320 64 99%
+ 321..352 39 99%
+ 353..384 44 99%
+ 385..416 25 99%
+ 417..448 24 99%
+ 449..480 26 99%
+ 481..512 27 99%
+ 513..1024 470 99%
+ 1025..2048 396 99%
+ 2049..4096 187 99%
+ 4097..8192 78 99%
+ 8193..16384 35 99%
+16385..32768 17 99%
+32769..65536 6 99%
+65537..65541 3 100%
+
+If the indices are omitted, the statistics for the 49 tables
+become the following:
+
+Entries: 451103
+Size: 30930282
+Avg Size: 69
+Key Size: 1804412
+Avg Key Size: 4
+Max Key Size: 4
+
+ 0..24 89 0%
+ 25..32 9417 2%
+ 33..40 119162 28%
+ 41..48 68710 43%
+ 49..56 9539 45%
+ 57..64 12435 48%
+ 65..80 38650 57%
+ 81..96 126877 85%
+ 97..112 38030 93%
+ 113..128 14183 96%
+ 129..144 7668 98%
+ 145..160 3302 99%
+ 161..176 1238 99%
+ 177..192 597 99%
+ 193..208 217 99%
+ 209..224 211 99%
+ 225..240 130 99%
+ 241..256 57 99%
+ 257..288 100 99%
+ 289..320 62 99%
+ 321..352 34 99%
+ 353..384 43 99%
+ 385..416 24 99%
+ 417..448 24 99%
+ 449..480 25 99%
+ 481..512 27 99%
+ 513..1024 153 99%
+ 1025..2048 92 99%
+ 2049..4096 7 100%
+
+The 56 indices have these statistics:
+
+Entries: 512422
+Size: 14879828
+Avg Size: 30
+Key Size: 9253204
+Avg Key Size: 19
+Max Key Size: 99
+
+ 0..8 246 0%
+ 9..12 5486 1%
+ 13..16 70717 14%
+ 17..24 178246 49%
+ 25..32 205722 89%
+ 33..40 31951 96%
+ 41..48 8768 97%
+ 49..56 4444 98%
+ 57..64 2046 99%
+ 65..80 2691 99%
+ 81..96 202 99%
+ 97..112 11 99%
+ 113..144 527 99%
+ 145..160 20 99%
+ 161..288 406 99%
+ 289..1024 316 99%
+ 1025..2048 304 99%
+ 2049..4096 180 99%
+ 4097..8192 78 99%
+ 8193..16384 35 99%
+16385..32768 17 99%
+32769..65536 6 99%
+65537..65541 3 100%
diff --git a/third_party/sqlite/ext/README.txt b/third_party/sqlite/ext/README.txt
new file mode 100755
index 0000000..009495f
--- /dev/null
+++ b/third_party/sqlite/ext/README.txt
@@ -0,0 +1,2 @@
+Version loadable extensions to SQLite are found in subfolders
+of this folder.
diff --git a/third_party/sqlite/ext/fts1/README.txt b/third_party/sqlite/ext/fts1/README.txt
new file mode 100755
index 0000000..292b7da
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/README.txt
@@ -0,0 +1,2 @@
+This folder contains source code to the first full-text search
+extension for SQLite.
diff --git a/third_party/sqlite/ext/fts1/ft_hash.c b/third_party/sqlite/ext/fts1/ft_hash.c
new file mode 100755
index 0000000..8b3a706
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/ft_hash.c
@@ -0,0 +1,404 @@
+/*
+** 2001 September 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 is the implementation of generic hash-tables used in SQLite.
+** We've modified it slightly to serve as a standalone hash table
+** implementation for the full-text indexing module.
+*/
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ft_hash.h"
+
+void *malloc_and_zero(int n){
+ void *p = malloc(n);
+ if( p ){
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+**
+** "pNew" is a pointer to the hash table that is to be initialized.
+** keyClass is one of the constants HASH_INT, HASH_POINTER,
+** HASH_BINARY, or HASH_STRING. The value of keyClass
+** determines what kind of key the hash table will use. "copyKey" is
+** true if the hash table should make its own private copy of keys and
+** false if it should just use the supplied pointer. CopyKey only makes
+** sense for HASH_STRING and HASH_BINARY and is ignored
+** for other key classes.
+*/
+void HashInit(Hash *pNew, int keyClass, int copyKey){
+ assert( pNew!=0 );
+ assert( keyClass>=HASH_STRING && keyClass<=HASH_BINARY );
+ pNew->keyClass = keyClass;
+#if 0
+ if( keyClass==HASH_POINTER || keyClass==HASH_INT ) copyKey = 0;
+#endif
+ pNew->copyKey = copyKey;
+ pNew->first = 0;
+ pNew->count = 0;
+ pNew->htsize = 0;
+ pNew->ht = 0;
+ pNew->xMalloc = malloc_and_zero;
+ pNew->xFree = free;
+}
+
+/* Remove all entries from a hash table. Reclaim all memory.
+** Call this routine to delete a hash table or to reset a hash table
+** to the empty state.
+*/
+void HashClear(Hash *pH){
+ HashElem *elem; /* For looping over all elements of the table */
+
+ assert( pH!=0 );
+ elem = pH->first;
+ pH->first = 0;
+ if( pH->ht ) pH->xFree(pH->ht);
+ pH->ht = 0;
+ pH->htsize = 0;
+ while( elem ){
+ HashElem *next_elem = elem->next;
+ if( pH->copyKey && elem->pKey ){
+ pH->xFree(elem->pKey);
+ }
+ pH->xFree(elem);
+ elem = next_elem;
+ }
+ pH->count = 0;
+}
+
+#if 0 /* NOT USED */
+/*
+** Hash and comparison functions when the mode is HASH_INT
+*/
+static int intHash(const void *pKey, int nKey){
+ return nKey ^ (nKey<<8) ^ (nKey>>8);
+}
+static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ return n2 - n1;
+}
+#endif
+
+#if 0 /* NOT USED */
+/*
+** Hash and comparison functions when the mode is HASH_POINTER
+*/
+static int ptrHash(const void *pKey, int nKey){
+ uptr x = Addr(pKey);
+ return x ^ (x<<8) ^ (x>>8);
+}
+static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( pKey1==pKey2 ) return 0;
+ if( pKey1<pKey2 ) return -1;
+ return 1;
+}
+#endif
+
+/*
+** Hash and comparison functions when the mode is HASH_STRING
+*/
+static int strHash(const void *pKey, int nKey){
+ const char *z = (const char *)pKey;
+ int h = 0;
+ if( nKey<=0 ) nKey = (int) strlen(z);
+ while( nKey > 0 ){
+ h = (h<<3) ^ h ^ *z++;
+ nKey--;
+ }
+ return h & 0x7fffffff;
+}
+static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return strncmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is HASH_BINARY
+*/
+static int binHash(const void *pKey, int nKey){
+ int h = 0;
+ const char *z = (const char *)pKey;
+ while( nKey-- > 0 ){
+ h = (h<<3) ^ h ^ *(z++);
+ }
+ return h & 0x7fffffff;
+}
+static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** The C syntax in this function definition may be unfamilar to some
+** programmers, so we provide the following additional explanation:
+**
+** The name of the function is "hashFunction". The function takes a
+** single parameter "keyClass". The return value of hashFunction()
+** is a pointer to another function. Specifically, the return value
+** of hashFunction() is a pointer to a function that takes two parameters
+** with types "const void*" and "int" and returns an "int".
+*/
+static int (*hashFunction(int keyClass))(const void*,int){
+#if 0 /* HASH_INT and HASH_POINTER are never used */
+ switch( keyClass ){
+ case HASH_INT: return &intHash;
+ case HASH_POINTER: return &ptrHash;
+ case HASH_STRING: return &strHash;
+ case HASH_BINARY: return &binHash;;
+ default: break;
+ }
+ return 0;
+#else
+ if( keyClass==HASH_STRING ){
+ return &strHash;
+ }else{
+ assert( keyClass==HASH_BINARY );
+ return &binHash;
+ }
+#endif
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** For help in interpreted the obscure C code in the function definition,
+** see the header comment on the previous function.
+*/
+static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
+#if 0 /* HASH_INT and HASH_POINTER are never used */
+ switch( keyClass ){
+ case HASH_INT: return &intCompare;
+ case HASH_POINTER: return &ptrCompare;
+ case HASH_STRING: return &strCompare;
+ case HASH_BINARY: return &binCompare;
+ default: break;
+ }
+ return 0;
+#else
+ if( keyClass==HASH_STRING ){
+ return &strCompare;
+ }else{
+ assert( keyClass==HASH_BINARY );
+ return &binCompare;
+ }
+#endif
+}
+
+/* Link an element into the hash table
+*/
+static void insertElement(
+ Hash *pH, /* The complete hash table */
+ struct _ht *pEntry, /* The entry into which pNew is inserted */
+ HashElem *pNew /* The element to be inserted */
+){
+ HashElem *pHead; /* First element already in pEntry */
+ pHead = pEntry->chain;
+ if( pHead ){
+ pNew->next = pHead;
+ pNew->prev = pHead->prev;
+ if( pHead->prev ){ pHead->prev->next = pNew; }
+ else { pH->first = pNew; }
+ pHead->prev = pNew;
+ }else{
+ pNew->next = pH->first;
+ if( pH->first ){ pH->first->prev = pNew; }
+ pNew->prev = 0;
+ pH->first = pNew;
+ }
+ pEntry->count++;
+ pEntry->chain = pNew;
+}
+
+
+/* Resize the hash table so that it cantains "new_size" buckets.
+** "new_size" must be a power of 2. The hash table might fail
+** to resize if sqliteMalloc() fails.
+*/
+static void rehash(Hash *pH, int new_size){
+ struct _ht *new_ht; /* The new hash table */
+ HashElem *elem, *next_elem; /* For looping over existing elements */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( (new_size & (new_size-1))==0 );
+ new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
+ if( new_ht==0 ) return;
+ if( pH->ht ) pH->xFree(pH->ht);
+ pH->ht = new_ht;
+ pH->htsize = new_size;
+ xHash = hashFunction(pH->keyClass);
+ for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+ int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+ next_elem = elem->next;
+ insertElement(pH, &new_ht[h], elem);
+ }
+}
+
+/* This function (for internal use only) locates an element in an
+** hash table that matches the given key. The hash for this key has
+** already been computed and is passed as the 4th parameter.
+*/
+static HashElem *findElementGivenHash(
+ const Hash *pH, /* The pH to be searched */
+ const void *pKey, /* The key we are searching for */
+ int nKey,
+ int h /* The hash for this key. */
+){
+ HashElem *elem; /* Used to loop thru the element list */
+ int count; /* Number of elements left to test */
+ int (*xCompare)(const void*,int,const void*,int); /* comparison function */
+
+ if( pH->ht ){
+ struct _ht *pEntry = &pH->ht[h];
+ elem = pEntry->chain;
+ count = pEntry->count;
+ xCompare = compareFunction(pH->keyClass);
+ while( count-- && elem ){
+ if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+ return elem;
+ }
+ elem = elem->next;
+ }
+ }
+ return 0;
+}
+
+/* Remove a single entry from the hash table given a pointer to that
+** element and a hash on the element's key.
+*/
+static void removeElementGivenHash(
+ Hash *pH, /* The pH containing "elem" */
+ HashElem* elem, /* The element to be removed from the pH */
+ int h /* Hash value for the element */
+){
+ struct _ht *pEntry;
+ if( elem->prev ){
+ elem->prev->next = elem->next;
+ }else{
+ pH->first = elem->next;
+ }
+ if( elem->next ){
+ elem->next->prev = elem->prev;
+ }
+ pEntry = &pH->ht[h];
+ if( pEntry->chain==elem ){
+ pEntry->chain = elem->next;
+ }
+ pEntry->count--;
+ if( pEntry->count<=0 ){
+ pEntry->chain = 0;
+ }
+ if( pH->copyKey && elem->pKey ){
+ pH->xFree(elem->pKey);
+ }
+ pH->xFree( elem );
+ pH->count--;
+ if( pH->count<=0 ){
+ assert( pH->first==0 );
+ assert( pH->count==0 );
+ HashClear(pH);
+ }
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey. Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *HashFind(const Hash *pH, const void *pKey, int nKey){
+ int h; /* A hash on key */
+ HashElem *elem; /* The element that matches key */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ if( pH==0 || pH->ht==0 ) return 0;
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ h = (*xHash)(pKey,nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
+ return elem ? elem->data : 0;
+}
+
+/* Insert an element into the hash table pH. The key is pKey,nKey
+** and the data is "data".
+**
+** If no element exists with a matching key, then a new
+** element is created. A copy of the key is made if the copyKey
+** flag is set. NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance. If a malloc fails, then
+** the new data is returned and the hash table is unchanged.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the hash table.
+*/
+void *HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
+ int hraw; /* Raw hash value of the key */
+ int h; /* the hash of the key modulo hash table size */
+ HashElem *elem; /* Used to loop thru the element list */
+ HashElem *new_elem; /* New element added to the pH */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( pH!=0 );
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ hraw = (*xHash)(pKey, nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ elem = findElementGivenHash(pH,pKey,nKey,h);
+ if( elem ){
+ void *old_data = elem->data;
+ if( data==0 ){
+ removeElementGivenHash(pH,elem,h);
+ }else{
+ elem->data = data;
+ }
+ return old_data;
+ }
+ if( data==0 ) return 0;
+ new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
+ if( new_elem==0 ) return data;
+ if( pH->copyKey && pKey!=0 ){
+ new_elem->pKey = pH->xMalloc( nKey );
+ if( new_elem->pKey==0 ){
+ pH->xFree(new_elem);
+ return data;
+ }
+ memcpy((void*)new_elem->pKey, pKey, nKey);
+ }else{
+ new_elem->pKey = (void*)pKey;
+ }
+ new_elem->nKey = nKey;
+ pH->count++;
+ if( pH->htsize==0 ){
+ rehash(pH,8);
+ if( pH->htsize==0 ){
+ pH->count = 0;
+ pH->xFree(new_elem);
+ return data;
+ }
+ }
+ if( pH->count > pH->htsize ){
+ rehash(pH,pH->htsize*2);
+ }
+ assert( pH->htsize>0 );
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ insertElement(pH, &pH->ht[h], new_elem);
+ new_elem->data = data;
+ return 0;
+}
diff --git a/third_party/sqlite/ext/fts1/ft_hash.h b/third_party/sqlite/ext/fts1/ft_hash.h
new file mode 100755
index 0000000..93b6dcf
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/ft_hash.h
@@ -0,0 +1,111 @@
+/*
+** 2001 September 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 is the header file for the generic hash-table implemenation
+** used in SQLite. We've modified it slightly to serve as a standalone
+** hash table implementation for the full-text indexing module.
+**
+*/
+#ifndef _HASH_H_
+#define _HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct Hash Hash;
+typedef struct HashElem HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly. Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct Hash {
+ char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
+ char copyKey; /* True if copy of key made on insert */
+ int count; /* Number of entries in this table */
+ HashElem *first; /* The first element of the array */
+ void *(*xMalloc)(int); /* malloc() function to use */
+ void (*xFree)(void *); /* free() function to use */
+ int htsize; /* Number of buckets in the hash table */
+ struct _ht { /* the hash table */
+ int count; /* Number of entries with this hash */
+ HashElem *chain; /* Pointer to first entry with this hash */
+ } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure. All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct HashElem {
+ HashElem *next, *prev; /* Next and previous elements in the table */
+ void *data; /* Data associated with this element */
+ void *pKey; int nKey; /* Key associated with this element */
+};
+
+/*
+** There are 4 different modes of operation for a hash table:
+**
+** HASH_INT nKey is used as the key and pKey is ignored.
+**
+** HASH_POINTER pKey is used as the key and nKey is ignored.
+**
+** HASH_STRING pKey points to a string that is nKey bytes long
+** (including the null-terminator, if any). Case
+** is respected in comparisons.
+**
+** HASH_BINARY pKey points to binary data nKey bytes long.
+** memcmp() is used to compare keys.
+**
+** A copy of the key is made for HASH_STRING and HASH_BINARY
+** if the copyKey parameter to HashInit is 1.
+*/
+/* #define HASH_INT 1 // NOT USED */
+/* #define HASH_POINTER 2 // NOT USED */
+#define HASH_STRING 3
+#define HASH_BINARY 4
+
+/*
+** Access routines. To delete, insert a NULL pointer.
+*/
+void HashInit(Hash*, int keytype, int copyKey);
+void *HashInsert(Hash*, const void *pKey, int nKey, void *pData);
+void *HashFind(const Hash*, const void *pKey, int nKey);
+void HashClear(Hash*);
+
+/*
+** Macros for looping over all elements of a hash table. The idiom is
+** like this:
+**
+** Hash h;
+** HashElem *p;
+** ...
+** for(p=HashFirst(&h); p; p=HashNext(p)){
+** SomeStructure *pData = HashData(p);
+** // do something with pData
+** }
+*/
+#define HashFirst(H) ((H)->first)
+#define HashNext(E) ((E)->next)
+#define HashData(E) ((E)->data)
+#define HashKey(E) ((E)->pKey)
+#define HashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define HashCount(H) ((H)->count)
+
+#endif /* _HASH_H_ */
diff --git a/third_party/sqlite/ext/fts1/fts1.c b/third_party/sqlite/ext/fts1/fts1.c
new file mode 100755
index 0000000..f067c55
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1.c
@@ -0,0 +1,3341 @@
+/* fts1 has a design flaw which can lead to database corruption (see
+** below). It is recommended not to use it any longer, instead use
+** fts3 (or higher). If you believe that your use of fts1 is safe,
+** add -DSQLITE_ENABLE_BROKEN_FTS1=1 to your CFLAGS.
+*/
+#if (!defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)) \
+ && !defined(SQLITE_ENABLE_BROKEN_FTS1)
+#error fts1 has a design flaw and has been deprecated.
+#endif
+/* The flaw is that fts1 uses the content table's unaliased rowid as
+** the unique docid. fts1 embeds the rowid in the index it builds,
+** and expects the rowid to not change. The SQLite VACUUM operation
+** will renumber such rowids, thereby breaking fts1. If you are using
+** fts1 in a system which has disabled VACUUM, then you can continue
+** to use it safely. Note that PRAGMA auto_vacuum does NOT disable
+** VACUUM, though systems using auto_vacuum are unlikely to invoke
+** VACUUM.
+**
+** fts1 should be safe even across VACUUM if you only insert documents
+** and never delete.
+*/
+
+/* The author disclaims copyright to this source code.
+ *
+ * This is an SQLite module implementing full-text search.
+ */
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS1 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS1 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
+
+#if defined(SQLITE_ENABLE_FTS1) && !defined(SQLITE_CORE)
+# define SQLITE_CORE 1
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts1.h"
+#include "fts1_hash.h"
+#include "fts1_tokenizer.h"
+#include "sqlite3.h"
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+
+
+#if 0
+# define TRACE(A) printf A; fflush(stdout)
+#else
+# define TRACE(A)
+#endif
+
+/* utility functions */
+
+typedef struct StringBuffer {
+ int len; /* length, not including null terminator */
+ int alloced; /* Space allocated for s[] */
+ char *s; /* Content of the string */
+} StringBuffer;
+
+static void initStringBuffer(StringBuffer *sb){
+ sb->len = 0;
+ sb->alloced = 100;
+ sb->s = malloc(100);
+ sb->s[0] = '\0';
+}
+
+static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
+ if( sb->len + nFrom >= sb->alloced ){
+ sb->alloced = sb->len + nFrom + 100;
+ sb->s = realloc(sb->s, sb->alloced+1);
+ if( sb->s==0 ){
+ initStringBuffer(sb);
+ return;
+ }
+ }
+ memcpy(sb->s + sb->len, zFrom, nFrom);
+ sb->len += nFrom;
+ sb->s[sb->len] = 0;
+}
+static void append(StringBuffer *sb, const char *zFrom){
+ nappend(sb, zFrom, strlen(zFrom));
+}
+
+/* We encode variable-length integers in little-endian order using seven bits
+ * per byte as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** and so on.
+*/
+
+/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
+#define VARINT_MAX 10
+
+/* Write a 64-bit variable-length integer to memory starting at p[0].
+ * The length of data written will be between 1 and VARINT_MAX bytes.
+ * The number of bytes written is returned. */
+static int putVarint(char *p, sqlite_int64 v){
+ unsigned char *q = (unsigned char *) p;
+ sqlite_uint64 vu = v;
+ do{
+ *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
+ vu >>= 7;
+ }while( vu!=0 );
+ q[-1] &= 0x7f; /* turn off high bit in final byte */
+ assert( q - (unsigned char *)p <= VARINT_MAX );
+ return (int) (q - (unsigned char *)p);
+}
+
+/* Read a 64-bit variable-length integer from memory starting at p[0].
+ * Return the number of bytes read, or 0 on error.
+ * The value is stored in *v. */
+static int getVarint(const char *p, sqlite_int64 *v){
+ const unsigned char *q = (const unsigned char *) p;
+ sqlite_uint64 x = 0, y = 1;
+ while( (*q & 0x80) == 0x80 ){
+ x += y * (*q++ & 0x7f);
+ y <<= 7;
+ if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
+ assert( 0 );
+ return 0;
+ }
+ }
+ x += y * (*q++);
+ *v = (sqlite_int64) x;
+ return (int) (q - (unsigned char *)p);
+}
+
+static int getVarint32(const char *p, int *pi){
+ sqlite_int64 i;
+ int ret = getVarint(p, &i);
+ *pi = (int) i;
+ assert( *pi==i );
+ return ret;
+}
+
+/*** Document lists ***
+ *
+ * A document list holds a sorted list of varint-encoded document IDs.
+ *
+ * A doclist with type DL_POSITIONS_OFFSETS is stored like this:
+ *
+ * array {
+ * varint docid;
+ * array {
+ * varint position; (delta from previous position plus POS_BASE)
+ * varint startOffset; (delta from previous startOffset)
+ * varint endOffset; (delta from startOffset)
+ * }
+ * }
+ *
+ * Here, array { X } means zero or more occurrences of X, adjacent in memory.
+ *
+ * A position list may hold positions for text in multiple columns. A position
+ * POS_COLUMN is followed by a varint containing the index of the column for
+ * following positions in the list. Any positions appearing before any
+ * occurrences of POS_COLUMN are for column 0.
+ *
+ * A doclist with type DL_POSITIONS is like the above, but holds only docids
+ * and positions without offset information.
+ *
+ * A doclist with type DL_DOCIDS is like the above, but holds only docids
+ * without positions or offset information.
+ *
+ * On disk, every document list has positions and offsets, so we don't bother
+ * to serialize a doclist's type.
+ *
+ * We don't yet delta-encode document IDs; doing so will probably be a
+ * modest win.
+ *
+ * NOTE(shess) I've thought of a slightly (1%) better offset encoding.
+ * After the first offset, estimate the next offset by using the
+ * current token position and the previous token position and offset,
+ * offset to handle some variance. So the estimate would be
+ * (iPosition*w->iStartOffset/w->iPosition-64), which is delta-encoded
+ * as normal. Offsets more than 64 chars from the estimate are
+ * encoded as the delta to the previous start offset + 128. An
+ * additional tiny increment can be gained by using the end offset of
+ * the previous token to make the estimate a tiny bit more precise.
+*/
+
+/* It is not safe to call isspace(), tolower(), or isalnum() on
+** hi-bit-set characters. This is the same solution used in the
+** tokenizer.
+*/
+/* TODO(shess) The snippet-generation code should be using the
+** tokenizer-generated tokens rather than doing its own local
+** tokenization.
+*/
+/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
+static int safe_isspace(char c){
+ return (c&0x80)==0 ? isspace(c) : 0;
+}
+static int safe_tolower(char c){
+ return (c&0x80)==0 ? tolower(c) : c;
+}
+static int safe_isalnum(char c){
+ return (c&0x80)==0 ? isalnum(c) : 0;
+}
+
+typedef enum DocListType {
+ DL_DOCIDS, /* docids only */
+ DL_POSITIONS, /* docids + positions */
+ DL_POSITIONS_OFFSETS /* docids + positions + offsets */
+} DocListType;
+
+/*
+** By default, only positions and not offsets are stored in the doclists.
+** To change this so that offsets are stored too, compile with
+**
+** -DDL_DEFAULT=DL_POSITIONS_OFFSETS
+**
+*/
+#ifndef DL_DEFAULT
+# define DL_DEFAULT DL_POSITIONS
+#endif
+
+typedef struct DocList {
+ char *pData;
+ int nData;
+ DocListType iType;
+ int iLastColumn; /* the last column written */
+ int iLastPos; /* the last position written */
+ int iLastOffset; /* the last start offset written */
+} DocList;
+
+enum {
+ POS_END = 0, /* end of this position list */
+ POS_COLUMN, /* followed by new column number */
+ POS_BASE
+};
+
+/* Initialize a new DocList to hold the given data. */
+static void docListInit(DocList *d, DocListType iType,
+ const char *pData, int nData){
+ d->nData = nData;
+ if( nData>0 ){
+ d->pData = malloc(nData);
+ memcpy(d->pData, pData, nData);
+ } else {
+ d->pData = NULL;
+ }
+ d->iType = iType;
+ d->iLastColumn = 0;
+ d->iLastPos = d->iLastOffset = 0;
+}
+
+/* Create a new dynamically-allocated DocList. */
+static DocList *docListNew(DocListType iType){
+ DocList *d = (DocList *) malloc(sizeof(DocList));
+ docListInit(d, iType, 0, 0);
+ return d;
+}
+
+static void docListDestroy(DocList *d){
+ free(d->pData);
+#ifndef NDEBUG
+ memset(d, 0x55, sizeof(*d));
+#endif
+}
+
+static void docListDelete(DocList *d){
+ docListDestroy(d);
+ free(d);
+}
+
+static char *docListEnd(DocList *d){
+ return d->pData + d->nData;
+}
+
+/* Append a varint to a DocList's data. */
+static void appendVarint(DocList *d, sqlite_int64 i){
+ char c[VARINT_MAX];
+ int n = putVarint(c, i);
+ d->pData = realloc(d->pData, d->nData + n);
+ memcpy(d->pData + d->nData, c, n);
+ d->nData += n;
+}
+
+static void docListAddDocid(DocList *d, sqlite_int64 iDocid){
+ appendVarint(d, iDocid);
+ if( d->iType>=DL_POSITIONS ){
+ appendVarint(d, POS_END); /* initially empty position list */
+ d->iLastColumn = 0;
+ d->iLastPos = d->iLastOffset = 0;
+ }
+}
+
+/* helper function for docListAddPos and docListAddPosOffset */
+static void addPos(DocList *d, int iColumn, int iPos){
+ assert( d->nData>0 );
+ --d->nData; /* remove previous terminator */
+ if( iColumn!=d->iLastColumn ){
+ assert( iColumn>d->iLastColumn );
+ appendVarint(d, POS_COLUMN);
+ appendVarint(d, iColumn);
+ d->iLastColumn = iColumn;
+ d->iLastPos = d->iLastOffset = 0;
+ }
+ assert( iPos>=d->iLastPos );
+ appendVarint(d, iPos-d->iLastPos+POS_BASE);
+ d->iLastPos = iPos;
+}
+
+/* Add a position to the last position list in a doclist. */
+static void docListAddPos(DocList *d, int iColumn, int iPos){
+ assert( d->iType==DL_POSITIONS );
+ addPos(d, iColumn, iPos);
+ appendVarint(d, POS_END); /* add new terminator */
+}
+
+/*
+** Add a position and starting and ending offsets to a doclist.
+**
+** If the doclist is setup to handle only positions, then insert
+** the position only and ignore the offsets.
+*/
+static void docListAddPosOffset(
+ DocList *d, /* Doclist under construction */
+ int iColumn, /* Column the inserted term is part of */
+ int iPos, /* Position of the inserted term */
+ int iStartOffset, /* Starting offset of inserted term */
+ int iEndOffset /* Ending offset of inserted term */
+){
+ assert( d->iType>=DL_POSITIONS );
+ addPos(d, iColumn, iPos);
+ if( d->iType==DL_POSITIONS_OFFSETS ){
+ assert( iStartOffset>=d->iLastOffset );
+ appendVarint(d, iStartOffset-d->iLastOffset);
+ d->iLastOffset = iStartOffset;
+ assert( iEndOffset>=iStartOffset );
+ appendVarint(d, iEndOffset-iStartOffset);
+ }
+ appendVarint(d, POS_END); /* add new terminator */
+}
+
+/*
+** A DocListReader object is a cursor into a doclist. Initialize
+** the cursor to the beginning of the doclist by calling readerInit().
+** Then use routines
+**
+** peekDocid()
+** readDocid()
+** readPosition()
+** skipPositionList()
+** and so forth...
+**
+** to read information out of the doclist. When we reach the end
+** of the doclist, atEnd() returns TRUE.
+*/
+typedef struct DocListReader {
+ DocList *pDoclist; /* The document list we are stepping through */
+ char *p; /* Pointer to next unread byte in the doclist */
+ int iLastColumn;
+ int iLastPos; /* the last position read, or -1 when not in a position list */
+} DocListReader;
+
+/*
+** Initialize the DocListReader r to point to the beginning of pDoclist.
+*/
+static void readerInit(DocListReader *r, DocList *pDoclist){
+ r->pDoclist = pDoclist;
+ if( pDoclist!=NULL ){
+ r->p = pDoclist->pData;
+ }
+ r->iLastColumn = -1;
+ r->iLastPos = -1;
+}
+
+/*
+** Return TRUE if we have reached then end of pReader and there is
+** nothing else left to read.
+*/
+static int atEnd(DocListReader *pReader){
+ return pReader->pDoclist==0 || (pReader->p >= docListEnd(pReader->pDoclist));
+}
+
+/* Peek at the next docid without advancing the read pointer.
+*/
+static sqlite_int64 peekDocid(DocListReader *pReader){
+ sqlite_int64 ret;
+ assert( !atEnd(pReader) );
+ assert( pReader->iLastPos==-1 );
+ getVarint(pReader->p, &ret);
+ return ret;
+}
+
+/* Read the next docid. See also nextDocid().
+*/
+static sqlite_int64 readDocid(DocListReader *pReader){
+ sqlite_int64 ret;
+ assert( !atEnd(pReader) );
+ assert( pReader->iLastPos==-1 );
+ pReader->p += getVarint(pReader->p, &ret);
+ if( pReader->pDoclist->iType>=DL_POSITIONS ){
+ pReader->iLastColumn = 0;
+ pReader->iLastPos = 0;
+ }
+ return ret;
+}
+
+/* Read the next position and column index from a position list.
+ * Returns the position, or -1 at the end of the list. */
+static int readPosition(DocListReader *pReader, int *iColumn){
+ int i;
+ int iType = pReader->pDoclist->iType;
+
+ if( pReader->iLastPos==-1 ){
+ return -1;
+ }
+ assert( !atEnd(pReader) );
+
+ if( iType<DL_POSITIONS ){
+ return -1;
+ }
+ pReader->p += getVarint32(pReader->p, &i);
+ if( i==POS_END ){
+ pReader->iLastColumn = pReader->iLastPos = -1;
+ *iColumn = -1;
+ return -1;
+ }
+ if( i==POS_COLUMN ){
+ pReader->p += getVarint32(pReader->p, &pReader->iLastColumn);
+ pReader->iLastPos = 0;
+ pReader->p += getVarint32(pReader->p, &i);
+ assert( i>=POS_BASE );
+ }
+ pReader->iLastPos += ((int) i)-POS_BASE;
+ if( iType>=DL_POSITIONS_OFFSETS ){
+ /* Skip over offsets, ignoring them for now. */
+ int iStart, iEnd;
+ pReader->p += getVarint32(pReader->p, &iStart);
+ pReader->p += getVarint32(pReader->p, &iEnd);
+ }
+ *iColumn = pReader->iLastColumn;
+ return pReader->iLastPos;
+}
+
+/* Skip past the end of a position list. */
+static void skipPositionList(DocListReader *pReader){
+ DocList *p = pReader->pDoclist;
+ if( p && p->iType>=DL_POSITIONS ){
+ int iColumn;
+ while( readPosition(pReader, &iColumn)!=-1 ){}
+ }
+}
+
+/* Skip over a docid, including its position list if the doclist has
+ * positions. */
+static void skipDocument(DocListReader *pReader){
+ readDocid(pReader);
+ skipPositionList(pReader);
+}
+
+/* Skip past all docids which are less than [iDocid]. Returns 1 if a docid
+ * matching [iDocid] was found. */
+static int skipToDocid(DocListReader *pReader, sqlite_int64 iDocid){
+ sqlite_int64 d = 0;
+ while( !atEnd(pReader) && (d=peekDocid(pReader))<iDocid ){
+ skipDocument(pReader);
+ }
+ return !atEnd(pReader) && d==iDocid;
+}
+
+/* Return the first document in a document list.
+*/
+static sqlite_int64 firstDocid(DocList *d){
+ DocListReader r;
+ readerInit(&r, d);
+ return readDocid(&r);
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** This routine is used for debugging purpose only.
+**
+** Write the content of a doclist to standard output.
+*/
+static void printDoclist(DocList *p){
+ DocListReader r;
+ const char *zSep = "";
+
+ readerInit(&r, p);
+ while( !atEnd(&r) ){
+ sqlite_int64 docid = readDocid(&r);
+ if( docid==0 ){
+ skipPositionList(&r);
+ continue;
+ }
+ printf("%s%lld", zSep, docid);
+ zSep = ",";
+ if( p->iType>=DL_POSITIONS ){
+ int iPos, iCol;
+ const char *zDiv = "";
+ printf("(");
+ while( (iPos = readPosition(&r, &iCol))>=0 ){
+ printf("%s%d:%d", zDiv, iCol, iPos);
+ zDiv = ":";
+ }
+ printf(")");
+ }
+ }
+ printf("\n");
+ fflush(stdout);
+}
+#endif /* SQLITE_DEBUG */
+
+/* Trim the given doclist to contain only positions in column
+ * [iRestrictColumn]. */
+static void docListRestrictColumn(DocList *in, int iRestrictColumn){
+ DocListReader r;
+ DocList out;
+
+ assert( in->iType>=DL_POSITIONS );
+ readerInit(&r, in);
+ docListInit(&out, DL_POSITIONS, NULL, 0);
+
+ while( !atEnd(&r) ){
+ sqlite_int64 iDocid = readDocid(&r);
+ int iPos, iColumn;
+
+ docListAddDocid(&out, iDocid);
+ while( (iPos = readPosition(&r, &iColumn)) != -1 ){
+ if( iColumn==iRestrictColumn ){
+ docListAddPos(&out, iColumn, iPos);
+ }
+ }
+ }
+
+ docListDestroy(in);
+ *in = out;
+}
+
+/* Trim the given doclist by discarding any docids without any remaining
+ * positions. */
+static void docListDiscardEmpty(DocList *in) {
+ DocListReader r;
+ DocList out;
+
+ /* TODO: It would be nice to implement this operation in place; that
+ * could save a significant amount of memory in queries with long doclists. */
+ assert( in->iType>=DL_POSITIONS );
+ readerInit(&r, in);
+ docListInit(&out, DL_POSITIONS, NULL, 0);
+
+ while( !atEnd(&r) ){
+ sqlite_int64 iDocid = readDocid(&r);
+ int match = 0;
+ int iPos, iColumn;
+ while( (iPos = readPosition(&r, &iColumn)) != -1 ){
+ if( !match ){
+ docListAddDocid(&out, iDocid);
+ match = 1;
+ }
+ docListAddPos(&out, iColumn, iPos);
+ }
+ }
+
+ docListDestroy(in);
+ *in = out;
+}
+
+/* Helper function for docListUpdate() and docListAccumulate().
+** Splices a doclist element into the doclist represented by r,
+** leaving r pointing after the newly spliced element.
+*/
+static void docListSpliceElement(DocListReader *r, sqlite_int64 iDocid,
+ const char *pSource, int nSource){
+ DocList *d = r->pDoclist;
+ char *pTarget;
+ int nTarget, found;
+
+ found = skipToDocid(r, iDocid);
+
+ /* Describe slice in d to place pSource/nSource. */
+ pTarget = r->p;
+ if( found ){
+ skipDocument(r);
+ nTarget = r->p-pTarget;
+ }else{
+ nTarget = 0;
+ }
+
+ /* The sense of the following is that there are three possibilities.
+ ** If nTarget==nSource, we should not move any memory nor realloc.
+ ** If nTarget>nSource, trim target and realloc.
+ ** If nTarget<nSource, realloc then expand target.
+ */
+ if( nTarget>nSource ){
+ memmove(pTarget+nSource, pTarget+nTarget, docListEnd(d)-(pTarget+nTarget));
+ }
+ if( nTarget!=nSource ){
+ int iDoclist = pTarget-d->pData;
+ d->pData = realloc(d->pData, d->nData+nSource-nTarget);
+ pTarget = d->pData+iDoclist;
+ }
+ if( nTarget<nSource ){
+ memmove(pTarget+nSource, pTarget+nTarget, docListEnd(d)-(pTarget+nTarget));
+ }
+
+ memcpy(pTarget, pSource, nSource);
+ d->nData += nSource-nTarget;
+ r->p = pTarget+nSource;
+}
+
+/* Insert/update pUpdate into the doclist. */
+static void docListUpdate(DocList *d, DocList *pUpdate){
+ DocListReader reader;
+
+ assert( d!=NULL && pUpdate!=NULL );
+ assert( d->iType==pUpdate->iType);
+
+ readerInit(&reader, d);
+ docListSpliceElement(&reader, firstDocid(pUpdate),
+ pUpdate->pData, pUpdate->nData);
+}
+
+/* Propagate elements from pUpdate to pAcc, overwriting elements with
+** matching docids.
+*/
+static void docListAccumulate(DocList *pAcc, DocList *pUpdate){
+ DocListReader accReader, updateReader;
+
+ /* Handle edge cases where one doclist is empty. */
+ assert( pAcc!=NULL );
+ if( pUpdate==NULL || pUpdate->nData==0 ) return;
+ if( pAcc->nData==0 ){
+ pAcc->pData = malloc(pUpdate->nData);
+ memcpy(pAcc->pData, pUpdate->pData, pUpdate->nData);
+ pAcc->nData = pUpdate->nData;
+ return;
+ }
+
+ readerInit(&accReader, pAcc);
+ readerInit(&updateReader, pUpdate);
+
+ while( !atEnd(&updateReader) ){
+ char *pSource = updateReader.p;
+ sqlite_int64 iDocid = readDocid(&updateReader);
+ skipPositionList(&updateReader);
+ docListSpliceElement(&accReader, iDocid, pSource, updateReader.p-pSource);
+ }
+}
+
+/*
+** Read the next docid off of pIn. Return 0 if we reach the end.
+*
+* TODO: This assumes that docids are never 0, but they may actually be 0 since
+* users can choose docids when inserting into a full-text table. Fix this.
+*/
+static sqlite_int64 nextDocid(DocListReader *pIn){
+ skipPositionList(pIn);
+ return atEnd(pIn) ? 0 : readDocid(pIn);
+}
+
+/*
+** pLeft and pRight are two DocListReaders that are pointing to
+** positions lists of the same document: iDocid.
+**
+** If there are no instances in pLeft or pRight where the position
+** of pLeft is one less than the position of pRight, then this
+** routine adds nothing to pOut.
+**
+** If there are one or more instances where positions from pLeft
+** are exactly one less than positions from pRight, then add a new
+** document record to pOut. If pOut wants to hold positions, then
+** include the positions from pRight that are one more than a
+** position in pLeft. In other words: pRight.iPos==pLeft.iPos+1.
+**
+** pLeft and pRight are left pointing at the next document record.
+*/
+static void mergePosList(
+ DocListReader *pLeft, /* Left position list */
+ DocListReader *pRight, /* Right position list */
+ sqlite_int64 iDocid, /* The docid from pLeft and pRight */
+ DocList *pOut /* Write the merged document record here */
+){
+ int iLeftCol, iLeftPos = readPosition(pLeft, &iLeftCol);
+ int iRightCol, iRightPos = readPosition(pRight, &iRightCol);
+ int match = 0;
+
+ /* Loop until we've reached the end of both position lists. */
+ while( iLeftPos!=-1 && iRightPos!=-1 ){
+ if( iLeftCol==iRightCol && iLeftPos+1==iRightPos ){
+ if( !match ){
+ docListAddDocid(pOut, iDocid);
+ match = 1;
+ }
+ if( pOut->iType>=DL_POSITIONS ){
+ docListAddPos(pOut, iRightCol, iRightPos);
+ }
+ iLeftPos = readPosition(pLeft, &iLeftCol);
+ iRightPos = readPosition(pRight, &iRightCol);
+ }else if( iRightCol<iLeftCol ||
+ (iRightCol==iLeftCol && iRightPos<iLeftPos+1) ){
+ iRightPos = readPosition(pRight, &iRightCol);
+ }else{
+ iLeftPos = readPosition(pLeft, &iLeftCol);
+ }
+ }
+ if( iLeftPos>=0 ) skipPositionList(pLeft);
+ if( iRightPos>=0 ) skipPositionList(pRight);
+}
+
+/* We have two doclists: pLeft and pRight.
+** Write the phrase intersection of these two doclists into pOut.
+**
+** A phrase intersection means that two documents only match
+** if pLeft.iPos+1==pRight.iPos.
+**
+** The output pOut may or may not contain positions. If pOut
+** does contain positions, they are the positions of pRight.
+*/
+static void docListPhraseMerge(
+ DocList *pLeft, /* Doclist resulting from the words on the left */
+ DocList *pRight, /* Doclist for the next word to the right */
+ DocList *pOut /* Write the combined doclist here */
+){
+ DocListReader left, right;
+ sqlite_int64 docidLeft, docidRight;
+
+ readerInit(&left, pLeft);
+ readerInit(&right, pRight);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+
+ while( docidLeft>0 && docidRight>0 ){
+ if( docidLeft<docidRight ){
+ docidLeft = nextDocid(&left);
+ }else if( docidRight<docidLeft ){
+ docidRight = nextDocid(&right);
+ }else{
+ mergePosList(&left, &right, docidLeft, pOut);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+ }
+ }
+}
+
+/* We have two doclists: pLeft and pRight.
+** Write the intersection of these two doclists into pOut.
+** Only docids are matched. Position information is ignored.
+**
+** The output pOut never holds positions.
+*/
+static void docListAndMerge(
+ DocList *pLeft, /* Doclist resulting from the words on the left */
+ DocList *pRight, /* Doclist for the next word to the right */
+ DocList *pOut /* Write the combined doclist here */
+){
+ DocListReader left, right;
+ sqlite_int64 docidLeft, docidRight;
+
+ assert( pOut->iType<DL_POSITIONS );
+
+ readerInit(&left, pLeft);
+ readerInit(&right, pRight);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+
+ while( docidLeft>0 && docidRight>0 ){
+ if( docidLeft<docidRight ){
+ docidLeft = nextDocid(&left);
+ }else if( docidRight<docidLeft ){
+ docidRight = nextDocid(&right);
+ }else{
+ docListAddDocid(pOut, docidLeft);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+ }
+ }
+}
+
+/* We have two doclists: pLeft and pRight.
+** Write the union of these two doclists into pOut.
+** Only docids are matched. Position information is ignored.
+**
+** The output pOut never holds positions.
+*/
+static void docListOrMerge(
+ DocList *pLeft, /* Doclist resulting from the words on the left */
+ DocList *pRight, /* Doclist for the next word to the right */
+ DocList *pOut /* Write the combined doclist here */
+){
+ DocListReader left, right;
+ sqlite_int64 docidLeft, docidRight, priorLeft;
+
+ readerInit(&left, pLeft);
+ readerInit(&right, pRight);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+
+ while( docidLeft>0 && docidRight>0 ){
+ if( docidLeft<=docidRight ){
+ docListAddDocid(pOut, docidLeft);
+ }else{
+ docListAddDocid(pOut, docidRight);
+ }
+ priorLeft = docidLeft;
+ if( docidLeft<=docidRight ){
+ docidLeft = nextDocid(&left);
+ }
+ if( docidRight>0 && docidRight<=priorLeft ){
+ docidRight = nextDocid(&right);
+ }
+ }
+ while( docidLeft>0 ){
+ docListAddDocid(pOut, docidLeft);
+ docidLeft = nextDocid(&left);
+ }
+ while( docidRight>0 ){
+ docListAddDocid(pOut, docidRight);
+ docidRight = nextDocid(&right);
+ }
+}
+
+/* We have two doclists: pLeft and pRight.
+** Write into pOut all documents that occur in pLeft but not
+** in pRight.
+**
+** Only docids are matched. Position information is ignored.
+**
+** The output pOut never holds positions.
+*/
+static void docListExceptMerge(
+ DocList *pLeft, /* Doclist resulting from the words on the left */
+ DocList *pRight, /* Doclist for the next word to the right */
+ DocList *pOut /* Write the combined doclist here */
+){
+ DocListReader left, right;
+ sqlite_int64 docidLeft, docidRight, priorLeft;
+
+ readerInit(&left, pLeft);
+ readerInit(&right, pRight);
+ docidLeft = nextDocid(&left);
+ docidRight = nextDocid(&right);
+
+ while( docidLeft>0 && docidRight>0 ){
+ priorLeft = docidLeft;
+ if( docidLeft<docidRight ){
+ docListAddDocid(pOut, docidLeft);
+ }
+ if( docidLeft<=docidRight ){
+ docidLeft = nextDocid(&left);
+ }
+ if( docidRight>0 && docidRight<=priorLeft ){
+ docidRight = nextDocid(&right);
+ }
+ }
+ while( docidLeft>0 ){
+ docListAddDocid(pOut, docidLeft);
+ docidLeft = nextDocid(&left);
+ }
+}
+
+static char *string_dup_n(const char *s, int n){
+ char *str = malloc(n + 1);
+ memcpy(str, s, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Duplicate a string; the caller must free() the returned string.
+ * (We don't use strdup() since it is not part of the standard C library and
+ * may not be available everywhere.) */
+static char *string_dup(const char *s){
+ return string_dup_n(s, strlen(s));
+}
+
+/* Format a string, replacing each occurrence of the % character with
+ * zDb.zName. This may be more convenient than sqlite_mprintf()
+ * when one string is used repeatedly in a format string.
+ * The caller must free() the returned string. */
+static char *string_format(const char *zFormat,
+ const char *zDb, const char *zName){
+ const char *p;
+ size_t len = 0;
+ size_t nDb = strlen(zDb);
+ size_t nName = strlen(zName);
+ size_t nFullTableName = nDb+1+nName;
+ char *result;
+ char *r;
+
+ /* first compute length needed */
+ for(p = zFormat ; *p ; ++p){
+ len += (*p=='%' ? nFullTableName : 1);
+ }
+ len += 1; /* for null terminator */
+
+ r = result = malloc(len);
+ for(p = zFormat; *p; ++p){
+ if( *p=='%' ){
+ memcpy(r, zDb, nDb);
+ r += nDb;
+ *r++ = '.';
+ memcpy(r, zName, nName);
+ r += nName;
+ } else {
+ *r++ = *p;
+ }
+ }
+ *r++ = '\0';
+ assert( r == result + len );
+ return result;
+}
+
+static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
+ const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
+ int rc;
+ TRACE(("FTS1 sql: %s\n", zCommand));
+ rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
+ free(zCommand);
+ return rc;
+}
+
+static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
+ sqlite3_stmt **ppStmt, const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
+ int rc;
+ TRACE(("FTS1 prepare: %s\n", zCommand));
+ rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
+ free(zCommand);
+ return rc;
+}
+
+/* end utility functions */
+
+/* Forward reference */
+typedef struct fulltext_vtab fulltext_vtab;
+
+/* A single term in a query is represented by an instances of
+** the following structure.
+*/
+typedef struct QueryTerm {
+ short int nPhrase; /* How many following terms are part of the same phrase */
+ short int iPhrase; /* This is the i-th term of a phrase. */
+ short int iColumn; /* Column of the index that must match this term */
+ signed char isOr; /* this term is preceded by "OR" */
+ signed char isNot; /* this term is preceded by "-" */
+ char *pTerm; /* text of the term. '\000' terminated. malloced */
+ int nTerm; /* Number of bytes in pTerm[] */
+} QueryTerm;
+
+
+/* A query string is parsed into a Query structure.
+ *
+ * We could, in theory, allow query strings to be complicated
+ * nested expressions with precedence determined by parentheses.
+ * But none of the major search engines do this. (Perhaps the
+ * feeling is that an parenthesized expression is two complex of
+ * an idea for the average user to grasp.) Taking our lead from
+ * the major search engines, we will allow queries to be a list
+ * of terms (with an implied AND operator) or phrases in double-quotes,
+ * with a single optional "-" before each non-phrase term to designate
+ * negation and an optional OR connector.
+ *
+ * OR binds more tightly than the implied AND, which is what the
+ * major search engines seem to do. So, for example:
+ *
+ * [one two OR three] ==> one AND (two OR three)
+ * [one OR two three] ==> (one OR two) AND three
+ *
+ * A "-" before a term matches all entries that lack that term.
+ * The "-" must occur immediately before the term with in intervening
+ * space. This is how the search engines do it.
+ *
+ * A NOT term cannot be the right-hand operand of an OR. If this
+ * occurs in the query string, the NOT is ignored:
+ *
+ * [one OR -two] ==> one OR two
+ *
+ */
+typedef struct Query {
+ fulltext_vtab *pFts; /* The full text index */
+ int nTerms; /* Number of terms in the query */
+ QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */
+ int nextIsOr; /* Set the isOr flag on the next inserted term */
+ int nextColumn; /* Next word parsed must be in this column */
+ int dfltColumn; /* The default column */
+} Query;
+
+
+/*
+** An instance of the following structure keeps track of generated
+** matching-word offset information and snippets.
+*/
+typedef struct Snippet {
+ int nMatch; /* Total number of matches */
+ int nAlloc; /* Space allocated for aMatch[] */
+ struct snippetMatch { /* One entry for each matching term */
+ char snStatus; /* Status flag for use while constructing snippets */
+ short int iCol; /* The column that contains the match */
+ short int iTerm; /* The index in Query.pTerms[] of the matching term */
+ short int nByte; /* Number of bytes in the term */
+ int iStart; /* The offset to the first character of the term */
+ } *aMatch; /* Points to space obtained from malloc */
+ char *zOffset; /* Text rendering of aMatch[] */
+ int nOffset; /* strlen(zOffset) */
+ char *zSnippet; /* Snippet text */
+ int nSnippet; /* strlen(zSnippet) */
+} Snippet;
+
+
+typedef enum QueryType {
+ QUERY_GENERIC, /* table scan */
+ QUERY_ROWID, /* lookup by rowid */
+ QUERY_FULLTEXT /* QUERY_FULLTEXT + [i] is a full-text search for column i*/
+} QueryType;
+
+/* TODO(shess) CHUNK_MAX controls how much data we allow in segment 0
+** before we start aggregating into larger segments. Lower CHUNK_MAX
+** means that for a given input we have more individual segments per
+** term, which means more rows in the table and a bigger index (due to
+** both more rows and bigger rowids). But it also reduces the average
+** cost of adding new elements to the segment 0 doclist, and it seems
+** to reduce the number of pages read and written during inserts. 256
+** was chosen by measuring insertion times for a certain input (first
+** 10k documents of Enron corpus), though including query performance
+** in the decision may argue for a larger value.
+*/
+#define CHUNK_MAX 256
+
+typedef enum fulltext_statement {
+ CONTENT_INSERT_STMT,
+ CONTENT_SELECT_STMT,
+ CONTENT_UPDATE_STMT,
+ CONTENT_DELETE_STMT,
+
+ TERM_SELECT_STMT,
+ TERM_SELECT_ALL_STMT,
+ TERM_INSERT_STMT,
+ TERM_UPDATE_STMT,
+ TERM_DELETE_STMT,
+
+ MAX_STMT /* Always at end! */
+} fulltext_statement;
+
+/* These must exactly match the enum above. */
+/* TODO(adam): Is there some risk that a statement (in particular,
+** pTermSelectStmt) will be used in two cursors at once, e.g. if a
+** query joins a virtual table to itself? If so perhaps we should
+** move some of these to the cursor object.
+*/
+static const char *const fulltext_zStatement[MAX_STMT] = {
+ /* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */
+ /* CONTENT_SELECT */ "select * from %_content where rowid = ?",
+ /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */
+ /* CONTENT_DELETE */ "delete from %_content where rowid = ?",
+
+ /* TERM_SELECT */
+ "select rowid, doclist from %_term where term = ? and segment = ?",
+ /* TERM_SELECT_ALL */
+ "select doclist from %_term where term = ? order by segment",
+ /* TERM_INSERT */
+ "insert into %_term (rowid, term, segment, doclist) values (?, ?, ?, ?)",
+ /* TERM_UPDATE */ "update %_term set doclist = ? where rowid = ?",
+ /* TERM_DELETE */ "delete from %_term where rowid = ?",
+};
+
+/*
+** A connection to a fulltext index is an instance of the following
+** structure. The xCreate and xConnect methods create an instance
+** of this structure and xDestroy and xDisconnect free that instance.
+** All other methods receive a pointer to the structure as one of their
+** arguments.
+*/
+struct fulltext_vtab {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ sqlite3 *db; /* The database connection */
+ const char *zDb; /* logical database name */
+ const char *zName; /* virtual table name */
+ int nColumn; /* number of columns in virtual table */
+ char **azColumn; /* column names. malloced */
+ char **azContentColumn; /* column names in content table; malloced */
+ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
+
+ /* Precompiled statements which we keep as long as the table is
+ ** open.
+ */
+ sqlite3_stmt *pFulltextStatements[MAX_STMT];
+};
+
+/*
+** When the core wants to do a query, it create a cursor using a
+** call to xOpen. This structure is an instance of a cursor. It
+** is destroyed by xClose.
+*/
+typedef struct fulltext_cursor {
+ sqlite3_vtab_cursor base; /* Base class used by SQLite core */
+ QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */
+ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
+ int eof; /* True if at End Of Results */
+ Query q; /* Parsed query string */
+ Snippet snippet; /* Cached snippet for the current row */
+ int iColumn; /* Column being searched */
+ DocListReader result; /* used when iCursorType == QUERY_FULLTEXT */
+} fulltext_cursor;
+
+static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
+ return (fulltext_vtab *) c->base.pVtab;
+}
+
+static const sqlite3_module fulltextModule; /* forward declaration */
+
+/* Append a list of strings separated by commas to a StringBuffer. */
+static void appendList(StringBuffer *sb, int nString, char **azString){
+ int i;
+ for(i=0; i<nString; ++i){
+ if( i>0 ) append(sb, ", ");
+ append(sb, azString[i]);
+ }
+}
+
+/* Return a dynamically generated statement of the form
+ * insert into %_content (rowid, ...) values (?, ...)
+ */
+static const char *contentInsertStatement(fulltext_vtab *v){
+ StringBuffer sb;
+ int i;
+
+ initStringBuffer(&sb);
+ append(&sb, "insert into %_content (rowid, ");
+ appendList(&sb, v->nColumn, v->azContentColumn);
+ append(&sb, ") values (?");
+ for(i=0; i<v->nColumn; ++i)
+ append(&sb, ", ?");
+ append(&sb, ")");
+ return sb.s;
+}
+
+/* Return a dynamically generated statement of the form
+ * update %_content set [col_0] = ?, [col_1] = ?, ...
+ * where rowid = ?
+ */
+static const char *contentUpdateStatement(fulltext_vtab *v){
+ StringBuffer sb;
+ int i;
+
+ initStringBuffer(&sb);
+ append(&sb, "update %_content set ");
+ for(i=0; i<v->nColumn; ++i) {
+ if( i>0 ){
+ append(&sb, ", ");
+ }
+ append(&sb, v->azContentColumn[i]);
+ append(&sb, " = ?");
+ }
+ append(&sb, " where rowid = ?");
+ return sb.s;
+}
+
+/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
+** If the indicated statement has never been prepared, it is prepared
+** and cached, otherwise the cached version is reset.
+*/
+static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ assert( iStmt<MAX_STMT );
+ if( v->pFulltextStatements[iStmt]==NULL ){
+ const char *zStmt;
+ int rc;
+ switch( iStmt ){
+ case CONTENT_INSERT_STMT:
+ zStmt = contentInsertStatement(v); break;
+ case CONTENT_UPDATE_STMT:
+ zStmt = contentUpdateStatement(v); break;
+ default:
+ zStmt = fulltext_zStatement[iStmt];
+ }
+ rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
+ zStmt);
+ if( zStmt != fulltext_zStatement[iStmt]) free((void *) zStmt);
+ if( rc!=SQLITE_OK ) return rc;
+ } else {
+ int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ *ppStmt = v->pFulltextStatements[iStmt];
+ return SQLITE_OK;
+}
+
+/* Step the indicated statement, handling errors SQLITE_BUSY (by
+** retrying) and SQLITE_SCHEMA (by re-preparing and transferring
+** bindings to the new statement).
+** TODO(adam): We should extend this function so that it can work with
+** statements declared locally, not only globally cached statements.
+*/
+static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ int rc;
+ sqlite3_stmt *s = *ppStmt;
+ assert( iStmt<MAX_STMT );
+ assert( s==v->pFulltextStatements[iStmt] );
+
+ while( (rc=sqlite3_step(s))!=SQLITE_DONE && rc!=SQLITE_ROW ){
+ if( rc==SQLITE_BUSY ) continue;
+ if( rc!=SQLITE_ERROR ) return rc;
+
+ /* If an SQLITE_SCHEMA error has occured, then finalizing this
+ * statement is going to delete the fulltext_vtab structure. If
+ * the statement just executed is in the pFulltextStatements[]
+ * array, it will be finalized twice. So remove it before
+ * calling sqlite3_finalize().
+ */
+ v->pFulltextStatements[iStmt] = NULL;
+ rc = sqlite3_finalize(s);
+ break;
+ }
+ return rc;
+}
+
+/* Like sql_step_statement(), but convert SQLITE_DONE to SQLITE_OK.
+** Useful for statements like UPDATE, where we expect no results.
+*/
+static int sql_single_step_statement(fulltext_vtab *v,
+ fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ int rc = sql_step_statement(v, iStmt, ppStmt);
+ return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
+}
+
+/* insert into %_content (rowid, ...) values ([rowid], [pValues]) */
+static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
+ sqlite3_value **pValues){
+ sqlite3_stmt *s;
+ int i;
+ int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_value(s, 1, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i=0; i<v->nColumn; ++i){
+ rc = sqlite3_bind_value(s, 2+i, pValues[i]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ return sql_single_step_statement(v, CONTENT_INSERT_STMT, &s);
+}
+
+/* update %_content set col0 = pValues[0], col1 = pValues[1], ...
+ * where rowid = [iRowid] */
+static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
+ sqlite_int64 iRowid){
+ sqlite3_stmt *s;
+ int i;
+ int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i=0; i<v->nColumn; ++i){
+ rc = sqlite3_bind_value(s, 1+i, pValues[i]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ rc = sqlite3_bind_int64(s, 1+v->nColumn, iRowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
+}
+
+static void freeStringArray(int nString, const char **pString){
+ int i;
+
+ for (i=0 ; i < nString ; ++i) {
+ if( pString[i]!=NULL ) free((void *) pString[i]);
+ }
+ free((void *) pString);
+}
+
+/* select * from %_content where rowid = [iRow]
+ * The caller must delete the returned array and all strings in it.
+ * null fields will be NULL in the returned array.
+ *
+ * TODO: Perhaps we should return pointer/length strings here for consistency
+ * with other code which uses pointer/length. */
+static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
+ const char ***pValues){
+ sqlite3_stmt *s;
+ const char **values;
+ int i;
+ int rc;
+
+ *pValues = NULL;
+
+ rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
+ if( rc!=SQLITE_ROW ) return rc;
+
+ values = (const char **) malloc(v->nColumn * sizeof(const char *));
+ for(i=0; i<v->nColumn; ++i){
+ if( sqlite3_column_type(s, i)==SQLITE_NULL ){
+ values[i] = NULL;
+ }else{
+ values[i] = string_dup((char*)sqlite3_column_text(s, i));
+ }
+ }
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ){
+ *pValues = values;
+ return SQLITE_OK;
+ }
+
+ freeStringArray(v->nColumn, values);
+ return rc;
+}
+
+/* delete from %_content where rowid = [iRow ] */
+static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, CONTENT_DELETE_STMT, &s);
+}
+
+/* select rowid, doclist from %_term
+ * where term = [pTerm] and segment = [iSegment]
+ * If found, returns SQLITE_ROW; the caller must free the
+ * returned doclist. If no rows found, returns SQLITE_DONE. */
+static int term_select(fulltext_vtab *v, const char *pTerm, int nTerm,
+ int iSegment,
+ sqlite_int64 *rowid, DocList *out){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 1, pTerm, nTerm, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 2, iSegment);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_step_statement(v, TERM_SELECT_STMT, &s);
+ if( rc!=SQLITE_ROW ) return rc;
+
+ *rowid = sqlite3_column_int64(s, 0);
+ docListInit(out, DL_DEFAULT,
+ sqlite3_column_blob(s, 1), sqlite3_column_bytes(s, 1));
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ return rc==SQLITE_DONE ? SQLITE_ROW : rc;
+}
+
+/* Load the segment doclists for term pTerm and merge them in
+** appropriate order into out. Returns SQLITE_OK if successful. If
+** there are no segments for pTerm, successfully returns an empty
+** doclist in out.
+**
+** Each document consists of 1 or more "columns". The number of
+** columns is v->nColumn. If iColumn==v->nColumn, then return
+** position information about all columns. If iColumn<v->nColumn,
+** then only return position information about the iColumn-th column
+** (where the first column is 0).
+*/
+static int term_select_all(
+ fulltext_vtab *v, /* The fulltext index we are querying against */
+ int iColumn, /* If <nColumn, only look at the iColumn-th column */
+ const char *pTerm, /* The term whose posting lists we want */
+ int nTerm, /* Number of bytes in pTerm */
+ DocList *out /* Write the resulting doclist here */
+){
+ DocList doclist;
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_SELECT_ALL_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 1, pTerm, nTerm, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ docListInit(&doclist, DL_DEFAULT, 0, 0);
+
+ /* TODO(shess) Handle schema and busy errors. */
+ while( (rc=sql_step_statement(v, TERM_SELECT_ALL_STMT, &s))==SQLITE_ROW ){
+ DocList old;
+
+ /* TODO(shess) If we processed doclists from oldest to newest, we
+ ** could skip the malloc() involved with the following call. For
+ ** now, I'd rather keep this logic similar to index_insert_term().
+ ** We could additionally drop elements when we see deletes, but
+ ** that would require a distinct version of docListAccumulate().
+ */
+ docListInit(&old, DL_DEFAULT,
+ sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0));
+
+ if( iColumn<v->nColumn ){ /* querying a single column */
+ docListRestrictColumn(&old, iColumn);
+ }
+
+ /* doclist contains the newer data, so write it over old. Then
+ ** steal accumulated result for doclist.
+ */
+ docListAccumulate(&old, &doclist);
+ docListDestroy(&doclist);
+ doclist = old;
+ }
+ if( rc!=SQLITE_DONE ){
+ docListDestroy(&doclist);
+ return rc;
+ }
+
+ docListDiscardEmpty(&doclist);
+ *out = doclist;
+ return SQLITE_OK;
+}
+
+/* insert into %_term (rowid, term, segment, doclist)
+ values ([piRowid], [pTerm], [iSegment], [doclist])
+** Lets sqlite select rowid if piRowid is NULL, else uses *piRowid.
+**
+** NOTE(shess) piRowid is IN, with values of "space of int64" plus
+** null, it is not used to pass data back to the caller.
+*/
+static int term_insert(fulltext_vtab *v, sqlite_int64 *piRowid,
+ const char *pTerm, int nTerm,
+ int iSegment, DocList *doclist){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ if( piRowid==NULL ){
+ rc = sqlite3_bind_null(s, 1);
+ }else{
+ rc = sqlite3_bind_int64(s, 1, *piRowid);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 2, pTerm, nTerm, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 3, iSegment);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 4, doclist->pData, doclist->nData, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_INSERT_STMT, &s);
+}
+
+/* update %_term set doclist = [doclist] where rowid = [rowid] */
+static int term_update(fulltext_vtab *v, sqlite_int64 rowid,
+ DocList *doclist){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_UPDATE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 1, doclist->pData, doclist->nData, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_UPDATE_STMT, &s);
+}
+
+static int term_delete(fulltext_vtab *v, sqlite_int64 rowid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_DELETE_STMT, &s);
+}
+
+/*
+** Free the memory used to contain a fulltext_vtab structure.
+*/
+static void fulltext_vtab_destroy(fulltext_vtab *v){
+ int iStmt, i;
+
+ TRACE(("FTS1 Destroy %p\n", v));
+ for( iStmt=0; iStmt<MAX_STMT; iStmt++ ){
+ if( v->pFulltextStatements[iStmt]!=NULL ){
+ sqlite3_finalize(v->pFulltextStatements[iStmt]);
+ v->pFulltextStatements[iStmt] = NULL;
+ }
+ }
+
+ if( v->pTokenizer!=NULL ){
+ v->pTokenizer->pModule->xDestroy(v->pTokenizer);
+ v->pTokenizer = NULL;
+ }
+
+ free(v->azColumn);
+ for(i = 0; i < v->nColumn; ++i) {
+ sqlite3_free(v->azContentColumn[i]);
+ }
+ free(v->azContentColumn);
+ free(v);
+}
+
+/*
+** Token types for parsing the arguments to xConnect or xCreate.
+*/
+#define TOKEN_EOF 0 /* End of file */
+#define TOKEN_SPACE 1 /* Any kind of whitespace */
+#define TOKEN_ID 2 /* An identifier */
+#define TOKEN_STRING 3 /* A string literal */
+#define TOKEN_PUNCT 4 /* A single punctuation character */
+
+/*
+** If X is a character that can be used in an identifier then
+** IdChar(X) will be true. Otherwise it is false.
+**
+** For ASCII, any character with the high-order bit set is
+** allowed in an identifier. For 7-bit characters,
+** sqlite3IsIdChar[X] must be 1.
+**
+** Ticket #1066. the SQL standard does not allow '$' in the
+** middle of identfiers. But many SQL implementations do.
+** SQLite will allow '$' in identifiers for compatibility.
+** But the feature is undocumented.
+*/
+static const char isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+};
+#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isIdChar[c-0x20]))
+
+
+/*
+** Return the length of the token that begins at z[0].
+** Store the token type in *tokenType before returning.
+*/
+static int getToken(const char *z, int *tokenType){
+ int i, c;
+ switch( *z ){
+ case 0: {
+ *tokenType = TOKEN_EOF;
+ return 0;
+ }
+ case ' ': case '\t': case '\n': case '\f': case '\r': {
+ for(i=1; safe_isspace(z[i]); i++){}
+ *tokenType = TOKEN_SPACE;
+ return i;
+ }
+ case '`':
+ case '\'':
+ case '"': {
+ int delim = z[0];
+ for(i=1; (c=z[i])!=0; i++){
+ if( c==delim ){
+ if( z[i+1]==delim ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ }
+ *tokenType = TOKEN_STRING;
+ return i + (c!=0);
+ }
+ case '[': {
+ for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
+ *tokenType = TOKEN_ID;
+ return i;
+ }
+ default: {
+ if( !IdChar(*z) ){
+ break;
+ }
+ for(i=1; IdChar(z[i]); i++){}
+ *tokenType = TOKEN_ID;
+ return i;
+ }
+ }
+ *tokenType = TOKEN_PUNCT;
+ return 1;
+}
+
+/*
+** A token extracted from a string is an instance of the following
+** structure.
+*/
+typedef struct Token {
+ const char *z; /* Pointer to token text. Not '\000' terminated */
+ short int n; /* Length of the token text in bytes. */
+} Token;
+
+/*
+** Given a input string (which is really one of the argv[] parameters
+** passed into xConnect or xCreate) split the string up into tokens.
+** Return an array of pointers to '\000' terminated strings, one string
+** for each non-whitespace token.
+**
+** The returned array is terminated by a single NULL pointer.
+**
+** Space to hold the returned array is obtained from a single
+** malloc and should be freed by passing the return value to free().
+** The individual strings within the token list are all a part of
+** the single memory allocation and will all be freed at once.
+*/
+static char **tokenizeString(const char *z, int *pnToken){
+ int nToken = 0;
+ Token *aToken = malloc( strlen(z) * sizeof(aToken[0]) );
+ int n = 1;
+ int e, i;
+ int totalSize = 0;
+ char **azToken;
+ char *zCopy;
+ while( n>0 ){
+ n = getToken(z, &e);
+ if( e!=TOKEN_SPACE ){
+ aToken[nToken].z = z;
+ aToken[nToken].n = n;
+ nToken++;
+ totalSize += n+1;
+ }
+ z += n;
+ }
+ azToken = (char**)malloc( nToken*sizeof(char*) + totalSize );
+ zCopy = (char*)&azToken[nToken];
+ nToken--;
+ for(i=0; i<nToken; i++){
+ azToken[i] = zCopy;
+ n = aToken[i].n;
+ memcpy(zCopy, aToken[i].z, n);
+ zCopy[n] = 0;
+ zCopy += n+1;
+ }
+ azToken[nToken] = 0;
+ free(aToken);
+ *pnToken = nToken;
+ return azToken;
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+** "abc" becomes abc
+** 'xyz' becomes xyz
+** [pqr] becomes pqr
+** `mno` becomes mno
+*/
+static void dequoteString(char *z){
+ int quote;
+ int i, j;
+ if( z==0 ) return;
+ quote = z[0];
+ switch( quote ){
+ case '\'': break;
+ case '"': break;
+ case '`': break; /* For MySQL compatibility */
+ case '[': quote = ']'; break; /* For MS SqlServer compatibility */
+ default: return;
+ }
+ for(i=1, j=0; z[i]; i++){
+ if( z[i]==quote ){
+ if( z[i+1]==quote ){
+ z[j++] = quote;
+ i++;
+ }else{
+ z[j++] = 0;
+ break;
+ }
+ }else{
+ z[j++] = z[i];
+ }
+ }
+}
+
+/*
+** The input azIn is a NULL-terminated list of tokens. Remove the first
+** token and all punctuation tokens. Remove the quotes from
+** around string literal tokens.
+**
+** Example:
+**
+** input: tokenize chinese ( 'simplifed' , 'mixed' )
+** output: chinese simplifed mixed
+**
+** Another example:
+**
+** input: delimiters ( '[' , ']' , '...' )
+** output: [ ] ...
+*/
+static void tokenListToIdList(char **azIn){
+ int i, j;
+ if( azIn ){
+ for(i=0, j=-1; azIn[i]; i++){
+ if( safe_isalnum(azIn[i][0]) || azIn[i][1] ){
+ dequoteString(azIn[i]);
+ if( j>=0 ){
+ azIn[j] = azIn[i];
+ }
+ j++;
+ }
+ }
+ azIn[j] = 0;
+ }
+}
+
+
+/*
+** Find the first alphanumeric token in the string zIn. Null-terminate
+** this token. Remove any quotation marks. And return a pointer to
+** the result.
+*/
+static char *firstToken(char *zIn, char **pzTail){
+ int n, ttype;
+ while(1){
+ n = getToken(zIn, &ttype);
+ if( ttype==TOKEN_SPACE ){
+ zIn += n;
+ }else if( ttype==TOKEN_EOF ){
+ *pzTail = zIn;
+ return 0;
+ }else{
+ zIn[n] = 0;
+ *pzTail = &zIn[1];
+ dequoteString(zIn);
+ return zIn;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/* Return true if...
+**
+** * s begins with the string t, ignoring case
+** * s is longer than t
+** * The first character of s beyond t is not a alphanumeric
+**
+** Ignore leading space in *s.
+**
+** To put it another way, return true if the first token of
+** s[] is t[].
+*/
+static int startsWith(const char *s, const char *t){
+ while( safe_isspace(*s) ){ s++; }
+ while( *t ){
+ if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0;
+ }
+ return *s!='_' && !safe_isalnum(*s);
+}
+
+/*
+** An instance of this structure defines the "spec" of a
+** full text index. This structure is populated by parseSpec
+** and use by fulltextConnect and fulltextCreate.
+*/
+typedef struct TableSpec {
+ const char *zDb; /* Logical database name */
+ const char *zName; /* Name of the full-text index */
+ int nColumn; /* Number of columns to be indexed */
+ char **azColumn; /* Original names of columns to be indexed */
+ char **azContentColumn; /* Column names for %_content */
+ char **azTokenizer; /* Name of tokenizer and its arguments */
+} TableSpec;
+
+/*
+** Reclaim all of the memory used by a TableSpec
+*/
+static void clearTableSpec(TableSpec *p) {
+ free(p->azColumn);
+ free(p->azContentColumn);
+ free(p->azTokenizer);
+}
+
+/* Parse a CREATE VIRTUAL TABLE statement, which looks like this:
+ *
+ * CREATE VIRTUAL TABLE email
+ * USING fts1(subject, body, tokenize mytokenizer(myarg))
+ *
+ * We return parsed information in a TableSpec structure.
+ *
+ */
+static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
+ char**pzErr){
+ int i, n;
+ char *z, *zDummy;
+ char **azArg;
+ const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */
+
+ assert( argc>=3 );
+ /* Current interface:
+ ** argv[0] - module name
+ ** argv[1] - database name
+ ** argv[2] - table name
+ ** argv[3..] - columns, optionally followed by tokenizer specification
+ ** and snippet delimiters specification.
+ */
+
+ /* Make a copy of the complete argv[][] array in a single allocation.
+ ** The argv[][] array is read-only and transient. We can write to the
+ ** copy in order to modify things and the copy is persistent.
+ */
+ memset(pSpec, 0, sizeof(*pSpec));
+ for(i=n=0; i<argc; i++){
+ n += strlen(argv[i]) + 1;
+ }
+ azArg = malloc( sizeof(char*)*argc + n );
+ if( azArg==0 ){
+ return SQLITE_NOMEM;
+ }
+ z = (char*)&azArg[argc];
+ for(i=0; i<argc; i++){
+ azArg[i] = z;
+ strcpy(z, argv[i]);
+ z += strlen(z)+1;
+ }
+
+ /* Identify the column names and the tokenizer and delimiter arguments
+ ** in the argv[][] array.
+ */
+ pSpec->zDb = azArg[1];
+ pSpec->zName = azArg[2];
+ pSpec->nColumn = 0;
+ pSpec->azColumn = azArg;
+ zTokenizer = "tokenize simple";
+ for(i=3; i<argc; ++i){
+ if( startsWith(azArg[i],"tokenize") ){
+ zTokenizer = azArg[i];
+ }else{
+ z = azArg[pSpec->nColumn] = firstToken(azArg[i], &zDummy);
+ pSpec->nColumn++;
+ }
+ }
+ if( pSpec->nColumn==0 ){
+ azArg[0] = "content";
+ pSpec->nColumn = 1;
+ }
+
+ /*
+ ** Construct the list of content column names.
+ **
+ ** Each content column name will be of the form cNNAAAA
+ ** where NN is the column number and AAAA is the sanitized
+ ** column name. "sanitized" means that special characters are
+ ** converted to "_". The cNN prefix guarantees that all column
+ ** names are unique.
+ **
+ ** The AAAA suffix is not strictly necessary. It is included
+ ** for the convenience of people who might examine the generated
+ ** %_content table and wonder what the columns are used for.
+ */
+ pSpec->azContentColumn = malloc( pSpec->nColumn * sizeof(char *) );
+ if( pSpec->azContentColumn==0 ){
+ clearTableSpec(pSpec);
+ return SQLITE_NOMEM;
+ }
+ for(i=0; i<pSpec->nColumn; i++){
+ char *p;
+ pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]);
+ for (p = pSpec->azContentColumn[i]; *p ; ++p) {
+ if( !safe_isalnum(*p) ) *p = '_';
+ }
+ }
+
+ /*
+ ** Parse the tokenizer specification string.
+ */
+ pSpec->azTokenizer = tokenizeString(zTokenizer, &n);
+ tokenListToIdList(pSpec->azTokenizer);
+
+ return SQLITE_OK;
+}
+
+/*
+** Generate a CREATE TABLE statement that describes the schema of
+** the virtual table. Return a pointer to this schema string.
+**
+** Space is obtained from sqlite3_mprintf() and should be freed
+** using sqlite3_free().
+*/
+static char *fulltextSchema(
+ int nColumn, /* Number of columns */
+ const char *const* azColumn, /* List of columns */
+ const char *zTableName /* Name of the table */
+){
+ int i;
+ char *zSchema, *zNext;
+ const char *zSep = "(";
+ zSchema = sqlite3_mprintf("CREATE TABLE x");
+ for(i=0; i<nColumn; i++){
+ zNext = sqlite3_mprintf("%s%s%Q", zSchema, zSep, azColumn[i]);
+ sqlite3_free(zSchema);
+ zSchema = zNext;
+ zSep = ",";
+ }
+ zNext = sqlite3_mprintf("%s,%Q)", zSchema, zTableName);
+ sqlite3_free(zSchema);
+ return zNext;
+}
+
+/*
+** Build a new sqlite3_vtab structure that will describe the
+** fulltext index defined by spec.
+*/
+static int constructVtab(
+ sqlite3 *db, /* The SQLite database connection */
+ TableSpec *spec, /* Parsed spec information from parseSpec() */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ int rc;
+ int n;
+ fulltext_vtab *v = 0;
+ const sqlite3_tokenizer_module *m = NULL;
+ char *schema;
+
+ v = (fulltext_vtab *) malloc(sizeof(fulltext_vtab));
+ if( v==0 ) return SQLITE_NOMEM;
+ memset(v, 0, sizeof(*v));
+ /* sqlite will initialize v->base */
+ v->db = db;
+ v->zDb = spec->zDb; /* Freed when azColumn is freed */
+ v->zName = spec->zName; /* Freed when azColumn is freed */
+ v->nColumn = spec->nColumn;
+ v->azContentColumn = spec->azContentColumn;
+ spec->azContentColumn = 0;
+ v->azColumn = spec->azColumn;
+ spec->azColumn = 0;
+
+ if( spec->azTokenizer==0 ){
+ return SQLITE_NOMEM;
+ }
+ /* TODO(shess) For now, add new tokenizers as else if clauses. */
+ if( spec->azTokenizer[0]==0 || startsWith(spec->azTokenizer[0], "simple") ){
+ sqlite3Fts1SimpleTokenizerModule(&m);
+ }else if( startsWith(spec->azTokenizer[0], "porter") ){
+ sqlite3Fts1PorterTokenizerModule(&m);
+ }else{
+ *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]);
+ rc = SQLITE_ERROR;
+ goto err;
+ }
+ for(n=0; spec->azTokenizer[n]; n++){}
+ if( n ){
+ rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1],
+ &v->pTokenizer);
+ }else{
+ rc = m->xCreate(0, 0, &v->pTokenizer);
+ }
+ if( rc!=SQLITE_OK ) goto err;
+ v->pTokenizer->pModule = m;
+
+ /* TODO: verify the existence of backing tables foo_content, foo_term */
+
+ schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn,
+ spec->zName);
+ rc = sqlite3_declare_vtab(db, schema);
+ sqlite3_free(schema);
+ if( rc!=SQLITE_OK ) goto err;
+
+ memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
+
+ *ppVTab = &v->base;
+ TRACE(("FTS1 Connect %p\n", v));
+
+ return rc;
+
+err:
+ fulltext_vtab_destroy(v);
+ return rc;
+}
+
+static int fulltextConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr
+){
+ TableSpec spec;
+ int rc = parseSpec(&spec, argc, argv, pzErr);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = constructVtab(db, &spec, ppVTab, pzErr);
+ clearTableSpec(&spec);
+ return rc;
+}
+
+ /* The %_content table holds the text of each document, with
+ ** the rowid used as the docid.
+ **
+ ** The %_term table maps each term to a document list blob
+ ** containing elements sorted by ascending docid, each element
+ ** encoded as:
+ **
+ ** docid varint-encoded
+ ** token elements:
+ ** position+1 varint-encoded as delta from previous position
+ ** start offset varint-encoded as delta from previous start offset
+ ** end offset varint-encoded as delta from start offset
+ **
+ ** The sentinel position of 0 indicates the end of the token list.
+ **
+ ** Additionally, doclist blobs are chunked into multiple segments,
+ ** using segment to order the segments. New elements are added to
+ ** the segment at segment 0, until it exceeds CHUNK_MAX. Then
+ ** segment 0 is deleted, and the doclist is inserted at segment 1.
+ ** If there is already a doclist at segment 1, the segment 0 doclist
+ ** is merged with it, the segment 1 doclist is deleted, and the
+ ** merged doclist is inserted at segment 2, repeating those
+ ** operations until an insert succeeds.
+ **
+ ** Since this structure doesn't allow us to update elements in place
+ ** in case of deletion or update, these are simply written to
+ ** segment 0 (with an empty token list in case of deletion), with
+ ** docListAccumulate() taking care to retain lower-segment
+ ** information in preference to higher-segment information.
+ */
+ /* TODO(shess) Provide a VACUUM type operation which both removes
+ ** deleted elements which are no longer necessary, and duplicated
+ ** elements. I suspect this will probably not be necessary in
+ ** practice, though.
+ */
+static int fulltextCreate(sqlite3 *db, void *pAux,
+ int argc, const char * const *argv,
+ sqlite3_vtab **ppVTab, char **pzErr){
+ int rc;
+ TableSpec spec;
+ StringBuffer schema;
+ TRACE(("FTS1 Create\n"));
+
+ rc = parseSpec(&spec, argc, argv, pzErr);
+ if( rc!=SQLITE_OK ) return rc;
+
+ initStringBuffer(&schema);
+ append(&schema, "CREATE TABLE %_content(");
+ appendList(&schema, spec.nColumn, spec.azContentColumn);
+ append(&schema, ")");
+ rc = sql_exec(db, spec.zDb, spec.zName, schema.s);
+ free(schema.s);
+ if( rc!=SQLITE_OK ) goto out;
+
+ rc = sql_exec(db, spec.zDb, spec.zName,
+ "create table %_term(term text, segment integer, doclist blob, "
+ "primary key(term, segment));");
+ if( rc!=SQLITE_OK ) goto out;
+
+ rc = constructVtab(db, &spec, ppVTab, pzErr);
+
+out:
+ clearTableSpec(&spec);
+ return rc;
+}
+
+/* Decide how to handle an SQL query. */
+static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+ int i;
+ TRACE(("FTS1 BestIndex\n"));
+
+ for(i=0; i<pInfo->nConstraint; ++i){
+ const struct sqlite3_index_constraint *pConstraint;
+ pConstraint = &pInfo->aConstraint[i];
+ if( pConstraint->usable ) {
+ if( pConstraint->iColumn==-1 &&
+ pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ pInfo->idxNum = QUERY_ROWID; /* lookup by rowid */
+ TRACE(("FTS1 QUERY_ROWID\n"));
+ } else if( pConstraint->iColumn>=0 &&
+ pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ /* full-text search */
+ pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
+ TRACE(("FTS1 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
+ } else continue;
+
+ pInfo->aConstraintUsage[i].argvIndex = 1;
+ pInfo->aConstraintUsage[i].omit = 1;
+
+ /* An arbitrary value for now.
+ * TODO: Perhaps rowid matches should be considered cheaper than
+ * full-text searches. */
+ pInfo->estimatedCost = 1.0;
+
+ return SQLITE_OK;
+ }
+ }
+ pInfo->idxNum = QUERY_GENERIC;
+ return SQLITE_OK;
+}
+
+static int fulltextDisconnect(sqlite3_vtab *pVTab){
+ TRACE(("FTS1 Disconnect %p\n", pVTab));
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextDestroy(sqlite3_vtab *pVTab){
+ fulltext_vtab *v = (fulltext_vtab *)pVTab;
+ int rc;
+
+ TRACE(("FTS1 Destroy %p\n", pVTab));
+ rc = sql_exec(v->db, v->zDb, v->zName,
+ "drop table if exists %_content;"
+ "drop table if exists %_term;"
+ );
+ if( rc!=SQLITE_OK ) return rc;
+
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ fulltext_cursor *c;
+
+ c = (fulltext_cursor *) calloc(sizeof(fulltext_cursor), 1);
+ /* sqlite will initialize c->base */
+ *ppCursor = &c->base;
+ TRACE(("FTS1 Open %p: %p\n", pVTab, c));
+
+ return SQLITE_OK;
+}
+
+
+/* Free all of the dynamically allocated memory held by *q
+*/
+static void queryClear(Query *q){
+ int i;
+ for(i = 0; i < q->nTerms; ++i){
+ free(q->pTerms[i].pTerm);
+ }
+ free(q->pTerms);
+ memset(q, 0, sizeof(*q));
+}
+
+/* Free all of the dynamically allocated memory held by the
+** Snippet
+*/
+static void snippetClear(Snippet *p){
+ free(p->aMatch);
+ free(p->zOffset);
+ free(p->zSnippet);
+ memset(p, 0, sizeof(*p));
+}
+/*
+** Append a single entry to the p->aMatch[] log.
+*/
+static void snippetAppendMatch(
+ Snippet *p, /* Append the entry to this snippet */
+ int iCol, int iTerm, /* The column and query term */
+ int iStart, int nByte /* Offset and size of the match */
+){
+ int i;
+ struct snippetMatch *pMatch;
+ if( p->nMatch+1>=p->nAlloc ){
+ p->nAlloc = p->nAlloc*2 + 10;
+ p->aMatch = realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
+ if( p->aMatch==0 ){
+ p->nMatch = 0;
+ p->nAlloc = 0;
+ return;
+ }
+ }
+ i = p->nMatch++;
+ pMatch = &p->aMatch[i];
+ pMatch->iCol = iCol;
+ pMatch->iTerm = iTerm;
+ pMatch->iStart = iStart;
+ pMatch->nByte = nByte;
+}
+
+/*
+** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
+*/
+#define FTS1_ROTOR_SZ (32)
+#define FTS1_ROTOR_MASK (FTS1_ROTOR_SZ-1)
+
+/*
+** Add entries to pSnippet->aMatch[] for every match that occurs against
+** document zDoc[0..nDoc-1] which is stored in column iColumn.
+*/
+static void snippetOffsetsOfColumn(
+ Query *pQuery,
+ Snippet *pSnippet,
+ int iColumn,
+ const char *zDoc,
+ int nDoc
+){
+ const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */
+ sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */
+ sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */
+ fulltext_vtab *pVtab; /* The full text index */
+ int nColumn; /* Number of columns in the index */
+ const QueryTerm *aTerm; /* Query string terms */
+ int nTerm; /* Number of query string terms */
+ int i, j; /* Loop counters */
+ int rc; /* Return code */
+ unsigned int match, prevMatch; /* Phrase search bitmasks */
+ const char *zToken; /* Next token from the tokenizer */
+ int nToken; /* Size of zToken */
+ int iBegin, iEnd, iPos; /* Offsets of beginning and end */
+
+ /* The following variables keep a circular buffer of the last
+ ** few tokens */
+ unsigned int iRotor = 0; /* Index of current token */
+ int iRotorBegin[FTS1_ROTOR_SZ]; /* Beginning offset of token */
+ int iRotorLen[FTS1_ROTOR_SZ]; /* Length of token */
+
+ pVtab = pQuery->pFts;
+ nColumn = pVtab->nColumn;
+ pTokenizer = pVtab->pTokenizer;
+ pTModule = pTokenizer->pModule;
+ rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor);
+ if( rc ) return;
+ pTCursor->pTokenizer = pTokenizer;
+ aTerm = pQuery->pTerms;
+ nTerm = pQuery->nTerms;
+ if( nTerm>=FTS1_ROTOR_SZ ){
+ nTerm = FTS1_ROTOR_SZ - 1;
+ }
+ prevMatch = 0;
+ while(1){
+ rc = pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
+ if( rc ) break;
+ iRotorBegin[iRotor&FTS1_ROTOR_MASK] = iBegin;
+ iRotorLen[iRotor&FTS1_ROTOR_MASK] = iEnd-iBegin;
+ match = 0;
+ for(i=0; i<nTerm; i++){
+ int iCol;
+ iCol = aTerm[i].iColumn;
+ if( iCol>=0 && iCol<nColumn && iCol!=iColumn ) continue;
+ if( aTerm[i].nTerm!=nToken ) continue;
+ if( memcmp(aTerm[i].pTerm, zToken, nToken) ) continue;
+ if( aTerm[i].iPhrase>1 && (prevMatch & (1<<i))==0 ) continue;
+ match |= 1<<i;
+ if( i==nTerm-1 || aTerm[i+1].iPhrase==1 ){
+ for(j=aTerm[i].iPhrase-1; j>=0; j--){
+ int k = (iRotor-j) & FTS1_ROTOR_MASK;
+ snippetAppendMatch(pSnippet, iColumn, i-j,
+ iRotorBegin[k], iRotorLen[k]);
+ }
+ }
+ }
+ prevMatch = match<<1;
+ iRotor++;
+ }
+ pTModule->xClose(pTCursor);
+}
+
+
+/*
+** Compute all offsets for the current row of the query.
+** If the offsets have already been computed, this routine is a no-op.
+*/
+static void snippetAllOffsets(fulltext_cursor *p){
+ int nColumn;
+ int iColumn, i;
+ int iFirst, iLast;
+ fulltext_vtab *pFts;
+
+ if( p->snippet.nMatch ) return;
+ if( p->q.nTerms==0 ) return;
+ pFts = p->q.pFts;
+ nColumn = pFts->nColumn;
+ iColumn = p->iCursorType - QUERY_FULLTEXT;
+ if( iColumn<0 || iColumn>=nColumn ){
+ iFirst = 0;
+ iLast = nColumn-1;
+ }else{
+ iFirst = iColumn;
+ iLast = iColumn;
+ }
+ for(i=iFirst; i<=iLast; i++){
+ const char *zDoc;
+ int nDoc;
+ zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1);
+ nDoc = sqlite3_column_bytes(p->pStmt, i+1);
+ snippetOffsetsOfColumn(&p->q, &p->snippet, i, zDoc, nDoc);
+ }
+}
+
+/*
+** Convert the information in the aMatch[] array of the snippet
+** into the string zOffset[0..nOffset-1].
+*/
+static void snippetOffsetText(Snippet *p){
+ int i;
+ int cnt = 0;
+ StringBuffer sb;
+ char zBuf[200];
+ if( p->zOffset ) return;
+ initStringBuffer(&sb);
+ for(i=0; i<p->nMatch; i++){
+ struct snippetMatch *pMatch = &p->aMatch[i];
+ zBuf[0] = ' ';
+ sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
+ pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
+ append(&sb, zBuf);
+ cnt++;
+ }
+ p->zOffset = sb.s;
+ p->nOffset = sb.len;
+}
+
+/*
+** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set
+** of matching words some of which might be in zDoc. zDoc is column
+** number iCol.
+**
+** iBreak is suggested spot in zDoc where we could begin or end an
+** excerpt. Return a value similar to iBreak but possibly adjusted
+** to be a little left or right so that the break point is better.
+*/
+static int wordBoundary(
+ int iBreak, /* The suggested break point */
+ const char *zDoc, /* Document text */
+ int nDoc, /* Number of bytes in zDoc[] */
+ struct snippetMatch *aMatch, /* Matching words */
+ int nMatch, /* Number of entries in aMatch[] */
+ int iCol /* The column number for zDoc[] */
+){
+ int i;
+ if( iBreak<=10 ){
+ return 0;
+ }
+ if( iBreak>=nDoc-10 ){
+ return nDoc;
+ }
+ for(i=0; i<nMatch && aMatch[i].iCol<iCol; i++){}
+ while( i<nMatch && aMatch[i].iStart+aMatch[i].nByte<iBreak ){ i++; }
+ if( i<nMatch ){
+ if( aMatch[i].iStart<iBreak+10 ){
+ return aMatch[i].iStart;
+ }
+ if( i>0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
+ return aMatch[i-1].iStart;
+ }
+ }
+ for(i=1; i<=10; i++){
+ if( safe_isspace(zDoc[iBreak-i]) ){
+ return iBreak - i + 1;
+ }
+ if( safe_isspace(zDoc[iBreak+i]) ){
+ return iBreak + i + 1;
+ }
+ }
+ return iBreak;
+}
+
+/*
+** If the StringBuffer does not end in white space, add a single
+** space character to the end.
+*/
+static void appendWhiteSpace(StringBuffer *p){
+ if( p->len==0 ) return;
+ if( safe_isspace(p->s[p->len-1]) ) return;
+ append(p, " ");
+}
+
+/*
+** Remove white space from teh end of the StringBuffer
+*/
+static void trimWhiteSpace(StringBuffer *p){
+ while( p->len>0 && safe_isspace(p->s[p->len-1]) ){
+ p->len--;
+ }
+}
+
+
+
+/*
+** Allowed values for Snippet.aMatch[].snStatus
+*/
+#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */
+#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */
+
+/*
+** Generate the text of a snippet.
+*/
+static void snippetText(
+ fulltext_cursor *pCursor, /* The cursor we need the snippet for */
+ const char *zStartMark, /* Markup to appear before each match */
+ const char *zEndMark, /* Markup to appear after each match */
+ const char *zEllipsis /* Ellipsis mark */
+){
+ int i, j;
+ struct snippetMatch *aMatch;
+ int nMatch;
+ int nDesired;
+ StringBuffer sb;
+ int tailCol;
+ int tailOffset;
+ int iCol;
+ int nDoc;
+ const char *zDoc;
+ int iStart, iEnd;
+ int tailEllipsis = 0;
+ int iMatch;
+
+
+ free(pCursor->snippet.zSnippet);
+ pCursor->snippet.zSnippet = 0;
+ aMatch = pCursor->snippet.aMatch;
+ nMatch = pCursor->snippet.nMatch;
+ initStringBuffer(&sb);
+
+ for(i=0; i<nMatch; i++){
+ aMatch[i].snStatus = SNIPPET_IGNORE;
+ }
+ nDesired = 0;
+ for(i=0; i<pCursor->q.nTerms; i++){
+ for(j=0; j<nMatch; j++){
+ if( aMatch[j].iTerm==i ){
+ aMatch[j].snStatus = SNIPPET_DESIRED;
+ nDesired++;
+ break;
+ }
+ }
+ }
+
+ iMatch = 0;
+ tailCol = -1;
+ tailOffset = 0;
+ for(i=0; i<nMatch && nDesired>0; i++){
+ if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue;
+ nDesired--;
+ iCol = aMatch[i].iCol;
+ zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1);
+ nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1);
+ iStart = aMatch[i].iStart - 40;
+ iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol);
+ if( iStart<=10 ){
+ iStart = 0;
+ }
+ if( iCol==tailCol && iStart<=tailOffset+20 ){
+ iStart = tailOffset;
+ }
+ if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
+ trimWhiteSpace(&sb);
+ appendWhiteSpace(&sb);
+ append(&sb, zEllipsis);
+ appendWhiteSpace(&sb);
+ }
+ iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
+ iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
+ if( iEnd>=nDoc-10 ){
+ iEnd = nDoc;
+ tailEllipsis = 0;
+ }else{
+ tailEllipsis = 1;
+ }
+ while( iMatch<nMatch && aMatch[iMatch].iCol<iCol ){ iMatch++; }
+ while( iStart<iEnd ){
+ while( iMatch<nMatch && aMatch[iMatch].iStart<iStart
+ && aMatch[iMatch].iCol<=iCol ){
+ iMatch++;
+ }
+ if( iMatch<nMatch && aMatch[iMatch].iStart<iEnd
+ && aMatch[iMatch].iCol==iCol ){
+ nappend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart);
+ iStart = aMatch[iMatch].iStart;
+ append(&sb, zStartMark);
+ nappend(&sb, &zDoc[iStart], aMatch[iMatch].nByte);
+ append(&sb, zEndMark);
+ iStart += aMatch[iMatch].nByte;
+ for(j=iMatch+1; j<nMatch; j++){
+ if( aMatch[j].iTerm==aMatch[iMatch].iTerm
+ && aMatch[j].snStatus==SNIPPET_DESIRED ){
+ nDesired--;
+ aMatch[j].snStatus = SNIPPET_IGNORE;
+ }
+ }
+ }else{
+ nappend(&sb, &zDoc[iStart], iEnd - iStart);
+ iStart = iEnd;
+ }
+ }
+ tailCol = iCol;
+ tailOffset = iEnd;
+ }
+ trimWhiteSpace(&sb);
+ if( tailEllipsis ){
+ appendWhiteSpace(&sb);
+ append(&sb, zEllipsis);
+ }
+ pCursor->snippet.zSnippet = sb.s;
+ pCursor->snippet.nSnippet = sb.len;
+}
+
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fulltextClose(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ TRACE(("FTS1 Close %p\n", c));
+ sqlite3_finalize(c->pStmt);
+ queryClear(&c->q);
+ snippetClear(&c->snippet);
+ if( c->result.pDoclist!=NULL ){
+ docListDelete(c->result.pDoclist);
+ }
+ free(c);
+ return SQLITE_OK;
+}
+
+static int fulltextNext(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ sqlite_int64 iDocid;
+ int rc;
+
+ TRACE(("FTS1 Next %p\n", pCursor));
+ snippetClear(&c->snippet);
+ if( c->iCursorType < QUERY_FULLTEXT ){
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ switch( rc ){
+ case SQLITE_ROW:
+ c->eof = 0;
+ return SQLITE_OK;
+ case SQLITE_DONE:
+ c->eof = 1;
+ return SQLITE_OK;
+ default:
+ c->eof = 1;
+ return rc;
+ }
+ } else { /* full-text query */
+ rc = sqlite3_reset(c->pStmt);
+ if( rc!=SQLITE_OK ) return rc;
+
+ iDocid = nextDocid(&c->result);
+ if( iDocid==0 ){
+ c->eof = 1;
+ return SQLITE_OK;
+ }
+ rc = sqlite3_bind_int64(c->pStmt, 1, iDocid);
+ if( rc!=SQLITE_OK ) return rc;
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ if( rc==SQLITE_ROW ){ /* the case we expect */
+ c->eof = 0;
+ return SQLITE_OK;
+ }
+ /* an error occurred; abort */
+ return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+ }
+}
+
+
+/* Return a DocList corresponding to the query term *pTerm. If *pTerm
+** is the first term of a phrase query, go ahead and evaluate the phrase
+** query and return the doclist for the entire phrase query.
+**
+** The result is stored in pTerm->doclist.
+*/
+static int docListOfTerm(
+ fulltext_vtab *v, /* The full text index */
+ int iColumn, /* column to restrict to. No restrition if >=nColumn */
+ QueryTerm *pQTerm, /* Term we are looking for, or 1st term of a phrase */
+ DocList **ppResult /* Write the result here */
+){
+ DocList *pLeft, *pRight, *pNew;
+ int i, rc;
+
+ pLeft = docListNew(DL_POSITIONS);
+ rc = term_select_all(v, iColumn, pQTerm->pTerm, pQTerm->nTerm, pLeft);
+ if( rc ){
+ docListDelete(pLeft);
+ return rc;
+ }
+ for(i=1; i<=pQTerm->nPhrase; i++){
+ pRight = docListNew(DL_POSITIONS);
+ rc = term_select_all(v, iColumn, pQTerm[i].pTerm, pQTerm[i].nTerm, pRight);
+ if( rc ){
+ docListDelete(pLeft);
+ return rc;
+ }
+ pNew = docListNew(i<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS);
+ docListPhraseMerge(pLeft, pRight, pNew);
+ docListDelete(pLeft);
+ docListDelete(pRight);
+ pLeft = pNew;
+ }
+ *ppResult = pLeft;
+ return SQLITE_OK;
+}
+
+/* Add a new term pTerm[0..nTerm-1] to the query *q.
+*/
+static void queryAdd(Query *q, const char *pTerm, int nTerm){
+ QueryTerm *t;
+ ++q->nTerms;
+ q->pTerms = realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0]));
+ if( q->pTerms==0 ){
+ q->nTerms = 0;
+ return;
+ }
+ t = &q->pTerms[q->nTerms - 1];
+ memset(t, 0, sizeof(*t));
+ t->pTerm = malloc(nTerm+1);
+ memcpy(t->pTerm, pTerm, nTerm);
+ t->pTerm[nTerm] = 0;
+ t->nTerm = nTerm;
+ t->isOr = q->nextIsOr;
+ q->nextIsOr = 0;
+ t->iColumn = q->nextColumn;
+ q->nextColumn = q->dfltColumn;
+}
+
+/*
+** Check to see if the string zToken[0...nToken-1] matches any
+** column name in the virtual table. If it does,
+** return the zero-indexed column number. If not, return -1.
+*/
+static int checkColumnSpecifier(
+ fulltext_vtab *pVtab, /* The virtual table */
+ const char *zToken, /* Text of the token */
+ int nToken /* Number of characters in the token */
+){
+ int i;
+ for(i=0; i<pVtab->nColumn; i++){
+ if( memcmp(pVtab->azColumn[i], zToken, nToken)==0
+ && pVtab->azColumn[i][nToken]==0 ){
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+** Parse the text at pSegment[0..nSegment-1]. Add additional terms
+** to the query being assemblied in pQuery.
+**
+** inPhrase is true if pSegment[0..nSegement-1] is contained within
+** double-quotes. If inPhrase is true, then the first term
+** is marked with the number of terms in the phrase less one and
+** OR and "-" syntax is ignored. If inPhrase is false, then every
+** term found is marked with nPhrase=0 and OR and "-" syntax is significant.
+*/
+static int tokenizeSegment(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer to use */
+ const char *pSegment, int nSegment, /* Query expression being parsed */
+ int inPhrase, /* True if within "..." */
+ Query *pQuery /* Append results here */
+){
+ const sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
+ sqlite3_tokenizer_cursor *pCursor;
+ int firstIndex = pQuery->nTerms;
+ int iCol;
+ int nTerm = 1;
+
+ int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+ pCursor->pTokenizer = pTokenizer;
+
+ while( 1 ){
+ const char *pToken;
+ int nToken, iBegin, iEnd, iPos;
+
+ rc = pModule->xNext(pCursor,
+ &pToken, &nToken,
+ &iBegin, &iEnd, &iPos);
+ if( rc!=SQLITE_OK ) break;
+ if( !inPhrase &&
+ pSegment[iEnd]==':' &&
+ (iCol = checkColumnSpecifier(pQuery->pFts, pToken, nToken))>=0 ){
+ pQuery->nextColumn = iCol;
+ continue;
+ }
+ if( !inPhrase && pQuery->nTerms>0 && nToken==2
+ && pSegment[iBegin]=='O' && pSegment[iBegin+1]=='R' ){
+ pQuery->nextIsOr = 1;
+ continue;
+ }
+ queryAdd(pQuery, pToken, nToken);
+ if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
+ pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
+ }
+ pQuery->pTerms[pQuery->nTerms-1].iPhrase = nTerm;
+ if( inPhrase ){
+ nTerm++;
+ }
+ }
+
+ if( inPhrase && pQuery->nTerms>firstIndex ){
+ pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
+ }
+
+ return pModule->xClose(pCursor);
+}
+
+/* Parse a query string, yielding a Query object pQuery.
+**
+** The calling function will need to queryClear() to clean up
+** the dynamically allocated memory held by pQuery.
+*/
+static int parseQuery(
+ fulltext_vtab *v, /* The fulltext index */
+ const char *zInput, /* Input text of the query string */
+ int nInput, /* Size of the input text */
+ int dfltColumn, /* Default column of the index to match against */
+ Query *pQuery /* Write the parse results here. */
+){
+ int iInput, inPhrase = 0;
+
+ if( zInput==0 ) nInput = 0;
+ if( nInput<0 ) nInput = strlen(zInput);
+ pQuery->nTerms = 0;
+ pQuery->pTerms = NULL;
+ pQuery->nextIsOr = 0;
+ pQuery->nextColumn = dfltColumn;
+ pQuery->dfltColumn = dfltColumn;
+ pQuery->pFts = v;
+
+ for(iInput=0; iInput<nInput; ++iInput){
+ int i;
+ for(i=iInput; i<nInput && zInput[i]!='"'; ++i){}
+ if( i>iInput ){
+ tokenizeSegment(v->pTokenizer, zInput+iInput, i-iInput, inPhrase,
+ pQuery);
+ }
+ iInput = i;
+ if( i<nInput ){
+ assert( zInput[i]=='"' );
+ inPhrase = !inPhrase;
+ }
+ }
+
+ if( inPhrase ){
+ /* unmatched quote */
+ queryClear(pQuery);
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/* Perform a full-text query using the search expression in
+** zInput[0..nInput-1]. Return a list of matching documents
+** in pResult.
+**
+** Queries must match column iColumn. Or if iColumn>=nColumn
+** they are allowed to match against any column.
+*/
+static int fulltextQuery(
+ fulltext_vtab *v, /* The full text index */
+ int iColumn, /* Match against this column by default */
+ const char *zInput, /* The query string */
+ int nInput, /* Number of bytes in zInput[] */
+ DocList **pResult, /* Write the result doclist here */
+ Query *pQuery /* Put parsed query string here */
+){
+ int i, iNext, rc;
+ DocList *pLeft = NULL;
+ DocList *pRight, *pNew, *pOr;
+ int nNot = 0;
+ QueryTerm *aTerm;
+
+ rc = parseQuery(v, zInput, nInput, iColumn, pQuery);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Merge AND terms. */
+ aTerm = pQuery->pTerms;
+ for(i = 0; i<pQuery->nTerms; i=iNext){
+ if( aTerm[i].isNot ){
+ /* Handle all NOT terms in a separate pass */
+ nNot++;
+ iNext = i + aTerm[i].nPhrase+1;
+ continue;
+ }
+ iNext = i + aTerm[i].nPhrase + 1;
+ rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &pRight);
+ if( rc ){
+ queryClear(pQuery);
+ return rc;
+ }
+ while( iNext<pQuery->nTerms && aTerm[iNext].isOr ){
+ rc = docListOfTerm(v, aTerm[iNext].iColumn, &aTerm[iNext], &pOr);
+ iNext += aTerm[iNext].nPhrase + 1;
+ if( rc ){
+ queryClear(pQuery);
+ return rc;
+ }
+ pNew = docListNew(DL_DOCIDS);
+ docListOrMerge(pRight, pOr, pNew);
+ docListDelete(pRight);
+ docListDelete(pOr);
+ pRight = pNew;
+ }
+ if( pLeft==0 ){
+ pLeft = pRight;
+ }else{
+ pNew = docListNew(DL_DOCIDS);
+ docListAndMerge(pLeft, pRight, pNew);
+ docListDelete(pRight);
+ docListDelete(pLeft);
+ pLeft = pNew;
+ }
+ }
+
+ if( nNot && pLeft==0 ){
+ /* We do not yet know how to handle a query of only NOT terms */
+ return SQLITE_ERROR;
+ }
+
+ /* Do the EXCEPT terms */
+ for(i=0; i<pQuery->nTerms; i += aTerm[i].nPhrase + 1){
+ if( !aTerm[i].isNot ) continue;
+ rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &pRight);
+ if( rc ){
+ queryClear(pQuery);
+ docListDelete(pLeft);
+ return rc;
+ }
+ pNew = docListNew(DL_DOCIDS);
+ docListExceptMerge(pLeft, pRight, pNew);
+ docListDelete(pRight);
+ docListDelete(pLeft);
+ pLeft = pNew;
+ }
+
+ *pResult = pLeft;
+ return rc;
+}
+
+/*
+** This is the xFilter interface for the virtual table. See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** If idxNum==QUERY_GENERIC then do a full table scan against
+** the %_content table.
+**
+** If idxNum==QUERY_ROWID then do a rowid lookup for a single entry
+** in the %_content table.
+**
+** If idxNum>=QUERY_FULLTEXT then use the full text index. The
+** column on the left-hand side of the MATCH operator is column
+** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand
+** side of the MATCH operator.
+*/
+/* TODO(shess) Upgrade the cursor initialization and destruction to
+** account for fulltextFilter() being called multiple times on the
+** same cursor. The current solution is very fragile. Apply fix to
+** fts2 as appropriate.
+*/
+static int fulltextFilter(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, const char *idxStr, /* Which indexing scheme to use */
+ int argc, sqlite3_value **argv /* Arguments for the indexing scheme */
+){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ fulltext_vtab *v = cursor_vtab(c);
+ int rc;
+ char *zSql;
+
+ TRACE(("FTS1 Filter %p\n",pCursor));
+
+ zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
+ idxNum==QUERY_GENERIC ? "" : "where rowid=?");
+ sqlite3_finalize(c->pStmt);
+ rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) return rc;
+
+ c->iCursorType = idxNum;
+ switch( idxNum ){
+ case QUERY_GENERIC:
+ break;
+
+ case QUERY_ROWID:
+ rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
+ if( rc!=SQLITE_OK ) return rc;
+ break;
+
+ default: /* full-text search */
+ {
+ const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
+ DocList *pResult;
+ assert( idxNum<=QUERY_FULLTEXT+v->nColumn);
+ assert( argc==1 );
+ queryClear(&c->q);
+ rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &pResult, &c->q);
+ if( rc!=SQLITE_OK ) return rc;
+ if( c->result.pDoclist!=NULL ) docListDelete(c->result.pDoclist);
+ readerInit(&c->result, pResult);
+ break;
+ }
+ }
+
+ return fulltextNext(pCursor);
+}
+
+/* This is the xEof method of the virtual table. The SQLite core
+** calls this routine to find out if it has reached the end of
+** a query's results set.
+*/
+static int fulltextEof(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ return c->eof;
+}
+
+/* This is the xColumn method of the virtual table. The SQLite
+** core calls this method during a query when it needs the value
+** of a column from the virtual table. This method needs to use
+** one of the sqlite3_result_*() routines to store the requested
+** value back in the pContext.
+*/
+static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *pContext, int idxCol){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ fulltext_vtab *v = cursor_vtab(c);
+
+ if( idxCol<v->nColumn ){
+ sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
+ sqlite3_result_value(pContext, pVal);
+ }else if( idxCol==v->nColumn ){
+ /* The extra column whose name is the same as the table.
+ ** Return a blob which is a pointer to the cursor
+ */
+ sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
+ }
+ return SQLITE_OK;
+}
+
+/* This is the xRowid method. The SQLite core calls this routine to
+** retrive the rowid for the current row of the result set. The
+** rowid should be written to *pRowid.
+*/
+static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+
+ *pRowid = sqlite3_column_int64(c->pStmt, 0);
+ return SQLITE_OK;
+}
+
+/* Add all terms in [zText] to the given hash table. If [iColumn] > 0,
+ * we also store positions and offsets in the hash table using the given
+ * column number. */
+static int buildTerms(fulltext_vtab *v, fts1Hash *terms, sqlite_int64 iDocid,
+ const char *zText, int iColumn){
+ sqlite3_tokenizer *pTokenizer = v->pTokenizer;
+ sqlite3_tokenizer_cursor *pCursor;
+ const char *pToken;
+ int nTokenBytes;
+ int iStartOffset, iEndOffset, iPosition;
+ int rc;
+
+ rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pCursor->pTokenizer = pTokenizer;
+ while( SQLITE_OK==pTokenizer->pModule->xNext(pCursor,
+ &pToken, &nTokenBytes,
+ &iStartOffset, &iEndOffset,
+ &iPosition) ){
+ DocList *p;
+
+ /* Positions can't be negative; we use -1 as a terminator internally. */
+ if( iPosition<0 ){
+ pTokenizer->pModule->xClose(pCursor);
+ return SQLITE_ERROR;
+ }
+
+ p = fts1HashFind(terms, pToken, nTokenBytes);
+ if( p==NULL ){
+ p = docListNew(DL_DEFAULT);
+ docListAddDocid(p, iDocid);
+ fts1HashInsert(terms, pToken, nTokenBytes, p);
+ }
+ if( iColumn>=0 ){
+ docListAddPosOffset(p, iColumn, iPosition, iStartOffset, iEndOffset);
+ }
+ }
+
+ /* TODO(shess) Check return? Should this be able to cause errors at
+ ** this point? Actually, same question about sqlite3_finalize(),
+ ** though one could argue that failure there means that the data is
+ ** not durable. *ponder*
+ */
+ pTokenizer->pModule->xClose(pCursor);
+ return rc;
+}
+
+/* Update the %_terms table to map the term [pTerm] to the given rowid. */
+static int index_insert_term(fulltext_vtab *v, const char *pTerm, int nTerm,
+ DocList *d){
+ sqlite_int64 iIndexRow;
+ DocList doclist;
+ int iSegment = 0, rc;
+
+ rc = term_select(v, pTerm, nTerm, iSegment, &iIndexRow, &doclist);
+ if( rc==SQLITE_DONE ){
+ docListInit(&doclist, DL_DEFAULT, 0, 0);
+ docListUpdate(&doclist, d);
+ /* TODO(shess) Consider length(doclist)>CHUNK_MAX? */
+ rc = term_insert(v, NULL, pTerm, nTerm, iSegment, &doclist);
+ goto err;
+ }
+ if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
+
+ docListUpdate(&doclist, d);
+ if( doclist.nData<=CHUNK_MAX ){
+ rc = term_update(v, iIndexRow, &doclist);
+ goto err;
+ }
+
+ /* Doclist doesn't fit, delete what's there, and accumulate
+ ** forward.
+ */
+ rc = term_delete(v, iIndexRow);
+ if( rc!=SQLITE_OK ) goto err;
+
+ /* Try to insert the doclist into a higher segment bucket. On
+ ** failure, accumulate existing doclist with the doclist from that
+ ** bucket, and put results in the next bucket.
+ */
+ iSegment++;
+ while( (rc=term_insert(v, &iIndexRow, pTerm, nTerm, iSegment,
+ &doclist))!=SQLITE_OK ){
+ sqlite_int64 iSegmentRow;
+ DocList old;
+ int rc2;
+
+ /* Retain old error in case the term_insert() error was really an
+ ** error rather than a bounced insert.
+ */
+ rc2 = term_select(v, pTerm, nTerm, iSegment, &iSegmentRow, &old);
+ if( rc2!=SQLITE_ROW ) goto err;
+
+ rc = term_delete(v, iSegmentRow);
+ if( rc!=SQLITE_OK ) goto err;
+
+ /* Reusing lowest-number deleted row keeps the index smaller. */
+ if( iSegmentRow<iIndexRow ) iIndexRow = iSegmentRow;
+
+ /* doclist contains the newer data, so accumulate it over old.
+ ** Then steal accumulated data for doclist.
+ */
+ docListAccumulate(&old, &doclist);
+ docListDestroy(&doclist);
+ doclist = old;
+
+ iSegment++;
+ }
+
+ err:
+ docListDestroy(&doclist);
+ return rc;
+}
+
+/* Add doclists for all terms in [pValues] to the hash table [terms]. */
+static int insertTerms(fulltext_vtab *v, fts1Hash *terms, sqlite_int64 iRowid,
+ sqlite3_value **pValues){
+ int i;
+ for(i = 0; i < v->nColumn ; ++i){
+ char *zText = (char*)sqlite3_value_text(pValues[i]);
+ int rc = buildTerms(v, terms, iRowid, zText, i);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ return SQLITE_OK;
+}
+
+/* Add empty doclists for all terms in the given row's content to the hash
+ * table [pTerms]. */
+static int deleteTerms(fulltext_vtab *v, fts1Hash *pTerms, sqlite_int64 iRowid){
+ const char **pValues;
+ int i;
+
+ int rc = content_select(v, iRowid, &pValues);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i = 0 ; i < v->nColumn; ++i) {
+ rc = buildTerms(v, pTerms, iRowid, pValues[i], -1);
+ if( rc!=SQLITE_OK ) break;
+ }
+
+ freeStringArray(v->nColumn, pValues);
+ return SQLITE_OK;
+}
+
+/* Insert a row into the %_content table; set *piRowid to be the ID of the
+ * new row. Fill [pTerms] with new doclists for the %_term table. */
+static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestRowid,
+ sqlite3_value **pValues,
+ sqlite_int64 *piRowid, fts1Hash *pTerms){
+ int rc;
+
+ rc = content_insert(v, pRequestRowid, pValues); /* execute an SQL INSERT */
+ if( rc!=SQLITE_OK ) return rc;
+ *piRowid = sqlite3_last_insert_rowid(v->db);
+ return insertTerms(v, pTerms, *piRowid, pValues);
+}
+
+/* Delete a row from the %_content table; fill [pTerms] with empty doclists
+ * to be written to the %_term table. */
+static int index_delete(fulltext_vtab *v, sqlite_int64 iRow, fts1Hash *pTerms){
+ int rc = deleteTerms(v, pTerms, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+ return content_delete(v, iRow); /* execute an SQL DELETE */
+}
+
+/* Update a row in the %_content table; fill [pTerms] with new doclists for the
+ * %_term table. */
+static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
+ sqlite3_value **pValues, fts1Hash *pTerms){
+ /* Generate an empty doclist for each term that previously appeared in this
+ * row. */
+ int rc = deleteTerms(v, pTerms, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Now add positions for terms which appear in the updated row. */
+ return insertTerms(v, pTerms, iRow, pValues);
+}
+
+/* This function implements the xUpdate callback; it is the top-level entry
+ * point for inserting, deleting or updating a row in a full-text table. */
+static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
+ sqlite_int64 *pRowid){
+ fulltext_vtab *v = (fulltext_vtab *) pVtab;
+ fts1Hash terms; /* maps term string -> PosList */
+ int rc;
+ fts1HashElem *e;
+
+ TRACE(("FTS1 Update %p\n", pVtab));
+
+ fts1HashInit(&terms, FTS1_HASH_STRING, 1);
+
+ if( nArg<2 ){
+ rc = index_delete(v, sqlite3_value_int64(ppArg[0]), &terms);
+ } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
+ /* An update:
+ * ppArg[0] = old rowid
+ * ppArg[1] = new rowid
+ * ppArg[2..2+v->nColumn-1] = values
+ * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+ */
+ sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]);
+ if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER ||
+ sqlite3_value_int64(ppArg[1]) != rowid ){
+ rc = SQLITE_ERROR; /* we don't allow changing the rowid */
+ } else {
+ assert( nArg==2+v->nColumn+1);
+ rc = index_update(v, rowid, &ppArg[2], &terms);
+ }
+ } else {
+ /* An insert:
+ * ppArg[1] = requested rowid
+ * ppArg[2..2+v->nColumn-1] = values
+ * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+ */
+ assert( nArg==2+v->nColumn+1);
+ rc = index_insert(v, ppArg[1], &ppArg[2], pRowid, &terms);
+ }
+
+ if( rc==SQLITE_OK ){
+ /* Write updated doclists to disk. */
+ for(e=fts1HashFirst(&terms); e; e=fts1HashNext(e)){
+ DocList *p = fts1HashData(e);
+ rc = index_insert_term(v, fts1HashKey(e), fts1HashKeysize(e), p);
+ if( rc!=SQLITE_OK ) break;
+ }
+ }
+
+ /* clean up */
+ for(e=fts1HashFirst(&terms); e; e=fts1HashNext(e)){
+ DocList *p = fts1HashData(e);
+ docListDelete(p);
+ }
+ fts1HashClear(&terms);
+
+ return rc;
+}
+
+/*
+** Implementation of the snippet() function for FTS1
+*/
+static void snippetFunc(
+ sqlite3_context *pContext,
+ int argc,
+ sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc<1 ) return;
+ if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1);
+ }else{
+ const char *zStart = "<b>";
+ const char *zEnd = "</b>";
+ const char *zEllipsis = "<b>...</b>";
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ if( argc>=2 ){
+ zStart = (const char*)sqlite3_value_text(argv[1]);
+ if( argc>=3 ){
+ zEnd = (const char*)sqlite3_value_text(argv[2]);
+ if( argc>=4 ){
+ zEllipsis = (const char*)sqlite3_value_text(argv[3]);
+ }
+ }
+ }
+ snippetAllOffsets(pCursor);
+ snippetText(pCursor, zStart, zEnd, zEllipsis);
+ sqlite3_result_text(pContext, pCursor->snippet.zSnippet,
+ pCursor->snippet.nSnippet, SQLITE_STATIC);
+ }
+}
+
+/*
+** Implementation of the offsets() function for FTS1
+*/
+static void snippetOffsetsFunc(
+ sqlite3_context *pContext,
+ int argc,
+ sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc<1 ) return;
+ if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ sqlite3_result_error(pContext, "illegal first argument to offsets",-1);
+ }else{
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ snippetAllOffsets(pCursor);
+ snippetOffsetText(&pCursor->snippet);
+ sqlite3_result_text(pContext,
+ pCursor->snippet.zOffset, pCursor->snippet.nOffset,
+ SQLITE_STATIC);
+ }
+}
+
+/*
+** This routine implements the xFindFunction method for the FTS1
+** virtual table.
+*/
+static int fulltextFindFunction(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ if( strcmp(zName,"snippet")==0 ){
+ *pxFunc = snippetFunc;
+ return 1;
+ }else if( strcmp(zName,"offsets")==0 ){
+ *pxFunc = snippetOffsetsFunc;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Rename an fts1 table.
+*/
+static int fulltextRename(
+ sqlite3_vtab *pVtab,
+ const char *zName
+){
+ fulltext_vtab *p = (fulltext_vtab *)pVtab;
+ int rc = SQLITE_NOMEM;
+ char *zSql = sqlite3_mprintf(
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';"
+ "ALTER TABLE %Q.'%q_term' RENAME TO '%q_term';"
+ , p->zDb, p->zName, zName
+ , p->zDb, p->zName, zName
+ );
+ if( zSql ){
+ rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+ return rc;
+}
+
+static const sqlite3_module fulltextModule = {
+ /* iVersion */ 0,
+ /* xCreate */ fulltextCreate,
+ /* xConnect */ fulltextConnect,
+ /* xBestIndex */ fulltextBestIndex,
+ /* xDisconnect */ fulltextDisconnect,
+ /* xDestroy */ fulltextDestroy,
+ /* xOpen */ fulltextOpen,
+ /* xClose */ fulltextClose,
+ /* xFilter */ fulltextFilter,
+ /* xNext */ fulltextNext,
+ /* xEof */ fulltextEof,
+ /* xColumn */ fulltextColumn,
+ /* xRowid */ fulltextRowid,
+ /* xUpdate */ fulltextUpdate,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindFunction */ fulltextFindFunction,
+ /* xRename */ fulltextRename,
+};
+
+int sqlite3Fts1Init(sqlite3 *db){
+ sqlite3_overload_function(db, "snippet", -1);
+ sqlite3_overload_function(db, "offsets", -1);
+ return sqlite3_create_module(db, "fts1", &fulltextModule, 0);
+}
+
+#if !SQLITE_CORE
+int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg,
+ const sqlite3_api_routines *pApi){
+ SQLITE_EXTENSION_INIT2(pApi)
+ return sqlite3Fts1Init(db);
+}
+#endif
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/third_party/sqlite/ext/fts1/fts1.h b/third_party/sqlite/ext/fts1/fts1.h
new file mode 100755
index 0000000..d55e689
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1.h
@@ -0,0 +1,11 @@
+#include "sqlite3.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int sqlite3Fts1Init(sqlite3 *db);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
diff --git a/third_party/sqlite/ext/fts1/fts1_hash.c b/third_party/sqlite/ext/fts1/fts1_hash.c
new file mode 100755
index 0000000..463a52b
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1_hash.c
@@ -0,0 +1,369 @@
+/*
+** 2001 September 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 is the implementation of generic hash-tables used in SQLite.
+** We've modified it slightly to serve as a standalone hash table
+** implementation for the full-text indexing module.
+*/
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS1 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS1 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
+
+
+#include "fts1_hash.h"
+
+static void *malloc_and_zero(int n){
+ void *p = malloc(n);
+ if( p ){
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+**
+** "pNew" is a pointer to the hash table that is to be initialized.
+** keyClass is one of the constants
+** FTS1_HASH_BINARY or FTS1_HASH_STRING. The value of keyClass
+** determines what kind of key the hash table will use. "copyKey" is
+** true if the hash table should make its own private copy of keys and
+** false if it should just use the supplied pointer.
+*/
+void sqlite3Fts1HashInit(fts1Hash *pNew, int keyClass, int copyKey){
+ assert( pNew!=0 );
+ assert( keyClass>=FTS1_HASH_STRING && keyClass<=FTS1_HASH_BINARY );
+ pNew->keyClass = keyClass;
+ pNew->copyKey = copyKey;
+ pNew->first = 0;
+ pNew->count = 0;
+ pNew->htsize = 0;
+ pNew->ht = 0;
+ pNew->xMalloc = malloc_and_zero;
+ pNew->xFree = free;
+}
+
+/* Remove all entries from a hash table. Reclaim all memory.
+** Call this routine to delete a hash table or to reset a hash table
+** to the empty state.
+*/
+void sqlite3Fts1HashClear(fts1Hash *pH){
+ fts1HashElem *elem; /* For looping over all elements of the table */
+
+ assert( pH!=0 );
+ elem = pH->first;
+ pH->first = 0;
+ if( pH->ht ) pH->xFree(pH->ht);
+ pH->ht = 0;
+ pH->htsize = 0;
+ while( elem ){
+ fts1HashElem *next_elem = elem->next;
+ if( pH->copyKey && elem->pKey ){
+ pH->xFree(elem->pKey);
+ }
+ pH->xFree(elem);
+ elem = next_elem;
+ }
+ pH->count = 0;
+}
+
+/*
+** Hash and comparison functions when the mode is FTS1_HASH_STRING
+*/
+static int strHash(const void *pKey, int nKey){
+ const char *z = (const char *)pKey;
+ int h = 0;
+ if( nKey<=0 ) nKey = (int) strlen(z);
+ while( nKey > 0 ){
+ h = (h<<3) ^ h ^ *z++;
+ nKey--;
+ }
+ return h & 0x7fffffff;
+}
+static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return strncmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is FTS1_HASH_BINARY
+*/
+static int binHash(const void *pKey, int nKey){
+ int h = 0;
+ const char *z = (const char *)pKey;
+ while( nKey-- > 0 ){
+ h = (h<<3) ^ h ^ *(z++);
+ }
+ return h & 0x7fffffff;
+}
+static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** The C syntax in this function definition may be unfamilar to some
+** programmers, so we provide the following additional explanation:
+**
+** The name of the function is "hashFunction". The function takes a
+** single parameter "keyClass". The return value of hashFunction()
+** is a pointer to another function. Specifically, the return value
+** of hashFunction() is a pointer to a function that takes two parameters
+** with types "const void*" and "int" and returns an "int".
+*/
+static int (*hashFunction(int keyClass))(const void*,int){
+ if( keyClass==FTS1_HASH_STRING ){
+ return &strHash;
+ }else{
+ assert( keyClass==FTS1_HASH_BINARY );
+ return &binHash;
+ }
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** For help in interpreted the obscure C code in the function definition,
+** see the header comment on the previous function.
+*/
+static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
+ if( keyClass==FTS1_HASH_STRING ){
+ return &strCompare;
+ }else{
+ assert( keyClass==FTS1_HASH_BINARY );
+ return &binCompare;
+ }
+}
+
+/* Link an element into the hash table
+*/
+static void insertElement(
+ fts1Hash *pH, /* The complete hash table */
+ struct _fts1ht *pEntry, /* The entry into which pNew is inserted */
+ fts1HashElem *pNew /* The element to be inserted */
+){
+ fts1HashElem *pHead; /* First element already in pEntry */
+ pHead = pEntry->chain;
+ if( pHead ){
+ pNew->next = pHead;
+ pNew->prev = pHead->prev;
+ if( pHead->prev ){ pHead->prev->next = pNew; }
+ else { pH->first = pNew; }
+ pHead->prev = pNew;
+ }else{
+ pNew->next = pH->first;
+ if( pH->first ){ pH->first->prev = pNew; }
+ pNew->prev = 0;
+ pH->first = pNew;
+ }
+ pEntry->count++;
+ pEntry->chain = pNew;
+}
+
+
+/* Resize the hash table so that it cantains "new_size" buckets.
+** "new_size" must be a power of 2. The hash table might fail
+** to resize if sqliteMalloc() fails.
+*/
+static void rehash(fts1Hash *pH, int new_size){
+ struct _fts1ht *new_ht; /* The new hash table */
+ fts1HashElem *elem, *next_elem; /* For looping over existing elements */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( (new_size & (new_size-1))==0 );
+ new_ht = (struct _fts1ht *)pH->xMalloc( new_size*sizeof(struct _fts1ht) );
+ if( new_ht==0 ) return;
+ if( pH->ht ) pH->xFree(pH->ht);
+ pH->ht = new_ht;
+ pH->htsize = new_size;
+ xHash = hashFunction(pH->keyClass);
+ for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+ int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+ next_elem = elem->next;
+ insertElement(pH, &new_ht[h], elem);
+ }
+}
+
+/* This function (for internal use only) locates an element in an
+** hash table that matches the given key. The hash for this key has
+** already been computed and is passed as the 4th parameter.
+*/
+static fts1HashElem *findElementGivenHash(
+ const fts1Hash *pH, /* The pH to be searched */
+ const void *pKey, /* The key we are searching for */
+ int nKey,
+ int h /* The hash for this key. */
+){
+ fts1HashElem *elem; /* Used to loop thru the element list */
+ int count; /* Number of elements left to test */
+ int (*xCompare)(const void*,int,const void*,int); /* comparison function */
+
+ if( pH->ht ){
+ struct _fts1ht *pEntry = &pH->ht[h];
+ elem = pEntry->chain;
+ count = pEntry->count;
+ xCompare = compareFunction(pH->keyClass);
+ while( count-- && elem ){
+ if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+ return elem;
+ }
+ elem = elem->next;
+ }
+ }
+ return 0;
+}
+
+/* Remove a single entry from the hash table given a pointer to that
+** element and a hash on the element's key.
+*/
+static void removeElementGivenHash(
+ fts1Hash *pH, /* The pH containing "elem" */
+ fts1HashElem* elem, /* The element to be removed from the pH */
+ int h /* Hash value for the element */
+){
+ struct _fts1ht *pEntry;
+ if( elem->prev ){
+ elem->prev->next = elem->next;
+ }else{
+ pH->first = elem->next;
+ }
+ if( elem->next ){
+ elem->next->prev = elem->prev;
+ }
+ pEntry = &pH->ht[h];
+ if( pEntry->chain==elem ){
+ pEntry->chain = elem->next;
+ }
+ pEntry->count--;
+ if( pEntry->count<=0 ){
+ pEntry->chain = 0;
+ }
+ if( pH->copyKey && elem->pKey ){
+ pH->xFree(elem->pKey);
+ }
+ pH->xFree( elem );
+ pH->count--;
+ if( pH->count<=0 ){
+ assert( pH->first==0 );
+ assert( pH->count==0 );
+ fts1HashClear(pH);
+ }
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey. Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *sqlite3Fts1HashFind(const fts1Hash *pH, const void *pKey, int nKey){
+ int h; /* A hash on key */
+ fts1HashElem *elem; /* The element that matches key */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ if( pH==0 || pH->ht==0 ) return 0;
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ h = (*xHash)(pKey,nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
+ return elem ? elem->data : 0;
+}
+
+/* Insert an element into the hash table pH. The key is pKey,nKey
+** and the data is "data".
+**
+** If no element exists with a matching key, then a new
+** element is created. A copy of the key is made if the copyKey
+** flag is set. NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance. If a malloc fails, then
+** the new data is returned and the hash table is unchanged.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the hash table.
+*/
+void *sqlite3Fts1HashInsert(
+ fts1Hash *pH, /* The hash table to insert into */
+ const void *pKey, /* The key */
+ int nKey, /* Number of bytes in the key */
+ void *data /* The data */
+){
+ int hraw; /* Raw hash value of the key */
+ int h; /* the hash of the key modulo hash table size */
+ fts1HashElem *elem; /* Used to loop thru the element list */
+ fts1HashElem *new_elem; /* New element added to the pH */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( pH!=0 );
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ hraw = (*xHash)(pKey, nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ elem = findElementGivenHash(pH,pKey,nKey,h);
+ if( elem ){
+ void *old_data = elem->data;
+ if( data==0 ){
+ removeElementGivenHash(pH,elem,h);
+ }else{
+ elem->data = data;
+ }
+ return old_data;
+ }
+ if( data==0 ) return 0;
+ new_elem = (fts1HashElem*)pH->xMalloc( sizeof(fts1HashElem) );
+ if( new_elem==0 ) return data;
+ if( pH->copyKey && pKey!=0 ){
+ new_elem->pKey = pH->xMalloc( nKey );
+ if( new_elem->pKey==0 ){
+ pH->xFree(new_elem);
+ return data;
+ }
+ memcpy((void*)new_elem->pKey, pKey, nKey);
+ }else{
+ new_elem->pKey = (void*)pKey;
+ }
+ new_elem->nKey = nKey;
+ pH->count++;
+ if( pH->htsize==0 ){
+ rehash(pH,8);
+ if( pH->htsize==0 ){
+ pH->count = 0;
+ pH->xFree(new_elem);
+ return data;
+ }
+ }
+ if( pH->count > pH->htsize ){
+ rehash(pH,pH->htsize*2);
+ }
+ assert( pH->htsize>0 );
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ insertElement(pH, &pH->ht[h], new_elem);
+ new_elem->data = data;
+ return 0;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/third_party/sqlite/ext/fts1/fts1_hash.h b/third_party/sqlite/ext/fts1/fts1_hash.h
new file mode 100755
index 0000000..c31c430
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1_hash.h
@@ -0,0 +1,112 @@
+/*
+** 2001 September 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 is the header file for the generic hash-table implemenation
+** used in SQLite. We've modified it slightly to serve as a standalone
+** hash table implementation for the full-text indexing module.
+**
+*/
+#ifndef _FTS1_HASH_H_
+#define _FTS1_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct fts1Hash fts1Hash;
+typedef struct fts1HashElem fts1HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly. Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct fts1Hash {
+ char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
+ char copyKey; /* True if copy of key made on insert */
+ int count; /* Number of entries in this table */
+ fts1HashElem *first; /* The first element of the array */
+ void *(*xMalloc)(int); /* malloc() function to use */
+ void (*xFree)(void *); /* free() function to use */
+ int htsize; /* Number of buckets in the hash table */
+ struct _fts1ht { /* the hash table */
+ int count; /* Number of entries with this hash */
+ fts1HashElem *chain; /* Pointer to first entry with this hash */
+ } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure. All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct fts1HashElem {
+ fts1HashElem *next, *prev; /* Next and previous elements in the table */
+ void *data; /* Data associated with this element */
+ void *pKey; int nKey; /* Key associated with this element */
+};
+
+/*
+** There are 2 different modes of operation for a hash table:
+**
+** FTS1_HASH_STRING pKey points to a string that is nKey bytes long
+** (including the null-terminator, if any). Case
+** is respected in comparisons.
+**
+** FTS1_HASH_BINARY pKey points to binary data nKey bytes long.
+** memcmp() is used to compare keys.
+**
+** A copy of the key is made if the copyKey parameter to fts1HashInit is 1.
+*/
+#define FTS1_HASH_STRING 1
+#define FTS1_HASH_BINARY 2
+
+/*
+** Access routines. To delete, insert a NULL pointer.
+*/
+void sqlite3Fts1HashInit(fts1Hash*, int keytype, int copyKey);
+void *sqlite3Fts1HashInsert(fts1Hash*, const void *pKey, int nKey, void *pData);
+void *sqlite3Fts1HashFind(const fts1Hash*, const void *pKey, int nKey);
+void sqlite3Fts1HashClear(fts1Hash*);
+
+/*
+** Shorthand for the functions above
+*/
+#define fts1HashInit sqlite3Fts1HashInit
+#define fts1HashInsert sqlite3Fts1HashInsert
+#define fts1HashFind sqlite3Fts1HashFind
+#define fts1HashClear sqlite3Fts1HashClear
+
+/*
+** Macros for looping over all elements of a hash table. The idiom is
+** like this:
+**
+** fts1Hash h;
+** fts1HashElem *p;
+** ...
+** for(p=fts1HashFirst(&h); p; p=fts1HashNext(p)){
+** SomeStructure *pData = fts1HashData(p);
+** // do something with pData
+** }
+*/
+#define fts1HashFirst(H) ((H)->first)
+#define fts1HashNext(E) ((E)->next)
+#define fts1HashData(E) ((E)->data)
+#define fts1HashKey(E) ((E)->pKey)
+#define fts1HashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define fts1HashCount(H) ((H)->count)
+
+#endif /* _FTS1_HASH_H_ */
diff --git a/third_party/sqlite/ext/fts1/fts1_porter.c b/third_party/sqlite/ext/fts1/fts1_porter.c
new file mode 100755
index 0000000..1d26236
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1_porter.c
@@ -0,0 +1,643 @@
+/*
+** 2006 September 30
+**
+** 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.
+**
+*************************************************************************
+** Implementation of the full-text-search tokenizer that implements
+** a Porter stemmer.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS1 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS1 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts1_tokenizer.h"
+
+/*
+** Class derived from sqlite3_tokenizer
+*/
+typedef struct porter_tokenizer {
+ sqlite3_tokenizer base; /* Base class */
+} porter_tokenizer;
+
+/*
+** Class derived from sqlit3_tokenizer_cursor
+*/
+typedef struct porter_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ const char *zInput; /* input we are tokenizing */
+ int nInput; /* size of the input */
+ int iOffset; /* current position in zInput */
+ int iToken; /* index of next token to be returned */
+ char *zToken; /* storage for current token */
+ int nAllocated; /* space allocated to zToken buffer */
+} porter_tokenizer_cursor;
+
+
+/* Forward declaration */
+static const sqlite3_tokenizer_module porterTokenizerModule;
+
+
+/*
+** Create a new tokenizer instance.
+*/
+static int porterCreate(
+ int argc, const char * const *argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ porter_tokenizer *t;
+ t = (porter_tokenizer *) calloc(sizeof(*t), 1);
+ if( t==NULL ) return SQLITE_NOMEM;
+
+ *ppTokenizer = &t->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int porterDestroy(sqlite3_tokenizer *pTokenizer){
+ free(pTokenizer);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is zInput[0..nInput-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int porterOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *zInput, int nInput, /* String to be tokenized */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ porter_tokenizer_cursor *c;
+
+ c = (porter_tokenizer_cursor *) malloc(sizeof(*c));
+ if( c==NULL ) return SQLITE_NOMEM;
+
+ c->zInput = zInput;
+ if( zInput==0 ){
+ c->nInput = 0;
+ }else if( nInput<0 ){
+ c->nInput = (int)strlen(zInput);
+ }else{
+ c->nInput = nInput;
+ }
+ c->iOffset = 0; /* start tokenizing at the beginning */
+ c->iToken = 0;
+ c->zToken = NULL; /* no space allocated, yet. */
+ c->nAllocated = 0;
+
+ *ppCursor = &c->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** porterOpen() above.
+*/
+static int porterClose(sqlite3_tokenizer_cursor *pCursor){
+ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
+ free(c->zToken);
+ free(c);
+ return SQLITE_OK;
+}
+/*
+** Vowel or consonant
+*/
+static const char cType[] = {
+ 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 2, 1
+};
+
+/*
+** isConsonant() and isVowel() determine if their first character in
+** the string they point to is a consonant or a vowel, according
+** to Porter ruls.
+**
+** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
+** 'Y' is a consonant unless it follows another consonant,
+** in which case it is a vowel.
+**
+** In these routine, the letters are in reverse order. So the 'y' rule
+** is that 'y' is a consonant unless it is followed by another
+** consonent.
+*/
+static int isVowel(const char*);
+static int isConsonant(const char *z){
+ int j;
+ char x = *z;
+ if( x==0 ) return 0;
+ assert( x>='a' && x<='z' );
+ j = cType[x-'a'];
+ if( j<2 ) return j;
+ return z[1]==0 || isVowel(z + 1);
+}
+static int isVowel(const char *z){
+ int j;
+ char x = *z;
+ if( x==0 ) return 0;
+ assert( x>='a' && x<='z' );
+ j = cType[x-'a'];
+ if( j<2 ) return 1-j;
+ return isConsonant(z + 1);
+}
+
+/*
+** Let any sequence of one or more vowels be represented by V and let
+** C be sequence of one or more consonants. Then every word can be
+** represented as:
+**
+** [C] (VC){m} [V]
+**
+** In prose: A word is an optional consonant followed by zero or
+** vowel-consonant pairs followed by an optional vowel. "m" is the
+** number of vowel consonant pairs. This routine computes the value
+** of m for the first i bytes of a word.
+**
+** Return true if the m-value for z is 1 or more. In other words,
+** return true if z contains at least one vowel that is followed
+** by a consonant.
+**
+** In this routine z[] is in reverse order. So we are really looking
+** for an instance of of a consonant followed by a vowel.
+*/
+static int m_gt_0(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/* Like mgt0 above except we are looking for a value of m which is
+** exactly 1
+*/
+static int m_eq_1(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 1;
+ while( isConsonant(z) ){ z++; }
+ return *z==0;
+}
+
+/* Like mgt0 above except we are looking for a value of m>1 instead
+** or m>0
+*/
+static int m_gt_1(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/*
+** Return TRUE if there is a vowel anywhere within z[0..n-1]
+*/
+static int hasVowel(const char *z){
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/*
+** Return TRUE if the word ends in a double consonant.
+**
+** The text is reversed here. So we are really looking at
+** the first two characters of z[].
+*/
+static int doubleConsonant(const char *z){
+ return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
+}
+
+/*
+** Return TRUE if the word ends with three letters which
+** are consonant-vowel-consonent and where the final consonant
+** is not 'w', 'x', or 'y'.
+**
+** The word is reversed here. So we are really checking the
+** first three letters and the first one cannot be in [wxy].
+*/
+static int star_oh(const char *z){
+ return
+ z[0]!=0 && isConsonant(z) &&
+ z[0]!='w' && z[0]!='x' && z[0]!='y' &&
+ z[1]!=0 && isVowel(z+1) &&
+ z[2]!=0 && isConsonant(z+2);
+}
+
+/*
+** If the word ends with zFrom and xCond() is true for the stem
+** of the word that preceeds the zFrom ending, then change the
+** ending to zTo.
+**
+** The input word *pz and zFrom are both in reverse order. zTo
+** is in normal order.
+**
+** Return TRUE if zFrom matches. Return FALSE if zFrom does not
+** match. Not that TRUE is returned even if xCond() fails and
+** no substitution occurs.
+*/
+static int stem(
+ char **pz, /* The word being stemmed (Reversed) */
+ const char *zFrom, /* If the ending matches this... (Reversed) */
+ const char *zTo, /* ... change the ending to this (not reversed) */
+ int (*xCond)(const char*) /* Condition that must be true */
+){
+ char *z = *pz;
+ while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
+ if( *zFrom!=0 ) return 0;
+ if( xCond && !xCond(z) ) return 1;
+ while( *zTo ){
+ *(--z) = *(zTo++);
+ }
+ *pz = z;
+ return 1;
+}
+
+/*
+** This is the fallback stemmer used when the porter stemmer is
+** inappropriate. The input word is copied into the output with
+** US-ASCII case folding. If the input word is too long (more
+** than 20 bytes if it contains no digits or more than 6 bytes if
+** it contains digits) then word is truncated to 20 or 6 bytes
+** by taking 10 or 3 bytes from the beginning and end.
+*/
+static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
+ int i, mx, j;
+ int hasDigit = 0;
+ for(i=0; i<nIn; i++){
+ int c = zIn[i];
+ if( c>='A' && c<='Z' ){
+ zOut[i] = c - 'A' + 'a';
+ }else{
+ if( c>='0' && c<='9' ) hasDigit = 1;
+ zOut[i] = c;
+ }
+ }
+ mx = hasDigit ? 3 : 10;
+ if( nIn>mx*2 ){
+ for(j=mx, i=nIn-mx; i<nIn; i++, j++){
+ zOut[j] = zOut[i];
+ }
+ i = j;
+ }
+ zOut[i] = 0;
+ *pnOut = i;
+}
+
+
+/*
+** Stem the input word zIn[0..nIn-1]. Store the output in zOut.
+** zOut is at least big enough to hold nIn bytes. Write the actual
+** size of the output word (exclusive of the '\0' terminator) into *pnOut.
+**
+** Any upper-case characters in the US-ASCII character set ([A-Z])
+** are converted to lower case. Upper-case UTF characters are
+** unchanged.
+**
+** Words that are longer than about 20 bytes are stemmed by retaining
+** a few bytes from the beginning and the end of the word. If the
+** word contains digits, 3 bytes are taken from the beginning and
+** 3 bytes from the end. For long words without digits, 10 bytes
+** are taken from each end. US-ASCII case folding still applies.
+**
+** If the input word contains not digits but does characters not
+** in [a-zA-Z] then no stemming is attempted and this routine just
+** copies the input into the input into the output with US-ASCII
+** case folding.
+**
+** Stemming never increases the length of the word. So there is
+** no chance of overflowing the zOut buffer.
+*/
+static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
+ int i, j, c;
+ char zReverse[28];
+ char *z, *z2;
+ if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
+ /* The word is too big or too small for the porter stemmer.
+ ** Fallback to the copy stemmer */
+ copy_stemmer(zIn, nIn, zOut, pnOut);
+ return;
+ }
+ for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
+ c = zIn[i];
+ if( c>='A' && c<='Z' ){
+ zReverse[j] = c + 'a' - 'A';
+ }else if( c>='a' && c<='z' ){
+ zReverse[j] = c;
+ }else{
+ /* The use of a character not in [a-zA-Z] means that we fallback
+ ** to the copy stemmer */
+ copy_stemmer(zIn, nIn, zOut, pnOut);
+ return;
+ }
+ }
+ memset(&zReverse[sizeof(zReverse)-5], 0, 5);
+ z = &zReverse[j+1];
+
+
+ /* Step 1a */
+ if( z[0]=='s' ){
+ if(
+ !stem(&z, "sess", "ss", 0) &&
+ !stem(&z, "sei", "i", 0) &&
+ !stem(&z, "ss", "ss", 0)
+ ){
+ z++;
+ }
+ }
+
+ /* Step 1b */
+ z2 = z;
+ if( stem(&z, "dee", "ee", m_gt_0) ){
+ /* Do nothing. The work was all in the test */
+ }else if(
+ (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
+ && z!=z2
+ ){
+ if( stem(&z, "ta", "ate", 0) ||
+ stem(&z, "lb", "ble", 0) ||
+ stem(&z, "zi", "ize", 0) ){
+ /* Do nothing. The work was all in the test */
+ }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
+ z++;
+ }else if( m_eq_1(z) && star_oh(z) ){
+ *(--z) = 'e';
+ }
+ }
+
+ /* Step 1c */
+ if( z[0]=='y' && hasVowel(z+1) ){
+ z[0] = 'i';
+ }
+
+ /* Step 2 */
+ switch( z[1] ){
+ case 'a':
+ stem(&z, "lanoita", "ate", m_gt_0) ||
+ stem(&z, "lanoit", "tion", m_gt_0);
+ break;
+ case 'c':
+ stem(&z, "icne", "ence", m_gt_0) ||
+ stem(&z, "icna", "ance", m_gt_0);
+ break;
+ case 'e':
+ stem(&z, "rezi", "ize", m_gt_0);
+ break;
+ case 'g':
+ stem(&z, "igol", "log", m_gt_0);
+ break;
+ case 'l':
+ stem(&z, "ilb", "ble", m_gt_0) ||
+ stem(&z, "illa", "al", m_gt_0) ||
+ stem(&z, "iltne", "ent", m_gt_0) ||
+ stem(&z, "ile", "e", m_gt_0) ||
+ stem(&z, "ilsuo", "ous", m_gt_0);
+ break;
+ case 'o':
+ stem(&z, "noitazi", "ize", m_gt_0) ||
+ stem(&z, "noita", "ate", m_gt_0) ||
+ stem(&z, "rota", "ate", m_gt_0);
+ break;
+ case 's':
+ stem(&z, "msila", "al", m_gt_0) ||
+ stem(&z, "ssenevi", "ive", m_gt_0) ||
+ stem(&z, "ssenluf", "ful", m_gt_0) ||
+ stem(&z, "ssensuo", "ous", m_gt_0);
+ break;
+ case 't':
+ stem(&z, "itila", "al", m_gt_0) ||
+ stem(&z, "itivi", "ive", m_gt_0) ||
+ stem(&z, "itilib", "ble", m_gt_0);
+ break;
+ }
+
+ /* Step 3 */
+ switch( z[0] ){
+ case 'e':
+ stem(&z, "etaci", "ic", m_gt_0) ||
+ stem(&z, "evita", "", m_gt_0) ||
+ stem(&z, "ezila", "al", m_gt_0);
+ break;
+ case 'i':
+ stem(&z, "itici", "ic", m_gt_0);
+ break;
+ case 'l':
+ stem(&z, "laci", "ic", m_gt_0) ||
+ stem(&z, "luf", "", m_gt_0);
+ break;
+ case 's':
+ stem(&z, "ssen", "", m_gt_0);
+ break;
+ }
+
+ /* Step 4 */
+ switch( z[1] ){
+ case 'a':
+ if( z[0]=='l' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'c':
+ if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
+ z += 4;
+ }
+ break;
+ case 'e':
+ if( z[0]=='r' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'i':
+ if( z[0]=='c' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'l':
+ if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
+ z += 4;
+ }
+ break;
+ case 'n':
+ if( z[0]=='t' ){
+ if( z[2]=='a' ){
+ if( m_gt_1(z+3) ){
+ z += 3;
+ }
+ }else if( z[2]=='e' ){
+ stem(&z, "tneme", "", m_gt_1) ||
+ stem(&z, "tnem", "", m_gt_1) ||
+ stem(&z, "tne", "", m_gt_1);
+ }
+ }
+ break;
+ case 'o':
+ if( z[0]=='u' ){
+ if( m_gt_1(z+2) ){
+ z += 2;
+ }
+ }else if( z[3]=='s' || z[3]=='t' ){
+ stem(&z, "noi", "", m_gt_1);
+ }
+ break;
+ case 's':
+ if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ case 't':
+ stem(&z, "eta", "", m_gt_1) ||
+ stem(&z, "iti", "", m_gt_1);
+ break;
+ case 'u':
+ if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ case 'v':
+ case 'z':
+ if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ }
+
+ /* Step 5a */
+ if( z[0]=='e' ){
+ if( m_gt_1(z+1) ){
+ z++;
+ }else if( m_eq_1(z+1) && !star_oh(z+1) ){
+ z++;
+ }
+ }
+
+ /* Step 5b */
+ if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
+ z++;
+ }
+
+ /* z[] is now the stemmed word in reverse order. Flip it back
+ ** around into forward order and return.
+ */
+ *pnOut = i = strlen(z);
+ zOut[i] = 0;
+ while( *z ){
+ zOut[--i] = *(z++);
+ }
+}
+
+/*
+** Characters that can be part of a token. We assume any character
+** whose value is greater than 0x80 (any UTF character) can be
+** part of a token. In other words, delimiters all must have
+** values of 0x7f or lower.
+*/
+static const char isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+};
+#define idChar(C) (((ch=C)&0x80)!=0 || (ch>0x2f && isIdChar[ch-0x30]))
+#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !isIdChar[ch-0x30]))
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to porterOpen().
+*/
+static int porterNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
+ const char **pzToken, /* OUT: *pzToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
+ const char *z = c->zInput;
+
+ while( c->iOffset<c->nInput ){
+ int iStartOffset, ch;
+
+ /* Scan past delimiter characters */
+ while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ /* Count non-delimiter characters. */
+ iStartOffset = c->iOffset;
+ while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ if( c->iOffset>iStartOffset ){
+ int n = c->iOffset-iStartOffset;
+ if( n>c->nAllocated ){
+ c->nAllocated = n+20;
+ c->zToken = realloc(c->zToken, c->nAllocated);
+ if( c->zToken==NULL ) return SQLITE_NOMEM;
+ }
+ porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
+ *pzToken = c->zToken;
+ *piStartOffset = iStartOffset;
+ *piEndOffset = c->iOffset;
+ *piPosition = c->iToken++;
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_DONE;
+}
+
+/*
+** The set of routines that implement the porter-stemmer tokenizer
+*/
+static const sqlite3_tokenizer_module porterTokenizerModule = {
+ 0,
+ porterCreate,
+ porterDestroy,
+ porterOpen,
+ porterClose,
+ porterNext,
+};
+
+/*
+** Allocate a new porter tokenizer. Return a pointer to the new
+** tokenizer in *ppModule
+*/
+void sqlite3Fts1PorterTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &porterTokenizerModule;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/third_party/sqlite/ext/fts1/fts1_tokenizer.h b/third_party/sqlite/ext/fts1/fts1_tokenizer.h
new file mode 100755
index 0000000..a48cb74
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1_tokenizer.h
@@ -0,0 +1,90 @@
+/*
+** 2006 July 10
+**
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Defines the interface to tokenizers used by fulltext-search. There
+** are three basic components:
+**
+** sqlite3_tokenizer_module is a singleton defining the tokenizer
+** interface functions. This is essentially the class structure for
+** tokenizers.
+**
+** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
+** including customization information defined at creation time.
+**
+** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
+** tokens from a particular input.
+*/
+#ifndef _FTS1_TOKENIZER_H_
+#define _FTS1_TOKENIZER_H_
+
+/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
+** If tokenizers are to be allowed to call sqlite3_*() functions, then
+** we will need a way to register the API consistently.
+*/
+#include "sqlite3.h"
+
+/*
+** Structures used by the tokenizer interface.
+*/
+typedef struct sqlite3_tokenizer sqlite3_tokenizer;
+typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
+typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
+
+struct sqlite3_tokenizer_module {
+ int iVersion; /* currently 0 */
+
+ /*
+ ** Create and destroy a tokenizer. argc/argv are passed down from
+ ** the fulltext virtual table creation to allow customization.
+ */
+ int (*xCreate)(int argc, const char *const*argv,
+ sqlite3_tokenizer **ppTokenizer);
+ int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
+
+ /*
+ ** Tokenize a particular input. Call xOpen() to prepare to
+ ** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
+ ** xClose() to free any internal state. The pInput passed to
+ ** xOpen() must exist until the cursor is closed. The ppToken
+ ** result from xNext() is only valid until the next call to xNext()
+ ** or until xClose() is called.
+ */
+ /* TODO(shess) current implementation requires pInput to be
+ ** nul-terminated. This should either be fixed, or pInput/nBytes
+ ** should be converted to zInput.
+ */
+ int (*xOpen)(sqlite3_tokenizer *pTokenizer,
+ const char *pInput, int nBytes,
+ sqlite3_tokenizer_cursor **ppCursor);
+ int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
+ int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
+ const char **ppToken, int *pnBytes,
+ int *piStartOffset, int *piEndOffset, int *piPosition);
+};
+
+struct sqlite3_tokenizer {
+ const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+struct sqlite3_tokenizer_cursor {
+ sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+/*
+** Get the module for a tokenizer which generates tokens based on a
+** set of non-token characters. The default is to break tokens at any
+** non-alnum character, though the set of delimiters can also be
+** specified by the first argv argument to xCreate().
+*/
+/* TODO(shess) This doesn't belong here. Need some sort of
+** registration process.
+*/
+void sqlite3Fts1SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+void sqlite3Fts1PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+
+#endif /* _FTS1_TOKENIZER_H_ */
diff --git a/third_party/sqlite/ext/fts1/fts1_tokenizer1.c b/third_party/sqlite/ext/fts1/fts1_tokenizer1.c
new file mode 100755
index 0000000..f58fba8
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fts1_tokenizer1.c
@@ -0,0 +1,221 @@
+/*
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Implementation of the "simple" full-text-search tokenizer.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS1 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS1 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts1_tokenizer.h"
+
+typedef struct simple_tokenizer {
+ sqlite3_tokenizer base;
+ char delim[128]; /* flag ASCII delimiters */
+} simple_tokenizer;
+
+typedef struct simple_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ const char *pInput; /* input we are tokenizing */
+ int nBytes; /* size of the input */
+ int iOffset; /* current position in pInput */
+ int iToken; /* index of next token to be returned */
+ char *pToken; /* storage for current token */
+ int nTokenAllocated; /* space allocated to zToken buffer */
+} simple_tokenizer_cursor;
+
+
+/* Forward declaration */
+static const sqlite3_tokenizer_module simpleTokenizerModule;
+
+static int isDelim(simple_tokenizer *t, unsigned char c){
+ return c<0x80 && t->delim[c];
+}
+
+/*
+** Create a new tokenizer instance.
+*/
+static int simpleCreate(
+ int argc, const char * const *argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ simple_tokenizer *t;
+
+ t = (simple_tokenizer *) calloc(sizeof(*t), 1);
+ if( t==NULL ) return SQLITE_NOMEM;
+
+ /* TODO(shess) Delimiters need to remain the same from run to run,
+ ** else we need to reindex. One solution would be a meta-table to
+ ** track such information in the database, then we'd only want this
+ ** information on the initial create.
+ */
+ if( argc>1 ){
+ int i, n = strlen(argv[1]);
+ for(i=0; i<n; i++){
+ unsigned char ch = argv[1][i];
+ /* We explicitly don't support UTF-8 delimiters for now. */
+ if( ch>=0x80 ){
+ free(t);
+ return SQLITE_ERROR;
+ }
+ t->delim[ch] = 1;
+ }
+ } else {
+ /* Mark non-alphanumeric ASCII characters as delimiters */
+ int i;
+ for(i=1; i<0x80; i++){
+ t->delim[i] = !isalnum(i);
+ }
+ }
+
+ *ppTokenizer = &t->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
+ free(pTokenizer);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is pInput[0..nBytes-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int simpleOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *pInput, int nBytes, /* String to be tokenized */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ simple_tokenizer_cursor *c;
+
+ c = (simple_tokenizer_cursor *) malloc(sizeof(*c));
+ if( c==NULL ) return SQLITE_NOMEM;
+
+ c->pInput = pInput;
+ if( pInput==0 ){
+ c->nBytes = 0;
+ }else if( nBytes<0 ){
+ c->nBytes = (int)strlen(pInput);
+ }else{
+ c->nBytes = nBytes;
+ }
+ c->iOffset = 0; /* start tokenizing at the beginning */
+ c->iToken = 0;
+ c->pToken = NULL; /* no space allocated, yet. */
+ c->nTokenAllocated = 0;
+
+ *ppCursor = &c->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** simpleOpen() above.
+*/
+static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+ free(c->pToken);
+ free(c);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to simpleOpen().
+*/
+static int simpleNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
+ const char **ppToken, /* OUT: *ppToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+ simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
+ unsigned char *p = (unsigned char *)c->pInput;
+
+ while( c->iOffset<c->nBytes ){
+ int iStartOffset;
+
+ /* Scan past delimiter characters */
+ while( c->iOffset<c->nBytes && isDelim(t, p[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ /* Count non-delimiter characters. */
+ iStartOffset = c->iOffset;
+ while( c->iOffset<c->nBytes && !isDelim(t, p[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ if( c->iOffset>iStartOffset ){
+ int i, n = c->iOffset-iStartOffset;
+ if( n>c->nTokenAllocated ){
+ c->nTokenAllocated = n+20;
+ c->pToken = realloc(c->pToken, c->nTokenAllocated);
+ if( c->pToken==NULL ) return SQLITE_NOMEM;
+ }
+ for(i=0; i<n; i++){
+ /* TODO(shess) This needs expansion to handle UTF-8
+ ** case-insensitivity.
+ */
+ unsigned char ch = p[iStartOffset+i];
+ c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
+ }
+ *ppToken = c->pToken;
+ *pnBytes = n;
+ *piStartOffset = iStartOffset;
+ *piEndOffset = c->iOffset;
+ *piPosition = c->iToken++;
+
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_DONE;
+}
+
+/*
+** The set of routines that implement the simple tokenizer
+*/
+static const sqlite3_tokenizer_module simpleTokenizerModule = {
+ 0,
+ simpleCreate,
+ simpleDestroy,
+ simpleOpen,
+ simpleClose,
+ simpleNext,
+};
+
+/*
+** Allocate a new simple tokenizer. Return a pointer to the new
+** tokenizer in *ppModule
+*/
+void sqlite3Fts1SimpleTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &simpleTokenizerModule;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/third_party/sqlite/ext/fts1/fulltext.c b/third_party/sqlite/ext/fts1/fulltext.c
new file mode 100755
index 0000000..e6034ba
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fulltext.c
@@ -0,0 +1,1496 @@
+/* The author disclaims copyright to this source code.
+ *
+ * This is an SQLite module implementing full-text search.
+ */
+
+#include <assert.h>
+#if !defined(__APPLE__)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fulltext.h"
+#include "ft_hash.h"
+#include "tokenizer.h"
+#include "sqlite3.h"
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+
+/* utility functions */
+
+/* We encode variable-length integers in little-endian order using seven bits
+ * per byte as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** and so on.
+*/
+
+/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
+#define VARINT_MAX 10
+
+/* Write a 64-bit variable-length integer to memory starting at p[0].
+ * The length of data written will be between 1 and VARINT_MAX bytes.
+ * The number of bytes written is returned. */
+static int putVarint(char *p, sqlite_int64 v){
+ unsigned char *q = (unsigned char *) p;
+ sqlite_uint64 vu = v;
+ do{
+ *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
+ vu >>= 7;
+ }while( vu!=0 );
+ q[-1] &= 0x7f; /* turn off high bit in final byte */
+ assert( q - (unsigned char *)p <= VARINT_MAX );
+ return (int) (q - (unsigned char *)p);
+}
+
+/* Read a 64-bit variable-length integer from memory starting at p[0].
+ * Return the number of bytes read, or 0 on error.
+ * The value is stored in *v. */
+static int getVarint(const char *p, sqlite_int64 *v){
+ const unsigned char *q = (const unsigned char *) p;
+ sqlite_uint64 x = 0, y = 1;
+ while( (*q & 0x80) == 0x80 ){
+ x += y * (*q++ & 0x7f);
+ y <<= 7;
+ if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
+ assert( 0 );
+ return 0;
+ }
+ }
+ x += y * (*q++);
+ *v = (sqlite_int64) x;
+ return (int) (q - (unsigned char *)p);
+}
+
+static int getVarint32(const char *p, int *pi){
+ sqlite_int64 i;
+ int ret = getVarint(p, &i);
+ *pi = (int) i;
+ assert( *pi==i );
+ return ret;
+}
+
+/*** Document lists ***
+ *
+ * A document list holds a sorted list of varint-encoded document IDs.
+ *
+ * A doclist with type DL_POSITIONS_OFFSETS is stored like this:
+ *
+ * array {
+ * varint docid;
+ * array {
+ * varint position; (delta from previous position plus 1, or 0 for end)
+ * varint startOffset; (delta from previous startOffset)
+ * varint endOffset; (delta from startOffset)
+ * }
+ * }
+ *
+ * Here, array { X } means zero or more occurrences of X, adjacent in memory.
+ *
+ * A doclist with type DL_POSITIONS is like the above, but holds only docids
+ * and positions without offset information.
+ *
+ * A doclist with type DL_DOCIDS is like the above, but holds only docids
+ * without positions or offset information.
+ *
+ * On disk, every document list has positions and offsets, so we don't bother
+ * to serialize a doclist's type.
+ *
+ * We don't yet delta-encode document IDs; doing so will probably be a
+ * modest win.
+ *
+ * NOTE(shess) I've thought of a slightly (1%) better offset encoding.
+ * After the first offset, estimate the next offset by using the
+ * current token position and the previous token position and offset,
+ * offset to handle some variance. So the estimate would be
+ * (iPosition*w->iStartOffset/w->iPosition-64), which is delta-encoded
+ * as normal. Offsets more than 64 chars from the estimate are
+ * encoded as the delta to the previous start offset + 128. An
+ * additional tiny increment can be gained by using the end offset of
+ * the previous token to make the estimate a tiny bit more precise.
+*/
+
+typedef enum DocListType {
+ DL_DOCIDS, /* docids only */
+ DL_POSITIONS, /* docids + positions */
+ DL_POSITIONS_OFFSETS /* docids + positions + offsets */
+} DocListType;
+
+typedef struct DocList {
+ char *pData;
+ int nData;
+ DocListType iType;
+ int iLastPos; /* the last position written */
+ int iLastOffset; /* the last start offset written */
+} DocList;
+
+/* Initialize a new DocList to hold the given data. */
+static void docListInit(DocList *d, DocListType iType,
+ const char *pData, int nData){
+ d->nData = nData;
+ if( nData>0 ){
+ d->pData = malloc(nData);
+ memcpy(d->pData, pData, nData);
+ } else {
+ d->pData = NULL;
+ }
+ d->iType = iType;
+ d->iLastPos = 0;
+ d->iLastOffset = 0;
+}
+
+/* Create a new dynamically-allocated DocList. */
+static DocList *docListNew(DocListType iType){
+ DocList *d = (DocList *) malloc(sizeof(DocList));
+ docListInit(d, iType, 0, 0);
+ return d;
+}
+
+static void docListDestroy(DocList *d){
+ free(d->pData);
+#ifndef NDEBUG
+ memset(d, 0x55, sizeof(*d));
+#endif
+}
+
+static void docListDelete(DocList *d){
+ docListDestroy(d);
+ free(d);
+}
+
+static char *docListEnd(DocList *d){
+ return d->pData + d->nData;
+}
+
+/* Append a varint to a DocList's data. */
+static void appendVarint(DocList *d, sqlite_int64 i){
+ char c[VARINT_MAX];
+ int n = putVarint(c, i);
+ d->pData = realloc(d->pData, d->nData + n);
+ memcpy(d->pData + d->nData, c, n);
+ d->nData += n;
+}
+
+static void docListAddDocid(DocList *d, sqlite_int64 iDocid){
+ appendVarint(d, iDocid);
+ d->iLastPos = 0;
+}
+
+/* Add a position to the last position list in a doclist. */
+static void docListAddPos(DocList *d, int iPos){
+ assert( d->iType>=DL_POSITIONS );
+ appendVarint(d, iPos-d->iLastPos+1);
+ d->iLastPos = iPos;
+}
+
+static void docListAddPosOffset(DocList *d, int iPos,
+ int iStartOffset, int iEndOffset){
+ assert( d->iType==DL_POSITIONS_OFFSETS );
+ docListAddPos(d, iPos);
+ appendVarint(d, iStartOffset-d->iLastOffset);
+ d->iLastOffset = iStartOffset;
+ appendVarint(d, iEndOffset-iStartOffset);
+}
+
+/* Terminate the last position list in the given doclist. */
+static void docListAddEndPos(DocList *d){
+ appendVarint(d, 0);
+}
+
+typedef struct DocListReader {
+ DocList *pDoclist;
+ char *p;
+ int iLastPos; /* the last position read */
+} DocListReader;
+
+static void readerInit(DocListReader *r, DocList *pDoclist){
+ r->pDoclist = pDoclist;
+ if( pDoclist!=NULL ){
+ r->p = pDoclist->pData;
+ }
+ r->iLastPos = 0;
+}
+
+static int readerAtEnd(DocListReader *pReader){
+ return pReader->p >= docListEnd(pReader->pDoclist);
+}
+
+/* Peek at the next docid without advancing the read pointer. */
+static sqlite_int64 peekDocid(DocListReader *pReader){
+ sqlite_int64 ret;
+ assert( !readerAtEnd(pReader) );
+ getVarint(pReader->p, &ret);
+ return ret;
+}
+
+/* Read the next docid. */
+static sqlite_int64 readDocid(DocListReader *pReader){
+ sqlite_int64 ret;
+ assert( !readerAtEnd(pReader) );
+ pReader->p += getVarint(pReader->p, &ret);
+ pReader->iLastPos = 0;
+ return ret;
+}
+
+/* Read the next position from a position list.
+ * Returns the position, or -1 at the end of the list. */
+static int readPosition(DocListReader *pReader){
+ int i;
+ int iType = pReader->pDoclist->iType;
+ assert( iType>=DL_POSITIONS );
+ assert( !readerAtEnd(pReader) );
+
+ pReader->p += getVarint32(pReader->p, &i);
+ if( i==0 ){
+ pReader->iLastPos = -1;
+ return -1;
+ }
+ pReader->iLastPos += ((int) i)-1;
+ if( iType>=DL_POSITIONS_OFFSETS ){
+ /* Skip over offsets, ignoring them for now. */
+ int iStart, iEnd;
+ pReader->p += getVarint32(pReader->p, &iStart);
+ pReader->p += getVarint32(pReader->p, &iEnd);
+ }
+ return pReader->iLastPos;
+}
+
+/* Skip past the end of a position list. */
+static void skipPositionList(DocListReader *pReader){
+ while( readPosition(pReader)!=-1 )
+ ;
+}
+
+/* Skip over a docid, including its position list if the doclist has
+ * positions. */
+static void skipDocument(DocListReader *pReader){
+ readDocid(pReader);
+ if( pReader->pDoclist->iType >= DL_POSITIONS ){
+ skipPositionList(pReader);
+ }
+}
+
+static sqlite_int64 firstDocid(DocList *d){
+ DocListReader r;
+ readerInit(&r, d);
+ return readDocid(&r);
+}
+
+/* Doclist multi-tool. Pass pUpdate==NULL to delete the indicated docid;
+ * otherwise pUpdate, which must contain only the single docid [iDocid], is
+ * inserted (if not present) or updated (if already present). */
+static int docListUpdate(DocList *d, sqlite_int64 iDocid, DocList *pUpdate){
+ int modified = 0;
+ DocListReader reader;
+ char *p;
+
+ if( pUpdate!=NULL ){
+ assert( d->iType==pUpdate->iType);
+ assert( iDocid==firstDocid(pUpdate) );
+ }
+
+ readerInit(&reader, d);
+ while( !readerAtEnd(&reader) && peekDocid(&reader)<iDocid ){
+ skipDocument(&reader);
+ }
+
+ p = reader.p;
+ /* Delete if there is a matching element. */
+ if( !readerAtEnd(&reader) && iDocid==peekDocid(&reader) ){
+ skipDocument(&reader);
+ memmove(p, reader.p, docListEnd(d) - reader.p);
+ d->nData -= (reader.p - p);
+ modified = 1;
+ }
+
+ /* Insert if indicated. */
+ if( pUpdate!=NULL ){
+ int iDoclist = p-d->pData;
+ docListAddEndPos(pUpdate);
+
+ d->pData = realloc(d->pData, d->nData+pUpdate->nData);
+ p = d->pData + iDoclist;
+
+ memmove(p+pUpdate->nData, p, docListEnd(d) - p);
+ memcpy(p, pUpdate->pData, pUpdate->nData);
+ d->nData += pUpdate->nData;
+ modified = 1;
+ }
+
+ return modified;
+}
+
+/* Split the second half of doclist d into a separate doclist d2. Returns 1
+ * if successful, or 0 if d contains a single document and hence can't be
+ * split. */
+static int docListSplit(DocList *d, DocList *d2){
+ const char *pSplitPoint = d->pData + d->nData / 2;
+ DocListReader reader;
+
+ readerInit(&reader, d);
+ while( reader.p<pSplitPoint ){
+ skipDocument(&reader);
+ }
+ if( readerAtEnd(&reader) ) return 0;
+ docListInit(d2, d->iType, reader.p, docListEnd(d) - reader.p);
+ d->nData = reader.p - d->pData;
+ d->pData = realloc(d->pData, d->nData);
+ return 1;
+}
+
+/* A DocListMerge computes the AND of an in-memory DocList [in] and a chunked
+ * on-disk doclist, resulting in another in-memory DocList [out]. [in]
+ * and [out] may or may not store position information according to the
+ * caller's wishes. The on-disk doclist always comes with positions.
+ *
+ * The caller must read each chunk of the on-disk doclist in succession and
+ * pass it to mergeBlock().
+ *
+ * If [in] has positions, then the merge output contains only documents with
+ * matching positions in the two input doclists. If [in] does not have
+ * positions, then the merge output contains all documents common to the two
+ * input doclists.
+ *
+ * If [in] is NULL, then the on-disk doclist is copied to [out] directly.
+ *
+ * A merge is performed using an integer [iOffset] provided by the caller.
+ * [iOffset] is subtracted from each position in the on-disk doclist for the
+ * purpose of position comparison; this is helpful in implementing phrase
+ * searches.
+ *
+ * A DocListMerge is not yet able to propagate offsets through query
+ * processing; we should add that capability soon.
+*/
+typedef struct DocListMerge {
+ DocListReader in;
+ DocList *pOut;
+ int iOffset;
+} DocListMerge;
+
+static void mergeInit(DocListMerge *m,
+ DocList *pIn, int iOffset, DocList *pOut){
+ readerInit(&m->in, pIn);
+ m->pOut = pOut;
+ m->iOffset = iOffset;
+
+ /* can't handle offsets yet */
+ assert( pIn==NULL || pIn->iType <= DL_POSITIONS );
+ assert( pOut->iType <= DL_POSITIONS );
+}
+
+/* A helper function for mergeBlock(), below. Merge the position lists
+ * pointed to by m->in and pBlockReader.
+ * If the merge matches, write [iDocid] to m->pOut; if m->pOut
+ * has positions then write all matching positions as well. */
+static void mergePosList(DocListMerge *m, sqlite_int64 iDocid,
+ DocListReader *pBlockReader){
+ int block_pos = readPosition(pBlockReader);
+ int in_pos = readPosition(&m->in);
+ int match = 0;
+ while( block_pos!=-1 || in_pos!=-1 ){
+ if( block_pos-m->iOffset==in_pos ){
+ if( !match ){
+ docListAddDocid(m->pOut, iDocid);
+ match = 1;
+ }
+ if( m->pOut->iType >= DL_POSITIONS ){
+ docListAddPos(m->pOut, in_pos);
+ }
+ block_pos = readPosition(pBlockReader);
+ in_pos = readPosition(&m->in);
+ } else if( in_pos==-1 || (block_pos!=-1 && block_pos-m->iOffset<in_pos) ){
+ block_pos = readPosition(pBlockReader);
+ } else {
+ in_pos = readPosition(&m->in);
+ }
+ }
+ if( m->pOut->iType >= DL_POSITIONS && match ){
+ docListAddEndPos(m->pOut);
+ }
+}
+
+/* Merge one block of an on-disk doclist into a DocListMerge. */
+static void mergeBlock(DocListMerge *m, DocList *pBlock){
+ DocListReader blockReader;
+ assert( pBlock->iType >= DL_POSITIONS );
+ readerInit(&blockReader, pBlock);
+ while( !readerAtEnd(&blockReader) ){
+ sqlite_int64 iDocid = readDocid(&blockReader);
+ if( m->in.pDoclist!=NULL ){
+ while( 1 ){
+ if( readerAtEnd(&m->in) ) return; /* nothing more to merge */
+ if( peekDocid(&m->in)>=iDocid ) break;
+ skipDocument(&m->in);
+ }
+ if( peekDocid(&m->in)>iDocid ){ /* [pIn] has no match with iDocid */
+ skipPositionList(&blockReader); /* skip this docid in the block */
+ continue;
+ }
+ readDocid(&m->in);
+ }
+ /* We have a document match. */
+ if( m->in.pDoclist==NULL || m->in.pDoclist->iType < DL_POSITIONS ){
+ /* We don't need to do a poslist merge. */
+ docListAddDocid(m->pOut, iDocid);
+ if( m->pOut->iType >= DL_POSITIONS ){
+ /* Copy all positions to the output doclist. */
+ while( 1 ){
+ int pos = readPosition(&blockReader);
+ if( pos==-1 ) break;
+ docListAddPos(m->pOut, pos);
+ }
+ docListAddEndPos(m->pOut);
+ } else skipPositionList(&blockReader);
+ continue;
+ }
+ mergePosList(m, iDocid, &blockReader);
+ }
+}
+
+static char *string_dup_n(const char *s, int n){
+ char *str = malloc(n + 1);
+ memcpy(str, s, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Duplicate a string; the caller must free() the returned string.
+ * (We don't use strdup() since it's not part of the standard C library and
+ * may not be available everywhere.) */
+static char *string_dup(const char *s){
+ return string_dup_n(s, strlen(s));
+}
+
+/* Format a string, replacing each occurrence of the % character with
+ * zName. This may be more convenient than sqlite_mprintf()
+ * when one string is used repeatedly in a format string.
+ * The caller must free() the returned string. */
+static char *string_format(const char *zFormat, const char *zName){
+ const char *p;
+ size_t len = 0;
+ size_t nName = strlen(zName);
+ char *result;
+ char *r;
+
+ /* first compute length needed */
+ for(p = zFormat ; *p ; ++p){
+ len += (*p=='%' ? nName : 1);
+ }
+ len += 1; /* for null terminator */
+
+ r = result = malloc(len);
+ for(p = zFormat; *p; ++p){
+ if( *p=='%' ){
+ memcpy(r, zName, nName);
+ r += nName;
+ } else {
+ *r++ = *p;
+ }
+ }
+ *r++ = '\0';
+ assert( r == result + len );
+ return result;
+}
+
+static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
+ char *zCommand = string_format(zFormat, zName);
+ int rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
+ free(zCommand);
+ return rc;
+}
+
+static int sql_prepare(sqlite3 *db, const char *zName, sqlite3_stmt **ppStmt,
+ const char *zFormat){
+ char *zCommand = string_format(zFormat, zName);
+ int rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
+ free(zCommand);
+ return rc;
+}
+
+/* end utility functions */
+
+#define QUERY_GENERIC 0
+#define QUERY_FULLTEXT 1
+
+#define CHUNK_MAX 1024
+
+typedef enum fulltext_statement {
+ CONTENT_INSERT_STMT,
+ CONTENT_SELECT_STMT,
+ CONTENT_DELETE_STMT,
+
+ TERM_SELECT_STMT,
+ TERM_CHUNK_SELECT_STMT,
+ TERM_INSERT_STMT,
+ TERM_UPDATE_STMT,
+ TERM_DELETE_STMT,
+
+ MAX_STMT /* Always at end! */
+} fulltext_statement;
+
+/* These must exactly match the enum above. */
+/* TODO(adam): Is there some risk that a statement (in particular,
+** pTermSelectStmt) will be used in two cursors at once, e.g. if a
+** query joins a virtual table to itself? If so perhaps we should
+** move some of these to the cursor object.
+*/
+static const char *fulltext_zStatement[MAX_STMT] = {
+ /* CONTENT_INSERT */ "insert into %_content (rowid, content) values (?, ?)",
+ /* CONTENT_SELECT */ "select content from %_content where rowid = ?",
+ /* CONTENT_DELETE */ "delete from %_content where rowid = ?",
+
+ /* TERM_SELECT */
+ "select rowid, doclist from %_term where term = ? and first = ?",
+ /* TERM_CHUNK_SELECT */
+ "select max(first) from %_term where term = ? and first <= ?",
+ /* TERM_INSERT */
+ "insert into %_term (term, first, doclist) values (?, ?, ?)",
+ /* TERM_UPDATE */ "update %_term set doclist = ? where rowid = ?",
+ /* TERM_DELETE */ "delete from %_term where rowid = ?",
+};
+
+typedef struct fulltext_vtab {
+ sqlite3_vtab base;
+ sqlite3 *db;
+ const char *zName; /* virtual table name */
+ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
+
+ /* Precompiled statements which we keep as long as the table is
+ ** open.
+ */
+ sqlite3_stmt *pFulltextStatements[MAX_STMT];
+} fulltext_vtab;
+
+typedef struct fulltext_cursor {
+ sqlite3_vtab_cursor base;
+ int iCursorType; /* QUERY_GENERIC or QUERY_FULLTEXT */
+
+ sqlite3_stmt *pStmt;
+
+ int eof;
+
+ /* The following is used only when iCursorType == QUERY_FULLTEXT. */
+ DocListReader result;
+} fulltext_cursor;
+
+static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
+ return (fulltext_vtab *) c->base.pVtab;
+}
+
+static sqlite3_module fulltextModule; /* forward declaration */
+
+/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
+** If the indicated statement has never been prepared, it is prepared
+** and cached, otherwise the cached version is reset.
+*/
+static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ assert( iStmt<MAX_STMT );
+ if( v->pFulltextStatements[iStmt]==NULL ){
+ int rc = sql_prepare(v->db, v->zName, &v->pFulltextStatements[iStmt],
+ fulltext_zStatement[iStmt]);
+ if( rc!=SQLITE_OK ) return rc;
+ } else {
+ int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ *ppStmt = v->pFulltextStatements[iStmt];
+ return SQLITE_OK;
+}
+
+/* Step the indicated statement, handling errors SQLITE_BUSY (by
+** retrying) and SQLITE_SCHEMA (by re-preparing and transferring
+** bindings to the new statement).
+** TODO(adam): We should extend this function so that it can work with
+** statements declared locally, not only globally cached statements.
+*/
+static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ int rc;
+ sqlite3_stmt *s = *ppStmt;
+ assert( iStmt<MAX_STMT );
+ assert( s==v->pFulltextStatements[iStmt] );
+
+ while( (rc=sqlite3_step(s))!=SQLITE_DONE && rc!=SQLITE_ROW ){
+ sqlite3_stmt *pNewStmt;
+
+ if( rc==SQLITE_BUSY ) continue;
+ if( rc!=SQLITE_ERROR ) return rc;
+
+ rc = sqlite3_reset(s);
+ if( rc!=SQLITE_SCHEMA ) return SQLITE_ERROR;
+
+ v->pFulltextStatements[iStmt] = NULL; /* Still in s */
+ rc = sql_get_statement(v, iStmt, &pNewStmt);
+ if( rc!=SQLITE_OK ) goto err;
+ *ppStmt = pNewStmt;
+
+ rc = sqlite3_transfer_bindings(s, pNewStmt);
+ if( rc!=SQLITE_OK ) goto err;
+
+ rc = sqlite3_finalize(s);
+ if( rc!=SQLITE_OK ) return rc;
+ s = pNewStmt;
+ }
+ return rc;
+
+ err:
+ sqlite3_finalize(s);
+ return rc;
+}
+
+/* Like sql_step_statement(), but convert SQLITE_DONE to SQLITE_OK.
+** Useful for statements like UPDATE, where we expect no results.
+*/
+static int sql_single_step_statement(fulltext_vtab *v,
+ fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ int rc = sql_step_statement(v, iStmt, ppStmt);
+ return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
+}
+
+/* insert into %_content (rowid, content) values ([rowid], [zContent]) */
+static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
+ const char *zContent, int nContent){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_value(s, 1, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 2, zContent, nContent, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, CONTENT_INSERT_STMT, &s);
+}
+
+/* select content from %_content where rowid = [iRow]
+ * The caller must delete the returned string. */
+static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
+ char **pzContent){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
+ if( rc!=SQLITE_ROW ) return rc;
+
+ *pzContent = string_dup((const char *)sqlite3_column_text(s, 0));
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ) return SQLITE_OK;
+
+ free(*pzContent);
+ return rc;
+}
+
+/* delete from %_content where rowid = [iRow ] */
+static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, CONTENT_DELETE_STMT, &s);
+}
+
+/* select rowid, doclist from %_term where term = [zTerm] and first = [iFirst]
+ * If found, returns SQLITE_OK; the caller must free the returned doclist.
+ * If no rows found, returns SQLITE_ERROR. */
+static int term_select(fulltext_vtab *v, const char *zTerm, int nTerm,
+ sqlite_int64 iFirst,
+ sqlite_int64 *rowid,
+ DocList *out){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_TRANSIENT);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, iFirst);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_step_statement(v, TERM_SELECT_STMT, &s);
+ if( rc!=SQLITE_ROW ) return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+
+ *rowid = sqlite3_column_int64(s, 0);
+ docListInit(out, DL_POSITIONS_OFFSETS,
+ sqlite3_column_blob(s, 1), sqlite3_column_bytes(s, 1));
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ return rc==SQLITE_DONE ? SQLITE_OK : rc;
+}
+
+/* select max(first) from %_term where term = [zTerm] and first <= [iFirst]
+ * If found, returns SQLITE_ROW and result in *piResult; if the query returns
+ * NULL (meaning no row found) returns SQLITE_DONE.
+ */
+static int term_chunk_select(fulltext_vtab *v, const char *zTerm, int nTerm,
+ sqlite_int64 iFirst, sqlite_int64 *piResult){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_CHUNK_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, iFirst);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_step_statement(v, TERM_CHUNK_SELECT_STMT, &s);
+ if( rc!=SQLITE_ROW ) return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+
+ switch( sqlite3_column_type(s, 0) ){
+ case SQLITE_NULL:
+ rc = SQLITE_DONE;
+ break;
+ case SQLITE_INTEGER:
+ *piResult = sqlite3_column_int64(s, 0);
+ break;
+ default:
+ return SQLITE_ERROR;
+ }
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ if( sqlite3_step(s) != SQLITE_DONE ) return SQLITE_ERROR;
+ return rc;
+}
+
+/* insert into %_term (term, first, doclist)
+ values ([zTerm], [iFirst], [doclist]) */
+static int term_insert(fulltext_vtab *v, const char *zTerm, int nTerm,
+ sqlite_int64 iFirst, DocList *doclist){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, iFirst);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 3, doclist->pData, doclist->nData, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_INSERT_STMT, &s);
+}
+
+/* update %_term set doclist = [doclist] where rowid = [rowid] */
+static int term_update(fulltext_vtab *v, sqlite_int64 rowid,
+ DocList *doclist){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_UPDATE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 1, doclist->pData, doclist->nData,
+ SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_UPDATE_STMT, &s);
+}
+
+static int term_delete(fulltext_vtab *v, sqlite_int64 rowid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, TERM_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, rowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step_statement(v, TERM_DELETE_STMT, &s);
+}
+
+static void fulltext_vtab_destroy(fulltext_vtab *v){
+ int iStmt;
+
+ for( iStmt=0; iStmt<MAX_STMT; iStmt++ ){
+ if( v->pFulltextStatements[iStmt]!=NULL ){
+ sqlite3_finalize(v->pFulltextStatements[iStmt]);
+ v->pFulltextStatements[iStmt] = NULL;
+ }
+ }
+
+ if( v->pTokenizer!=NULL ){
+ v->pTokenizer->pModule->xDestroy(v->pTokenizer);
+ v->pTokenizer = NULL;
+ }
+
+ free((void *) v->zName);
+ free(v);
+}
+
+/* Current interface:
+** argv[0] - module name
+** argv[1] - database name
+** argv[2] - table name
+** argv[3] - tokenizer name (optional, a sensible default is provided)
+** argv[4..] - passed to tokenizer (optional based on tokenizer)
+**/
+static int fulltextConnect(sqlite3 *db, void *pAux, int argc, char **argv,
+ sqlite3_vtab **ppVTab){
+ int rc;
+ fulltext_vtab *v;
+ sqlite3_tokenizer_module *m = NULL;
+
+ assert( argc>=3 );
+ v = (fulltext_vtab *) malloc(sizeof(fulltext_vtab));
+ /* sqlite will initialize v->base */
+ v->db = db;
+ v->zName = string_dup(argv[2]);
+ v->pTokenizer = NULL;
+
+ if( argc==3 ){
+ get_simple_tokenizer_module(&m);
+ } else {
+ /* TODO(shess) For now, add new tokenizers as else if clauses. */
+ if( !strcmp(argv[3], "simple") ){
+ get_simple_tokenizer_module(&m);
+ } else {
+ assert( "unrecognized tokenizer"==NULL );
+ }
+ }
+
+ /* TODO(shess) Since tokenization impacts the index, the parameters
+ ** to the tokenizer need to be identical when a persistent virtual
+ ** table is re-created. One solution would be a meta-table to track
+ ** such information in the database. Then we could verify that the
+ ** information is identical on subsequent creates.
+ */
+ /* TODO(shess) Why isn't argv already (const char **)? */
+ rc = m->xCreate(argc-3, (const char **) (argv+3), &v->pTokenizer);
+ if( rc!=SQLITE_OK ) return rc;
+ v->pTokenizer->pModule = m;
+
+ /* TODO: verify the existence of backing tables foo_content, foo_term */
+
+ rc = sqlite3_declare_vtab(db, "create table x(content text)");
+ if( rc!=SQLITE_OK ) return rc;
+
+ memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
+
+ *ppVTab = &v->base;
+ return SQLITE_OK;
+}
+
+static int fulltextCreate(sqlite3 *db, void *pAux, int argc, char **argv,
+ sqlite3_vtab **ppVTab){
+ int rc;
+ assert( argc>=3 );
+
+ /* The %_content table holds the text of each full-text item, with
+ ** the rowid used as the docid.
+ **
+ ** The %_term table maps each term to a document list blob
+ ** containing elements sorted by ascending docid, each element
+ ** encoded as:
+ **
+ ** docid varint-encoded
+ ** token count varint-encoded
+ ** "count" token elements (poslist):
+ ** position varint-encoded as delta from previous position
+ ** start offset varint-encoded as delta from previous start offset
+ ** end offset varint-encoded as delta from start offset
+ **
+ ** Additionally, doclist blobs can be chunked into multiple rows,
+ ** using "first" to order the blobs. "first" is simply the first
+ ** docid in the blob.
+ */
+ /*
+ ** NOTE(shess) That last sentence is incorrect in the face of
+ ** deletion, which can leave a doclist that doesn't contain the
+ ** first from that row. I _believe_ this does not matter to the
+ ** operation of the system, but it might be reasonable to update
+ ** appropriately in case this assumption becomes more important.
+ */
+ rc = sql_exec(db, argv[2],
+ "create table %_content(content text);"
+ "create table %_term(term text, first integer, doclist blob);"
+ "create index %_index on %_term(term, first)");
+ if( rc!=SQLITE_OK ) return rc;
+
+ return fulltextConnect(db, pAux, argc, argv, ppVTab);
+}
+
+/* Decide how to handle an SQL query.
+ * At the moment, MATCH queries can include implicit boolean ANDs; we
+ * haven't implemented phrase searches or OR yet. */
+static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+ int i;
+
+ for(i=0; i<pInfo->nConstraint; ++i){
+ const struct sqlite3_index_constraint *pConstraint;
+ pConstraint = &pInfo->aConstraint[i];
+ if( pConstraint->iColumn==0 &&
+ pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH &&
+ pConstraint->usable ){ /* a full-text search */
+ pInfo->aConstraintUsage[i].argvIndex = 1;
+ pInfo->aConstraintUsage[i].omit = 1;
+ pInfo->idxNum = QUERY_FULLTEXT;
+ pInfo->estimatedCost = 1.0; /* an arbitrary value for now */
+ return SQLITE_OK;
+ }
+ }
+ pInfo->idxNum = QUERY_GENERIC;
+ return SQLITE_OK;
+}
+
+static int fulltextDisconnect(sqlite3_vtab *pVTab){
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextDestroy(sqlite3_vtab *pVTab){
+ fulltext_vtab *v = (fulltext_vtab *)pVTab;
+
+ int rc = sql_exec(v->db, v->zName,
+ "drop table %_content; drop table %_term");
+ if( rc!=SQLITE_OK ) return rc;
+
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ fulltext_cursor *c;
+
+ c = (fulltext_cursor *) calloc(sizeof(fulltext_cursor), 1);
+ /* sqlite will initialize c->base */
+ *ppCursor = &c->base;
+
+ return SQLITE_OK;
+}
+
+static int fulltextClose(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ sqlite3_finalize(c->pStmt);
+ if( c->result.pDoclist!=NULL ){
+ docListDelete(c->result.pDoclist);
+ }
+ free(c);
+ return SQLITE_OK;
+}
+
+static int fulltextNext(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ sqlite_int64 iDocid;
+ int rc;
+
+ switch( c->iCursorType ){
+ case QUERY_GENERIC:
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ switch( rc ){
+ case SQLITE_ROW:
+ c->eof = 0;
+ return SQLITE_OK;
+ case SQLITE_DONE:
+ c->eof = 1;
+ return SQLITE_OK;
+ default:
+ c->eof = 1;
+ return rc;
+ }
+ case QUERY_FULLTEXT:
+ rc = sqlite3_reset(c->pStmt);
+ if( rc!=SQLITE_OK ) return rc;
+
+ if( readerAtEnd(&c->result)){
+ c->eof = 1;
+ return SQLITE_OK;
+ }
+ iDocid = readDocid(&c->result);
+ rc = sqlite3_bind_int64(c->pStmt, 1, iDocid);
+ if( rc!=SQLITE_OK ) return rc;
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ if( rc==SQLITE_ROW ){ /* the case we expect */
+ c->eof = 0;
+ return SQLITE_OK;
+ }
+ /* an error occurred; abort */
+ return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+ default:
+ assert( 0 );
+ return SQLITE_ERROR; /* not reached */
+ }
+}
+
+static int term_select_doclist(fulltext_vtab *v, const char *pTerm, int nTerm,
+ sqlite3_stmt **ppStmt){
+ int rc;
+ if( *ppStmt ){
+ rc = sqlite3_reset(*ppStmt);
+ } else {
+ rc = sql_prepare(v->db, v->zName, ppStmt,
+ "select doclist from %_term where term = ? order by first");
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_text(*ppStmt, 1, pTerm, nTerm, SQLITE_TRANSIENT);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sqlite3_step(*ppStmt); /* TODO(adamd): handle schema error */
+}
+
+/* Read the posting list for [zTerm]; AND it with the doclist [in] to
+ * produce the doclist [out], using the given offset [iOffset] for phrase
+ * matching.
+ * (*pSelect) is used to hold an SQLite statement used inside this function;
+ * the caller should initialize *pSelect to NULL before the first call.
+ */
+static int query_merge(fulltext_vtab *v, sqlite3_stmt **pSelect,
+ const char *zTerm,
+ DocList *pIn, int iOffset, DocList *out){
+ int rc;
+ DocListMerge merge;
+
+ if( pIn!=NULL && !pIn->nData ){
+ /* If [pIn] is already empty, there's no point in reading the
+ * posting list to AND it in; return immediately. */
+ return SQLITE_OK;
+ }
+
+ rc = term_select_doclist(v, zTerm, -1, pSelect);
+ if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc;
+
+ mergeInit(&merge, pIn, iOffset, out);
+ while( rc==SQLITE_ROW ){
+ DocList block;
+ docListInit(&block, DL_POSITIONS_OFFSETS,
+ sqlite3_column_blob(*pSelect, 0),
+ sqlite3_column_bytes(*pSelect, 0));
+ mergeBlock(&merge, &block);
+ docListDestroy(&block);
+
+ rc = sqlite3_step(*pSelect);
+ if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+ return rc;
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+typedef struct QueryTerm {
+ int is_phrase; /* true if this term begins a new phrase */
+ const char *zTerm;
+} QueryTerm;
+
+/* A parsed query.
+ *
+ * As an example, parsing the query ["four score" years "new nation"] will
+ * yield a Query with 5 terms:
+ * "four", is_phrase = 1
+ * "score", is_phrase = 0
+ * "years", is_phrase = 1
+ * "new", is_phrase = 1
+ * "nation", is_phrase = 0
+ */
+typedef struct Query {
+ int nTerms;
+ QueryTerm *pTerm;
+} Query;
+
+static void query_add(Query *q, int is_phrase, const char *zTerm){
+ QueryTerm *t;
+ ++q->nTerms;
+ q->pTerm = realloc(q->pTerm, q->nTerms * sizeof(q->pTerm[0]));
+ t = &q->pTerm[q->nTerms - 1];
+ t->is_phrase = is_phrase;
+ t->zTerm = zTerm;
+}
+
+static void query_free(Query *q){
+ int i;
+ for(i = 0; i < q->nTerms; ++i){
+ free((void *) q->pTerm[i].zTerm);
+ }
+ free(q->pTerm);
+}
+
+static int tokenize_segment(sqlite3_tokenizer *pTokenizer,
+ const char *zQuery, int in_phrase,
+ Query *pQuery){
+ sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
+ sqlite3_tokenizer_cursor *pCursor;
+ int is_first = 1;
+
+ int rc = pModule->xOpen(pTokenizer, zQuery, -1, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+ pCursor->pTokenizer = pTokenizer;
+
+ while( 1 ){
+ const char *zToken;
+ int nToken, iStartOffset, iEndOffset, dummy_pos;
+
+ rc = pModule->xNext(pCursor,
+ &zToken, &nToken,
+ &iStartOffset, &iEndOffset,
+ &dummy_pos);
+ if( rc!=SQLITE_OK ) break;
+ query_add(pQuery, !in_phrase || is_first, string_dup_n(zToken, nToken));
+ is_first = 0;
+ }
+
+ return pModule->xClose(pCursor);
+}
+
+/* Parse a query string, yielding a Query object. */
+static int parse_query(fulltext_vtab *v, const char *zQuery, Query *pQuery){
+ char *zQuery1 = string_dup(zQuery);
+ int in_phrase = 0;
+ char *s = zQuery1;
+ pQuery->nTerms = 0;
+ pQuery->pTerm = NULL;
+
+ while( *s ){
+ char *t = s;
+ while( *t ){
+ if( *t=='"' ){
+ *t++ = '\0';
+ break;
+ }
+ ++t;
+ }
+ if( *s ){
+ tokenize_segment(v->pTokenizer, s, in_phrase, pQuery);
+ }
+ s = t;
+ in_phrase = !in_phrase;
+ }
+
+ free(zQuery1);
+ return SQLITE_OK;
+}
+
+/* Perform a full-text query; return a list of documents in [pResult]. */
+static int fulltext_query(fulltext_vtab *v, const char *zQuery,
+ DocList **pResult){
+ Query q;
+ int phrase_start = -1;
+ int i;
+ sqlite3_stmt *pSelect = NULL;
+ DocList *d = NULL;
+
+ int rc = parse_query(v, zQuery, &q);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Merge terms. */
+ for(i = 0 ; i < q.nTerms ; ++i){
+ /* In each merge step, we need to generate positions whenever we're
+ * processing a phrase which hasn't ended yet. */
+ int need_positions = i<q.nTerms-1 && !q.pTerm[i+1].is_phrase;
+ DocList *next = docListNew(need_positions ? DL_POSITIONS : DL_DOCIDS);
+ if( q.pTerm[i].is_phrase ){
+ phrase_start = i;
+ }
+ rc = query_merge(v, &pSelect, q.pTerm[i].zTerm, d, i - phrase_start, next);
+ if( rc!=SQLITE_OK ) break;
+ if( d!=NULL ){
+ docListDelete(d);
+ }
+ d = next;
+ }
+
+ sqlite3_finalize(pSelect);
+ query_free(&q);
+ *pResult = d;
+ return rc;
+}
+
+static int fulltextFilter(sqlite3_vtab_cursor *pCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ fulltext_vtab *v = cursor_vtab(c);
+ int rc;
+ const char *zStatement;
+
+ c->iCursorType = idxNum;
+ switch( idxNum ){
+ case QUERY_GENERIC:
+ zStatement = "select rowid, content from %_content";
+ break;
+
+ case QUERY_FULLTEXT: /* full-text search */
+ {
+ const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
+ DocList *pResult;
+ assert( argc==1 );
+ rc = fulltext_query(v, zQuery, &pResult);
+ if( rc!=SQLITE_OK ) return rc;
+ readerInit(&c->result, pResult);
+ zStatement = "select rowid, content from %_content where rowid = ?";
+ break;
+ }
+
+ default:
+ assert( 0 );
+ }
+
+ rc = sql_prepare(v->db, v->zName, &c->pStmt, zStatement);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return fulltextNext(pCursor);
+}
+
+static int fulltextEof(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ return c->eof;
+}
+
+static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *pContext, int idxCol){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ const char *s;
+
+ assert( idxCol==0 );
+ s = (const char *) sqlite3_column_text(c->pStmt, 1);
+ sqlite3_result_text(pContext, s, -1, SQLITE_TRANSIENT);
+
+ return SQLITE_OK;
+}
+
+static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+
+ *pRowid = sqlite3_column_int64(c->pStmt, 0);
+ return SQLITE_OK;
+}
+
+/* Build a hash table containing all terms in zText. */
+static int build_terms(Hash *terms, sqlite3_tokenizer *pTokenizer,
+ const char *zText, sqlite_int64 iDocid){
+ sqlite3_tokenizer_cursor *pCursor;
+ const char *pToken;
+ int nTokenBytes;
+ int iStartOffset, iEndOffset, iPosition;
+
+ int rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pCursor->pTokenizer = pTokenizer;
+ HashInit(terms, HASH_STRING, 1);
+ while( SQLITE_OK==pTokenizer->pModule->xNext(pCursor,
+ &pToken, &nTokenBytes,
+ &iStartOffset, &iEndOffset,
+ &iPosition) ){
+ DocList *p;
+
+ /* Positions can't be negative; we use -1 as a terminator internally. */
+ if( iPosition<0 ) {
+ rc = SQLITE_ERROR;
+ goto err;
+ }
+
+ p = HashFind(terms, pToken, nTokenBytes);
+ if( p==NULL ){
+ p = docListNew(DL_POSITIONS_OFFSETS);
+ docListAddDocid(p, iDocid);
+ HashInsert(terms, pToken, nTokenBytes, p);
+ }
+ docListAddPosOffset(p, iPosition, iStartOffset, iEndOffset);
+ }
+
+err:
+ /* TODO(shess) Check return? Should this be able to cause errors at
+ ** this point? Actually, same question about sqlite3_finalize(),
+ ** though one could argue that failure there means that the data is
+ ** not durable. *ponder*
+ */
+ pTokenizer->pModule->xClose(pCursor);
+ return rc;
+}
+/* Update the %_terms table to map the term [zTerm] to the given rowid. */
+static int index_insert_term(fulltext_vtab *v, const char *zTerm, int nTerm,
+ sqlite_int64 iDocid, DocList *p){
+ sqlite_int64 iFirst;
+ sqlite_int64 iIndexRow;
+ DocList doclist;
+
+ int rc = term_chunk_select(v, zTerm, nTerm, iDocid, &iFirst);
+ if( rc==SQLITE_DONE ){
+ docListInit(&doclist, DL_POSITIONS_OFFSETS, 0, 0);
+ if( docListUpdate(&doclist, iDocid, p) ){
+ rc = term_insert(v, zTerm, nTerm, iDocid, &doclist);
+ docListDestroy(&doclist);
+ return rc;
+ }
+ return SQLITE_OK;
+ }
+ if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
+
+ /* This word is in the index; add this document ID to its blob. */
+
+ rc = term_select(v, zTerm, nTerm, iFirst, &iIndexRow, &doclist);
+ if( rc!=SQLITE_OK ) return rc;
+
+ if( docListUpdate(&doclist, iDocid, p) ){
+ /* If the blob is too big, split it in half. */
+ if( doclist.nData>CHUNK_MAX ){
+ DocList half;
+ if( docListSplit(&doclist, &half) ){
+ rc = term_insert(v, zTerm, nTerm, firstDocid(&half), &half);
+ docListDestroy(&half);
+ if( rc!=SQLITE_OK ) goto err;
+ }
+ }
+ rc = term_update(v, iIndexRow, &doclist);
+ }
+
+err:
+ docListDestroy(&doclist);
+ return rc;
+}
+
+/* Insert a row into the full-text index; set *piRowid to be the ID of the
+ * new row. */
+static int index_insert(fulltext_vtab *v,
+ sqlite3_value *pRequestRowid, const char *zText,
+ sqlite_int64 *piRowid){
+ Hash terms; /* maps term string -> PosList */
+ HashElem *e;
+
+ int rc = content_insert(v, pRequestRowid, zText, -1);
+ if( rc!=SQLITE_OK ) return rc;
+ *piRowid = sqlite3_last_insert_rowid(v->db);
+
+ if( !zText ) return SQLITE_OK; /* nothing to index */
+
+ rc = build_terms(&terms, v->pTokenizer, zText, *piRowid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(e=HashFirst(&terms); e; e=HashNext(e)){
+ DocList *p = HashData(e);
+ rc = index_insert_term(v, HashKey(e), HashKeysize(e), *piRowid, p);
+ if( rc!=SQLITE_OK ) break;
+ }
+
+ for(e=HashFirst(&terms); e; e=HashNext(e)){
+ DocList *p = HashData(e);
+ docListDelete(p);
+ }
+ HashClear(&terms);
+ return rc;
+}
+
+static int index_delete_term(fulltext_vtab *v, const char *zTerm, int nTerm,
+ sqlite_int64 iDocid){
+ sqlite_int64 iFirst;
+ sqlite_int64 iIndexRow;
+ DocList doclist;
+
+ int rc = term_chunk_select(v, zTerm, nTerm, iDocid, &iFirst);
+ if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
+
+ rc = term_select(v, zTerm, nTerm, iFirst, &iIndexRow, &doclist);
+ if( rc!=SQLITE_OK ) return rc;
+
+ if( docListUpdate(&doclist, iDocid, NULL) ){
+ if( doclist.nData>0 ){
+ rc = term_update(v, iIndexRow, &doclist);
+ } else { /* empty posting list */
+ rc = term_delete(v, iIndexRow);
+ }
+ }
+ docListDestroy(&doclist);
+ return rc;
+}
+
+/* Delete a row from the full-text index. */
+static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){
+ char *zText;
+ Hash terms;
+ HashElem *e;
+
+ int rc = content_select(v, iRow, &zText);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = build_terms(&terms, v->pTokenizer, zText, iRow);
+ free(zText);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(e=HashFirst(&terms); e; e=HashNext(e)){
+ rc = index_delete_term(v, HashKey(e), HashKeysize(e), iRow);
+ if( rc!=SQLITE_OK ) break;
+ }
+ for(e=HashFirst(&terms); e; e=HashNext(e)){
+ DocList *p = HashData(e);
+ docListDelete(p);
+ }
+ HashClear(&terms);
+
+ return content_delete(v, iRow);
+}
+
+static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
+ sqlite_int64 *pRowid){
+ fulltext_vtab *v = (fulltext_vtab *) pVtab;
+
+ if( nArg<2 ){
+ return index_delete(v, sqlite3_value_int64(ppArg[0]));
+ }
+
+ if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
+ return SQLITE_ERROR; /* an update; not yet supported */
+ }
+
+ assert( nArg==3 ); /* ppArg[1] = rowid, ppArg[2] = content */
+ return index_insert(v, ppArg[1],
+ (const char *)sqlite3_value_text(ppArg[2]), pRowid);
+}
+
+static sqlite3_module fulltextModule = {
+ 0,
+ fulltextCreate,
+ fulltextConnect,
+ fulltextBestIndex,
+ fulltextDisconnect,
+ fulltextDestroy,
+ fulltextOpen,
+ fulltextClose,
+ fulltextFilter,
+ fulltextNext,
+ fulltextEof,
+ fulltextColumn,
+ fulltextRowid,
+ fulltextUpdate
+};
+
+int fulltext_init(sqlite3 *db){
+ return sqlite3_create_module(db, "fulltext", &fulltextModule, 0);
+}
+
+#if !SQLITE_CORE
+int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg,
+ const sqlite3_api_routines *pApi){
+ SQLITE_EXTENSION_INIT2(pApi)
+ return fulltext_init(db);
+}
+#endif
diff --git a/third_party/sqlite/ext/fts1/fulltext.h b/third_party/sqlite/ext/fts1/fulltext.h
new file mode 100755
index 0000000..477dcab
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/fulltext.h
@@ -0,0 +1,11 @@
+#include "sqlite3.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int fulltext_init(sqlite3 *db);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
diff --git a/third_party/sqlite/ext/fts1/simple_tokenizer.c b/third_party/sqlite/ext/fts1/simple_tokenizer.c
new file mode 100755
index 0000000..d00a770
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/simple_tokenizer.c
@@ -0,0 +1,174 @@
+/*
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Implementation of the "simple" full-text-search tokenizer.
+*/
+
+#include <assert.h>
+#if !defined(__APPLE__)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "tokenizer.h"
+
+/* Duplicate a string; the caller must free() the returned string.
+ * (We don't use strdup() since it's not part of the standard C library and
+ * may not be available everywhere.) */
+/* TODO(shess) Copied from fulltext.c, consider util.c for such
+** things. */
+static char *string_dup(const char *s){
+ char *str = malloc(strlen(s) + 1);
+ strcpy(str, s);
+ return str;
+}
+
+typedef struct simple_tokenizer {
+ sqlite3_tokenizer base;
+ const char *zDelim; /* token delimiters */
+} simple_tokenizer;
+
+typedef struct simple_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ const char *pInput; /* input we are tokenizing */
+ int nBytes; /* size of the input */
+ const char *pCurrent; /* current position in pInput */
+ int iToken; /* index of next token to be returned */
+ char *zToken; /* storage for current token */
+ int nTokenBytes; /* actual size of current token */
+ int nTokenAllocated; /* space allocated to zToken buffer */
+} simple_tokenizer_cursor;
+
+static sqlite3_tokenizer_module simpleTokenizerModule;/* forward declaration */
+
+static int simpleCreate(
+ int argc, const char **argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ simple_tokenizer *t;
+
+ t = (simple_tokenizer *) malloc(sizeof(simple_tokenizer));
+ /* TODO(shess) Delimiters need to remain the same from run to run,
+ ** else we need to reindex. One solution would be a meta-table to
+ ** track such information in the database, then we'd only want this
+ ** information on the initial create.
+ */
+ if( argc>1 ){
+ t->zDelim = string_dup(argv[1]);
+ } else {
+ /* Build a string excluding alphanumeric ASCII characters */
+ char zDelim[0x80]; /* nul-terminated, so nul not a member */
+ int i, j;
+ for(i=1, j=0; i<0x80; i++){
+ if( !isalnum(i) ){
+ zDelim[j++] = i;
+ }
+ }
+ zDelim[j++] = '\0';
+ assert( j<=sizeof(zDelim) );
+ t->zDelim = string_dup(zDelim);
+ }
+
+ *ppTokenizer = &t->base;
+ return SQLITE_OK;
+}
+
+static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
+ simple_tokenizer *t = (simple_tokenizer *) pTokenizer;
+
+ free((void *) t->zDelim);
+ free(t);
+
+ return SQLITE_OK;
+}
+
+static int simpleOpen(
+ sqlite3_tokenizer *pTokenizer,
+ const char *pInput, int nBytes,
+ sqlite3_tokenizer_cursor **ppCursor
+){
+ simple_tokenizer_cursor *c;
+
+ c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
+ c->pInput = pInput;
+ c->nBytes = nBytes<0 ? (int) strlen(pInput) : nBytes;
+ c->pCurrent = c->pInput; /* start tokenizing at the beginning */
+ c->iToken = 0;
+ c->zToken = NULL; /* no space allocated, yet. */
+ c->nTokenBytes = 0;
+ c->nTokenAllocated = 0;
+
+ *ppCursor = &c->base;
+ return SQLITE_OK;
+}
+
+static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+
+ if( NULL!=c->zToken ){
+ free(c->zToken);
+ }
+ free(c);
+
+ return SQLITE_OK;
+}
+
+static int simpleNext(
+ sqlite3_tokenizer_cursor *pCursor,
+ const char **ppToken, int *pnBytes,
+ int *piStartOffset, int *piEndOffset, int *piPosition
+){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+ simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
+ int ii;
+
+ while( c->pCurrent-c->pInput<c->nBytes ){
+ int n = (int) strcspn(c->pCurrent, t->zDelim);
+ if( n>0 ){
+ if( n+1>c->nTokenAllocated ){
+ c->zToken = realloc(c->zToken, n+1);
+ }
+ for(ii=0; ii<n; ii++){
+ /* TODO(shess) This needs expansion to handle UTF-8
+ ** case-insensitivity.
+ */
+ char ch = c->pCurrent[ii];
+ c->zToken[ii] = (unsigned char)ch<0x80 ? tolower(ch) : ch;
+ }
+ c->zToken[n] = '\0';
+ *ppToken = c->zToken;
+ *pnBytes = n;
+ *piStartOffset = (int) (c->pCurrent-c->pInput);
+ *piEndOffset = *piStartOffset+n;
+ *piPosition = c->iToken++;
+ c->pCurrent += n + 1;
+
+ return SQLITE_OK;
+ }
+ c->pCurrent += n + 1;
+ /* TODO(shess) could strspn() to skip delimiters en masse. Needs
+ ** to happen in two places, though, which is annoying.
+ */
+ }
+ return SQLITE_DONE;
+}
+
+static sqlite3_tokenizer_module simpleTokenizerModule = {
+ 0,
+ simpleCreate,
+ simpleDestroy,
+ simpleOpen,
+ simpleClose,
+ simpleNext,
+};
+
+void get_simple_tokenizer_module(
+ sqlite3_tokenizer_module **ppModule
+){
+ *ppModule = &simpleTokenizerModule;
+}
diff --git a/third_party/sqlite/ext/fts1/tokenizer.h b/third_party/sqlite/ext/fts1/tokenizer.h
new file mode 100755
index 0000000..1d7bd1f
--- /dev/null
+++ b/third_party/sqlite/ext/fts1/tokenizer.h
@@ -0,0 +1,89 @@
+/*
+** 2006 July 10
+**
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Defines the interface to tokenizers used by fulltext-search. There
+** are three basic components:
+**
+** sqlite3_tokenizer_module is a singleton defining the tokenizer
+** interface functions. This is essentially the class structure for
+** tokenizers.
+**
+** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
+** including customization information defined at creation time.
+**
+** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
+** tokens from a particular input.
+*/
+#ifndef _TOKENIZER_H_
+#define _TOKENIZER_H_
+
+/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
+** If tokenizers are to be allowed to call sqlite3_*() functions, then
+** we will need a way to register the API consistently.
+*/
+#include "sqlite3.h"
+
+/*
+** Structures used by the tokenizer interface.
+*/
+typedef struct sqlite3_tokenizer sqlite3_tokenizer;
+typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
+typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
+
+struct sqlite3_tokenizer_module {
+ int iVersion; /* currently 0 */
+
+ /*
+ ** Create and destroy a tokenizer. argc/argv are passed down from
+ ** the fulltext virtual table creation to allow customization.
+ */
+ int (*xCreate)(int argc, const char **argv,
+ sqlite3_tokenizer **ppTokenizer);
+ int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
+
+ /*
+ ** Tokenize a particular input. Call xOpen() to prepare to
+ ** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
+ ** xClose() to free any internal state. The pInput passed to
+ ** xOpen() must exist until the cursor is closed. The ppToken
+ ** result from xNext() is only valid until the next call to xNext()
+ ** or until xClose() is called.
+ */
+ /* TODO(shess) current implementation requires pInput to be
+ ** nul-terminated. This should either be fixed, or pInput/nBytes
+ ** should be converted to zInput.
+ */
+ int (*xOpen)(sqlite3_tokenizer *pTokenizer,
+ const char *pInput, int nBytes,
+ sqlite3_tokenizer_cursor **ppCursor);
+ int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
+ int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
+ const char **ppToken, int *pnBytes,
+ int *piStartOffset, int *piEndOffset, int *piPosition);
+};
+
+struct sqlite3_tokenizer {
+ sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+struct sqlite3_tokenizer_cursor {
+ sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+/*
+** Get the module for a tokenizer which generates tokens based on a
+** set of non-token characters. The default is to break tokens at any
+** non-alnum character, though the set of delimiters can also be
+** specified by the first argv argument to xCreate().
+*/
+/* TODO(shess) This doesn't belong here. Need some sort of
+** registration process.
+*/
+void get_simple_tokenizer_module(sqlite3_tokenizer_module **ppModule);
+
+#endif /* _TOKENIZER_H_ */
diff --git a/third_party/sqlite/ext/fts2/README.tokenizers b/third_party/sqlite/ext/fts2/README.tokenizers
new file mode 100755
index 0000000..98d2021
--- /dev/null
+++ b/third_party/sqlite/ext/fts2/README.tokenizers
@@ -0,0 +1,133 @@
+
+1. FTS2 Tokenizers
+
+ When creating a new full-text table, FTS2 allows the user to select
+ the text tokenizer implementation to be used when indexing text
+ by specifying a "tokenizer" clause as part of the CREATE VIRTUAL TABLE
+ statement:
+
+ CREATE VIRTUAL TABLE <table-name> USING fts2(
+ <columns ...> [, tokenizer <tokenizer-name> [<tokenizer-args>]]
+ );
+
+ The built-in tokenizers (valid values to pass as <tokenizer name>) are
+ "simple" and "porter".
+
+ <tokenizer-args> should consist of zero or more white-space separated
+ arguments to pass to the selected tokenizer implementation. The
+ interpretation of the arguments, if any, depends on the individual
+ tokenizer.
+
+2. Custom Tokenizers
+
+ FTS2 allows users to provide custom tokenizer implementations. The
+ interface used to create a new tokenizer is defined and described in
+ the fts2_tokenizer.h source file.
+
+ Registering a new FTS2 tokenizer is similar to registering a new
+ virtual table module with SQLite. The user passes a pointer to a
+ structure containing pointers to various callback functions that
+ make up the implementation of the new tokenizer type. For tokenizers,
+ the structure (defined in fts2_tokenizer.h) is called
+ "sqlite3_tokenizer_module".
+
+ FTS2 does not expose a C-function that users call to register new
+ tokenizer types with a database handle. Instead, the pointer must
+ be encoded as an SQL blob value and passed to FTS2 through the SQL
+ engine by evaluating a special scalar function, "fts2_tokenizer()".
+ The fts2_tokenizer() function may be called with one or two arguments,
+ as follows:
+
+ SELECT fts2_tokenizer(<tokenizer-name>);
+ SELECT fts2_tokenizer(<tokenizer-name>, <sqlite3_tokenizer_module ptr>);
+
+ Where <tokenizer-name> is a string identifying the tokenizer and
+ <sqlite3_tokenizer_module ptr> is a pointer to an sqlite3_tokenizer_module
+ structure encoded as an SQL blob. If the second argument is present,
+ it is registered as tokenizer <tokenizer-name> and a copy of it
+ returned. If only one argument is passed, a pointer to the tokenizer
+ implementation currently registered as <tokenizer-name> is returned,
+ encoded as a blob. Or, if no such tokenizer exists, an SQL exception
+ (error) is raised.
+
+ SECURITY: If the fts2 extension is used in an environment where potentially
+ malicious users may execute arbitrary SQL (i.e. gears), they should be
+ prevented from invoking the fts2_tokenizer() function, possibly using the
+ authorisation callback.
+
+ See "Sample code" below for an example of calling the fts2_tokenizer()
+ function from C code.
+
+3. ICU Library Tokenizers
+
+ If this extension is compiled with the SQLITE_ENABLE_ICU pre-processor
+ symbol defined, then there exists a built-in tokenizer named "icu"
+ implemented using the ICU library. The first argument passed to the
+ xCreate() method (see fts2_tokenizer.h) of this tokenizer may be
+ an ICU locale identifier. For example "tr_TR" for Turkish as used
+ in Turkey, or "en_AU" for English as used in Australia. For example:
+
+ "CREATE VIRTUAL TABLE thai_text USING fts2(text, tokenizer icu th_TH)"
+
+ The ICU tokenizer implementation is very simple. It splits the input
+ text according to the ICU rules for finding word boundaries and discards
+ any tokens that consist entirely of white-space. This may be suitable
+ for some applications in some locales, but not all. If more complex
+ processing is required, for example to implement stemming or
+ discard punctuation, this can be done by creating a tokenizer
+ implementation that uses the ICU tokenizer as part of its implementation.
+
+ When using the ICU tokenizer this way, it is safe to overwrite the
+ contents of the strings returned by the xNext() method (see
+ fts2_tokenizer.h).
+
+4. Sample code.
+
+ The following two code samples illustrate the way C code should invoke
+ the fts2_tokenizer() scalar function:
+
+ int registerTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module *p
+ ){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts2_tokenizer(?, ?)";
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
+ sqlite3_step(pStmt);
+
+ return sqlite3_finalize(pStmt);
+ }
+
+ int queryTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module **pp
+ ){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts2_tokenizer(?)";
+
+ *pp = 0;
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
+ memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
+ }
+ }
+
+ return sqlite3_finalize(pStmt);
+ }
diff --git a/third_party/sqlite/ext/fts2/README.txt b/third_party/sqlite/ext/fts2/README.txt
new file mode 100755
index 0000000..517a2a0
--- /dev/null
+++ b/third_party/sqlite/ext/fts2/README.txt
@@ -0,0 +1,4 @@
+This folder contains source code to the second full-text search
+extension for SQLite. While the API is the same, this version uses a
+substantially different storage schema from fts1, so tables will need
+to be rebuilt.
diff --git a/third_party/sqlite/fts2.c b/third_party/sqlite/ext/fts2/fts2.c
index 15ed0b5..35ab87c 100644..100755
--- a/third_party/sqlite/fts2.c
+++ b/third_party/sqlite/ext/fts2/fts2.c
@@ -37,6 +37,20 @@
** This is an SQLite module implementing full-text search.
*/
+/* TODO(shess): To make it easier to spot changes without groveling
+** through changelogs, I've defined GEARS_FTS2_CHANGES to call them
+** out, and I will document them here. On imports, these changes
+** should be reviewed to make sure they are still present, or are
+** dropped as appropriate.
+**
+** SQLite core adds the custom function fts2_tokenizer() to be used
+** for defining new tokenizers. The second parameter is a vtable
+** pointer encoded as a blob. Obviously this cannot be exposed to
+** Gears callers for security reasons. It could be suppressed in the
+** authorizer, but for now I have simply commented the definition out.
+*/
+#define GEARS_FTS2_CHANGES 1
+
/*
** The code in this file is only compiled if:
**
@@ -274,7 +288,7 @@
** even with many segments.
**
** TODO(shess) That said, it would be nice to have a better query-side
-** argument for MERGE_COUNT of 16. Also, it's possible/likely that
+** argument for MERGE_COUNT of 16. Also, it is possible/likely that
** optimizations to things like doclist merging will swing the sweet
** spot around.
**
@@ -335,6 +349,16 @@ SQLITE_EXTENSION_INIT1
# define TRACE(A)
#endif
+#if 0
+/* Useful to set breakpoints. See main.c sqlite3Corrupt(). */
+static int fts2Corrupt(void){
+ return SQLITE_CORRUPT;
+}
+# define SQLITE_CORRUPT_BKPT fts2Corrupt()
+#else
+# define SQLITE_CORRUPT_BKPT SQLITE_CORRUPT
+#endif
+
/* It is not safe to call isspace(), tolower(), or isalnum() on
** hi-bit-set characters. This is the same solution used in the
** tokenizer.
@@ -455,6 +479,7 @@ static int getVarint32(const char *p, int *pi){
** dataBufferInit - create a buffer with given initial capacity.
** dataBufferReset - forget buffer's data, retaining capacity.
** dataBufferDestroy - free buffer's data.
+** dataBufferSwap - swap contents of two buffers.
** dataBufferExpand - expand capacity without adding data.
** dataBufferAppend - append data.
** dataBufferAppend2 - append two pieces of data at once.
@@ -479,6 +504,11 @@ static void dataBufferDestroy(DataBuffer *pBuffer){
if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData);
SCRAMBLE(pBuffer);
}
+static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){
+ DataBuffer tmp = *pBuffer1;
+ *pBuffer1 = *pBuffer2;
+ *pBuffer2 = tmp;
+}
static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){
assert( nAddCapacity>0 );
/* TODO(shess) Consider expanding more aggressively. Note that the
@@ -694,7 +724,7 @@ static void dlrDestroy(DLReader *pReader){
#ifndef NDEBUG
/* Verify that the doclist can be validly decoded. Also returns the
-** last docid found because it's convenient in other assertions for
+** last docid found because it is convenient in other assertions for
** DLWriter.
*/
static void docListValidate(DocListType iType, const char *pData, int nData,
@@ -1057,7 +1087,7 @@ typedef struct DLCollector {
/* TODO(shess) This could also be done by calling plwTerminate() and
** dataBufferAppend(). I tried that, expecting nominal performance
** differences, but it seemed to pretty reliably be worth 1% to code
-** it this way. I suspect it's the incremental malloc overhead (some
+** it this way. I suspect it is the incremental malloc overhead (some
** percentage of the plwTerminate() calls will cause a realloc), so
** this might be worth revisiting if the DataBuffer implementation
** changes.
@@ -1615,7 +1645,7 @@ static char *string_dup_n(const char *s, int n){
}
/* Duplicate a string; the caller must free() the returned string.
- * (We don't use strdup() since it's not part of the standard C library and
+ * (We don't use strdup() since it is not part of the standard C library and
* may not be available everywhere.) */
static char *string_dup(const char *s){
return string_dup_n(s, strlen(s));
@@ -3186,8 +3216,8 @@ static void snippetOffsetText(Snippet *p){
for(i=0; i<p->nMatch; i++){
struct snippetMatch *pMatch = &p->aMatch[i];
zBuf[0] = ' ';
- sprintf(&zBuf[cnt>0], "%d %d %d %d", pMatch->iCol,
- pMatch->iTerm, pMatch->iStart, pMatch->nByte);
+ sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
+ pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
append(&sb, zBuf);
cnt++;
}
@@ -3415,8 +3445,11 @@ static int fulltextNext(sqlite3_vtab_cursor *pCursor){
c->eof = 0;
return SQLITE_OK;
}
- /* an error occurred; abort */
- return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+
+ /* Corrupt if the index refers to missing document. */
+ if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT;
+
+ return rc;
}
}
@@ -3563,17 +3596,20 @@ static int tokenizeSegment(
pQuery->nextIsOr = 1;
continue;
}
-
- // If a tokenizer recognizes "*" as a separate word, we mark the word
- // before it as a prefix search. The other isPrefix code below that tests
- // for a word that's tokenized with the star as its last character.
+
+ /*
+ * The ICU tokenizer considers '*' a break character, so the code below
+ * sets isPrefix correctly, but since that code doesn't eat the '*', the
+ * ICU tokenizer returns it as the next token. So eat it here until a
+ * better solution presents itself.
+ */
if( pQuery->nTerms>0 && nToken==1 && pSegment[iBegin]=='*' &&
iEndLast==iBegin){
pQuery->pTerms[pQuery->nTerms-1].isPrefix = 1;
continue;
}
iEndLast = iEnd;
-
+
queryAdd(pQuery, pToken, nToken);
if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
@@ -3783,18 +3819,38 @@ static int fulltextFilter(
fulltext_cursor *c = (fulltext_cursor *) pCursor;
fulltext_vtab *v = cursor_vtab(c);
int rc;
- char *zSql;
TRACE(("FTS2 Filter %p\n",pCursor));
- zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
- idxNum==QUERY_GENERIC ? "" : "where rowid=?");
- sqlite3_finalize(c->pStmt);
- rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
- sqlite3_free(zSql);
- if( rc!=SQLITE_OK ) return rc;
+ /* If the cursor has a statement that was not prepared according to
+ ** idxNum, clear it. I believe all calls to fulltextFilter with a
+ ** given cursor will have the same idxNum , but in this case it's
+ ** easy to be safe.
+ */
+ if( c->pStmt && c->iCursorType!=idxNum ){
+ sqlite3_finalize(c->pStmt);
+ c->pStmt = NULL;
+ }
+
+ /* Get a fresh statement appropriate to idxNum. */
+ /* TODO(shess): Add a prepared-statement cache in the vt structure.
+ ** The cache must handle multiple open cursors. Easier to cache the
+ ** statement variants at the vt to reduce malloc/realloc/free here.
+ ** Or we could have a StringBuffer variant which allowed stack
+ ** construction for small values.
+ */
+ if( !c->pStmt ){
+ char *zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
+ idxNum==QUERY_GENERIC ? "" : "where rowid=?");
+ rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) return rc;
+ c->iCursorType = idxNum;
+ }else{
+ sqlite3_reset(c->pStmt);
+ assert( c->iCursorType==idxNum );
+ }
- c->iCursorType = idxNum;
switch( idxNum ){
case QUERY_GENERIC:
break;
@@ -3889,17 +3945,18 @@ static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid,
if( rc!=SQLITE_OK ) return rc;
pCursor->pTokenizer = pTokenizer;
- while( SQLITE_OK==pTokenizer->pModule->xNext(pCursor,
- &pToken, &nTokenBytes,
- &iStartOffset, &iEndOffset,
- &iPosition) ){
+ while( SQLITE_OK==(rc=pTokenizer->pModule->xNext(pCursor,
+ &pToken, &nTokenBytes,
+ &iStartOffset, &iEndOffset,
+ &iPosition)) ){
DLCollector *p;
int nData; /* Size of doclist before our update. */
- /* Positions can't be negative; we use -1 as a terminator internally. */
+ /* Positions can't be negative; we use -1 as a terminator
+ * internally. Token can't be NULL or empty. */
if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){
- pTokenizer->pModule->xClose(pCursor);
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
+ break;
}
p = fts2HashFind(&v->pendingTerms, pToken, nTokenBytes);
@@ -3928,6 +3985,7 @@ static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid,
** not durable. *ponder*
*/
pTokenizer->pModule->xClose(pCursor);
+ if( SQLITE_DONE == rc ) return SQLITE_OK;
return rc;
}
@@ -5061,6 +5119,9 @@ static void leavesReaderDestroy(LeavesReader *pReader){
** the leaf data was entirely contained in the root), or from the
** stream of blocks between iStartBlockid and iEndBlockid, inclusive.
*/
+/* TODO(shess): Figure out a means of indicating how many leaves are
+** expected, for purposes of detecting corruption.
+*/
static int leavesReaderInit(fulltext_vtab *v,
int idx,
sqlite_int64 iStartBlockid,
@@ -5072,6 +5133,10 @@ static int leavesReaderInit(fulltext_vtab *v,
dataBufferInit(&pReader->rootData, 0);
if( iStartBlockid==0 ){
+ /* Corrupt if this can't be a leaf node. */
+ if( pRootData==NULL || nRootData<1 || pRootData[0]!='\0' ){
+ return SQLITE_CORRUPT_BKPT;
+ }
/* Entire leaf level fit in root data. */
dataBufferReplace(&pReader->rootData, pRootData, nRootData);
leafReaderInit(pReader->rootData.pData, pReader->rootData.nData,
@@ -5082,22 +5147,48 @@ static int leavesReaderInit(fulltext_vtab *v,
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_bind_int64(s, 1, iStartBlockid);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ) goto err;
rc = sqlite3_bind_int64(s, 2, iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ) goto err;
rc = sqlite3_step(s);
+
+ /* Corrupt if interior node referenced missing leaf node. */
if( rc==SQLITE_DONE ){
- pReader->eof = 1;
- return SQLITE_OK;
+ rc = SQLITE_CORRUPT_BKPT;
+ goto err;
+ }
+
+ if( rc!=SQLITE_ROW ) goto err;
+ rc = SQLITE_OK;
+
+ /* Corrupt if leaf data isn't a blob. */
+ if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ const char *pLeafData = sqlite3_column_blob(s, 0);
+ int nLeafData = sqlite3_column_bytes(s, 0);
+
+ /* Corrupt if this can't be a leaf node. */
+ if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ leafReaderInit(pLeafData, nLeafData, &pReader->leafReader);
+ }
+ }
+
+ err:
+ if( rc!=SQLITE_OK ){
+ if( idx==-1 ){
+ sqlite3_finalize(s);
+ }else{
+ sqlite3_reset(s);
+ }
+ return rc;
}
- if( rc!=SQLITE_ROW ) return rc;
pReader->pStmt = s;
- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
- sqlite3_column_bytes(pReader->pStmt, 0),
- &pReader->leafReader);
}
return SQLITE_OK;
}
@@ -5120,10 +5211,22 @@ static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){
pReader->eof = 1;
return rc==SQLITE_DONE ? SQLITE_OK : rc;
}
- leafReaderDestroy(&pReader->leafReader);
- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
- sqlite3_column_bytes(pReader->pStmt, 0),
- &pReader->leafReader);
+
+ /* Corrupt if leaf data isn't a blob. */
+ if( sqlite3_column_type(pReader->pStmt, 0)!=SQLITE_BLOB ){
+ return SQLITE_CORRUPT_BKPT;
+ }else{
+ const char *pLeafData = sqlite3_column_blob(pReader->pStmt, 0);
+ int nLeafData = sqlite3_column_bytes(pReader->pStmt, 0);
+
+ /* Corrupt if this can't be a leaf node. */
+ if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+
+ leafReaderDestroy(&pReader->leafReader);
+ leafReaderInit(pLeafData, nLeafData, &pReader->leafReader);
+ }
}
return SQLITE_OK;
}
@@ -5185,6 +5288,14 @@ static int leavesReadersInit(fulltext_vtab *v, int iLevel,
const char *pRootData = sqlite3_column_blob(s, 2);
int nRootData = sqlite3_column_bytes(s, 2);
+ /* Corrupt if we get back different types than we stored. */
+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){
+ rc = SQLITE_CORRUPT_BKPT;
+ break;
+ }
+
assert( i<MERGE_COUNT );
rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData,
&pReaders[i]);
@@ -5196,6 +5307,7 @@ static int leavesReadersInit(fulltext_vtab *v, int iLevel,
while( i-->0 ){
leavesReaderDestroy(&pReaders[i]);
}
+ sqlite3_reset(s); /* So we don't leave a lock. */
return rc;
}
@@ -5324,6 +5436,26 @@ static int segmentMerge(fulltext_vtab *v, int iLevel){
return rc;
}
+/* Accumulate the union of *acc and *pData into *acc. */
+static void docListAccumulateUnion(DataBuffer *acc,
+ const char *pData, int nData) {
+ DataBuffer tmp = *acc;
+ dataBufferInit(acc, tmp.nData+nData);
+ docListUnion(tmp.pData, tmp.nData, pData, nData, acc);
+ dataBufferDestroy(&tmp);
+}
+
+/* TODO(shess) It might be interesting to explore different merge
+** strategies, here. For instance, since this is a sorted merge, we
+** could easily merge many doclists in parallel. With some
+** comprehension of the storage format, we could merge all of the
+** doclists within a leaf node directly from the leaf node's storage.
+** It may be worthwhile to merge smaller doclists before larger
+** doclists, since they can be traversed more quickly - but the
+** results may have less overlap, making them more expensive in a
+** different way.
+*/
+
/* Scan pReader for pTerm/nTerm, and merge the term's doclist over
** *out (any doclists with duplicate docids overwrite those in *out).
** Internal function for loadSegmentLeaf().
@@ -5331,39 +5463,116 @@ static int segmentMerge(fulltext_vtab *v, int iLevel){
static int loadSegmentLeavesInt(fulltext_vtab *v, LeavesReader *pReader,
const char *pTerm, int nTerm, int isPrefix,
DataBuffer *out){
+ /* doclist data is accumulated into pBuffers similar to how one does
+ ** increment in binary arithmetic. If index 0 is empty, the data is
+ ** stored there. If there is data there, it is merged and the
+ ** results carried into position 1, with further merge-and-carry
+ ** until an empty position is found.
+ */
+ DataBuffer *pBuffers = NULL;
+ int nBuffers = 0, nMaxBuffers = 0, rc;
+
assert( nTerm>0 );
- /* Process while the prefix matches. */
- while( !leavesReaderAtEnd(pReader) ){
+ for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader);
+ rc=leavesReaderStep(v, pReader)){
/* TODO(shess) Really want leavesReaderTermCmp(), but that name is
** already taken to compare the terms of two LeavesReaders. Think
** on a better name. [Meanwhile, break encapsulation rather than
** use a confusing name.]
*/
- int rc;
int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix);
+ if( c>0 ) break; /* Past any possible matches. */
if( c==0 ){
const char *pData = leavesReaderData(pReader);
- int nData = leavesReaderDataBytes(pReader);
- if( out->nData==0 ){
- dataBufferReplace(out, pData, nData);
+ int iBuffer, nData = leavesReaderDataBytes(pReader);
+
+ /* Find the first empty buffer. */
+ for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+ if( 0==pBuffers[iBuffer].nData ) break;
+ }
+
+ /* Out of buffers, add an empty one. */
+ if( iBuffer==nBuffers ){
+ if( nBuffers==nMaxBuffers ){
+ DataBuffer *p;
+ nMaxBuffers += 20;
+
+ /* Manual realloc so we can handle NULL appropriately. */
+ p = sqlite3_malloc(nMaxBuffers*sizeof(*pBuffers));
+ if( p==NULL ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+
+ if( nBuffers>0 ){
+ assert(pBuffers!=NULL);
+ memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers));
+ sqlite3_free(pBuffers);
+ }
+ pBuffers = p;
+ }
+ dataBufferInit(&(pBuffers[nBuffers]), 0);
+ nBuffers++;
+ }
+
+ /* At this point, must have an empty at iBuffer. */
+ assert(iBuffer<nBuffers && pBuffers[iBuffer].nData==0);
+
+ /* If empty was first buffer, no need for merge logic. */
+ if( iBuffer==0 ){
+ dataBufferReplace(&(pBuffers[0]), pData, nData);
}else{
- DataBuffer result;
- dataBufferInit(&result, out->nData+nData);
- docListUnion(out->pData, out->nData, pData, nData, &result);
- dataBufferDestroy(out);
- *out = result;
- /* TODO(shess) Rather than destroy out, we could retain it for
- ** later reuse.
+ /* pAcc is the empty buffer the merged data will end up in. */
+ DataBuffer *pAcc = &(pBuffers[iBuffer]);
+ DataBuffer *p = &(pBuffers[0]);
+
+ /* Handle position 0 specially to avoid need to prime pAcc
+ ** with pData/nData.
*/
+ dataBufferSwap(p, pAcc);
+ docListAccumulateUnion(pAcc, pData, nData);
+
+ /* Accumulate remaining doclists into pAcc. */
+ for(++p; p<pAcc; ++p){
+ docListAccumulateUnion(pAcc, p->pData, p->nData);
+
+ /* dataBufferReset() could allow a large doclist to blow up
+ ** our memory requirements.
+ */
+ if( p->nCapacity<1024 ){
+ dataBufferReset(p);
+ }else{
+ dataBufferDestroy(p);
+ dataBufferInit(p, 0);
+ }
+ }
}
}
- if( c>0 ) break; /* Past any possible matches. */
+ }
- rc = leavesReaderStep(v, pReader);
- if( rc!=SQLITE_OK ) return rc;
+ /* Union all the doclists together into *out. */
+ /* TODO(shess) What if *out is big? Sigh. */
+ if( rc==SQLITE_OK && nBuffers>0 ){
+ int iBuffer;
+ for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+ if( pBuffers[iBuffer].nData>0 ){
+ if( out->nData==0 ){
+ dataBufferSwap(out, &(pBuffers[iBuffer]));
+ }else{
+ docListAccumulateUnion(out, pBuffers[iBuffer].pData,
+ pBuffers[iBuffer].nData);
+ }
+ }
+ }
}
- return SQLITE_OK;
+
+ while( nBuffers-- ){
+ dataBufferDestroy(&(pBuffers[nBuffers]));
+ }
+ if( pBuffers!=NULL ) sqlite3_free(pBuffers);
+
+ return rc;
}
/* Call loadSegmentLeavesInt() with pData/nData as input. */
@@ -5414,7 +5623,7 @@ static int loadSegmentLeaves(fulltext_vtab *v,
/* TODO(shess) The calling code may already know that the end child is
** not worth calculating, because the end may be in a later sibling
** node. Consider whether breaking symmetry is worthwhile. I suspect
-** it's not worthwhile.
+** it is not worthwhile.
*/
static void getChildrenContaining(const char *pData, int nData,
const char *pTerm, int nTerm, int isPrefix,
@@ -5475,11 +5684,27 @@ static int loadAndGetChildrenContaining(
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_ERROR;
+ /* Corrupt if interior node references missing child node. */
+ if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT;
if( rc!=SQLITE_ROW ) return rc;
- getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
- pTerm, nTerm, isPrefix, piStartChild, piEndChild);
+ /* Corrupt if child node isn't a blob. */
+ if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){
+ sqlite3_reset(s); /* So we don't leave a lock. */
+ return SQLITE_CORRUPT_BKPT;
+ }else{
+ const char *pData = sqlite3_column_blob(s, 0);
+ int nData = sqlite3_column_bytes(s, 0);
+
+ /* Corrupt if child is not a valid interior node. */
+ if( pData==NULL || nData<1 || pData[0]=='\0' ){
+ sqlite3_reset(s); /* So we don't leave a lock. */
+ return SQLITE_CORRUPT_BKPT;
+ }
+
+ getChildrenContaining(pData, nData, pTerm, nTerm,
+ isPrefix, piStartChild, piEndChild);
+ }
/* We expect only one row. We must execute another sqlite3_step()
* to complete the iteration; otherwise the table will remain
@@ -5562,7 +5787,8 @@ static int loadSegment(fulltext_vtab *v, const char *pData, int nData,
DataBuffer result;
int rc;
- assert( nData>1 );
+ /* Corrupt if segment root can't be valid. */
+ if( pData==NULL || nData<1 ) return SQLITE_CORRUPT_BKPT;
/* This code should never be called with buffered updates. */
assert( v->nPendingData<0 );
@@ -5616,6 +5842,14 @@ static int termSelect(fulltext_vtab *v, int iColumn,
const char *pData = sqlite3_column_blob(s, 2);
const int nData = sqlite3_column_bytes(s, 2);
const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
+
+ /* Corrupt if we get back different types than we stored. */
+ if( sqlite3_column_type(s, 1)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto err;
+ }
+
rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix,
&doclist);
if( rc!=SQLITE_OK ) goto err;
@@ -5635,6 +5869,7 @@ static int termSelect(fulltext_vtab *v, int iColumn,
}
err:
+ sqlite3_reset(s); /* So we don't leave a lock. */
dataBufferDestroy(&doclist);
return rc;
}
@@ -5757,7 +5992,7 @@ static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){
return SQLITE_OK;
}
-/* This function implements the xUpdate callback; it's the top-level entry
+/* This function implements the xUpdate callback; it is the top-level entry
* point for inserting, deleting or updating a row in a full-text table. */
static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
sqlite_int64 *pRowid){
@@ -6127,6 +6362,14 @@ static void optimizeFunc(sqlite3_context *pContext,
const char *pRootData = sqlite3_column_blob(s, 2);
int nRootData = sqlite3_column_bytes(s, 2);
+ /* Corrupt if we get back different types than we stored. */
+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){
+ rc = SQLITE_CORRUPT_BKPT;
+ break;
+ }
+
assert( i<nReaders );
rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData,
&readers[i].reader);
@@ -6140,6 +6383,8 @@ static void optimizeFunc(sqlite3_context *pContext,
if( rc==SQLITE_DONE ){
assert( i==nReaders );
rc = optimizeInternal(v, readers, nReaders, &writer);
+ }else{
+ sqlite3_reset(s); /* So we don't leave a lock. */
}
while( i-- > 0 ){
@@ -6173,7 +6418,7 @@ static void optimizeFunc(sqlite3_context *pContext,
{
char buf[512];
sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s",
- sqlite3_errmsg(v->db));
+ sqlite3_errmsg(sqlite3_context_db_handle(pContext)));
sqlite3_result_error(pContext, buf, -1);
}
}
@@ -6203,9 +6448,18 @@ static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s,
const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1);
const char *pRootData = sqlite3_column_blob(s, 2);
const int nRootData = sqlite3_column_bytes(s, 2);
+ int rc;
LeavesReader reader;
- int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid,
- pRootData, nRootData, &reader);
+
+ /* Corrupt if we get back different types than we stored. */
+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER ||
+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+
+ rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid,
+ pRootData, nRootData, &reader);
if( rc!=SQLITE_OK ) return rc;
while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){
@@ -6709,7 +6963,11 @@ int sqlite3Fts2Init(sqlite3 *db){
** module with sqlite.
*/
if( SQLITE_OK==rc
+#if GEARS_FTS2_CHANGES && !SQLITE_TEST
+ /* fts2_tokenizer() disabled for security reasons. */
+#else
&& SQLITE_OK==(rc = sqlite3Fts2InitHashTable(db, pHash, "fts2_tokenizer"))
+#endif
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1))
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1))
diff --git a/third_party/sqlite/fts2.h b/third_party/sqlite/ext/fts2/fts2.h
index 4da4c38..4da4c38 100644..100755
--- a/third_party/sqlite/fts2.h
+++ b/third_party/sqlite/ext/fts2/fts2.h
diff --git a/third_party/sqlite/fts2_hash.c b/third_party/sqlite/ext/fts2/fts2_hash.c
index fcd5cc2..f22fcc91 100644..100755
--- a/third_party/sqlite/fts2_hash.c
+++ b/third_party/sqlite/ext/fts2/fts2_hash.c
@@ -29,15 +29,22 @@
#include <stdlib.h>
#include <string.h>
+#include "sqlite3.h"
#include "fts2_hash.h"
-static void *malloc_and_zero(int n){
- void *p = malloc(n);
+/*
+** Malloc and Free functions
+*/
+static void *fts2HashMalloc(int n){
+ void *p = sqlite3_malloc(n);
if( p ){
memset(p, 0, n);
}
return p;
}
+static void fts2HashFree(void *p){
+ sqlite3_free(p);
+}
/* Turn bulk memory into a hash table object by initializing the
** fields of the Hash structure.
@@ -58,8 +65,6 @@ void sqlite3Fts2HashInit(fts2Hash *pNew, int keyClass, int copyKey){
pNew->count = 0;
pNew->htsize = 0;
pNew->ht = 0;
- pNew->xMalloc = malloc_and_zero;
- pNew->xFree = free;
}
/* Remove all entries from a hash table. Reclaim all memory.
@@ -72,15 +77,15 @@ void sqlite3Fts2HashClear(fts2Hash *pH){
assert( pH!=0 );
elem = pH->first;
pH->first = 0;
- if( pH->ht ) pH->xFree(pH->ht);
+ fts2HashFree(pH->ht);
pH->ht = 0;
pH->htsize = 0;
while( elem ){
fts2HashElem *next_elem = elem->next;
if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
+ fts2HashFree(elem->pKey);
}
- pH->xFree(elem);
+ fts2HashFree(elem);
elem = next_elem;
}
pH->count = 0;
@@ -192,9 +197,9 @@ static void rehash(fts2Hash *pH, int new_size){
int (*xHash)(const void*,int); /* The hash function */
assert( (new_size & (new_size-1))==0 );
- new_ht = (struct _fts2ht *)pH->xMalloc( new_size*sizeof(struct _fts2ht) );
+ new_ht = (struct _fts2ht *)fts2HashMalloc( new_size*sizeof(struct _fts2ht) );
if( new_ht==0 ) return;
- if( pH->ht ) pH->xFree(pH->ht);
+ fts2HashFree(pH->ht);
pH->ht = new_ht;
pH->htsize = new_size;
xHash = hashFunction(pH->keyClass);
@@ -260,9 +265,9 @@ static void removeElementGivenHash(
pEntry->chain = 0;
}
if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
+ fts2HashFree(elem->pKey);
}
- pH->xFree( elem );
+ fts2HashFree( elem );
pH->count--;
if( pH->count<=0 ){
assert( pH->first==0 );
@@ -333,12 +338,12 @@ void *sqlite3Fts2HashInsert(
return old_data;
}
if( data==0 ) return 0;
- new_elem = (fts2HashElem*)pH->xMalloc( sizeof(fts2HashElem) );
+ new_elem = (fts2HashElem*)fts2HashMalloc( sizeof(fts2HashElem) );
if( new_elem==0 ) return data;
if( pH->copyKey && pKey!=0 ){
- new_elem->pKey = pH->xMalloc( nKey );
+ new_elem->pKey = fts2HashMalloc( nKey );
if( new_elem->pKey==0 ){
- pH->xFree(new_elem);
+ fts2HashFree(new_elem);
return data;
}
memcpy((void*)new_elem->pKey, pKey, nKey);
@@ -351,7 +356,7 @@ void *sqlite3Fts2HashInsert(
rehash(pH,8);
if( pH->htsize==0 ){
pH->count = 0;
- pH->xFree(new_elem);
+ fts2HashFree(new_elem);
return data;
}
}
diff --git a/third_party/sqlite/fts2_hash.h b/third_party/sqlite/ext/fts2/fts2_hash.h
index 97f3529..571aa2c1 100644..100755
--- a/third_party/sqlite/fts2_hash.h
+++ b/third_party/sqlite/ext/fts2/fts2_hash.h
@@ -34,8 +34,6 @@ struct fts2Hash {
char copyKey; /* True if copy of key made on insert */
int count; /* Number of entries in this table */
fts2HashElem *first; /* The first element of the array */
- void *(*xMalloc)(int); /* malloc() function to use */
- void (*xFree)(void *); /* free() function to use */
int htsize; /* Number of buckets in the hash table */
struct _fts2ht { /* the hash table */
int count; /* Number of entries with this hash */
diff --git a/third_party/sqlite/fts2_icu.c b/third_party/sqlite/ext/fts2/fts2_icu.c
index 7c777e5..917c2ec 100644..100755
--- a/third_party/sqlite/fts2_icu.c
+++ b/third_party/sqlite/ext/fts2/fts2_icu.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file implements a tokenizer for fts2 based on the ICU library.
**
-** $Id: fts2_icu.c,v 1.1 2007/06/22 15:21:16 danielk1977 Exp $
+** $Id: fts2_icu.c,v 1.2 2008/07/22 22:20:50 shess Exp $
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
@@ -112,10 +112,7 @@ static int icuOpen(
*ppCursor = 0;
- // TODO(evanm): why is this necessary?
- if (nInput == -1)
- nInput = strlen(zInput);
-
+ if( -1 == nInput ) nInput = strlen(zInput);
nChar = nInput+1;
pCsr = (IcuCursor *)sqlite3_malloc(
sizeof(IcuCursor) + /* IcuCursor */
diff --git a/third_party/sqlite/fts2_porter.c b/third_party/sqlite/ext/fts2/fts2_porter.c
index 97a95c8..97a95c8 100644..100755
--- a/third_party/sqlite/fts2_porter.c
+++ b/third_party/sqlite/ext/fts2/fts2_porter.c
diff --git a/third_party/sqlite/fts2_tokenizer.c b/third_party/sqlite/ext/fts2/fts2_tokenizer.c
index d3cbc8e..43eba11 100644..100755
--- a/third_party/sqlite/fts2_tokenizer.c
+++ b/third_party/sqlite/ext/fts2/fts2_tokenizer.c
@@ -25,9 +25,6 @@
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
-// TODO(evanm): figure out why this is the only file that #includes sqlite3.h
-// without #defining SQLITE_CORE first.
-#define SQLITE_CORE 1
#include "sqlite3.h"
#include "sqlite3ext.h"
@@ -36,6 +33,7 @@ SQLITE_EXTENSION_INIT1
#include "fts2_hash.h"
#include "fts2_tokenizer.h"
#include <assert.h>
+#include <stddef.h>
/*
** Implementation of the SQL scalar function for accessing the underlying
diff --git a/third_party/sqlite/fts2_tokenizer.h b/third_party/sqlite/ext/fts2/fts2_tokenizer.h
index 8c256b2..8c256b2 100644..100755
--- a/third_party/sqlite/fts2_tokenizer.h
+++ b/third_party/sqlite/ext/fts2/fts2_tokenizer.h
diff --git a/third_party/sqlite/fts2_tokenizer1.c b/third_party/sqlite/ext/fts2/fts2_tokenizer1.c
index f2ba49e..f2ba49e 100644..100755
--- a/third_party/sqlite/fts2_tokenizer1.c
+++ b/third_party/sqlite/ext/fts2/fts2_tokenizer1.c
diff --git a/third_party/sqlite/ext/fts2/mkfts2amal.tcl b/third_party/sqlite/ext/fts2/mkfts2amal.tcl
new file mode 100755
index 0000000..5c8d1e9
--- /dev/null
+++ b/third_party/sqlite/ext/fts2/mkfts2amal.tcl
@@ -0,0 +1,116 @@
+#!/usr/bin/tclsh
+#
+# This script builds a single C code file holding all of FTS2 code.
+# The name of the output file is fts2amal.c. To build this file,
+# first do:
+#
+# make target_source
+#
+# The make target above moves all of the source code files into
+# a subdirectory named "tsrc". (This script expects to find the files
+# there and will not work if they are not found.)
+#
+# After the "tsrc" directory has been created and populated, run
+# this script:
+#
+# tclsh mkfts2amal.tcl
+#
+# The amalgamated FTS2 code will be written into fts2amal.c
+#
+
+# Open the output file and write a header comment at the beginning
+# of the file.
+#
+set out [open fts2amal.c w]
+set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1]
+puts $out [subst \
+{/******************************************************************************
+** This file is an amalgamation of separate C source files from the SQLite
+** Full Text Search extension 2 (fts2). By combining all the individual C
+** code files into this single large file, the entire code can be compiled
+** as a one translation unit. This allows many compilers to do optimizations
+** that would not be possible if the files were compiled separately. It also
+** makes the code easier to import into other projects.
+**
+** This amalgamation was generated on $today.
+*/}]
+
+# These are the header files used by FTS2. The first time any of these
+# files are seen in a #include statement in the C code, include the complete
+# text of the file in-line. The file only needs to be included once.
+#
+foreach hdr {
+ fts2.h
+ fts2_hash.h
+ fts2_tokenizer.h
+ sqlite3.h
+ sqlite3ext.h
+} {
+ set available_hdr($hdr) 1
+}
+
+# 78 stars used for comment formatting.
+set s78 \
+{*****************************************************************************}
+
+# Insert a comment into the code
+#
+proc section_comment {text} {
+ global out s78
+ set n [string length $text]
+ set nstar [expr {60 - $n}]
+ set stars [string range $s78 0 $nstar]
+ puts $out "/************** $text $stars/"
+}
+
+# Read the source file named $filename and write it into the
+# sqlite3.c output file. If any #include statements are seen,
+# process them approprately.
+#
+proc copy_file {filename} {
+ global seen_hdr available_hdr out
+ set tail [file tail $filename]
+ section_comment "Begin file $tail"
+ set in [open $filename r]
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} {
+ if {[info exists available_hdr($hdr)]} {
+ if {$available_hdr($hdr)} {
+ section_comment "Include $hdr in the middle of $tail"
+ copy_file tsrc/$hdr
+ section_comment "Continuing where we left off in $tail"
+ }
+ } elseif {![info exists seen_hdr($hdr)]} {
+ set seen_hdr($hdr) 1
+ puts $out $line
+ }
+ } elseif {[regexp {^#ifdef __cplusplus} $line]} {
+ puts $out "#if 0"
+ } elseif {[regexp {^#line} $line]} {
+ # Skip #line directives.
+ } else {
+ puts $out $line
+ }
+ }
+ close $in
+ section_comment "End of $tail"
+}
+
+
+# Process the source files. Process files containing commonly
+# used subroutines first in order to help the compiler find
+# inlining opportunities.
+#
+foreach file {
+ fts2.c
+ fts2_hash.c
+ fts2_porter.c
+ fts2_tokenizer.c
+ fts2_tokenizer1.c
+ fts2_icu.c
+} {
+ copy_file tsrc/$file
+}
+
+close $out
diff --git a/third_party/sqlite/ext/fts3/README.tokenizers b/third_party/sqlite/ext/fts3/README.tokenizers
new file mode 100755
index 0000000..83d2f9d
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/README.tokenizers
@@ -0,0 +1,133 @@
+
+1. FTS3 Tokenizers
+
+ When creating a new full-text table, FTS3 allows the user to select
+ the text tokenizer implementation to be used when indexing text
+ by specifying a "tokenizer" clause as part of the CREATE VIRTUAL TABLE
+ statement:
+
+ CREATE VIRTUAL TABLE <table-name> USING fts3(
+ <columns ...> [, tokenizer <tokenizer-name> [<tokenizer-args>]]
+ );
+
+ The built-in tokenizers (valid values to pass as <tokenizer name>) are
+ "simple" and "porter".
+
+ <tokenizer-args> should consist of zero or more white-space separated
+ arguments to pass to the selected tokenizer implementation. The
+ interpretation of the arguments, if any, depends on the individual
+ tokenizer.
+
+2. Custom Tokenizers
+
+ FTS3 allows users to provide custom tokenizer implementations. The
+ interface used to create a new tokenizer is defined and described in
+ the fts3_tokenizer.h source file.
+
+ Registering a new FTS3 tokenizer is similar to registering a new
+ virtual table module with SQLite. The user passes a pointer to a
+ structure containing pointers to various callback functions that
+ make up the implementation of the new tokenizer type. For tokenizers,
+ the structure (defined in fts3_tokenizer.h) is called
+ "sqlite3_tokenizer_module".
+
+ FTS3 does not expose a C-function that users call to register new
+ tokenizer types with a database handle. Instead, the pointer must
+ be encoded as an SQL blob value and passed to FTS3 through the SQL
+ engine by evaluating a special scalar function, "fts3_tokenizer()".
+ The fts3_tokenizer() function may be called with one or two arguments,
+ as follows:
+
+ SELECT fts3_tokenizer(<tokenizer-name>);
+ SELECT fts3_tokenizer(<tokenizer-name>, <sqlite3_tokenizer_module ptr>);
+
+ Where <tokenizer-name> is a string identifying the tokenizer and
+ <sqlite3_tokenizer_module ptr> is a pointer to an sqlite3_tokenizer_module
+ structure encoded as an SQL blob. If the second argument is present,
+ it is registered as tokenizer <tokenizer-name> and a copy of it
+ returned. If only one argument is passed, a pointer to the tokenizer
+ implementation currently registered as <tokenizer-name> is returned,
+ encoded as a blob. Or, if no such tokenizer exists, an SQL exception
+ (error) is raised.
+
+ SECURITY: If the fts3 extension is used in an environment where potentially
+ malicious users may execute arbitrary SQL (i.e. gears), they should be
+ prevented from invoking the fts3_tokenizer() function, possibly using the
+ authorisation callback.
+
+ See "Sample code" below for an example of calling the fts3_tokenizer()
+ function from C code.
+
+3. ICU Library Tokenizers
+
+ If this extension is compiled with the SQLITE_ENABLE_ICU pre-processor
+ symbol defined, then there exists a built-in tokenizer named "icu"
+ implemented using the ICU library. The first argument passed to the
+ xCreate() method (see fts3_tokenizer.h) of this tokenizer may be
+ an ICU locale identifier. For example "tr_TR" for Turkish as used
+ in Turkey, or "en_AU" for English as used in Australia. For example:
+
+ "CREATE VIRTUAL TABLE thai_text USING fts3(text, tokenizer icu th_TH)"
+
+ The ICU tokenizer implementation is very simple. It splits the input
+ text according to the ICU rules for finding word boundaries and discards
+ any tokens that consist entirely of white-space. This may be suitable
+ for some applications in some locales, but not all. If more complex
+ processing is required, for example to implement stemming or
+ discard punctuation, this can be done by creating a tokenizer
+ implementation that uses the ICU tokenizer as part of its implementation.
+
+ When using the ICU tokenizer this way, it is safe to overwrite the
+ contents of the strings returned by the xNext() method (see
+ fts3_tokenizer.h).
+
+4. Sample code.
+
+ The following two code samples illustrate the way C code should invoke
+ the fts3_tokenizer() scalar function:
+
+ int registerTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module *p
+ ){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
+ sqlite3_step(pStmt);
+
+ return sqlite3_finalize(pStmt);
+ }
+
+ int queryTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module **pp
+ ){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts3_tokenizer(?)";
+
+ *pp = 0;
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
+ memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
+ }
+ }
+
+ return sqlite3_finalize(pStmt);
+ }
diff --git a/third_party/sqlite/ext/fts3/README.txt b/third_party/sqlite/ext/fts3/README.txt
new file mode 100755
index 0000000..517a2a0
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/README.txt
@@ -0,0 +1,4 @@
+This folder contains source code to the second full-text search
+extension for SQLite. While the API is the same, this version uses a
+substantially different storage schema from fts1, so tables will need
+to be rebuilt.
diff --git a/third_party/sqlite/ext/fts3/fts3.c b/third_party/sqlite/ext/fts3/fts3.c
new file mode 100755
index 0000000..cc6dcd8
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3.c
@@ -0,0 +1,7216 @@
+/*
+** 2006 Oct 10
+**
+** 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 is an SQLite module implementing full-text search.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS3 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS3 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
+*/
+
+/* TODO(shess) Consider exporting this comment to an HTML file or the
+** wiki.
+*/
+/* The full-text index is stored in a series of b+tree (-like)
+** structures called segments which map terms to doclists. The
+** structures are like b+trees in layout, but are constructed from the
+** bottom up in optimal fashion and are not updatable. Since trees
+** are built from the bottom up, things will be described from the
+** bottom up.
+**
+**
+**** Varints ****
+** The basic unit of encoding is a variable-length integer called a
+** varint. We encode variable-length integers in little-endian order
+** using seven bits * per byte as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** and so on.
+**
+** This is identical to how sqlite encodes varints (see util.c).
+**
+**
+**** Document lists ****
+** A doclist (document list) holds a docid-sorted list of hits for a
+** given term. Doclists hold docids, and can optionally associate
+** token positions and offsets with docids.
+**
+** A DL_POSITIONS_OFFSETS doclist is stored like this:
+**
+** array {
+** varint docid;
+** array { (position list for column 0)
+** varint position; (delta from previous position plus POS_BASE)
+** varint startOffset; (delta from previous startOffset)
+** varint endOffset; (delta from startOffset)
+** }
+** array {
+** varint POS_COLUMN; (marks start of position list for new column)
+** varint column; (index of new column)
+** array {
+** varint position; (delta from previous position plus POS_BASE)
+** varint startOffset;(delta from previous startOffset)
+** varint endOffset; (delta from startOffset)
+** }
+** }
+** varint POS_END; (marks end of positions for this document.
+** }
+**
+** Here, array { X } means zero or more occurrences of X, adjacent in
+** memory. A "position" is an index of a token in the token stream
+** generated by the tokenizer, while an "offset" is a byte offset,
+** both based at 0. Note that POS_END and POS_COLUMN occur in the
+** same logical place as the position element, and act as sentinals
+** ending a position list array.
+**
+** A DL_POSITIONS doclist omits the startOffset and endOffset
+** information. A DL_DOCIDS doclist omits both the position and
+** offset information, becoming an array of varint-encoded docids.
+**
+** On-disk data is stored as type DL_DEFAULT, so we don't serialize
+** the type. Due to how deletion is implemented in the segmentation
+** system, on-disk doclists MUST store at least positions.
+**
+**
+**** Segment leaf nodes ****
+** Segment leaf nodes store terms and doclists, ordered by term. Leaf
+** nodes are written using LeafWriter, and read using LeafReader (to
+** iterate through a single leaf node's data) and LeavesReader (to
+** iterate through a segment's entire leaf layer). Leaf nodes have
+** the format:
+**
+** varint iHeight; (height from leaf level, always 0)
+** varint nTerm; (length of first term)
+** char pTerm[nTerm]; (content of first term)
+** varint nDoclist; (length of term's associated doclist)
+** char pDoclist[nDoclist]; (content of doclist)
+** array {
+** (further terms are delta-encoded)
+** varint nPrefix; (length of prefix shared with previous term)
+** varint nSuffix; (length of unshared suffix)
+** char pTermSuffix[nSuffix];(unshared suffix of next term)
+** varint nDoclist; (length of term's associated doclist)
+** char pDoclist[nDoclist]; (content of doclist)
+** }
+**
+** Here, array { X } means zero or more occurrences of X, adjacent in
+** memory.
+**
+** Leaf nodes are broken into blocks which are stored contiguously in
+** the %_segments table in sorted order. This means that when the end
+** of a node is reached, the next term is in the node with the next
+** greater node id.
+**
+** New data is spilled to a new leaf node when the current node
+** exceeds LEAF_MAX bytes (default 2048). New data which itself is
+** larger than STANDALONE_MIN (default 1024) is placed in a standalone
+** node (a leaf node with a single term and doclist). The goal of
+** these settings is to pack together groups of small doclists while
+** making it efficient to directly access large doclists. The
+** assumption is that large doclists represent terms which are more
+** likely to be query targets.
+**
+** TODO(shess) It may be useful for blocking decisions to be more
+** dynamic. For instance, it may make more sense to have a 2.5k leaf
+** node rather than splitting into 2k and .5k nodes. My intuition is
+** that this might extend through 2x or 4x the pagesize.
+**
+**
+**** Segment interior nodes ****
+** Segment interior nodes store blockids for subtree nodes and terms
+** to describe what data is stored by the each subtree. Interior
+** nodes are written using InteriorWriter, and read using
+** InteriorReader. InteriorWriters are created as needed when
+** SegmentWriter creates new leaf nodes, or when an interior node
+** itself grows too big and must be split. The format of interior
+** nodes:
+**
+** varint iHeight; (height from leaf level, always >0)
+** varint iBlockid; (block id of node's leftmost subtree)
+** optional {
+** varint nTerm; (length of first term)
+** char pTerm[nTerm]; (content of first term)
+** array {
+** (further terms are delta-encoded)
+** varint nPrefix; (length of shared prefix with previous term)
+** varint nSuffix; (length of unshared suffix)
+** char pTermSuffix[nSuffix]; (unshared suffix of next term)
+** }
+** }
+**
+** Here, optional { X } means an optional element, while array { X }
+** means zero or more occurrences of X, adjacent in memory.
+**
+** An interior node encodes n terms separating n+1 subtrees. The
+** subtree blocks are contiguous, so only the first subtree's blockid
+** is encoded. The subtree at iBlockid will contain all terms less
+** than the first term encoded (or all terms if no term is encoded).
+** Otherwise, for terms greater than or equal to pTerm[i] but less
+** than pTerm[i+1], the subtree for that term will be rooted at
+** iBlockid+i. Interior nodes only store enough term data to
+** distinguish adjacent children (if the rightmost term of the left
+** child is "something", and the leftmost term of the right child is
+** "wicked", only "w" is stored).
+**
+** New data is spilled to a new interior node at the same height when
+** the current node exceeds INTERIOR_MAX bytes (default 2048).
+** INTERIOR_MIN_TERMS (default 7) keeps large terms from monopolizing
+** interior nodes and making the tree too skinny. The interior nodes
+** at a given height are naturally tracked by interior nodes at
+** height+1, and so on.
+**
+**
+**** Segment directory ****
+** The segment directory in table %_segdir stores meta-information for
+** merging and deleting segments, and also the root node of the
+** segment's tree.
+**
+** The root node is the top node of the segment's tree after encoding
+** the entire segment, restricted to ROOT_MAX bytes (default 1024).
+** This could be either a leaf node or an interior node. If the top
+** node requires more than ROOT_MAX bytes, it is flushed to %_segments
+** and a new root interior node is generated (which should always fit
+** within ROOT_MAX because it only needs space for 2 varints, the
+** height and the blockid of the previous root).
+**
+** The meta-information in the segment directory is:
+** level - segment level (see below)
+** idx - index within level
+** - (level,idx uniquely identify a segment)
+** start_block - first leaf node
+** leaves_end_block - last leaf node
+** end_block - last block (including interior nodes)
+** root - contents of root node
+**
+** If the root node is a leaf node, then start_block,
+** leaves_end_block, and end_block are all 0.
+**
+**
+**** Segment merging ****
+** To amortize update costs, segments are groups into levels and
+** merged in matches. Each increase in level represents exponentially
+** more documents.
+**
+** New documents (actually, document updates) are tokenized and
+** written individually (using LeafWriter) to a level 0 segment, with
+** incrementing idx. When idx reaches MERGE_COUNT (default 16), all
+** level 0 segments are merged into a single level 1 segment. Level 1
+** is populated like level 0, and eventually MERGE_COUNT level 1
+** segments are merged to a single level 2 segment (representing
+** MERGE_COUNT^2 updates), and so on.
+**
+** A segment merge traverses all segments at a given level in
+** parallel, performing a straightforward sorted merge. Since segment
+** leaf nodes are written in to the %_segments table in order, this
+** merge traverses the underlying sqlite disk structures efficiently.
+** After the merge, all segment blocks from the merged level are
+** deleted.
+**
+** MERGE_COUNT controls how often we merge segments. 16 seems to be
+** somewhat of a sweet spot for insertion performance. 32 and 64 show
+** very similar performance numbers to 16 on insertion, though they're
+** a tiny bit slower (perhaps due to more overhead in merge-time
+** sorting). 8 is about 20% slower than 16, 4 about 50% slower than
+** 16, 2 about 66% slower than 16.
+**
+** At query time, high MERGE_COUNT increases the number of segments
+** which need to be scanned and merged. For instance, with 100k docs
+** inserted:
+**
+** MERGE_COUNT segments
+** 16 25
+** 8 12
+** 4 10
+** 2 6
+**
+** This appears to have only a moderate impact on queries for very
+** frequent terms (which are somewhat dominated by segment merge
+** costs), and infrequent and non-existent terms still seem to be fast
+** even with many segments.
+**
+** TODO(shess) That said, it would be nice to have a better query-side
+** argument for MERGE_COUNT of 16. Also, it is possible/likely that
+** optimizations to things like doclist merging will swing the sweet
+** spot around.
+**
+**
+**
+**** Handling of deletions and updates ****
+** Since we're using a segmented structure, with no docid-oriented
+** index into the term index, we clearly cannot simply update the term
+** index when a document is deleted or updated. For deletions, we
+** write an empty doclist (varint(docid) varint(POS_END)), for updates
+** we simply write the new doclist. Segment merges overwrite older
+** data for a particular docid with newer data, so deletes or updates
+** will eventually overtake the earlier data and knock it out. The
+** query logic likewise merges doclists so that newer data knocks out
+** older data.
+**
+** TODO(shess) Provide a VACUUM type operation to clear out all
+** deletions and duplications. This would basically be a forced merge
+** into a single segment.
+*/
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
+# define SQLITE_CORE 1
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts3.h"
+#include "fts3_hash.h"
+#include "fts3_tokenizer.h"
+#ifndef SQLITE_CORE
+# include "sqlite3ext.h"
+ SQLITE_EXTENSION_INIT1
+#endif
+
+
+/* TODO(shess) MAN, this thing needs some refactoring. At minimum, it
+** would be nice to order the file better, perhaps something along the
+** lines of:
+**
+** - utility functions
+** - table setup functions
+** - table update functions
+** - table query functions
+**
+** Put the query functions last because they're likely to reference
+** typedefs or functions from the table update section.
+*/
+
+#if 0
+# define FTSTRACE(A) printf A; fflush(stdout)
+#else
+# define FTSTRACE(A)
+#endif
+
+/*
+** Default span for NEAR operators.
+*/
+#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
+
+/* It is not safe to call isspace(), tolower(), or isalnum() on
+** hi-bit-set characters. This is the same solution used in the
+** tokenizer.
+*/
+/* TODO(shess) The snippet-generation code should be using the
+** tokenizer-generated tokens rather than doing its own local
+** tokenization.
+*/
+/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
+static int safe_isspace(char c){
+ return (c&0x80)==0 ? isspace(c) : 0;
+}
+static int safe_tolower(char c){
+ return (c&0x80)==0 ? tolower(c) : c;
+}
+static int safe_isalnum(char c){
+ return (c&0x80)==0 ? isalnum(c) : 0;
+}
+
+typedef enum DocListType {
+ DL_DOCIDS, /* docids only */
+ DL_POSITIONS, /* docids + positions */
+ DL_POSITIONS_OFFSETS /* docids + positions + offsets */
+} DocListType;
+
+/*
+** By default, only positions and not offsets are stored in the doclists.
+** To change this so that offsets are stored too, compile with
+**
+** -DDL_DEFAULT=DL_POSITIONS_OFFSETS
+**
+** If DL_DEFAULT is set to DL_DOCIDS, your table can only be inserted
+** into (no deletes or updates).
+*/
+#ifndef DL_DEFAULT
+# define DL_DEFAULT DL_POSITIONS
+#endif
+
+enum {
+ POS_END = 0, /* end of this position list */
+ POS_COLUMN, /* followed by new column number */
+ POS_BASE
+};
+
+/* MERGE_COUNT controls how often we merge segments (see comment at
+** top of file).
+*/
+#define MERGE_COUNT 16
+
+/* utility functions */
+
+/* CLEAR() and SCRAMBLE() abstract memset() on a pointer to a single
+** record to prevent errors of the form:
+**
+** my_function(SomeType *b){
+** memset(b, '\0', sizeof(b)); // sizeof(b)!=sizeof(*b)
+** }
+*/
+/* TODO(shess) Obvious candidates for a header file. */
+#define CLEAR(b) memset(b, '\0', sizeof(*(b)))
+
+#ifndef NDEBUG
+# define SCRAMBLE(b) memset(b, 0x55, sizeof(*(b)))
+#else
+# define SCRAMBLE(b)
+#endif
+
+/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
+#define VARINT_MAX 10
+
+/* Write a 64-bit variable-length integer to memory starting at p[0].
+ * The length of data written will be between 1 and VARINT_MAX bytes.
+ * The number of bytes written is returned. */
+static int fts3PutVarint(char *p, sqlite_int64 v){
+ unsigned char *q = (unsigned char *) p;
+ sqlite_uint64 vu = v;
+ do{
+ *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
+ vu >>= 7;
+ }while( vu!=0 );
+ q[-1] &= 0x7f; /* turn off high bit in final byte */
+ assert( q - (unsigned char *)p <= VARINT_MAX );
+ return (int) (q - (unsigned char *)p);
+}
+
+/* Read a 64-bit variable-length integer from memory starting at p[0].
+ * Return the number of bytes read, or 0 on error.
+ * The value is stored in *v. */
+static int fts3GetVarint(const char *p, sqlite_int64 *v){
+ const unsigned char *q = (const unsigned char *) p;
+ sqlite_uint64 x = 0, y = 1;
+ while( (*q & 0x80) == 0x80 ){
+ x += y * (*q++ & 0x7f);
+ y <<= 7;
+ if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
+ assert( 0 );
+ return 0;
+ }
+ }
+ x += y * (*q++);
+ *v = (sqlite_int64) x;
+ return (int) (q - (unsigned char *)p);
+}
+
+static int fts3GetVarint32(const char *p, int *pi){
+ sqlite_int64 i;
+ int ret = fts3GetVarint(p, &i);
+ *pi = (int) i;
+ assert( *pi==i );
+ return ret;
+}
+
+/*******************************************************************/
+/* DataBuffer is used to collect data into a buffer in piecemeal
+** fashion. It implements the usual distinction between amount of
+** data currently stored (nData) and buffer capacity (nCapacity).
+**
+** dataBufferInit - create a buffer with given initial capacity.
+** dataBufferReset - forget buffer's data, retaining capacity.
+** dataBufferDestroy - free buffer's data.
+** dataBufferSwap - swap contents of two buffers.
+** dataBufferExpand - expand capacity without adding data.
+** dataBufferAppend - append data.
+** dataBufferAppend2 - append two pieces of data at once.
+** dataBufferReplace - replace buffer's data.
+*/
+typedef struct DataBuffer {
+ char *pData; /* Pointer to malloc'ed buffer. */
+ int nCapacity; /* Size of pData buffer. */
+ int nData; /* End of data loaded into pData. */
+} DataBuffer;
+
+static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){
+ assert( nCapacity>=0 );
+ pBuffer->nData = 0;
+ pBuffer->nCapacity = nCapacity;
+ pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity);
+}
+static void dataBufferReset(DataBuffer *pBuffer){
+ pBuffer->nData = 0;
+}
+static void dataBufferDestroy(DataBuffer *pBuffer){
+ if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData);
+ SCRAMBLE(pBuffer);
+}
+static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){
+ DataBuffer tmp = *pBuffer1;
+ *pBuffer1 = *pBuffer2;
+ *pBuffer2 = tmp;
+}
+static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){
+ assert( nAddCapacity>0 );
+ /* TODO(shess) Consider expanding more aggressively. Note that the
+ ** underlying malloc implementation may take care of such things for
+ ** us already.
+ */
+ if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){
+ pBuffer->nCapacity = pBuffer->nData+nAddCapacity;
+ pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity);
+ }
+}
+static void dataBufferAppend(DataBuffer *pBuffer,
+ const char *pSource, int nSource){
+ assert( nSource>0 && pSource!=NULL );
+ dataBufferExpand(pBuffer, nSource);
+ memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource);
+ pBuffer->nData += nSource;
+}
+static void dataBufferAppend2(DataBuffer *pBuffer,
+ const char *pSource1, int nSource1,
+ const char *pSource2, int nSource2){
+ assert( nSource1>0 && pSource1!=NULL );
+ assert( nSource2>0 && pSource2!=NULL );
+ dataBufferExpand(pBuffer, nSource1+nSource2);
+ memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1);
+ memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2);
+ pBuffer->nData += nSource1+nSource2;
+}
+static void dataBufferReplace(DataBuffer *pBuffer,
+ const char *pSource, int nSource){
+ dataBufferReset(pBuffer);
+ dataBufferAppend(pBuffer, pSource, nSource);
+}
+
+/* StringBuffer is a null-terminated version of DataBuffer. */
+typedef struct StringBuffer {
+ DataBuffer b; /* Includes null terminator. */
+} StringBuffer;
+
+static void initStringBuffer(StringBuffer *sb){
+ dataBufferInit(&sb->b, 100);
+ dataBufferReplace(&sb->b, "", 1);
+}
+static int stringBufferLength(StringBuffer *sb){
+ return sb->b.nData-1;
+}
+static char *stringBufferData(StringBuffer *sb){
+ return sb->b.pData;
+}
+static void stringBufferDestroy(StringBuffer *sb){
+ dataBufferDestroy(&sb->b);
+}
+
+static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
+ assert( sb->b.nData>0 );
+ if( nFrom>0 ){
+ sb->b.nData--;
+ dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1);
+ }
+}
+static void append(StringBuffer *sb, const char *zFrom){
+ nappend(sb, zFrom, strlen(zFrom));
+}
+
+/* Append a list of strings separated by commas. */
+static void appendList(StringBuffer *sb, int nString, char **azString){
+ int i;
+ for(i=0; i<nString; ++i){
+ if( i>0 ) append(sb, ", ");
+ append(sb, azString[i]);
+ }
+}
+
+static int endsInWhiteSpace(StringBuffer *p){
+ return stringBufferLength(p)>0 &&
+ safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]);
+}
+
+/* If the StringBuffer ends in something other than white space, add a
+** single space character to the end.
+*/
+static void appendWhiteSpace(StringBuffer *p){
+ if( stringBufferLength(p)==0 ) return;
+ if( !endsInWhiteSpace(p) ) append(p, " ");
+}
+
+/* Remove white space from the end of the StringBuffer */
+static void trimWhiteSpace(StringBuffer *p){
+ while( endsInWhiteSpace(p) ){
+ p->b.pData[--p->b.nData-1] = '\0';
+ }
+}
+
+/*******************************************************************/
+/* DLReader is used to read document elements from a doclist. The
+** current docid is cached, so dlrDocid() is fast. DLReader does not
+** own the doclist buffer.
+**
+** dlrAtEnd - true if there's no more data to read.
+** dlrDocid - docid of current document.
+** dlrDocData - doclist data for current document (including docid).
+** dlrDocDataBytes - length of same.
+** dlrAllDataBytes - length of all remaining data.
+** dlrPosData - position data for current document.
+** dlrPosDataLen - length of pos data for current document (incl POS_END).
+** dlrStep - step to current document.
+** dlrInit - initial for doclist of given type against given data.
+** dlrDestroy - clean up.
+**
+** Expected usage is something like:
+**
+** DLReader reader;
+** dlrInit(&reader, pData, nData);
+** while( !dlrAtEnd(&reader) ){
+** // calls to dlrDocid() and kin.
+** dlrStep(&reader);
+** }
+** dlrDestroy(&reader);
+*/
+typedef struct DLReader {
+ DocListType iType;
+ const char *pData;
+ int nData;
+
+ sqlite_int64 iDocid;
+ int nElement;
+} DLReader;
+
+static int dlrAtEnd(DLReader *pReader){
+ assert( pReader->nData>=0 );
+ return pReader->nData==0;
+}
+static sqlite_int64 dlrDocid(DLReader *pReader){
+ assert( !dlrAtEnd(pReader) );
+ return pReader->iDocid;
+}
+static const char *dlrDocData(DLReader *pReader){
+ assert( !dlrAtEnd(pReader) );
+ return pReader->pData;
+}
+static int dlrDocDataBytes(DLReader *pReader){
+ assert( !dlrAtEnd(pReader) );
+ return pReader->nElement;
+}
+static int dlrAllDataBytes(DLReader *pReader){
+ assert( !dlrAtEnd(pReader) );
+ return pReader->nData;
+}
+/* TODO(shess) Consider adding a field to track iDocid varint length
+** to make these two functions faster. This might matter (a tiny bit)
+** for queries.
+*/
+static const char *dlrPosData(DLReader *pReader){
+ sqlite_int64 iDummy;
+ int n = fts3GetVarint(pReader->pData, &iDummy);
+ assert( !dlrAtEnd(pReader) );
+ return pReader->pData+n;
+}
+static int dlrPosDataLen(DLReader *pReader){
+ sqlite_int64 iDummy;
+ int n = fts3GetVarint(pReader->pData, &iDummy);
+ assert( !dlrAtEnd(pReader) );
+ return pReader->nElement-n;
+}
+static void dlrStep(DLReader *pReader){
+ assert( !dlrAtEnd(pReader) );
+
+ /* Skip past current doclist element. */
+ assert( pReader->nElement<=pReader->nData );
+ pReader->pData += pReader->nElement;
+ pReader->nData -= pReader->nElement;
+
+ /* If there is more data, read the next doclist element. */
+ if( pReader->nData!=0 ){
+ sqlite_int64 iDocidDelta;
+ int iDummy, n = fts3GetVarint(pReader->pData, &iDocidDelta);
+ pReader->iDocid += iDocidDelta;
+ if( pReader->iType>=DL_POSITIONS ){
+ assert( n<pReader->nData );
+ while( 1 ){
+ n += fts3GetVarint32(pReader->pData+n, &iDummy);
+ assert( n<=pReader->nData );
+ if( iDummy==POS_END ) break;
+ if( iDummy==POS_COLUMN ){
+ n += fts3GetVarint32(pReader->pData+n, &iDummy);
+ assert( n<pReader->nData );
+ }else if( pReader->iType==DL_POSITIONS_OFFSETS ){
+ n += fts3GetVarint32(pReader->pData+n, &iDummy);
+ n += fts3GetVarint32(pReader->pData+n, &iDummy);
+ assert( n<pReader->nData );
+ }
+ }
+ }
+ pReader->nElement = n;
+ assert( pReader->nElement<=pReader->nData );
+ }
+}
+static void dlrInit(DLReader *pReader, DocListType iType,
+ const char *pData, int nData){
+ assert( pData!=NULL && nData!=0 );
+ pReader->iType = iType;
+ pReader->pData = pData;
+ pReader->nData = nData;
+ pReader->nElement = 0;
+ pReader->iDocid = 0;
+
+ /* Load the first element's data. There must be a first element. */
+ dlrStep(pReader);
+}
+static void dlrDestroy(DLReader *pReader){
+ SCRAMBLE(pReader);
+}
+
+#ifndef NDEBUG
+/* Verify that the doclist can be validly decoded. Also returns the
+** last docid found because it is convenient in other assertions for
+** DLWriter.
+*/
+static void docListValidate(DocListType iType, const char *pData, int nData,
+ sqlite_int64 *pLastDocid){
+ sqlite_int64 iPrevDocid = 0;
+ assert( nData>0 );
+ assert( pData!=0 );
+ assert( pData+nData>pData );
+ while( nData!=0 ){
+ sqlite_int64 iDocidDelta;
+ int n = fts3GetVarint(pData, &iDocidDelta);
+ iPrevDocid += iDocidDelta;
+ if( iType>DL_DOCIDS ){
+ int iDummy;
+ while( 1 ){
+ n += fts3GetVarint32(pData+n, &iDummy);
+ if( iDummy==POS_END ) break;
+ if( iDummy==POS_COLUMN ){
+ n += fts3GetVarint32(pData+n, &iDummy);
+ }else if( iType>DL_POSITIONS ){
+ n += fts3GetVarint32(pData+n, &iDummy);
+ n += fts3GetVarint32(pData+n, &iDummy);
+ }
+ assert( n<=nData );
+ }
+ }
+ assert( n<=nData );
+ pData += n;
+ nData -= n;
+ }
+ if( pLastDocid ) *pLastDocid = iPrevDocid;
+}
+#define ASSERT_VALID_DOCLIST(i, p, n, o) docListValidate(i, p, n, o)
+#else
+#define ASSERT_VALID_DOCLIST(i, p, n, o) assert( 1 )
+#endif
+
+/*******************************************************************/
+/* DLWriter is used to write doclist data to a DataBuffer. DLWriter
+** always appends to the buffer and does not own it.
+**
+** dlwInit - initialize to write a given type doclistto a buffer.
+** dlwDestroy - clear the writer's memory. Does not free buffer.
+** dlwAppend - append raw doclist data to buffer.
+** dlwCopy - copy next doclist from reader to writer.
+** dlwAdd - construct doclist element and append to buffer.
+** Only apply dlwAdd() to DL_DOCIDS doclists (else use PLWriter).
+*/
+typedef struct DLWriter {
+ DocListType iType;
+ DataBuffer *b;
+ sqlite_int64 iPrevDocid;
+#ifndef NDEBUG
+ int has_iPrevDocid;
+#endif
+} DLWriter;
+
+static void dlwInit(DLWriter *pWriter, DocListType iType, DataBuffer *b){
+ pWriter->b = b;
+ pWriter->iType = iType;
+ pWriter->iPrevDocid = 0;
+#ifndef NDEBUG
+ pWriter->has_iPrevDocid = 0;
+#endif
+}
+static void dlwDestroy(DLWriter *pWriter){
+ SCRAMBLE(pWriter);
+}
+/* iFirstDocid is the first docid in the doclist in pData. It is
+** needed because pData may point within a larger doclist, in which
+** case the first item would be delta-encoded.
+**
+** iLastDocid is the final docid in the doclist in pData. It is
+** needed to create the new iPrevDocid for future delta-encoding. The
+** code could decode the passed doclist to recreate iLastDocid, but
+** the only current user (docListMerge) already has decoded this
+** information.
+*/
+/* TODO(shess) This has become just a helper for docListMerge.
+** Consider a refactor to make this cleaner.
+*/
+static void dlwAppend(DLWriter *pWriter,
+ const char *pData, int nData,
+ sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){
+ sqlite_int64 iDocid = 0;
+ char c[VARINT_MAX];
+ int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */
+#ifndef NDEBUG
+ sqlite_int64 iLastDocidDelta;
+#endif
+
+ /* Recode the initial docid as delta from iPrevDocid. */
+ nFirstOld = fts3GetVarint(pData, &iDocid);
+ assert( nFirstOld<nData || (nFirstOld==nData && pWriter->iType==DL_DOCIDS) );
+ nFirstNew = fts3PutVarint(c, iFirstDocid-pWriter->iPrevDocid);
+
+ /* Verify that the incoming doclist is valid AND that it ends with
+ ** the expected docid. This is essential because we'll trust this
+ ** docid in future delta-encoding.
+ */
+ ASSERT_VALID_DOCLIST(pWriter->iType, pData, nData, &iLastDocidDelta);
+ assert( iLastDocid==iFirstDocid-iDocid+iLastDocidDelta );
+
+ /* Append recoded initial docid and everything else. Rest of docids
+ ** should have been delta-encoded from previous initial docid.
+ */
+ if( nFirstOld<nData ){
+ dataBufferAppend2(pWriter->b, c, nFirstNew,
+ pData+nFirstOld, nData-nFirstOld);
+ }else{
+ dataBufferAppend(pWriter->b, c, nFirstNew);
+ }
+ pWriter->iPrevDocid = iLastDocid;
+}
+static void dlwCopy(DLWriter *pWriter, DLReader *pReader){
+ dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader),
+ dlrDocid(pReader), dlrDocid(pReader));
+}
+static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){
+ char c[VARINT_MAX];
+ int n = fts3PutVarint(c, iDocid-pWriter->iPrevDocid);
+
+ /* Docids must ascend. */
+ assert( !pWriter->has_iPrevDocid || iDocid>pWriter->iPrevDocid );
+ assert( pWriter->iType==DL_DOCIDS );
+
+ dataBufferAppend(pWriter->b, c, n);
+ pWriter->iPrevDocid = iDocid;
+#ifndef NDEBUG
+ pWriter->has_iPrevDocid = 1;
+#endif
+}
+
+/*******************************************************************/
+/* PLReader is used to read data from a document's position list. As
+** the caller steps through the list, data is cached so that varints
+** only need to be decoded once.
+**
+** plrInit, plrDestroy - create/destroy a reader.
+** plrColumn, plrPosition, plrStartOffset, plrEndOffset - accessors
+** plrAtEnd - at end of stream, only call plrDestroy once true.
+** plrStep - step to the next element.
+*/
+typedef struct PLReader {
+ /* These refer to the next position's data. nData will reach 0 when
+ ** reading the last position, so plrStep() signals EOF by setting
+ ** pData to NULL.
+ */
+ const char *pData;
+ int nData;
+
+ DocListType iType;
+ int iColumn; /* the last column read */
+ int iPosition; /* the last position read */
+ int iStartOffset; /* the last start offset read */
+ int iEndOffset; /* the last end offset read */
+} PLReader;
+
+static int plrAtEnd(PLReader *pReader){
+ return pReader->pData==NULL;
+}
+static int plrColumn(PLReader *pReader){
+ assert( !plrAtEnd(pReader) );
+ return pReader->iColumn;
+}
+static int plrPosition(PLReader *pReader){
+ assert( !plrAtEnd(pReader) );
+ return pReader->iPosition;
+}
+static int plrStartOffset(PLReader *pReader){
+ assert( !plrAtEnd(pReader) );
+ return pReader->iStartOffset;
+}
+static int plrEndOffset(PLReader *pReader){
+ assert( !plrAtEnd(pReader) );
+ return pReader->iEndOffset;
+}
+static void plrStep(PLReader *pReader){
+ int i, n;
+
+ assert( !plrAtEnd(pReader) );
+
+ if( pReader->nData==0 ){
+ pReader->pData = NULL;
+ return;
+ }
+
+ n = fts3GetVarint32(pReader->pData, &i);
+ if( i==POS_COLUMN ){
+ n += fts3GetVarint32(pReader->pData+n, &pReader->iColumn);
+ pReader->iPosition = 0;
+ pReader->iStartOffset = 0;
+ n += fts3GetVarint32(pReader->pData+n, &i);
+ }
+ /* Should never see adjacent column changes. */
+ assert( i!=POS_COLUMN );
+
+ if( i==POS_END ){
+ pReader->nData = 0;
+ pReader->pData = NULL;
+ return;
+ }
+
+ pReader->iPosition += i-POS_BASE;
+ if( pReader->iType==DL_POSITIONS_OFFSETS ){
+ n += fts3GetVarint32(pReader->pData+n, &i);
+ pReader->iStartOffset += i;
+ n += fts3GetVarint32(pReader->pData+n, &i);
+ pReader->iEndOffset = pReader->iStartOffset+i;
+ }
+ assert( n<=pReader->nData );
+ pReader->pData += n;
+ pReader->nData -= n;
+}
+
+static void plrInit(PLReader *pReader, DLReader *pDLReader){
+ pReader->pData = dlrPosData(pDLReader);
+ pReader->nData = dlrPosDataLen(pDLReader);
+ pReader->iType = pDLReader->iType;
+ pReader->iColumn = 0;
+ pReader->iPosition = 0;
+ pReader->iStartOffset = 0;
+ pReader->iEndOffset = 0;
+ plrStep(pReader);
+}
+static void plrDestroy(PLReader *pReader){
+ SCRAMBLE(pReader);
+}
+
+/*******************************************************************/
+/* PLWriter is used in constructing a document's position list. As a
+** convenience, if iType is DL_DOCIDS, PLWriter becomes a no-op.
+** PLWriter writes to the associated DLWriter's buffer.
+**
+** plwInit - init for writing a document's poslist.
+** plwDestroy - clear a writer.
+** plwAdd - append position and offset information.
+** plwCopy - copy next position's data from reader to writer.
+** plwTerminate - add any necessary doclist terminator.
+**
+** Calling plwAdd() after plwTerminate() may result in a corrupt
+** doclist.
+*/
+/* TODO(shess) Until we've written the second item, we can cache the
+** first item's information. Then we'd have three states:
+**
+** - initialized with docid, no positions.
+** - docid and one position.
+** - docid and multiple positions.
+**
+** Only the last state needs to actually write to dlw->b, which would
+** be an improvement in the DLCollector case.
+*/
+typedef struct PLWriter {
+ DLWriter *dlw;
+
+ int iColumn; /* the last column written */
+ int iPos; /* the last position written */
+ int iOffset; /* the last start offset written */
+} PLWriter;
+
+/* TODO(shess) In the case where the parent is reading these values
+** from a PLReader, we could optimize to a copy if that PLReader has
+** the same type as pWriter.
+*/
+static void plwAdd(PLWriter *pWriter, int iColumn, int iPos,
+ int iStartOffset, int iEndOffset){
+ /* Worst-case space for POS_COLUMN, iColumn, iPosDelta,
+ ** iStartOffsetDelta, and iEndOffsetDelta.
+ */
+ char c[5*VARINT_MAX];
+ int n = 0;
+
+ /* Ban plwAdd() after plwTerminate(). */
+ assert( pWriter->iPos!=-1 );
+
+ if( pWriter->dlw->iType==DL_DOCIDS ) return;
+
+ if( iColumn!=pWriter->iColumn ){
+ n += fts3PutVarint(c+n, POS_COLUMN);
+ n += fts3PutVarint(c+n, iColumn);
+ pWriter->iColumn = iColumn;
+ pWriter->iPos = 0;
+ pWriter->iOffset = 0;
+ }
+ assert( iPos>=pWriter->iPos );
+ n += fts3PutVarint(c+n, POS_BASE+(iPos-pWriter->iPos));
+ pWriter->iPos = iPos;
+ if( pWriter->dlw->iType==DL_POSITIONS_OFFSETS ){
+ assert( iStartOffset>=pWriter->iOffset );
+ n += fts3PutVarint(c+n, iStartOffset-pWriter->iOffset);
+ pWriter->iOffset = iStartOffset;
+ assert( iEndOffset>=iStartOffset );
+ n += fts3PutVarint(c+n, iEndOffset-iStartOffset);
+ }
+ dataBufferAppend(pWriter->dlw->b, c, n);
+}
+static void plwCopy(PLWriter *pWriter, PLReader *pReader){
+ plwAdd(pWriter, plrColumn(pReader), plrPosition(pReader),
+ plrStartOffset(pReader), plrEndOffset(pReader));
+}
+static void plwInit(PLWriter *pWriter, DLWriter *dlw, sqlite_int64 iDocid){
+ char c[VARINT_MAX];
+ int n;
+
+ pWriter->dlw = dlw;
+
+ /* Docids must ascend. */
+ assert( !pWriter->dlw->has_iPrevDocid || iDocid>pWriter->dlw->iPrevDocid );
+ n = fts3PutVarint(c, iDocid-pWriter->dlw->iPrevDocid);
+ dataBufferAppend(pWriter->dlw->b, c, n);
+ pWriter->dlw->iPrevDocid = iDocid;
+#ifndef NDEBUG
+ pWriter->dlw->has_iPrevDocid = 1;
+#endif
+
+ pWriter->iColumn = 0;
+ pWriter->iPos = 0;
+ pWriter->iOffset = 0;
+}
+/* TODO(shess) Should plwDestroy() also terminate the doclist? But
+** then plwDestroy() would no longer be just a destructor, it would
+** also be doing work, which isn't consistent with the overall idiom.
+** Another option would be for plwAdd() to always append any necessary
+** terminator, so that the output is always correct. But that would
+** add incremental work to the common case with the only benefit being
+** API elegance. Punt for now.
+*/
+static void plwTerminate(PLWriter *pWriter){
+ if( pWriter->dlw->iType>DL_DOCIDS ){
+ char c[VARINT_MAX];
+ int n = fts3PutVarint(c, POS_END);
+ dataBufferAppend(pWriter->dlw->b, c, n);
+ }
+#ifndef NDEBUG
+ /* Mark as terminated for assert in plwAdd(). */
+ pWriter->iPos = -1;
+#endif
+}
+static void plwDestroy(PLWriter *pWriter){
+ SCRAMBLE(pWriter);
+}
+
+/*******************************************************************/
+/* DLCollector wraps PLWriter and DLWriter to provide a
+** dynamically-allocated doclist area to use during tokenization.
+**
+** dlcNew - malloc up and initialize a collector.
+** dlcDelete - destroy a collector and all contained items.
+** dlcAddPos - append position and offset information.
+** dlcAddDoclist - add the collected doclist to the given buffer.
+** dlcNext - terminate the current document and open another.
+*/
+typedef struct DLCollector {
+ DataBuffer b;
+ DLWriter dlw;
+ PLWriter plw;
+} DLCollector;
+
+/* TODO(shess) This could also be done by calling plwTerminate() and
+** dataBufferAppend(). I tried that, expecting nominal performance
+** differences, but it seemed to pretty reliably be worth 1% to code
+** it this way. I suspect it is the incremental malloc overhead (some
+** percentage of the plwTerminate() calls will cause a realloc), so
+** this might be worth revisiting if the DataBuffer implementation
+** changes.
+*/
+static void dlcAddDoclist(DLCollector *pCollector, DataBuffer *b){
+ if( pCollector->dlw.iType>DL_DOCIDS ){
+ char c[VARINT_MAX];
+ int n = fts3PutVarint(c, POS_END);
+ dataBufferAppend2(b, pCollector->b.pData, pCollector->b.nData, c, n);
+ }else{
+ dataBufferAppend(b, pCollector->b.pData, pCollector->b.nData);
+ }
+}
+static void dlcNext(DLCollector *pCollector, sqlite_int64 iDocid){
+ plwTerminate(&pCollector->plw);
+ plwDestroy(&pCollector->plw);
+ plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
+}
+static void dlcAddPos(DLCollector *pCollector, int iColumn, int iPos,
+ int iStartOffset, int iEndOffset){
+ plwAdd(&pCollector->plw, iColumn, iPos, iStartOffset, iEndOffset);
+}
+
+static DLCollector *dlcNew(sqlite_int64 iDocid, DocListType iType){
+ DLCollector *pCollector = sqlite3_malloc(sizeof(DLCollector));
+ dataBufferInit(&pCollector->b, 0);
+ dlwInit(&pCollector->dlw, iType, &pCollector->b);
+ plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
+ return pCollector;
+}
+static void dlcDelete(DLCollector *pCollector){
+ plwDestroy(&pCollector->plw);
+ dlwDestroy(&pCollector->dlw);
+ dataBufferDestroy(&pCollector->b);
+ SCRAMBLE(pCollector);
+ sqlite3_free(pCollector);
+}
+
+
+/* Copy the doclist data of iType in pData/nData into *out, trimming
+** unnecessary data as we go. Only columns matching iColumn are
+** copied, all columns copied if iColumn is -1. Elements with no
+** matching columns are dropped. The output is an iOutType doclist.
+*/
+/* NOTE(shess) This code is only valid after all doclists are merged.
+** If this is run before merges, then doclist items which represent
+** deletion will be trimmed, and will thus not effect a deletion
+** during the merge.
+*/
+static void docListTrim(DocListType iType, const char *pData, int nData,
+ int iColumn, DocListType iOutType, DataBuffer *out){
+ DLReader dlReader;
+ DLWriter dlWriter;
+
+ assert( iOutType<=iType );
+
+ dlrInit(&dlReader, iType, pData, nData);
+ dlwInit(&dlWriter, iOutType, out);
+
+ while( !dlrAtEnd(&dlReader) ){
+ PLReader plReader;
+ PLWriter plWriter;
+ int match = 0;
+
+ plrInit(&plReader, &dlReader);
+
+ while( !plrAtEnd(&plReader) ){
+ if( iColumn==-1 || plrColumn(&plReader)==iColumn ){
+ if( !match ){
+ plwInit(&plWriter, &dlWriter, dlrDocid(&dlReader));
+ match = 1;
+ }
+ plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader),
+ plrStartOffset(&plReader), plrEndOffset(&plReader));
+ }
+ plrStep(&plReader);
+ }
+ if( match ){
+ plwTerminate(&plWriter);
+ plwDestroy(&plWriter);
+ }
+
+ plrDestroy(&plReader);
+ dlrStep(&dlReader);
+ }
+ dlwDestroy(&dlWriter);
+ dlrDestroy(&dlReader);
+}
+
+/* Used by docListMerge() to keep doclists in the ascending order by
+** docid, then ascending order by age (so the newest comes first).
+*/
+typedef struct OrderedDLReader {
+ DLReader *pReader;
+
+ /* TODO(shess) If we assume that docListMerge pReaders is ordered by
+ ** age (which we do), then we could use pReader comparisons to break
+ ** ties.
+ */
+ int idx;
+} OrderedDLReader;
+
+/* Order eof to end, then by docid asc, idx desc. */
+static int orderedDLReaderCmp(OrderedDLReader *r1, OrderedDLReader *r2){
+ if( dlrAtEnd(r1->pReader) ){
+ if( dlrAtEnd(r2->pReader) ) return 0; /* Both atEnd(). */
+ return 1; /* Only r1 atEnd(). */
+ }
+ if( dlrAtEnd(r2->pReader) ) return -1; /* Only r2 atEnd(). */
+
+ if( dlrDocid(r1->pReader)<dlrDocid(r2->pReader) ) return -1;
+ if( dlrDocid(r1->pReader)>dlrDocid(r2->pReader) ) return 1;
+
+ /* Descending on idx. */
+ return r2->idx-r1->idx;
+}
+
+/* Bubble p[0] to appropriate place in p[1..n-1]. Assumes that
+** p[1..n-1] is already sorted.
+*/
+/* TODO(shess) Is this frequent enough to warrant a binary search?
+** Before implementing that, instrument the code to check. In most
+** current usage, I expect that p[0] will be less than p[1] a very
+** high proportion of the time.
+*/
+static void orderedDLReaderReorder(OrderedDLReader *p, int n){
+ while( n>1 && orderedDLReaderCmp(p, p+1)>0 ){
+ OrderedDLReader tmp = p[0];
+ p[0] = p[1];
+ p[1] = tmp;
+ n--;
+ p++;
+ }
+}
+
+/* Given an array of doclist readers, merge their doclist elements
+** into out in sorted order (by docid), dropping elements from older
+** readers when there is a duplicate docid. pReaders is assumed to be
+** ordered by age, oldest first.
+*/
+/* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably
+** be fixed.
+*/
+static void docListMerge(DataBuffer *out,
+ DLReader *pReaders, int nReaders){
+ OrderedDLReader readers[MERGE_COUNT];
+ DLWriter writer;
+ int i, n;
+ const char *pStart = 0;
+ int nStart = 0;
+ sqlite_int64 iFirstDocid = 0, iLastDocid = 0;
+
+ assert( nReaders>0 );
+ if( nReaders==1 ){
+ dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders));
+ return;
+ }
+
+ assert( nReaders<=MERGE_COUNT );
+ n = 0;
+ for(i=0; i<nReaders; i++){
+ assert( pReaders[i].iType==pReaders[0].iType );
+ readers[i].pReader = pReaders+i;
+ readers[i].idx = i;
+ n += dlrAllDataBytes(&pReaders[i]);
+ }
+ /* Conservatively size output to sum of inputs. Output should end
+ ** up strictly smaller than input.
+ */
+ dataBufferExpand(out, n);
+
+ /* Get the readers into sorted order. */
+ while( i-->0 ){
+ orderedDLReaderReorder(readers+i, nReaders-i);
+ }
+
+ dlwInit(&writer, pReaders[0].iType, out);
+ while( !dlrAtEnd(readers[0].pReader) ){
+ sqlite_int64 iDocid = dlrDocid(readers[0].pReader);
+
+ /* If this is a continuation of the current buffer to copy, extend
+ ** that buffer. memcpy() seems to be more efficient if it has a
+ ** lots of data to copy.
+ */
+ if( dlrDocData(readers[0].pReader)==pStart+nStart ){
+ nStart += dlrDocDataBytes(readers[0].pReader);
+ }else{
+ if( pStart!=0 ){
+ dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
+ }
+ pStart = dlrDocData(readers[0].pReader);
+ nStart = dlrDocDataBytes(readers[0].pReader);
+ iFirstDocid = iDocid;
+ }
+ iLastDocid = iDocid;
+ dlrStep(readers[0].pReader);
+
+ /* Drop all of the older elements with the same docid. */
+ for(i=1; i<nReaders &&
+ !dlrAtEnd(readers[i].pReader) &&
+ dlrDocid(readers[i].pReader)==iDocid; i++){
+ dlrStep(readers[i].pReader);
+ }
+
+ /* Get the readers back into order. */
+ while( i-->0 ){
+ orderedDLReaderReorder(readers+i, nReaders-i);
+ }
+ }
+
+ /* Copy over any remaining elements. */
+ if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
+ dlwDestroy(&writer);
+}
+
+/* Helper function for posListUnion(). Compares the current position
+** between left and right, returning as standard C idiom of <0 if
+** left<right, >0 if left>right, and 0 if left==right. "End" always
+** compares greater.
+*/
+static int posListCmp(PLReader *pLeft, PLReader *pRight){
+ assert( pLeft->iType==pRight->iType );
+ if( pLeft->iType==DL_DOCIDS ) return 0;
+
+ if( plrAtEnd(pLeft) ) return plrAtEnd(pRight) ? 0 : 1;
+ if( plrAtEnd(pRight) ) return -1;
+
+ if( plrColumn(pLeft)<plrColumn(pRight) ) return -1;
+ if( plrColumn(pLeft)>plrColumn(pRight) ) return 1;
+
+ if( plrPosition(pLeft)<plrPosition(pRight) ) return -1;
+ if( plrPosition(pLeft)>plrPosition(pRight) ) return 1;
+ if( pLeft->iType==DL_POSITIONS ) return 0;
+
+ if( plrStartOffset(pLeft)<plrStartOffset(pRight) ) return -1;
+ if( plrStartOffset(pLeft)>plrStartOffset(pRight) ) return 1;
+
+ if( plrEndOffset(pLeft)<plrEndOffset(pRight) ) return -1;
+ if( plrEndOffset(pLeft)>plrEndOffset(pRight) ) return 1;
+
+ return 0;
+}
+
+/* Write the union of position lists in pLeft and pRight to pOut.
+** "Union" in this case meaning "All unique position tuples". Should
+** work with any doclist type, though both inputs and the output
+** should be the same type.
+*/
+static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){
+ PLReader left, right;
+ PLWriter writer;
+
+ assert( dlrDocid(pLeft)==dlrDocid(pRight) );
+ assert( pLeft->iType==pRight->iType );
+ assert( pLeft->iType==pOut->iType );
+
+ plrInit(&left, pLeft);
+ plrInit(&right, pRight);
+ plwInit(&writer, pOut, dlrDocid(pLeft));
+
+ while( !plrAtEnd(&left) || !plrAtEnd(&right) ){
+ int c = posListCmp(&left, &right);
+ if( c<0 ){
+ plwCopy(&writer, &left);
+ plrStep(&left);
+ }else if( c>0 ){
+ plwCopy(&writer, &right);
+ plrStep(&right);
+ }else{
+ plwCopy(&writer, &left);
+ plrStep(&left);
+ plrStep(&right);
+ }
+ }
+
+ plwTerminate(&writer);
+ plwDestroy(&writer);
+ plrDestroy(&left);
+ plrDestroy(&right);
+}
+
+/* Write the union of doclists in pLeft and pRight to pOut. For
+** docids in common between the inputs, the union of the position
+** lists is written. Inputs and outputs are always type DL_DEFAULT.
+*/
+static void docListUnion(
+ const char *pLeft, int nLeft,
+ const char *pRight, int nRight,
+ DataBuffer *pOut /* Write the combined doclist here */
+){
+ DLReader left, right;
+ DLWriter writer;
+
+ if( nLeft==0 ){
+ if( nRight!=0) dataBufferAppend(pOut, pRight, nRight);
+ return;
+ }
+ if( nRight==0 ){
+ dataBufferAppend(pOut, pLeft, nLeft);
+ return;
+ }
+
+ dlrInit(&left, DL_DEFAULT, pLeft, nLeft);
+ dlrInit(&right, DL_DEFAULT, pRight, nRight);
+ dlwInit(&writer, DL_DEFAULT, pOut);
+
+ while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){
+ if( dlrAtEnd(&right) ){
+ dlwCopy(&writer, &left);
+ dlrStep(&left);
+ }else if( dlrAtEnd(&left) ){
+ dlwCopy(&writer, &right);
+ dlrStep(&right);
+ }else if( dlrDocid(&left)<dlrDocid(&right) ){
+ dlwCopy(&writer, &left);
+ dlrStep(&left);
+ }else if( dlrDocid(&left)>dlrDocid(&right) ){
+ dlwCopy(&writer, &right);
+ dlrStep(&right);
+ }else{
+ posListUnion(&left, &right, &writer);
+ dlrStep(&left);
+ dlrStep(&right);
+ }
+ }
+
+ dlrDestroy(&left);
+ dlrDestroy(&right);
+ dlwDestroy(&writer);
+}
+
+/*
+** This function is used as part of the implementation of phrase and
+** NEAR matching.
+**
+** pLeft and pRight are DLReaders positioned to the same docid in
+** lists of type DL_POSITION. This function writes an entry to the
+** DLWriter pOut for each position in pRight that is less than
+** (nNear+1) greater (but not equal to or smaller) than a position
+** in pLeft. For example, if nNear is 0, and the positions contained
+** by pLeft and pRight are:
+**
+** pLeft: 5 10 15 20
+** pRight: 6 9 17 21
+**
+** then the docid is added to pOut. If pOut is of type DL_POSITIONS,
+** then a positionids "6" and "21" are also added to pOut.
+**
+** If boolean argument isSaveLeft is true, then positionids are copied
+** from pLeft instead of pRight. In the example above, the positions "5"
+** and "20" would be added instead of "6" and "21".
+*/
+static void posListPhraseMerge(
+ DLReader *pLeft,
+ DLReader *pRight,
+ int nNear,
+ int isSaveLeft,
+ DLWriter *pOut
+){
+ PLReader left, right;
+ PLWriter writer;
+ int match = 0;
+
+ assert( dlrDocid(pLeft)==dlrDocid(pRight) );
+ assert( pOut->iType!=DL_POSITIONS_OFFSETS );
+
+ plrInit(&left, pLeft);
+ plrInit(&right, pRight);
+
+ while( !plrAtEnd(&left) && !plrAtEnd(&right) ){
+ if( plrColumn(&left)<plrColumn(&right) ){
+ plrStep(&left);
+ }else if( plrColumn(&left)>plrColumn(&right) ){
+ plrStep(&right);
+ }else if( plrPosition(&left)>=plrPosition(&right) ){
+ plrStep(&right);
+ }else{
+ if( (plrPosition(&right)-plrPosition(&left))<=(nNear+1) ){
+ if( !match ){
+ plwInit(&writer, pOut, dlrDocid(pLeft));
+ match = 1;
+ }
+ if( !isSaveLeft ){
+ plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0);
+ }else{
+ plwAdd(&writer, plrColumn(&left), plrPosition(&left), 0, 0);
+ }
+ plrStep(&right);
+ }else{
+ plrStep(&left);
+ }
+ }
+ }
+
+ if( match ){
+ plwTerminate(&writer);
+ plwDestroy(&writer);
+ }
+
+ plrDestroy(&left);
+ plrDestroy(&right);
+}
+
+/*
+** Compare the values pointed to by the PLReaders passed as arguments.
+** Return -1 if the value pointed to by pLeft is considered less than
+** the value pointed to by pRight, +1 if it is considered greater
+** than it, or 0 if it is equal. i.e.
+**
+** (*pLeft - *pRight)
+**
+** A PLReader that is in the EOF condition is considered greater than
+** any other. If neither argument is in EOF state, the return value of
+** plrColumn() is used. If the plrColumn() values are equal, the
+** comparison is on the basis of plrPosition().
+*/
+static int plrCompare(PLReader *pLeft, PLReader *pRight){
+ assert(!plrAtEnd(pLeft) || !plrAtEnd(pRight));
+
+ if( plrAtEnd(pRight) || plrAtEnd(pLeft) ){
+ return (plrAtEnd(pRight) ? -1 : 1);
+ }
+ if( plrColumn(pLeft)!=plrColumn(pRight) ){
+ return ((plrColumn(pLeft)<plrColumn(pRight)) ? -1 : 1);
+ }
+ if( plrPosition(pLeft)!=plrPosition(pRight) ){
+ return ((plrPosition(pLeft)<plrPosition(pRight)) ? -1 : 1);
+ }
+ return 0;
+}
+
+/* We have two doclists with positions: pLeft and pRight. Depending
+** on the value of the nNear parameter, perform either a phrase
+** intersection (if nNear==0) or a NEAR intersection (if nNear>0)
+** and write the results into pOut.
+**
+** A phrase intersection means that two documents only match
+** if pLeft.iPos+1==pRight.iPos.
+**
+** A NEAR intersection means that two documents only match if
+** (abs(pLeft.iPos-pRight.iPos)<nNear).
+**
+** If a NEAR intersection is requested, then the nPhrase argument should
+** be passed the number of tokens in the two operands to the NEAR operator
+** combined. For example:
+**
+** Query syntax nPhrase
+** ------------------------------------
+** "A B C" NEAR "D E" 5
+** A NEAR B 2
+**
+** iType controls the type of data written to pOut. If iType is
+** DL_POSITIONS, the positions are those from pRight.
+*/
+static void docListPhraseMerge(
+ const char *pLeft, int nLeft,
+ const char *pRight, int nRight,
+ int nNear, /* 0 for a phrase merge, non-zero for a NEAR merge */
+ int nPhrase, /* Number of tokens in left+right operands to NEAR */
+ DocListType iType, /* Type of doclist to write to pOut */
+ DataBuffer *pOut /* Write the combined doclist here */
+){
+ DLReader left, right;
+ DLWriter writer;
+
+ if( nLeft==0 || nRight==0 ) return;
+
+ assert( iType!=DL_POSITIONS_OFFSETS );
+
+ dlrInit(&left, DL_POSITIONS, pLeft, nLeft);
+ dlrInit(&right, DL_POSITIONS, pRight, nRight);
+ dlwInit(&writer, iType, pOut);
+
+ while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){
+ if( dlrDocid(&left)<dlrDocid(&right) ){
+ dlrStep(&left);
+ }else if( dlrDocid(&right)<dlrDocid(&left) ){
+ dlrStep(&right);
+ }else{
+ if( nNear==0 ){
+ posListPhraseMerge(&left, &right, 0, 0, &writer);
+ }else{
+ /* This case occurs when two terms (simple terms or phrases) are
+ * connected by a NEAR operator, span (nNear+1). i.e.
+ *
+ * '"terrible company" NEAR widget'
+ */
+ DataBuffer one = {0, 0, 0};
+ DataBuffer two = {0, 0, 0};
+
+ DLWriter dlwriter2;
+ DLReader dr1 = {0, 0, 0, 0, 0};
+ DLReader dr2 = {0, 0, 0, 0, 0};
+
+ dlwInit(&dlwriter2, iType, &one);
+ posListPhraseMerge(&right, &left, nNear-3+nPhrase, 1, &dlwriter2);
+ dlwInit(&dlwriter2, iType, &two);
+ posListPhraseMerge(&left, &right, nNear-1, 0, &dlwriter2);
+
+ if( one.nData) dlrInit(&dr1, iType, one.pData, one.nData);
+ if( two.nData) dlrInit(&dr2, iType, two.pData, two.nData);
+
+ if( !dlrAtEnd(&dr1) || !dlrAtEnd(&dr2) ){
+ PLReader pr1 = {0};
+ PLReader pr2 = {0};
+
+ PLWriter plwriter;
+ plwInit(&plwriter, &writer, dlrDocid(dlrAtEnd(&dr1)?&dr2:&dr1));
+
+ if( one.nData ) plrInit(&pr1, &dr1);
+ if( two.nData ) plrInit(&pr2, &dr2);
+ while( !plrAtEnd(&pr1) || !plrAtEnd(&pr2) ){
+ int iCompare = plrCompare(&pr1, &pr2);
+ switch( iCompare ){
+ case -1:
+ plwCopy(&plwriter, &pr1);
+ plrStep(&pr1);
+ break;
+ case 1:
+ plwCopy(&plwriter, &pr2);
+ plrStep(&pr2);
+ break;
+ case 0:
+ plwCopy(&plwriter, &pr1);
+ plrStep(&pr1);
+ plrStep(&pr2);
+ break;
+ }
+ }
+ plwTerminate(&plwriter);
+ }
+ dataBufferDestroy(&one);
+ dataBufferDestroy(&two);
+ }
+ dlrStep(&left);
+ dlrStep(&right);
+ }
+ }
+
+ dlrDestroy(&left);
+ dlrDestroy(&right);
+ dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists: pLeft and pRight.
+** Write the intersection of these two doclists into pOut as a
+** DL_DOCIDS doclist.
+*/
+static void docListAndMerge(
+ const char *pLeft, int nLeft,
+ const char *pRight, int nRight,
+ DataBuffer *pOut /* Write the combined doclist here */
+){
+ DLReader left, right;
+ DLWriter writer;
+
+ if( nLeft==0 || nRight==0 ) return;
+
+ dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+ dlrInit(&right, DL_DOCIDS, pRight, nRight);
+ dlwInit(&writer, DL_DOCIDS, pOut);
+
+ while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){
+ if( dlrDocid(&left)<dlrDocid(&right) ){
+ dlrStep(&left);
+ }else if( dlrDocid(&right)<dlrDocid(&left) ){
+ dlrStep(&right);
+ }else{
+ dlwAdd(&writer, dlrDocid(&left));
+ dlrStep(&left);
+ dlrStep(&right);
+ }
+ }
+
+ dlrDestroy(&left);
+ dlrDestroy(&right);
+ dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists: pLeft and pRight.
+** Write the union of these two doclists into pOut as a
+** DL_DOCIDS doclist.
+*/
+static void docListOrMerge(
+ const char *pLeft, int nLeft,
+ const char *pRight, int nRight,
+ DataBuffer *pOut /* Write the combined doclist here */
+){
+ DLReader left, right;
+ DLWriter writer;
+
+ if( nLeft==0 ){
+ if( nRight!=0 ) dataBufferAppend(pOut, pRight, nRight);
+ return;
+ }
+ if( nRight==0 ){
+ dataBufferAppend(pOut, pLeft, nLeft);
+ return;
+ }
+
+ dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+ dlrInit(&right, DL_DOCIDS, pRight, nRight);
+ dlwInit(&writer, DL_DOCIDS, pOut);
+
+ while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){
+ if( dlrAtEnd(&right) ){
+ dlwAdd(&writer, dlrDocid(&left));
+ dlrStep(&left);
+ }else if( dlrAtEnd(&left) ){
+ dlwAdd(&writer, dlrDocid(&right));
+ dlrStep(&right);
+ }else if( dlrDocid(&left)<dlrDocid(&right) ){
+ dlwAdd(&writer, dlrDocid(&left));
+ dlrStep(&left);
+ }else if( dlrDocid(&right)<dlrDocid(&left) ){
+ dlwAdd(&writer, dlrDocid(&right));
+ dlrStep(&right);
+ }else{
+ dlwAdd(&writer, dlrDocid(&left));
+ dlrStep(&left);
+ dlrStep(&right);
+ }
+ }
+
+ dlrDestroy(&left);
+ dlrDestroy(&right);
+ dlwDestroy(&writer);
+}
+
+/* We have two DL_DOCIDS doclists: pLeft and pRight.
+** Write into pOut as DL_DOCIDS doclist containing all documents that
+** occur in pLeft but not in pRight.
+*/
+static void docListExceptMerge(
+ const char *pLeft, int nLeft,
+ const char *pRight, int nRight,
+ DataBuffer *pOut /* Write the combined doclist here */
+){
+ DLReader left, right;
+ DLWriter writer;
+
+ if( nLeft==0 ) return;
+ if( nRight==0 ){
+ dataBufferAppend(pOut, pLeft, nLeft);
+ return;
+ }
+
+ dlrInit(&left, DL_DOCIDS, pLeft, nLeft);
+ dlrInit(&right, DL_DOCIDS, pRight, nRight);
+ dlwInit(&writer, DL_DOCIDS, pOut);
+
+ while( !dlrAtEnd(&left) ){
+ while( !dlrAtEnd(&right) && dlrDocid(&right)<dlrDocid(&left) ){
+ dlrStep(&right);
+ }
+ if( dlrAtEnd(&right) || dlrDocid(&left)<dlrDocid(&right) ){
+ dlwAdd(&writer, dlrDocid(&left));
+ }
+ dlrStep(&left);
+ }
+
+ dlrDestroy(&left);
+ dlrDestroy(&right);
+ dlwDestroy(&writer);
+}
+
+static char *string_dup_n(const char *s, int n){
+ char *str = sqlite3_malloc(n + 1);
+ memcpy(str, s, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Duplicate a string; the caller must free() the returned string.
+ * (We don't use strdup() since it is not part of the standard C library and
+ * may not be available everywhere.) */
+static char *string_dup(const char *s){
+ return string_dup_n(s, strlen(s));
+}
+
+/* Format a string, replacing each occurrence of the % character with
+ * zDb.zName. This may be more convenient than sqlite_mprintf()
+ * when one string is used repeatedly in a format string.
+ * The caller must free() the returned string. */
+static char *string_format(const char *zFormat,
+ const char *zDb, const char *zName){
+ const char *p;
+ size_t len = 0;
+ size_t nDb = strlen(zDb);
+ size_t nName = strlen(zName);
+ size_t nFullTableName = nDb+1+nName;
+ char *result;
+ char *r;
+
+ /* first compute length needed */
+ for(p = zFormat ; *p ; ++p){
+ len += (*p=='%' ? nFullTableName : 1);
+ }
+ len += 1; /* for null terminator */
+
+ r = result = sqlite3_malloc(len);
+ for(p = zFormat; *p; ++p){
+ if( *p=='%' ){
+ memcpy(r, zDb, nDb);
+ r += nDb;
+ *r++ = '.';
+ memcpy(r, zName, nName);
+ r += nName;
+ } else {
+ *r++ = *p;
+ }
+ }
+ *r++ = '\0';
+ assert( r == result + len );
+ return result;
+}
+
+static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
+ const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
+ int rc;
+ FTSTRACE(("FTS3 sql: %s\n", zCommand));
+ rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
+ sqlite3_free(zCommand);
+ return rc;
+}
+
+static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
+ sqlite3_stmt **ppStmt, const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
+ int rc;
+ FTSTRACE(("FTS3 prepare: %s\n", zCommand));
+ rc = sqlite3_prepare_v2(db, zCommand, -1, ppStmt, NULL);
+ sqlite3_free(zCommand);
+ return rc;
+}
+
+/* end utility functions */
+
+/* Forward reference */
+typedef struct fulltext_vtab fulltext_vtab;
+
+/* A single term in a query is represented by an instances of
+** the following structure. Each word which may match against
+** document content is a term. Operators, like NEAR or OR, are
+** not terms. Query terms are organized as a flat list stored
+** in the Query.pTerms array.
+**
+** If the QueryTerm.nPhrase variable is non-zero, then the QueryTerm
+** is the first in a contiguous string of terms that are either part
+** of the same phrase, or connected by the NEAR operator.
+**
+** If the QueryTerm.nNear variable is non-zero, then the token is followed
+** by a NEAR operator with span set to (nNear-1). For example, the
+** following query:
+**
+** The QueryTerm.iPhrase variable stores the index of the token within
+** its phrase, indexed starting at 1, or 1 if the token is not part
+** of any phrase.
+**
+** For example, the data structure used to represent the following query:
+**
+** ... MATCH 'sqlite NEAR/5 google NEAR/2 "search engine"'
+**
+** is:
+**
+** {nPhrase=4, iPhrase=1, nNear=6, pTerm="sqlite"},
+** {nPhrase=0, iPhrase=1, nNear=3, pTerm="google"},
+** {nPhrase=0, iPhrase=1, nNear=0, pTerm="search"},
+** {nPhrase=0, iPhrase=2, nNear=0, pTerm="engine"},
+**
+** compiling the FTS3 syntax to Query structures is done by the parseQuery()
+** function.
+*/
+typedef struct QueryTerm {
+ short int nPhrase; /* How many following terms are part of the same phrase */
+ short int iPhrase; /* This is the i-th term of a phrase. */
+ short int iColumn; /* Column of the index that must match this term */
+ signed char nNear; /* term followed by a NEAR operator with span=(nNear-1) */
+ signed char isOr; /* this term is preceded by "OR" */
+ signed char isNot; /* this term is preceded by "-" */
+ signed char isPrefix; /* this term is followed by "*" */
+ char *pTerm; /* text of the term. '\000' terminated. malloced */
+ int nTerm; /* Number of bytes in pTerm[] */
+} QueryTerm;
+
+
+/* A query string is parsed into a Query structure.
+ *
+ * We could, in theory, allow query strings to be complicated
+ * nested expressions with precedence determined by parentheses.
+ * But none of the major search engines do this. (Perhaps the
+ * feeling is that an parenthesized expression is two complex of
+ * an idea for the average user to grasp.) Taking our lead from
+ * the major search engines, we will allow queries to be a list
+ * of terms (with an implied AND operator) or phrases in double-quotes,
+ * with a single optional "-" before each non-phrase term to designate
+ * negation and an optional OR connector.
+ *
+ * OR binds more tightly than the implied AND, which is what the
+ * major search engines seem to do. So, for example:
+ *
+ * [one two OR three] ==> one AND (two OR three)
+ * [one OR two three] ==> (one OR two) AND three
+ *
+ * A "-" before a term matches all entries that lack that term.
+ * The "-" must occur immediately before the term with in intervening
+ * space. This is how the search engines do it.
+ *
+ * A NOT term cannot be the right-hand operand of an OR. If this
+ * occurs in the query string, the NOT is ignored:
+ *
+ * [one OR -two] ==> one OR two
+ *
+ */
+typedef struct Query {
+ fulltext_vtab *pFts; /* The full text index */
+ int nTerms; /* Number of terms in the query */
+ QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */
+ int nextIsOr; /* Set the isOr flag on the next inserted term */
+ int nextIsNear; /* Set the isOr flag on the next inserted term */
+ int nextColumn; /* Next word parsed must be in this column */
+ int dfltColumn; /* The default column */
+} Query;
+
+
+/*
+** An instance of the following structure keeps track of generated
+** matching-word offset information and snippets.
+*/
+typedef struct Snippet {
+ int nMatch; /* Total number of matches */
+ int nAlloc; /* Space allocated for aMatch[] */
+ struct snippetMatch { /* One entry for each matching term */
+ char snStatus; /* Status flag for use while constructing snippets */
+ short int iCol; /* The column that contains the match */
+ short int iTerm; /* The index in Query.pTerms[] of the matching term */
+ int iToken; /* The index of the matching document token */
+ short int nByte; /* Number of bytes in the term */
+ int iStart; /* The offset to the first character of the term */
+ } *aMatch; /* Points to space obtained from malloc */
+ char *zOffset; /* Text rendering of aMatch[] */
+ int nOffset; /* strlen(zOffset) */
+ char *zSnippet; /* Snippet text */
+ int nSnippet; /* strlen(zSnippet) */
+} Snippet;
+
+
+typedef enum QueryType {
+ QUERY_GENERIC, /* table scan */
+ QUERY_DOCID, /* lookup by docid */
+ QUERY_FULLTEXT /* QUERY_FULLTEXT + [i] is a full-text search for column i*/
+} QueryType;
+
+typedef enum fulltext_statement {
+ CONTENT_INSERT_STMT,
+ CONTENT_SELECT_STMT,
+ CONTENT_UPDATE_STMT,
+ CONTENT_DELETE_STMT,
+ CONTENT_EXISTS_STMT,
+
+ BLOCK_INSERT_STMT,
+ BLOCK_SELECT_STMT,
+ BLOCK_DELETE_STMT,
+ BLOCK_DELETE_ALL_STMT,
+
+ SEGDIR_MAX_INDEX_STMT,
+ SEGDIR_SET_STMT,
+ SEGDIR_SELECT_LEVEL_STMT,
+ SEGDIR_SPAN_STMT,
+ SEGDIR_DELETE_STMT,
+ SEGDIR_SELECT_SEGMENT_STMT,
+ SEGDIR_SELECT_ALL_STMT,
+ SEGDIR_DELETE_ALL_STMT,
+ SEGDIR_COUNT_STMT,
+
+ MAX_STMT /* Always at end! */
+} fulltext_statement;
+
+/* These must exactly match the enum above. */
+/* TODO(shess): Is there some risk that a statement will be used in two
+** cursors at once, e.g. if a query joins a virtual table to itself?
+** If so perhaps we should move some of these to the cursor object.
+*/
+static const char *const fulltext_zStatement[MAX_STMT] = {
+ /* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */
+ /* CONTENT_SELECT */ NULL, /* generated in contentSelectStatement() */
+ /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */
+ /* CONTENT_DELETE */ "delete from %_content where docid = ?",
+ /* CONTENT_EXISTS */ "select docid from %_content limit 1",
+
+ /* BLOCK_INSERT */
+ "insert into %_segments (blockid, block) values (null, ?)",
+ /* BLOCK_SELECT */ "select block from %_segments where blockid = ?",
+ /* BLOCK_DELETE */ "delete from %_segments where blockid between ? and ?",
+ /* BLOCK_DELETE_ALL */ "delete from %_segments",
+
+ /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?",
+ /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)",
+ /* SEGDIR_SELECT_LEVEL */
+ "select start_block, leaves_end_block, root from %_segdir "
+ " where level = ? order by idx",
+ /* SEGDIR_SPAN */
+ "select min(start_block), max(end_block) from %_segdir "
+ " where level = ? and start_block <> 0",
+ /* SEGDIR_DELETE */ "delete from %_segdir where level = ?",
+
+ /* NOTE(shess): The first three results of the following two
+ ** statements must match.
+ */
+ /* SEGDIR_SELECT_SEGMENT */
+ "select start_block, leaves_end_block, root from %_segdir "
+ " where level = ? and idx = ?",
+ /* SEGDIR_SELECT_ALL */
+ "select start_block, leaves_end_block, root from %_segdir "
+ " order by level desc, idx asc",
+ /* SEGDIR_DELETE_ALL */ "delete from %_segdir",
+ /* SEGDIR_COUNT */ "select count(*), ifnull(max(level),0) from %_segdir",
+};
+
+/*
+** A connection to a fulltext index is an instance of the following
+** structure. The xCreate and xConnect methods create an instance
+** of this structure and xDestroy and xDisconnect free that instance.
+** All other methods receive a pointer to the structure as one of their
+** arguments.
+*/
+struct fulltext_vtab {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ sqlite3 *db; /* The database connection */
+ const char *zDb; /* logical database name */
+ const char *zName; /* virtual table name */
+ int nColumn; /* number of columns in virtual table */
+ char **azColumn; /* column names. malloced */
+ char **azContentColumn; /* column names in content table; malloced */
+ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
+
+ /* Precompiled statements which we keep as long as the table is
+ ** open.
+ */
+ sqlite3_stmt *pFulltextStatements[MAX_STMT];
+
+ /* Precompiled statements used for segment merges. We run a
+ ** separate select across the leaf level of each tree being merged.
+ */
+ sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT];
+ /* The statement used to prepare pLeafSelectStmts. */
+#define LEAF_SELECT \
+ "select block from %_segments where blockid between ? and ? order by blockid"
+
+ /* These buffer pending index updates during transactions.
+ ** nPendingData estimates the memory size of the pending data. It
+ ** doesn't include the hash-bucket overhead, nor any malloc
+ ** overhead. When nPendingData exceeds kPendingThreshold, the
+ ** buffer is flushed even before the transaction closes.
+ ** pendingTerms stores the data, and is only valid when nPendingData
+ ** is >=0 (nPendingData<0 means pendingTerms has not been
+ ** initialized). iPrevDocid is the last docid written, used to make
+ ** certain we're inserting in sorted order.
+ */
+ int nPendingData;
+#define kPendingThreshold (1*1024*1024)
+ sqlite_int64 iPrevDocid;
+ fts3Hash pendingTerms;
+};
+
+/*
+** When the core wants to do a query, it create a cursor using a
+** call to xOpen. This structure is an instance of a cursor. It
+** is destroyed by xClose.
+*/
+typedef struct fulltext_cursor {
+ sqlite3_vtab_cursor base; /* Base class used by SQLite core */
+ QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */
+ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
+ int eof; /* True if at End Of Results */
+ Query q; /* Parsed query string */
+ Snippet snippet; /* Cached snippet for the current row */
+ int iColumn; /* Column being searched */
+ DataBuffer result; /* Doclist results from fulltextQuery */
+ DLReader reader; /* Result reader if result not empty */
+} fulltext_cursor;
+
+static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
+ return (fulltext_vtab *) c->base.pVtab;
+}
+
+static const sqlite3_module fts3Module; /* forward declaration */
+
+/* Return a dynamically generated statement of the form
+ * insert into %_content (docid, ...) values (?, ...)
+ */
+static const char *contentInsertStatement(fulltext_vtab *v){
+ StringBuffer sb;
+ int i;
+
+ initStringBuffer(&sb);
+ append(&sb, "insert into %_content (docid, ");
+ appendList(&sb, v->nColumn, v->azContentColumn);
+ append(&sb, ") values (?");
+ for(i=0; i<v->nColumn; ++i)
+ append(&sb, ", ?");
+ append(&sb, ")");
+ return stringBufferData(&sb);
+}
+
+/* Return a dynamically generated statement of the form
+ * select <content columns> from %_content where docid = ?
+ */
+static const char *contentSelectStatement(fulltext_vtab *v){
+ StringBuffer sb;
+ initStringBuffer(&sb);
+ append(&sb, "SELECT ");
+ appendList(&sb, v->nColumn, v->azContentColumn);
+ append(&sb, " FROM %_content WHERE docid = ?");
+ return stringBufferData(&sb);
+}
+
+/* Return a dynamically generated statement of the form
+ * update %_content set [col_0] = ?, [col_1] = ?, ...
+ * where docid = ?
+ */
+static const char *contentUpdateStatement(fulltext_vtab *v){
+ StringBuffer sb;
+ int i;
+
+ initStringBuffer(&sb);
+ append(&sb, "update %_content set ");
+ for(i=0; i<v->nColumn; ++i) {
+ if( i>0 ){
+ append(&sb, ", ");
+ }
+ append(&sb, v->azContentColumn[i]);
+ append(&sb, " = ?");
+ }
+ append(&sb, " where docid = ?");
+ return stringBufferData(&sb);
+}
+
+/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
+** If the indicated statement has never been prepared, it is prepared
+** and cached, otherwise the cached version is reset.
+*/
+static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
+ sqlite3_stmt **ppStmt){
+ assert( iStmt<MAX_STMT );
+ if( v->pFulltextStatements[iStmt]==NULL ){
+ const char *zStmt;
+ int rc;
+ switch( iStmt ){
+ case CONTENT_INSERT_STMT:
+ zStmt = contentInsertStatement(v); break;
+ case CONTENT_SELECT_STMT:
+ zStmt = contentSelectStatement(v); break;
+ case CONTENT_UPDATE_STMT:
+ zStmt = contentUpdateStatement(v); break;
+ default:
+ zStmt = fulltext_zStatement[iStmt];
+ }
+ rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
+ zStmt);
+ if( zStmt != fulltext_zStatement[iStmt]) sqlite3_free((void *) zStmt);
+ if( rc!=SQLITE_OK ) return rc;
+ } else {
+ int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ *ppStmt = v->pFulltextStatements[iStmt];
+ return SQLITE_OK;
+}
+
+/* Like sqlite3_step(), but convert SQLITE_DONE to SQLITE_OK and
+** SQLITE_ROW to SQLITE_ERROR. Useful for statements like UPDATE,
+** where we expect no results.
+*/
+static int sql_single_step(sqlite3_stmt *s){
+ int rc = sqlite3_step(s);
+ return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
+}
+
+/* Like sql_get_statement(), but for special replicated LEAF_SELECT
+** statements. idx -1 is a special case for an uncached version of
+** the statement (used in the optimize implementation).
+*/
+/* TODO(shess) Write version for generic statements and then share
+** that between the cached-statement functions.
+*/
+static int sql_get_leaf_statement(fulltext_vtab *v, int idx,
+ sqlite3_stmt **ppStmt){
+ assert( idx>=-1 && idx<MERGE_COUNT );
+ if( idx==-1 ){
+ return sql_prepare(v->db, v->zDb, v->zName, ppStmt, LEAF_SELECT);
+ }else if( v->pLeafSelectStmts[idx]==NULL ){
+ int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx],
+ LEAF_SELECT);
+ if( rc!=SQLITE_OK ) return rc;
+ }else{
+ int rc = sqlite3_reset(v->pLeafSelectStmts[idx]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ *ppStmt = v->pLeafSelectStmts[idx];
+ return SQLITE_OK;
+}
+
+/* insert into %_content (docid, ...) values ([docid], [pValues])
+** If the docid contains SQL NULL, then a unique docid will be
+** generated.
+*/
+static int content_insert(fulltext_vtab *v, sqlite3_value *docid,
+ sqlite3_value **pValues){
+ sqlite3_stmt *s;
+ int i;
+ int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_value(s, 1, docid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i=0; i<v->nColumn; ++i){
+ rc = sqlite3_bind_value(s, 2+i, pValues[i]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ return sql_single_step(s);
+}
+
+/* update %_content set col0 = pValues[0], col1 = pValues[1], ...
+ * where docid = [iDocid] */
+static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
+ sqlite_int64 iDocid){
+ sqlite3_stmt *s;
+ int i;
+ int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i=0; i<v->nColumn; ++i){
+ rc = sqlite3_bind_value(s, 1+i, pValues[i]);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ rc = sqlite3_bind_int64(s, 1+v->nColumn, iDocid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+static void freeStringArray(int nString, const char **pString){
+ int i;
+
+ for (i=0 ; i < nString ; ++i) {
+ if( pString[i]!=NULL ) sqlite3_free((void *) pString[i]);
+ }
+ sqlite3_free((void *) pString);
+}
+
+/* select * from %_content where docid = [iDocid]
+ * The caller must delete the returned array and all strings in it.
+ * null fields will be NULL in the returned array.
+ *
+ * TODO: Perhaps we should return pointer/length strings here for consistency
+ * with other code which uses pointer/length. */
+static int content_select(fulltext_vtab *v, sqlite_int64 iDocid,
+ const char ***pValues){
+ sqlite3_stmt *s;
+ const char **values;
+ int i;
+ int rc;
+
+ *pValues = NULL;
+
+ rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iDocid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc!=SQLITE_ROW ) return rc;
+
+ values = (const char **) sqlite3_malloc(v->nColumn * sizeof(const char *));
+ for(i=0; i<v->nColumn; ++i){
+ if( sqlite3_column_type(s, i)==SQLITE_NULL ){
+ values[i] = NULL;
+ }else{
+ values[i] = string_dup((char*)sqlite3_column_text(s, i));
+ }
+ }
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ){
+ *pValues = values;
+ return SQLITE_OK;
+ }
+
+ freeStringArray(v->nColumn, values);
+ return rc;
+}
+
+/* delete from %_content where docid = [iDocid ] */
+static int content_delete(fulltext_vtab *v, sqlite_int64 iDocid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iDocid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if
+** no rows exist, and any error in case of failure.
+*/
+static int content_exists(fulltext_vtab *v){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc!=SQLITE_ROW ) return rc;
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ) return SQLITE_ROW;
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ return rc;
+}
+
+/* insert into %_segments values ([pData])
+** returns assigned blockid in *piBlockid
+*/
+static int block_insert(fulltext_vtab *v, const char *pData, int nData,
+ sqlite_int64 *piBlockid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, BLOCK_INSERT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 1, pData, nData, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ if( rc!=SQLITE_DONE ) return rc;
+
+ /* blockid column is an alias for rowid. */
+ *piBlockid = sqlite3_last_insert_rowid(v->db);
+ return SQLITE_OK;
+}
+
+/* delete from %_segments
+** where blockid between [iStartBlockid] and [iEndBlockid]
+**
+** Deletes the range of blocks, inclusive, used to delete the blocks
+** which form a segment.
+*/
+static int block_delete(fulltext_vtab *v,
+ sqlite_int64 iStartBlockid, sqlite_int64 iEndBlockid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, BLOCK_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iStartBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, iEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+/* Returns SQLITE_ROW with *pidx set to the maximum segment idx found
+** at iLevel. Returns SQLITE_DONE if there are no segments at
+** iLevel. Otherwise returns an error.
+*/
+static int segdir_max_index(fulltext_vtab *v, int iLevel, int *pidx){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_MAX_INDEX_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 1, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ /* Should always get at least one row due to how max() works. */
+ if( rc==SQLITE_DONE ) return SQLITE_DONE;
+ if( rc!=SQLITE_ROW ) return rc;
+
+ /* NULL means that there were no inputs to max(). */
+ if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ return rc;
+ }
+
+ *pidx = sqlite3_column_int(s, 0);
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ if( rc!=SQLITE_DONE ) return rc;
+ return SQLITE_ROW;
+}
+
+/* insert into %_segdir values (
+** [iLevel], [idx],
+** [iStartBlockid], [iLeavesEndBlockid], [iEndBlockid],
+** [pRootData]
+** )
+*/
+static int segdir_set(fulltext_vtab *v, int iLevel, int idx,
+ sqlite_int64 iStartBlockid,
+ sqlite_int64 iLeavesEndBlockid,
+ sqlite_int64 iEndBlockid,
+ const char *pRootData, int nRootData){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_SET_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 1, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 2, idx);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 3, iStartBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 4, iLeavesEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 5, iEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_blob(s, 6, pRootData, nRootData, SQLITE_STATIC);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+/* Queries %_segdir for the block span of the segments in level
+** iLevel. Returns SQLITE_DONE if there are no blocks for iLevel,
+** SQLITE_ROW if there are blocks, else an error.
+*/
+static int segdir_span(fulltext_vtab *v, int iLevel,
+ sqlite_int64 *piStartBlockid,
+ sqlite_int64 *piEndBlockid){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_SPAN_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 1, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ) return SQLITE_DONE; /* Should never happen */
+ if( rc!=SQLITE_ROW ) return rc;
+
+ /* This happens if all segments at this level are entirely inline. */
+ if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ int rc2 = sqlite3_step(s);
+ if( rc2==SQLITE_ROW ) return SQLITE_ERROR;
+ return rc2;
+ }
+
+ *piStartBlockid = sqlite3_column_int64(s, 0);
+ *piEndBlockid = sqlite3_column_int64(s, 1);
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ if( rc!=SQLITE_DONE ) return rc;
+ return SQLITE_ROW;
+}
+
+/* Delete the segment blocks and segment directory records for all
+** segments at iLevel.
+*/
+static int segdir_delete(fulltext_vtab *v, int iLevel){
+ sqlite3_stmt *s;
+ sqlite_int64 iStartBlockid, iEndBlockid;
+ int rc = segdir_span(v, iLevel, &iStartBlockid, &iEndBlockid);
+ if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc;
+
+ if( rc==SQLITE_ROW ){
+ rc = block_delete(v, iStartBlockid, iEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ /* Delete the segment directory itself. */
+ rc = sql_get_statement(v, SEGDIR_DELETE_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+/* Delete entire fts index, SQLITE_OK on success, relevant error on
+** failure.
+*/
+static int segdir_delete_all(fulltext_vtab *v){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_single_step(s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return sql_single_step(s);
+}
+
+/* Returns SQLITE_OK with *pnSegments set to the number of entries in
+** %_segdir and *piMaxLevel set to the highest level which has a
+** segment. Otherwise returns the SQLite error which caused failure.
+*/
+static int segdir_count(fulltext_vtab *v, int *pnSegments, int *piMaxLevel){
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_COUNT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ /* TODO(shess): This case should not be possible? Should stronger
+ ** measures be taken if it happens?
+ */
+ if( rc==SQLITE_DONE ){
+ *pnSegments = 0;
+ *piMaxLevel = 0;
+ return SQLITE_OK;
+ }
+ if( rc!=SQLITE_ROW ) return rc;
+
+ *pnSegments = sqlite3_column_int(s, 0);
+ *piMaxLevel = sqlite3_column_int(s, 1);
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ) return SQLITE_OK;
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ return rc;
+}
+
+/* TODO(shess) clearPendingTerms() is far down the file because
+** writeZeroSegment() is far down the file because LeafWriter is far
+** down the file. Consider refactoring the code to move the non-vtab
+** code above the vtab code so that we don't need this forward
+** reference.
+*/
+static int clearPendingTerms(fulltext_vtab *v);
+
+/*
+** Free the memory used to contain a fulltext_vtab structure.
+*/
+static void fulltext_vtab_destroy(fulltext_vtab *v){
+ int iStmt, i;
+
+ FTSTRACE(("FTS3 Destroy %p\n", v));
+ for( iStmt=0; iStmt<MAX_STMT; iStmt++ ){
+ if( v->pFulltextStatements[iStmt]!=NULL ){
+ sqlite3_finalize(v->pFulltextStatements[iStmt]);
+ v->pFulltextStatements[iStmt] = NULL;
+ }
+ }
+
+ for( i=0; i<MERGE_COUNT; i++ ){
+ if( v->pLeafSelectStmts[i]!=NULL ){
+ sqlite3_finalize(v->pLeafSelectStmts[i]);
+ v->pLeafSelectStmts[i] = NULL;
+ }
+ }
+
+ if( v->pTokenizer!=NULL ){
+ v->pTokenizer->pModule->xDestroy(v->pTokenizer);
+ v->pTokenizer = NULL;
+ }
+
+ clearPendingTerms(v);
+
+ sqlite3_free(v->azColumn);
+ for(i = 0; i < v->nColumn; ++i) {
+ sqlite3_free(v->azContentColumn[i]);
+ }
+ sqlite3_free(v->azContentColumn);
+ sqlite3_free(v);
+}
+
+/*
+** Token types for parsing the arguments to xConnect or xCreate.
+*/
+#define TOKEN_EOF 0 /* End of file */
+#define TOKEN_SPACE 1 /* Any kind of whitespace */
+#define TOKEN_ID 2 /* An identifier */
+#define TOKEN_STRING 3 /* A string literal */
+#define TOKEN_PUNCT 4 /* A single punctuation character */
+
+/*
+** If X is a character that can be used in an identifier then
+** ftsIdChar(X) will be true. Otherwise it is false.
+**
+** For ASCII, any character with the high-order bit set is
+** allowed in an identifier. For 7-bit characters,
+** isFtsIdChar[X] must be 1.
+**
+** Ticket #1066. the SQL standard does not allow '$' in the
+** middle of identfiers. But many SQL implementations do.
+** SQLite will allow '$' in identifiers for compatibility.
+** But the feature is undocumented.
+*/
+static const char isFtsIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+};
+#define ftsIdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isFtsIdChar[c-0x20]))
+
+
+/*
+** Return the length of the token that begins at z[0].
+** Store the token type in *tokenType before returning.
+*/
+static int ftsGetToken(const char *z, int *tokenType){
+ int i, c;
+ switch( *z ){
+ case 0: {
+ *tokenType = TOKEN_EOF;
+ return 0;
+ }
+ case ' ': case '\t': case '\n': case '\f': case '\r': {
+ for(i=1; safe_isspace(z[i]); i++){}
+ *tokenType = TOKEN_SPACE;
+ return i;
+ }
+ case '`':
+ case '\'':
+ case '"': {
+ int delim = z[0];
+ for(i=1; (c=z[i])!=0; i++){
+ if( c==delim ){
+ if( z[i+1]==delim ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ }
+ *tokenType = TOKEN_STRING;
+ return i + (c!=0);
+ }
+ case '[': {
+ for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
+ *tokenType = TOKEN_ID;
+ return i;
+ }
+ default: {
+ if( !ftsIdChar(*z) ){
+ break;
+ }
+ for(i=1; ftsIdChar(z[i]); i++){}
+ *tokenType = TOKEN_ID;
+ return i;
+ }
+ }
+ *tokenType = TOKEN_PUNCT;
+ return 1;
+}
+
+/*
+** A token extracted from a string is an instance of the following
+** structure.
+*/
+typedef struct FtsToken {
+ const char *z; /* Pointer to token text. Not '\000' terminated */
+ short int n; /* Length of the token text in bytes. */
+} FtsToken;
+
+/*
+** Given a input string (which is really one of the argv[] parameters
+** passed into xConnect or xCreate) split the string up into tokens.
+** Return an array of pointers to '\000' terminated strings, one string
+** for each non-whitespace token.
+**
+** The returned array is terminated by a single NULL pointer.
+**
+** Space to hold the returned array is obtained from a single
+** malloc and should be freed by passing the return value to free().
+** The individual strings within the token list are all a part of
+** the single memory allocation and will all be freed at once.
+*/
+static char **tokenizeString(const char *z, int *pnToken){
+ int nToken = 0;
+ FtsToken *aToken = sqlite3_malloc( strlen(z) * sizeof(aToken[0]) );
+ int n = 1;
+ int e, i;
+ int totalSize = 0;
+ char **azToken;
+ char *zCopy;
+ while( n>0 ){
+ n = ftsGetToken(z, &e);
+ if( e!=TOKEN_SPACE ){
+ aToken[nToken].z = z;
+ aToken[nToken].n = n;
+ nToken++;
+ totalSize += n+1;
+ }
+ z += n;
+ }
+ azToken = (char**)sqlite3_malloc( nToken*sizeof(char*) + totalSize );
+ zCopy = (char*)&azToken[nToken];
+ nToken--;
+ for(i=0; i<nToken; i++){
+ azToken[i] = zCopy;
+ n = aToken[i].n;
+ memcpy(zCopy, aToken[i].z, n);
+ zCopy[n] = 0;
+ zCopy += n+1;
+ }
+ azToken[nToken] = 0;
+ sqlite3_free(aToken);
+ *pnToken = nToken;
+ return azToken;
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+** "abc" becomes abc
+** 'xyz' becomes xyz
+** [pqr] becomes pqr
+** `mno` becomes mno
+*/
+static void dequoteString(char *z){
+ int quote;
+ int i, j;
+ if( z==0 ) return;
+ quote = z[0];
+ switch( quote ){
+ case '\'': break;
+ case '"': break;
+ case '`': break; /* For MySQL compatibility */
+ case '[': quote = ']'; break; /* For MS SqlServer compatibility */
+ default: return;
+ }
+ for(i=1, j=0; z[i]; i++){
+ if( z[i]==quote ){
+ if( z[i+1]==quote ){
+ z[j++] = quote;
+ i++;
+ }else{
+ z[j++] = 0;
+ break;
+ }
+ }else{
+ z[j++] = z[i];
+ }
+ }
+}
+
+/*
+** The input azIn is a NULL-terminated list of tokens. Remove the first
+** token and all punctuation tokens. Remove the quotes from
+** around string literal tokens.
+**
+** Example:
+**
+** input: tokenize chinese ( 'simplifed' , 'mixed' )
+** output: chinese simplifed mixed
+**
+** Another example:
+**
+** input: delimiters ( '[' , ']' , '...' )
+** output: [ ] ...
+*/
+static void tokenListToIdList(char **azIn){
+ int i, j;
+ if( azIn ){
+ for(i=0, j=-1; azIn[i]; i++){
+ if( safe_isalnum(azIn[i][0]) || azIn[i][1] ){
+ dequoteString(azIn[i]);
+ if( j>=0 ){
+ azIn[j] = azIn[i];
+ }
+ j++;
+ }
+ }
+ azIn[j] = 0;
+ }
+}
+
+
+/*
+** Find the first alphanumeric token in the string zIn. Null-terminate
+** this token. Remove any quotation marks. And return a pointer to
+** the result.
+*/
+static char *firstToken(char *zIn, char **pzTail){
+ int n, ttype;
+ while(1){
+ n = ftsGetToken(zIn, &ttype);
+ if( ttype==TOKEN_SPACE ){
+ zIn += n;
+ }else if( ttype==TOKEN_EOF ){
+ *pzTail = zIn;
+ return 0;
+ }else{
+ zIn[n] = 0;
+ *pzTail = &zIn[1];
+ dequoteString(zIn);
+ return zIn;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/* Return true if...
+**
+** * s begins with the string t, ignoring case
+** * s is longer than t
+** * The first character of s beyond t is not a alphanumeric
+**
+** Ignore leading space in *s.
+**
+** To put it another way, return true if the first token of
+** s[] is t[].
+*/
+static int startsWith(const char *s, const char *t){
+ while( safe_isspace(*s) ){ s++; }
+ while( *t ){
+ if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0;
+ }
+ return *s!='_' && !safe_isalnum(*s);
+}
+
+/*
+** An instance of this structure defines the "spec" of a
+** full text index. This structure is populated by parseSpec
+** and use by fulltextConnect and fulltextCreate.
+*/
+typedef struct TableSpec {
+ const char *zDb; /* Logical database name */
+ const char *zName; /* Name of the full-text index */
+ int nColumn; /* Number of columns to be indexed */
+ char **azColumn; /* Original names of columns to be indexed */
+ char **azContentColumn; /* Column names for %_content */
+ char **azTokenizer; /* Name of tokenizer and its arguments */
+} TableSpec;
+
+/*
+** Reclaim all of the memory used by a TableSpec
+*/
+static void clearTableSpec(TableSpec *p) {
+ sqlite3_free(p->azColumn);
+ sqlite3_free(p->azContentColumn);
+ sqlite3_free(p->azTokenizer);
+}
+
+/* Parse a CREATE VIRTUAL TABLE statement, which looks like this:
+ *
+ * CREATE VIRTUAL TABLE email
+ * USING fts3(subject, body, tokenize mytokenizer(myarg))
+ *
+ * We return parsed information in a TableSpec structure.
+ *
+ */
+static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
+ char**pzErr){
+ int i, n;
+ char *z, *zDummy;
+ char **azArg;
+ const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */
+
+ assert( argc>=3 );
+ /* Current interface:
+ ** argv[0] - module name
+ ** argv[1] - database name
+ ** argv[2] - table name
+ ** argv[3..] - columns, optionally followed by tokenizer specification
+ ** and snippet delimiters specification.
+ */
+
+ /* Make a copy of the complete argv[][] array in a single allocation.
+ ** The argv[][] array is read-only and transient. We can write to the
+ ** copy in order to modify things and the copy is persistent.
+ */
+ CLEAR(pSpec);
+ for(i=n=0; i<argc; i++){
+ n += strlen(argv[i]) + 1;
+ }
+ azArg = sqlite3_malloc( sizeof(char*)*argc + n );
+ if( azArg==0 ){
+ return SQLITE_NOMEM;
+ }
+ z = (char*)&azArg[argc];
+ for(i=0; i<argc; i++){
+ azArg[i] = z;
+ strcpy(z, argv[i]);
+ z += strlen(z)+1;
+ }
+
+ /* Identify the column names and the tokenizer and delimiter arguments
+ ** in the argv[][] array.
+ */
+ pSpec->zDb = azArg[1];
+ pSpec->zName = azArg[2];
+ pSpec->nColumn = 0;
+ pSpec->azColumn = azArg;
+ zTokenizer = "tokenize simple";
+ for(i=3; i<argc; ++i){
+ if( startsWith(azArg[i],"tokenize") ){
+ zTokenizer = azArg[i];
+ }else{
+ z = azArg[pSpec->nColumn] = firstToken(azArg[i], &zDummy);
+ pSpec->nColumn++;
+ }
+ }
+ if( pSpec->nColumn==0 ){
+ azArg[0] = "content";
+ pSpec->nColumn = 1;
+ }
+
+ /*
+ ** Construct the list of content column names.
+ **
+ ** Each content column name will be of the form cNNAAAA
+ ** where NN is the column number and AAAA is the sanitized
+ ** column name. "sanitized" means that special characters are
+ ** converted to "_". The cNN prefix guarantees that all column
+ ** names are unique.
+ **
+ ** The AAAA suffix is not strictly necessary. It is included
+ ** for the convenience of people who might examine the generated
+ ** %_content table and wonder what the columns are used for.
+ */
+ pSpec->azContentColumn = sqlite3_malloc( pSpec->nColumn * sizeof(char *) );
+ if( pSpec->azContentColumn==0 ){
+ clearTableSpec(pSpec);
+ return SQLITE_NOMEM;
+ }
+ for(i=0; i<pSpec->nColumn; i++){
+ char *p;
+ pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]);
+ for (p = pSpec->azContentColumn[i]; *p ; ++p) {
+ if( !safe_isalnum(*p) ) *p = '_';
+ }
+ }
+
+ /*
+ ** Parse the tokenizer specification string.
+ */
+ pSpec->azTokenizer = tokenizeString(zTokenizer, &n);
+ tokenListToIdList(pSpec->azTokenizer);
+
+ return SQLITE_OK;
+}
+
+/*
+** Generate a CREATE TABLE statement that describes the schema of
+** the virtual table. Return a pointer to this schema string.
+**
+** Space is obtained from sqlite3_mprintf() and should be freed
+** using sqlite3_free().
+*/
+static char *fulltextSchema(
+ int nColumn, /* Number of columns */
+ const char *const* azColumn, /* List of columns */
+ const char *zTableName /* Name of the table */
+){
+ int i;
+ char *zSchema, *zNext;
+ const char *zSep = "(";
+ zSchema = sqlite3_mprintf("CREATE TABLE x");
+ for(i=0; i<nColumn; i++){
+ zNext = sqlite3_mprintf("%s%s%Q", zSchema, zSep, azColumn[i]);
+ sqlite3_free(zSchema);
+ zSchema = zNext;
+ zSep = ",";
+ }
+ zNext = sqlite3_mprintf("%s,%Q HIDDEN", zSchema, zTableName);
+ sqlite3_free(zSchema);
+ zSchema = zNext;
+ zNext = sqlite3_mprintf("%s,docid HIDDEN)", zSchema);
+ sqlite3_free(zSchema);
+ return zNext;
+}
+
+/*
+** Build a new sqlite3_vtab structure that will describe the
+** fulltext index defined by spec.
+*/
+static int constructVtab(
+ sqlite3 *db, /* The SQLite database connection */
+ fts3Hash *pHash, /* Hash table containing tokenizers */
+ TableSpec *spec, /* Parsed spec information from parseSpec() */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ int rc;
+ int n;
+ fulltext_vtab *v = 0;
+ const sqlite3_tokenizer_module *m = NULL;
+ char *schema;
+
+ char const *zTok; /* Name of tokenizer to use for this fts table */
+ int nTok; /* Length of zTok, including nul terminator */
+
+ v = (fulltext_vtab *) sqlite3_malloc(sizeof(fulltext_vtab));
+ if( v==0 ) return SQLITE_NOMEM;
+ CLEAR(v);
+ /* sqlite will initialize v->base */
+ v->db = db;
+ v->zDb = spec->zDb; /* Freed when azColumn is freed */
+ v->zName = spec->zName; /* Freed when azColumn is freed */
+ v->nColumn = spec->nColumn;
+ v->azContentColumn = spec->azContentColumn;
+ spec->azContentColumn = 0;
+ v->azColumn = spec->azColumn;
+ spec->azColumn = 0;
+
+ if( spec->azTokenizer==0 ){
+ return SQLITE_NOMEM;
+ }
+
+ zTok = spec->azTokenizer[0];
+ if( !zTok ){
+ zTok = "simple";
+ }
+ nTok = strlen(zTok)+1;
+
+ m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zTok, nTok);
+ if( !m ){
+ *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]);
+ rc = SQLITE_ERROR;
+ goto err;
+ }
+
+ for(n=0; spec->azTokenizer[n]; n++){}
+ if( n ){
+ rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1],
+ &v->pTokenizer);
+ }else{
+ rc = m->xCreate(0, 0, &v->pTokenizer);
+ }
+ if( rc!=SQLITE_OK ) goto err;
+ v->pTokenizer->pModule = m;
+
+ /* TODO: verify the existence of backing tables foo_content, foo_term */
+
+ schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn,
+ spec->zName);
+ rc = sqlite3_declare_vtab(db, schema);
+ sqlite3_free(schema);
+ if( rc!=SQLITE_OK ) goto err;
+
+ memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
+
+ /* Indicate that the buffer is not live. */
+ v->nPendingData = -1;
+
+ *ppVTab = &v->base;
+ FTSTRACE(("FTS3 Connect %p\n", v));
+
+ return rc;
+
+err:
+ fulltext_vtab_destroy(v);
+ return rc;
+}
+
+static int fulltextConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr
+){
+ TableSpec spec;
+ int rc = parseSpec(&spec, argc, argv, pzErr);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr);
+ clearTableSpec(&spec);
+ return rc;
+}
+
+/* The %_content table holds the text of each document, with
+** the docid column exposed as the SQLite rowid for the table.
+*/
+/* TODO(shess) This comment needs elaboration to match the updated
+** code. Work it into the top-of-file comment at that time.
+*/
+static int fulltextCreate(sqlite3 *db, void *pAux,
+ int argc, const char * const *argv,
+ sqlite3_vtab **ppVTab, char **pzErr){
+ int rc;
+ TableSpec spec;
+ StringBuffer schema;
+ FTSTRACE(("FTS3 Create\n"));
+
+ rc = parseSpec(&spec, argc, argv, pzErr);
+ if( rc!=SQLITE_OK ) return rc;
+
+ initStringBuffer(&schema);
+ append(&schema, "CREATE TABLE %_content(");
+ append(&schema, " docid INTEGER PRIMARY KEY,");
+ appendList(&schema, spec.nColumn, spec.azContentColumn);
+ append(&schema, ")");
+ rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema));
+ stringBufferDestroy(&schema);
+ if( rc!=SQLITE_OK ) goto out;
+
+ rc = sql_exec(db, spec.zDb, spec.zName,
+ "create table %_segments("
+ " blockid INTEGER PRIMARY KEY,"
+ " block blob"
+ ");"
+ );
+ if( rc!=SQLITE_OK ) goto out;
+
+ rc = sql_exec(db, spec.zDb, spec.zName,
+ "create table %_segdir("
+ " level integer,"
+ " idx integer,"
+ " start_block integer,"
+ " leaves_end_block integer,"
+ " end_block integer,"
+ " root blob,"
+ " primary key(level, idx)"
+ ");");
+ if( rc!=SQLITE_OK ) goto out;
+
+ rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr);
+
+out:
+ clearTableSpec(&spec);
+ return rc;
+}
+
+/* Decide how to handle an SQL query. */
+static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+ fulltext_vtab *v = (fulltext_vtab *)pVTab;
+ int i;
+ FTSTRACE(("FTS3 BestIndex\n"));
+
+ for(i=0; i<pInfo->nConstraint; ++i){
+ const struct sqlite3_index_constraint *pConstraint;
+ pConstraint = &pInfo->aConstraint[i];
+ if( pConstraint->usable ) {
+ if( (pConstraint->iColumn==-1 || pConstraint->iColumn==v->nColumn+1) &&
+ pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ pInfo->idxNum = QUERY_DOCID; /* lookup by docid */
+ FTSTRACE(("FTS3 QUERY_DOCID\n"));
+ } else if( pConstraint->iColumn>=0 && pConstraint->iColumn<=v->nColumn &&
+ pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ /* full-text search */
+ pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
+ FTSTRACE(("FTS3 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
+ } else continue;
+
+ pInfo->aConstraintUsage[i].argvIndex = 1;
+ pInfo->aConstraintUsage[i].omit = 1;
+
+ /* An arbitrary value for now.
+ * TODO: Perhaps docid matches should be considered cheaper than
+ * full-text searches. */
+ pInfo->estimatedCost = 1.0;
+
+ return SQLITE_OK;
+ }
+ }
+ pInfo->idxNum = QUERY_GENERIC;
+ return SQLITE_OK;
+}
+
+static int fulltextDisconnect(sqlite3_vtab *pVTab){
+ FTSTRACE(("FTS3 Disconnect %p\n", pVTab));
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextDestroy(sqlite3_vtab *pVTab){
+ fulltext_vtab *v = (fulltext_vtab *)pVTab;
+ int rc;
+
+ FTSTRACE(("FTS3 Destroy %p\n", pVTab));
+ rc = sql_exec(v->db, v->zDb, v->zName,
+ "drop table if exists %_content;"
+ "drop table if exists %_segments;"
+ "drop table if exists %_segdir;"
+ );
+ if( rc!=SQLITE_OK ) return rc;
+
+ fulltext_vtab_destroy((fulltext_vtab *)pVTab);
+ return SQLITE_OK;
+}
+
+static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ fulltext_cursor *c;
+
+ c = (fulltext_cursor *) sqlite3_malloc(sizeof(fulltext_cursor));
+ if( c ){
+ memset(c, 0, sizeof(fulltext_cursor));
+ /* sqlite will initialize c->base */
+ *ppCursor = &c->base;
+ FTSTRACE(("FTS3 Open %p: %p\n", pVTab, c));
+ return SQLITE_OK;
+ }else{
+ return SQLITE_NOMEM;
+ }
+}
+
+
+/* Free all of the dynamically allocated memory held by *q
+*/
+static void queryClear(Query *q){
+ int i;
+ for(i = 0; i < q->nTerms; ++i){
+ sqlite3_free(q->pTerms[i].pTerm);
+ }
+ sqlite3_free(q->pTerms);
+ CLEAR(q);
+}
+
+/* Free all of the dynamically allocated memory held by the
+** Snippet
+*/
+static void snippetClear(Snippet *p){
+ sqlite3_free(p->aMatch);
+ sqlite3_free(p->zOffset);
+ sqlite3_free(p->zSnippet);
+ CLEAR(p);
+}
+/*
+** Append a single entry to the p->aMatch[] log.
+*/
+static void snippetAppendMatch(
+ Snippet *p, /* Append the entry to this snippet */
+ int iCol, int iTerm, /* The column and query term */
+ int iToken, /* Matching token in document */
+ int iStart, int nByte /* Offset and size of the match */
+){
+ int i;
+ struct snippetMatch *pMatch;
+ if( p->nMatch+1>=p->nAlloc ){
+ p->nAlloc = p->nAlloc*2 + 10;
+ p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
+ if( p->aMatch==0 ){
+ p->nMatch = 0;
+ p->nAlloc = 0;
+ return;
+ }
+ }
+ i = p->nMatch++;
+ pMatch = &p->aMatch[i];
+ pMatch->iCol = iCol;
+ pMatch->iTerm = iTerm;
+ pMatch->iToken = iToken;
+ pMatch->iStart = iStart;
+ pMatch->nByte = nByte;
+}
+
+/*
+** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
+*/
+#define FTS3_ROTOR_SZ (32)
+#define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1)
+
+/*
+** Add entries to pSnippet->aMatch[] for every match that occurs against
+** document zDoc[0..nDoc-1] which is stored in column iColumn.
+*/
+static void snippetOffsetsOfColumn(
+ Query *pQuery,
+ Snippet *pSnippet,
+ int iColumn,
+ const char *zDoc,
+ int nDoc
+){
+ const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */
+ sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */
+ sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */
+ fulltext_vtab *pVtab; /* The full text index */
+ int nColumn; /* Number of columns in the index */
+ const QueryTerm *aTerm; /* Query string terms */
+ int nTerm; /* Number of query string terms */
+ int i, j; /* Loop counters */
+ int rc; /* Return code */
+ unsigned int match, prevMatch; /* Phrase search bitmasks */
+ const char *zToken; /* Next token from the tokenizer */
+ int nToken; /* Size of zToken */
+ int iBegin, iEnd, iPos; /* Offsets of beginning and end */
+
+ /* The following variables keep a circular buffer of the last
+ ** few tokens */
+ unsigned int iRotor = 0; /* Index of current token */
+ int iRotorBegin[FTS3_ROTOR_SZ]; /* Beginning offset of token */
+ int iRotorLen[FTS3_ROTOR_SZ]; /* Length of token */
+
+ pVtab = pQuery->pFts;
+ nColumn = pVtab->nColumn;
+ pTokenizer = pVtab->pTokenizer;
+ pTModule = pTokenizer->pModule;
+ rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor);
+ if( rc ) return;
+ pTCursor->pTokenizer = pTokenizer;
+ aTerm = pQuery->pTerms;
+ nTerm = pQuery->nTerms;
+ if( nTerm>=FTS3_ROTOR_SZ ){
+ nTerm = FTS3_ROTOR_SZ - 1;
+ }
+ prevMatch = 0;
+ while(1){
+ rc = pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
+ if( rc ) break;
+ iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin;
+ iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin;
+ match = 0;
+ for(i=0; i<nTerm; i++){
+ int iCol;
+ iCol = aTerm[i].iColumn;
+ if( iCol>=0 && iCol<nColumn && iCol!=iColumn ) continue;
+ if( aTerm[i].nTerm>nToken ) continue;
+ if( !aTerm[i].isPrefix && aTerm[i].nTerm<nToken ) continue;
+ assert( aTerm[i].nTerm<=nToken );
+ if( memcmp(aTerm[i].pTerm, zToken, aTerm[i].nTerm) ) continue;
+ if( aTerm[i].iPhrase>1 && (prevMatch & (1<<i))==0 ) continue;
+ match |= 1<<i;
+ if( i==nTerm-1 || aTerm[i+1].iPhrase==1 ){
+ for(j=aTerm[i].iPhrase-1; j>=0; j--){
+ int k = (iRotor-j) & FTS3_ROTOR_MASK;
+ snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j,
+ iRotorBegin[k], iRotorLen[k]);
+ }
+ }
+ }
+ prevMatch = match<<1;
+ iRotor++;
+ }
+ pTModule->xClose(pTCursor);
+}
+
+/*
+** Remove entries from the pSnippet structure to account for the NEAR
+** operator. When this is called, pSnippet contains the list of token
+** offsets produced by treating all NEAR operators as AND operators.
+** This function removes any entries that should not be present after
+** accounting for the NEAR restriction. For example, if the queried
+** document is:
+**
+** "A B C D E A"
+**
+** and the query is:
+**
+** A NEAR/0 E
+**
+** then when this function is called the Snippet contains token offsets
+** 0, 4 and 5. This function removes the "0" entry (because the first A
+** is not near enough to an E).
+*/
+static void trimSnippetOffsetsForNear(Query *pQuery, Snippet *pSnippet){
+ int ii;
+ int iDir = 1;
+
+ while(iDir>-2) {
+ assert( iDir==1 || iDir==-1 );
+ for(ii=0; ii<pSnippet->nMatch; ii++){
+ int jj;
+ int nNear;
+ struct snippetMatch *pMatch = &pSnippet->aMatch[ii];
+ QueryTerm *pQueryTerm = &pQuery->pTerms[pMatch->iTerm];
+
+ if( (pMatch->iTerm+iDir)<0
+ || (pMatch->iTerm+iDir)>=pQuery->nTerms
+ ){
+ continue;
+ }
+
+ nNear = pQueryTerm->nNear;
+ if( iDir<0 ){
+ nNear = pQueryTerm[-1].nNear;
+ }
+
+ if( pMatch->iTerm>=0 && nNear ){
+ int isOk = 0;
+ int iNextTerm = pMatch->iTerm+iDir;
+ int iPrevTerm = iNextTerm;
+
+ int iEndToken;
+ int iStartToken;
+
+ if( iDir<0 ){
+ int nPhrase = 1;
+ iStartToken = pMatch->iToken;
+ while( (pMatch->iTerm+nPhrase)<pQuery->nTerms
+ && pQuery->pTerms[pMatch->iTerm+nPhrase].iPhrase>1
+ ){
+ nPhrase++;
+ }
+ iEndToken = iStartToken + nPhrase - 1;
+ }else{
+ iEndToken = pMatch->iToken;
+ iStartToken = pMatch->iToken+1-pQueryTerm->iPhrase;
+ }
+
+ while( pQuery->pTerms[iNextTerm].iPhrase>1 ){
+ iNextTerm--;
+ }
+ while( (iPrevTerm+1)<pQuery->nTerms &&
+ pQuery->pTerms[iPrevTerm+1].iPhrase>1
+ ){
+ iPrevTerm++;
+ }
+
+ for(jj=0; isOk==0 && jj<pSnippet->nMatch; jj++){
+ struct snippetMatch *p = &pSnippet->aMatch[jj];
+ if( p->iCol==pMatch->iCol && ((
+ p->iTerm==iNextTerm &&
+ p->iToken>iEndToken &&
+ p->iToken<=iEndToken+nNear
+ ) || (
+ p->iTerm==iPrevTerm &&
+ p->iToken<iStartToken &&
+ p->iToken>=iStartToken-nNear
+ ))){
+ isOk = 1;
+ }
+ }
+ if( !isOk ){
+ for(jj=1-pQueryTerm->iPhrase; jj<=0; jj++){
+ pMatch[jj].iTerm = -1;
+ }
+ ii = -1;
+ iDir = 1;
+ }
+ }
+ }
+ iDir -= 2;
+ }
+}
+
+/*
+** Compute all offsets for the current row of the query.
+** If the offsets have already been computed, this routine is a no-op.
+*/
+static void snippetAllOffsets(fulltext_cursor *p){
+ int nColumn;
+ int iColumn, i;
+ int iFirst, iLast;
+ fulltext_vtab *pFts;
+
+ if( p->snippet.nMatch ) return;
+ if( p->q.nTerms==0 ) return;
+ pFts = p->q.pFts;
+ nColumn = pFts->nColumn;
+ iColumn = (p->iCursorType - QUERY_FULLTEXT);
+ if( iColumn<0 || iColumn>=nColumn ){
+ iFirst = 0;
+ iLast = nColumn-1;
+ }else{
+ iFirst = iColumn;
+ iLast = iColumn;
+ }
+ for(i=iFirst; i<=iLast; i++){
+ const char *zDoc;
+ int nDoc;
+ zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1);
+ nDoc = sqlite3_column_bytes(p->pStmt, i+1);
+ snippetOffsetsOfColumn(&p->q, &p->snippet, i, zDoc, nDoc);
+ }
+
+ trimSnippetOffsetsForNear(&p->q, &p->snippet);
+}
+
+/*
+** Convert the information in the aMatch[] array of the snippet
+** into the string zOffset[0..nOffset-1].
+*/
+static void snippetOffsetText(Snippet *p){
+ int i;
+ int cnt = 0;
+ StringBuffer sb;
+ char zBuf[200];
+ if( p->zOffset ) return;
+ initStringBuffer(&sb);
+ for(i=0; i<p->nMatch; i++){
+ struct snippetMatch *pMatch = &p->aMatch[i];
+ if( pMatch->iTerm>=0 ){
+ /* If snippetMatch.iTerm is less than 0, then the match was
+ ** discarded as part of processing the NEAR operator (see the
+ ** trimSnippetOffsetsForNear() function for details). Ignore
+ ** it in this case
+ */
+ zBuf[0] = ' ';
+ sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
+ pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
+ append(&sb, zBuf);
+ cnt++;
+ }
+ }
+ p->zOffset = stringBufferData(&sb);
+ p->nOffset = stringBufferLength(&sb);
+}
+
+/*
+** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set
+** of matching words some of which might be in zDoc. zDoc is column
+** number iCol.
+**
+** iBreak is suggested spot in zDoc where we could begin or end an
+** excerpt. Return a value similar to iBreak but possibly adjusted
+** to be a little left or right so that the break point is better.
+*/
+static int wordBoundary(
+ int iBreak, /* The suggested break point */
+ const char *zDoc, /* Document text */
+ int nDoc, /* Number of bytes in zDoc[] */
+ struct snippetMatch *aMatch, /* Matching words */
+ int nMatch, /* Number of entries in aMatch[] */
+ int iCol /* The column number for zDoc[] */
+){
+ int i;
+ if( iBreak<=10 ){
+ return 0;
+ }
+ if( iBreak>=nDoc-10 ){
+ return nDoc;
+ }
+ for(i=0; i<nMatch && aMatch[i].iCol<iCol; i++){}
+ while( i<nMatch && aMatch[i].iStart+aMatch[i].nByte<iBreak ){ i++; }
+ if( i<nMatch ){
+ if( aMatch[i].iStart<iBreak+10 ){
+ return aMatch[i].iStart;
+ }
+ if( i>0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
+ return aMatch[i-1].iStart;
+ }
+ }
+ for(i=1; i<=10; i++){
+ if( safe_isspace(zDoc[iBreak-i]) ){
+ return iBreak - i + 1;
+ }
+ if( safe_isspace(zDoc[iBreak+i]) ){
+ return iBreak + i + 1;
+ }
+ }
+ return iBreak;
+}
+
+
+
+/*
+** Allowed values for Snippet.aMatch[].snStatus
+*/
+#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */
+#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */
+
+/*
+** Generate the text of a snippet.
+*/
+static void snippetText(
+ fulltext_cursor *pCursor, /* The cursor we need the snippet for */
+ const char *zStartMark, /* Markup to appear before each match */
+ const char *zEndMark, /* Markup to appear after each match */
+ const char *zEllipsis /* Ellipsis mark */
+){
+ int i, j;
+ struct snippetMatch *aMatch;
+ int nMatch;
+ int nDesired;
+ StringBuffer sb;
+ int tailCol;
+ int tailOffset;
+ int iCol;
+ int nDoc;
+ const char *zDoc;
+ int iStart, iEnd;
+ int tailEllipsis = 0;
+ int iMatch;
+
+
+ sqlite3_free(pCursor->snippet.zSnippet);
+ pCursor->snippet.zSnippet = 0;
+ aMatch = pCursor->snippet.aMatch;
+ nMatch = pCursor->snippet.nMatch;
+ initStringBuffer(&sb);
+
+ for(i=0; i<nMatch; i++){
+ aMatch[i].snStatus = SNIPPET_IGNORE;
+ }
+ nDesired = 0;
+ for(i=0; i<pCursor->q.nTerms; i++){
+ for(j=0; j<nMatch; j++){
+ if( aMatch[j].iTerm==i ){
+ aMatch[j].snStatus = SNIPPET_DESIRED;
+ nDesired++;
+ break;
+ }
+ }
+ }
+
+ iMatch = 0;
+ tailCol = -1;
+ tailOffset = 0;
+ for(i=0; i<nMatch && nDesired>0; i++){
+ if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue;
+ nDesired--;
+ iCol = aMatch[i].iCol;
+ zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1);
+ nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1);
+ iStart = aMatch[i].iStart - 40;
+ iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol);
+ if( iStart<=10 ){
+ iStart = 0;
+ }
+ if( iCol==tailCol && iStart<=tailOffset+20 ){
+ iStart = tailOffset;
+ }
+ if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
+ trimWhiteSpace(&sb);
+ appendWhiteSpace(&sb);
+ append(&sb, zEllipsis);
+ appendWhiteSpace(&sb);
+ }
+ iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
+ iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
+ if( iEnd>=nDoc-10 ){
+ iEnd = nDoc;
+ tailEllipsis = 0;
+ }else{
+ tailEllipsis = 1;
+ }
+ while( iMatch<nMatch && aMatch[iMatch].iCol<iCol ){ iMatch++; }
+ while( iStart<iEnd ){
+ while( iMatch<nMatch && aMatch[iMatch].iStart<iStart
+ && aMatch[iMatch].iCol<=iCol ){
+ iMatch++;
+ }
+ if( iMatch<nMatch && aMatch[iMatch].iStart<iEnd
+ && aMatch[iMatch].iCol==iCol ){
+ nappend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart);
+ iStart = aMatch[iMatch].iStart;
+ append(&sb, zStartMark);
+ nappend(&sb, &zDoc[iStart], aMatch[iMatch].nByte);
+ append(&sb, zEndMark);
+ iStart += aMatch[iMatch].nByte;
+ for(j=iMatch+1; j<nMatch; j++){
+ if( aMatch[j].iTerm==aMatch[iMatch].iTerm
+ && aMatch[j].snStatus==SNIPPET_DESIRED ){
+ nDesired--;
+ aMatch[j].snStatus = SNIPPET_IGNORE;
+ }
+ }
+ }else{
+ nappend(&sb, &zDoc[iStart], iEnd - iStart);
+ iStart = iEnd;
+ }
+ }
+ tailCol = iCol;
+ tailOffset = iEnd;
+ }
+ trimWhiteSpace(&sb);
+ if( tailEllipsis ){
+ appendWhiteSpace(&sb);
+ append(&sb, zEllipsis);
+ }
+ pCursor->snippet.zSnippet = stringBufferData(&sb);
+ pCursor->snippet.nSnippet = stringBufferLength(&sb);
+}
+
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fulltextClose(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ FTSTRACE(("FTS3 Close %p\n", c));
+ sqlite3_finalize(c->pStmt);
+ queryClear(&c->q);
+ snippetClear(&c->snippet);
+ if( c->result.nData!=0 ) dlrDestroy(&c->reader);
+ dataBufferDestroy(&c->result);
+ sqlite3_free(c);
+ return SQLITE_OK;
+}
+
+static int fulltextNext(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ int rc;
+
+ FTSTRACE(("FTS3 Next %p\n", pCursor));
+ snippetClear(&c->snippet);
+ if( c->iCursorType < QUERY_FULLTEXT ){
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ switch( rc ){
+ case SQLITE_ROW:
+ c->eof = 0;
+ return SQLITE_OK;
+ case SQLITE_DONE:
+ c->eof = 1;
+ return SQLITE_OK;
+ default:
+ c->eof = 1;
+ return rc;
+ }
+ } else { /* full-text query */
+ rc = sqlite3_reset(c->pStmt);
+ if( rc!=SQLITE_OK ) return rc;
+
+ if( c->result.nData==0 || dlrAtEnd(&c->reader) ){
+ c->eof = 1;
+ return SQLITE_OK;
+ }
+ rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader));
+ dlrStep(&c->reader);
+ if( rc!=SQLITE_OK ) return rc;
+ /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
+ rc = sqlite3_step(c->pStmt);
+ if( rc==SQLITE_ROW ){ /* the case we expect */
+ c->eof = 0;
+ return SQLITE_OK;
+ }
+ /* an error occurred; abort */
+ return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
+ }
+}
+
+
+/* TODO(shess) If we pushed LeafReader to the top of the file, or to
+** another file, term_select() could be pushed above
+** docListOfTerm().
+*/
+static int termSelect(fulltext_vtab *v, int iColumn,
+ const char *pTerm, int nTerm, int isPrefix,
+ DocListType iType, DataBuffer *out);
+
+/* Return a DocList corresponding to the query term *pTerm. If *pTerm
+** is the first term of a phrase query, go ahead and evaluate the phrase
+** query and return the doclist for the entire phrase query.
+**
+** The resulting DL_DOCIDS doclist is stored in pResult, which is
+** overwritten.
+*/
+static int docListOfTerm(
+ fulltext_vtab *v, /* The full text index */
+ int iColumn, /* column to restrict to. No restriction if >=nColumn */
+ QueryTerm *pQTerm, /* Term we are looking for, or 1st term of a phrase */
+ DataBuffer *pResult /* Write the result here */
+){
+ DataBuffer left, right, new;
+ int i, rc;
+
+ /* No phrase search if no position info. */
+ assert( pQTerm->nPhrase==0 || DL_DEFAULT!=DL_DOCIDS );
+
+ /* This code should never be called with buffered updates. */
+ assert( v->nPendingData<0 );
+
+ dataBufferInit(&left, 0);
+ rc = termSelect(v, iColumn, pQTerm->pTerm, pQTerm->nTerm, pQTerm->isPrefix,
+ (0<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS), &left);
+ if( rc ) return rc;
+ for(i=1; i<=pQTerm->nPhrase && left.nData>0; i++){
+ /* If this token is connected to the next by a NEAR operator, and
+ ** the next token is the start of a phrase, then set nPhraseRight
+ ** to the number of tokens in the phrase. Otherwise leave it at 1.
+ */
+ int nPhraseRight = 1;
+ while( (i+nPhraseRight)<=pQTerm->nPhrase
+ && pQTerm[i+nPhraseRight].nNear==0
+ ){
+ nPhraseRight++;
+ }
+
+ dataBufferInit(&right, 0);
+ rc = termSelect(v, iColumn, pQTerm[i].pTerm, pQTerm[i].nTerm,
+ pQTerm[i].isPrefix, DL_POSITIONS, &right);
+ if( rc ){
+ dataBufferDestroy(&left);
+ return rc;
+ }
+ dataBufferInit(&new, 0);
+ docListPhraseMerge(left.pData, left.nData, right.pData, right.nData,
+ pQTerm[i-1].nNear, pQTerm[i-1].iPhrase + nPhraseRight,
+ ((i<pQTerm->nPhrase) ? DL_POSITIONS : DL_DOCIDS),
+ &new);
+ dataBufferDestroy(&left);
+ dataBufferDestroy(&right);
+ left = new;
+ }
+ *pResult = left;
+ return SQLITE_OK;
+}
+
+/* Add a new term pTerm[0..nTerm-1] to the query *q.
+*/
+static void queryAdd(Query *q, const char *pTerm, int nTerm){
+ QueryTerm *t;
+ ++q->nTerms;
+ q->pTerms = sqlite3_realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0]));
+ if( q->pTerms==0 ){
+ q->nTerms = 0;
+ return;
+ }
+ t = &q->pTerms[q->nTerms - 1];
+ CLEAR(t);
+ t->pTerm = sqlite3_malloc(nTerm+1);
+ memcpy(t->pTerm, pTerm, nTerm);
+ t->pTerm[nTerm] = 0;
+ t->nTerm = nTerm;
+ t->isOr = q->nextIsOr;
+ t->isPrefix = 0;
+ q->nextIsOr = 0;
+ t->iColumn = q->nextColumn;
+ q->nextColumn = q->dfltColumn;
+}
+
+/*
+** Check to see if the string zToken[0...nToken-1] matches any
+** column name in the virtual table. If it does,
+** return the zero-indexed column number. If not, return -1.
+*/
+static int checkColumnSpecifier(
+ fulltext_vtab *pVtab, /* The virtual table */
+ const char *zToken, /* Text of the token */
+ int nToken /* Number of characters in the token */
+){
+ int i;
+ for(i=0; i<pVtab->nColumn; i++){
+ if( memcmp(pVtab->azColumn[i], zToken, nToken)==0
+ && pVtab->azColumn[i][nToken]==0 ){
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+** Parse the text at pSegment[0..nSegment-1]. Add additional terms
+** to the query being assemblied in pQuery.
+**
+** inPhrase is true if pSegment[0..nSegement-1] is contained within
+** double-quotes. If inPhrase is true, then the first term
+** is marked with the number of terms in the phrase less one and
+** OR and "-" syntax is ignored. If inPhrase is false, then every
+** term found is marked with nPhrase=0 and OR and "-" syntax is significant.
+*/
+static int tokenizeSegment(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer to use */
+ const char *pSegment, int nSegment, /* Query expression being parsed */
+ int inPhrase, /* True if within "..." */
+ Query *pQuery /* Append results here */
+){
+ const sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
+ sqlite3_tokenizer_cursor *pCursor;
+ int firstIndex = pQuery->nTerms;
+ int iCol;
+ int nTerm = 1;
+
+ int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+ pCursor->pTokenizer = pTokenizer;
+
+ while( 1 ){
+ const char *pToken;
+ int nToken, iBegin, iEnd, iPos;
+
+ rc = pModule->xNext(pCursor,
+ &pToken, &nToken,
+ &iBegin, &iEnd, &iPos);
+ if( rc!=SQLITE_OK ) break;
+ if( !inPhrase &&
+ pSegment[iEnd]==':' &&
+ (iCol = checkColumnSpecifier(pQuery->pFts, pToken, nToken))>=0 ){
+ pQuery->nextColumn = iCol;
+ continue;
+ }
+ if( !inPhrase && pQuery->nTerms>0 && nToken==2
+ && pSegment[iBegin+0]=='O'
+ && pSegment[iBegin+1]=='R'
+ ){
+ pQuery->nextIsOr = 1;
+ continue;
+ }
+ if( !inPhrase && pQuery->nTerms>0 && !pQuery->nextIsOr && nToken==4
+ && pSegment[iBegin+0]=='N'
+ && pSegment[iBegin+1]=='E'
+ && pSegment[iBegin+2]=='A'
+ && pSegment[iBegin+3]=='R'
+ ){
+ QueryTerm *pTerm = &pQuery->pTerms[pQuery->nTerms-1];
+ if( (iBegin+6)<nSegment
+ && pSegment[iBegin+4] == '/'
+ && pSegment[iBegin+5]>='0' && pSegment[iBegin+5]<='9'
+ ){
+ pTerm->nNear = (pSegment[iBegin+5] - '0');
+ nToken += 2;
+ if( pSegment[iBegin+6]>='0' && pSegment[iBegin+6]<=9 ){
+ pTerm->nNear = pTerm->nNear * 10 + (pSegment[iBegin+6] - '0');
+ iEnd++;
+ }
+ pModule->xNext(pCursor, &pToken, &nToken, &iBegin, &iEnd, &iPos);
+ } else {
+ pTerm->nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM;
+ }
+ pTerm->nNear++;
+ continue;
+ }
+
+ queryAdd(pQuery, pToken, nToken);
+ if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
+ pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
+ }
+ if( iEnd<nSegment && pSegment[iEnd]=='*' ){
+ pQuery->pTerms[pQuery->nTerms-1].isPrefix = 1;
+ }
+ pQuery->pTerms[pQuery->nTerms-1].iPhrase = nTerm;
+ if( inPhrase ){
+ nTerm++;
+ }
+ }
+
+ if( inPhrase && pQuery->nTerms>firstIndex ){
+ pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
+ }
+
+ return pModule->xClose(pCursor);
+}
+
+/* Parse a query string, yielding a Query object pQuery.
+**
+** The calling function will need to queryClear() to clean up
+** the dynamically allocated memory held by pQuery.
+*/
+static int parseQuery(
+ fulltext_vtab *v, /* The fulltext index */
+ const char *zInput, /* Input text of the query string */
+ int nInput, /* Size of the input text */
+ int dfltColumn, /* Default column of the index to match against */
+ Query *pQuery /* Write the parse results here. */
+){
+ int iInput, inPhrase = 0;
+ int ii;
+ QueryTerm *aTerm;
+
+ if( zInput==0 ) nInput = 0;
+ if( nInput<0 ) nInput = strlen(zInput);
+ pQuery->nTerms = 0;
+ pQuery->pTerms = NULL;
+ pQuery->nextIsOr = 0;
+ pQuery->nextColumn = dfltColumn;
+ pQuery->dfltColumn = dfltColumn;
+ pQuery->pFts = v;
+
+ for(iInput=0; iInput<nInput; ++iInput){
+ int i;
+ for(i=iInput; i<nInput && zInput[i]!='"'; ++i){}
+ if( i>iInput ){
+ tokenizeSegment(v->pTokenizer, zInput+iInput, i-iInput, inPhrase,
+ pQuery);
+ }
+ iInput = i;
+ if( i<nInput ){
+ assert( zInput[i]=='"' );
+ inPhrase = !inPhrase;
+ }
+ }
+
+ if( inPhrase ){
+ /* unmatched quote */
+ queryClear(pQuery);
+ return SQLITE_ERROR;
+ }
+
+ /* Modify the values of the QueryTerm.nPhrase variables to account for
+ ** the NEAR operator. For the purposes of QueryTerm.nPhrase, phrases
+ ** and tokens connected by the NEAR operator are handled as a single
+ ** phrase. See comments above the QueryTerm structure for details.
+ */
+ aTerm = pQuery->pTerms;
+ for(ii=0; ii<pQuery->nTerms; ii++){
+ if( aTerm[ii].nNear || aTerm[ii].nPhrase ){
+ while (aTerm[ii+aTerm[ii].nPhrase].nNear) {
+ aTerm[ii].nPhrase += (1 + aTerm[ii+aTerm[ii].nPhrase+1].nPhrase);
+ }
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/* TODO(shess) Refactor the code to remove this forward decl. */
+static int flushPendingTerms(fulltext_vtab *v);
+
+/* Perform a full-text query using the search expression in
+** zInput[0..nInput-1]. Return a list of matching documents
+** in pResult.
+**
+** Queries must match column iColumn. Or if iColumn>=nColumn
+** they are allowed to match against any column.
+*/
+static int fulltextQuery(
+ fulltext_vtab *v, /* The full text index */
+ int iColumn, /* Match against this column by default */
+ const char *zInput, /* The query string */
+ int nInput, /* Number of bytes in zInput[] */
+ DataBuffer *pResult, /* Write the result doclist here */
+ Query *pQuery /* Put parsed query string here */
+){
+ int i, iNext, rc;
+ DataBuffer left, right, or, new;
+ int nNot = 0;
+ QueryTerm *aTerm;
+
+ /* TODO(shess) Instead of flushing pendingTerms, we could query for
+ ** the relevant term and merge the doclist into what we receive from
+ ** the database. Wait and see if this is a common issue, first.
+ **
+ ** A good reason not to flush is to not generate update-related
+ ** error codes from here.
+ */
+
+ /* Flush any buffered updates before executing the query. */
+ rc = flushPendingTerms(v);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* TODO(shess) I think that the queryClear() calls below are not
+ ** necessary, because fulltextClose() already clears the query.
+ */
+ rc = parseQuery(v, zInput, nInput, iColumn, pQuery);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Empty or NULL queries return no results. */
+ if( pQuery->nTerms==0 ){
+ dataBufferInit(pResult, 0);
+ return SQLITE_OK;
+ }
+
+ /* Merge AND terms. */
+ /* TODO(shess) I think we can early-exit if( i>nNot && left.nData==0 ). */
+ aTerm = pQuery->pTerms;
+ for(i = 0; i<pQuery->nTerms; i=iNext){
+ if( aTerm[i].isNot ){
+ /* Handle all NOT terms in a separate pass */
+ nNot++;
+ iNext = i + aTerm[i].nPhrase+1;
+ continue;
+ }
+ iNext = i + aTerm[i].nPhrase + 1;
+ rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
+ if( rc ){
+ if( i!=nNot ) dataBufferDestroy(&left);
+ queryClear(pQuery);
+ return rc;
+ }
+ while( iNext<pQuery->nTerms && aTerm[iNext].isOr ){
+ rc = docListOfTerm(v, aTerm[iNext].iColumn, &aTerm[iNext], &or);
+ iNext += aTerm[iNext].nPhrase + 1;
+ if( rc ){
+ if( i!=nNot ) dataBufferDestroy(&left);
+ dataBufferDestroy(&right);
+ queryClear(pQuery);
+ return rc;
+ }
+ dataBufferInit(&new, 0);
+ docListOrMerge(right.pData, right.nData, or.pData, or.nData, &new);
+ dataBufferDestroy(&right);
+ dataBufferDestroy(&or);
+ right = new;
+ }
+ if( i==nNot ){ /* first term processed. */
+ left = right;
+ }else{
+ dataBufferInit(&new, 0);
+ docListAndMerge(left.pData, left.nData, right.pData, right.nData, &new);
+ dataBufferDestroy(&right);
+ dataBufferDestroy(&left);
+ left = new;
+ }
+ }
+
+ if( nNot==pQuery->nTerms ){
+ /* We do not yet know how to handle a query of only NOT terms */
+ return SQLITE_ERROR;
+ }
+
+ /* Do the EXCEPT terms */
+ for(i=0; i<pQuery->nTerms; i += aTerm[i].nPhrase + 1){
+ if( !aTerm[i].isNot ) continue;
+ rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
+ if( rc ){
+ queryClear(pQuery);
+ dataBufferDestroy(&left);
+ return rc;
+ }
+ dataBufferInit(&new, 0);
+ docListExceptMerge(left.pData, left.nData, right.pData, right.nData, &new);
+ dataBufferDestroy(&right);
+ dataBufferDestroy(&left);
+ left = new;
+ }
+
+ *pResult = left;
+ return rc;
+}
+
+/*
+** This is the xFilter interface for the virtual table. See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** If idxNum==QUERY_GENERIC then do a full table scan against
+** the %_content table.
+**
+** If idxNum==QUERY_DOCID then do a docid lookup for a single entry
+** in the %_content table.
+**
+** If idxNum>=QUERY_FULLTEXT then use the full text index. The
+** column on the left-hand side of the MATCH operator is column
+** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand
+** side of the MATCH operator.
+*/
+/* TODO(shess) Upgrade the cursor initialization and destruction to
+** account for fulltextFilter() being called multiple times on the
+** same cursor. The current solution is very fragile. Apply fix to
+** fts3 as appropriate.
+*/
+static int fulltextFilter(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, const char *idxStr, /* Which indexing scheme to use */
+ int argc, sqlite3_value **argv /* Arguments for the indexing scheme */
+){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ fulltext_vtab *v = cursor_vtab(c);
+ int rc;
+
+ FTSTRACE(("FTS3 Filter %p\n",pCursor));
+
+ /* If the cursor has a statement that was not prepared according to
+ ** idxNum, clear it. I believe all calls to fulltextFilter with a
+ ** given cursor will have the same idxNum , but in this case it's
+ ** easy to be safe.
+ */
+ if( c->pStmt && c->iCursorType!=idxNum ){
+ sqlite3_finalize(c->pStmt);
+ c->pStmt = NULL;
+ }
+
+ /* Get a fresh statement appropriate to idxNum. */
+ /* TODO(shess): Add a prepared-statement cache in the vt structure.
+ ** The cache must handle multiple open cursors. Easier to cache the
+ ** statement variants at the vt to reduce malloc/realloc/free here.
+ ** Or we could have a StringBuffer variant which allowed stack
+ ** construction for small values.
+ */
+ if( !c->pStmt ){
+ StringBuffer sb;
+ initStringBuffer(&sb);
+ append(&sb, "SELECT docid, ");
+ appendList(&sb, v->nColumn, v->azContentColumn);
+ append(&sb, " FROM %_content");
+ if( idxNum!=QUERY_GENERIC ) append(&sb, " WHERE docid = ?");
+ rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt,
+ stringBufferData(&sb));
+ stringBufferDestroy(&sb);
+ if( rc!=SQLITE_OK ) return rc;
+ c->iCursorType = idxNum;
+ }else{
+ sqlite3_reset(c->pStmt);
+ assert( c->iCursorType==idxNum );
+ }
+
+ switch( idxNum ){
+ case QUERY_GENERIC:
+ break;
+
+ case QUERY_DOCID:
+ rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
+ if( rc!=SQLITE_OK ) return rc;
+ break;
+
+ default: /* full-text search */
+ {
+ const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
+ assert( idxNum<=QUERY_FULLTEXT+v->nColumn);
+ assert( argc==1 );
+ queryClear(&c->q);
+ if( c->result.nData!=0 ){
+ /* This case happens if the same cursor is used repeatedly. */
+ dlrDestroy(&c->reader);
+ dataBufferReset(&c->result);
+ }else{
+ dataBufferInit(&c->result, 0);
+ }
+ rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &c->result, &c->q);
+ if( rc!=SQLITE_OK ) return rc;
+ if( c->result.nData!=0 ){
+ dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData);
+ }
+ break;
+ }
+ }
+
+ return fulltextNext(pCursor);
+}
+
+/* This is the xEof method of the virtual table. The SQLite core
+** calls this routine to find out if it has reached the end of
+** a query's results set.
+*/
+static int fulltextEof(sqlite3_vtab_cursor *pCursor){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ return c->eof;
+}
+
+/* This is the xColumn method of the virtual table. The SQLite
+** core calls this method during a query when it needs the value
+** of a column from the virtual table. This method needs to use
+** one of the sqlite3_result_*() routines to store the requested
+** value back in the pContext.
+*/
+static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *pContext, int idxCol){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+ fulltext_vtab *v = cursor_vtab(c);
+
+ if( idxCol<v->nColumn ){
+ sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
+ sqlite3_result_value(pContext, pVal);
+ }else if( idxCol==v->nColumn ){
+ /* The extra column whose name is the same as the table.
+ ** Return a blob which is a pointer to the cursor
+ */
+ sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
+ }else if( idxCol==v->nColumn+1 ){
+ /* The docid column, which is an alias for rowid. */
+ sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0);
+ sqlite3_result_value(pContext, pVal);
+ }
+ return SQLITE_OK;
+}
+
+/* This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. fts3
+** exposes %_content.docid as the rowid for the virtual table. The
+** rowid should be written to *pRowid.
+*/
+static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ fulltext_cursor *c = (fulltext_cursor *) pCursor;
+
+ *pRowid = sqlite3_column_int64(c->pStmt, 0);
+ return SQLITE_OK;
+}
+
+/* Add all terms in [zText] to pendingTerms table. If [iColumn] > 0,
+** we also store positions and offsets in the hash table using that
+** column number.
+*/
+static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid,
+ const char *zText, int iColumn){
+ sqlite3_tokenizer *pTokenizer = v->pTokenizer;
+ sqlite3_tokenizer_cursor *pCursor;
+ const char *pToken;
+ int nTokenBytes;
+ int iStartOffset, iEndOffset, iPosition;
+ int rc;
+
+ rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pCursor->pTokenizer = pTokenizer;
+ while( SQLITE_OK==(rc=pTokenizer->pModule->xNext(pCursor,
+ &pToken, &nTokenBytes,
+ &iStartOffset, &iEndOffset,
+ &iPosition)) ){
+ DLCollector *p;
+ int nData; /* Size of doclist before our update. */
+
+ /* Positions can't be negative; we use -1 as a terminator
+ * internally. Token can't be NULL or empty. */
+ if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ p = fts3HashFind(&v->pendingTerms, pToken, nTokenBytes);
+ if( p==NULL ){
+ nData = 0;
+ p = dlcNew(iDocid, DL_DEFAULT);
+ fts3HashInsert(&v->pendingTerms, pToken, nTokenBytes, p);
+
+ /* Overhead for our hash table entry, the key, and the value. */
+ v->nPendingData += sizeof(struct fts3HashElem)+sizeof(*p)+nTokenBytes;
+ }else{
+ nData = p->b.nData;
+ if( p->dlw.iPrevDocid!=iDocid ) dlcNext(p, iDocid);
+ }
+ if( iColumn>=0 ){
+ dlcAddPos(p, iColumn, iPosition, iStartOffset, iEndOffset);
+ }
+
+ /* Accumulate data added by dlcNew or dlcNext, and dlcAddPos. */
+ v->nPendingData += p->b.nData-nData;
+ }
+
+ /* TODO(shess) Check return? Should this be able to cause errors at
+ ** this point? Actually, same question about sqlite3_finalize(),
+ ** though one could argue that failure there means that the data is
+ ** not durable. *ponder*
+ */
+ pTokenizer->pModule->xClose(pCursor);
+ if( SQLITE_DONE == rc ) return SQLITE_OK;
+ return rc;
+}
+
+/* Add doclists for all terms in [pValues] to pendingTerms table. */
+static int insertTerms(fulltext_vtab *v, sqlite_int64 iDocid,
+ sqlite3_value **pValues){
+ int i;
+ for(i = 0; i < v->nColumn ; ++i){
+ char *zText = (char*)sqlite3_value_text(pValues[i]);
+ int rc = buildTerms(v, iDocid, zText, i);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ return SQLITE_OK;
+}
+
+/* Add empty doclists for all terms in the given row's content to
+** pendingTerms.
+*/
+static int deleteTerms(fulltext_vtab *v, sqlite_int64 iDocid){
+ const char **pValues;
+ int i, rc;
+
+ /* TODO(shess) Should we allow such tables at all? */
+ if( DL_DEFAULT==DL_DOCIDS ) return SQLITE_ERROR;
+
+ rc = content_select(v, iDocid, &pValues);
+ if( rc!=SQLITE_OK ) return rc;
+
+ for(i = 0 ; i < v->nColumn; ++i) {
+ rc = buildTerms(v, iDocid, pValues[i], -1);
+ if( rc!=SQLITE_OK ) break;
+ }
+
+ freeStringArray(v->nColumn, pValues);
+ return SQLITE_OK;
+}
+
+/* TODO(shess) Refactor the code to remove this forward decl. */
+static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid);
+
+/* Insert a row into the %_content table; set *piDocid to be the ID of the
+** new row. Add doclists for terms to pendingTerms.
+*/
+static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestDocid,
+ sqlite3_value **pValues, sqlite_int64 *piDocid){
+ int rc;
+
+ rc = content_insert(v, pRequestDocid, pValues); /* execute an SQL INSERT */
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* docid column is an alias for rowid. */
+ *piDocid = sqlite3_last_insert_rowid(v->db);
+ rc = initPendingTerms(v, *piDocid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return insertTerms(v, *piDocid, pValues);
+}
+
+/* Delete a row from the %_content table; add empty doclists for terms
+** to pendingTerms.
+*/
+static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){
+ int rc = initPendingTerms(v, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = deleteTerms(v, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ return content_delete(v, iRow); /* execute an SQL DELETE */
+}
+
+/* Update a row in the %_content table; add delete doclists to
+** pendingTerms for old terms not in the new data, add insert doclists
+** to pendingTerms for terms in the new data.
+*/
+static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
+ sqlite3_value **pValues){
+ int rc = initPendingTerms(v, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Generate an empty doclist for each term that previously appeared in this
+ * row. */
+ rc = deleteTerms(v, iRow);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Now add positions for terms which appear in the updated row. */
+ return insertTerms(v, iRow, pValues);
+}
+
+/*******************************************************************/
+/* InteriorWriter is used to collect terms and block references into
+** interior nodes in %_segments. See commentary at top of file for
+** format.
+*/
+
+/* How large interior nodes can grow. */
+#define INTERIOR_MAX 2048
+
+/* Minimum number of terms per interior node (except the root). This
+** prevents large terms from making the tree too skinny - must be >0
+** so that the tree always makes progress. Note that the min tree
+** fanout will be INTERIOR_MIN_TERMS+1.
+*/
+#define INTERIOR_MIN_TERMS 7
+#if INTERIOR_MIN_TERMS<1
+# error INTERIOR_MIN_TERMS must be greater than 0.
+#endif
+
+/* ROOT_MAX controls how much data is stored inline in the segment
+** directory.
+*/
+/* TODO(shess) Push ROOT_MAX down to whoever is writing things. It's
+** only here so that interiorWriterRootInfo() and leafWriterRootInfo()
+** can both see it, but if the caller passed it in, we wouldn't even
+** need a define.
+*/
+#define ROOT_MAX 1024
+#if ROOT_MAX<VARINT_MAX*2
+# error ROOT_MAX must have enough space for a header.
+#endif
+
+/* InteriorBlock stores a linked-list of interior blocks while a lower
+** layer is being constructed.
+*/
+typedef struct InteriorBlock {
+ DataBuffer term; /* Leftmost term in block's subtree. */
+ DataBuffer data; /* Accumulated data for the block. */
+ struct InteriorBlock *next;
+} InteriorBlock;
+
+static InteriorBlock *interiorBlockNew(int iHeight, sqlite_int64 iChildBlock,
+ const char *pTerm, int nTerm){
+ InteriorBlock *block = sqlite3_malloc(sizeof(InteriorBlock));
+ char c[VARINT_MAX+VARINT_MAX];
+ int n;
+
+ if( block ){
+ memset(block, 0, sizeof(*block));
+ dataBufferInit(&block->term, 0);
+ dataBufferReplace(&block->term, pTerm, nTerm);
+
+ n = fts3PutVarint(c, iHeight);
+ n += fts3PutVarint(c+n, iChildBlock);
+ dataBufferInit(&block->data, INTERIOR_MAX);
+ dataBufferReplace(&block->data, c, n);
+ }
+ return block;
+}
+
+#ifndef NDEBUG
+/* Verify that the data is readable as an interior node. */
+static void interiorBlockValidate(InteriorBlock *pBlock){
+ const char *pData = pBlock->data.pData;
+ int nData = pBlock->data.nData;
+ int n, iDummy;
+ sqlite_int64 iBlockid;
+
+ assert( nData>0 );
+ assert( pData!=0 );
+ assert( pData+nData>pData );
+
+ /* Must lead with height of node as a varint(n), n>0 */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n<nData );
+ pData += n;
+ nData -= n;
+
+ /* Must contain iBlockid. */
+ n = fts3GetVarint(pData, &iBlockid);
+ assert( n>0 );
+ assert( n<=nData );
+ pData += n;
+ nData -= n;
+
+ /* Zero or more terms of positive length */
+ if( nData!=0 ){
+ /* First term is not delta-encoded. */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0);
+ assert( n+iDummy<=nData );
+ pData += n+iDummy;
+ nData -= n+iDummy;
+
+ /* Following terms delta-encoded. */
+ while( nData!=0 ){
+ /* Length of shared prefix. */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>=0 );
+ assert( n<nData );
+ pData += n;
+ nData -= n;
+
+ /* Length and data of distinct suffix. */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0);
+ assert( n+iDummy<=nData );
+ pData += n+iDummy;
+ nData -= n+iDummy;
+ }
+ }
+}
+#define ASSERT_VALID_INTERIOR_BLOCK(x) interiorBlockValidate(x)
+#else
+#define ASSERT_VALID_INTERIOR_BLOCK(x) assert( 1 )
+#endif
+
+typedef struct InteriorWriter {
+ int iHeight; /* from 0 at leaves. */
+ InteriorBlock *first, *last;
+ struct InteriorWriter *parentWriter;
+
+ DataBuffer term; /* Last term written to block "last". */
+ sqlite_int64 iOpeningChildBlock; /* First child block in block "last". */
+#ifndef NDEBUG
+ sqlite_int64 iLastChildBlock; /* for consistency checks. */
+#endif
+} InteriorWriter;
+
+/* Initialize an interior node where pTerm[nTerm] marks the leftmost
+** term in the tree. iChildBlock is the leftmost child block at the
+** next level down the tree.
+*/
+static void interiorWriterInit(int iHeight, const char *pTerm, int nTerm,
+ sqlite_int64 iChildBlock,
+ InteriorWriter *pWriter){
+ InteriorBlock *block;
+ assert( iHeight>0 );
+ CLEAR(pWriter);
+
+ pWriter->iHeight = iHeight;
+ pWriter->iOpeningChildBlock = iChildBlock;
+#ifndef NDEBUG
+ pWriter->iLastChildBlock = iChildBlock;
+#endif
+ block = interiorBlockNew(iHeight, iChildBlock, pTerm, nTerm);
+ pWriter->last = pWriter->first = block;
+ ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+ dataBufferInit(&pWriter->term, 0);
+}
+
+/* Append the child node rooted at iChildBlock to the interior node,
+** with pTerm[nTerm] as the leftmost term in iChildBlock's subtree.
+*/
+static void interiorWriterAppend(InteriorWriter *pWriter,
+ const char *pTerm, int nTerm,
+ sqlite_int64 iChildBlock){
+ char c[VARINT_MAX+VARINT_MAX];
+ int n, nPrefix = 0;
+
+ ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+
+ /* The first term written into an interior node is actually
+ ** associated with the second child added (the first child was added
+ ** in interiorWriterInit, or in the if clause at the bottom of this
+ ** function). That term gets encoded straight up, with nPrefix left
+ ** at 0.
+ */
+ if( pWriter->term.nData==0 ){
+ n = fts3PutVarint(c, nTerm);
+ }else{
+ while( nPrefix<pWriter->term.nData &&
+ pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
+ nPrefix++;
+ }
+
+ n = fts3PutVarint(c, nPrefix);
+ n += fts3PutVarint(c+n, nTerm-nPrefix);
+ }
+
+#ifndef NDEBUG
+ pWriter->iLastChildBlock++;
+#endif
+ assert( pWriter->iLastChildBlock==iChildBlock );
+
+ /* Overflow to a new block if the new term makes the current block
+ ** too big, and the current block already has enough terms.
+ */
+ if( pWriter->last->data.nData+n+nTerm-nPrefix>INTERIOR_MAX &&
+ iChildBlock-pWriter->iOpeningChildBlock>INTERIOR_MIN_TERMS ){
+ pWriter->last->next = interiorBlockNew(pWriter->iHeight, iChildBlock,
+ pTerm, nTerm);
+ pWriter->last = pWriter->last->next;
+ pWriter->iOpeningChildBlock = iChildBlock;
+ dataBufferReset(&pWriter->term);
+ }else{
+ dataBufferAppend2(&pWriter->last->data, c, n,
+ pTerm+nPrefix, nTerm-nPrefix);
+ dataBufferReplace(&pWriter->term, pTerm, nTerm);
+ }
+ ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
+}
+
+/* Free the space used by pWriter, including the linked-list of
+** InteriorBlocks, and parentWriter, if present.
+*/
+static int interiorWriterDestroy(InteriorWriter *pWriter){
+ InteriorBlock *block = pWriter->first;
+
+ while( block!=NULL ){
+ InteriorBlock *b = block;
+ block = block->next;
+ dataBufferDestroy(&b->term);
+ dataBufferDestroy(&b->data);
+ sqlite3_free(b);
+ }
+ if( pWriter->parentWriter!=NULL ){
+ interiorWriterDestroy(pWriter->parentWriter);
+ sqlite3_free(pWriter->parentWriter);
+ }
+ dataBufferDestroy(&pWriter->term);
+ SCRAMBLE(pWriter);
+ return SQLITE_OK;
+}
+
+/* If pWriter can fit entirely in ROOT_MAX, return it as the root info
+** directly, leaving *piEndBlockid unchanged. Otherwise, flush
+** pWriter to %_segments, building a new layer of interior nodes, and
+** recursively ask for their root into.
+*/
+static int interiorWriterRootInfo(fulltext_vtab *v, InteriorWriter *pWriter,
+ char **ppRootInfo, int *pnRootInfo,
+ sqlite_int64 *piEndBlockid){
+ InteriorBlock *block = pWriter->first;
+ sqlite_int64 iBlockid = 0;
+ int rc;
+
+ /* If we can fit the segment inline */
+ if( block==pWriter->last && block->data.nData<ROOT_MAX ){
+ *ppRootInfo = block->data.pData;
+ *pnRootInfo = block->data.nData;
+ return SQLITE_OK;
+ }
+
+ /* Flush the first block to %_segments, and create a new level of
+ ** interior node.
+ */
+ ASSERT_VALID_INTERIOR_BLOCK(block);
+ rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+ *piEndBlockid = iBlockid;
+
+ pWriter->parentWriter = sqlite3_malloc(sizeof(*pWriter->parentWriter));
+ interiorWriterInit(pWriter->iHeight+1,
+ block->term.pData, block->term.nData,
+ iBlockid, pWriter->parentWriter);
+
+ /* Flush additional blocks and append to the higher interior
+ ** node.
+ */
+ for(block=block->next; block!=NULL; block=block->next){
+ ASSERT_VALID_INTERIOR_BLOCK(block);
+ rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+ *piEndBlockid = iBlockid;
+
+ interiorWriterAppend(pWriter->parentWriter,
+ block->term.pData, block->term.nData, iBlockid);
+ }
+
+ /* Parent node gets the chance to be the root. */
+ return interiorWriterRootInfo(v, pWriter->parentWriter,
+ ppRootInfo, pnRootInfo, piEndBlockid);
+}
+
+/****************************************************************/
+/* InteriorReader is used to read off the data from an interior node
+** (see comment at top of file for the format).
+*/
+typedef struct InteriorReader {
+ const char *pData;
+ int nData;
+
+ DataBuffer term; /* previous term, for decoding term delta. */
+
+ sqlite_int64 iBlockid;
+} InteriorReader;
+
+static void interiorReaderDestroy(InteriorReader *pReader){
+ dataBufferDestroy(&pReader->term);
+ SCRAMBLE(pReader);
+}
+
+/* TODO(shess) The assertions are great, but what if we're in NDEBUG
+** and the blob is empty or otherwise contains suspect data?
+*/
+static void interiorReaderInit(const char *pData, int nData,
+ InteriorReader *pReader){
+ int n, nTerm;
+
+ /* Require at least the leading flag byte */
+ assert( nData>0 );
+ assert( pData[0]!='\0' );
+
+ CLEAR(pReader);
+
+ /* Decode the base blockid, and set the cursor to the first term. */
+ n = fts3GetVarint(pData+1, &pReader->iBlockid);
+ assert( 1+n<=nData );
+ pReader->pData = pData+1+n;
+ pReader->nData = nData-(1+n);
+
+ /* A single-child interior node (such as when a leaf node was too
+ ** large for the segment directory) won't have any terms.
+ ** Otherwise, decode the first term.
+ */
+ if( pReader->nData==0 ){
+ dataBufferInit(&pReader->term, 0);
+ }else{
+ n = fts3GetVarint32(pReader->pData, &nTerm);
+ dataBufferInit(&pReader->term, nTerm);
+ dataBufferReplace(&pReader->term, pReader->pData+n, nTerm);
+ assert( n+nTerm<=pReader->nData );
+ pReader->pData += n+nTerm;
+ pReader->nData -= n+nTerm;
+ }
+}
+
+static int interiorReaderAtEnd(InteriorReader *pReader){
+ return pReader->term.nData==0;
+}
+
+static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){
+ return pReader->iBlockid;
+}
+
+static int interiorReaderTermBytes(InteriorReader *pReader){
+ assert( !interiorReaderAtEnd(pReader) );
+ return pReader->term.nData;
+}
+static const char *interiorReaderTerm(InteriorReader *pReader){
+ assert( !interiorReaderAtEnd(pReader) );
+ return pReader->term.pData;
+}
+
+/* Step forward to the next term in the node. */
+static void interiorReaderStep(InteriorReader *pReader){
+ assert( !interiorReaderAtEnd(pReader) );
+
+ /* If the last term has been read, signal eof, else construct the
+ ** next term.
+ */
+ if( pReader->nData==0 ){
+ dataBufferReset(&pReader->term);
+ }else{
+ int n, nPrefix, nSuffix;
+
+ n = fts3GetVarint32(pReader->pData, &nPrefix);
+ n += fts3GetVarint32(pReader->pData+n, &nSuffix);
+
+ /* Truncate the current term and append suffix data. */
+ pReader->term.nData = nPrefix;
+ dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
+
+ assert( n+nSuffix<=pReader->nData );
+ pReader->pData += n+nSuffix;
+ pReader->nData -= n+nSuffix;
+ }
+ pReader->iBlockid++;
+}
+
+/* Compare the current term to pTerm[nTerm], returning strcmp-style
+** results. If isPrefix, equality means equal through nTerm bytes.
+*/
+static int interiorReaderTermCmp(InteriorReader *pReader,
+ const char *pTerm, int nTerm, int isPrefix){
+ const char *pReaderTerm = interiorReaderTerm(pReader);
+ int nReaderTerm = interiorReaderTermBytes(pReader);
+ int c, n = nReaderTerm<nTerm ? nReaderTerm : nTerm;
+
+ if( n==0 ){
+ if( nReaderTerm>0 ) return -1;
+ if( nTerm>0 ) return 1;
+ return 0;
+ }
+
+ c = memcmp(pReaderTerm, pTerm, n);
+ if( c!=0 ) return c;
+ if( isPrefix && n==nTerm ) return 0;
+ return nReaderTerm - nTerm;
+}
+
+/****************************************************************/
+/* LeafWriter is used to collect terms and associated doclist data
+** into leaf blocks in %_segments (see top of file for format info).
+** Expected usage is:
+**
+** LeafWriter writer;
+** leafWriterInit(0, 0, &writer);
+** while( sorted_terms_left_to_process ){
+** // data is doclist data for that term.
+** rc = leafWriterStep(v, &writer, pTerm, nTerm, pData, nData);
+** if( rc!=SQLITE_OK ) goto err;
+** }
+** rc = leafWriterFinalize(v, &writer);
+**err:
+** leafWriterDestroy(&writer);
+** return rc;
+**
+** leafWriterStep() may write a collected leaf out to %_segments.
+** leafWriterFinalize() finishes writing any buffered data and stores
+** a root node in %_segdir. leafWriterDestroy() frees all buffers and
+** InteriorWriters allocated as part of writing this segment.
+**
+** TODO(shess) Document leafWriterStepMerge().
+*/
+
+/* Put terms with data this big in their own block. */
+#define STANDALONE_MIN 1024
+
+/* Keep leaf blocks below this size. */
+#define LEAF_MAX 2048
+
+typedef struct LeafWriter {
+ int iLevel;
+ int idx;
+ sqlite_int64 iStartBlockid; /* needed to create the root info */
+ sqlite_int64 iEndBlockid; /* when we're done writing. */
+
+ DataBuffer term; /* previous encoded term */
+ DataBuffer data; /* encoding buffer */
+
+ /* bytes of first term in the current node which distinguishes that
+ ** term from the last term of the previous node.
+ */
+ int nTermDistinct;
+
+ InteriorWriter parentWriter; /* if we overflow */
+ int has_parent;
+} LeafWriter;
+
+static void leafWriterInit(int iLevel, int idx, LeafWriter *pWriter){
+ CLEAR(pWriter);
+ pWriter->iLevel = iLevel;
+ pWriter->idx = idx;
+
+ dataBufferInit(&pWriter->term, 32);
+
+ /* Start out with a reasonably sized block, though it can grow. */
+ dataBufferInit(&pWriter->data, LEAF_MAX);
+}
+
+#ifndef NDEBUG
+/* Verify that the data is readable as a leaf node. */
+static void leafNodeValidate(const char *pData, int nData){
+ int n, iDummy;
+
+ if( nData==0 ) return;
+ assert( nData>0 );
+ assert( pData!=0 );
+ assert( pData+nData>pData );
+
+ /* Must lead with a varint(0) */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( iDummy==0 );
+ assert( n>0 );
+ assert( n<nData );
+ pData += n;
+ nData -= n;
+
+ /* Leading term length and data must fit in buffer. */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0 );
+ assert( n+iDummy<nData );
+ pData += n+iDummy;
+ nData -= n+iDummy;
+
+ /* Leading term's doclist length and data must fit. */
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0 );
+ assert( n+iDummy<=nData );
+ ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
+ pData += n+iDummy;
+ nData -= n+iDummy;
+
+ /* Verify that trailing terms and doclists also are readable. */
+ while( nData!=0 ){
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>=0 );
+ assert( n<nData );
+ pData += n;
+ nData -= n;
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0 );
+ assert( n+iDummy<nData );
+ pData += n+iDummy;
+ nData -= n+iDummy;
+
+ n = fts3GetVarint32(pData, &iDummy);
+ assert( n>0 );
+ assert( iDummy>0 );
+ assert( n+iDummy>0 );
+ assert( n+iDummy<=nData );
+ ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
+ pData += n+iDummy;
+ nData -= n+iDummy;
+ }
+}
+#define ASSERT_VALID_LEAF_NODE(p, n) leafNodeValidate(p, n)
+#else
+#define ASSERT_VALID_LEAF_NODE(p, n) assert( 1 )
+#endif
+
+/* Flush the current leaf node to %_segments, and adding the resulting
+** blockid and the starting term to the interior node which will
+** contain it.
+*/
+static int leafWriterInternalFlush(fulltext_vtab *v, LeafWriter *pWriter,
+ int iData, int nData){
+ sqlite_int64 iBlockid = 0;
+ const char *pStartingTerm;
+ int nStartingTerm, rc, n;
+
+ /* Must have the leading varint(0) flag, plus at least some
+ ** valid-looking data.
+ */
+ assert( nData>2 );
+ assert( iData>=0 );
+ assert( iData+nData<=pWriter->data.nData );
+ ASSERT_VALID_LEAF_NODE(pWriter->data.pData+iData, nData);
+
+ rc = block_insert(v, pWriter->data.pData+iData, nData, &iBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( iBlockid!=0 );
+
+ /* Reconstruct the first term in the leaf for purposes of building
+ ** the interior node.
+ */
+ n = fts3GetVarint32(pWriter->data.pData+iData+1, &nStartingTerm);
+ pStartingTerm = pWriter->data.pData+iData+1+n;
+ assert( pWriter->data.nData>iData+1+n+nStartingTerm );
+ assert( pWriter->nTermDistinct>0 );
+ assert( pWriter->nTermDistinct<=nStartingTerm );
+ nStartingTerm = pWriter->nTermDistinct;
+
+ if( pWriter->has_parent ){
+ interiorWriterAppend(&pWriter->parentWriter,
+ pStartingTerm, nStartingTerm, iBlockid);
+ }else{
+ interiorWriterInit(1, pStartingTerm, nStartingTerm, iBlockid,
+ &pWriter->parentWriter);
+ pWriter->has_parent = 1;
+ }
+
+ /* Track the span of this segment's leaf nodes. */
+ if( pWriter->iEndBlockid==0 ){
+ pWriter->iEndBlockid = pWriter->iStartBlockid = iBlockid;
+ }else{
+ pWriter->iEndBlockid++;
+ assert( iBlockid==pWriter->iEndBlockid );
+ }
+
+ return SQLITE_OK;
+}
+static int leafWriterFlush(fulltext_vtab *v, LeafWriter *pWriter){
+ int rc = leafWriterInternalFlush(v, pWriter, 0, pWriter->data.nData);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Re-initialize the output buffer. */
+ dataBufferReset(&pWriter->data);
+
+ return SQLITE_OK;
+}
+
+/* Fetch the root info for the segment. If the entire leaf fits
+** within ROOT_MAX, then it will be returned directly, otherwise it
+** will be flushed and the root info will be returned from the
+** interior node. *piEndBlockid is set to the blockid of the last
+** interior or leaf node written to disk (0 if none are written at
+** all).
+*/
+static int leafWriterRootInfo(fulltext_vtab *v, LeafWriter *pWriter,
+ char **ppRootInfo, int *pnRootInfo,
+ sqlite_int64 *piEndBlockid){
+ /* we can fit the segment entirely inline */
+ if( !pWriter->has_parent && pWriter->data.nData<ROOT_MAX ){
+ *ppRootInfo = pWriter->data.pData;
+ *pnRootInfo = pWriter->data.nData;
+ *piEndBlockid = 0;
+ return SQLITE_OK;
+ }
+
+ /* Flush remaining leaf data. */
+ if( pWriter->data.nData>0 ){
+ int rc = leafWriterFlush(v, pWriter);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ /* We must have flushed a leaf at some point. */
+ assert( pWriter->has_parent );
+
+ /* Tenatively set the end leaf blockid as the end blockid. If the
+ ** interior node can be returned inline, this will be the final
+ ** blockid, otherwise it will be overwritten by
+ ** interiorWriterRootInfo().
+ */
+ *piEndBlockid = pWriter->iEndBlockid;
+
+ return interiorWriterRootInfo(v, &pWriter->parentWriter,
+ ppRootInfo, pnRootInfo, piEndBlockid);
+}
+
+/* Collect the rootInfo data and store it into the segment directory.
+** This has the effect of flushing the segment's leaf data to
+** %_segments, and also flushing any interior nodes to %_segments.
+*/
+static int leafWriterFinalize(fulltext_vtab *v, LeafWriter *pWriter){
+ sqlite_int64 iEndBlockid;
+ char *pRootInfo;
+ int rc, nRootInfo;
+
+ rc = leafWriterRootInfo(v, pWriter, &pRootInfo, &nRootInfo, &iEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Don't bother storing an entirely empty segment. */
+ if( iEndBlockid==0 && nRootInfo==0 ) return SQLITE_OK;
+
+ return segdir_set(v, pWriter->iLevel, pWriter->idx,
+ pWriter->iStartBlockid, pWriter->iEndBlockid,
+ iEndBlockid, pRootInfo, nRootInfo);
+}
+
+static void leafWriterDestroy(LeafWriter *pWriter){
+ if( pWriter->has_parent ) interiorWriterDestroy(&pWriter->parentWriter);
+ dataBufferDestroy(&pWriter->term);
+ dataBufferDestroy(&pWriter->data);
+}
+
+/* Encode a term into the leafWriter, delta-encoding as appropriate.
+** Returns the length of the new term which distinguishes it from the
+** previous term, which can be used to set nTermDistinct when a node
+** boundary is crossed.
+*/
+static int leafWriterEncodeTerm(LeafWriter *pWriter,
+ const char *pTerm, int nTerm){
+ char c[VARINT_MAX+VARINT_MAX];
+ int n, nPrefix = 0;
+
+ assert( nTerm>0 );
+ while( nPrefix<pWriter->term.nData &&
+ pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
+ nPrefix++;
+ /* Failing this implies that the terms weren't in order. */
+ assert( nPrefix<nTerm );
+ }
+
+ if( pWriter->data.nData==0 ){
+ /* Encode the node header and leading term as:
+ ** varint(0)
+ ** varint(nTerm)
+ ** char pTerm[nTerm]
+ */
+ n = fts3PutVarint(c, '\0');
+ n += fts3PutVarint(c+n, nTerm);
+ dataBufferAppend2(&pWriter->data, c, n, pTerm, nTerm);
+ }else{
+ /* Delta-encode the term as:
+ ** varint(nPrefix)
+ ** varint(nSuffix)
+ ** char pTermSuffix[nSuffix]
+ */
+ n = fts3PutVarint(c, nPrefix);
+ n += fts3PutVarint(c+n, nTerm-nPrefix);
+ dataBufferAppend2(&pWriter->data, c, n, pTerm+nPrefix, nTerm-nPrefix);
+ }
+ dataBufferReplace(&pWriter->term, pTerm, nTerm);
+
+ return nPrefix+1;
+}
+
+/* Used to avoid a memmove when a large amount of doclist data is in
+** the buffer. This constructs a node and term header before
+** iDoclistData and flushes the resulting complete node using
+** leafWriterInternalFlush().
+*/
+static int leafWriterInlineFlush(fulltext_vtab *v, LeafWriter *pWriter,
+ const char *pTerm, int nTerm,
+ int iDoclistData){
+ char c[VARINT_MAX+VARINT_MAX];
+ int iData, n = fts3PutVarint(c, 0);
+ n += fts3PutVarint(c+n, nTerm);
+
+ /* There should always be room for the header. Even if pTerm shared
+ ** a substantial prefix with the previous term, the entire prefix
+ ** could be constructed from earlier data in the doclist, so there
+ ** should be room.
+ */
+ assert( iDoclistData>=n+nTerm );
+
+ iData = iDoclistData-(n+nTerm);
+ memcpy(pWriter->data.pData+iData, c, n);
+ memcpy(pWriter->data.pData+iData+n, pTerm, nTerm);
+
+ return leafWriterInternalFlush(v, pWriter, iData, pWriter->data.nData-iData);
+}
+
+/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
+** %_segments.
+*/
+static int leafWriterStepMerge(fulltext_vtab *v, LeafWriter *pWriter,
+ const char *pTerm, int nTerm,
+ DLReader *pReaders, int nReaders){
+ char c[VARINT_MAX+VARINT_MAX];
+ int iTermData = pWriter->data.nData, iDoclistData;
+ int i, nData, n, nActualData, nActual, rc, nTermDistinct;
+
+ ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
+ nTermDistinct = leafWriterEncodeTerm(pWriter, pTerm, nTerm);
+
+ /* Remember nTermDistinct if opening a new node. */
+ if( iTermData==0 ) pWriter->nTermDistinct = nTermDistinct;
+
+ iDoclistData = pWriter->data.nData;
+
+ /* Estimate the length of the merged doclist so we can leave space
+ ** to encode it.
+ */
+ for(i=0, nData=0; i<nReaders; i++){
+ nData += dlrAllDataBytes(&pReaders[i]);
+ }
+ n = fts3PutVarint(c, nData);
+ dataBufferAppend(&pWriter->data, c, n);
+
+ docListMerge(&pWriter->data, pReaders, nReaders);
+ ASSERT_VALID_DOCLIST(DL_DEFAULT,
+ pWriter->data.pData+iDoclistData+n,
+ pWriter->data.nData-iDoclistData-n, NULL);
+
+ /* The actual amount of doclist data at this point could be smaller
+ ** than the length we encoded. Additionally, the space required to
+ ** encode this length could be smaller. For small doclists, this is
+ ** not a big deal, we can just use memmove() to adjust things.
+ */
+ nActualData = pWriter->data.nData-(iDoclistData+n);
+ nActual = fts3PutVarint(c, nActualData);
+ assert( nActualData<=nData );
+ assert( nActual<=n );
+
+ /* If the new doclist is big enough for force a standalone leaf
+ ** node, we can immediately flush it inline without doing the
+ ** memmove().
+ */
+ /* TODO(shess) This test matches leafWriterStep(), which does this
+ ** test before it knows the cost to varint-encode the term and
+ ** doclist lengths. At some point, change to
+ ** pWriter->data.nData-iTermData>STANDALONE_MIN.
+ */
+ if( nTerm+nActualData>STANDALONE_MIN ){
+ /* Push leaf node from before this term. */
+ if( iTermData>0 ){
+ rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pWriter->nTermDistinct = nTermDistinct;
+ }
+
+ /* Fix the encoded doclist length. */
+ iDoclistData += n - nActual;
+ memcpy(pWriter->data.pData+iDoclistData, c, nActual);
+
+ /* Push the standalone leaf node. */
+ rc = leafWriterInlineFlush(v, pWriter, pTerm, nTerm, iDoclistData);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Leave the node empty. */
+ dataBufferReset(&pWriter->data);
+
+ return rc;
+ }
+
+ /* At this point, we know that the doclist was small, so do the
+ ** memmove if indicated.
+ */
+ if( nActual<n ){
+ memmove(pWriter->data.pData+iDoclistData+nActual,
+ pWriter->data.pData+iDoclistData+n,
+ pWriter->data.nData-(iDoclistData+n));
+ pWriter->data.nData -= n-nActual;
+ }
+
+ /* Replace written length with actual length. */
+ memcpy(pWriter->data.pData+iDoclistData, c, nActual);
+
+ /* If the node is too large, break things up. */
+ /* TODO(shess) This test matches leafWriterStep(), which does this
+ ** test before it knows the cost to varint-encode the term and
+ ** doclist lengths. At some point, change to
+ ** pWriter->data.nData>LEAF_MAX.
+ */
+ if( iTermData+nTerm+nActualData>LEAF_MAX ){
+ /* Flush out the leading data as a node */
+ rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pWriter->nTermDistinct = nTermDistinct;
+
+ /* Rebuild header using the current term */
+ n = fts3PutVarint(pWriter->data.pData, 0);
+ n += fts3PutVarint(pWriter->data.pData+n, nTerm);
+ memcpy(pWriter->data.pData+n, pTerm, nTerm);
+ n += nTerm;
+
+ /* There should always be room, because the previous encoding
+ ** included all data necessary to construct the term.
+ */
+ assert( n<iDoclistData );
+ /* So long as STANDALONE_MIN is half or less of LEAF_MAX, the
+ ** following memcpy() is safe (as opposed to needing a memmove).
+ */
+ assert( 2*STANDALONE_MIN<=LEAF_MAX );
+ assert( n+pWriter->data.nData-iDoclistData<iDoclistData );
+ memcpy(pWriter->data.pData+n,
+ pWriter->data.pData+iDoclistData,
+ pWriter->data.nData-iDoclistData);
+ pWriter->data.nData -= iDoclistData-n;
+ }
+ ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
+
+ return SQLITE_OK;
+}
+
+/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
+** %_segments.
+*/
+/* TODO(shess) Revise writeZeroSegment() so that doclists are
+** constructed directly in pWriter->data.
+*/
+static int leafWriterStep(fulltext_vtab *v, LeafWriter *pWriter,
+ const char *pTerm, int nTerm,
+ const char *pData, int nData){
+ int rc;
+ DLReader reader;
+
+ dlrInit(&reader, DL_DEFAULT, pData, nData);
+ rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1);
+ dlrDestroy(&reader);
+
+ return rc;
+}
+
+
+/****************************************************************/
+/* LeafReader is used to iterate over an individual leaf node. */
+typedef struct LeafReader {
+ DataBuffer term; /* copy of current term. */
+
+ const char *pData; /* data for current term. */
+ int nData;
+} LeafReader;
+
+static void leafReaderDestroy(LeafReader *pReader){
+ dataBufferDestroy(&pReader->term);
+ SCRAMBLE(pReader);
+}
+
+static int leafReaderAtEnd(LeafReader *pReader){
+ return pReader->nData<=0;
+}
+
+/* Access the current term. */
+static int leafReaderTermBytes(LeafReader *pReader){
+ return pReader->term.nData;
+}
+static const char *leafReaderTerm(LeafReader *pReader){
+ assert( pReader->term.nData>0 );
+ return pReader->term.pData;
+}
+
+/* Access the doclist data for the current term. */
+static int leafReaderDataBytes(LeafReader *pReader){
+ int nData;
+ assert( pReader->term.nData>0 );
+ fts3GetVarint32(pReader->pData, &nData);
+ return nData;
+}
+static const char *leafReaderData(LeafReader *pReader){
+ int n, nData;
+ assert( pReader->term.nData>0 );
+ n = fts3GetVarint32(pReader->pData, &nData);
+ return pReader->pData+n;
+}
+
+static void leafReaderInit(const char *pData, int nData,
+ LeafReader *pReader){
+ int nTerm, n;
+
+ assert( nData>0 );
+ assert( pData[0]=='\0' );
+
+ CLEAR(pReader);
+
+ /* Read the first term, skipping the header byte. */
+ n = fts3GetVarint32(pData+1, &nTerm);
+ dataBufferInit(&pReader->term, nTerm);
+ dataBufferReplace(&pReader->term, pData+1+n, nTerm);
+
+ /* Position after the first term. */
+ assert( 1+n+nTerm<nData );
+ pReader->pData = pData+1+n+nTerm;
+ pReader->nData = nData-1-n-nTerm;
+}
+
+/* Step the reader forward to the next term. */
+static void leafReaderStep(LeafReader *pReader){
+ int n, nData, nPrefix, nSuffix;
+ assert( !leafReaderAtEnd(pReader) );
+
+ /* Skip previous entry's data block. */
+ n = fts3GetVarint32(pReader->pData, &nData);
+ assert( n+nData<=pReader->nData );
+ pReader->pData += n+nData;
+ pReader->nData -= n+nData;
+
+ if( !leafReaderAtEnd(pReader) ){
+ /* Construct the new term using a prefix from the old term plus a
+ ** suffix from the leaf data.
+ */
+ n = fts3GetVarint32(pReader->pData, &nPrefix);
+ n += fts3GetVarint32(pReader->pData+n, &nSuffix);
+ assert( n+nSuffix<pReader->nData );
+ pReader->term.nData = nPrefix;
+ dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
+
+ pReader->pData += n+nSuffix;
+ pReader->nData -= n+nSuffix;
+ }
+}
+
+/* strcmp-style comparison of pReader's current term against pTerm.
+** If isPrefix, equality means equal through nTerm bytes.
+*/
+static int leafReaderTermCmp(LeafReader *pReader,
+ const char *pTerm, int nTerm, int isPrefix){
+ int c, n = pReader->term.nData<nTerm ? pReader->term.nData : nTerm;
+ if( n==0 ){
+ if( pReader->term.nData>0 ) return -1;
+ if(nTerm>0 ) return 1;
+ return 0;
+ }
+
+ c = memcmp(pReader->term.pData, pTerm, n);
+ if( c!=0 ) return c;
+ if( isPrefix && n==nTerm ) return 0;
+ return pReader->term.nData - nTerm;
+}
+
+
+/****************************************************************/
+/* LeavesReader wraps LeafReader to allow iterating over the entire
+** leaf layer of the tree.
+*/
+typedef struct LeavesReader {
+ int idx; /* Index within the segment. */
+
+ sqlite3_stmt *pStmt; /* Statement we're streaming leaves from. */
+ int eof; /* we've seen SQLITE_DONE from pStmt. */
+
+ LeafReader leafReader; /* reader for the current leaf. */
+ DataBuffer rootData; /* root data for inline. */
+} LeavesReader;
+
+/* Access the current term. */
+static int leavesReaderTermBytes(LeavesReader *pReader){
+ assert( !pReader->eof );
+ return leafReaderTermBytes(&pReader->leafReader);
+}
+static const char *leavesReaderTerm(LeavesReader *pReader){
+ assert( !pReader->eof );
+ return leafReaderTerm(&pReader->leafReader);
+}
+
+/* Access the doclist data for the current term. */
+static int leavesReaderDataBytes(LeavesReader *pReader){
+ assert( !pReader->eof );
+ return leafReaderDataBytes(&pReader->leafReader);
+}
+static const char *leavesReaderData(LeavesReader *pReader){
+ assert( !pReader->eof );
+ return leafReaderData(&pReader->leafReader);
+}
+
+static int leavesReaderAtEnd(LeavesReader *pReader){
+ return pReader->eof;
+}
+
+/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus
+** leaving the statement handle open, which locks the table.
+*/
+/* TODO(shess) This "solution" is not satisfactory. Really, there
+** should be check-in function for all statement handles which
+** arranges to call sqlite3_reset(). This most likely will require
+** modification to control flow all over the place, though, so for now
+** just punt.
+**
+** Note the the current system assumes that segment merges will run to
+** completion, which is why this particular probably hasn't arisen in
+** this case. Probably a brittle assumption.
+*/
+static int leavesReaderReset(LeavesReader *pReader){
+ return sqlite3_reset(pReader->pStmt);
+}
+
+static void leavesReaderDestroy(LeavesReader *pReader){
+ /* If idx is -1, that means we're using a non-cached statement
+ ** handle in the optimize() case, so we need to release it.
+ */
+ if( pReader->pStmt!=NULL && pReader->idx==-1 ){
+ sqlite3_finalize(pReader->pStmt);
+ }
+ leafReaderDestroy(&pReader->leafReader);
+ dataBufferDestroy(&pReader->rootData);
+ SCRAMBLE(pReader);
+}
+
+/* Initialize pReader with the given root data (if iStartBlockid==0
+** the leaf data was entirely contained in the root), or from the
+** stream of blocks between iStartBlockid and iEndBlockid, inclusive.
+*/
+static int leavesReaderInit(fulltext_vtab *v,
+ int idx,
+ sqlite_int64 iStartBlockid,
+ sqlite_int64 iEndBlockid,
+ const char *pRootData, int nRootData,
+ LeavesReader *pReader){
+ CLEAR(pReader);
+ pReader->idx = idx;
+
+ dataBufferInit(&pReader->rootData, 0);
+ if( iStartBlockid==0 ){
+ /* Entire leaf level fit in root data. */
+ dataBufferReplace(&pReader->rootData, pRootData, nRootData);
+ leafReaderInit(pReader->rootData.pData, pReader->rootData.nData,
+ &pReader->leafReader);
+ }else{
+ sqlite3_stmt *s;
+ int rc = sql_get_leaf_statement(v, idx, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iStartBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 2, iEndBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ){
+ pReader->eof = 1;
+ return SQLITE_OK;
+ }
+ if( rc!=SQLITE_ROW ) return rc;
+
+ pReader->pStmt = s;
+ leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
+ sqlite3_column_bytes(pReader->pStmt, 0),
+ &pReader->leafReader);
+ }
+ return SQLITE_OK;
+}
+
+/* Step the current leaf forward to the next term. If we reach the
+** end of the current leaf, step forward to the next leaf block.
+*/
+static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){
+ assert( !leavesReaderAtEnd(pReader) );
+ leafReaderStep(&pReader->leafReader);
+
+ if( leafReaderAtEnd(&pReader->leafReader) ){
+ int rc;
+ if( pReader->rootData.pData ){
+ pReader->eof = 1;
+ return SQLITE_OK;
+ }
+ rc = sqlite3_step(pReader->pStmt);
+ if( rc!=SQLITE_ROW ){
+ pReader->eof = 1;
+ return rc==SQLITE_DONE ? SQLITE_OK : rc;
+ }
+ leafReaderDestroy(&pReader->leafReader);
+ leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
+ sqlite3_column_bytes(pReader->pStmt, 0),
+ &pReader->leafReader);
+ }
+ return SQLITE_OK;
+}
+
+/* Order LeavesReaders by their term, ignoring idx. Readers at eof
+** always sort to the end.
+*/
+static int leavesReaderTermCmp(LeavesReader *lr1, LeavesReader *lr2){
+ if( leavesReaderAtEnd(lr1) ){
+ if( leavesReaderAtEnd(lr2) ) return 0;
+ return 1;
+ }
+ if( leavesReaderAtEnd(lr2) ) return -1;
+
+ return leafReaderTermCmp(&lr1->leafReader,
+ leavesReaderTerm(lr2), leavesReaderTermBytes(lr2),
+ 0);
+}
+
+/* Similar to leavesReaderTermCmp(), with additional ordering by idx
+** so that older segments sort before newer segments.
+*/
+static int leavesReaderCmp(LeavesReader *lr1, LeavesReader *lr2){
+ int c = leavesReaderTermCmp(lr1, lr2);
+ if( c!=0 ) return c;
+ return lr1->idx-lr2->idx;
+}
+
+/* Assume that pLr[1]..pLr[nLr] are sorted. Bubble pLr[0] into its
+** sorted position.
+*/
+static void leavesReaderReorder(LeavesReader *pLr, int nLr){
+ while( nLr>1 && leavesReaderCmp(pLr, pLr+1)>0 ){
+ LeavesReader tmp = pLr[0];
+ pLr[0] = pLr[1];
+ pLr[1] = tmp;
+ nLr--;
+ pLr++;
+ }
+}
+
+/* Initializes pReaders with the segments from level iLevel, returning
+** the number of segments in *piReaders. Leaves pReaders in sorted
+** order.
+*/
+static int leavesReadersInit(fulltext_vtab *v, int iLevel,
+ LeavesReader *pReaders, int *piReaders){
+ sqlite3_stmt *s;
+ int i, rc = sql_get_statement(v, SEGDIR_SELECT_LEVEL_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int(s, 1, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+
+ i = 0;
+ while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+ sqlite_int64 iStart = sqlite3_column_int64(s, 0);
+ sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
+ const char *pRootData = sqlite3_column_blob(s, 2);
+ int nRootData = sqlite3_column_bytes(s, 2);
+
+ assert( i<MERGE_COUNT );
+ rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData,
+ &pReaders[i]);
+ if( rc!=SQLITE_OK ) break;
+
+ i++;
+ }
+ if( rc!=SQLITE_DONE ){
+ while( i-->0 ){
+ leavesReaderDestroy(&pReaders[i]);
+ }
+ return rc;
+ }
+
+ *piReaders = i;
+
+ /* Leave our results sorted by term, then age. */
+ while( i-- ){
+ leavesReaderReorder(pReaders+i, *piReaders-i);
+ }
+ return SQLITE_OK;
+}
+
+/* Merge doclists from pReaders[nReaders] into a single doclist, which
+** is written to pWriter. Assumes pReaders is ordered oldest to
+** newest.
+*/
+/* TODO(shess) Consider putting this inline in segmentMerge(). */
+static int leavesReadersMerge(fulltext_vtab *v,
+ LeavesReader *pReaders, int nReaders,
+ LeafWriter *pWriter){
+ DLReader dlReaders[MERGE_COUNT];
+ const char *pTerm = leavesReaderTerm(pReaders);
+ int i, nTerm = leavesReaderTermBytes(pReaders);
+
+ assert( nReaders<=MERGE_COUNT );
+
+ for(i=0; i<nReaders; i++){
+ dlrInit(&dlReaders[i], DL_DEFAULT,
+ leavesReaderData(pReaders+i),
+ leavesReaderDataBytes(pReaders+i));
+ }
+
+ return leafWriterStepMerge(v, pWriter, pTerm, nTerm, dlReaders, nReaders);
+}
+
+/* Forward ref due to mutual recursion with segdirNextIndex(). */
+static int segmentMerge(fulltext_vtab *v, int iLevel);
+
+/* Put the next available index at iLevel into *pidx. If iLevel
+** already has MERGE_COUNT segments, they are merged to a higher
+** level to make room.
+*/
+static int segdirNextIndex(fulltext_vtab *v, int iLevel, int *pidx){
+ int rc = segdir_max_index(v, iLevel, pidx);
+ if( rc==SQLITE_DONE ){ /* No segments at iLevel. */
+ *pidx = 0;
+ }else if( rc==SQLITE_ROW ){
+ if( *pidx==(MERGE_COUNT-1) ){
+ rc = segmentMerge(v, iLevel);
+ if( rc!=SQLITE_OK ) return rc;
+ *pidx = 0;
+ }else{
+ (*pidx)++;
+ }
+ }else{
+ return rc;
+ }
+ return SQLITE_OK;
+}
+
+/* Merge MERGE_COUNT segments at iLevel into a new segment at
+** iLevel+1. If iLevel+1 is already full of segments, those will be
+** merged to make room.
+*/
+static int segmentMerge(fulltext_vtab *v, int iLevel){
+ LeafWriter writer;
+ LeavesReader lrs[MERGE_COUNT];
+ int i, rc, idx = 0;
+
+ /* Determine the next available segment index at the next level,
+ ** merging as necessary.
+ */
+ rc = segdirNextIndex(v, iLevel+1, &idx);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* TODO(shess) This assumes that we'll always see exactly
+ ** MERGE_COUNT segments to merge at a given level. That will be
+ ** broken if we allow the developer to request preemptive or
+ ** deferred merging.
+ */
+ memset(&lrs, '\0', sizeof(lrs));
+ rc = leavesReadersInit(v, iLevel, lrs, &i);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( i==MERGE_COUNT );
+
+ leafWriterInit(iLevel+1, idx, &writer);
+
+ /* Since leavesReaderReorder() pushes readers at eof to the end,
+ ** when the first reader is empty, all will be empty.
+ */
+ while( !leavesReaderAtEnd(lrs) ){
+ /* Figure out how many readers share their next term. */
+ for(i=1; i<MERGE_COUNT && !leavesReaderAtEnd(lrs+i); i++){
+ if( 0!=leavesReaderTermCmp(lrs, lrs+i) ) break;
+ }
+
+ rc = leavesReadersMerge(v, lrs, i, &writer);
+ if( rc!=SQLITE_OK ) goto err;
+
+ /* Step forward those that were merged. */
+ while( i-->0 ){
+ rc = leavesReaderStep(v, lrs+i);
+ if( rc!=SQLITE_OK ) goto err;
+
+ /* Reorder by term, then by age. */
+ leavesReaderReorder(lrs+i, MERGE_COUNT-i);
+ }
+ }
+
+ for(i=0; i<MERGE_COUNT; i++){
+ leavesReaderDestroy(&lrs[i]);
+ }
+
+ rc = leafWriterFinalize(v, &writer);
+ leafWriterDestroy(&writer);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Delete the merged segment data. */
+ return segdir_delete(v, iLevel);
+
+ err:
+ for(i=0; i<MERGE_COUNT; i++){
+ leavesReaderDestroy(&lrs[i]);
+ }
+ leafWriterDestroy(&writer);
+ return rc;
+}
+
+/* Accumulate the union of *acc and *pData into *acc. */
+static void docListAccumulateUnion(DataBuffer *acc,
+ const char *pData, int nData) {
+ DataBuffer tmp = *acc;
+ dataBufferInit(acc, tmp.nData+nData);
+ docListUnion(tmp.pData, tmp.nData, pData, nData, acc);
+ dataBufferDestroy(&tmp);
+}
+
+/* TODO(shess) It might be interesting to explore different merge
+** strategies, here. For instance, since this is a sorted merge, we
+** could easily merge many doclists in parallel. With some
+** comprehension of the storage format, we could merge all of the
+** doclists within a leaf node directly from the leaf node's storage.
+** It may be worthwhile to merge smaller doclists before larger
+** doclists, since they can be traversed more quickly - but the
+** results may have less overlap, making them more expensive in a
+** different way.
+*/
+
+/* Scan pReader for pTerm/nTerm, and merge the term's doclist over
+** *out (any doclists with duplicate docids overwrite those in *out).
+** Internal function for loadSegmentLeaf().
+*/
+static int loadSegmentLeavesInt(fulltext_vtab *v, LeavesReader *pReader,
+ const char *pTerm, int nTerm, int isPrefix,
+ DataBuffer *out){
+ /* doclist data is accumulated into pBuffers similar to how one does
+ ** increment in binary arithmetic. If index 0 is empty, the data is
+ ** stored there. If there is data there, it is merged and the
+ ** results carried into position 1, with further merge-and-carry
+ ** until an empty position is found.
+ */
+ DataBuffer *pBuffers = NULL;
+ int nBuffers = 0, nMaxBuffers = 0, rc;
+
+ assert( nTerm>0 );
+
+ for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader);
+ rc=leavesReaderStep(v, pReader)){
+ /* TODO(shess) Really want leavesReaderTermCmp(), but that name is
+ ** already taken to compare the terms of two LeavesReaders. Think
+ ** on a better name. [Meanwhile, break encapsulation rather than
+ ** use a confusing name.]
+ */
+ int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix);
+ if( c>0 ) break; /* Past any possible matches. */
+ if( c==0 ){
+ const char *pData = leavesReaderData(pReader);
+ int iBuffer, nData = leavesReaderDataBytes(pReader);
+
+ /* Find the first empty buffer. */
+ for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+ if( 0==pBuffers[iBuffer].nData ) break;
+ }
+
+ /* Out of buffers, add an empty one. */
+ if( iBuffer==nBuffers ){
+ if( nBuffers==nMaxBuffers ){
+ DataBuffer *p;
+ nMaxBuffers += 20;
+
+ /* Manual realloc so we can handle NULL appropriately. */
+ p = sqlite3_malloc(nMaxBuffers*sizeof(*pBuffers));
+ if( p==NULL ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+
+ if( nBuffers>0 ){
+ assert(pBuffers!=NULL);
+ memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers));
+ sqlite3_free(pBuffers);
+ }
+ pBuffers = p;
+ }
+ dataBufferInit(&(pBuffers[nBuffers]), 0);
+ nBuffers++;
+ }
+
+ /* At this point, must have an empty at iBuffer. */
+ assert(iBuffer<nBuffers && pBuffers[iBuffer].nData==0);
+
+ /* If empty was first buffer, no need for merge logic. */
+ if( iBuffer==0 ){
+ dataBufferReplace(&(pBuffers[0]), pData, nData);
+ }else{
+ /* pAcc is the empty buffer the merged data will end up in. */
+ DataBuffer *pAcc = &(pBuffers[iBuffer]);
+ DataBuffer *p = &(pBuffers[0]);
+
+ /* Handle position 0 specially to avoid need to prime pAcc
+ ** with pData/nData.
+ */
+ dataBufferSwap(p, pAcc);
+ docListAccumulateUnion(pAcc, pData, nData);
+
+ /* Accumulate remaining doclists into pAcc. */
+ for(++p; p<pAcc; ++p){
+ docListAccumulateUnion(pAcc, p->pData, p->nData);
+
+ /* dataBufferReset() could allow a large doclist to blow up
+ ** our memory requirements.
+ */
+ if( p->nCapacity<1024 ){
+ dataBufferReset(p);
+ }else{
+ dataBufferDestroy(p);
+ dataBufferInit(p, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /* Union all the doclists together into *out. */
+ /* TODO(shess) What if *out is big? Sigh. */
+ if( rc==SQLITE_OK && nBuffers>0 ){
+ int iBuffer;
+ for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){
+ if( pBuffers[iBuffer].nData>0 ){
+ if( out->nData==0 ){
+ dataBufferSwap(out, &(pBuffers[iBuffer]));
+ }else{
+ docListAccumulateUnion(out, pBuffers[iBuffer].pData,
+ pBuffers[iBuffer].nData);
+ }
+ }
+ }
+ }
+
+ while( nBuffers-- ){
+ dataBufferDestroy(&(pBuffers[nBuffers]));
+ }
+ if( pBuffers!=NULL ) sqlite3_free(pBuffers);
+
+ return rc;
+}
+
+/* Call loadSegmentLeavesInt() with pData/nData as input. */
+static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData,
+ const char *pTerm, int nTerm, int isPrefix,
+ DataBuffer *out){
+ LeavesReader reader;
+ int rc;
+
+ assert( nData>1 );
+ assert( *pData=='\0' );
+ rc = leavesReaderInit(v, 0, 0, 0, pData, nData, &reader);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
+ leavesReaderReset(&reader);
+ leavesReaderDestroy(&reader);
+ return rc;
+}
+
+/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to
+** iEndLeaf (inclusive) as input, and merge the resulting doclist into
+** out.
+*/
+static int loadSegmentLeaves(fulltext_vtab *v,
+ sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf,
+ const char *pTerm, int nTerm, int isPrefix,
+ DataBuffer *out){
+ int rc;
+ LeavesReader reader;
+
+ assert( iStartLeaf<=iEndLeaf );
+ rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
+ leavesReaderReset(&reader);
+ leavesReaderDestroy(&reader);
+ return rc;
+}
+
+/* Taking pData/nData as an interior node, find the sequence of child
+** nodes which could include pTerm/nTerm/isPrefix. Note that the
+** interior node terms logically come between the blocks, so there is
+** one more blockid than there are terms (that block contains terms >=
+** the last interior-node term).
+*/
+/* TODO(shess) The calling code may already know that the end child is
+** not worth calculating, because the end may be in a later sibling
+** node. Consider whether breaking symmetry is worthwhile. I suspect
+** it is not worthwhile.
+*/
+static void getChildrenContaining(const char *pData, int nData,
+ const char *pTerm, int nTerm, int isPrefix,
+ sqlite_int64 *piStartChild,
+ sqlite_int64 *piEndChild){
+ InteriorReader reader;
+
+ assert( nData>1 );
+ assert( *pData!='\0' );
+ interiorReaderInit(pData, nData, &reader);
+
+ /* Scan for the first child which could contain pTerm/nTerm. */
+ while( !interiorReaderAtEnd(&reader) ){
+ if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break;
+ interiorReaderStep(&reader);
+ }
+ *piStartChild = interiorReaderCurrentBlockid(&reader);
+
+ /* Keep scanning to find a term greater than our term, using prefix
+ ** comparison if indicated. If isPrefix is false, this will be the
+ ** same blockid as the starting block.
+ */
+ while( !interiorReaderAtEnd(&reader) ){
+ if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break;
+ interiorReaderStep(&reader);
+ }
+ *piEndChild = interiorReaderCurrentBlockid(&reader);
+
+ interiorReaderDestroy(&reader);
+
+ /* Children must ascend, and if !prefix, both must be the same. */
+ assert( *piEndChild>=*piStartChild );
+ assert( isPrefix || *piStartChild==*piEndChild );
+}
+
+/* Read block at iBlockid and pass it with other params to
+** getChildrenContaining().
+*/
+static int loadAndGetChildrenContaining(
+ fulltext_vtab *v,
+ sqlite_int64 iBlockid,
+ const char *pTerm, int nTerm, int isPrefix,
+ sqlite_int64 *piStartChild, sqlite_int64 *piEndChild
+){
+ sqlite3_stmt *s = NULL;
+ int rc;
+
+ assert( iBlockid!=0 );
+ assert( pTerm!=NULL );
+ assert( nTerm!=0 ); /* TODO(shess) Why not allow this? */
+ assert( piStartChild!=NULL );
+ assert( piEndChild!=NULL );
+
+ rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_bind_int64(s, 1, iBlockid);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_DONE ) return SQLITE_ERROR;
+ if( rc!=SQLITE_ROW ) return rc;
+
+ getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
+ pTerm, nTerm, isPrefix, piStartChild, piEndChild);
+
+ /* We expect only one row. We must execute another sqlite3_step()
+ * to complete the iteration; otherwise the table will remain
+ * locked. */
+ rc = sqlite3_step(s);
+ if( rc==SQLITE_ROW ) return SQLITE_ERROR;
+ if( rc!=SQLITE_DONE ) return rc;
+
+ return SQLITE_OK;
+}
+
+/* Traverse the tree represented by pData[nData] looking for
+** pTerm[nTerm], placing its doclist into *out. This is internal to
+** loadSegment() to make error-handling cleaner.
+*/
+static int loadSegmentInt(fulltext_vtab *v, const char *pData, int nData,
+ sqlite_int64 iLeavesEnd,
+ const char *pTerm, int nTerm, int isPrefix,
+ DataBuffer *out){
+ /* Special case where root is a leaf. */
+ if( *pData=='\0' ){
+ return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, isPrefix, out);
+ }else{
+ int rc;
+ sqlite_int64 iStartChild, iEndChild;
+
+ /* Process pData as an interior node, then loop down the tree
+ ** until we find the set of leaf nodes to scan for the term.
+ */
+ getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix,
+ &iStartChild, &iEndChild);
+ while( iStartChild>iLeavesEnd ){
+ sqlite_int64 iNextStart, iNextEnd;
+ rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix,
+ &iNextStart, &iNextEnd);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* If we've branched, follow the end branch, too. */
+ if( iStartChild!=iEndChild ){
+ sqlite_int64 iDummy;
+ rc = loadAndGetChildrenContaining(v, iEndChild, pTerm, nTerm, isPrefix,
+ &iDummy, &iNextEnd);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ assert( iNextStart<=iNextEnd );
+ iStartChild = iNextStart;
+ iEndChild = iNextEnd;
+ }
+ assert( iStartChild<=iLeavesEnd );
+ assert( iEndChild<=iLeavesEnd );
+
+ /* Scan through the leaf segments for doclists. */
+ return loadSegmentLeaves(v, iStartChild, iEndChild,
+ pTerm, nTerm, isPrefix, out);
+ }
+}
+
+/* Call loadSegmentInt() to collect the doclist for pTerm/nTerm, then
+** merge its doclist over *out (any duplicate doclists read from the
+** segment rooted at pData will overwrite those in *out).
+*/
+/* TODO(shess) Consider changing this to determine the depth of the
+** leaves using either the first characters of interior nodes (when
+** ==1, we're one level above the leaves), or the first character of
+** the root (which will describe the height of the tree directly).
+** Either feels somewhat tricky to me.
+*/
+/* TODO(shess) The current merge is likely to be slow for large
+** doclists (though it should process from newest/smallest to
+** oldest/largest, so it may not be that bad). It might be useful to
+** modify things to allow for N-way merging. This could either be
+** within a segment, with pairwise merges across segments, or across
+** all segments at once.
+*/
+static int loadSegment(fulltext_vtab *v, const char *pData, int nData,
+ sqlite_int64 iLeavesEnd,
+ const char *pTerm, int nTerm, int isPrefix,
+ DataBuffer *out){
+ DataBuffer result;
+ int rc;
+
+ assert( nData>1 );
+
+ /* This code should never be called with buffered updates. */
+ assert( v->nPendingData<0 );
+
+ dataBufferInit(&result, 0);
+ rc = loadSegmentInt(v, pData, nData, iLeavesEnd,
+ pTerm, nTerm, isPrefix, &result);
+ if( rc==SQLITE_OK && result.nData>0 ){
+ if( out->nData==0 ){
+ DataBuffer tmp = *out;
+ *out = result;
+ result = tmp;
+ }else{
+ DataBuffer merged;
+ DLReader readers[2];
+
+ dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData);
+ dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData);
+ dataBufferInit(&merged, out->nData+result.nData);
+ docListMerge(&merged, readers, 2);
+ dataBufferDestroy(out);
+ *out = merged;
+ dlrDestroy(&readers[0]);
+ dlrDestroy(&readers[1]);
+ }
+ }
+ dataBufferDestroy(&result);
+ return rc;
+}
+
+/* Scan the database and merge together the posting lists for the term
+** into *out.
+*/
+static int termSelect(fulltext_vtab *v, int iColumn,
+ const char *pTerm, int nTerm, int isPrefix,
+ DocListType iType, DataBuffer *out){
+ DataBuffer doclist;
+ sqlite3_stmt *s;
+ int rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* This code should never be called with buffered updates. */
+ assert( v->nPendingData<0 );
+
+ dataBufferInit(&doclist, 0);
+
+ /* Traverse the segments from oldest to newest so that newer doclist
+ ** elements for given docids overwrite older elements.
+ */
+ while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+ const char *pData = sqlite3_column_blob(s, 2);
+ const int nData = sqlite3_column_bytes(s, 2);
+ const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
+ rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix,
+ &doclist);
+ if( rc!=SQLITE_OK ) goto err;
+ }
+ if( rc==SQLITE_DONE ){
+ if( doclist.nData!=0 ){
+ /* TODO(shess) The old term_select_all() code applied the column
+ ** restrict as we merged segments, leading to smaller buffers.
+ ** This is probably worthwhile to bring back, once the new storage
+ ** system is checked in.
+ */
+ if( iColumn==v->nColumn) iColumn = -1;
+ docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
+ iColumn, iType, out);
+ }
+ rc = SQLITE_OK;
+ }
+
+ err:
+ dataBufferDestroy(&doclist);
+ return rc;
+}
+
+/****************************************************************/
+/* Used to hold hashtable data for sorting. */
+typedef struct TermData {
+ const char *pTerm;
+ int nTerm;
+ DLCollector *pCollector;
+} TermData;
+
+/* Orders TermData elements in strcmp fashion ( <0 for less-than, 0
+** for equal, >0 for greater-than).
+*/
+static int termDataCmp(const void *av, const void *bv){
+ const TermData *a = (const TermData *)av;
+ const TermData *b = (const TermData *)bv;
+ int n = a->nTerm<b->nTerm ? a->nTerm : b->nTerm;
+ int c = memcmp(a->pTerm, b->pTerm, n);
+ if( c!=0 ) return c;
+ return a->nTerm-b->nTerm;
+}
+
+/* Order pTerms data by term, then write a new level 0 segment using
+** LeafWriter.
+*/
+static int writeZeroSegment(fulltext_vtab *v, fts3Hash *pTerms){
+ fts3HashElem *e;
+ int idx, rc, i, n;
+ TermData *pData;
+ LeafWriter writer;
+ DataBuffer dl;
+
+ /* Determine the next index at level 0, merging as necessary. */
+ rc = segdirNextIndex(v, 0, &idx);
+ if( rc!=SQLITE_OK ) return rc;
+
+ n = fts3HashCount(pTerms);
+ pData = sqlite3_malloc(n*sizeof(TermData));
+
+ for(i = 0, e = fts3HashFirst(pTerms); e; i++, e = fts3HashNext(e)){
+ assert( i<n );
+ pData[i].pTerm = fts3HashKey(e);
+ pData[i].nTerm = fts3HashKeysize(e);
+ pData[i].pCollector = fts3HashData(e);
+ }
+ assert( i==n );
+
+ /* TODO(shess) Should we allow user-defined collation sequences,
+ ** here? I think we only need that once we support prefix searches.
+ */
+ if( n>1 ) qsort(pData, n, sizeof(*pData), termDataCmp);
+
+ /* TODO(shess) Refactor so that we can write directly to the segment
+ ** DataBuffer, as happens for segment merges.
+ */
+ leafWriterInit(0, idx, &writer);
+ dataBufferInit(&dl, 0);
+ for(i=0; i<n; i++){
+ dataBufferReset(&dl);
+ dlcAddDoclist(pData[i].pCollector, &dl);
+ rc = leafWriterStep(v, &writer,
+ pData[i].pTerm, pData[i].nTerm, dl.pData, dl.nData);
+ if( rc!=SQLITE_OK ) goto err;
+ }
+ rc = leafWriterFinalize(v, &writer);
+
+ err:
+ dataBufferDestroy(&dl);
+ sqlite3_free(pData);
+ leafWriterDestroy(&writer);
+ return rc;
+}
+
+/* If pendingTerms has data, free it. */
+static int clearPendingTerms(fulltext_vtab *v){
+ if( v->nPendingData>=0 ){
+ fts3HashElem *e;
+ for(e=fts3HashFirst(&v->pendingTerms); e; e=fts3HashNext(e)){
+ dlcDelete(fts3HashData(e));
+ }
+ fts3HashClear(&v->pendingTerms);
+ v->nPendingData = -1;
+ }
+ return SQLITE_OK;
+}
+
+/* If pendingTerms has data, flush it to a level-zero segment, and
+** free it.
+*/
+static int flushPendingTerms(fulltext_vtab *v){
+ if( v->nPendingData>=0 ){
+ int rc = writeZeroSegment(v, &v->pendingTerms);
+ if( rc==SQLITE_OK ) clearPendingTerms(v);
+ return rc;
+ }
+ return SQLITE_OK;
+}
+
+/* If pendingTerms is "too big", or docid is out of order, flush it.
+** Regardless, be certain that pendingTerms is initialized for use.
+*/
+static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){
+ /* TODO(shess) Explore whether partially flushing the buffer on
+ ** forced-flush would provide better performance. I suspect that if
+ ** we ordered the doclists by size and flushed the largest until the
+ ** buffer was half empty, that would let the less frequent terms
+ ** generate longer doclists.
+ */
+ if( iDocid<=v->iPrevDocid || v->nPendingData>kPendingThreshold ){
+ int rc = flushPendingTerms(v);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ if( v->nPendingData<0 ){
+ fts3HashInit(&v->pendingTerms, FTS3_HASH_STRING, 1);
+ v->nPendingData = 0;
+ }
+ v->iPrevDocid = iDocid;
+ return SQLITE_OK;
+}
+
+/* This function implements the xUpdate callback; it is the top-level entry
+ * point for inserting, deleting or updating a row in a full-text table. */
+static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
+ sqlite_int64 *pRowid){
+ fulltext_vtab *v = (fulltext_vtab *) pVtab;
+ int rc;
+
+ FTSTRACE(("FTS3 Update %p\n", pVtab));
+
+ if( nArg<2 ){
+ rc = index_delete(v, sqlite3_value_int64(ppArg[0]));
+ if( rc==SQLITE_OK ){
+ /* If we just deleted the last row in the table, clear out the
+ ** index data.
+ */
+ rc = content_exists(v);
+ if( rc==SQLITE_ROW ){
+ rc = SQLITE_OK;
+ }else if( rc==SQLITE_DONE ){
+ /* Clear the pending terms so we don't flush a useless level-0
+ ** segment when the transaction closes.
+ */
+ rc = clearPendingTerms(v);
+ if( rc==SQLITE_OK ){
+ rc = segdir_delete_all(v);
+ }
+ }
+ }
+ } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
+ /* An update:
+ * ppArg[0] = old rowid
+ * ppArg[1] = new rowid
+ * ppArg[2..2+v->nColumn-1] = values
+ * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+ * ppArg[2+v->nColumn+1] = value for docid
+ */
+ sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]);
+ if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER ||
+ sqlite3_value_int64(ppArg[1]) != rowid ){
+ rc = SQLITE_ERROR; /* we don't allow changing the rowid */
+ }else if( sqlite3_value_type(ppArg[2+v->nColumn+1]) != SQLITE_INTEGER ||
+ sqlite3_value_int64(ppArg[2+v->nColumn+1]) != rowid ){
+ rc = SQLITE_ERROR; /* we don't allow changing the docid */
+ }else{
+ assert( nArg==2+v->nColumn+2);
+ rc = index_update(v, rowid, &ppArg[2]);
+ }
+ } else {
+ /* An insert:
+ * ppArg[1] = requested rowid
+ * ppArg[2..2+v->nColumn-1] = values
+ * ppArg[2+v->nColumn] = value for magic column (we ignore this)
+ * ppArg[2+v->nColumn+1] = value for docid
+ */
+ sqlite3_value *pRequestDocid = ppArg[2+v->nColumn+1];
+ assert( nArg==2+v->nColumn+2);
+ if( SQLITE_NULL != sqlite3_value_type(pRequestDocid) &&
+ SQLITE_NULL != sqlite3_value_type(ppArg[1]) ){
+ /* TODO(shess) Consider allowing this to work if the values are
+ ** identical. I'm inclined to discourage that usage, though,
+ ** given that both rowid and docid are special columns. Better
+ ** would be to define one or the other as the default winner,
+ ** but should it be fts3-centric (docid) or SQLite-centric
+ ** (rowid)?
+ */
+ rc = SQLITE_ERROR;
+ }else{
+ if( SQLITE_NULL == sqlite3_value_type(pRequestDocid) ){
+ pRequestDocid = ppArg[1];
+ }
+ rc = index_insert(v, pRequestDocid, &ppArg[2], pRowid);
+ }
+ }
+
+ return rc;
+}
+
+static int fulltextSync(sqlite3_vtab *pVtab){
+ FTSTRACE(("FTS3 xSync()\n"));
+ return flushPendingTerms((fulltext_vtab *)pVtab);
+}
+
+static int fulltextBegin(sqlite3_vtab *pVtab){
+ fulltext_vtab *v = (fulltext_vtab *) pVtab;
+ FTSTRACE(("FTS3 xBegin()\n"));
+
+ /* Any buffered updates should have been cleared by the previous
+ ** transaction.
+ */
+ assert( v->nPendingData<0 );
+ return clearPendingTerms(v);
+}
+
+static int fulltextCommit(sqlite3_vtab *pVtab){
+ fulltext_vtab *v = (fulltext_vtab *) pVtab;
+ FTSTRACE(("FTS3 xCommit()\n"));
+
+ /* Buffered updates should have been cleared by fulltextSync(). */
+ assert( v->nPendingData<0 );
+ return clearPendingTerms(v);
+}
+
+static int fulltextRollback(sqlite3_vtab *pVtab){
+ FTSTRACE(("FTS3 xRollback()\n"));
+ return clearPendingTerms((fulltext_vtab *)pVtab);
+}
+
+/*
+** Implementation of the snippet() function for FTS3
+*/
+static void snippetFunc(
+ sqlite3_context *pContext,
+ int argc,
+ sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc<1 ) return;
+ if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1);
+ }else{
+ const char *zStart = "<b>";
+ const char *zEnd = "</b>";
+ const char *zEllipsis = "<b>...</b>";
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ if( argc>=2 ){
+ zStart = (const char*)sqlite3_value_text(argv[1]);
+ if( argc>=3 ){
+ zEnd = (const char*)sqlite3_value_text(argv[2]);
+ if( argc>=4 ){
+ zEllipsis = (const char*)sqlite3_value_text(argv[3]);
+ }
+ }
+ }
+ snippetAllOffsets(pCursor);
+ snippetText(pCursor, zStart, zEnd, zEllipsis);
+ sqlite3_result_text(pContext, pCursor->snippet.zSnippet,
+ pCursor->snippet.nSnippet, SQLITE_STATIC);
+ }
+}
+
+/*
+** Implementation of the offsets() function for FTS3
+*/
+static void snippetOffsetsFunc(
+ sqlite3_context *pContext,
+ int argc,
+ sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc<1 ) return;
+ if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ sqlite3_result_error(pContext, "illegal first argument to offsets",-1);
+ }else{
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ snippetAllOffsets(pCursor);
+ snippetOffsetText(&pCursor->snippet);
+ sqlite3_result_text(pContext,
+ pCursor->snippet.zOffset, pCursor->snippet.nOffset,
+ SQLITE_STATIC);
+ }
+}
+
+/* OptLeavesReader is nearly identical to LeavesReader, except that
+** where LeavesReader is geared towards the merging of complete
+** segment levels (with exactly MERGE_COUNT segments), OptLeavesReader
+** is geared towards implementation of the optimize() function, and
+** can merge all segments simultaneously. This version may be
+** somewhat less efficient than LeavesReader because it merges into an
+** accumulator rather than doing an N-way merge, but since segment
+** size grows exponentially (so segment count logrithmically) this is
+** probably not an immediate problem.
+*/
+/* TODO(shess): Prove that assertion, or extend the merge code to
+** merge tree fashion (like the prefix-searching code does).
+*/
+/* TODO(shess): OptLeavesReader and LeavesReader could probably be
+** merged with little or no loss of performance for LeavesReader. The
+** merged code would need to handle >MERGE_COUNT segments, and would
+** also need to be able to optionally optimize away deletes.
+*/
+typedef struct OptLeavesReader {
+ /* Segment number, to order readers by age. */
+ int segment;
+ LeavesReader reader;
+} OptLeavesReader;
+
+static int optLeavesReaderAtEnd(OptLeavesReader *pReader){
+ return leavesReaderAtEnd(&pReader->reader);
+}
+static int optLeavesReaderTermBytes(OptLeavesReader *pReader){
+ return leavesReaderTermBytes(&pReader->reader);
+}
+static const char *optLeavesReaderData(OptLeavesReader *pReader){
+ return leavesReaderData(&pReader->reader);
+}
+static int optLeavesReaderDataBytes(OptLeavesReader *pReader){
+ return leavesReaderDataBytes(&pReader->reader);
+}
+static const char *optLeavesReaderTerm(OptLeavesReader *pReader){
+ return leavesReaderTerm(&pReader->reader);
+}
+static int optLeavesReaderStep(fulltext_vtab *v, OptLeavesReader *pReader){
+ return leavesReaderStep(v, &pReader->reader);
+}
+static int optLeavesReaderTermCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
+ return leavesReaderTermCmp(&lr1->reader, &lr2->reader);
+}
+/* Order by term ascending, segment ascending (oldest to newest), with
+** exhausted readers to the end.
+*/
+static int optLeavesReaderCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
+ int c = optLeavesReaderTermCmp(lr1, lr2);
+ if( c!=0 ) return c;
+ return lr1->segment-lr2->segment;
+}
+/* Bubble pLr[0] to appropriate place in pLr[1..nLr-1]. Assumes that
+** pLr[1..nLr-1] is already sorted.
+*/
+static void optLeavesReaderReorder(OptLeavesReader *pLr, int nLr){
+ while( nLr>1 && optLeavesReaderCmp(pLr, pLr+1)>0 ){
+ OptLeavesReader tmp = pLr[0];
+ pLr[0] = pLr[1];
+ pLr[1] = tmp;
+ nLr--;
+ pLr++;
+ }
+}
+
+/* optimize() helper function. Put the readers in order and iterate
+** through them, merging doclists for matching terms into pWriter.
+** Returns SQLITE_OK on success, or the SQLite error code which
+** prevented success.
+*/
+static int optimizeInternal(fulltext_vtab *v,
+ OptLeavesReader *readers, int nReaders,
+ LeafWriter *pWriter){
+ int i, rc = SQLITE_OK;
+ DataBuffer doclist, merged, tmp;
+
+ /* Order the readers. */
+ i = nReaders;
+ while( i-- > 0 ){
+ optLeavesReaderReorder(&readers[i], nReaders-i);
+ }
+
+ dataBufferInit(&doclist, LEAF_MAX);
+ dataBufferInit(&merged, LEAF_MAX);
+
+ /* Exhausted readers bubble to the end, so when the first reader is
+ ** at eof, all are at eof.
+ */
+ while( !optLeavesReaderAtEnd(&readers[0]) ){
+
+ /* Figure out how many readers share the next term. */
+ for(i=1; i<nReaders && !optLeavesReaderAtEnd(&readers[i]); i++){
+ if( 0!=optLeavesReaderTermCmp(&readers[0], &readers[i]) ) break;
+ }
+
+ /* Special-case for no merge. */
+ if( i==1 ){
+ /* Trim deletions from the doclist. */
+ dataBufferReset(&merged);
+ docListTrim(DL_DEFAULT,
+ optLeavesReaderData(&readers[0]),
+ optLeavesReaderDataBytes(&readers[0]),
+ -1, DL_DEFAULT, &merged);
+ }else{
+ DLReader dlReaders[MERGE_COUNT];
+ int iReader, nReaders;
+
+ /* Prime the pipeline with the first reader's doclist. After
+ ** one pass index 0 will reference the accumulated doclist.
+ */
+ dlrInit(&dlReaders[0], DL_DEFAULT,
+ optLeavesReaderData(&readers[0]),
+ optLeavesReaderDataBytes(&readers[0]));
+ iReader = 1;
+
+ assert( iReader<i ); /* Must execute the loop at least once. */
+ while( iReader<i ){
+ /* Merge 16 inputs per pass. */
+ for( nReaders=1; iReader<i && nReaders<MERGE_COUNT;
+ iReader++, nReaders++ ){
+ dlrInit(&dlReaders[nReaders], DL_DEFAULT,
+ optLeavesReaderData(&readers[iReader]),
+ optLeavesReaderDataBytes(&readers[iReader]));
+ }
+
+ /* Merge doclists and swap result into accumulator. */
+ dataBufferReset(&merged);
+ docListMerge(&merged, dlReaders, nReaders);
+ tmp = merged;
+ merged = doclist;
+ doclist = tmp;
+
+ while( nReaders-- > 0 ){
+ dlrDestroy(&dlReaders[nReaders]);
+ }
+
+ /* Accumulated doclist to reader 0 for next pass. */
+ dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData);
+ }
+
+ /* Destroy reader that was left in the pipeline. */
+ dlrDestroy(&dlReaders[0]);
+
+ /* Trim deletions from the doclist. */
+ dataBufferReset(&merged);
+ docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
+ -1, DL_DEFAULT, &merged);
+ }
+
+ /* Only pass doclists with hits (skip if all hits deleted). */
+ if( merged.nData>0 ){
+ rc = leafWriterStep(v, pWriter,
+ optLeavesReaderTerm(&readers[0]),
+ optLeavesReaderTermBytes(&readers[0]),
+ merged.pData, merged.nData);
+ if( rc!=SQLITE_OK ) goto err;
+ }
+
+ /* Step merged readers to next term and reorder. */
+ while( i-- > 0 ){
+ rc = optLeavesReaderStep(v, &readers[i]);
+ if( rc!=SQLITE_OK ) goto err;
+
+ optLeavesReaderReorder(&readers[i], nReaders-i);
+ }
+ }
+
+ err:
+ dataBufferDestroy(&doclist);
+ dataBufferDestroy(&merged);
+ return rc;
+}
+
+/* Implement optimize() function for FTS3. optimize(t) merges all
+** segments in the fts index into a single segment. 't' is the magic
+** table-named column.
+*/
+static void optimizeFunc(sqlite3_context *pContext,
+ int argc, sqlite3_value **argv){
+ fulltext_cursor *pCursor;
+ if( argc>1 ){
+ sqlite3_result_error(pContext, "excess arguments to optimize()",-1);
+ }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ sqlite3_result_error(pContext, "illegal first argument to optimize",-1);
+ }else{
+ fulltext_vtab *v;
+ int i, rc, iMaxLevel;
+ OptLeavesReader *readers;
+ int nReaders;
+ LeafWriter writer;
+ sqlite3_stmt *s;
+
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ v = cursor_vtab(pCursor);
+
+ /* Flush any buffered updates before optimizing. */
+ rc = flushPendingTerms(v);
+ if( rc!=SQLITE_OK ) goto err;
+
+ rc = segdir_count(v, &nReaders, &iMaxLevel);
+ if( rc!=SQLITE_OK ) goto err;
+ if( nReaders==0 || nReaders==1 ){
+ sqlite3_result_text(pContext, "Index already optimal", -1,
+ SQLITE_STATIC);
+ return;
+ }
+
+ rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+ if( rc!=SQLITE_OK ) goto err;
+
+ readers = sqlite3_malloc(nReaders*sizeof(readers[0]));
+ if( readers==NULL ) goto err;
+
+ /* Note that there will already be a segment at this position
+ ** until we call segdir_delete() on iMaxLevel.
+ */
+ leafWriterInit(iMaxLevel, 0, &writer);
+
+ i = 0;
+ while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+ sqlite_int64 iStart = sqlite3_column_int64(s, 0);
+ sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
+ const char *pRootData = sqlite3_column_blob(s, 2);
+ int nRootData = sqlite3_column_bytes(s, 2);
+
+ assert( i<nReaders );
+ rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData,
+ &readers[i].reader);
+ if( rc!=SQLITE_OK ) break;
+
+ readers[i].segment = i;
+ i++;
+ }
+
+ /* If we managed to succesfully read them all, optimize them. */
+ if( rc==SQLITE_DONE ){
+ assert( i==nReaders );
+ rc = optimizeInternal(v, readers, nReaders, &writer);
+ }
+
+ while( i-- > 0 ){
+ leavesReaderDestroy(&readers[i].reader);
+ }
+ sqlite3_free(readers);
+
+ /* If we've successfully gotten to here, delete the old segments
+ ** and flush the interior structure of the new segment.
+ */
+ if( rc==SQLITE_OK ){
+ for( i=0; i<=iMaxLevel; i++ ){
+ rc = segdir_delete(v, i);
+ if( rc!=SQLITE_OK ) break;
+ }
+
+ if( rc==SQLITE_OK ) rc = leafWriterFinalize(v, &writer);
+ }
+
+ leafWriterDestroy(&writer);
+
+ if( rc!=SQLITE_OK ) goto err;
+
+ sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC);
+ return;
+
+ /* TODO(shess): Error-handling needs to be improved along the
+ ** lines of the dump_ functions.
+ */
+ err:
+ {
+ char buf[512];
+ sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s",
+ sqlite3_errmsg(sqlite3_context_db_handle(pContext)));
+ sqlite3_result_error(pContext, buf, -1);
+ }
+ }
+}
+
+#ifdef SQLITE_TEST
+/* Generate an error of the form "<prefix>: <msg>". If msg is NULL,
+** pull the error from the context's db handle.
+*/
+static void generateError(sqlite3_context *pContext,
+ const char *prefix, const char *msg){
+ char buf[512];
+ if( msg==NULL ) msg = sqlite3_errmsg(sqlite3_context_db_handle(pContext));
+ sqlite3_snprintf(sizeof(buf), buf, "%s: %s", prefix, msg);
+ sqlite3_result_error(pContext, buf, -1);
+}
+
+/* Helper function to collect the set of terms in the segment into
+** pTerms. The segment is defined by the leaf nodes between
+** iStartBlockid and iEndBlockid, inclusive, or by the contents of
+** pRootData if iStartBlockid is 0 (in which case the entire segment
+** fit in a leaf).
+*/
+static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s,
+ fts3Hash *pTerms){
+ const sqlite_int64 iStartBlockid = sqlite3_column_int64(s, 0);
+ const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1);
+ const char *pRootData = sqlite3_column_blob(s, 2);
+ const int nRootData = sqlite3_column_bytes(s, 2);
+ LeavesReader reader;
+ int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid,
+ pRootData, nRootData, &reader);
+ if( rc!=SQLITE_OK ) return rc;
+
+ while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){
+ const char *pTerm = leavesReaderTerm(&reader);
+ const int nTerm = leavesReaderTermBytes(&reader);
+ void *oldValue = sqlite3Fts3HashFind(pTerms, pTerm, nTerm);
+ void *newValue = (void *)((char *)oldValue+1);
+
+ /* From the comment before sqlite3Fts3HashInsert in fts3_hash.c,
+ ** the data value passed is returned in case of malloc failure.
+ */
+ if( newValue==sqlite3Fts3HashInsert(pTerms, pTerm, nTerm, newValue) ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = leavesReaderStep(v, &reader);
+ }
+ }
+
+ leavesReaderDestroy(&reader);
+ return rc;
+}
+
+/* Helper function to build the result string for dump_terms(). */
+static int generateTermsResult(sqlite3_context *pContext, fts3Hash *pTerms){
+ int iTerm, nTerms, nResultBytes, iByte;
+ char *result;
+ TermData *pData;
+ fts3HashElem *e;
+
+ /* Iterate pTerms to generate an array of terms in pData for
+ ** sorting.
+ */
+ nTerms = fts3HashCount(pTerms);
+ assert( nTerms>0 );
+ pData = sqlite3_malloc(nTerms*sizeof(TermData));
+ if( pData==NULL ) return SQLITE_NOMEM;
+
+ nResultBytes = 0;
+ for(iTerm = 0, e = fts3HashFirst(pTerms); e; iTerm++, e = fts3HashNext(e)){
+ nResultBytes += fts3HashKeysize(e)+1; /* Term plus trailing space */
+ assert( iTerm<nTerms );
+ pData[iTerm].pTerm = fts3HashKey(e);
+ pData[iTerm].nTerm = fts3HashKeysize(e);
+ pData[iTerm].pCollector = fts3HashData(e); /* unused */
+ }
+ assert( iTerm==nTerms );
+
+ assert( nResultBytes>0 ); /* nTerms>0, nResultsBytes must be, too. */
+ result = sqlite3_malloc(nResultBytes);
+ if( result==NULL ){
+ sqlite3_free(pData);
+ return SQLITE_NOMEM;
+ }
+
+ if( nTerms>1 ) qsort(pData, nTerms, sizeof(*pData), termDataCmp);
+
+ /* Read the terms in order to build the result. */
+ iByte = 0;
+ for(iTerm=0; iTerm<nTerms; ++iTerm){
+ memcpy(result+iByte, pData[iTerm].pTerm, pData[iTerm].nTerm);
+ iByte += pData[iTerm].nTerm;
+ result[iByte++] = ' ';
+ }
+ assert( iByte==nResultBytes );
+ assert( result[nResultBytes-1]==' ' );
+ result[nResultBytes-1] = '\0';
+
+ /* Passes away ownership of result. */
+ sqlite3_result_text(pContext, result, nResultBytes-1, sqlite3_free);
+ sqlite3_free(pData);
+ return SQLITE_OK;
+}
+
+/* Implements dump_terms() for use in inspecting the fts3 index from
+** tests. TEXT result containing the ordered list of terms joined by
+** spaces. dump_terms(t, level, idx) dumps the terms for the segment
+** specified by level, idx (in %_segdir), while dump_terms(t) dumps
+** all terms in the index. In both cases t is the fts table's magic
+** table-named column.
+*/
+static void dumpTermsFunc(
+ sqlite3_context *pContext,
+ int argc, sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc!=3 && argc!=1 ){
+ generateError(pContext, "dump_terms", "incorrect arguments");
+ }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ generateError(pContext, "dump_terms", "illegal first argument");
+ }else{
+ fulltext_vtab *v;
+ fts3Hash terms;
+ sqlite3_stmt *s = NULL;
+ int rc;
+
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ v = cursor_vtab(pCursor);
+
+ /* If passed only the cursor column, get all segments. Otherwise
+ ** get the segment described by the following two arguments.
+ */
+ if( argc==1 ){
+ rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
+ }else{
+ rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[1]));
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[2]));
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ generateError(pContext, "dump_terms", NULL);
+ return;
+ }
+
+ /* Collect the terms for each segment. */
+ sqlite3Fts3HashInit(&terms, FTS3_HASH_STRING, 1);
+ while( (rc = sqlite3_step(s))==SQLITE_ROW ){
+ rc = collectSegmentTerms(v, s, &terms);
+ if( rc!=SQLITE_OK ) break;
+ }
+
+ if( rc!=SQLITE_DONE ){
+ sqlite3_reset(s);
+ generateError(pContext, "dump_terms", NULL);
+ }else{
+ const int nTerms = fts3HashCount(&terms);
+ if( nTerms>0 ){
+ rc = generateTermsResult(pContext, &terms);
+ if( rc==SQLITE_NOMEM ){
+ generateError(pContext, "dump_terms", "out of memory");
+ }else{
+ assert( rc==SQLITE_OK );
+ }
+ }else if( argc==3 ){
+ /* The specific segment asked for could not be found. */
+ generateError(pContext, "dump_terms", "segment not found");
+ }else{
+ /* No segments found. */
+ /* TODO(shess): It should be impossible to reach this. This
+ ** case can only happen for an empty table, in which case
+ ** SQLite has no rows to call this function on.
+ */
+ sqlite3_result_null(pContext);
+ }
+ }
+ sqlite3Fts3HashClear(&terms);
+ }
+}
+
+/* Expand the DL_DEFAULT doclist in pData into a text result in
+** pContext.
+*/
+static void createDoclistResult(sqlite3_context *pContext,
+ const char *pData, int nData){
+ DataBuffer dump;
+ DLReader dlReader;
+
+ assert( pData!=NULL && nData>0 );
+
+ dataBufferInit(&dump, 0);
+ dlrInit(&dlReader, DL_DEFAULT, pData, nData);
+ for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){
+ char buf[256];
+ PLReader plReader;
+
+ plrInit(&plReader, &dlReader);
+ if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){
+ sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader));
+ dataBufferAppend(&dump, buf, strlen(buf));
+ }else{
+ int iColumn = plrColumn(&plReader);
+
+ sqlite3_snprintf(sizeof(buf), buf, "[%lld %d[",
+ dlrDocid(&dlReader), iColumn);
+ dataBufferAppend(&dump, buf, strlen(buf));
+
+ for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){
+ if( plrColumn(&plReader)!=iColumn ){
+ iColumn = plrColumn(&plReader);
+ sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn);
+ assert( dump.nData>0 );
+ dump.nData--; /* Overwrite trailing space. */
+ assert( dump.pData[dump.nData]==' ');
+ dataBufferAppend(&dump, buf, strlen(buf));
+ }
+ if( DL_DEFAULT==DL_POSITIONS_OFFSETS ){
+ sqlite3_snprintf(sizeof(buf), buf, "%d,%d,%d ",
+ plrPosition(&plReader),
+ plrStartOffset(&plReader), plrEndOffset(&plReader));
+ }else if( DL_DEFAULT==DL_POSITIONS ){
+ sqlite3_snprintf(sizeof(buf), buf, "%d ", plrPosition(&plReader));
+ }else{
+ assert( NULL=="Unhandled DL_DEFAULT value");
+ }
+ dataBufferAppend(&dump, buf, strlen(buf));
+ }
+ plrDestroy(&plReader);
+
+ assert( dump.nData>0 );
+ dump.nData--; /* Overwrite trailing space. */
+ assert( dump.pData[dump.nData]==' ');
+ dataBufferAppend(&dump, "]] ", 3);
+ }
+ }
+ dlrDestroy(&dlReader);
+
+ assert( dump.nData>0 );
+ dump.nData--; /* Overwrite trailing space. */
+ assert( dump.pData[dump.nData]==' ');
+ dump.pData[dump.nData] = '\0';
+ assert( dump.nData>0 );
+
+ /* Passes ownership of dump's buffer to pContext. */
+ sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free);
+ dump.pData = NULL;
+ dump.nData = dump.nCapacity = 0;
+}
+
+/* Implements dump_doclist() for use in inspecting the fts3 index from
+** tests. TEXT result containing a string representation of the
+** doclist for the indicated term. dump_doclist(t, term, level, idx)
+** dumps the doclist for term from the segment specified by level, idx
+** (in %_segdir), while dump_doclist(t, term) dumps the logical
+** doclist for the term across all segments. The per-segment doclist
+** can contain deletions, while the full-index doclist will not
+** (deletions are omitted).
+**
+** Result formats differ with the setting of DL_DEFAULTS. Examples:
+**
+** DL_DOCIDS: [1] [3] [7]
+** DL_POSITIONS: [1 0[0 4] 1[17]] [3 1[5]]
+** DL_POSITIONS_OFFSETS: [1 0[0,0,3 4,23,26] 1[17,102,105]] [3 1[5,20,23]]
+**
+** In each case the number after the outer '[' is the docid. In the
+** latter two cases, the number before the inner '[' is the column
+** associated with the values within. For DL_POSITIONS the numbers
+** within are the positions, for DL_POSITIONS_OFFSETS they are the
+** position, the start offset, and the end offset.
+*/
+static void dumpDoclistFunc(
+ sqlite3_context *pContext,
+ int argc, sqlite3_value **argv
+){
+ fulltext_cursor *pCursor;
+ if( argc!=2 && argc!=4 ){
+ generateError(pContext, "dump_doclist", "incorrect arguments");
+ }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
+ sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
+ generateError(pContext, "dump_doclist", "illegal first argument");
+ }else if( sqlite3_value_text(argv[1])==NULL ||
+ sqlite3_value_text(argv[1])[0]=='\0' ){
+ generateError(pContext, "dump_doclist", "empty second argument");
+ }else{
+ const char *pTerm = (const char *)sqlite3_value_text(argv[1]);
+ const int nTerm = strlen(pTerm);
+ fulltext_vtab *v;
+ int rc;
+ DataBuffer doclist;
+
+ memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
+ v = cursor_vtab(pCursor);
+
+ dataBufferInit(&doclist, 0);
+
+ /* termSelect() yields the same logical doclist that queries are
+ ** run against.
+ */
+ if( argc==2 ){
+ rc = termSelect(v, v->nColumn, pTerm, nTerm, 0, DL_DEFAULT, &doclist);
+ }else{
+ sqlite3_stmt *s = NULL;
+
+ /* Get our specific segment's information. */
+ rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[2]));
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[3]));
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(s);
+
+ if( rc==SQLITE_DONE ){
+ dataBufferDestroy(&doclist);
+ generateError(pContext, "dump_doclist", "segment not found");
+ return;
+ }
+
+ /* Found a segment, load it into doclist. */
+ if( rc==SQLITE_ROW ){
+ const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
+ const char *pData = sqlite3_column_blob(s, 2);
+ const int nData = sqlite3_column_bytes(s, 2);
+
+ /* loadSegment() is used by termSelect() to load each
+ ** segment's data.
+ */
+ rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, 0,
+ &doclist);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(s);
+
+ /* Should not have more than one matching segment. */
+ if( rc!=SQLITE_DONE ){
+ sqlite3_reset(s);
+ dataBufferDestroy(&doclist);
+ generateError(pContext, "dump_doclist", "invalid segdir");
+ return;
+ }
+ rc = SQLITE_OK;
+ }
+ }
+ }
+
+ sqlite3_reset(s);
+ }
+
+ if( rc==SQLITE_OK ){
+ if( doclist.nData>0 ){
+ createDoclistResult(pContext, doclist.pData, doclist.nData);
+ }else{
+ /* TODO(shess): This can happen if the term is not present, or
+ ** if all instances of the term have been deleted and this is
+ ** an all-index dump. It may be interesting to distinguish
+ ** these cases.
+ */
+ sqlite3_result_text(pContext, "", 0, SQLITE_STATIC);
+ }
+ }else if( rc==SQLITE_NOMEM ){
+ /* Handle out-of-memory cases specially because if they are
+ ** generated in fts3 code they may not be reflected in the db
+ ** handle.
+ */
+ /* TODO(shess): Handle this more comprehensively.
+ ** sqlite3ErrStr() has what I need, but is internal.
+ */
+ generateError(pContext, "dump_doclist", "out of memory");
+ }else{
+ generateError(pContext, "dump_doclist", NULL);
+ }
+
+ dataBufferDestroy(&doclist);
+ }
+}
+#endif
+
+/*
+** This routine implements the xFindFunction method for the FTS3
+** virtual table.
+*/
+static int fulltextFindFunction(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ if( strcmp(zName,"snippet")==0 ){
+ *pxFunc = snippetFunc;
+ return 1;
+ }else if( strcmp(zName,"offsets")==0 ){
+ *pxFunc = snippetOffsetsFunc;
+ return 1;
+ }else if( strcmp(zName,"optimize")==0 ){
+ *pxFunc = optimizeFunc;
+ return 1;
+#ifdef SQLITE_TEST
+ /* NOTE(shess): These functions are present only for testing
+ ** purposes. No particular effort is made to optimize their
+ ** execution or how they build their results.
+ */
+ }else if( strcmp(zName,"dump_terms")==0 ){
+ /* fprintf(stderr, "Found dump_terms\n"); */
+ *pxFunc = dumpTermsFunc;
+ return 1;
+ }else if( strcmp(zName,"dump_doclist")==0 ){
+ /* fprintf(stderr, "Found dump_doclist\n"); */
+ *pxFunc = dumpDoclistFunc;
+ return 1;
+#endif
+ }
+ return 0;
+}
+
+/*
+** Rename an fts3 table.
+*/
+static int fulltextRename(
+ sqlite3_vtab *pVtab,
+ const char *zName
+){
+ fulltext_vtab *p = (fulltext_vtab *)pVtab;
+ int rc = SQLITE_NOMEM;
+ char *zSql = sqlite3_mprintf(
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';"
+ "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';"
+ "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';"
+ , p->zDb, p->zName, zName
+ , p->zDb, p->zName, zName
+ , p->zDb, p->zName, zName
+ );
+ if( zSql ){
+ rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+ return rc;
+}
+
+static const sqlite3_module fts3Module = {
+ /* iVersion */ 0,
+ /* xCreate */ fulltextCreate,
+ /* xConnect */ fulltextConnect,
+ /* xBestIndex */ fulltextBestIndex,
+ /* xDisconnect */ fulltextDisconnect,
+ /* xDestroy */ fulltextDestroy,
+ /* xOpen */ fulltextOpen,
+ /* xClose */ fulltextClose,
+ /* xFilter */ fulltextFilter,
+ /* xNext */ fulltextNext,
+ /* xEof */ fulltextEof,
+ /* xColumn */ fulltextColumn,
+ /* xRowid */ fulltextRowid,
+ /* xUpdate */ fulltextUpdate,
+ /* xBegin */ fulltextBegin,
+ /* xSync */ fulltextSync,
+ /* xCommit */ fulltextCommit,
+ /* xRollback */ fulltextRollback,
+ /* xFindFunction */ fulltextFindFunction,
+ /* xRename */ fulltextRename,
+};
+
+static void hashDestroy(void *p){
+ fts3Hash *pHash = (fts3Hash *)p;
+ sqlite3Fts3HashClear(pHash);
+ sqlite3_free(pHash);
+}
+
+/*
+** The fts3 built-in tokenizers - "simple" and "porter" - are implemented
+** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following
+** two forward declarations are for functions declared in these files
+** used to retrieve the respective implementations.
+**
+** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
+** to by the argument to point a the "simple" tokenizer implementation.
+** Function ...PorterTokenizerModule() sets *pModule to point to the
+** porter tokenizer/stemmer implementation.
+*/
+void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+
+int sqlite3Fts3InitHashTable(sqlite3 *, fts3Hash *, const char *);
+
+/*
+** Initialise the fts3 extension. If this extension is built as part
+** of the sqlite library, then this function is called directly by
+** SQLite. If fts3 is built as a dynamically loadable extension, this
+** function is called by the sqlite3_extension_init() entry point.
+*/
+int sqlite3Fts3Init(sqlite3 *db){
+ int rc = SQLITE_OK;
+ fts3Hash *pHash = 0;
+ const sqlite3_tokenizer_module *pSimple = 0;
+ const sqlite3_tokenizer_module *pPorter = 0;
+ const sqlite3_tokenizer_module *pIcu = 0;
+
+ sqlite3Fts3SimpleTokenizerModule(&pSimple);
+ sqlite3Fts3PorterTokenizerModule(&pPorter);
+#ifdef SQLITE_ENABLE_ICU
+ sqlite3Fts3IcuTokenizerModule(&pIcu);
+#endif
+
+ /* Allocate and initialise the hash-table used to store tokenizers. */
+ pHash = sqlite3_malloc(sizeof(fts3Hash));
+ if( !pHash ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
+ }
+
+ /* Load the built-in tokenizers into the hash table */
+ if( rc==SQLITE_OK ){
+ if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
+ || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
+ || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
+ ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ /* Create the virtual table wrapper around the hash-table and overload
+ ** the two scalar functions. If this is successful, register the
+ ** module with sqlite.
+ */
+ if( SQLITE_OK==rc
+ && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer"))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1))
+#ifdef SQLITE_TEST
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_terms", -1))
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_doclist", -1))
+#endif
+ ){
+ return sqlite3_create_module_v2(
+ db, "fts3", &fts3Module, (void *)pHash, hashDestroy
+ );
+ }
+
+ /* An error has occured. Delete the hash table and return the error code. */
+ assert( rc!=SQLITE_OK );
+ if( pHash ){
+ sqlite3Fts3HashClear(pHash);
+ sqlite3_free(pHash);
+ }
+ return rc;
+}
+
+#if !SQLITE_CORE
+int sqlite3_extension_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi)
+ return sqlite3Fts3Init(db);
+}
+#endif
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/fts3.h b/third_party/sqlite/ext/fts3/fts3.h
new file mode 100755
index 0000000..c1aa8ca
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3.h
@@ -0,0 +1,26 @@
+/*
+** 2006 Oct 10
+**
+** 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 header file is used by programs that want to link against the
+** FTS3 library. All it does is declare the sqlite3Fts3Init() interface.
+*/
+#include "sqlite3.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int sqlite3Fts3Init(sqlite3 *db);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
diff --git a/third_party/sqlite/ext/fts3/fts3_hash.c b/third_party/sqlite/ext/fts3/fts3_hash.c
new file mode 100755
index 0000000..64bd5d0
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_hash.c
@@ -0,0 +1,374 @@
+/*
+** 2001 September 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 is the implementation of generic hash-tables used in SQLite.
+** We've modified it slightly to serve as a standalone hash table
+** implementation for the full-text indexing module.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS3 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS3 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sqlite3.h"
+#include "fts3_hash.h"
+
+/*
+** Malloc and Free functions
+*/
+static void *fts3HashMalloc(int n){
+ void *p = sqlite3_malloc(n);
+ if( p ){
+ memset(p, 0, n);
+ }
+ return p;
+}
+static void fts3HashFree(void *p){
+ sqlite3_free(p);
+}
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+**
+** "pNew" is a pointer to the hash table that is to be initialized.
+** keyClass is one of the constants
+** FTS3_HASH_BINARY or FTS3_HASH_STRING. The value of keyClass
+** determines what kind of key the hash table will use. "copyKey" is
+** true if the hash table should make its own private copy of keys and
+** false if it should just use the supplied pointer.
+*/
+void sqlite3Fts3HashInit(fts3Hash *pNew, int keyClass, int copyKey){
+ assert( pNew!=0 );
+ assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY );
+ pNew->keyClass = keyClass;
+ pNew->copyKey = copyKey;
+ pNew->first = 0;
+ pNew->count = 0;
+ pNew->htsize = 0;
+ pNew->ht = 0;
+}
+
+/* Remove all entries from a hash table. Reclaim all memory.
+** Call this routine to delete a hash table or to reset a hash table
+** to the empty state.
+*/
+void sqlite3Fts3HashClear(fts3Hash *pH){
+ fts3HashElem *elem; /* For looping over all elements of the table */
+
+ assert( pH!=0 );
+ elem = pH->first;
+ pH->first = 0;
+ fts3HashFree(pH->ht);
+ pH->ht = 0;
+ pH->htsize = 0;
+ while( elem ){
+ fts3HashElem *next_elem = elem->next;
+ if( pH->copyKey && elem->pKey ){
+ fts3HashFree(elem->pKey);
+ }
+ fts3HashFree(elem);
+ elem = next_elem;
+ }
+ pH->count = 0;
+}
+
+/*
+** Hash and comparison functions when the mode is FTS3_HASH_STRING
+*/
+static int fts3StrHash(const void *pKey, int nKey){
+ const char *z = (const char *)pKey;
+ int h = 0;
+ if( nKey<=0 ) nKey = (int) strlen(z);
+ while( nKey > 0 ){
+ h = (h<<3) ^ h ^ *z++;
+ nKey--;
+ }
+ return h & 0x7fffffff;
+}
+static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return strncmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is FTS3_HASH_BINARY
+*/
+static int fts3BinHash(const void *pKey, int nKey){
+ int h = 0;
+ const char *z = (const char *)pKey;
+ while( nKey-- > 0 ){
+ h = (h<<3) ^ h ^ *(z++);
+ }
+ return h & 0x7fffffff;
+}
+static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** The C syntax in this function definition may be unfamilar to some
+** programmers, so we provide the following additional explanation:
+**
+** The name of the function is "ftsHashFunction". The function takes a
+** single parameter "keyClass". The return value of ftsHashFunction()
+** is a pointer to another function. Specifically, the return value
+** of ftsHashFunction() is a pointer to a function that takes two parameters
+** with types "const void*" and "int" and returns an "int".
+*/
+static int (*ftsHashFunction(int keyClass))(const void*,int){
+ if( keyClass==FTS3_HASH_STRING ){
+ return &fts3StrHash;
+ }else{
+ assert( keyClass==FTS3_HASH_BINARY );
+ return &fts3BinHash;
+ }
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** For help in interpreted the obscure C code in the function definition,
+** see the header comment on the previous function.
+*/
+static int (*ftsCompareFunction(int keyClass))(const void*,int,const void*,int){
+ if( keyClass==FTS3_HASH_STRING ){
+ return &fts3StrCompare;
+ }else{
+ assert( keyClass==FTS3_HASH_BINARY );
+ return &fts3BinCompare;
+ }
+}
+
+/* Link an element into the hash table
+*/
+static void fts3HashInsertElement(
+ fts3Hash *pH, /* The complete hash table */
+ struct _fts3ht *pEntry, /* The entry into which pNew is inserted */
+ fts3HashElem *pNew /* The element to be inserted */
+){
+ fts3HashElem *pHead; /* First element already in pEntry */
+ pHead = pEntry->chain;
+ if( pHead ){
+ pNew->next = pHead;
+ pNew->prev = pHead->prev;
+ if( pHead->prev ){ pHead->prev->next = pNew; }
+ else { pH->first = pNew; }
+ pHead->prev = pNew;
+ }else{
+ pNew->next = pH->first;
+ if( pH->first ){ pH->first->prev = pNew; }
+ pNew->prev = 0;
+ pH->first = pNew;
+ }
+ pEntry->count++;
+ pEntry->chain = pNew;
+}
+
+
+/* Resize the hash table so that it cantains "new_size" buckets.
+** "new_size" must be a power of 2. The hash table might fail
+** to resize if sqliteMalloc() fails.
+*/
+static void fts3Rehash(fts3Hash *pH, int new_size){
+ struct _fts3ht *new_ht; /* The new hash table */
+ fts3HashElem *elem, *next_elem; /* For looping over existing elements */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( (new_size & (new_size-1))==0 );
+ new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) );
+ if( new_ht==0 ) return;
+ fts3HashFree(pH->ht);
+ pH->ht = new_ht;
+ pH->htsize = new_size;
+ xHash = ftsHashFunction(pH->keyClass);
+ for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+ int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+ next_elem = elem->next;
+ fts3HashInsertElement(pH, &new_ht[h], elem);
+ }
+}
+
+/* This function (for internal use only) locates an element in an
+** hash table that matches the given key. The hash for this key has
+** already been computed and is passed as the 4th parameter.
+*/
+static fts3HashElem *fts3FindElementByHash(
+ const fts3Hash *pH, /* The pH to be searched */
+ const void *pKey, /* The key we are searching for */
+ int nKey,
+ int h /* The hash for this key. */
+){
+ fts3HashElem *elem; /* Used to loop thru the element list */
+ int count; /* Number of elements left to test */
+ int (*xCompare)(const void*,int,const void*,int); /* comparison function */
+
+ if( pH->ht ){
+ struct _fts3ht *pEntry = &pH->ht[h];
+ elem = pEntry->chain;
+ count = pEntry->count;
+ xCompare = ftsCompareFunction(pH->keyClass);
+ while( count-- && elem ){
+ if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+ return elem;
+ }
+ elem = elem->next;
+ }
+ }
+ return 0;
+}
+
+/* Remove a single entry from the hash table given a pointer to that
+** element and a hash on the element's key.
+*/
+static void fts3RemoveElementByHash(
+ fts3Hash *pH, /* The pH containing "elem" */
+ fts3HashElem* elem, /* The element to be removed from the pH */
+ int h /* Hash value for the element */
+){
+ struct _fts3ht *pEntry;
+ if( elem->prev ){
+ elem->prev->next = elem->next;
+ }else{
+ pH->first = elem->next;
+ }
+ if( elem->next ){
+ elem->next->prev = elem->prev;
+ }
+ pEntry = &pH->ht[h];
+ if( pEntry->chain==elem ){
+ pEntry->chain = elem->next;
+ }
+ pEntry->count--;
+ if( pEntry->count<=0 ){
+ pEntry->chain = 0;
+ }
+ if( pH->copyKey && elem->pKey ){
+ fts3HashFree(elem->pKey);
+ }
+ fts3HashFree( elem );
+ pH->count--;
+ if( pH->count<=0 ){
+ assert( pH->first==0 );
+ assert( pH->count==0 );
+ fts3HashClear(pH);
+ }
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey. Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *sqlite3Fts3HashFind(const fts3Hash *pH, const void *pKey, int nKey){
+ int h; /* A hash on key */
+ fts3HashElem *elem; /* The element that matches key */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ if( pH==0 || pH->ht==0 ) return 0;
+ xHash = ftsHashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ h = (*xHash)(pKey,nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ elem = fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1));
+ return elem ? elem->data : 0;
+}
+
+/* Insert an element into the hash table pH. The key is pKey,nKey
+** and the data is "data".
+**
+** If no element exists with a matching key, then a new
+** element is created. A copy of the key is made if the copyKey
+** flag is set. NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance. If a malloc fails, then
+** the new data is returned and the hash table is unchanged.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the hash table.
+*/
+void *sqlite3Fts3HashInsert(
+ fts3Hash *pH, /* The hash table to insert into */
+ const void *pKey, /* The key */
+ int nKey, /* Number of bytes in the key */
+ void *data /* The data */
+){
+ int hraw; /* Raw hash value of the key */
+ int h; /* the hash of the key modulo hash table size */
+ fts3HashElem *elem; /* Used to loop thru the element list */
+ fts3HashElem *new_elem; /* New element added to the pH */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( pH!=0 );
+ xHash = ftsHashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ hraw = (*xHash)(pKey, nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ elem = fts3FindElementByHash(pH,pKey,nKey,h);
+ if( elem ){
+ void *old_data = elem->data;
+ if( data==0 ){
+ fts3RemoveElementByHash(pH,elem,h);
+ }else{
+ elem->data = data;
+ }
+ return old_data;
+ }
+ if( data==0 ) return 0;
+ new_elem = (fts3HashElem*)fts3HashMalloc( sizeof(fts3HashElem) );
+ if( new_elem==0 ) return data;
+ if( pH->copyKey && pKey!=0 ){
+ new_elem->pKey = fts3HashMalloc( nKey );
+ if( new_elem->pKey==0 ){
+ fts3HashFree(new_elem);
+ return data;
+ }
+ memcpy((void*)new_elem->pKey, pKey, nKey);
+ }else{
+ new_elem->pKey = (void*)pKey;
+ }
+ new_elem->nKey = nKey;
+ pH->count++;
+ if( pH->htsize==0 ){
+ fts3Rehash(pH,8);
+ if( pH->htsize==0 ){
+ pH->count = 0;
+ fts3HashFree(new_elem);
+ return data;
+ }
+ }
+ if( pH->count > pH->htsize ){
+ fts3Rehash(pH,pH->htsize*2);
+ }
+ assert( pH->htsize>0 );
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ fts3HashInsertElement(pH, &pH->ht[h], new_elem);
+ new_elem->data = data;
+ return 0;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/fts3_hash.h b/third_party/sqlite/ext/fts3/fts3_hash.h
new file mode 100755
index 0000000..e01954e
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_hash.h
@@ -0,0 +1,110 @@
+/*
+** 2001 September 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 is the header file for the generic hash-table implemenation
+** used in SQLite. We've modified it slightly to serve as a standalone
+** hash table implementation for the full-text indexing module.
+**
+*/
+#ifndef _FTS3_HASH_H_
+#define _FTS3_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct fts3Hash fts3Hash;
+typedef struct fts3HashElem fts3HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly. Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct fts3Hash {
+ char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
+ char copyKey; /* True if copy of key made on insert */
+ int count; /* Number of entries in this table */
+ fts3HashElem *first; /* The first element of the array */
+ int htsize; /* Number of buckets in the hash table */
+ struct _fts3ht { /* the hash table */
+ int count; /* Number of entries with this hash */
+ fts3HashElem *chain; /* Pointer to first entry with this hash */
+ } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure. All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct fts3HashElem {
+ fts3HashElem *next, *prev; /* Next and previous elements in the table */
+ void *data; /* Data associated with this element */
+ void *pKey; int nKey; /* Key associated with this element */
+};
+
+/*
+** There are 2 different modes of operation for a hash table:
+**
+** FTS3_HASH_STRING pKey points to a string that is nKey bytes long
+** (including the null-terminator, if any). Case
+** is respected in comparisons.
+**
+** FTS3_HASH_BINARY pKey points to binary data nKey bytes long.
+** memcmp() is used to compare keys.
+**
+** A copy of the key is made if the copyKey parameter to fts3HashInit is 1.
+*/
+#define FTS3_HASH_STRING 1
+#define FTS3_HASH_BINARY 2
+
+/*
+** Access routines. To delete, insert a NULL pointer.
+*/
+void sqlite3Fts3HashInit(fts3Hash*, int keytype, int copyKey);
+void *sqlite3Fts3HashInsert(fts3Hash*, const void *pKey, int nKey, void *pData);
+void *sqlite3Fts3HashFind(const fts3Hash*, const void *pKey, int nKey);
+void sqlite3Fts3HashClear(fts3Hash*);
+
+/*
+** Shorthand for the functions above
+*/
+#define fts3HashInit sqlite3Fts3HashInit
+#define fts3HashInsert sqlite3Fts3HashInsert
+#define fts3HashFind sqlite3Fts3HashFind
+#define fts3HashClear sqlite3Fts3HashClear
+
+/*
+** Macros for looping over all elements of a hash table. The idiom is
+** like this:
+**
+** fts3Hash h;
+** fts3HashElem *p;
+** ...
+** for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){
+** SomeStructure *pData = fts3HashData(p);
+** // do something with pData
+** }
+*/
+#define fts3HashFirst(H) ((H)->first)
+#define fts3HashNext(E) ((E)->next)
+#define fts3HashData(E) ((E)->data)
+#define fts3HashKey(E) ((E)->pKey)
+#define fts3HashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define fts3HashCount(H) ((H)->count)
+
+#endif /* _FTS3_HASH_H_ */
diff --git a/third_party/sqlite/ext/fts3/fts3_icu.c b/third_party/sqlite/ext/fts3/fts3_icu.c
new file mode 100755
index 0000000..e2d040c
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_icu.c
@@ -0,0 +1,258 @@
+/*
+** 2007 June 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 a tokenizer for fts3 based on the ICU library.
+**
+** $Id: fts3_icu.c,v 1.2 2007/10/24 21:52:37 shess Exp $
+*/
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+#ifdef SQLITE_ENABLE_ICU
+
+#include <assert.h>
+#include <string.h>
+#include "fts3_tokenizer.h"
+
+#include <unicode/ubrk.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+#include <unicode/utf16.h>
+
+typedef struct IcuTokenizer IcuTokenizer;
+typedef struct IcuCursor IcuCursor;
+
+struct IcuTokenizer {
+ sqlite3_tokenizer base;
+ char *zLocale;
+};
+
+struct IcuCursor {
+ sqlite3_tokenizer_cursor base;
+
+ UBreakIterator *pIter; /* ICU break-iterator object */
+ int nChar; /* Number of UChar elements in pInput */
+ UChar *aChar; /* Copy of input using utf-16 encoding */
+ int *aOffset; /* Offsets of each character in utf-8 input */
+
+ int nBuffer;
+ char *zBuffer;
+
+ int iToken;
+};
+
+/*
+** Create a new tokenizer instance.
+*/
+static int icuCreate(
+ int argc, /* Number of entries in argv[] */
+ const char * const *argv, /* Tokenizer creation arguments */
+ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
+){
+ IcuTokenizer *p;
+ int n = 0;
+
+ if( argc>0 ){
+ n = strlen(argv[0])+1;
+ }
+ p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n);
+ if( !p ){
+ return SQLITE_NOMEM;
+ }
+ memset(p, 0, sizeof(IcuTokenizer));
+
+ if( n ){
+ p->zLocale = (char *)&p[1];
+ memcpy(p->zLocale, argv[0], n);
+ }
+
+ *ppTokenizer = (sqlite3_tokenizer *)p;
+
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int icuDestroy(sqlite3_tokenizer *pTokenizer){
+ IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is pInput[0..nBytes-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int icuOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *zInput, /* Input string */
+ int nInput, /* Length of zInput in bytes */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
+ IcuCursor *pCsr;
+
+ const int32_t opt = U_FOLD_CASE_DEFAULT;
+ UErrorCode status = U_ZERO_ERROR;
+ int nChar;
+
+ UChar32 c;
+ int iInput = 0;
+ int iOut = 0;
+
+ *ppCursor = 0;
+
+ if( -1 == nInput ) nInput = strlen(nInput);
+ nChar = nInput+1;
+ pCsr = (IcuCursor *)sqlite3_malloc(
+ sizeof(IcuCursor) + /* IcuCursor */
+ nChar * sizeof(UChar) + /* IcuCursor.aChar[] */
+ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
+ );
+ if( !pCsr ){
+ return SQLITE_NOMEM;
+ }
+ memset(pCsr, 0, sizeof(IcuCursor));
+ pCsr->aChar = (UChar *)&pCsr[1];
+ pCsr->aOffset = (int *)&pCsr->aChar[nChar];
+
+ pCsr->aOffset[iOut] = iInput;
+ U8_NEXT(zInput, iInput, nInput, c);
+ while( c>0 ){
+ int isError = 0;
+ c = u_foldCase(c, opt);
+ U16_APPEND(pCsr->aChar, iOut, nChar, c, isError);
+ if( isError ){
+ sqlite3_free(pCsr);
+ return SQLITE_ERROR;
+ }
+ pCsr->aOffset[iOut] = iInput;
+
+ if( iInput<nInput ){
+ U8_NEXT(zInput, iInput, nInput, c);
+ }else{
+ c = 0;
+ }
+ }
+
+ pCsr->pIter = ubrk_open(UBRK_WORD, p->zLocale, pCsr->aChar, iOut, &status);
+ if( !U_SUCCESS(status) ){
+ sqlite3_free(pCsr);
+ return SQLITE_ERROR;
+ }
+ pCsr->nChar = iOut;
+
+ ubrk_first(pCsr->pIter);
+ *ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to icuOpen().
+*/
+static int icuClose(sqlite3_tokenizer_cursor *pCursor){
+ IcuCursor *pCsr = (IcuCursor *)pCursor;
+ ubrk_close(pCsr->pIter);
+ sqlite3_free(pCsr->zBuffer);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor.
+*/
+static int icuNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
+ const char **ppToken, /* OUT: *ppToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ IcuCursor *pCsr = (IcuCursor *)pCursor;
+
+ int iStart = 0;
+ int iEnd = 0;
+ int nByte = 0;
+
+ while( iStart==iEnd ){
+ UChar32 c;
+
+ iStart = ubrk_current(pCsr->pIter);
+ iEnd = ubrk_next(pCsr->pIter);
+ if( iEnd==UBRK_DONE ){
+ return SQLITE_DONE;
+ }
+
+ while( iStart<iEnd ){
+ int iWhite = iStart;
+ U8_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
+ if( u_isspace(c) ){
+ iStart = iWhite;
+ }else{
+ break;
+ }
+ }
+ assert(iStart<=iEnd);
+ }
+
+ do {
+ UErrorCode status = U_ZERO_ERROR;
+ if( nByte ){
+ char *zNew = sqlite3_realloc(pCsr->zBuffer, nByte);
+ if( !zNew ){
+ return SQLITE_NOMEM;
+ }
+ pCsr->zBuffer = zNew;
+ pCsr->nBuffer = nByte;
+ }
+
+ u_strToUTF8(
+ pCsr->zBuffer, pCsr->nBuffer, &nByte, /* Output vars */
+ &pCsr->aChar[iStart], iEnd-iStart, /* Input vars */
+ &status /* Output success/failure */
+ );
+ } while( nByte>pCsr->nBuffer );
+
+ *ppToken = pCsr->zBuffer;
+ *pnBytes = nByte;
+ *piStartOffset = pCsr->aOffset[iStart];
+ *piEndOffset = pCsr->aOffset[iEnd];
+ *piPosition = pCsr->iToken++;
+
+ return SQLITE_OK;
+}
+
+/*
+** The set of routines that implement the simple tokenizer
+*/
+static const sqlite3_tokenizer_module icuTokenizerModule = {
+ 0, /* iVersion */
+ icuCreate, /* xCreate */
+ icuDestroy, /* xCreate */
+ icuOpen, /* xOpen */
+ icuClose, /* xClose */
+ icuNext, /* xNext */
+};
+
+/*
+** Set *ppModule to point at the implementation of the ICU tokenizer.
+*/
+void sqlite3Fts3IcuTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &icuTokenizerModule;
+}
+
+#endif /* defined(SQLITE_ENABLE_ICU) */
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/fts3_porter.c b/third_party/sqlite/ext/fts3/fts3_porter.c
new file mode 100755
index 0000000..6ff67a9
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_porter.c
@@ -0,0 +1,642 @@
+/*
+** 2006 September 30
+**
+** 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.
+**
+*************************************************************************
+** Implementation of the full-text-search tokenizer that implements
+** a Porter stemmer.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS3 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS3 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts3_tokenizer.h"
+
+/*
+** Class derived from sqlite3_tokenizer
+*/
+typedef struct porter_tokenizer {
+ sqlite3_tokenizer base; /* Base class */
+} porter_tokenizer;
+
+/*
+** Class derived from sqlit3_tokenizer_cursor
+*/
+typedef struct porter_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ const char *zInput; /* input we are tokenizing */
+ int nInput; /* size of the input */
+ int iOffset; /* current position in zInput */
+ int iToken; /* index of next token to be returned */
+ char *zToken; /* storage for current token */
+ int nAllocated; /* space allocated to zToken buffer */
+} porter_tokenizer_cursor;
+
+
+/* Forward declaration */
+static const sqlite3_tokenizer_module porterTokenizerModule;
+
+
+/*
+** Create a new tokenizer instance.
+*/
+static int porterCreate(
+ int argc, const char * const *argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ porter_tokenizer *t;
+ t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t));
+ if( t==NULL ) return SQLITE_NOMEM;
+ memset(t, 0, sizeof(*t));
+ *ppTokenizer = &t->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int porterDestroy(sqlite3_tokenizer *pTokenizer){
+ sqlite3_free(pTokenizer);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is zInput[0..nInput-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int porterOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *zInput, int nInput, /* String to be tokenized */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ porter_tokenizer_cursor *c;
+
+ c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
+ if( c==NULL ) return SQLITE_NOMEM;
+
+ c->zInput = zInput;
+ if( zInput==0 ){
+ c->nInput = 0;
+ }else if( nInput<0 ){
+ c->nInput = (int)strlen(zInput);
+ }else{
+ c->nInput = nInput;
+ }
+ c->iOffset = 0; /* start tokenizing at the beginning */
+ c->iToken = 0;
+ c->zToken = NULL; /* no space allocated, yet. */
+ c->nAllocated = 0;
+
+ *ppCursor = &c->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** porterOpen() above.
+*/
+static int porterClose(sqlite3_tokenizer_cursor *pCursor){
+ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
+ sqlite3_free(c->zToken);
+ sqlite3_free(c);
+ return SQLITE_OK;
+}
+/*
+** Vowel or consonant
+*/
+static const char cType[] = {
+ 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 2, 1
+};
+
+/*
+** isConsonant() and isVowel() determine if their first character in
+** the string they point to is a consonant or a vowel, according
+** to Porter ruls.
+**
+** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
+** 'Y' is a consonant unless it follows another consonant,
+** in which case it is a vowel.
+**
+** In these routine, the letters are in reverse order. So the 'y' rule
+** is that 'y' is a consonant unless it is followed by another
+** consonent.
+*/
+static int isVowel(const char*);
+static int isConsonant(const char *z){
+ int j;
+ char x = *z;
+ if( x==0 ) return 0;
+ assert( x>='a' && x<='z' );
+ j = cType[x-'a'];
+ if( j<2 ) return j;
+ return z[1]==0 || isVowel(z + 1);
+}
+static int isVowel(const char *z){
+ int j;
+ char x = *z;
+ if( x==0 ) return 0;
+ assert( x>='a' && x<='z' );
+ j = cType[x-'a'];
+ if( j<2 ) return 1-j;
+ return isConsonant(z + 1);
+}
+
+/*
+** Let any sequence of one or more vowels be represented by V and let
+** C be sequence of one or more consonants. Then every word can be
+** represented as:
+**
+** [C] (VC){m} [V]
+**
+** In prose: A word is an optional consonant followed by zero or
+** vowel-consonant pairs followed by an optional vowel. "m" is the
+** number of vowel consonant pairs. This routine computes the value
+** of m for the first i bytes of a word.
+**
+** Return true if the m-value for z is 1 or more. In other words,
+** return true if z contains at least one vowel that is followed
+** by a consonant.
+**
+** In this routine z[] is in reverse order. So we are really looking
+** for an instance of of a consonant followed by a vowel.
+*/
+static int m_gt_0(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/* Like mgt0 above except we are looking for a value of m which is
+** exactly 1
+*/
+static int m_eq_1(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 1;
+ while( isConsonant(z) ){ z++; }
+ return *z==0;
+}
+
+/* Like mgt0 above except we are looking for a value of m>1 instead
+** or m>0
+*/
+static int m_gt_1(const char *z){
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isVowel(z) ){ z++; }
+ if( *z==0 ) return 0;
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/*
+** Return TRUE if there is a vowel anywhere within z[0..n-1]
+*/
+static int hasVowel(const char *z){
+ while( isConsonant(z) ){ z++; }
+ return *z!=0;
+}
+
+/*
+** Return TRUE if the word ends in a double consonant.
+**
+** The text is reversed here. So we are really looking at
+** the first two characters of z[].
+*/
+static int doubleConsonant(const char *z){
+ return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
+}
+
+/*
+** Return TRUE if the word ends with three letters which
+** are consonant-vowel-consonent and where the final consonant
+** is not 'w', 'x', or 'y'.
+**
+** The word is reversed here. So we are really checking the
+** first three letters and the first one cannot be in [wxy].
+*/
+static int star_oh(const char *z){
+ return
+ z[0]!=0 && isConsonant(z) &&
+ z[0]!='w' && z[0]!='x' && z[0]!='y' &&
+ z[1]!=0 && isVowel(z+1) &&
+ z[2]!=0 && isConsonant(z+2);
+}
+
+/*
+** If the word ends with zFrom and xCond() is true for the stem
+** of the word that preceeds the zFrom ending, then change the
+** ending to zTo.
+**
+** The input word *pz and zFrom are both in reverse order. zTo
+** is in normal order.
+**
+** Return TRUE if zFrom matches. Return FALSE if zFrom does not
+** match. Not that TRUE is returned even if xCond() fails and
+** no substitution occurs.
+*/
+static int stem(
+ char **pz, /* The word being stemmed (Reversed) */
+ const char *zFrom, /* If the ending matches this... (Reversed) */
+ const char *zTo, /* ... change the ending to this (not reversed) */
+ int (*xCond)(const char*) /* Condition that must be true */
+){
+ char *z = *pz;
+ while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
+ if( *zFrom!=0 ) return 0;
+ if( xCond && !xCond(z) ) return 1;
+ while( *zTo ){
+ *(--z) = *(zTo++);
+ }
+ *pz = z;
+ return 1;
+}
+
+/*
+** This is the fallback stemmer used when the porter stemmer is
+** inappropriate. The input word is copied into the output with
+** US-ASCII case folding. If the input word is too long (more
+** than 20 bytes if it contains no digits or more than 6 bytes if
+** it contains digits) then word is truncated to 20 or 6 bytes
+** by taking 10 or 3 bytes from the beginning and end.
+*/
+static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
+ int i, mx, j;
+ int hasDigit = 0;
+ for(i=0; i<nIn; i++){
+ int c = zIn[i];
+ if( c>='A' && c<='Z' ){
+ zOut[i] = c - 'A' + 'a';
+ }else{
+ if( c>='0' && c<='9' ) hasDigit = 1;
+ zOut[i] = c;
+ }
+ }
+ mx = hasDigit ? 3 : 10;
+ if( nIn>mx*2 ){
+ for(j=mx, i=nIn-mx; i<nIn; i++, j++){
+ zOut[j] = zOut[i];
+ }
+ i = j;
+ }
+ zOut[i] = 0;
+ *pnOut = i;
+}
+
+
+/*
+** Stem the input word zIn[0..nIn-1]. Store the output in zOut.
+** zOut is at least big enough to hold nIn bytes. Write the actual
+** size of the output word (exclusive of the '\0' terminator) into *pnOut.
+**
+** Any upper-case characters in the US-ASCII character set ([A-Z])
+** are converted to lower case. Upper-case UTF characters are
+** unchanged.
+**
+** Words that are longer than about 20 bytes are stemmed by retaining
+** a few bytes from the beginning and the end of the word. If the
+** word contains digits, 3 bytes are taken from the beginning and
+** 3 bytes from the end. For long words without digits, 10 bytes
+** are taken from each end. US-ASCII case folding still applies.
+**
+** If the input word contains not digits but does characters not
+** in [a-zA-Z] then no stemming is attempted and this routine just
+** copies the input into the input into the output with US-ASCII
+** case folding.
+**
+** Stemming never increases the length of the word. So there is
+** no chance of overflowing the zOut buffer.
+*/
+static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
+ int i, j, c;
+ char zReverse[28];
+ char *z, *z2;
+ if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
+ /* The word is too big or too small for the porter stemmer.
+ ** Fallback to the copy stemmer */
+ copy_stemmer(zIn, nIn, zOut, pnOut);
+ return;
+ }
+ for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
+ c = zIn[i];
+ if( c>='A' && c<='Z' ){
+ zReverse[j] = c + 'a' - 'A';
+ }else if( c>='a' && c<='z' ){
+ zReverse[j] = c;
+ }else{
+ /* The use of a character not in [a-zA-Z] means that we fallback
+ ** to the copy stemmer */
+ copy_stemmer(zIn, nIn, zOut, pnOut);
+ return;
+ }
+ }
+ memset(&zReverse[sizeof(zReverse)-5], 0, 5);
+ z = &zReverse[j+1];
+
+
+ /* Step 1a */
+ if( z[0]=='s' ){
+ if(
+ !stem(&z, "sess", "ss", 0) &&
+ !stem(&z, "sei", "i", 0) &&
+ !stem(&z, "ss", "ss", 0)
+ ){
+ z++;
+ }
+ }
+
+ /* Step 1b */
+ z2 = z;
+ if( stem(&z, "dee", "ee", m_gt_0) ){
+ /* Do nothing. The work was all in the test */
+ }else if(
+ (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
+ && z!=z2
+ ){
+ if( stem(&z, "ta", "ate", 0) ||
+ stem(&z, "lb", "ble", 0) ||
+ stem(&z, "zi", "ize", 0) ){
+ /* Do nothing. The work was all in the test */
+ }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
+ z++;
+ }else if( m_eq_1(z) && star_oh(z) ){
+ *(--z) = 'e';
+ }
+ }
+
+ /* Step 1c */
+ if( z[0]=='y' && hasVowel(z+1) ){
+ z[0] = 'i';
+ }
+
+ /* Step 2 */
+ switch( z[1] ){
+ case 'a':
+ stem(&z, "lanoita", "ate", m_gt_0) ||
+ stem(&z, "lanoit", "tion", m_gt_0);
+ break;
+ case 'c':
+ stem(&z, "icne", "ence", m_gt_0) ||
+ stem(&z, "icna", "ance", m_gt_0);
+ break;
+ case 'e':
+ stem(&z, "rezi", "ize", m_gt_0);
+ break;
+ case 'g':
+ stem(&z, "igol", "log", m_gt_0);
+ break;
+ case 'l':
+ stem(&z, "ilb", "ble", m_gt_0) ||
+ stem(&z, "illa", "al", m_gt_0) ||
+ stem(&z, "iltne", "ent", m_gt_0) ||
+ stem(&z, "ile", "e", m_gt_0) ||
+ stem(&z, "ilsuo", "ous", m_gt_0);
+ break;
+ case 'o':
+ stem(&z, "noitazi", "ize", m_gt_0) ||
+ stem(&z, "noita", "ate", m_gt_0) ||
+ stem(&z, "rota", "ate", m_gt_0);
+ break;
+ case 's':
+ stem(&z, "msila", "al", m_gt_0) ||
+ stem(&z, "ssenevi", "ive", m_gt_0) ||
+ stem(&z, "ssenluf", "ful", m_gt_0) ||
+ stem(&z, "ssensuo", "ous", m_gt_0);
+ break;
+ case 't':
+ stem(&z, "itila", "al", m_gt_0) ||
+ stem(&z, "itivi", "ive", m_gt_0) ||
+ stem(&z, "itilib", "ble", m_gt_0);
+ break;
+ }
+
+ /* Step 3 */
+ switch( z[0] ){
+ case 'e':
+ stem(&z, "etaci", "ic", m_gt_0) ||
+ stem(&z, "evita", "", m_gt_0) ||
+ stem(&z, "ezila", "al", m_gt_0);
+ break;
+ case 'i':
+ stem(&z, "itici", "ic", m_gt_0);
+ break;
+ case 'l':
+ stem(&z, "laci", "ic", m_gt_0) ||
+ stem(&z, "luf", "", m_gt_0);
+ break;
+ case 's':
+ stem(&z, "ssen", "", m_gt_0);
+ break;
+ }
+
+ /* Step 4 */
+ switch( z[1] ){
+ case 'a':
+ if( z[0]=='l' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'c':
+ if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
+ z += 4;
+ }
+ break;
+ case 'e':
+ if( z[0]=='r' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'i':
+ if( z[0]=='c' && m_gt_1(z+2) ){
+ z += 2;
+ }
+ break;
+ case 'l':
+ if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
+ z += 4;
+ }
+ break;
+ case 'n':
+ if( z[0]=='t' ){
+ if( z[2]=='a' ){
+ if( m_gt_1(z+3) ){
+ z += 3;
+ }
+ }else if( z[2]=='e' ){
+ stem(&z, "tneme", "", m_gt_1) ||
+ stem(&z, "tnem", "", m_gt_1) ||
+ stem(&z, "tne", "", m_gt_1);
+ }
+ }
+ break;
+ case 'o':
+ if( z[0]=='u' ){
+ if( m_gt_1(z+2) ){
+ z += 2;
+ }
+ }else if( z[3]=='s' || z[3]=='t' ){
+ stem(&z, "noi", "", m_gt_1);
+ }
+ break;
+ case 's':
+ if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ case 't':
+ stem(&z, "eta", "", m_gt_1) ||
+ stem(&z, "iti", "", m_gt_1);
+ break;
+ case 'u':
+ if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ case 'v':
+ case 'z':
+ if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
+ z += 3;
+ }
+ break;
+ }
+
+ /* Step 5a */
+ if( z[0]=='e' ){
+ if( m_gt_1(z+1) ){
+ z++;
+ }else if( m_eq_1(z+1) && !star_oh(z+1) ){
+ z++;
+ }
+ }
+
+ /* Step 5b */
+ if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
+ z++;
+ }
+
+ /* z[] is now the stemmed word in reverse order. Flip it back
+ ** around into forward order and return.
+ */
+ *pnOut = i = strlen(z);
+ zOut[i] = 0;
+ while( *z ){
+ zOut[--i] = *(z++);
+ }
+}
+
+/*
+** Characters that can be part of a token. We assume any character
+** whose value is greater than 0x80 (any UTF character) can be
+** part of a token. In other words, delimiters all must have
+** values of 0x7f or lower.
+*/
+static const char porterIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+};
+#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !porterIdChar[ch-0x30]))
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to porterOpen().
+*/
+static int porterNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
+ const char **pzToken, /* OUT: *pzToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
+ const char *z = c->zInput;
+
+ while( c->iOffset<c->nInput ){
+ int iStartOffset, ch;
+
+ /* Scan past delimiter characters */
+ while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ /* Count non-delimiter characters. */
+ iStartOffset = c->iOffset;
+ while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ if( c->iOffset>iStartOffset ){
+ int n = c->iOffset-iStartOffset;
+ if( n>c->nAllocated ){
+ c->nAllocated = n+20;
+ c->zToken = sqlite3_realloc(c->zToken, c->nAllocated);
+ if( c->zToken==NULL ) return SQLITE_NOMEM;
+ }
+ porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
+ *pzToken = c->zToken;
+ *piStartOffset = iStartOffset;
+ *piEndOffset = c->iOffset;
+ *piPosition = c->iToken++;
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_DONE;
+}
+
+/*
+** The set of routines that implement the porter-stemmer tokenizer
+*/
+static const sqlite3_tokenizer_module porterTokenizerModule = {
+ 0,
+ porterCreate,
+ porterDestroy,
+ porterOpen,
+ porterClose,
+ porterNext,
+};
+
+/*
+** Allocate a new porter tokenizer. Return a pointer to the new
+** tokenizer in *ppModule
+*/
+void sqlite3Fts3PorterTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &porterTokenizerModule;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/fts3_tokenizer.c b/third_party/sqlite/ext/fts3/fts3_tokenizer.c
new file mode 100755
index 0000000..ef19995
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_tokenizer.c
@@ -0,0 +1,371 @@
+/*
+** 2007 June 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 is part of an SQLite module implementing full-text search.
+** This particular file implements the generic tokenizer interface.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS3 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS3 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+#include "sqlite3ext.h"
+#ifndef SQLITE_CORE
+ SQLITE_EXTENSION_INIT1
+#endif
+
+#include "fts3_hash.h"
+#include "fts3_tokenizer.h"
+#include <assert.h>
+
+/*
+** Implementation of the SQL scalar function for accessing the underlying
+** hash table. This function may be called as follows:
+**
+** SELECT <function-name>(<key-name>);
+** SELECT <function-name>(<key-name>, <pointer>);
+**
+** where <function-name> is the name passed as the second argument
+** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer').
+**
+** If the <pointer> argument is specified, it must be a blob value
+** containing a pointer to be stored as the hash data corresponding
+** to the string <key-name>. If <pointer> is not specified, then
+** the string <key-name> must already exist in the has table. Otherwise,
+** an error is returned.
+**
+** Whether or not the <pointer> argument is specified, the value returned
+** is a blob containing the pointer stored as the hash data corresponding
+** to string <key-name> (after the hash-table is updated, if applicable).
+*/
+static void scalarFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ fts3Hash *pHash;
+ void *pPtr = 0;
+ const unsigned char *zName;
+ int nName;
+
+ assert( argc==1 || argc==2 );
+
+ pHash = (fts3Hash *)sqlite3_user_data(context);
+
+ zName = sqlite3_value_text(argv[0]);
+ nName = sqlite3_value_bytes(argv[0])+1;
+
+ if( argc==2 ){
+ void *pOld;
+ int n = sqlite3_value_bytes(argv[1]);
+ if( n!=sizeof(pPtr) ){
+ sqlite3_result_error(context, "argument type mismatch", -1);
+ return;
+ }
+ pPtr = *(void **)sqlite3_value_blob(argv[1]);
+ pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr);
+ if( pOld==pPtr ){
+ sqlite3_result_error(context, "out of memory", -1);
+ return;
+ }
+ }else{
+ pPtr = sqlite3Fts3HashFind(pHash, zName, nName);
+ if( !pPtr ){
+ char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName);
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
+ }
+
+ sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
+}
+
+#ifdef SQLITE_TEST
+
+#include <tcl.h>
+#include <string.h>
+
+/*
+** Implementation of a special SQL scalar function for testing tokenizers
+** designed to be used in concert with the Tcl testing framework. This
+** function must be called with two arguments:
+**
+** SELECT <function-name>(<key-name>, <input-string>);
+** SELECT <function-name>(<key-name>, <pointer>);
+**
+** where <function-name> is the name passed as the second argument
+** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer')
+** concatenated with the string '_test' (e.g. 'fts3_tokenizer_test').
+**
+** The return value is a string that may be interpreted as a Tcl
+** list. For each token in the <input-string>, three elements are
+** added to the returned list. The first is the token position, the
+** second is the token text (folded, stemmed, etc.) and the third is the
+** substring of <input-string> associated with the token. For example,
+** using the built-in "simple" tokenizer:
+**
+** SELECT fts_tokenizer_test('simple', 'I don't see how');
+**
+** will return the string:
+**
+** "{0 i I 1 dont don't 2 see see 3 how how}"
+**
+*/
+static void testFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ fts3Hash *pHash;
+ sqlite3_tokenizer_module *p;
+ sqlite3_tokenizer *pTokenizer = 0;
+ sqlite3_tokenizer_cursor *pCsr = 0;
+
+ const char *zErr = 0;
+
+ const char *zName;
+ int nName;
+ const char *zInput;
+ int nInput;
+
+ const char *zArg = 0;
+
+ const char *zToken;
+ int nToken;
+ int iStart;
+ int iEnd;
+ int iPos;
+
+ Tcl_Obj *pRet;
+
+ assert( argc==2 || argc==3 );
+
+ nName = sqlite3_value_bytes(argv[0]);
+ zName = (const char *)sqlite3_value_text(argv[0]);
+ nInput = sqlite3_value_bytes(argv[argc-1]);
+ zInput = (const char *)sqlite3_value_text(argv[argc-1]);
+
+ if( argc==3 ){
+ zArg = (const char *)sqlite3_value_text(argv[1]);
+ }
+
+ pHash = (fts3Hash *)sqlite3_user_data(context);
+ p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1);
+
+ if( !p ){
+ char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName);
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
+
+ pRet = Tcl_NewObj();
+ Tcl_IncrRefCount(pRet);
+
+ if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){
+ zErr = "error in xCreate()";
+ goto finish;
+ }
+ pTokenizer->pModule = p;
+ if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){
+ zErr = "error in xOpen()";
+ goto finish;
+ }
+ pCsr->pTokenizer = pTokenizer;
+
+ while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken));
+ zToken = &zInput[iStart];
+ nToken = iEnd-iStart;
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken));
+ }
+
+ if( SQLITE_OK!=p->xClose(pCsr) ){
+ zErr = "error in xClose()";
+ goto finish;
+ }
+ if( SQLITE_OK!=p->xDestroy(pTokenizer) ){
+ zErr = "error in xDestroy()";
+ goto finish;
+ }
+
+finish:
+ if( zErr ){
+ sqlite3_result_error(context, zErr, -1);
+ }else{
+ sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
+ }
+ Tcl_DecrRefCount(pRet);
+}
+
+static
+int registerTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module *p
+){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
+ sqlite3_step(pStmt);
+
+ return sqlite3_finalize(pStmt);
+}
+
+static
+int queryTokenizer(
+ sqlite3 *db,
+ char *zName,
+ const sqlite3_tokenizer_module **pp
+){
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts3_tokenizer(?)";
+
+ *pp = 0;
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
+ memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
+ }
+ }
+
+ return sqlite3_finalize(pStmt);
+}
+
+void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+
+/*
+** Implementation of the scalar function fts3_tokenizer_internal_test().
+** This function is used for testing only, it is not included in the
+** build unless SQLITE_TEST is defined.
+**
+** The purpose of this is to test that the fts3_tokenizer() function
+** can be used as designed by the C-code in the queryTokenizer and
+** registerTokenizer() functions above. These two functions are repeated
+** in the README.tokenizer file as an example, so it is important to
+** test them.
+**
+** To run the tests, evaluate the fts3_tokenizer_internal_test() scalar
+** function with no arguments. An assert() will fail if a problem is
+** detected. i.e.:
+**
+** SELECT fts3_tokenizer_internal_test();
+**
+*/
+static void intTestFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int rc;
+ const sqlite3_tokenizer_module *p1;
+ const sqlite3_tokenizer_module *p2;
+ sqlite3 *db = (sqlite3 *)sqlite3_user_data(context);
+
+ /* Test the query function */
+ sqlite3Fts3SimpleTokenizerModule(&p1);
+ rc = queryTokenizer(db, "simple", &p2);
+ assert( rc==SQLITE_OK );
+ assert( p1==p2 );
+ rc = queryTokenizer(db, "nosuchtokenizer", &p2);
+ assert( rc==SQLITE_ERROR );
+ assert( p2==0 );
+ assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") );
+
+ /* Test the storage function */
+ rc = registerTokenizer(db, "nosuchtokenizer", p1);
+ assert( rc==SQLITE_OK );
+ rc = queryTokenizer(db, "nosuchtokenizer", &p2);
+ assert( rc==SQLITE_OK );
+ assert( p2==p1 );
+
+ sqlite3_result_text(context, "ok", -1, SQLITE_STATIC);
+}
+
+#endif
+
+/*
+** Set up SQL objects in database db used to access the contents of
+** the hash table pointed to by argument pHash. The hash table must
+** been initialised to use string keys, and to take a private copy
+** of the key when a value is inserted. i.e. by a call similar to:
+**
+** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
+**
+** This function adds a scalar function (see header comment above
+** scalarFunc() in this file for details) and, if ENABLE_TABLE is
+** defined at compilation time, a temporary virtual table (see header
+** comment above struct HashTableVtab) to the database schema. Both
+** provide read/write access to the contents of *pHash.
+**
+** The third argument to this function, zName, is used as the name
+** of both the scalar and, if created, the virtual table.
+*/
+int sqlite3Fts3InitHashTable(
+ sqlite3 *db,
+ fts3Hash *pHash,
+ const char *zName
+){
+ int rc = SQLITE_OK;
+ void *p = (void *)pHash;
+ const int any = SQLITE_ANY;
+ char *zTest = 0;
+ char *zTest2 = 0;
+
+#ifdef SQLITE_TEST
+ void *pdb = (void *)db;
+ zTest = sqlite3_mprintf("%s_test", zName);
+ zTest2 = sqlite3_mprintf("%s_internal_test", zName);
+ if( !zTest || !zTest2 ){
+ rc = SQLITE_NOMEM;
+ }
+#endif
+
+ if( rc!=SQLITE_OK
+ || (rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0))
+ || (rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0))
+#ifdef SQLITE_TEST
+ || (rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0))
+ || (rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0))
+ || (rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0))
+#endif
+ );
+
+ sqlite3_free(zTest);
+ sqlite3_free(zTest2);
+ return rc;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/fts3_tokenizer.h b/third_party/sqlite/ext/fts3/fts3_tokenizer.h
new file mode 100755
index 0000000..4faef56
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_tokenizer.h
@@ -0,0 +1,145 @@
+/*
+** 2006 July 10
+**
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Defines the interface to tokenizers used by fulltext-search. There
+** are three basic components:
+**
+** sqlite3_tokenizer_module is a singleton defining the tokenizer
+** interface functions. This is essentially the class structure for
+** tokenizers.
+**
+** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
+** including customization information defined at creation time.
+**
+** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
+** tokens from a particular input.
+*/
+#ifndef _FTS3_TOKENIZER_H_
+#define _FTS3_TOKENIZER_H_
+
+/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
+** If tokenizers are to be allowed to call sqlite3_*() functions, then
+** we will need a way to register the API consistently.
+*/
+#include "sqlite3.h"
+
+/*
+** Structures used by the tokenizer interface. When a new tokenizer
+** implementation is registered, the caller provides a pointer to
+** an sqlite3_tokenizer_module containing pointers to the callback
+** functions that make up an implementation.
+**
+** When an fts3 table is created, it passes any arguments passed to
+** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
+** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
+** implementation. The xCreate() function in turn returns an
+** sqlite3_tokenizer structure representing the specific tokenizer to
+** be used for the fts3 table (customized by the tokenizer clause arguments).
+**
+** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
+** method is called. It returns an sqlite3_tokenizer_cursor object
+** that may be used to tokenize a specific input buffer based on
+** the tokenization rules supplied by a specific sqlite3_tokenizer
+** object.
+*/
+typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
+typedef struct sqlite3_tokenizer sqlite3_tokenizer;
+typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
+
+struct sqlite3_tokenizer_module {
+
+ /*
+ ** Structure version. Should always be set to 0.
+ */
+ int iVersion;
+
+ /*
+ ** Create a new tokenizer. The values in the argv[] array are the
+ ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
+ ** TABLE statement that created the fts3 table. For example, if
+ ** the following SQL is executed:
+ **
+ ** CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
+ **
+ ** then argc is set to 2, and the argv[] array contains pointers
+ ** to the strings "arg1" and "arg2".
+ **
+ ** This method should return either SQLITE_OK (0), or an SQLite error
+ ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
+ ** to point at the newly created tokenizer structure. The generic
+ ** sqlite3_tokenizer.pModule variable should not be initialised by
+ ** this callback. The caller will do so.
+ */
+ int (*xCreate)(
+ int argc, /* Size of argv array */
+ const char *const*argv, /* Tokenizer argument strings */
+ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
+ );
+
+ /*
+ ** Destroy an existing tokenizer. The fts3 module calls this method
+ ** exactly once for each successful call to xCreate().
+ */
+ int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
+
+ /*
+ ** Create a tokenizer cursor to tokenize an input buffer. The caller
+ ** is responsible for ensuring that the input buffer remains valid
+ ** until the cursor is closed (using the xClose() method).
+ */
+ int (*xOpen)(
+ sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
+ const char *pInput, int nBytes, /* Input buffer */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
+ );
+
+ /*
+ ** Destroy an existing tokenizer cursor. The fts3 module calls this
+ ** method exactly once for each successful call to xOpen().
+ */
+ int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
+
+ /*
+ ** Retrieve the next token from the tokenizer cursor pCursor. This
+ ** method should either return SQLITE_OK and set the values of the
+ ** "OUT" variables identified below, or SQLITE_DONE to indicate that
+ ** the end of the buffer has been reached, or an SQLite error code.
+ **
+ ** *ppToken should be set to point at a buffer containing the
+ ** normalized version of the token (i.e. after any case-folding and/or
+ ** stemming has been performed). *pnBytes should be set to the length
+ ** of this buffer in bytes. The input text that generated the token is
+ ** identified by the byte offsets returned in *piStartOffset and
+ ** *piEndOffset.
+ **
+ ** The buffer *ppToken is set to point at is managed by the tokenizer
+ ** implementation. It is only required to be valid until the next call
+ ** to xNext() or xClose().
+ */
+ /* TODO(shess) current implementation requires pInput to be
+ ** nul-terminated. This should either be fixed, or pInput/nBytes
+ ** should be converted to zInput.
+ */
+ int (*xNext)(
+ sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
+ const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
+ int *piStartOffset, /* OUT: Byte offset of token in input buffer */
+ int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
+ int *piPosition /* OUT: Number of tokens returned before this one */
+ );
+};
+
+struct sqlite3_tokenizer {
+ const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+struct sqlite3_tokenizer_cursor {
+ sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
+ /* Tokenizer implementations will typically add additional fields */
+};
+
+#endif /* _FTS3_TOKENIZER_H_ */
diff --git a/third_party/sqlite/ext/fts3/fts3_tokenizer1.c b/third_party/sqlite/ext/fts3/fts3_tokenizer1.c
new file mode 100755
index 0000000..da255d9
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/fts3_tokenizer1.c
@@ -0,0 +1,230 @@
+/*
+** 2006 Oct 10
+**
+** 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.
+**
+******************************************************************************
+**
+** Implementation of the "simple" full-text-search tokenizer.
+*/
+
+/*
+** The code in this file is only compiled if:
+**
+** * The FTS3 module is being built as an extension
+** (in which case SQLITE_CORE is not defined), or
+**
+** * The FTS3 module is being built into the core of
+** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fts3_tokenizer.h"
+
+typedef struct simple_tokenizer {
+ sqlite3_tokenizer base;
+ char delim[128]; /* flag ASCII delimiters */
+} simple_tokenizer;
+
+typedef struct simple_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ const char *pInput; /* input we are tokenizing */
+ int nBytes; /* size of the input */
+ int iOffset; /* current position in pInput */
+ int iToken; /* index of next token to be returned */
+ char *pToken; /* storage for current token */
+ int nTokenAllocated; /* space allocated to zToken buffer */
+} simple_tokenizer_cursor;
+
+
+/* Forward declaration */
+static const sqlite3_tokenizer_module simpleTokenizerModule;
+
+static int simpleDelim(simple_tokenizer *t, unsigned char c){
+ return c<0x80 && t->delim[c];
+}
+
+/*
+** Create a new tokenizer instance.
+*/
+static int simpleCreate(
+ int argc, const char * const *argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ simple_tokenizer *t;
+
+ t = (simple_tokenizer *) sqlite3_malloc(sizeof(*t));
+ if( t==NULL ) return SQLITE_NOMEM;
+ memset(t, 0, sizeof(*t));
+
+ /* TODO(shess) Delimiters need to remain the same from run to run,
+ ** else we need to reindex. One solution would be a meta-table to
+ ** track such information in the database, then we'd only want this
+ ** information on the initial create.
+ */
+ if( argc>1 ){
+ int i, n = strlen(argv[1]);
+ for(i=0; i<n; i++){
+ unsigned char ch = argv[1][i];
+ /* We explicitly don't support UTF-8 delimiters for now. */
+ if( ch>=0x80 ){
+ sqlite3_free(t);
+ return SQLITE_ERROR;
+ }
+ t->delim[ch] = 1;
+ }
+ } else {
+ /* Mark non-alphanumeric ASCII characters as delimiters */
+ int i;
+ for(i=1; i<0x80; i++){
+ t->delim[i] = !isalnum(i);
+ }
+ }
+
+ *ppTokenizer = &t->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
+ sqlite3_free(pTokenizer);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is pInput[0..nBytes-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int simpleOpen(
+ sqlite3_tokenizer *pTokenizer, /* The tokenizer */
+ const char *pInput, int nBytes, /* String to be tokenized */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
+){
+ simple_tokenizer_cursor *c;
+
+ c = (simple_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
+ if( c==NULL ) return SQLITE_NOMEM;
+
+ c->pInput = pInput;
+ if( pInput==0 ){
+ c->nBytes = 0;
+ }else if( nBytes<0 ){
+ c->nBytes = (int)strlen(pInput);
+ }else{
+ c->nBytes = nBytes;
+ }
+ c->iOffset = 0; /* start tokenizing at the beginning */
+ c->iToken = 0;
+ c->pToken = NULL; /* no space allocated, yet. */
+ c->nTokenAllocated = 0;
+
+ *ppCursor = &c->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** simpleOpen() above.
+*/
+static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+ sqlite3_free(c->pToken);
+ sqlite3_free(c);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to simpleOpen().
+*/
+static int simpleNext(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
+ const char **ppToken, /* OUT: *ppToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
+ simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
+ unsigned char *p = (unsigned char *)c->pInput;
+
+ while( c->iOffset<c->nBytes ){
+ int iStartOffset;
+
+ /* Scan past delimiter characters */
+ while( c->iOffset<c->nBytes && simpleDelim(t, p[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ /* Count non-delimiter characters. */
+ iStartOffset = c->iOffset;
+ while( c->iOffset<c->nBytes && !simpleDelim(t, p[c->iOffset]) ){
+ c->iOffset++;
+ }
+
+ if( c->iOffset>iStartOffset ){
+ int i, n = c->iOffset-iStartOffset;
+ if( n>c->nTokenAllocated ){
+ c->nTokenAllocated = n+20;
+ c->pToken = sqlite3_realloc(c->pToken, c->nTokenAllocated);
+ if( c->pToken==NULL ) return SQLITE_NOMEM;
+ }
+ for(i=0; i<n; i++){
+ /* TODO(shess) This needs expansion to handle UTF-8
+ ** case-insensitivity.
+ */
+ unsigned char ch = p[iStartOffset+i];
+ c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
+ }
+ *ppToken = c->pToken;
+ *pnBytes = n;
+ *piStartOffset = iStartOffset;
+ *piEndOffset = c->iOffset;
+ *piPosition = c->iToken++;
+
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_DONE;
+}
+
+/*
+** The set of routines that implement the simple tokenizer
+*/
+static const sqlite3_tokenizer_module simpleTokenizerModule = {
+ 0,
+ simpleCreate,
+ simpleDestroy,
+ simpleOpen,
+ simpleClose,
+ simpleNext,
+};
+
+/*
+** Allocate a new simple tokenizer. Return a pointer to the new
+** tokenizer in *ppModule
+*/
+void sqlite3Fts3SimpleTokenizerModule(
+ sqlite3_tokenizer_module const**ppModule
+){
+ *ppModule = &simpleTokenizerModule;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
diff --git a/third_party/sqlite/ext/fts3/mkfts3amal.tcl b/third_party/sqlite/ext/fts3/mkfts3amal.tcl
new file mode 100755
index 0000000..0590487
--- /dev/null
+++ b/third_party/sqlite/ext/fts3/mkfts3amal.tcl
@@ -0,0 +1,115 @@
+#!/usr/bin/tclsh
+#
+# This script builds a single C code file holding all of FTS3 code.
+# The name of the output file is fts3amal.c. To build this file,
+# first do:
+#
+# make target_source
+#
+# The make target above moves all of the source code files into
+# a subdirectory named "tsrc". (This script expects to find the files
+# there and will not work if they are not found.)
+#
+# After the "tsrc" directory has been created and populated, run
+# this script:
+#
+# tclsh mkfts3amal.tcl
+#
+# The amalgamated FTS3 code will be written into fts3amal.c
+#
+
+# Open the output file and write a header comment at the beginning
+# of the file.
+#
+set out [open fts3amal.c w]
+set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1]
+puts $out [subst \
+{/******************************************************************************
+** This file is an amalgamation of separate C source files from the SQLite
+** Full Text Search extension 2 (fts3). By combining all the individual C
+** code files into this single large file, the entire code can be compiled
+** as a one translation unit. This allows many compilers to do optimizations
+** that would not be possible if the files were compiled separately. It also
+** makes the code easier to import into other projects.
+**
+** This amalgamation was generated on $today.
+*/}]
+
+# These are the header files used by FTS3. The first time any of these
+# files are seen in a #include statement in the C code, include the complete
+# text of the file in-line. The file only needs to be included once.
+#
+foreach hdr {
+ fts3.h
+ fts3_hash.h
+ fts3_tokenizer.h
+ sqlite3.h
+ sqlite3ext.h
+} {
+ set available_hdr($hdr) 1
+}
+
+# 78 stars used for comment formatting.
+set s78 \
+{*****************************************************************************}
+
+# Insert a comment into the code
+#
+proc section_comment {text} {
+ global out s78
+ set n [string length $text]
+ set nstar [expr {60 - $n}]
+ set stars [string range $s78 0 $nstar]
+ puts $out "/************** $text $stars/"
+}
+
+# Read the source file named $filename and write it into the
+# sqlite3.c output file. If any #include statements are seen,
+# process them approprately.
+#
+proc copy_file {filename} {
+ global seen_hdr available_hdr out
+ set tail [file tail $filename]
+ section_comment "Begin file $tail"
+ set in [open $filename r]
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} {
+ if {[info exists available_hdr($hdr)]} {
+ if {$available_hdr($hdr)} {
+ section_comment "Include $hdr in the middle of $tail"
+ copy_file tsrc/$hdr
+ section_comment "Continuing where we left off in $tail"
+ }
+ } elseif {![info exists seen_hdr($hdr)]} {
+ set seen_hdr($hdr) 1
+ puts $out $line
+ }
+ } elseif {[regexp {^#ifdef __cplusplus} $line]} {
+ puts $out "#if 0"
+ } elseif {[regexp {^#line} $line]} {
+ # Skip #line directives.
+ } else {
+ puts $out $line
+ }
+ }
+ close $in
+ section_comment "End of $tail"
+}
+
+
+# Process the source files. Process files containing commonly
+# used subroutines first in order to help the compiler find
+# inlining opportunities.
+#
+foreach file {
+ fts3.c
+ fts3_hash.c
+ fts3_porter.c
+ fts3_tokenizer.c
+ fts3_tokenizer1.c
+} {
+ copy_file tsrc/$file
+}
+
+close $out
diff --git a/third_party/sqlite/ext/icu/README.txt b/third_party/sqlite/ext/icu/README.txt
new file mode 100755
index 0000000..5c995cc
--- /dev/null
+++ b/third_party/sqlite/ext/icu/README.txt
@@ -0,0 +1,170 @@
+
+This directory contains source code for the SQLite "ICU" extension, an
+integration of the "International Components for Unicode" library with
+SQLite. Documentation follows.
+
+ 1. Features
+
+ 1.1 SQL Scalars upper() and lower()
+ 1.2 Unicode Aware LIKE Operator
+ 1.3 ICU Collation Sequences
+ 1.4 SQL REGEXP Operator
+
+ 2. Compilation and Usage
+
+ 3. Bugs, Problems and Security Issues
+
+ 3.1 The "case_sensitive_like" Pragma
+ 3.2 The SQLITE_MAX_LIKE_PATTERN_LENGTH Macro
+ 3.3 Collation Sequence Security Issue
+
+
+1. FEATURES
+
+ 1.1 SQL Scalars upper() and lower()
+
+ SQLite's built-in implementations of these two functions only
+ provide case mapping for the 26 letters used in the English
+ language. The ICU based functions provided by this extension
+ provide case mapping, where defined, for the full range of
+ unicode characters.
+
+ ICU provides two types of case mapping, "general" case mapping and
+ "language specific". Refer to ICU documentation for the differences
+ between the two. Specifically:
+
+ http://www.icu-project.org/userguide/caseMappings.html
+ http://www.icu-project.org/userguide/posix.html#case_mappings
+
+ To utilise "general" case mapping, the upper() or lower() scalar
+ functions are invoked with one argument:
+
+ upper('ABC') -> 'abc'
+ lower('abc') -> 'ABC'
+
+ To access ICU "language specific" case mapping, upper() or lower()
+ should be invoked with two arguments. The second argument is the name
+ of the locale to use. Passing an empty string ("") or SQL NULL value
+ as the second argument is the same as invoking the 1 argument version
+ of upper() or lower():
+
+ lower('I', 'en_us') -> 'i'
+ lower('I', 'tr_tr') -> 'ı' (small dotless i)
+
+ 1.2 Unicode Aware LIKE Operator
+
+ Similarly to the upper() and lower() functions, the built-in SQLite LIKE
+ operator understands case equivalence for the 26 letters of the English
+ language alphabet. The implementation of LIKE included in this
+ extension uses the ICU function u_foldCase() to provide case
+ independent comparisons for the full range of unicode characters.
+
+ The U_FOLD_CASE_DEFAULT flag is passed to u_foldCase(), meaning the
+ dotless 'I' character used in the Turkish language is considered
+ to be in the same equivalence class as the dotted 'I' character
+ used by many languages (including English).
+
+ 1.3 ICU Collation Sequences
+
+ A special SQL scalar function, icu_load_collation() is provided that
+ may be used to register ICU collation sequences with SQLite. It
+ is always called with exactly two arguments, the ICU locale
+ identifying the collation sequence to ICU, and the name of the
+ SQLite collation sequence to create. For example, to create an
+ SQLite collation sequence named "turkish" using Turkish language
+ sorting rules, the SQL statement:
+
+ SELECT icu_load_collation('tr_TR', 'turkish');
+
+ Or, for Australian English:
+
+ SELECT icu_load_collation('en_AU', 'australian');
+
+ The identifiers "turkish" and "australian" may then be used
+ as collation sequence identifiers in SQL statements:
+
+ CREATE TABLE aust_turkish_penpals(
+ australian_penpal_name TEXT COLLATE australian,
+ turkish_penpal_name TEXT COLLATE turkish
+ );
+
+ 1.4 SQL REGEXP Operator
+
+ This extension provides an implementation of the SQL binary
+ comparision operator "REGEXP", based on the regular expression functions
+ provided by the ICU library. The syntax of the operator is as described
+ in SQLite documentation:
+
+ <string> REGEXP <re-pattern>
+
+ This extension uses the ICU defaults for regular expression matching
+ behaviour. Specifically, this means that:
+
+ * Matching is case-sensitive,
+ * Regular expression comments are not allowed within patterns, and
+ * The '^' and '$' characters match the beginning and end of the
+ <string> argument, not the beginning and end of lines within
+ the <string> argument.
+
+ Even more specifically, the value passed to the "flags" parameter
+ of ICU C function uregex_open() is 0.
+
+
+2 COMPILATION AND USAGE
+
+ The easiest way to compile and use the ICU extension is to build
+ and use it as a dynamically loadable SQLite extension. To do this
+ using gcc on *nix:
+
+ gcc -shared icu.c `icu-config --ldflags` -o libSqliteIcu.so
+
+ You may need to add "-I" flags so that gcc can find sqlite3ext.h
+ and sqlite3.h. The resulting shared lib, libSqliteIcu.so, may be
+ loaded into sqlite in the same way as any other dynamically loadable
+ extension.
+
+
+3 BUGS, PROBLEMS AND SECURITY ISSUES
+
+ 3.1 The "case_sensitive_like" Pragma
+
+ This extension does not work well with the "case_sensitive_like"
+ pragma. If this pragma is used before the ICU extension is loaded,
+ then the pragma has no effect. If the pragma is used after the ICU
+ extension is loaded, then SQLite ignores the ICU implementation and
+ always uses the built-in LIKE operator.
+
+ The ICU extension LIKE operator is always case insensitive.
+
+ 3.2 The SQLITE_MAX_LIKE_PATTERN_LENGTH Macro
+
+ Passing very long patterns to the built-in SQLite LIKE operator can
+ cause a stack overflow. To curb this problem, SQLite defines the
+ SQLITE_MAX_LIKE_PATTERN_LENGTH macro as the maximum length of a
+ pattern in bytes (irrespective of encoding). The default value is
+ defined in internal header file "limits.h".
+
+ The ICU extension LIKE implementation suffers from the same
+ problem and uses the same solution. However, since the ICU extension
+ code does not include the SQLite file "limits.h", modifying
+ the default value therein does not affect the ICU extension.
+ The default value of SQLITE_MAX_LIKE_PATTERN_LENGTH used by
+ the ICU extension LIKE operator is 50000, defined in source
+ file "icu.c".
+
+ 3.3 Collation Sequence Security Issue
+
+ Internally, SQLite assumes that indices stored in database files
+ are sorted according to the collation sequence indicated by the
+ SQL schema. Changing the definition of a collation sequence after
+ an index has been built is therefore equivalent to database
+ corruption. The SQLite library is not very well tested under
+ these conditions, and may contain potential buffer overruns
+ or other programming errors that could be exploited by a malicious
+ programmer.
+
+ If the ICU extension is used in an environment where potentially
+ malicious users may execute arbitrary SQL (i.e. gears), they
+ should be prevented from invoking the icu_load_collation() function,
+ possibly using the authorisation callback.
+
diff --git a/third_party/sqlite/icu.c b/third_party/sqlite/ext/icu/icu.c
index aa9b510..57d59a6 100644..100755
--- a/third_party/sqlite/icu.c
+++ b/third_party/sqlite/ext/icu/icu.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: icu.c,v 1.6 2007/06/22 15:21:16 danielk1977 Exp $
+** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
**
** This file implements an integration between the ICU library
** ("International Components for Unicode", an open-source library
@@ -39,7 +39,7 @@
#include <assert.h>
// TODO(evanm): this is cut'n'pasted from fts2.c. Why is it necessary?
-#if defined(SQLITE_ENABLE_FTS2) && !defined(SQLITE_CORE)
+#if !defined(SQLITE_CORE)
# define SQLITE_CORE 1
#endif
@@ -303,7 +303,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
/*
** Implementations of scalar functions for case mapping - upper() and
-** lower(). Function upper() converts it's input to upper-case (ABC).
+** lower(). Function upper() converts its input to upper-case (ABC).
** Function lower() converts to lower-case (abc).
**
** ICU provides two types of case mapping, "general" case mapping and
diff --git a/third_party/sqlite/ext/rtree/README b/third_party/sqlite/ext/rtree/README
new file mode 100755
index 0000000..3736f45
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/README
@@ -0,0 +1,120 @@
+
+This directory contains an SQLite extension that implements a virtual
+table type that allows users to create, query and manipulate r-tree[1]
+data structures inside of SQLite databases. Users create, populate
+and query r-tree structures using ordinary SQL statements.
+
+ 1. SQL Interface
+
+ 1.1 Table Creation
+ 1.2 Data Manipulation
+ 1.3 Data Querying
+ 1.4 Introspection and Analysis
+
+ 2. Compilation and Deployment
+
+ 3. References
+
+
+1. SQL INTERFACE
+
+ 1.1 Table Creation.
+
+ All r-tree virtual tables have an odd number of columns between
+ 3 and 11. Unlike regular SQLite tables, r-tree tables are strongly
+ typed.
+
+ The leftmost column is always the pimary key and contains 64-bit
+ integer values. Each subsequent column contains a 32-bit real
+ value. For each pair of real values, the first (leftmost) must be
+ less than or equal to the second. R-tree tables may be
+ constructed using the following syntax:
+
+ CREATE VIRTUAL TABLE <name> USING rtree(<column-names>)
+
+ For example:
+
+ CREATE VIRTUAL TABLE boxes USING rtree(boxno, xmin, xmax, ymin, ymax);
+ INSERT INTO boxes VALUES(1, 1.0, 3.0, 2.0, 4.0);
+
+ Constructing a virtual r-tree table <name> creates the following three
+ real tables in the database to store the data structure:
+
+ <name>_node
+ <name>_rowid
+ <name>_parent
+
+ Dropping or modifying the contents of these tables directly will
+ corrupt the r-tree structure. To delete an r-tree from a database,
+ use a regular DROP TABLE statement:
+
+ DROP TABLE <name>;
+
+ Dropping the main r-tree table automatically drops the automatically
+ created tables.
+
+ 1.2 Data Manipulation (INSERT, UPDATE, DELETE).
+
+ The usual INSERT, UPDATE or DELETE syntax is used to manipulate data
+ stored in an r-tree table. Please note the following:
+
+ * Inserting a NULL value into the primary key column has the
+ same effect as inserting a NULL into an INTEGER PRIMARY KEY
+ column of a regular table. The system automatically assigns
+ an unused integer key value to the new record. Usually, this
+ is one greater than the largest primary key value currently
+ present in the table.
+
+ * Attempting to insert a duplicate primary key value fails with
+ an SQLITE_CONSTRAINT error.
+
+ * Attempting to insert or modify a record such that the value
+ stored in the (N*2)th column is greater than that stored in
+ the (N*2+1)th column fails with an SQLITE_CONSTRAINT error.
+
+ * When a record is inserted, values are always converted to
+ the required type (64-bit integer or 32-bit real) as if they
+ were part of an SQL CAST expression. Non-numeric strings are
+ converted to zero.
+
+ 1.3 Queries.
+
+ R-tree tables may be queried using all of the same SQL syntax supported
+ by regular tables. However, some query patterns are more efficient
+ than others.
+
+ R-trees support fast lookup by primary key value (O(logN), like
+ regular tables).
+
+ Any combination of equality and range (<, <=, >, >=) constraints
+ on spatial data columns may be used to optimize other queries. This
+ is the key advantage to using r-tree tables instead of creating
+ indices on regular tables.
+
+ 1.4 Introspection and Analysis.
+
+ TODO: Describe rtreenode() and rtreedepth() functions.
+
+
+2. COMPILATION AND USAGE
+
+ The easiest way to compile and use the RTREE extension is to build
+ and use it as a dynamically loadable SQLite extension. To do this
+ using gcc on *nix:
+
+ gcc -shared rtree.c -o libSqliteRtree.so
+
+ You may need to add "-I" flags so that gcc can find sqlite3ext.h
+ and sqlite3.h. The resulting shared lib, libSqliteRtree.so, may be
+ loaded into sqlite in the same way as any other dynamicly loadable
+ extension.
+
+
+3. REFERENCES
+
+ [1] Atonin Guttman, "R-trees - A Dynamic Index Structure For Spatial
+ Searching", University of California Berkeley, 1984.
+
+ [2] Norbert Beckmann, Hans-Peter Kriegel, Ralf Schneider, Bernhard Seeger,
+ "The R*-tree: An Efficient and Robust Access Method for Points and
+ Rectangles", Universitaet Bremen, 1990.
diff --git a/third_party/sqlite/ext/rtree/rtree.c b/third_party/sqlite/ext/rtree/rtree.c
new file mode 100755
index 0000000..39116ce
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree.c
@@ -0,0 +1,2826 @@
+/*
+** 2001 September 15
+**
+** 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 contains code for implementations of the r-tree and r*-tree
+** algorithms packaged as an SQLite virtual table module.
+**
+** $Id: rtree.c,v 1.7 2008/07/16 14:43:35 drh Exp $
+*/
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
+
+/*
+** This file contains an implementation of a couple of different variants
+** of the r-tree algorithm. See the README file for further details. The
+** same data-structure is used for all, but the algorithms for insert and
+** delete operations vary. The variants used are selected at compile time
+** by defining the following symbols:
+*/
+
+/* Either, both or none of the following may be set to activate
+** r*tree variant algorithms.
+*/
+#define VARIANT_RSTARTREE_CHOOSESUBTREE 0
+#define VARIANT_RSTARTREE_REINSERT 1
+
+/*
+** Exactly one of the following must be set to 1.
+*/
+#define VARIANT_GUTTMAN_QUADRATIC_SPLIT 0
+#define VARIANT_GUTTMAN_LINEAR_SPLIT 0
+#define VARIANT_RSTARTREE_SPLIT 1
+
+#define VARIANT_GUTTMAN_SPLIT \
+ (VARIANT_GUTTMAN_LINEAR_SPLIT||VARIANT_GUTTMAN_QUADRATIC_SPLIT)
+
+#if VARIANT_GUTTMAN_QUADRATIC_SPLIT
+ #define PickNext QuadraticPickNext
+ #define PickSeeds QuadraticPickSeeds
+ #define AssignCells splitNodeGuttman
+#endif
+#if VARIANT_GUTTMAN_LINEAR_SPLIT
+ #define PickNext LinearPickNext
+ #define PickSeeds LinearPickSeeds
+ #define AssignCells splitNodeGuttman
+#endif
+#if VARIANT_RSTARTREE_SPLIT
+ #define AssignCells splitNodeStartree
+#endif
+
+
+#ifndef SQLITE_CORE
+ #include "sqlite3ext.h"
+ SQLITE_EXTENSION_INIT1
+#else
+ #include "sqlite3.h"
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#ifndef SQLITE_AMALGAMATION
+typedef sqlite3_int64 i64;
+typedef unsigned char u8;
+typedef unsigned int u32;
+#endif
+
+typedef struct Rtree Rtree;
+typedef struct RtreeCursor RtreeCursor;
+typedef struct RtreeNode RtreeNode;
+typedef struct RtreeCell RtreeCell;
+typedef struct RtreeConstraint RtreeConstraint;
+typedef union RtreeCoord RtreeCoord;
+
+/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
+#define RTREE_MAX_DIMENSIONS 5
+
+/* Size of hash table Rtree.aHash. This hash table is not expected to
+** ever contain very many entries, so a fixed number of buckets is
+** used.
+*/
+#define HASHSIZE 128
+
+/*
+** An rtree virtual-table object.
+*/
+struct Rtree {
+ sqlite3_vtab base;
+ sqlite3 *db; /* Host database connection */
+ int iNodeSize; /* Size in bytes of each node in the node table */
+ int nDim; /* Number of dimensions */
+ int nBytesPerCell; /* Bytes consumed per cell */
+ int iDepth; /* Current depth of the r-tree structure */
+ char *zDb; /* Name of database containing r-tree table */
+ char *zName; /* Name of r-tree table */
+ RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
+ int nBusy; /* Current number of users of this structure */
+
+ /* List of nodes removed during a CondenseTree operation. List is
+ ** linked together via the pointer normally used for hash chains -
+ ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree
+ ** headed by the node (leaf nodes have RtreeNode.iNode==0).
+ */
+ RtreeNode *pDeleted;
+ int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
+
+ /* Statements to read/write/delete a record from xxx_node */
+ sqlite3_stmt *pReadNode;
+ sqlite3_stmt *pWriteNode;
+ sqlite3_stmt *pDeleteNode;
+
+ /* Statements to read/write/delete a record from xxx_rowid */
+ sqlite3_stmt *pReadRowid;
+ sqlite3_stmt *pWriteRowid;
+ sqlite3_stmt *pDeleteRowid;
+
+ /* Statements to read/write/delete a record from xxx_parent */
+ sqlite3_stmt *pReadParent;
+ sqlite3_stmt *pWriteParent;
+ sqlite3_stmt *pDeleteParent;
+
+ int eCoordType;
+};
+
+/* Possible values for eCoordType: */
+#define RTREE_COORD_REAL32 0
+#define RTREE_COORD_INT32 1
+
+/*
+** The minimum number of cells allowed for a node is a third of the
+** maximum. In Gutman's notation:
+**
+** m = M/3
+**
+** If an R*-tree "Reinsert" operation is required, the same number of
+** cells are removed from the overfull node and reinserted into the tree.
+*/
+#define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3)
+#define RTREE_REINSERT(p) RTREE_MINCELLS(p)
+#define RTREE_MAXCELLS 51
+
+/*
+** An rtree cursor object.
+*/
+struct RtreeCursor {
+ sqlite3_vtab_cursor base;
+ RtreeNode *pNode; /* Node cursor is currently pointing at */
+ int iCell; /* Index of current cell in pNode */
+ int iStrategy; /* Copy of idxNum search parameter */
+ int nConstraint; /* Number of entries in aConstraint */
+ RtreeConstraint *aConstraint; /* Search constraints. */
+};
+
+union RtreeCoord {
+ float f;
+ int i;
+};
+
+/*
+** The argument is an RtreeCoord. Return the value stored within the RtreeCoord
+** formatted as a double. This macro assumes that local variable pRtree points
+** to the Rtree structure associated with the RtreeCoord.
+*/
+#define DCOORD(coord) ( \
+ (pRtree->eCoordType==RTREE_COORD_REAL32) ? \
+ ((double)coord.f) : \
+ ((double)coord.i) \
+)
+
+/*
+** A search constraint.
+*/
+struct RtreeConstraint {
+ int iCoord; /* Index of constrained coordinate */
+ int op; /* Constraining operation */
+ double rValue; /* Constraint value. */
+};
+
+/* Possible values for RtreeConstraint.op */
+#define RTREE_EQ 0x41
+#define RTREE_LE 0x42
+#define RTREE_LT 0x43
+#define RTREE_GE 0x44
+#define RTREE_GT 0x45
+
+/*
+** An rtree structure node.
+**
+** Data format (RtreeNode.zData):
+**
+** 1. If the node is the root node (node 1), then the first 2 bytes
+** of the node contain the tree depth as a big-endian integer.
+** For non-root nodes, the first 2 bytes are left unused.
+**
+** 2. The next 2 bytes contain the number of entries currently
+** stored in the node.
+**
+** 3. The remainder of the node contains the node entries. Each entry
+** consists of a single 8-byte integer followed by an even number
+** of 4-byte coordinates. For leaf nodes the integer is the rowid
+** of a record. For internal nodes it is the node number of a
+** child page.
+*/
+struct RtreeNode {
+ RtreeNode *pParent; /* Parent node */
+ i64 iNode;
+ int nRef;
+ int isDirty;
+ u8 *zData;
+ RtreeNode *pNext; /* Next node in this hash chain */
+};
+#define NCELL(pNode) readInt16(&(pNode)->zData[2])
+
+/*
+** Structure to store a deserialized rtree record.
+*/
+struct RtreeCell {
+ i64 iRowid;
+ RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
+};
+
+#define MAX(x,y) ((x) < (y) ? (y) : (x))
+#define MIN(x,y) ((x) > (y) ? (y) : (x))
+
+/*
+** Functions to deserialize a 16 bit integer, 32 bit real number and
+** 64 bit integer. The deserialized value is returned.
+*/
+static int readInt16(u8 *p){
+ return (p[0]<<8) + p[1];
+}
+static void readCoord(u8 *p, RtreeCoord *pCoord){
+ u32 i = (
+ (((u32)p[0]) << 24) +
+ (((u32)p[1]) << 16) +
+ (((u32)p[2]) << 8) +
+ (((u32)p[3]) << 0)
+ );
+ *(u32 *)pCoord = i;
+}
+static i64 readInt64(u8 *p){
+ return (
+ (((i64)p[0]) << 56) +
+ (((i64)p[1]) << 48) +
+ (((i64)p[2]) << 40) +
+ (((i64)p[3]) << 32) +
+ (((i64)p[4]) << 24) +
+ (((i64)p[5]) << 16) +
+ (((i64)p[6]) << 8) +
+ (((i64)p[7]) << 0)
+ );
+}
+
+/*
+** Functions to serialize a 16 bit integer, 32 bit real number and
+** 64 bit integer. The value returned is the number of bytes written
+** to the argument buffer (always 2, 4 and 8 respectively).
+*/
+static int writeInt16(u8 *p, int i){
+ p[0] = (i>> 8)&0xFF;
+ p[1] = (i>> 0)&0xFF;
+ return 2;
+}
+static int writeCoord(u8 *p, RtreeCoord *pCoord){
+ u32 i;
+ assert( sizeof(RtreeCoord)==4 );
+ assert( sizeof(u32)==4 );
+ i = *(u32 *)pCoord;
+ p[0] = (i>>24)&0xFF;
+ p[1] = (i>>16)&0xFF;
+ p[2] = (i>> 8)&0xFF;
+ p[3] = (i>> 0)&0xFF;
+ return 4;
+}
+static int writeInt64(u8 *p, i64 i){
+ p[0] = (i>>56)&0xFF;
+ p[1] = (i>>48)&0xFF;
+ p[2] = (i>>40)&0xFF;
+ p[3] = (i>>32)&0xFF;
+ p[4] = (i>>24)&0xFF;
+ p[5] = (i>>16)&0xFF;
+ p[6] = (i>> 8)&0xFF;
+ p[7] = (i>> 0)&0xFF;
+ return 8;
+}
+
+/*
+** Increment the reference count of node p.
+*/
+static void nodeReference(RtreeNode *p){
+ if( p ){
+ p->nRef++;
+ }
+}
+
+/*
+** Clear the content of node p (set all bytes to 0x00).
+*/
+static void nodeZero(Rtree *pRtree, RtreeNode *p){
+ if( p ){
+ memset(&p->zData[2], 0, pRtree->iNodeSize-2);
+ p->isDirty = 1;
+ }
+}
+
+/*
+** Given a node number iNode, return the corresponding key to use
+** in the Rtree.aHash table.
+*/
+static int nodeHash(i64 iNode){
+ return (
+ (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^
+ (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0)
+ ) % HASHSIZE;
+}
+
+/*
+** Search the node hash table for node iNode. If found, return a pointer
+** to it. Otherwise, return 0.
+*/
+static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){
+ RtreeNode *p;
+ assert( iNode!=0 );
+ for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext);
+ return p;
+}
+
+/*
+** Add node pNode to the node hash table.
+*/
+static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){
+ if( pNode ){
+ int iHash;
+ assert( pNode->pNext==0 );
+ iHash = nodeHash(pNode->iNode);
+ pNode->pNext = pRtree->aHash[iHash];
+ pRtree->aHash[iHash] = pNode;
+ }
+}
+
+/*
+** Remove node pNode from the node hash table.
+*/
+static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){
+ RtreeNode **pp;
+ if( pNode->iNode!=0 ){
+ pp = &pRtree->aHash[nodeHash(pNode->iNode)];
+ for( ; (*pp)!=pNode; pp = &(*pp)->pNext){ assert(*pp); }
+ *pp = pNode->pNext;
+ pNode->pNext = 0;
+ }
+}
+
+/*
+** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0),
+** indicating that node has not yet been assigned a node number. It is
+** assigned a node number when nodeWrite() is called to write the
+** node contents out to the database.
+*/
+static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent, int zero){
+ RtreeNode *pNode;
+ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize);
+ if( pNode ){
+ memset(pNode, 0, sizeof(RtreeNode) + (zero?pRtree->iNodeSize:0));
+ pNode->zData = (u8 *)&pNode[1];
+ pNode->nRef = 1;
+ pNode->pParent = pParent;
+ pNode->isDirty = 1;
+ nodeReference(pParent);
+ }
+ return pNode;
+}
+
+/*
+** Obtain a reference to an r-tree node.
+*/
+static int
+nodeAcquire(
+ Rtree *pRtree, /* R-tree structure */
+ i64 iNode, /* Node number to load */
+ RtreeNode *pParent, /* Either the parent node or NULL */
+ RtreeNode **ppNode /* OUT: Acquired node */
+){
+ int rc;
+ RtreeNode *pNode;
+
+ /* Check if the requested node is already in the hash table. If so,
+ ** increase its reference count and return it.
+ */
+ if( (pNode = nodeHashLookup(pRtree, iNode)) ){
+ assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
+ if( pParent ){
+ pNode->pParent = pParent;
+ }
+ pNode->nRef++;
+ *ppNode = pNode;
+ return SQLITE_OK;
+ }
+
+ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize);
+ if( !pNode ){
+ *ppNode = 0;
+ return SQLITE_NOMEM;
+ }
+ pNode->pParent = pParent;
+ pNode->zData = (u8 *)&pNode[1];
+ pNode->nRef = 1;
+ pNode->iNode = iNode;
+ pNode->isDirty = 0;
+ pNode->pNext = 0;
+
+ sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
+ rc = sqlite3_step(pRtree->pReadNode);
+ if( rc==SQLITE_ROW ){
+ const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);
+ memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
+ nodeReference(pParent);
+ }else{
+ sqlite3_free(pNode);
+ pNode = 0;
+ }
+
+ *ppNode = pNode;
+ rc = sqlite3_reset(pRtree->pReadNode);
+
+ if( rc==SQLITE_OK && iNode==1 ){
+ pRtree->iDepth = readInt16(pNode->zData);
+ }
+
+ assert( (rc==SQLITE_OK && pNode) || (pNode==0 && rc!=SQLITE_OK) );
+ nodeHashInsert(pRtree, pNode);
+
+ return rc;
+}
+
+/*
+** Overwrite cell iCell of node pNode with the contents of pCell.
+*/
+static void nodeOverwriteCell(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ RtreeCell *pCell,
+ int iCell
+){
+ int ii;
+ u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
+ p += writeInt64(p, pCell->iRowid);
+ for(ii=0; ii<(pRtree->nDim*2); ii++){
+ p += writeCoord(p, &pCell->aCoord[ii]);
+ }
+ pNode->isDirty = 1;
+}
+
+/*
+** Remove cell the cell with index iCell from node pNode.
+*/
+static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){
+ u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
+ u8 *pSrc = &pDst[pRtree->nBytesPerCell];
+ int nByte = (NCELL(pNode) - iCell - 1) * pRtree->nBytesPerCell;
+ memmove(pDst, pSrc, nByte);
+ writeInt16(&pNode->zData[2], NCELL(pNode)-1);
+ pNode->isDirty = 1;
+}
+
+/*
+** Insert the contents of cell pCell into node pNode. If the insert
+** is successful, return SQLITE_OK.
+**
+** If there is not enough free space in pNode, return SQLITE_FULL.
+*/
+static int
+nodeInsertCell(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ RtreeCell *pCell
+){
+ int nCell; /* Current number of cells in pNode */
+ int nMaxCell; /* Maximum number of cells for pNode */
+
+ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell;
+ nCell = NCELL(pNode);
+
+ assert(nCell<=nMaxCell);
+
+ if( nCell<nMaxCell ){
+ nodeOverwriteCell(pRtree, pNode, pCell, nCell);
+ writeInt16(&pNode->zData[2], nCell+1);
+ pNode->isDirty = 1;
+ }
+
+ return (nCell==nMaxCell);
+}
+
+/*
+** If the node is dirty, write it out to the database.
+*/
+static int
+nodeWrite(Rtree *pRtree, RtreeNode *pNode){
+ int rc = SQLITE_OK;
+ if( pNode->isDirty ){
+ sqlite3_stmt *p = pRtree->pWriteNode;
+ if( pNode->iNode ){
+ sqlite3_bind_int64(p, 1, pNode->iNode);
+ }else{
+ sqlite3_bind_null(p, 1);
+ }
+ sqlite3_bind_blob(p, 2, pNode->zData, pRtree->iNodeSize, SQLITE_STATIC);
+ sqlite3_step(p);
+ pNode->isDirty = 0;
+ rc = sqlite3_reset(p);
+ if( pNode->iNode==0 && rc==SQLITE_OK ){
+ pNode->iNode = sqlite3_last_insert_rowid(pRtree->db);
+ nodeHashInsert(pRtree, pNode);
+ }
+ }
+ return rc;
+}
+
+/*
+** Release a reference to a node. If the node is dirty and the reference
+** count drops to zero, the node data is written to the database.
+*/
+static int
+nodeRelease(Rtree *pRtree, RtreeNode *pNode){
+ int rc = SQLITE_OK;
+ if( pNode ){
+ assert( pNode->nRef>0 );
+ pNode->nRef--;
+ if( pNode->nRef==0 ){
+ if( pNode->iNode==1 ){
+ pRtree->iDepth = -1;
+ }
+ if( pNode->pParent ){
+ rc = nodeRelease(pRtree, pNode->pParent);
+ }
+ if( rc==SQLITE_OK ){
+ rc = nodeWrite(pRtree, pNode);
+ }
+ nodeHashDelete(pRtree, pNode);
+ sqlite3_free(pNode);
+ }
+ }
+ return rc;
+}
+
+/*
+** Return the 64-bit integer value associated with cell iCell of
+** node pNode. If pNode is a leaf node, this is a rowid. If it is
+** an internal node, then the 64-bit integer is a child page number.
+*/
+static i64 nodeGetRowid(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ int iCell
+){
+ assert( iCell<NCELL(pNode) );
+ return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]);
+}
+
+/*
+** Return coordinate iCoord from cell iCell in node pNode.
+*/
+static void nodeGetCoord(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ int iCell,
+ int iCoord,
+ RtreeCoord *pCoord /* Space to write result to */
+){
+ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
+}
+
+/*
+** Deserialize cell iCell of node pNode. Populate the structure pointed
+** to by pCell with the results.
+*/
+static void nodeGetCell(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ int iCell,
+ RtreeCell *pCell
+){
+ int ii;
+ pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
+ for(ii=0; ii<pRtree->nDim*2; ii++){
+ nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]);
+ }
+}
+
+
+/* Forward declaration for the function that does the work of
+** the virtual table module xCreate() and xConnect() methods.
+*/
+static int rtreeInit(
+ sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int, int
+);
+
+/*
+** Rtree virtual table module xCreate method.
+*/
+static int rtreeCreate(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1, (int)pAux);
+}
+
+/*
+** Rtree virtual table module xConnect method.
+*/
+static int rtreeConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0, (int)pAux);
+}
+
+/*
+** Increment the r-tree reference count.
+*/
+static void rtreeReference(Rtree *pRtree){
+ pRtree->nBusy++;
+}
+
+/*
+** Decrement the r-tree reference count. When the reference count reaches
+** zero the structure is deleted.
+*/
+static void rtreeRelease(Rtree *pRtree){
+ pRtree->nBusy--;
+ if( pRtree->nBusy==0 ){
+ sqlite3_finalize(pRtree->pReadNode);
+ sqlite3_finalize(pRtree->pWriteNode);
+ sqlite3_finalize(pRtree->pDeleteNode);
+ sqlite3_finalize(pRtree->pReadRowid);
+ sqlite3_finalize(pRtree->pWriteRowid);
+ sqlite3_finalize(pRtree->pDeleteRowid);
+ sqlite3_finalize(pRtree->pReadParent);
+ sqlite3_finalize(pRtree->pWriteParent);
+ sqlite3_finalize(pRtree->pDeleteParent);
+ sqlite3_free(pRtree);
+ }
+}
+
+/*
+** Rtree virtual table module xDisconnect method.
+*/
+static int rtreeDisconnect(sqlite3_vtab *pVtab){
+ rtreeRelease((Rtree *)pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Rtree virtual table module xDestroy method.
+*/
+static int rtreeDestroy(sqlite3_vtab *pVtab){
+ Rtree *pRtree = (Rtree *)pVtab;
+ int rc;
+ char *zCreate = sqlite3_mprintf(
+ "DROP TABLE '%q'.'%q_node';"
+ "DROP TABLE '%q'.'%q_rowid';"
+ "DROP TABLE '%q'.'%q_parent';",
+ pRtree->zDb, pRtree->zName,
+ pRtree->zDb, pRtree->zName,
+ pRtree->zDb, pRtree->zName
+ );
+ if( !zCreate ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0);
+ sqlite3_free(zCreate);
+ }
+ if( rc==SQLITE_OK ){
+ rtreeRelease(pRtree);
+ }
+
+ return rc;
+}
+
+/*
+** Rtree virtual table module xOpen method.
+*/
+static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ int rc = SQLITE_NOMEM;
+ RtreeCursor *pCsr;
+
+ pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor));
+ if( pCsr ){
+ memset(pCsr, 0, sizeof(RtreeCursor));
+ pCsr->base.pVtab = pVTab;
+ rc = SQLITE_OK;
+ }
+ *ppCursor = (sqlite3_vtab_cursor *)pCsr;
+
+ return rc;
+}
+
+/*
+** Rtree virtual table module xClose method.
+*/
+static int rtreeClose(sqlite3_vtab_cursor *cur){
+ Rtree *pRtree = (Rtree *)(cur->pVtab);
+ int rc;
+ RtreeCursor *pCsr = (RtreeCursor *)cur;
+ sqlite3_free(pCsr->aConstraint);
+ rc = nodeRelease(pRtree, pCsr->pNode);
+ sqlite3_free(pCsr);
+ return rc;
+}
+
+/*
+** Rtree virtual table module xEof method.
+**
+** Return non-zero if the cursor does not currently point to a valid
+** record (i.e if the scan has finished), or zero otherwise.
+*/
+static int rtreeEof(sqlite3_vtab_cursor *cur){
+ RtreeCursor *pCsr = (RtreeCursor *)cur;
+ return (pCsr->pNode==0);
+}
+
+/*
+** Cursor pCursor currently points to a cell in a non-leaf page.
+** Return true if the sub-tree headed by the cell is filtered
+** (excluded) by the constraints in the pCursor->aConstraint[]
+** array, or false otherwise.
+*/
+static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){
+ RtreeCell cell;
+ int ii;
+ int bRes = 0;
+
+ nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
+ for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
+ RtreeConstraint *p = &pCursor->aConstraint[ii];
+ double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
+ double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
+
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ
+ );
+
+ switch( p->op ){
+ case RTREE_LE: case RTREE_LT: bRes = p->rValue<cell_min; break;
+ case RTREE_GE: case RTREE_GT: bRes = p->rValue>cell_max; break;
+ case RTREE_EQ:
+ bRes = (p->rValue>cell_max || p->rValue<cell_min);
+ break;
+ }
+ }
+
+ return bRes;
+}
+
+/*
+** Return true if the cell that cursor pCursor currently points to
+** would be filtered (excluded) by the constraints in the
+** pCursor->aConstraint[] array, or false otherwise.
+**
+** This function assumes that the cell is part of a leaf node.
+*/
+static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor){
+ RtreeCell cell;
+ int ii;
+
+ nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
+ for(ii=0; ii<pCursor->nConstraint; ii++){
+ RtreeConstraint *p = &pCursor->aConstraint[ii];
+ double coord = DCOORD(cell.aCoord[p->iCoord]);
+ int res;
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ
+ );
+ switch( p->op ){
+ case RTREE_LE: res = (coord<=p->rValue); break;
+ case RTREE_LT: res = (coord<p->rValue); break;
+ case RTREE_GE: res = (coord>=p->rValue); break;
+ case RTREE_GT: res = (coord>p->rValue); break;
+ case RTREE_EQ: res = (coord==p->rValue); break;
+ }
+
+ if( !res ) return 1;
+ }
+
+ return 0;
+}
+
+/*
+** Cursor pCursor currently points at a node that heads a sub-tree of
+** height iHeight (if iHeight==0, then the node is a leaf). Descend
+** to point to the left-most cell of the sub-tree that matches the
+** configured constraints.
+*/
+static int descendToCell(
+ Rtree *pRtree,
+ RtreeCursor *pCursor,
+ int iHeight,
+ int *pEof /* OUT: Set to true if cannot descend */
+){
+ int isEof;
+ int rc;
+ int ii;
+ RtreeNode *pChild;
+ sqlite3_int64 iRowid;
+
+ RtreeNode *pSavedNode = pCursor->pNode;
+ int iSavedCell = pCursor->iCell;
+
+ assert( iHeight>=0 );
+
+ if( iHeight==0 ){
+ isEof = testRtreeEntry(pRtree, pCursor);
+ }else{
+ isEof = testRtreeCell(pRtree, pCursor);
+ }
+ if( isEof || iHeight==0 ){
+ *pEof = isEof;
+ return SQLITE_OK;
+ }
+
+ iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell);
+ rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ nodeRelease(pRtree, pCursor->pNode);
+ pCursor->pNode = pChild;
+ isEof = 1;
+ for(ii=0; isEof && ii<NCELL(pChild); ii++){
+ pCursor->iCell = ii;
+ rc = descendToCell(pRtree, pCursor, iHeight-1, &isEof);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+
+ if( isEof ){
+ assert( pCursor->pNode==pChild );
+ nodeReference(pSavedNode);
+ nodeRelease(pRtree, pChild);
+ pCursor->pNode = pSavedNode;
+ pCursor->iCell = iSavedCell;
+ }
+
+ *pEof = isEof;
+ return SQLITE_OK;
+}
+
+/*
+** One of the cells in node pNode is guaranteed to have a 64-bit
+** integer value equal to iRowid. Return the index of this cell.
+*/
+static int nodeRowidIndex(Rtree *pRtree, RtreeNode *pNode, i64 iRowid){
+ int ii;
+ for(ii=0; nodeGetRowid(pRtree, pNode, ii)!=iRowid; ii++){
+ assert( ii<(NCELL(pNode)-1) );
+ }
+ return ii;
+}
+
+/*
+** Return the index of the cell containing a pointer to node pNode
+** in its parent. If pNode is the root node, return -1.
+*/
+static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode){
+ RtreeNode *pParent = pNode->pParent;
+ if( pParent ){
+ return nodeRowidIndex(pRtree, pParent, pNode->iNode);
+ }
+ return -1;
+}
+
+/*
+** Rtree virtual table module xNext method.
+*/
+static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
+ Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab);
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+ int rc = SQLITE_OK;
+
+ if( pCsr->iStrategy==1 ){
+ /* This "scan" is a direct lookup by rowid. There is no next entry. */
+ nodeRelease(pRtree, pCsr->pNode);
+ pCsr->pNode = 0;
+ }
+
+ else if( pCsr->pNode ){
+ /* Move to the next entry that matches the configured constraints. */
+ int iHeight = 0;
+ while( pCsr->pNode ){
+ RtreeNode *pNode = pCsr->pNode;
+ int nCell = NCELL(pNode);
+ for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){
+ int isEof;
+ rc = descendToCell(pRtree, pCsr, iHeight, &isEof);
+ if( rc!=SQLITE_OK || !isEof ){
+ return rc;
+ }
+ }
+ pCsr->pNode = pNode->pParent;
+ pCsr->iCell = nodeParentIndex(pRtree, pNode);
+ nodeReference(pCsr->pNode);
+ nodeRelease(pRtree, pNode);
+ iHeight++;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Rtree virtual table module xRowid method.
+*/
+static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
+ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+
+ assert(pCsr->pNode);
+ *pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
+
+ return SQLITE_OK;
+}
+
+/*
+** Rtree virtual table module xColumn method.
+*/
+static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ Rtree *pRtree = (Rtree *)cur->pVtab;
+ RtreeCursor *pCsr = (RtreeCursor *)cur;
+
+ if( i==0 ){
+ i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
+ sqlite3_result_int64(ctx, iRowid);
+ }else{
+ RtreeCoord c;
+ nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ sqlite3_result_double(ctx, c.f);
+ }else{
+ assert( pRtree->eCoordType==RTREE_COORD_INT32 );
+ sqlite3_result_int(ctx, c.i);
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Use nodeAcquire() to obtain the leaf node containing the record with
+** rowid iRowid. If successful, set *ppLeaf to point to the node and
+** return SQLITE_OK. If there is no such record in the table, set
+** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf
+** to zero and return an SQLite error code.
+*/
+static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
+ int rc;
+ *ppLeaf = 0;
+ sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid);
+ if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){
+ i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0);
+ rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
+ sqlite3_reset(pRtree->pReadRowid);
+ }else{
+ rc = sqlite3_reset(pRtree->pReadRowid);
+ }
+ return rc;
+}
+
+
+/*
+** Rtree virtual table module xFilter method.
+*/
+static int rtreeFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+
+ RtreeNode *pRoot = 0;
+ int ii;
+ int rc = SQLITE_OK;
+
+ rtreeReference(pRtree);
+
+ sqlite3_free(pCsr->aConstraint);
+ pCsr->aConstraint = 0;
+ pCsr->iStrategy = idxNum;
+
+ if( idxNum==1 ){
+ /* Special case - lookup by rowid. */
+ RtreeNode *pLeaf; /* Leaf on which the required cell resides */
+ i64 iRowid = sqlite3_value_int64(argv[0]);
+ rc = findLeafNode(pRtree, iRowid, &pLeaf);
+ pCsr->pNode = pLeaf;
+ if( pLeaf && rc==SQLITE_OK ){
+ pCsr->iCell = nodeRowidIndex(pRtree, pLeaf, iRowid);
+ }
+ }else{
+ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
+ ** with the configured constraints.
+ */
+ if( argc>0 ){
+ pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc);
+ pCsr->nConstraint = argc;
+ if( !pCsr->aConstraint ){
+ rc = SQLITE_NOMEM;
+ }else{
+ assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 );
+ for(ii=0; ii<argc; ii++){
+ RtreeConstraint *p = &pCsr->aConstraint[ii];
+ p->op = idxStr[ii*2];
+ p->iCoord = idxStr[ii*2+1]-'a';
+ p->rValue = sqlite3_value_double(argv[ii]);
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pCsr->pNode = 0;
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+ }
+ if( rc==SQLITE_OK ){
+ int isEof = 1;
+ int nCell = NCELL(pRoot);
+ pCsr->pNode = pRoot;
+ for(pCsr->iCell=0; rc==SQLITE_OK && pCsr->iCell<nCell; pCsr->iCell++){
+ assert( pCsr->pNode==pRoot );
+ rc = descendToCell(pRtree, pCsr, pRtree->iDepth, &isEof);
+ if( !isEof ){
+ break;
+ }
+ }
+ if( rc==SQLITE_OK && isEof ){
+ assert( pCsr->pNode==pRoot );
+ nodeRelease(pRtree, pRoot);
+ pCsr->pNode = 0;
+ }
+ assert( rc!=SQLITE_OK || !pCsr->pNode || pCsr->iCell<NCELL(pCsr->pNode) );
+ }
+ }
+
+ rtreeRelease(pRtree);
+ return rc;
+}
+
+/*
+** Rtree virtual table module xBestIndex method. There are three
+** table scan strategies to choose from (in order from most to
+** least desirable):
+**
+** idxNum idxStr Strategy
+** ------------------------------------------------
+** 1 Unused Direct lookup by rowid.
+** 2 See below R-tree query.
+** 3 Unused Full table scan.
+** ------------------------------------------------
+**
+** If strategy 1 or 3 is used, then idxStr is not meaningful. If strategy
+** 2 is used, idxStr is formatted to contain 2 bytes for each
+** constraint used. The first two bytes of idxStr correspond to
+** the constraint in sqlite3_index_info.aConstraintUsage[] with
+** (argvIndex==1) etc.
+**
+** The first of each pair of bytes in idxStr identifies the constraint
+** operator as follows:
+**
+** Operator Byte Value
+** ----------------------
+** = 0x41 ('A')
+** <= 0x42 ('B')
+** < 0x43 ('C')
+** >= 0x44 ('D')
+** > 0x45 ('E')
+** ----------------------
+**
+** The second of each pair of bytes identifies the coordinate column
+** to which the constraint applies. The leftmost coordinate column
+** is 'a', the second from the left 'b' etc.
+*/
+static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int rc = SQLITE_OK;
+ int ii, cCol;
+
+ int iIdx = 0;
+ char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
+ memset(zIdxStr, 0, sizeof(zIdxStr));
+
+ assert( pIdxInfo->idxStr==0 );
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
+
+ if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ /* We have an equality constraint on the rowid. Use strategy 1. */
+ int jj;
+ for(jj=0; jj<ii; jj++){
+ pIdxInfo->aConstraintUsage[jj].argvIndex = 0;
+ pIdxInfo->aConstraintUsage[jj].omit = 0;
+ }
+ pIdxInfo->idxNum = 1;
+ pIdxInfo->aConstraintUsage[ii].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[jj].omit = 1;
+ return SQLITE_OK;
+ }
+
+ if( p->usable && p->iColumn>0 ){
+ u8 op = 0;
+ switch( p->op ){
+ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
+ case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break;
+ case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break;
+ case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break;
+ case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break;
+ }
+ if( op ){
+ /* Make sure this particular constraint has not been used before.
+ ** If it has been used before, ignore it.
+ **
+ ** A <= or < can be used if there is a prior >= or >.
+ ** A >= or > can be used if there is a prior < or <=.
+ ** A <= or < is disqualified if there is a prior <=, <, or ==.
+ ** A >= or > is disqualified if there is a prior >=, >, or ==.
+ ** A == is disqualifed if there is any prior constraint.
+ */
+ int j, opmsk;
+ static const unsigned char compatible[] = { 0, 0, 1, 1, 2, 2 };
+ assert( compatible[RTREE_EQ & 7]==0 );
+ assert( compatible[RTREE_LT & 7]==1 );
+ assert( compatible[RTREE_LE & 7]==1 );
+ assert( compatible[RTREE_GT & 7]==2 );
+ assert( compatible[RTREE_GE & 7]==2 );
+ cCol = p->iColumn - 1 + 'a';
+ opmsk = compatible[op & 7];
+ for(j=0; j<iIdx; j+=2){
+ if( zIdxStr[j+1]==cCol && (compatible[zIdxStr[j] & 7] & opmsk)!=0 ){
+ op = 0;
+ break;
+ }
+ }
+ }
+ if( op ){
+ assert( iIdx<sizeof(zIdxStr)-1 );
+ zIdxStr[iIdx++] = op;
+ zIdxStr[iIdx++] = cCol;
+ pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
+ pIdxInfo->aConstraintUsage[ii].omit = 1;
+ }
+ }
+ }
+
+ pIdxInfo->idxNum = 2;
+ pIdxInfo->needToFreeIdxStr = 1;
+ if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
+ return SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+/*
+** Return the N-dimensional volumn of the cell stored in *p.
+*/
+static float cellArea(Rtree *pRtree, RtreeCell *p){
+ float area = 1.0;
+ int ii;
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ area = area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
+ }
+ return area;
+}
+
+/*
+** Return the margin length of cell p. The margin length is the sum
+** of the objects size in each dimension.
+*/
+static float cellMargin(Rtree *pRtree, RtreeCell *p){
+ float margin = 0.0;
+ int ii;
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
+ }
+ return margin;
+}
+
+/*
+** Store the union of cells p1 and p2 in p1.
+*/
+static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
+ int ii;
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f);
+ p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f);
+ }
+ }else{
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i);
+ p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i);
+ }
+ }
+}
+
+/*
+** Return the amount cell p would grow by if it were unioned with pCell.
+*/
+static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
+ float area;
+ RtreeCell cell;
+ memcpy(&cell, p, sizeof(RtreeCell));
+ area = cellArea(pRtree, &cell);
+ cellUnion(pRtree, &cell, pCell);
+ return (cellArea(pRtree, &cell)-area);
+}
+
+#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT
+static float cellOverlap(
+ Rtree *pRtree,
+ RtreeCell *p,
+ RtreeCell *aCell,
+ int nCell,
+ int iExclude
+){
+ int ii;
+ float overlap = 0.0;
+ for(ii=0; ii<nCell; ii++){
+ if( ii!=iExclude ){
+ int jj;
+ float o = 1.0;
+ for(jj=0; jj<(pRtree->nDim*2); jj+=2){
+ double x1;
+ double x2;
+
+ x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
+ x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
+
+ if( x2<x1 ){
+ o = 0.0;
+ break;
+ }else{
+ o = o * (x2-x1);
+ }
+ }
+ overlap += o;
+ }
+ }
+ return overlap;
+}
+#endif
+
+#if VARIANT_RSTARTREE_CHOOSESUBTREE
+static float cellOverlapEnlargement(
+ Rtree *pRtree,
+ RtreeCell *p,
+ RtreeCell *pInsert,
+ RtreeCell *aCell,
+ int nCell,
+ int iExclude
+){
+ float before;
+ float after;
+ before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
+ cellUnion(pRtree, p, pInsert);
+ after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
+ return after-before;
+}
+#endif
+
+
+/*
+** This function implements the ChooseLeaf algorithm from Gutman[84].
+** ChooseSubTree in r*tree terminology.
+*/
+static int ChooseLeaf(
+ Rtree *pRtree, /* Rtree table */
+ RtreeCell *pCell, /* Cell to insert into rtree */
+ int iHeight, /* Height of sub-tree rooted at pCell */
+ RtreeNode **ppLeaf /* OUT: Selected leaf page */
+){
+ int rc;
+ int ii;
+ RtreeNode *pNode;
+ rc = nodeAcquire(pRtree, 1, 0, &pNode);
+
+ for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
+ int iCell;
+ sqlite3_int64 iBest;
+
+ float fMinGrowth;
+ float fMinArea;
+ float fMinOverlap;
+
+ int nCell = NCELL(pNode);
+ RtreeCell cell;
+ RtreeNode *pChild;
+
+ RtreeCell *aCell = 0;
+
+#if VARIANT_RSTARTREE_CHOOSESUBTREE
+ if( ii==(pRtree->iDepth-1) ){
+ int jj;
+ aCell = sqlite3_malloc(sizeof(RtreeCell)*nCell);
+ if( !aCell ){
+ rc = SQLITE_NOMEM;
+ nodeRelease(pRtree, pNode);
+ pNode = 0;
+ continue;
+ }
+ for(jj=0; jj<nCell; jj++){
+ nodeGetCell(pRtree, pNode, jj, &aCell[jj]);
+ }
+ }
+#endif
+
+ /* Select the child node which will be enlarged the least if pCell
+ ** is inserted into it. Resolve ties by choosing the entry with
+ ** the smallest area.
+ */
+ for(iCell=0; iCell<nCell; iCell++){
+ float growth;
+ float area;
+ float overlap = 0.0;
+ nodeGetCell(pRtree, pNode, iCell, &cell);
+ growth = cellGrowth(pRtree, &cell, pCell);
+ area = cellArea(pRtree, &cell);
+#if VARIANT_RSTARTREE_CHOOSESUBTREE
+ if( ii==(pRtree->iDepth-1) ){
+ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell);
+ }
+#endif
+ if( (iCell==0)
+ || (overlap<fMinOverlap)
+ || (overlap==fMinOverlap && growth<fMinGrowth)
+ || (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea)
+ ){
+ fMinOverlap = overlap;
+ fMinGrowth = growth;
+ fMinArea = area;
+ iBest = cell.iRowid;
+ }
+ }
+
+ sqlite3_free(aCell);
+ rc = nodeAcquire(pRtree, iBest, pNode, &pChild);
+ nodeRelease(pRtree, pNode);
+ pNode = pChild;
+ }
+
+ *ppLeaf = pNode;
+ return rc;
+}
+
+/*
+** A cell with the same content as pCell has just been inserted into
+** the node pNode. This function updates the bounding box cells in
+** all ancestor elements.
+*/
+static void AdjustTree(
+ Rtree *pRtree, /* Rtree table */
+ RtreeNode *pNode, /* Adjust ancestry of this node. */
+ RtreeCell *pCell /* This cell was just inserted */
+){
+ RtreeNode *p = pNode;
+ while( p->pParent ){
+ RtreeCell cell;
+ RtreeNode *pParent = p->pParent;
+ int iCell = nodeParentIndex(pRtree, p);
+
+ nodeGetCell(pRtree, pParent, iCell, &cell);
+ if( cellGrowth(pRtree, &cell, pCell)>0.0 ){
+ cellUnion(pRtree, &cell, pCell);
+ nodeOverwriteCell(pRtree, pParent, &cell, iCell);
+ }
+
+ p = pParent;
+ }
+}
+
+/*
+** Write mapping (iRowid->iNode) to the <rtree>_rowid table.
+*/
+static int rowidWrite(Rtree *pRtree, sqlite3_int64 iRowid, sqlite3_int64 iNode){
+ sqlite3_bind_int64(pRtree->pWriteRowid, 1, iRowid);
+ sqlite3_bind_int64(pRtree->pWriteRowid, 2, iNode);
+ sqlite3_step(pRtree->pWriteRowid);
+ return sqlite3_reset(pRtree->pWriteRowid);
+}
+
+/*
+** Write mapping (iNode->iPar) to the <rtree>_parent table.
+*/
+static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
+ sqlite3_bind_int64(pRtree->pWriteParent, 1, iNode);
+ sqlite3_bind_int64(pRtree->pWriteParent, 2, iPar);
+ sqlite3_step(pRtree->pWriteParent);
+ return sqlite3_reset(pRtree->pWriteParent);
+}
+
+static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int);
+
+#if VARIANT_GUTTMAN_LINEAR_SPLIT
+/*
+** Implementation of the linear variant of the PickNext() function from
+** Guttman[84].
+*/
+static RtreeCell *LinearPickNext(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ RtreeCell *pLeftBox,
+ RtreeCell *pRightBox,
+ int *aiUsed
+){
+ int ii;
+ for(ii=0; aiUsed[ii]; ii++);
+ aiUsed[ii] = 1;
+ return &aCell[ii];
+}
+
+/*
+** Implementation of the linear variant of the PickSeeds() function from
+** Guttman[84].
+*/
+static void LinearPickSeeds(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ int *piLeftSeed,
+ int *piRightSeed
+){
+ int i;
+ int iLeftSeed = 0;
+ int iRightSeed = 1;
+ float maxNormalInnerWidth = 0.0;
+
+ /* Pick two "seed" cells from the array of cells. The algorithm used
+ ** here is the LinearPickSeeds algorithm from Gutman[1984]. The
+ ** indices of the two seed cells in the array are stored in local
+ ** variables iLeftSeek and iRightSeed.
+ */
+ for(i=0; i<pRtree->nDim; i++){
+ float x1 = aCell[0].aCoord[i*2];
+ float x2 = aCell[0].aCoord[i*2+1];
+ float x3 = x1;
+ float x4 = x2;
+ int jj;
+
+ int iCellLeft = 0;
+ int iCellRight = 0;
+
+ for(jj=1; jj<nCell; jj++){
+ float left = aCell[jj].aCoord[i*2];
+ float right = aCell[jj].aCoord[i*2+1];
+
+ if( left<x1 ) x1 = left;
+ if( right>x4 ) x4 = right;
+ if( left>x3 ){
+ x3 = left;
+ iCellRight = jj;
+ }
+ if( right<x2 ){
+ x2 = right;
+ iCellLeft = jj;
+ }
+ }
+
+ if( x4!=x1 ){
+ float normalwidth = (x3 - x2) / (x4 - x1);
+ if( normalwidth>maxNormalInnerWidth ){
+ iLeftSeed = iCellLeft;
+ iRightSeed = iCellRight;
+ }
+ }
+ }
+
+ *piLeftSeed = iLeftSeed;
+ *piRightSeed = iRightSeed;
+}
+#endif /* VARIANT_GUTTMAN_LINEAR_SPLIT */
+
+#if VARIANT_GUTTMAN_QUADRATIC_SPLIT
+/*
+** Implementation of the quadratic variant of the PickNext() function from
+** Guttman[84].
+*/
+static RtreeCell *QuadraticPickNext(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ RtreeCell *pLeftBox,
+ RtreeCell *pRightBox,
+ int *aiUsed
+){
+ #define FABS(a) ((a)<0.0?-1.0*(a):(a))
+
+ int iSelect = -1;
+ float fDiff;
+ int ii;
+ for(ii=0; ii<nCell; ii++){
+ if( aiUsed[ii]==0 ){
+ float left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
+ float right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
+ float diff = FABS(right-left);
+ if( iSelect<0 || diff>fDiff ){
+ fDiff = diff;
+ iSelect = ii;
+ }
+ }
+ }
+ aiUsed[iSelect] = 1;
+ return &aCell[iSelect];
+}
+
+/*
+** Implementation of the quadratic variant of the PickSeeds() function from
+** Guttman[84].
+*/
+static void QuadraticPickSeeds(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ int *piLeftSeed,
+ int *piRightSeed
+){
+ int ii;
+ int jj;
+
+ int iLeftSeed = 0;
+ int iRightSeed = 1;
+ float fWaste = 0.0;
+
+ for(ii=0; ii<nCell; ii++){
+ for(jj=ii+1; jj<nCell; jj++){
+ float right = cellArea(pRtree, &aCell[jj]);
+ float growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
+ float waste = growth - right;
+
+ if( waste>fWaste ){
+ iLeftSeed = ii;
+ iRightSeed = jj;
+ fWaste = waste;
+ }
+ }
+ }
+
+ *piLeftSeed = iLeftSeed;
+ *piRightSeed = iRightSeed;
+}
+#endif /* VARIANT_GUTTMAN_QUADRATIC_SPLIT */
+
+/*
+** Arguments aIdx, aDistance and aSpare all point to arrays of size
+** nIdx. The aIdx array contains the set of integers from 0 to
+** (nIdx-1) in no particular order. This function sorts the values
+** in aIdx according to the indexed values in aDistance. For
+** example, assuming the inputs:
+**
+** aIdx = { 0, 1, 2, 3 }
+** aDistance = { 5.0, 2.0, 7.0, 6.0 }
+**
+** this function sets the aIdx array to contain:
+**
+** aIdx = { 0, 1, 2, 3 }
+**
+** The aSpare array is used as temporary working space by the
+** sorting algorithm.
+*/
+static void SortByDistance(
+ int *aIdx,
+ int nIdx,
+ float *aDistance,
+ int *aSpare
+){
+ if( nIdx>1 ){
+ int iLeft = 0;
+ int iRight = 0;
+
+ int nLeft = nIdx/2;
+ int nRight = nIdx-nLeft;
+ int *aLeft = aIdx;
+ int *aRight = &aIdx[nLeft];
+
+ SortByDistance(aLeft, nLeft, aDistance, aSpare);
+ SortByDistance(aRight, nRight, aDistance, aSpare);
+
+ memcpy(aSpare, aLeft, sizeof(int)*nLeft);
+ aLeft = aSpare;
+
+ while( iLeft<nLeft || iRight<nRight ){
+ if( iLeft==nLeft ){
+ aIdx[iLeft+iRight] = aRight[iRight];
+ iRight++;
+ }else if( iRight==nRight ){
+ aIdx[iLeft+iRight] = aLeft[iLeft];
+ iLeft++;
+ }else{
+ float fLeft = aDistance[aLeft[iLeft]];
+ float fRight = aDistance[aRight[iRight]];
+ if( fLeft<fRight ){
+ aIdx[iLeft+iRight] = aLeft[iLeft];
+ iLeft++;
+ }else{
+ aIdx[iLeft+iRight] = aRight[iRight];
+ iRight++;
+ }
+ }
+ }
+
+#if 0
+ /* Check that the sort worked */
+ {
+ int jj;
+ for(jj=1; jj<nIdx; jj++){
+ float left = aDistance[aIdx[jj-1]];
+ float right = aDistance[aIdx[jj]];
+ assert( left<=right );
+ }
+ }
+#endif
+ }
+}
+
+/*
+** Arguments aIdx, aCell and aSpare all point to arrays of size
+** nIdx. The aIdx array contains the set of integers from 0 to
+** (nIdx-1) in no particular order. This function sorts the values
+** in aIdx according to dimension iDim of the cells in aCell. The
+** minimum value of dimension iDim is considered first, the
+** maximum used to break ties.
+**
+** The aSpare array is used as temporary working space by the
+** sorting algorithm.
+*/
+static void SortByDimension(
+ Rtree *pRtree,
+ int *aIdx,
+ int nIdx,
+ int iDim,
+ RtreeCell *aCell,
+ int *aSpare
+){
+ if( nIdx>1 ){
+
+ int iLeft = 0;
+ int iRight = 0;
+
+ int nLeft = nIdx/2;
+ int nRight = nIdx-nLeft;
+ int *aLeft = aIdx;
+ int *aRight = &aIdx[nLeft];
+
+ SortByDimension(pRtree, aLeft, nLeft, iDim, aCell, aSpare);
+ SortByDimension(pRtree, aRight, nRight, iDim, aCell, aSpare);
+
+ memcpy(aSpare, aLeft, sizeof(int)*nLeft);
+ aLeft = aSpare;
+ while( iLeft<nLeft || iRight<nRight ){
+ double xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
+ double xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
+ double xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
+ double xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
+ if( (iLeft!=nLeft) && ((iRight==nRight)
+ || (xleft1<xright1)
+ || (xleft1==xright1 && xleft2<xright2)
+ )){
+ aIdx[iLeft+iRight] = aLeft[iLeft];
+ iLeft++;
+ }else{
+ aIdx[iLeft+iRight] = aRight[iRight];
+ iRight++;
+ }
+ }
+
+#if 0
+ /* Check that the sort worked */
+ {
+ int jj;
+ for(jj=1; jj<nIdx; jj++){
+ float xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
+ float xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
+ float xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
+ float xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
+ assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) );
+ }
+ }
+#endif
+ }
+}
+
+#if VARIANT_RSTARTREE_SPLIT
+/*
+** Implementation of the R*-tree variant of SplitNode from Beckman[1990].
+*/
+static int splitNodeStartree(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ RtreeNode *pLeft,
+ RtreeNode *pRight,
+ RtreeCell *pBboxLeft,
+ RtreeCell *pBboxRight
+){
+ int **aaSorted;
+ int *aSpare;
+ int ii;
+
+ int iBestDim;
+ int iBestSplit;
+ float fBestMargin;
+
+ int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
+
+ aaSorted = (int **)sqlite3_malloc(nByte);
+ if( !aaSorted ){
+ return SQLITE_NOMEM;
+ }
+
+ aSpare = &((int *)&aaSorted[pRtree->nDim])[pRtree->nDim*nCell];
+ memset(aaSorted, 0, nByte);
+ for(ii=0; ii<pRtree->nDim; ii++){
+ int jj;
+ aaSorted[ii] = &((int *)&aaSorted[pRtree->nDim])[ii*nCell];
+ for(jj=0; jj<nCell; jj++){
+ aaSorted[ii][jj] = jj;
+ }
+ SortByDimension(pRtree, aaSorted[ii], nCell, ii, aCell, aSpare);
+ }
+
+ for(ii=0; ii<pRtree->nDim; ii++){
+ float margin = 0.0;
+ float fBestOverlap;
+ float fBestArea;
+ int iBestLeft;
+ int nLeft;
+
+ for(
+ nLeft=RTREE_MINCELLS(pRtree);
+ nLeft<=(nCell-RTREE_MINCELLS(pRtree));
+ nLeft++
+ ){
+ RtreeCell left;
+ RtreeCell right;
+ int kk;
+ float overlap;
+ float area;
+
+ memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell));
+ memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell));
+ for(kk=1; kk<(nCell-1); kk++){
+ if( kk<nLeft ){
+ cellUnion(pRtree, &left, &aCell[aaSorted[ii][kk]]);
+ }else{
+ cellUnion(pRtree, &right, &aCell[aaSorted[ii][kk]]);
+ }
+ }
+ margin += cellMargin(pRtree, &left);
+ margin += cellMargin(pRtree, &right);
+ overlap = cellOverlap(pRtree, &left, &right, 1, -1);
+ area = cellArea(pRtree, &left) + cellArea(pRtree, &right);
+ if( (nLeft==RTREE_MINCELLS(pRtree))
+ || (overlap<fBestOverlap)
+ || (overlap==fBestOverlap && area<fBestArea)
+ ){
+ iBestLeft = nLeft;
+ fBestOverlap = overlap;
+ fBestArea = area;
+ }
+ }
+
+ if( ii==0 || margin<fBestMargin ){
+ iBestDim = ii;
+ fBestMargin = margin;
+ iBestSplit = iBestLeft;
+ }
+ }
+
+ memcpy(pBboxLeft, &aCell[aaSorted[iBestDim][0]], sizeof(RtreeCell));
+ memcpy(pBboxRight, &aCell[aaSorted[iBestDim][iBestSplit]], sizeof(RtreeCell));
+ for(ii=0; ii<nCell; ii++){
+ RtreeNode *pTarget = (ii<iBestSplit)?pLeft:pRight;
+ RtreeCell *pBbox = (ii<iBestSplit)?pBboxLeft:pBboxRight;
+ RtreeCell *pCell = &aCell[aaSorted[iBestDim][ii]];
+ nodeInsertCell(pRtree, pTarget, pCell);
+ cellUnion(pRtree, pBbox, pCell);
+ }
+
+ sqlite3_free(aaSorted);
+ return SQLITE_OK;
+}
+#endif
+
+#if VARIANT_GUTTMAN_SPLIT
+/*
+** Implementation of the regular R-tree SplitNode from Guttman[1984].
+*/
+static int splitNodeGuttman(
+ Rtree *pRtree,
+ RtreeCell *aCell,
+ int nCell,
+ RtreeNode *pLeft,
+ RtreeNode *pRight,
+ RtreeCell *pBboxLeft,
+ RtreeCell *pBboxRight
+){
+ int iLeftSeed = 0;
+ int iRightSeed = 1;
+ int *aiUsed;
+ int i;
+
+ aiUsed = sqlite3_malloc(sizeof(int)*nCell);
+ memset(aiUsed, 0, sizeof(int)*nCell);
+
+ PickSeeds(pRtree, aCell, nCell, &iLeftSeed, &iRightSeed);
+
+ memcpy(pBboxLeft, &aCell[iLeftSeed], sizeof(RtreeCell));
+ memcpy(pBboxRight, &aCell[iRightSeed], sizeof(RtreeCell));
+ nodeInsertCell(pRtree, pLeft, &aCell[iLeftSeed]);
+ nodeInsertCell(pRtree, pRight, &aCell[iRightSeed]);
+ aiUsed[iLeftSeed] = 1;
+ aiUsed[iRightSeed] = 1;
+
+ for(i=nCell-2; i>0; i--){
+ RtreeCell *pNext;
+ pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed);
+ float diff =
+ cellGrowth(pRtree, pBboxLeft, pNext) -
+ cellGrowth(pRtree, pBboxRight, pNext)
+ ;
+ if( (RTREE_MINCELLS(pRtree)-NCELL(pRight)==i)
+ || (diff>0.0 && (RTREE_MINCELLS(pRtree)-NCELL(pLeft)!=i))
+ ){
+ nodeInsertCell(pRtree, pRight, pNext);
+ cellUnion(pRtree, pBboxRight, pNext);
+ }else{
+ nodeInsertCell(pRtree, pLeft, pNext);
+ cellUnion(pRtree, pBboxLeft, pNext);
+ }
+ }
+
+ sqlite3_free(aiUsed);
+ return SQLITE_OK;
+}
+#endif
+
+static int updateMapping(
+ Rtree *pRtree,
+ i64 iRowid,
+ RtreeNode *pNode,
+ int iHeight
+){
+ int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64);
+ xSetMapping = ((iHeight==0)?rowidWrite:parentWrite);
+ if( iHeight>0 ){
+ RtreeNode *pChild = nodeHashLookup(pRtree, iRowid);
+ if( pChild ){
+ nodeRelease(pRtree, pChild->pParent);
+ nodeReference(pNode);
+ pChild->pParent = pNode;
+ }
+ }
+ return xSetMapping(pRtree, iRowid, pNode->iNode);
+}
+
+static int SplitNode(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ RtreeCell *pCell,
+ int iHeight
+){
+ int i;
+ int newCellIsRight = 0;
+
+ int rc = SQLITE_OK;
+ int nCell = NCELL(pNode);
+ RtreeCell *aCell;
+ int *aiUsed;
+
+ RtreeNode *pLeft = 0;
+ RtreeNode *pRight = 0;
+
+ RtreeCell leftbbox;
+ RtreeCell rightbbox;
+
+ /* Allocate an array and populate it with a copy of pCell and
+ ** all cells from node pLeft. Then zero the original node.
+ */
+ aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1));
+ if( !aCell ){
+ rc = SQLITE_NOMEM;
+ goto splitnode_out;
+ }
+ aiUsed = (int *)&aCell[nCell+1];
+ memset(aiUsed, 0, sizeof(int)*(nCell+1));
+ for(i=0; i<nCell; i++){
+ nodeGetCell(pRtree, pNode, i, &aCell[i]);
+ }
+ nodeZero(pRtree, pNode);
+ memcpy(&aCell[nCell], pCell, sizeof(RtreeCell));
+ nCell++;
+
+ if( pNode->iNode==1 ){
+ pRight = nodeNew(pRtree, pNode, 1);
+ pLeft = nodeNew(pRtree, pNode, 1);
+ pRtree->iDepth++;
+ pNode->isDirty = 1;
+ writeInt16(pNode->zData, pRtree->iDepth);
+ }else{
+ pLeft = pNode;
+ pRight = nodeNew(pRtree, pLeft->pParent, 1);
+ nodeReference(pLeft);
+ }
+
+ if( !pLeft || !pRight ){
+ rc = SQLITE_NOMEM;
+ goto splitnode_out;
+ }
+
+ memset(pLeft->zData, 0, pRtree->iNodeSize);
+ memset(pRight->zData, 0, pRtree->iNodeSize);
+
+ rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox);
+ if( rc!=SQLITE_OK ){
+ goto splitnode_out;
+ }
+
+ /* Ensure both child nodes have node numbers assigned to them. */
+ if( (0==pRight->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pRight)))
+ || (0==pLeft->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pLeft)))
+ ){
+ goto splitnode_out;
+ }
+
+ rightbbox.iRowid = pRight->iNode;
+ leftbbox.iRowid = pLeft->iNode;
+
+ if( pNode->iNode==1 ){
+ rc = rtreeInsertCell(pRtree, pLeft->pParent, &leftbbox, iHeight+1);
+ if( rc!=SQLITE_OK ){
+ goto splitnode_out;
+ }
+ }else{
+ RtreeNode *pParent = pLeft->pParent;
+ int iCell = nodeParentIndex(pRtree, pLeft);
+ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell);
+ AdjustTree(pRtree, pParent, &leftbbox);
+ }
+ if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){
+ goto splitnode_out;
+ }
+
+ for(i=0; i<NCELL(pRight); i++){
+ i64 iRowid = nodeGetRowid(pRtree, pRight, i);
+ rc = updateMapping(pRtree, iRowid, pRight, iHeight);
+ if( iRowid==pCell->iRowid ){
+ newCellIsRight = 1;
+ }
+ if( rc!=SQLITE_OK ){
+ goto splitnode_out;
+ }
+ }
+ if( pNode->iNode==1 ){
+ for(i=0; i<NCELL(pLeft); i++){
+ i64 iRowid = nodeGetRowid(pRtree, pLeft, i);
+ rc = updateMapping(pRtree, iRowid, pLeft, iHeight);
+ if( rc!=SQLITE_OK ){
+ goto splitnode_out;
+ }
+ }
+ }else if( newCellIsRight==0 ){
+ rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = nodeRelease(pRtree, pRight);
+ pRight = 0;
+ }
+ if( rc==SQLITE_OK ){
+ rc = nodeRelease(pRtree, pLeft);
+ pLeft = 0;
+ }
+
+splitnode_out:
+ nodeRelease(pRtree, pRight);
+ nodeRelease(pRtree, pLeft);
+ sqlite3_free(aCell);
+ return rc;
+}
+
+static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){
+ int rc = SQLITE_OK;
+ if( pLeaf->iNode!=1 && pLeaf->pParent==0 ){
+ sqlite3_bind_int64(pRtree->pReadParent, 1, pLeaf->iNode);
+ if( sqlite3_step(pRtree->pReadParent)==SQLITE_ROW ){
+ i64 iNode = sqlite3_column_int64(pRtree->pReadParent, 0);
+ rc = nodeAcquire(pRtree, iNode, 0, &pLeaf->pParent);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ sqlite3_reset(pRtree->pReadParent);
+ if( rc==SQLITE_OK ){
+ rc = fixLeafParent(pRtree, pLeaf->pParent);
+ }
+ }
+ return rc;
+}
+
+static int deleteCell(Rtree *, RtreeNode *, int, int);
+
+static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
+ int rc;
+ RtreeNode *pParent;
+ int iCell;
+
+ assert( pNode->nRef==1 );
+
+ /* Remove the entry in the parent cell. */
+ iCell = nodeParentIndex(pRtree, pNode);
+ pParent = pNode->pParent;
+ pNode->pParent = 0;
+ if( SQLITE_OK!=(rc = deleteCell(pRtree, pParent, iCell, iHeight+1))
+ || SQLITE_OK!=(rc = nodeRelease(pRtree, pParent))
+ ){
+ return rc;
+ }
+
+ /* Remove the xxx_node entry. */
+ sqlite3_bind_int64(pRtree->pDeleteNode, 1, pNode->iNode);
+ sqlite3_step(pRtree->pDeleteNode);
+ if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteNode)) ){
+ return rc;
+ }
+
+ /* Remove the xxx_parent entry. */
+ sqlite3_bind_int64(pRtree->pDeleteParent, 1, pNode->iNode);
+ sqlite3_step(pRtree->pDeleteParent);
+ if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteParent)) ){
+ return rc;
+ }
+
+ /* Remove the node from the in-memory hash table and link it into
+ ** the Rtree.pDeleted list. Its contents will be re-inserted later on.
+ */
+ nodeHashDelete(pRtree, pNode);
+ pNode->iNode = iHeight;
+ pNode->pNext = pRtree->pDeleted;
+ pNode->nRef++;
+ pRtree->pDeleted = pNode;
+
+ return SQLITE_OK;
+}
+
+static void fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){
+ RtreeNode *pParent = pNode->pParent;
+ if( pParent ){
+ int ii;
+ int nCell = NCELL(pNode);
+ RtreeCell box; /* Bounding box for pNode */
+ nodeGetCell(pRtree, pNode, 0, &box);
+ for(ii=1; ii<nCell; ii++){
+ RtreeCell cell;
+ nodeGetCell(pRtree, pNode, ii, &cell);
+ cellUnion(pRtree, &box, &cell);
+ }
+ box.iRowid = pNode->iNode;
+ ii = nodeParentIndex(pRtree, pNode);
+ nodeOverwriteCell(pRtree, pParent, &box, ii);
+ fixBoundingBox(pRtree, pParent);
+ }
+}
+
+/*
+** Delete the cell at index iCell of node pNode. After removing the
+** cell, adjust the r-tree data structure if required.
+*/
+static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){
+ int rc;
+
+ if( SQLITE_OK!=(rc = fixLeafParent(pRtree, pNode)) ){
+ return rc;
+ }
+
+ /* Remove the cell from the node. This call just moves bytes around
+ ** the in-memory node image, so it cannot fail.
+ */
+ nodeDeleteCell(pRtree, pNode, iCell);
+
+ /* If the node is not the tree root and now has less than the minimum
+ ** number of cells, remove it from the tree. Otherwise, update the
+ ** cell in the parent node so that it tightly contains the updated
+ ** node.
+ */
+ if( pNode->iNode!=1 ){
+ RtreeNode *pParent = pNode->pParent;
+ if( (pParent->iNode!=1 || NCELL(pParent)!=1)
+ && (NCELL(pNode)<RTREE_MINCELLS(pRtree))
+ ){
+ rc = removeNode(pRtree, pNode, iHeight);
+ }else{
+ fixBoundingBox(pRtree, pNode);
+ }
+ }
+
+ return rc;
+}
+
+static int Reinsert(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ RtreeCell *pCell,
+ int iHeight
+){
+ int *aOrder;
+ int *aSpare;
+ RtreeCell *aCell;
+ float *aDistance;
+ int nCell;
+ float aCenterCoord[RTREE_MAX_DIMENSIONS];
+ int iDim;
+ int ii;
+ int rc = SQLITE_OK;
+
+ memset(aCenterCoord, 0, sizeof(float)*RTREE_MAX_DIMENSIONS);
+
+ nCell = NCELL(pNode)+1;
+
+ /* Allocate the buffers used by this operation. The allocation is
+ ** relinquished before this function returns.
+ */
+ aCell = (RtreeCell *)sqlite3_malloc(nCell * (
+ sizeof(RtreeCell) + /* aCell array */
+ sizeof(int) + /* aOrder array */
+ sizeof(int) + /* aSpare array */
+ sizeof(float) /* aDistance array */
+ ));
+ if( !aCell ){
+ return SQLITE_NOMEM;
+ }
+ aOrder = (int *)&aCell[nCell];
+ aSpare = (int *)&aOrder[nCell];
+ aDistance = (float *)&aSpare[nCell];
+
+ for(ii=0; ii<nCell; ii++){
+ if( ii==(nCell-1) ){
+ memcpy(&aCell[ii], pCell, sizeof(RtreeCell));
+ }else{
+ nodeGetCell(pRtree, pNode, ii, &aCell[ii]);
+ }
+ aOrder[ii] = ii;
+ for(iDim=0; iDim<pRtree->nDim; iDim++){
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
+ }
+ }
+ for(iDim=0; iDim<pRtree->nDim; iDim++){
+ aCenterCoord[iDim] = aCenterCoord[iDim]/((float)nCell*2.0);
+ }
+
+ for(ii=0; ii<nCell; ii++){
+ aDistance[ii] = 0.0;
+ for(iDim=0; iDim<pRtree->nDim; iDim++){
+ float coord = DCOORD(aCell[ii].aCoord[iDim*2+1]) -
+ DCOORD(aCell[ii].aCoord[iDim*2]);
+ aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
+ }
+ }
+
+ SortByDistance(aOrder, nCell, aDistance, aSpare);
+ nodeZero(pRtree, pNode);
+
+ for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){
+ RtreeCell *p = &aCell[aOrder[ii]];
+ nodeInsertCell(pRtree, pNode, p);
+ if( p->iRowid==pCell->iRowid ){
+ if( iHeight==0 ){
+ rc = rowidWrite(pRtree, p->iRowid, pNode->iNode);
+ }else{
+ rc = parentWrite(pRtree, p->iRowid, pNode->iNode);
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ fixBoundingBox(pRtree, pNode);
+ }
+ for(; rc==SQLITE_OK && ii<nCell; ii++){
+ /* Find a node to store this cell in. pNode->iNode currently contains
+ ** the height of the sub-tree headed by the cell.
+ */
+ RtreeNode *pInsert;
+ RtreeCell *p = &aCell[aOrder[ii]];
+ rc = ChooseLeaf(pRtree, p, iHeight, &pInsert);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ rc = rtreeInsertCell(pRtree, pInsert, p, iHeight);
+ rc2 = nodeRelease(pRtree, pInsert);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ }
+
+ sqlite3_free(aCell);
+ return rc;
+}
+
+/*
+** Insert cell pCell into node pNode. Node pNode is the head of a
+** subtree iHeight high (leaf nodes have iHeight==0).
+*/
+static int rtreeInsertCell(
+ Rtree *pRtree,
+ RtreeNode *pNode,
+ RtreeCell *pCell,
+ int iHeight
+){
+ int rc = SQLITE_OK;
+ if( iHeight>0 ){
+ RtreeNode *pChild = nodeHashLookup(pRtree, pCell->iRowid);
+ if( pChild ){
+ nodeRelease(pRtree, pChild->pParent);
+ nodeReference(pNode);
+ pChild->pParent = pNode;
+ }
+ }
+ if( nodeInsertCell(pRtree, pNode, pCell) ){
+#if VARIANT_RSTARTREE_REINSERT
+ if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){
+ rc = SplitNode(pRtree, pNode, pCell, iHeight);
+ }else{
+ pRtree->iReinsertHeight = iHeight;
+ rc = Reinsert(pRtree, pNode, pCell, iHeight);
+ }
+#else
+ rc = SplitNode(pRtree, pNode, pCell, iHeight);
+#endif
+ }else{
+ AdjustTree(pRtree, pNode, pCell);
+ if( iHeight==0 ){
+ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
+ }else{
+ rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode);
+ }
+ }
+ return rc;
+}
+
+static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){
+ int ii;
+ int rc = SQLITE_OK;
+ int nCell = NCELL(pNode);
+
+ for(ii=0; rc==SQLITE_OK && ii<nCell; ii++){
+ RtreeNode *pInsert;
+ RtreeCell cell;
+ nodeGetCell(pRtree, pNode, ii, &cell);
+
+ /* Find a node to store this cell in. pNode->iNode currently contains
+ ** the height of the sub-tree headed by the cell.
+ */
+ rc = ChooseLeaf(pRtree, &cell, pNode->iNode, &pInsert);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ rc = rtreeInsertCell(pRtree, pInsert, &cell, pNode->iNode);
+ rc2 = nodeRelease(pRtree, pInsert);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Select a currently unused rowid for a new r-tree record.
+*/
+static int newRowid(Rtree *pRtree, i64 *piRowid){
+ int rc;
+ sqlite3_bind_null(pRtree->pWriteRowid, 1);
+ sqlite3_bind_null(pRtree->pWriteRowid, 2);
+ sqlite3_step(pRtree->pWriteRowid);
+ rc = sqlite3_reset(pRtree->pWriteRowid);
+ *piRowid = sqlite3_last_insert_rowid(pRtree->db);
+ return rc;
+}
+
+#ifndef NDEBUG
+static int hashIsEmpty(Rtree *pRtree){
+ int ii;
+ for(ii=0; ii<HASHSIZE; ii++){
+ assert( !pRtree->aHash[ii] );
+ }
+ return 1;
+}
+#endif
+
+/*
+** The xUpdate method for rtree module virtual tables.
+*/
+int rtreeUpdate(
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **azData,
+ sqlite_int64 *pRowid
+){
+ Rtree *pRtree = (Rtree *)pVtab;
+ int rc = SQLITE_OK;
+
+ rtreeReference(pRtree);
+
+ assert(nData>=1);
+ assert(hashIsEmpty(pRtree));
+
+ /* If azData[0] is not an SQL NULL value, it is the rowid of a
+ ** record to delete from the r-tree table. The following block does
+ ** just that.
+ */
+ if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
+ i64 iDelete; /* The rowid to delete */
+ RtreeNode *pLeaf; /* Leaf node containing record iDelete */
+ int iCell; /* Index of iDelete cell in pLeaf */
+ RtreeNode *pRoot;
+
+ /* Obtain a reference to the root node to initialise Rtree.iDepth */
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+
+ /* Obtain a reference to the leaf node that contains the entry
+ ** about to be deleted.
+ */
+ if( rc==SQLITE_OK ){
+ iDelete = sqlite3_value_int64(azData[0]);
+ rc = findLeafNode(pRtree, iDelete, &pLeaf);
+ }
+
+ /* Delete the cell in question from the leaf node. */
+ if( rc==SQLITE_OK ){
+ int rc2;
+ iCell = nodeRowidIndex(pRtree, pLeaf, iDelete);
+ rc = deleteCell(pRtree, pLeaf, iCell, 0);
+ rc2 = nodeRelease(pRtree, pLeaf);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+
+ /* Delete the corresponding entry in the <rtree>_rowid table. */
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
+ sqlite3_step(pRtree->pDeleteRowid);
+ rc = sqlite3_reset(pRtree->pDeleteRowid);
+ }
+
+ /* Check if the root node now has exactly one child. If so, remove
+ ** it, schedule the contents of the child for reinsertion and
+ ** reduce the tree height by one.
+ **
+ ** This is equivalent to copying the contents of the child into
+ ** the root node (the operation that Gutman's paper says to perform
+ ** in this scenario).
+ */
+ if( rc==SQLITE_OK && pRtree->iDepth>0 ){
+ if( rc==SQLITE_OK && NCELL(pRoot)==1 ){
+ RtreeNode *pChild;
+ i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
+ rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
+ if( rc==SQLITE_OK ){
+ rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
+ }
+ if( rc==SQLITE_OK ){
+ pRtree->iDepth--;
+ writeInt16(pRoot->zData, pRtree->iDepth);
+ pRoot->isDirty = 1;
+ }
+ }
+ }
+
+ /* Re-insert the contents of any underfull nodes removed from the tree. */
+ for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
+ if( rc==SQLITE_OK ){
+ rc = reinsertNodeContent(pRtree, pLeaf);
+ }
+ pRtree->pDeleted = pLeaf->pNext;
+ sqlite3_free(pLeaf);
+ }
+
+ /* Release the reference to the root node. */
+ if( rc==SQLITE_OK ){
+ rc = nodeRelease(pRtree, pRoot);
+ }else{
+ nodeRelease(pRtree, pRoot);
+ }
+ }
+
+ /* If the azData[] array contains more than one element, elements
+ ** (azData[2]..azData[argc-1]) contain a new record to insert into
+ ** the r-tree structure.
+ */
+ if( rc==SQLITE_OK && nData>1 ){
+ /* Insert a new record into the r-tree */
+ RtreeCell cell;
+ int ii;
+ RtreeNode *pLeaf;
+
+ /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
+ assert( nData==(pRtree->nDim*2 + 3) );
+ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ cell.aCoord[ii].f = (float)sqlite3_value_double(azData[ii+3]);
+ cell.aCoord[ii+1].f = (float)sqlite3_value_double(azData[ii+4]);
+ if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
+ }
+ }else{
+ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
+ cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
+ if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
+ }
+ }
+
+ /* Figure out the rowid of the new row. */
+ if( sqlite3_value_type(azData[2])==SQLITE_NULL ){
+ rc = newRowid(pRtree, &cell.iRowid);
+ }else{
+ cell.iRowid = sqlite3_value_int64(azData[2]);
+ sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
+ if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){
+ sqlite3_reset(pRtree->pReadRowid);
+ rc = SQLITE_CONSTRAINT;
+ goto constraint;
+ }
+ rc = sqlite3_reset(pRtree->pReadRowid);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf);
+ }
+ if( rc==SQLITE_OK ){
+ int rc2;
+ pRtree->iReinsertHeight = -1;
+ rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
+ rc2 = nodeRelease(pRtree, pLeaf);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ }
+
+constraint:
+ rtreeRelease(pRtree);
+ return rc;
+}
+
+/*
+** The xRename method for rtree module virtual tables.
+*/
+static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
+ Rtree *pRtree = (Rtree *)pVtab;
+ int rc = SQLITE_NOMEM;
+ char *zSql = sqlite3_mprintf(
+ "ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";"
+ "ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";"
+ "ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";"
+ , pRtree->zDb, pRtree->zName, zNewName
+ , pRtree->zDb, pRtree->zName, zNewName
+ , pRtree->zDb, pRtree->zName, zNewName
+ );
+ if( zSql ){
+ rc = sqlite3_exec(pRtree->db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+ return rc;
+}
+
+static sqlite3_module rtreeModule = {
+ 0, /* iVersion */
+ rtreeCreate, /* xCreate - create a table */
+ rtreeConnect, /* xConnect - connect to an existing table */
+ rtreeBestIndex, /* xBestIndex - Determine search strategy */
+ rtreeDisconnect, /* xDisconnect - Disconnect from a table */
+ rtreeDestroy, /* xDestroy - Drop a table */
+ rtreeOpen, /* xOpen - open a cursor */
+ rtreeClose, /* xClose - close a cursor */
+ rtreeFilter, /* xFilter - configure scan constraints */
+ rtreeNext, /* xNext - advance a cursor */
+ rtreeEof, /* xEof */
+ rtreeColumn, /* xColumn - read data */
+ rtreeRowid, /* xRowid - read data */
+ rtreeUpdate, /* xUpdate - write data */
+ 0, /* xBegin - begin transaction */
+ 0, /* xSync - sync transaction */
+ 0, /* xCommit - commit transaction */
+ 0, /* xRollback - rollback transaction */
+ 0, /* xFindFunction - function overloading */
+ rtreeRename /* xRename - rename the table */
+};
+
+static int rtreeSqlInit(
+ Rtree *pRtree,
+ sqlite3 *db,
+ const char *zDb,
+ const char *zPrefix,
+ int isCreate
+){
+ int rc = SQLITE_OK;
+
+ #define N_STATEMENT 9
+ static const char *azSql[N_STATEMENT] = {
+ /* Read and write the xxx_node table */
+ "SELECT data FROM '%q'.'%q_node' WHERE nodeno = :1",
+ "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
+ "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
+
+ /* Read and write the xxx_rowid table */
+ "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1",
+ "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)",
+ "DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1",
+
+ /* Read and write the xxx_parent table */
+ "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = :1",
+ "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(:1, :2)",
+ "DELETE FROM '%q'.'%q_parent' WHERE nodeno = :1"
+ };
+ sqlite3_stmt **appStmt[N_STATEMENT];
+ int i;
+
+ pRtree->db = db;
+
+ if( isCreate ){
+ char *zCreate = sqlite3_mprintf(
+"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);"
+"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);"
+"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY, parentnode INTEGER);"
+"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))",
+ zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize
+ );
+ if( !zCreate ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_exec(db, zCreate, 0, 0, 0);
+ sqlite3_free(zCreate);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+
+ appStmt[0] = &pRtree->pReadNode;
+ appStmt[1] = &pRtree->pWriteNode;
+ appStmt[2] = &pRtree->pDeleteNode;
+ appStmt[3] = &pRtree->pReadRowid;
+ appStmt[4] = &pRtree->pWriteRowid;
+ appStmt[5] = &pRtree->pDeleteRowid;
+ appStmt[6] = &pRtree->pReadParent;
+ appStmt[7] = &pRtree->pWriteParent;
+ appStmt[8] = &pRtree->pDeleteParent;
+
+ for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
+ char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
+ if( zSql ){
+ rc = sqlite3_prepare_v2(db, zSql, -1, appStmt[i], 0);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
+/*
+** This routine queries database handle db for the page-size used by
+** database zDb. If successful, the page-size in bytes is written to
+** *piPageSize and SQLITE_OK returned. Otherwise, and an SQLite error
+** code is returned.
+*/
+static int getPageSize(sqlite3 *db, const char *zDb, int *piPageSize){
+ int rc = SQLITE_NOMEM;
+ char *zSql;
+ sqlite3_stmt *pStmt = 0;
+
+ zSql = sqlite3_mprintf("PRAGMA %Q.page_size", zDb);
+ if( !zSql ){
+ return SQLITE_NOMEM;
+ }
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ *piPageSize = sqlite3_column_int(pStmt, 0);
+ }
+ return sqlite3_finalize(pStmt);
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the r-tree virtual table.
+**
+** argv[0] -> module name
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> column names...
+*/
+static int rtreeInit(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to head of rtree list */
+ int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
+ sqlite3_vtab **ppVtab, /* OUT: New virtual table */
+ char **pzErr, /* OUT: Error message, if any */
+ int isCreate, /* True for xCreate, false for xConnect */
+ int eCoordType /* One of the RTREE_COORD_* constants */
+){
+ int rc = SQLITE_OK;
+ int iPageSize = 0;
+ Rtree *pRtree;
+ int nDb; /* Length of string argv[1] */
+ int nName; /* Length of string argv[2] */
+
+ const char *aErrMsg[] = {
+ 0, /* 0 */
+ "Wrong number of columns for an rtree table", /* 1 */
+ "Too few columns for an rtree table", /* 2 */
+ "Too many columns for an rtree table" /* 3 */
+ };
+
+ int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
+ if( aErrMsg[iErr] ){
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
+ return SQLITE_ERROR;
+ }
+
+ rc = getPageSize(db, argv[1], &iPageSize);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ /* Allocate the sqlite3_vtab structure */
+ nDb = strlen(argv[1]);
+ nName = strlen(argv[2]);
+ pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
+ if( !pRtree ){
+ return SQLITE_NOMEM;
+ }
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ pRtree->nBusy = 1;
+ pRtree->base.pModule = &rtreeModule;
+ pRtree->zDb = (char *)&pRtree[1];
+ pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->nDim = (argc-4)/2;
+ pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
+ pRtree->eCoordType = eCoordType;
+ memcpy(pRtree->zDb, argv[1], nDb);
+ memcpy(pRtree->zName, argv[2], nName);
+
+ /* Figure out the node size to use. By default, use 64 bytes less than
+ ** the database page-size. This ensures that each node is stored on
+ ** a single database page.
+ **
+ ** If the databasd page-size is so large that more than RTREE_MAXCELLS
+ ** entries would fit in a single node, use a smaller node-size.
+ */
+ pRtree->iNodeSize = iPageSize-64;
+ if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
+ pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
+ }
+
+ /* Create/Connect to the underlying relational database schema. If
+ ** that is successful, call sqlite3_declare_vtab() to configure
+ ** the r-tree table schema.
+ */
+ if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }else{
+ char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
+ char *zTmp;
+ int ii;
+ for(ii=4; zSql && ii<argc; ii++){
+ zTmp = zSql;
+ zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
+ sqlite3_free(zTmp);
+ }
+ if( zSql ){
+ zTmp = zSql;
+ zSql = sqlite3_mprintf("%s);", zTmp);
+ sqlite3_free(zTmp);
+ }
+ if( !zSql || sqlite3_declare_vtab(db, zSql) ){
+ rc = SQLITE_NOMEM;
+ }
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ *ppVtab = (sqlite3_vtab *)pRtree;
+ }else{
+ rtreeRelease(pRtree);
+ }
+ return rc;
+}
+
+
+/*
+** Implementation of a scalar function that decodes r-tree nodes to
+** human readable strings. This can be used for debugging and analysis.
+**
+** The scalar function takes two arguments, a blob of data containing
+** an r-tree node, and the number of dimensions the r-tree indexes.
+** For a two-dimensional r-tree structure called "rt", to deserialize
+** all nodes, a statement like:
+**
+** SELECT rtreenode(2, data) FROM rt_node;
+**
+** The human readable string takes the form of a Tcl list with one
+** entry for each cell in the r-tree node. Each entry is itself a
+** list, containing the 8-byte rowid/pageno followed by the
+** <num-dimension>*2 coordinates.
+*/
+static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
+ char *zText = 0;
+ RtreeNode node;
+ Rtree tree;
+ int ii;
+
+ memset(&node, 0, sizeof(RtreeNode));
+ memset(&tree, 0, sizeof(Rtree));
+ tree.nDim = sqlite3_value_int(apArg[0]);
+ tree.nBytesPerCell = 8 + 8 * tree.nDim;
+ node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
+
+ for(ii=0; ii<NCELL(&node); ii++){
+ char zCell[512];
+ int nCell = 0;
+ RtreeCell cell;
+ int jj;
+
+ nodeGetCell(&tree, &node, ii, &cell);
+ sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid);
+ nCell = strlen(zCell);
+ for(jj=0; jj<tree.nDim*2; jj++){
+ sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f);
+ nCell = strlen(zCell);
+ }
+
+ if( zText ){
+ char *zTextNew = sqlite3_mprintf("%s {%s}", zText, zCell);
+ sqlite3_free(zText);
+ zText = zTextNew;
+ }else{
+ zText = sqlite3_mprintf("{%s}", zCell);
+ }
+ }
+
+ sqlite3_result_text(ctx, zText, -1, sqlite3_free);
+}
+
+static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
+ if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB
+ || sqlite3_value_bytes(apArg[0])<2
+ ){
+ sqlite3_result_error(ctx, "Invalid argument to rtreedepth()", -1);
+ }else{
+ u8 *zBlob = (u8 *)sqlite3_value_blob(apArg[0]);
+ sqlite3_result_int(ctx, readInt16(zBlob));
+ }
+}
+
+/*
+** Register the r-tree module with database handle db. This creates the
+** virtual table module "rtree" and the debugging/analysis scalar
+** function "rtreenode".
+*/
+int sqlite3RtreeInit(sqlite3 *db){
+ int rc = SQLITE_OK;
+
+ if( rc==SQLITE_OK ){
+ int utf8 = SQLITE_UTF8;
+ rc = sqlite3_create_function(db, "rtreenode", 2, utf8, 0, rtreenode, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ int utf8 = SQLITE_UTF8;
+ rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ void *c = (void *)RTREE_COORD_REAL32;
+ rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
+ }
+ if( rc==SQLITE_OK ){
+ void *c = (void *)RTREE_COORD_INT32;
+ rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
+ }
+
+ return rc;
+}
+
+#if !SQLITE_CORE
+int sqlite3_extension_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi)
+ return sqlite3RtreeInit(db);
+}
+#endif
+
+#endif
diff --git a/third_party/sqlite/ext/rtree/rtree.h b/third_party/sqlite/ext/rtree/rtree.h
new file mode 100755
index 0000000..1fdbccc
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree.h
@@ -0,0 +1,26 @@
+/*
+** 2008 May 26
+**
+** 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 header file is used by programs that want to link against the
+** RTREE library. All it does is declare the sqlite3RtreeInit() interface.
+*/
+#include "sqlite3.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int sqlite3RtreeInit(sqlite3 *db);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
diff --git a/third_party/sqlite/ext/rtree/rtree1.test b/third_party/sqlite/ext/rtree/rtree1.test
new file mode 100755
index 0000000..ee9866f
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree1.test
@@ -0,0 +1,364 @@
+# 2008 Feb 19
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the r-tree extension.
+#
+# $Id: rtree1.test,v 1.5 2008/07/14 15:37:01 danielk1977 Exp $
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname $argv0] .. .. test]
+}
+source [file join [file dirname [info script]] rtree_util.tcl]
+source $testdir/tester.tcl
+
+# Test plan:
+#
+# rtree-1.*: Creating/destroying r-tree tables.
+# rtree-2.*: Test the implicit constraints - unique rowid and
+# (coord[N]<=coord[N+1]) for even values of N. Also
+# automatic assigning of rowid values.
+# rtree-3.*: Linear scans of r-tree data.
+# rtree-4.*: Test INSERT
+# rtree-5.*: Test DELETE
+# rtree-6.*: Test UPDATE
+# rtree-7.*: Test renaming an r-tree table.
+# rtree-8.*: Test constrained scans of r-tree data.
+#
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-1.* test CREATE and DROP table statements.
+#
+
+# Test creating and dropping an rtree table.
+#
+do_test rtree-1.1.1 {
+ execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) }
+} {}
+do_test rtree-1.1.2 {
+ execsql { SELECT name FROM sqlite_master ORDER BY name }
+} {t1 t1_node t1_parent t1_rowid}
+do_test rtree-1.1.3 {
+ execsql {
+ DROP TABLE t1;
+ SELECT name FROM sqlite_master ORDER BY name;
+ }
+} {}
+
+# Test creating and dropping an rtree table with an odd name in
+# an attached database.
+#
+do_test rtree-1.2.1 {
+ file delete -force test2.db
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE VIRTUAL TABLE aux.'a" "b' USING rtree(ii, x1, x2, y1, y2);
+ }
+} {}
+do_test rtree-1.2.2 {
+ execsql { SELECT name FROM sqlite_master ORDER BY name }
+} {}
+do_test rtree-1.2.3 {
+ execsql { SELECT name FROM aux.sqlite_master ORDER BY name }
+} {{a" "b} {a" "b_node} {a" "b_parent} {a" "b_rowid}}
+do_test rtree-1.2.4 {
+ execsql {
+ DROP TABLE aux.'a" "b';
+ SELECT name FROM aux.sqlite_master ORDER BY name;
+ }
+} {}
+
+# Test that the logic for checking the number of columns specified
+# for an rtree table. Acceptable values are odd numbers between 3 and
+# 11, inclusive.
+#
+set cols [list i1 i2 i3 i4 i5 i6 i7 i8 i9 iA iB iC iD iE iF iG iH iI iJ iK]
+for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
+
+ set columns [join [lrange $cols 0 [expr {$nCol-1}]] ,]
+
+ set X {0 {}}
+ if {$nCol%2 == 0} { set X {1 {Wrong number of columns for an rtree table}} }
+ if {$nCol < 3} { set X {1 {Too few columns for an rtree table}} }
+ if {$nCol > 11} { set X {1 {Too many columns for an rtree table}} }
+
+ do_test rtree-1.3.$nCol {
+ catchsql "
+ CREATE VIRTUAL TABLE t1 USING rtree($columns);
+ "
+ } $X
+
+ catchsql { DROP TABLE t1 }
+}
+
+# Test that it is possible to open an existing database that contains
+# r-tree tables.
+#
+do_test rtree-1.4.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
+ INSERT INTO t1 VALUES(1, 5.0, 10.0);
+ INSERT INTO t1 VALUES(2, 15.0, 20.0);
+ }
+} {}
+do_test rtree-1.4.2 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM t1 ORDER BY ii }
+} {1 5.0 10.0 2 15.0 20.0}
+do_test rtree-1.4.3 {
+ execsql { DROP TABLE t1 }
+} {}
+
+# Test that it is possible to create an r-tree table with ridiculous
+# column names.
+#
+do_test rtree-1.5.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
+ INSERT INTO t1 VALUES(1, 2, 3);
+ SELECT "the key", "x dim.", "x2'dim" FROM t1;
+ }
+} {1 2.0 3.0}
+do_test rtree-1.5.1 {
+ execsql { DROP TABLE t1 }
+} {}
+
+# Force the r-tree constructor to fail.
+#
+do_test rtree-1.6.1 {
+ execsql { CREATE TABLE t1_rowid(a); }
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
+ }
+} {1 {table "t1_rowid" already exists}}
+do_test rtree-1.6.1 {
+ execsql { DROP TABLE t1_rowid }
+} {}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-2.*
+#
+do_test rtree-2.1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
+ SELECT * FROM t1;
+ }
+} {}
+
+do_test rtree-2.1.2 {
+ execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
+ execsql { SELECT * FROM t1 }
+} {1 1.0 3.0 2.0 4.0}
+do_test rtree-2.1.3 {
+ execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
+ execsql { SELECT rowid FROM t1 ORDER BY rowid }
+} {1 2}
+do_test rtree-2.1.3 {
+ execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
+ execsql { SELECT ii FROM t1 ORDER BY ii }
+} {1 2 3}
+
+do_test rtree-2.2.1 {
+ catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) }
+} {1 {constraint failed}}
+do_test rtree-2.2.2 {
+ catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) }
+} {1 {constraint failed}}
+do_test rtree-2.2.3 {
+ catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) }
+} {1 {constraint failed}}
+do_test rtree-2.2.4 {
+ execsql { SELECT ii FROM t1 ORDER BY ii }
+} {1 2 3}
+
+do_test rtree-2.X {
+ execsql { DROP TABLE t1 }
+} {}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-3.* test linear scans of r-tree table data. To test
+# this we have to insert some data into an r-tree, but that is not the
+# focus of these tests.
+#
+do_test rtree-3.1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
+ SELECT * FROM t1;
+ }
+} {}
+do_test rtree-3.1.2 {
+ execsql {
+ INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
+ SELECT * FROM t1;
+ }
+} {5 1.0 3.0 2.0 4.0}
+do_test rtree-3.1.3 {
+ execsql {
+ INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
+ SELECT * FROM t1;
+ }
+} {5 1.0 3.0 2.0 4.0 6 2.0 6.0 4.0 8.0}
+
+# Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)):
+do_test rtree-3.2.1 {
+ catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) }
+} {1 {constraint failed}}
+do_test rtree-3.2.2 {
+ catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) }
+} {0 {}}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-5.* test DELETE operations.
+#
+do_test rtree-5.1.1 {
+ execsql { CREATE VIRTUAL TABLE t2 USING rtree(ii, x1, x2) }
+} {}
+do_test rtree-5.1.2 {
+ execsql {
+ INSERT INTO t2 VALUES(1, 10, 20);
+ INSERT INTO t2 VALUES(2, 30, 40);
+ INSERT INTO t2 VALUES(3, 50, 60);
+ SELECT * FROM t2 ORDER BY ii;
+ }
+} {1 10.0 20.0 2 30.0 40.0 3 50.0 60.0}
+do_test rtree-5.1.3 {
+ execsql {
+ DELETE FROM t2 WHERE ii=2;
+ SELECT * FROM t2 ORDER BY ii;
+ }
+} {1 10.0 20.0 3 50.0 60.0}
+do_test rtree-5.1.4 {
+ execsql {
+ DELETE FROM t2 WHERE ii=1;
+ SELECT * FROM t2 ORDER BY ii;
+ }
+} {3 50.0 60.0}
+do_test rtree-5.1.5 {
+ execsql {
+ DELETE FROM t2 WHERE ii=3;
+ SELECT * FROM t2 ORDER BY ii;
+ }
+} {}
+do_test rtree-5.1.6 {
+ execsql { SELECT * FROM t2_rowid }
+} {}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-5.* test UPDATE operations.
+#
+do_test rtree-6.1.1 {
+ execsql { CREATE VIRTUAL TABLE t3 USING rtree(ii, x1, x2, y1, y2) }
+} {}
+do_test rtree-6.1.2 {
+ execsql {
+ INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
+ UPDATE t3 SET x2=5;
+ SELECT * FROM t3;
+ }
+} {1 2.0 5.0 4.0 5.0}
+do_test rtree-6.1.3 {
+ execsql { UPDATE t3 SET ii = 2 }
+ execsql { SELECT * FROM t3 }
+} {2 2.0 5.0 4.0 5.0}
+
+#----------------------------------------------------------------------------
+# Test cases rtree-7.* test rename operations.
+#
+do_test rtree-7.1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t4 USING rtree(ii, x1, x2, y1, y2, z1, z2);
+ INSERT INTO t4 VALUES(1, 2, 3, 4, 5, 6, 7);
+ }
+} {}
+do_test rtree-7.1.2 {
+ execsql { ALTER TABLE t4 RENAME TO t5 }
+ execsql { SELECT * FROM t5 }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.1.3 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM t5 }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.1.4 {
+ execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
+ execsql { SELECT * FROM "raisara ""one""'" }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.1.5 {
+ execsql { SELECT * FROM 'raisara "one"''' }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.1.6 {
+ execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
+ execsql { SELECT * FROM "abc 123" }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.1.7 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM "abc 123" }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+
+# An error midway through a rename operation.
+do_test rtree-7.2.1 {
+ execsql {
+ CREATE TABLE t4_node(a);
+ }
+ catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
+} {1 {SQL logic error or missing database}}
+do_test rtree-7.2.2 {
+ execsql { SELECT * FROM "abc 123" }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.2.3 {
+ execsql {
+ DROP TABLE t4_node;
+ CREATE TABLE t4_rowid(a);
+ }
+ catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
+} {1 {SQL logic error or missing database}}
+do_test rtree-7.2.4 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM "abc 123" }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+do_test rtree-7.2.5 {
+ execsql { DROP TABLE t4_rowid }
+ execsql { ALTER TABLE "abc 123" RENAME TO t4 }
+ execsql { SELECT * FROM t4 }
+} {1 2.0 3.0 4.0 5.0 6.0 7.0}
+
+
+#----------------------------------------------------------------------------
+# Test cases rtree-8.*
+#
+
+# Test that the function to determine if a leaf cell is part of the
+# result set works.
+do_test rtree-8.1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t6 USING rtree(ii, x1, x2);
+ INSERT INTO t6 VALUES(1, 3, 7);
+ INSERT INTO t6 VALUES(2, 4, 6);
+ }
+} {}
+do_test rtree-8.1.2 { execsql { SELECT ii FROM t6 WHERE x1>2 } } {1 2}
+do_test rtree-8.1.3 { execsql { SELECT ii FROM t6 WHERE x1>3 } } {2}
+do_test rtree-8.1.4 { execsql { SELECT ii FROM t6 WHERE x1>4 } } {}
+do_test rtree-8.1.5 { execsql { SELECT ii FROM t6 WHERE x1>5 } } {}
+do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
+do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
+do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
+
+
+finish_test
diff --git a/third_party/sqlite/ext/rtree/rtree2.test b/third_party/sqlite/ext/rtree/rtree2.test
new file mode 100755
index 0000000..7e38c8f
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree2.test
@@ -0,0 +1,152 @@
+# 2008 Feb 19
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the r-tree extension.
+#
+# $Id: rtree2.test,v 1.4 2008/07/14 15:37:01 danielk1977 Exp $
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname $argv0] .. .. test]
+}
+source [file join [file dirname [info script]] rtree_util.tcl]
+source $testdir/tester.tcl
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+set ::NROW 1000
+set ::NDEL 10
+set ::NSELECT 100
+
+if {[info exists ISQUICK] && $ISQUICK} {
+ set ::NROW 100
+ set ::NSELECT 10
+}
+
+foreach module {rtree_i32 rtree} {
+ for {set nDim 1} {$nDim <= 5} {incr nDim} {
+
+ do_test rtree2-$module.$nDim.1 {
+ set cols [list]
+ foreach c [list c0 c1 c2 c3 c4 c5 c6 c7 c8 c9] {
+ lappend cols "$c REAL"
+ }
+ set cols [join [lrange $cols 0 [expr {$nDim*2-1}]] ", "]
+ execsql "
+ CREATE VIRTUAL TABLE t1 USING ${module}(ii, $cols);
+ CREATE TABLE t2 (ii, $cols);
+ "
+ } {}
+
+ do_test rtree2-$module.$nDim.2 {
+ db transaction {
+ for {set ii 0} {$ii < $::NROW} {incr ii} {
+ #puts "Row $ii"
+ set values [list]
+ for {set jj 0} {$jj<$nDim*2} {incr jj} {
+ lappend values [expr int(rand()*1000)]
+ }
+ set values [join $values ,]
+ #puts [rtree_treedump db t1]
+ #puts "INSERT INTO t2 VALUES($ii, $values)"
+ set rc [catch {db eval "INSERT INTO t1 VALUES($ii, $values)"}]
+ if {$rc} {
+ incr ii -1
+ } else {
+ db eval "INSERT INTO t2 VALUES($ii, $values)"
+ }
+ #if {[rtree_check db t1]} {
+ #puts [rtree_treedump db t1]
+ #exit
+ #}
+ }
+ }
+
+ set t1 [execsql {SELECT * FROM t1 ORDER BY ii}]
+ set t2 [execsql {SELECT * FROM t2 ORDER BY ii}]
+ set rc [expr {$t1 eq $t2}]
+ if {$rc != 1} {
+ puts $t1
+ puts $t2
+ }
+ set rc
+ } {1}
+
+ do_test rtree2-$module.$nDim.3 {
+ rtree_check db t1
+ } 0
+
+ set OPS [list < > <= >= =]
+ for {set ii 0} {$ii < $::NSELECT} {incr ii} {
+ do_test rtree2-$module.$nDim.4.$ii.1 {
+ set where [list]
+ foreach look_three_dots! {. . .} {
+ set colidx [expr int(rand()*($nDim*2+1))-1]
+ if {$colidx<0} {
+ set col ii
+ } else {
+ set col "c$colidx"
+ }
+ set op [lindex $OPS [expr int(rand()*[llength $OPS])]]
+ set val [expr int(rand()*1000)]
+ lappend where "$col $op $val"
+ }
+ set where [join $where " AND "]
+
+ set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
+ set t2 [execsql "SELECT * FROM t2 WHERE $where ORDER BY ii"]
+ set rc [expr {$t1 eq $t2}]
+ if {$rc != 1} {
+ #puts $where
+ puts $t1
+ puts $t2
+ #puts [rtree_treedump db t1]
+ #breakpoint
+ #set t1 [execsql "SELECT * FROM t1 WHERE $where ORDER BY ii"]
+ #exit
+ }
+ set rc
+ } {1}
+ }
+
+ for {set ii 0} {$ii < $::NROW} {incr ii $::NDEL} {
+ #puts [rtree_treedump db t1]
+ do_test rtree2-$module.$nDim.5.$ii.1 {
+ execsql "DELETE FROM t2 WHERE ii <= $::ii"
+ execsql "DELETE FROM t1 WHERE ii <= $::ii"
+
+ set t1 [execsql {SELECT * FROM t1 ORDER BY ii}]
+ set t2 [execsql {SELECT * FROM t2 ORDER BY ii}]
+ set rc [expr {$t1 eq $t2}]
+ if {$rc != 1} {
+ puts $t1
+ puts $t2
+ }
+ set rc
+ } {1}
+ do_test rtree2-$module.$nDim.5.$ii.2 {
+ rtree_check db t1
+ } {0}
+ }
+
+ do_test rtree2-$module.$nDim.6 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }
+ } {}
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/ext/rtree/rtree3.test b/third_party/sqlite/ext/rtree/rtree3.test
new file mode 100755
index 0000000..b83ceeb4
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree3.test
@@ -0,0 +1,74 @@
+# 2008 Feb 19
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing that the r-tree correctly handles
+# out-of-memory conditions.
+#
+# $Id: rtree3.test,v 1.2 2008/06/23 15:55:52 danielk1977 Exp $
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname $argv0] .. .. test]
+}
+source $testdir/tester.tcl
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+# Only run these tests if memory debugging is turned on.
+#
+source $testdir/malloc_common.tcl
+if {!$MEMDEBUG} {
+ puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+do_malloc_test rtree3-1 -sqlbody {
+ BEGIN TRANSACTION;
+ CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
+ INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
+ INSERT INTO rt VALUES(NULL, 13, 15, 17, 19);
+ DELETE FROM rt WHERE ii = 1;
+ SELECT * FROM rt;
+ SELECT ii FROM rt WHERE ii = 2;
+ COMMIT;
+}
+do_malloc_test rtree3-2 -sqlprep {
+ CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
+ INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
+} -sqlbody {
+ DROP TABLE rt;
+}
+
+
+do_malloc_test rtree3-3 -sqlprep {
+ CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
+ INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
+} -tclbody {
+ db eval BEGIN
+ for {set ii 0} {$ii < 100} {incr ii} {
+ set f [expr rand()]
+ db eval {INSERT INTO rt VALUES(NULL, $f*10.0, $f*10.0, $f*15.0, $f*15.0)}
+ }
+ db eval COMMIT
+ db eval BEGIN
+ for {set ii 0} {$ii < 100} {incr ii} {
+ set f [expr rand()]
+ db eval { DELETE FROM rt WHERE x1<($f*10.0) AND x1>($f*10.5) }
+ }
+ db eval COMMIT
+}
+
+finish_test
+
diff --git a/third_party/sqlite/ext/rtree/rtree4.test b/third_party/sqlite/ext/rtree/rtree4.test
new file mode 100755
index 0000000..d73e7a6
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree4.test
@@ -0,0 +1,236 @@
+# 2008 May 23
+#
+# 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.
+#
+#***********************************************************************
+#
+# Randomized test cases for the rtree extension.
+#
+# $Id: rtree4.test,v 1.3 2008/06/23 15:55:52 danielk1977 Exp $
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname $argv0] .. .. test]
+}
+source $testdir/tester.tcl
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+set ::NROW 2500
+if {[info exists ISQUICK] && $ISQUICK} {
+ set ::NROW 250
+}
+
+# Return a floating point number between -X and X.
+#
+proc rand {X} {
+ return [expr {int((rand()-0.5)*1024.0*$X)/512.0}]
+}
+
+# Return a positive floating point number less than or equal to X
+#
+proc randincr {X} {
+ while 1 {
+ set r [expr {int(rand()*$X*32.0)/32.0}]
+ if {$r>0.0} {return $r}
+ }
+}
+
+# Scramble the $inlist into a random order.
+#
+proc scramble {inlist} {
+ set y {}
+ foreach x $inlist {
+ lappend y [list [expr {rand()}] $x]
+ }
+ set y [lsort $y]
+ set outlist {}
+ foreach x $y {
+ lappend outlist [lindex $x 1]
+ }
+ return $outlist
+}
+
+# Always use the same random seed so that the sequence of tests
+# is repeatable.
+#
+expr {srand(1234)}
+
+# Run these tests for all number of dimensions between 1 and 5.
+#
+for {set nDim 1} {$nDim<=5} {incr nDim} {
+
+ # Construct an rtree virtual table and an ordinary btree table
+ # to mirror it. The ordinary table should be much slower (since
+ # it has to do a full table scan) but should give the exact same
+ # answers.
+ #
+ do_test rtree4-$nDim.1 {
+ set clist {}
+ set cklist {}
+ for {set i 0} {$i<$nDim} {incr i} {
+ lappend clist mn$i mx$i
+ lappend cklist "mn$i<mx$i"
+ }
+ db eval "DROP TABLE IF EXISTS rx"
+ db eval "DROP TABLE IF EXISTS bx"
+ db eval "CREATE VIRTUAL TABLE rx USING rtree(id, [join $clist ,])"
+ db eval "CREATE TABLE bx(id INTEGER PRIMARY KEY,\
+ [join $clist ,], CHECK( [join $cklist { AND }] ))"
+ } {}
+
+ # Do many insertions of small objects. Do both overlapping and
+ # contained-within queries after each insert to verify that all
+ # is well.
+ #
+ unset -nocomplain where
+ for {set i 1} {$i<$::NROW} {incr i} {
+ # Do a random insert
+ #
+ do_test rtree-$nDim.2.$i.1 {
+ set vlist {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 50]}]
+ lappend vlist $mn $mx
+ }
+ db eval "INSERT INTO rx VALUES(NULL, [join $vlist ,])"
+ db eval "INSERT INTO bx VALUES(NULL, [join $vlist ,])"
+ } {}
+
+ # Do a contained-in query on all dimensions
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mn$j>=$mn mx$j<=$mx
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.2 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do an overlaps query on all dimensions
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mx$j>=$mn mn$j<=$mx
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.3 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do a contained-in query with surplus contraints at the beginning.
+ # This should force a full-table scan on the rtree.
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ lappend where mn$j>-10000 mx$j<10000
+ }
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mn$j>=$mn mx$j<=$mx
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.3 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do an overlaps query with surplus contraints at the beginning.
+ # This should force a full-table scan on the rtree.
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ lappend where mn$j>=-10000 mx$j<=10000
+ }
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mx$j>$mn mn$j<$mx
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.4 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do a contained-in query with surplus contraints at the end
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mn$j>=$mn mx$j<$mx
+ }
+ for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} {
+ lappend where mn$j>=-10000 mx$j<10000
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.5 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do an overlaps query with surplus contraints at the end
+ #
+ set where {}
+ for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} {
+ set mn [rand 10000]
+ set mx [expr {$mn+[randincr 500]}]
+ lappend where mx$j>$mn mn$j<=$mx
+ }
+ for {set j 0} {$j<$nDim} {incr j} {
+ lappend where mx$j>-10000 mn$j<=10000
+ }
+ set where "WHERE [join $where { AND }]"
+ do_test rtree-$nDim.2.$i.6 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do a contained-in query with surplus contraints where the
+ # constraints appear in a random order.
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn1 [rand 10000]
+ set mn2 [expr {$mn1+[randincr 100]}]
+ set mx1 [expr {$mn2+[randincr 400]}]
+ set mx2 [expr {$mx1+[randincr 100]}]
+ lappend where mn$j>=$mn1 mn$j>$mn2 mx$j<$mx1 mx$j<=$mx2
+ }
+ set where "WHERE [join [scramble $where] { AND }]"
+ do_test rtree-$nDim.2.$i.7 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+
+ # Do an overlaps query with surplus contraints where the
+ # constraints appear in a random order.
+ #
+ set where {}
+ for {set j 0} {$j<$nDim} {incr j} {
+ set mn1 [rand 10000]
+ set mn2 [expr {$mn1+[randincr 100]}]
+ set mx1 [expr {$mn2+[randincr 400]}]
+ set mx2 [expr {$mx1+[randincr 100]}]
+ lappend where mx$j>=$mn1 mx$j>$mn2 mn$j<$mx1 mn$j<=$mx2
+ }
+ set where "WHERE [join [scramble $where] { AND }]"
+ do_test rtree-$nDim.2.$i.8 {
+ list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
+ } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
+ }
+
+}
+
+finish_test
diff --git a/third_party/sqlite/ext/rtree/rtree5.test b/third_party/sqlite/ext/rtree/rtree5.test
new file mode 100755
index 0000000..4fa007f
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree5.test
@@ -0,0 +1,80 @@
+# 2008 Jul 14
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the r-tree extension when it is
+# configured to store values as 32 bit integers.
+#
+# $Id: rtree5.test,v 1.1 2008/07/14 15:37:01 danielk1977 Exp $
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname $argv0] .. .. test]
+}
+source $testdir/tester.tcl
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+do_test rtree5-1.0 {
+ execsql { CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2, y1, y2) }
+} {}
+do_test rtree5-1.1 {
+ execsql { INSERT INTO t1 VALUES(1, 5, 10, 4, 11.2) }
+} {}
+do_test rtree5-1.2 {
+ execsql { SELECT * FROM t1 }
+} {1 5 10 4 11}
+do_test rtree5-1.3 {
+ execsql { SELECT typeof(x1) FROM t1 }
+} {integer}
+
+do_test rtree5-1.4 {
+ execsql { SELECT x1==5 FROM t1 }
+} {1}
+do_test rtree5-1.5 {
+ execsql { SELECT x1==5.2 FROM t1 }
+} {0}
+do_test rtree5-1.6 {
+ execsql { SELECT x1==5.0 FROM t1 }
+} {1}
+
+do_test rtree5-1.7 {
+ execsql { SELECT count(*) FROM t1 WHERE x1==5 }
+} {1}
+do_test rtree5-1.8 {
+ execsql { SELECT count(*) FROM t1 WHERE x1==5.2 }
+} {0}
+do_test rtree5-1.9 {
+ execsql { SELECT count(*) FROM t1 WHERE x1==5.0 }
+} {1}
+
+do_test rtree5-1.10 {
+ execsql { SELECT (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5 }
+} {2147483643 2147483647 -2147483648 -2147483643}
+do_test rtree5-1.10 {
+ execsql {
+ INSERT INTO t1 VALUES(2, (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5)
+ }
+} {}
+do_test rtree5-1.12 {
+ execsql { SELECT * FROM t1 WHERE id=2 }
+} {2 2147483643 2147483647 -2147483648 -2147483643}
+do_test rtree5-1.13 {
+ execsql {
+ SELECT * FROM t1 WHERE
+ x1=2147483643 AND x2=2147483647 AND
+ y1=-2147483648 AND y2=-2147483643
+ }
+} {2 2147483643 2147483647 -2147483648 -2147483643}
+
+finish_test
diff --git a/third_party/sqlite/ext/rtree/rtree_perf.tcl b/third_party/sqlite/ext/rtree/rtree_perf.tcl
new file mode 100755
index 0000000..fa3a4d3
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree_perf.tcl
@@ -0,0 +1,76 @@
+
+set testdir [file join [file dirname $argv0] .. .. test]
+source $testdir/tester.tcl
+
+ifcapable !rtree {
+ finish_test
+ return
+}
+
+set NROW 10000
+set NQUERY 500
+
+puts "Generating $NROW rows of data..."
+set data [list]
+for {set ii 0} {$ii < $NROW} {incr ii} {
+ set x1 [expr {rand()*1000}]
+ set x2 [expr {$x1+rand()*50}]
+ set y1 [expr {rand()*1000}]
+ set y2 [expr {$y1+rand()*50}]
+ lappend data $x1 $x2 $y1 $y2
+}
+puts "Finished generating data"
+
+
+set sql1 {CREATE TABLE btree(ii INTEGER PRIMARY KEY, x1, x2, y1, y2)}
+set sql2 {CREATE VIRTUAL TABLE rtree USING rtree(ii, x1, x2, y1, y2)}
+puts "Creating tables:"
+puts " $sql1"
+puts " $sql2"
+db eval $sql1
+db eval $sql2
+
+db eval "pragma cache_size=100"
+
+puts -nonewline "Inserting into btree... "
+flush stdout
+set btree_time [time {db transaction {
+ set ii 1
+ foreach {x1 x2 y1 y2} $data {
+ db eval {INSERT INTO btree VALUES($ii, $x1, $x2, $y1, $y2)}
+ incr ii
+ }
+}}]
+puts "$btree_time"
+
+puts -nonewline "Inserting into rtree... "
+flush stdout
+set rtree_time [time {db transaction {
+ set ii 1
+ foreach {x1 x2 y1 y2} $data {
+ incr ii
+ db eval {INSERT INTO rtree VALUES($ii, $x1, $x2, $y1, $y2)}
+ }
+}}]
+puts "$rtree_time"
+
+
+puts -nonewline "Selecting from btree... "
+flush stdout
+set btree_select_time [time {
+ foreach {x1 x2 y1 y2} [lrange $data 0 [expr $NQUERY*4-1]] {
+ db eval {SELECT * FROM btree WHERE x1<$x1 AND x2>$x2 AND y1<$y1 AND y2>$y2}
+ }
+}]
+puts "$btree_select_time"
+
+puts -nonewline "Selecting from rtree... "
+flush stdout
+set rtree_select_time [time {
+ foreach {x1 x2 y1 y2} [lrange $data 0 [expr $NQUERY*4-1]] {
+ db eval {SELECT * FROM rtree WHERE x1<$x1 AND x2>$x2 AND y1<$y1 AND y2>$y2}
+ }
+}]
+puts "$rtree_select_time"
+
+
diff --git a/third_party/sqlite/ext/rtree/rtree_util.tcl b/third_party/sqlite/ext/rtree/rtree_util.tcl
new file mode 100755
index 0000000..55482e4
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/rtree_util.tcl
@@ -0,0 +1,195 @@
+# 2008 Feb 19
+#
+# 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 contains Tcl code that may be useful for testing or
+# analyzing r-tree structures created with this module. It is
+# used by both test procedures and the r-tree viewer application.
+#
+# $Id: rtree_util.tcl,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
+#
+
+
+#--------------------------------------------------------------------------
+# PUBLIC API:
+#
+# rtree_depth
+# rtree_ndim
+# rtree_node
+# rtree_mincells
+# rtree_check
+# rtree_dump
+# rtree_treedump
+#
+
+proc rtree_depth {db zTab} {
+ $db one "SELECT rtreedepth(data) FROM ${zTab}_node WHERE nodeno=1"
+}
+
+proc rtree_nodedepth {db zTab iNode} {
+ set iDepth [rtree_depth $db $zTab]
+
+ set ii $iNode
+ while {$ii != 1} {
+ set sql "SELECT parentnode FROM ${zTab}_parent WHERE nodeno = $ii"
+ set ii [db one $sql]
+ incr iDepth -1
+ }
+
+ return $iDepth
+}
+
+# Return the number of dimensions of the rtree.
+#
+proc rtree_ndim {db zTab} {
+ set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
+}
+
+# Return the contents of rtree node $iNode.
+#
+proc rtree_node {db zTab iNode {iPrec 6}} {
+ set nDim [rtree_ndim $db $zTab]
+ set sql "
+ SELECT rtreenode($nDim, data) FROM ${zTab}_node WHERE nodeno = $iNode
+ "
+ set node [db one $sql]
+
+ set nCell [llength $node]
+ set nCoord [expr $nDim*2]
+ for {set ii 0} {$ii < $nCell} {incr ii} {
+ for {set jj 1} {$jj <= $nCoord} {incr jj} {
+ set newval [format "%.${iPrec}f" [lindex $node $ii $jj]]
+ lset node $ii $jj $newval
+ }
+ }
+ set node
+}
+
+proc rtree_mincells {db zTab} {
+ set n [$db one "select length(data) FROM ${zTab}_node LIMIT 1"]
+ set nMax [expr {int(($n-4)/(8+[rtree_ndim $db $zTab]*2*4))}]
+ return [expr {int($nMax/3)}]
+}
+
+# An integrity check for the rtree $zTab accessible via database
+# connection $db.
+#
+proc rtree_check {db zTab} {
+ array unset ::checked
+
+ # Check each r-tree node.
+ set rc [catch {
+ rtree_node_check $db $zTab 1 [rtree_depth $db $zTab]
+ } msg]
+ if {$rc && $msg ne ""} { error $msg }
+
+ # Check that the _rowid and _parent tables have the right
+ # number of entries.
+ set nNode [$db one "SELECT count(*) FROM ${zTab}_node"]
+ set nRow [$db one "SELECT count(*) FROM ${zTab}"]
+ set nRowid [$db one "SELECT count(*) FROM ${zTab}_rowid"]
+ set nParent [$db one "SELECT count(*) FROM ${zTab}_parent"]
+
+ if {$nNode != ($nParent+1)} {
+ error "Wrong number of entries in ${zTab}_parent"
+ }
+ if {$nRow != $nRowid} {
+ error "Wrong number of entries in ${zTab}_rowid"
+ }
+
+ return $rc
+}
+
+proc rtree_node_check {db zTab iNode iDepth} {
+ if {[info exists ::checked($iNode)]} { error "Second ref to $iNode" }
+ set ::checked($iNode) 1
+
+ set node [rtree_node $db $zTab $iNode]
+ if {$iNode!=1 && [llength $node]==0} { error "No such node: $iNode" }
+
+ if {$iNode != 1 && [llength $node]<[rtree_mincells $db $zTab]} {
+ puts "Node $iNode: Has only [llength $node] cells"
+ error ""
+ }
+ if {$iNode == 1 && [llength $node]==1 && [rtree_depth $db $zTab]>0} {
+ set depth [rtree_depth $db $zTab]
+ puts "Node $iNode: Has only 1 child (tree depth is $depth)"
+ error ""
+ }
+
+ set nDim [expr {([llength [lindex $node 0]]-1)/2}]
+
+ if {$iDepth > 0} {
+ set d [expr $iDepth-1]
+ foreach cell $node {
+ set shouldbe [rtree_node_check $db $zTab [lindex $cell 0] $d]
+ if {$cell ne $shouldbe} {
+ puts "Node $iNode: Cell is: {$cell}, should be {$shouldbe}"
+ error ""
+ }
+ }
+ }
+
+ set mapping_table "${zTab}_parent"
+ set mapping_sql "SELECT parentnode FROM $mapping_table WHERE rowid = \$rowid"
+ if {$iDepth==0} {
+ set mapping_table "${zTab}_rowid"
+ set mapping_sql "SELECT nodeno FROM $mapping_table WHERE rowid = \$rowid"
+ }
+ foreach cell $node {
+ set rowid [lindex $cell 0]
+ set mapping [db one $mapping_sql]
+ if {$mapping != $iNode} {
+ puts "Node $iNode: $mapping_table entry for cell $rowid is $mapping"
+ error ""
+ }
+ }
+
+ set ret [list $iNode]
+ for {set ii 1} {$ii <= $nDim*2} {incr ii} {
+ set f [lindex $node 0 $ii]
+ foreach cell $node {
+ set f2 [lindex $cell $ii]
+ if {($ii%2)==1 && $f2<$f} {set f $f2}
+ if {($ii%2)==0 && $f2>$f} {set f $f2}
+ }
+ lappend ret $f
+ }
+ return $ret
+}
+
+proc rtree_dump {db zTab} {
+ set zRet ""
+ set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
+ set sql "SELECT nodeno, rtreenode($nDim, data) AS node FROM ${zTab}_node"
+ $db eval $sql {
+ append zRet [format "% -10s %s\n" $nodeno $node]
+ }
+ set zRet
+}
+
+proc rtree_nodetreedump {db zTab zIndent iDepth iNode} {
+ set ret ""
+ set node [rtree_node $db $zTab $iNode 1]
+ append ret [format "%-3d %s%s\n" $iNode $zIndent $node]
+ if {$iDepth>0} {
+ foreach cell $node {
+ set i [lindex $cell 0]
+ append ret [rtree_nodetreedump $db $zTab "$zIndent " [expr $iDepth-1] $i]
+ }
+ }
+ set ret
+}
+
+proc rtree_treedump {db zTab} {
+ set d [rtree_depth $db $zTab]
+ rtree_nodetreedump $db $zTab "" $d 1
+}
+
diff --git a/third_party/sqlite/ext/rtree/viewrtree.tcl b/third_party/sqlite/ext/rtree/viewrtree.tcl
new file mode 100755
index 0000000..2b4dd1b
--- /dev/null
+++ b/third_party/sqlite/ext/rtree/viewrtree.tcl
@@ -0,0 +1,189 @@
+
+load ./libsqlite3.dylib
+#package require sqlite3
+source [file join [file dirname $argv0] rtree_util.tcl]
+
+wm title . "SQLite r-tree viewer"
+
+if {[llength $argv]!=1} {
+ puts stderr "Usage: $argv0 <database-file>"
+ puts stderr ""
+ exit
+}
+sqlite3 db [lindex $argv 0]
+
+canvas .c -background white -width 400 -height 300 -highlightthickness 0
+
+button .b -text "Parent Node" -command {
+ set sql "SELECT parentnode FROM $::O(zTab)_parent WHERE nodeno = $::O(iNode)"
+ set ::O(iNode) [db one $sql]
+ if {$::O(iNode) eq ""} {set ::O(iNode) 1}
+ view_node
+}
+
+set O(iNode) 1
+set O(zTab) ""
+set O(listbox_captions) [list]
+set O(listbox_itemmap) [list]
+set O(listbox_highlight) -1
+
+listbox .l -listvariable ::O(listbox_captions) -yscrollcommand {.ls set}
+scrollbar .ls -command {.l yview}
+label .status -font courier -anchor w
+label .title -anchor w -text "Node 1:" -background white -borderwidth 0
+
+
+set rtree_tables [list]
+db eval {
+ SELECT name
+ FROM sqlite_master
+ WHERE type='table' AND sql LIKE '%virtual%table%using%rtree%'
+} {
+ set nCol [expr [llength [db eval "pragma table_info($name)"]]/6]
+ if {$nCol != 5} {
+ puts stderr "Not viewing $name - is not 2-dimensional"
+ } else {
+ lappend rtree_tables [list Table $name]
+ }
+}
+if {$rtree_tables eq ""} {
+ puts stderr "Cannot find an r-tree table in database [lindex $argv 0]"
+ puts stderr ""
+ exit
+}
+eval tk_optionMenu .select option_var $rtree_tables
+trace add variable option_var write set_option_var
+proc set_option_var {args} {
+ set ::O(zTab) [lindex $::option_var 1]
+ set ::O(iNode) 1
+ view_node
+}
+set ::O(zTab) [lindex $::rtree_tables 0 1]
+
+bind .l <1> {listbox_click [.l nearest %y]}
+bind .l <Motion> {listbox_mouseover [.l nearest %y]}
+bind .l <Leave> {listbox_mouseover -1}
+
+proc listbox_click {sel} {
+ if {$sel ne ""} {
+ set ::O(iNode) [lindex $::O(listbox_captions) $sel 1]
+ view_node
+ }
+}
+proc listbox_mouseover {i} {
+ set oldid [lindex $::O(listbox_itemmap) $::O(listbox_highlight)]
+ .c itemconfigure $oldid -fill ""
+
+ .l selection clear 0 end
+ .status configure -text ""
+ if {$i>=0} {
+ set id [lindex $::O(listbox_itemmap) $i]
+ .c itemconfigure $id -fill grey
+ .c lower $id
+ set ::O(listbox_highlight) $i
+ .l selection set $i
+ .status configure -text [cell_report db $::O(zTab) $::O(iNode) $i]
+ }
+}
+
+grid configure .select -row 0 -column 0 -columnspan 2 -sticky nsew
+grid configure .b -row 1 -column 0 -columnspan 2 -sticky nsew
+grid configure .l -row 2 -column 0 -sticky nsew
+grid configure .status -row 3 -column 0 -columnspan 3 -sticky nsew
+
+grid configure .title -row 0 -column 2 -sticky nsew
+grid configure .c -row 1 -column 2 -rowspan 2 -sticky nsew
+grid configure .ls -row 2 -column 1 -sticky nsew
+
+grid columnconfigure . 2 -weight 1
+grid rowconfigure . 2 -weight 1
+
+proc node_bbox {data} {
+ set xmin 0
+ set xmax 0
+ set ymin 0
+ set ymax 0
+ foreach {rowid xmin xmax ymin ymax} [lindex $data 0] break
+ foreach cell [lrange $data 1 end] {
+ foreach {rowid x1 x2 y1 y2} $cell break
+ if {$x1 < $xmin} {set xmin $x1}
+ if {$x2 > $xmax} {set xmax $x2}
+ if {$y1 < $ymin} {set ymin $y1}
+ if {$y2 > $ymax} {set ymax $y2}
+ }
+ list $xmin $xmax $ymin $ymax
+}
+
+proc view_node {} {
+ set iNode $::O(iNode)
+ set zTab $::O(zTab)
+
+ set data [rtree_node db $zTab $iNode 12]
+ set depth [rtree_nodedepth db $zTab $iNode]
+
+ .c delete all
+ set ::O(listbox_captions) [list]
+ set ::O(listbox_itemmap) [list]
+ set $::O(listbox_highlight) -1
+
+ .b configure -state normal
+ if {$iNode == 1} {.b configure -state disabled}
+ .title configure -text "Node $iNode: [cell_report db $zTab $iNode -1]"
+
+ foreach {xmin xmax ymin ymax} [node_bbox $data] break
+ set total_area 0.0
+
+ set xscale [expr {double([winfo width .c]-20)/($xmax-$xmin)}]
+ set yscale [expr {double([winfo height .c]-20)/($ymax-$ymin)}]
+
+ set xoff [expr {10.0 - $xmin*$xscale}]
+ set yoff [expr {10.0 - $ymin*$yscale}]
+
+ foreach cell $data {
+ foreach {rowid x1 x2 y1 y2} $cell break
+ set total_area [expr {$total_area + ($x2-$x1)*($y2-$y1)}]
+ set x1 [expr {$x1*$xscale + $xoff}]
+ set x2 [expr {$x2*$xscale + $xoff}]
+ set y1 [expr {$y1*$yscale + $yoff}]
+ set y2 [expr {$y2*$yscale + $yoff}]
+
+ set id [.c create rectangle $x1 $y1 $x2 $y2]
+ if {$depth>0} {
+ lappend ::O(listbox_captions) "Node $rowid"
+ lappend ::O(listbox_itemmap) $id
+ }
+ }
+}
+
+proc cell_report {db zTab iParent iCell} {
+ set data [rtree_node db $zTab $iParent 12]
+ set cell [lindex $data $iCell]
+
+ foreach {xmin xmax ymin ymax} [node_bbox $data] break
+ set total_area [expr ($xmax-$xmin)*($ymax-$ymin)]
+
+ if {$cell eq ""} {
+ set cell_area 0.0
+ foreach cell $data {
+ foreach {rowid x1 x2 y1 y2} $cell break
+ set cell_area [expr $cell_area+($x2-$x1)*($y2-$y1)]
+ }
+ set cell_area [expr $cell_area/[llength $data]]
+ set zReport [format "Size = %.1f x %.1f Average child area = %.1f%%" \
+ [expr $xmax-$xmin] [expr $ymax-$ymin] [expr 100.0*$cell_area/$total_area]\
+ ]
+ append zReport " Sub-tree height: [rtree_nodedepth db $zTab $iParent]"
+ } else {
+ foreach {rowid x1 x2 y1 y2} $cell break
+ set cell_area [expr ($x2-$x1)*($y2-$y1)]
+ set zReport [format "Size = %.1f x %.1f Area = %.1f%%" \
+ [expr $x2-$x1] [expr $y2-$y1] [expr 100.0*$cell_area/$total_area]
+ ]
+ }
+
+ return $zReport
+}
+
+view_node
+bind .c <Configure> view_node
+
diff --git a/third_party/sqlite/google_generate_preprocessed.sh b/third_party/sqlite/google_generate_preprocessed.sh
new file mode 100755
index 0000000..d28038f
--- /dev/null
+++ b/third_party/sqlite/google_generate_preprocessed.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2007 Google Inc. All Rights Reserved.
+# Author: shess@google.com (Scott Hess)
+
+g4 edit preprocessed/...
+
+mkdir bld
+cd bld
+../configure
+FILES="keywordhash.h opcodes.c opcodes.h parse.c parse.h sqlite3.h"
+OPTS=""
+# These options should match those in ../../tools/config.mk.
+OPTS="$OPTS -DSQLITE_OMIT_LOAD_EXTENSION=1"
+OPTS="$OPTS -DSQLITE_TRANSACTION_DEFAULT_IMMEDIATE=1"
+make "OPTS=$OPTS" $FILES
+cp -f $FILES ../preprocessed
+
+cd ..
+rm -rf bld
+
+g4 revert -a preprocessed/...
+
+# TODO(shess) I can't find config.h, which exists in the original
+# third_party/sqlite/ directory. I also haven't found a client of it,
+# yet, so maybe it's not a file we need.
diff --git a/third_party/sqlite/google_update_sqlite.sh b/third_party/sqlite/google_update_sqlite.sh
new file mode 100755
index 0000000..d9009d5
--- /dev/null
+++ b/third_party/sqlite/google_update_sqlite.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# A simple script to make it easier to merge in newer versions of sqlite.
+# It may not work perfectly, in which case, it at least serves as an outline
+# of the procedure to follow.
+
+if [ "$1" = "" ]; then
+ echo "Usage: $0 <Date to pull from CVS> [<merge tool>]"
+ echo "Example: $0 '2007/01/24 09:54:56'"
+ exit 1
+fi
+
+if [ "$2" = "" ]; then
+ MERGE="kdiff3 -m"
+fi
+
+BASE_DATE=`cat VERSION_DATE`
+NEW_DATE="$1"
+
+cd ..
+echo "_____ Logging in to sqlite.org cvs (log in as anonymous)..."
+cvs -d :pserver:anonymous@www.sqlite.org:/sqlite login
+cvs -d :pserver:anonymous@www.sqlite.org:/sqlite checkout -P -D "$BASE_DATE" -d sqlite-base sqlite
+cvs -d :pserver:anonymous@www.sqlite.org:/sqlite checkout -P -D "$NEW_DATE" -d sqlite-latest sqlite
+
+echo "_____ Running merge tool..."
+$MERGE sqlite-base sqlite-latest sqlite
+
+cd sqlite
+
+echo "_____ Updating VERSION_DATE to be $NEW_DATE ..."
+echo $NEW_DATE > VERSION_DATE
+
+echo "_____ Processing generated files..."
+./google_generate_preprocessed.sh
diff --git a/third_party/sqlite/install-sh b/third_party/sqlite/install-sh
new file mode 100755
index 0000000..e9de238
--- /dev/null
+++ b/third_party/sqlite/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/third_party/sqlite/ltmain.sh b/third_party/sqlite/ltmain.sh
new file mode 100755
index 0000000..7b6c74f
--- /dev/null
+++ b/third_party/sqlite/ltmain.sh
@@ -0,0 +1,7929 @@
+# Generated from ltmain.m4sh.
+
+# ltmain.sh (GNU libtool) 2.2.4
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+# --config show all configuration variables
+# --debug enable verbose shell tracing
+# -n, --dry-run display commands without modifying any files
+# --features display basic configuration information and exit
+# --mode=MODE use operation mode MODE
+# --preserve-dup-deps don't remove duplicate dependency libraries
+# --quiet, --silent don't print informational messages
+# --tag=TAG use configuration variables from tag TAG
+# -v, --verbose print informational messages (default)
+# --version print version information
+# -h, --help print short or long help message
+#
+# MODE must be one of the following:
+#
+# clean remove files from the build directory
+# compile compile a source file into a libtool object
+# execute automatically set library path, then run a program
+# finish complete the installation of libtool libraries
+# install install libraries or executables
+# link create a library or an executable
+# uninstall remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+# host-triplet: $host
+# shell: $SHELL
+# compiler: $LTCC
+# compiler flags: $LTCFLAGS
+# linker: $LD (gnu? $with_gnu_ld)
+# $progname: (GNU libtool) 2.2.4
+# automake: $automake_version
+# autoconf: $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=2.2.4
+TIMESTAMP=""
+package_revision=1.2976
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# NLS nuisances: We save the old values to restore during execute mode.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test \"\${$lt_var+set}\" = set; then
+ save_$lt_var=\$$lt_var
+ $lt_var=C
+ export $lt_var
+ lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+ lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+ fi"
+done
+
+$lt_unset CDPATH
+
+
+
+
+
+: ${CP="cp -f"}
+: ${ECHO="echo"}
+: ${EGREP="/usr/bin/grep -E"}
+: ${FGREP="/usr/bin/grep -F"}
+: ${GREP="/usr/bin/grep"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SED="/opt/local/bin/gsed"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS=" $lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+# Generated shell functions inserted here.
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+# The name of this program:
+# In the unlikely event $progname began with a '-', it would play havoc with
+# func_echo (imagine progname=-n), so we prepend ./ in that case:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+case $progname in
+ -*) progname=./$progname ;;
+esac
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=$func_dirname_result
+ progdir=`cd "$progdir" && pwd`
+ progpath="$progdir/$progname"
+ ;;
+ *)
+ save_IFS="$IFS"
+ IFS=:
+ for progdir in $PATH; do
+ IFS="$save_IFS"
+ test -x "$progdir/$progname" && break
+ done
+ IFS="$save_IFS"
+ test -n "$progdir" || progdir=`pwd`
+ progpath="$progdir/$progname"
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same. If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'. `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+ s/$bs4/&\\
+/g
+ s/^$bs2$dollar/$bs&/
+ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+ s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+ $ECHO "$progname${mode+: }$mode: $*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $opt_verbose && func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+ $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2
+
+ # bash bug again:
+ :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ func_error ${1+"$@"}
+ func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information." ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ my_directory_path="$1"
+ my_dir_list=
+
+ if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+ # Protect directory names starting with `-'
+ case $my_directory_path in
+ -*) my_directory_path="./$my_directory_path" ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$my_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ my_dir_list="$my_directory_path:$my_dir_list"
+
+ # If the last portion added has no slash in it, the list is done
+ case $my_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"`
+ done
+ my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'`
+
+ save_mkdir_p_IFS="$IFS"; IFS=':'
+ for my_dir in $my_dir_list; do
+ IFS="$save_mkdir_p_IFS"
+ # mkdir can fail with a `File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$my_dir" 2>/dev/null || :
+ done
+ IFS="$save_mkdir_p_IFS"
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$my_directory_path" || \
+ func_fatal_error "Failed to create \`$1'"
+ fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+ my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+ if test "$opt_dry_run" = ":"; then
+ # Return a directory name, but don't create it in dry-run mode
+ my_tmpdir="${my_template}-$$"
+ else
+
+ # If mktemp works, use that first and foremost
+ my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$my_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+ save_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$my_tmpdir"
+ umask $save_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$my_tmpdir" || \
+ func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+ fi
+
+ $ECHO "X$my_tmpdir" | $Xsed
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+ case $1 in
+ *[\\\`\"\$]*)
+ func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;;
+ *)
+ func_quote_for_eval_unquoted_result="$1" ;;
+ esac
+
+ case $func_quote_for_eval_unquoted_result in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and and variable
+ # expansion for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+ ;;
+ *)
+ func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+ esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ case $1 in
+ *[\\\`\"]*)
+ my_arg=`$ECHO "X$1" | $Xsed \
+ -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ my_arg="$1" ;;
+ esac
+
+ case $my_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ my_arg="\"$my_arg\""
+ ;;
+ esac
+
+ func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$my_cmd"
+ my_status=$?
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$lt_user_locale
+ $my_cmd"
+ my_status=$?
+ eval "$lt_safe_locale"
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / {
+ s/^# //
+ s/^# *$//
+ s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $SED -n '/^# Usage:/,/# -h/ {
+ s/^# //
+ s/^# *$//
+ s/\$progname/'$progname'/
+ p
+ }' < "$progpath"
+ $ECHO
+ $ECHO "run \`$progname --help | more' for full usage"
+ exit $?
+}
+
+# func_help
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $SED -n '/^# Usage:/,/# Report bugs to/ {
+ s/^# //
+ s/^# *$//
+ s*\$progname*'$progname'*
+ s*\$host*'"$host"'*
+ s*\$SHELL*'"$SHELL"'*
+ s*\$LTCC*'"$LTCC"'*
+ s*\$LTCFLAGS*'"$LTCFLAGS"'*
+ s*\$LD*'"$LD"'*
+ s/\$with_gnu_ld/'"$with_gnu_ld"'/
+ s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/
+ s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ func_error "missing argument for $1"
+ exit_cmd=exit
+}
+
+exit_cmd=:
+
+
+
+
+
+# Check that we have a working $ECHO.
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell, and then maybe $ECHO will work.
+ exec $SHELL "$progpath" --no-reexec ${1+"$@"}
+fi
+# Same for EGREP, and just to be sure, do LTCC as well
+if test "x$EGREP" = x ; then
+ EGREP=egrep
+fi
+if test "x$LTCC" = x ; then
+ LTCC=${CC-gcc}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<EOF
+$*
+EOF
+ exit $EXIT_SUCCESS
+fi
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+# $mode is unset
+nonopt=
+execute_dlfiles=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+opt_dry_run=false
+opt_duplicate_deps=false
+opt_silent=false
+opt_debug=:
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func_error ${1+"$@"}
+ func_error "See the $PACKAGE documentation for more information."
+ func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+ $ECHO "host: $host"
+ if test "$build_libtool_libs" = yes; then
+ $ECHO "enable shared libraries"
+ else
+ $ECHO "disable shared libraries"
+ fi
+ if test "$build_old_libs" = yes; then
+ $ECHO "enable static libraries"
+ else
+ $ECHO "disable static libraries"
+ fi
+
+ exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname="$1"
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+# Parse options once, thoroughly. This comes as soon as possible in
+# the script to make things like `libtool --version' happen quickly.
+{
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Parse non-mode specific arguments:
+ while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --config) func_config ;;
+
+ --debug) preserve_args="$preserve_args $opt"
+ func_echo "enabling shell trace mode"
+ opt_debug='set -x'
+ $opt_debug
+ ;;
+
+ -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ execute_dlfiles="$execute_dlfiles $1"
+ shift
+ ;;
+
+ --dry-run | -n) opt_dry_run=: ;;
+ --features) func_features ;;
+ --finish) mode="finish" ;;
+
+ --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ case $1 in
+ # Valid mode arguments:
+ clean) ;;
+ compile) ;;
+ execute) ;;
+ finish) ;;
+ install) ;;
+ link) ;;
+ relink) ;;
+ uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+
+ mode="$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_duplicate_deps=: ;;
+
+ --quiet|--silent) preserve_args="$preserve_args $opt"
+ opt_silent=:
+ ;;
+
+ --verbose| -v) preserve_args="$preserve_args $opt"
+ opt_silent=false
+ ;;
+
+ --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ preserve_args="$preserve_args $opt $1"
+ func_enable_tag "$1" # tagname is set here
+ shift
+ ;;
+
+ # Separate optargs to long options:
+ -dlopen=*|--mode=*|--tag=*)
+ func_opt_split "$opt"
+ set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"}
+ shift
+ ;;
+
+ -\?|-h) func_usage ;;
+ --help) opt_help=: ;;
+ --version) func_version ;;
+
+ -*) func_fatal_help "unrecognized option \`$opt'" ;;
+
+ *) nonopt="$opt"
+ break
+ ;;
+ esac
+ done
+
+
+ case $host in
+ *cygwin* | *mingw* | *pw32*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_duplicate_deps
+ ;;
+ esac
+
+ # Having warned about all mis-specified options, bail out if
+ # anything was wrong.
+ $exit_cmd $EXIT_FAILURE
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+$opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+ func_fatal_configuration "not configured to build any kind of library"
+ fi
+
+ test -z "$mode" && func_fatal_error "error: you must specify a MODE."
+
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$execute_dlfiles" && test "$mode" != execute; then
+ func_error "unrecognized option \`-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help="$help"
+ help="Try \`$progname --help --mode=$mode' for more information."
+}
+
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ $SED -e 4q "$1" 2>/dev/null \
+ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case "$lalib_p_line" in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_ltwrapper_scriptname_result=""
+ if func_ltwrapper_executable_p "$1"; then
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+ fi
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $opt_debug
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$save_ifs
+ eval cmd=\"$cmd\"
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $opt_debug
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $opt_debug
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case "$@ " in
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with \`--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=${1}
+ if test "$build_libtool_libs" = yes; then
+ write_lobj=\'${2}\'
+ else
+ write_lobj=none
+ fi
+
+ if test "$build_old_libs" = yes; then
+ write_oldobj=\'${3}\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "${write_libobj}"
+ }
+}
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $opt_debug
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile="$nonopt" # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg="$arg"
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj="$arg"
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify \`-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ pie_flag="$pie_flag $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ later="$later $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs="$IFS"; IFS=','
+ for arg in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$arg"
+ lastarg="$lastarg $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ base_compile="$base_compile $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg="$srcfile"
+ srcfile="$arg"
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_quote_for_eval "$lastarg"
+ base_compile="$base_compile $func_quote_for_eval_result"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with \`-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj="$func_basename_result"
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.obj | *.sx)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from \`$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name \`$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname="$func_basename_result"
+ xdir="$func_dirname_result"
+ lobj=${xdir}$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test "$build_old_libs" = yes; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2*)
+ pic_mode=default
+ ;;
+ esac
+ if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test "$compiler_c_o" = no; then
+ output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+ lockfile="$output_obj.lock"
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test "$need_locks" = yes; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test "$need_locks" = warn; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ removelist="$removelist $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ removelist="$removelist $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ if test -n "$fix_srcfile_path"; then
+ eval srcfile=\"$fix_srcfile_path\"
+ fi
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test "$build_libtool_libs" = yes; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test "$pic_mode" != no; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ command="$command -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test "$suppress_opt" = yes; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test "$build_old_libs" = yes; then
+ if test "$pic_mode" != yes; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test "$compiler_c_o" = yes; then
+ command="$command -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ command="$command$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test "$need_locks" != no; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+test "$mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to building PIC objects only
+ -prefer-non-pic try to building non-PIC objects only
+ -shared do not build a \`.o' file suitable for static linking
+ -static only build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the \`--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE Use a list of object files found in FILE to specify objects
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename. Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode \`$mode'"
+ ;;
+ esac
+
+ $ECHO
+ $ECHO "Try \`$progname --help' for more information about other modes."
+
+ exit $?
+}
+
+ # Now that we've collected a possible --mode arg, show help if necessary
+ $opt_help && func_mode_help
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $opt_debug
+ # The first argument is the command name.
+ cmd="$nonopt"
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $execute_dlfiles; do
+ test -f "$file" \
+ || func_fatal_help "\`$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "\`$file' was not linked with \`-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+
+ if test -f "$dir/$objdir/$dlname"; then
+ dir="$dir/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ ;;
+
+ *)
+ func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir="$absdir"
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic="$magic"
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -*) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_quote_for_eval "$file"
+ args="$args $func_quote_for_eval_result"
+ done
+
+ if test "X$opt_dry_run" = Xfalse; then
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd="\$cmd$args"
+ else
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ $ECHO "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $opt_debug
+ libdirs="$nonopt"
+ admincmds=
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for dir
+ do
+ libdirs="$libdirs $dir"
+ done
+
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || admincmds="$admincmds
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_silent && exit $EXIT_SUCCESS
+
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ $ECHO "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ $ECHO
+ $ECHO "If you ever happen to want to link against installed libraries"
+ $ECHO "in a given directory, LIBDIR, you must either use libtool, and"
+ $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'"
+ $ECHO "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable"
+ $ECHO " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ $ECHO " - add LIBDIR to the \`$runpath_var' environment variable"
+ $ECHO " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the \`$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+ fi
+ $ECHO
+
+ $ECHO "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ $ECHO "pages."
+ ;;
+ *)
+ $ECHO "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ exit $EXIT_SUCCESS
+}
+
+test "$mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $opt_debug
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+ # Allow the use of GNU shtool's install command.
+ $ECHO "X$nonopt" | $GREP shtool >/dev/null; then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog$func_quote_for_eval_result"
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=no
+ stripme=
+ for arg
+ do
+ if test -n "$dest"; then
+ files="$files $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=yes ;;
+ -f)
+ case " $install_prog " in
+ *[\\\ /]cp\ *) ;;
+ *) prev=$arg ;;
+ esac
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prev' option requires an argument"
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=yes
+ if test "$isdir" = yes; then
+ destdir="$dest"
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir="$func_dirname_result"
+ destname="$func_basename_result"
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "\`$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "\`$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ staticlibs="$staticlibs $file"
+ ;;
+
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) current_libdirs="$current_libdirs $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) future_libdirs="$future_libdirs $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir="$func_dirname_result"
+ dir="$dir$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking \`$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname="$1"
+ shift
+
+ srcname="$realname"
+ test -n "$relink_command" && srcname="$realname"T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme="$stripme"
+ case $host_os in
+ cygwin* | mingw* | pw32*)
+ case $realname in
+ *.dll.a)
+ tstripme=""
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try `ln -sf' first, because the `ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib="$destdir/$realname"
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name="$func_basename_result"
+ instname="$dir/$name"i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest="$destfile"
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to \`$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test "$build_old_libs" = yes; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=""
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=".exe"
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin*|*mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+ finalize=yes
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "\`$lib' has not been installed in \`$libdir'"
+ finalize=no
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test "$fast_install" = no && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if test "$finalize" = yes; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file="$func_basename_result"
+ outputname="$tmpdir/$file"
+ # Replace the output file specification.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_silent || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink \`$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file="$outputname"
+ else
+ func_warning "cannot relink \`$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name="$func_basename_result"
+
+ # Set up the ranlib parameters.
+ oldlib="$destdir/$name"
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $opt_debug
+ my_outputname="$1"
+ my_originator="$2"
+ my_pic_p="${3-no}"
+ my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms="${my_outputname}S.c"
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist="$output_objdir/${my_outputname}.nm"
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test "$dlself" = yes; then
+ func_verbose "generating symbol list for \`$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_verbose "extracting global C symbols from \`$progfile'"
+ $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols="$output_objdir/$outputname.exp"
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin | *mingw* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from \`$dlprefile'"
+ func_basename "$dlprefile"
+ name="$func_basename_result"
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+"
+ case $host in
+ *cygwin* | *mingw* )
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+/* DATA imports from DLLs on WIN32 con't be const, because
+ runtime relocations are performed -- see ld's documentation
+ on pseudo-relocs. */"
+ lt_dlsym_const= ;;
+ *osf5*)
+ echo >> "$output_objdir/$my_dlsyms" "\
+/* This system does not cope well with relocations in const data */"
+ lt_dlsym_const= ;;
+ *)
+ lt_dlsym_const=const ;;
+ esac
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+extern $lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+$lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+ { \"$my_originator\", (void *) 0 },"
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ if test "X$my_pic_p" != Xno; then
+ pic_flag_for_symtable=" $pic_flag"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) symtab_cflags="$symtab_cflags $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj="$output_objdir/${my_outputname}S.$objext"
+ case $host in
+ *cygwin* | *mingw* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for \`$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+func_win32_libid ()
+{
+ $opt_debug
+ win32_libid_type="unknown"
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
+ win32_nmres=`eval $NM -f posix -A $1 |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s,.*,import,
+ p
+ q
+ }
+ }'`
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $opt_debug
+ f_ex_an_ar_dir="$1"; shift
+ f_ex_an_ar_oldlib="$1"
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?'
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $opt_debug
+ my_gentop="$1"; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=""
+ my_xlib=""
+ my_xabs=""
+ my_xdir=""
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib="$func_basename_result"
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir="$my_gentop/$my_xlib_u"
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ darwin_base_archive=`basename "$darwin_archive"`
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches ; do
+ func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+ cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
+}
+
+
+
+# func_emit_wrapper arg
+#
+# emit a libtool wrapper script on stdout
+# don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variable
+# set therein.
+#
+# arg is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the '.lib' directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_arg1=$1
+ fi
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='${SED} -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ ECHO=\"$qecho\"
+ file=\"\$0\"
+ # Make sure echo works.
+ if test \"X\$1\" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+ elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then
+ # Yippee, \$ECHO works!
+ :
+ else
+ # Restart under the correct shell, and then maybe \$ECHO will work.
+ exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+ fi
+ fi\
+"
+ $ECHO "\
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test "$fast_install" = yes; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ $ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # Export our shlibpath_var if we have one.
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ # fixup the dll searchpath if we need to.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2*)
+ $ECHO "\
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+# end: func_emit_wrapper
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+
+ Currently, it simply execs the wrapper *script* "$SHELL $output",
+ but could eventually absorb all of the scripts functionality and
+ exec $objdir/$outputname directly.
+*/
+EOF
+ cat <<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+# define setmode _setmode
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+#ifdef _MSC_VER
+# define S_IXUSR _S_IEXEC
+# define stat _stat
+# ifndef _INTPTR_T_DEFINED
+# define intptr_t int
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+ defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifdef __CYGWIN__
+# define FOPEN_WB "wb"
+#endif
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#undef LTWRAPPER_DEBUGPRINTF
+#if defined DEBUGWRAPPER
+# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args
+static void
+ltwrapper_debugprintf (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+}
+#else
+# define LTWRAPPER_DEBUGPRINTF(args)
+#endif
+
+const char *program_name = NULL;
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+
+static const char *script_text =
+EOF
+
+ func_emit_wrapper yes |
+ $SED -e 's/\([\\"]\)/\\\1/g' \
+ -e 's/^/ "/' -e 's/$/\\n"/'
+ echo ";"
+
+ cat <<EOF
+const char * MAGIC_EXE = "$magic_exe";
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *shwrapper_name;
+ intptr_t rval = 127;
+ FILE *shwrapper;
+
+ const char *dumpscript_opt = "--lt-dump-script";
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) argv[0] : %s\n", argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) program_name : %s\n", program_name));
+
+ /* very simple arg parsing; don't want to rely on getopt */
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp (argv[i], dumpscript_opt) == 0)
+ {
+EOF
+ case "$host" in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<EOF
+ printf ("%s", script_text);
+ return 0;
+ }
+ }
+
+ newargz = XMALLOC (char *, argc + 2);
+EOF
+
+ if test -n "$TARGETSHELL" ; then
+ # no path translation at all
+ lt_newargv0=$TARGETSHELL
+ else
+ case "$host" in
+ *mingw* )
+ # awkward: cmd appends spaces to result
+ lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+ lt_newargv0=`( cmd //c echo $SHELL | $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo $SHELL`
+ case $lt_newargv0 in
+ *.exe | *.EXE) ;;
+ *) lt_newargv0=$lt_newargv0.exe ;;
+ esac
+ ;;
+ * ) lt_newargv0=$SHELL ;;
+ esac
+ fi
+
+ cat <<EOF
+ newargz[0] = (char *) xstrdup ("$lt_newargv0");
+EOF
+
+ cat <<"EOF"
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal ("Couldn't find %s", argv[0]);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (before symlink chase) at : %s\n",
+ tmp_pathspec));
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (after symlink chase) at : %s\n",
+ actual_cwrapper_path));
+ XFREE (tmp_pathspec);
+
+ shwrapper_name = (char *) xstrdup (base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, shwrapper_name);
+
+ /* shwrapper_name transforms */
+ strendzap (shwrapper_name, ".exe");
+ tmp_pathspec = XMALLOC (char, (strlen (shwrapper_name) +
+ strlen ("_ltshwrapperTMP") + 1));
+ strcpy (tmp_pathspec, shwrapper_name);
+ strcat (tmp_pathspec, "_ltshwrapperTMP");
+ XFREE (shwrapper_name);
+ shwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+ LTWRAPPER_DEBUGPRINTF (("(main) libtool shell wrapper name: %s\n",
+ shwrapper_name));
+EOF
+
+ cat <<EOF
+ newargz[1] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (shwrapper_name) + 1));
+ strcpy (newargz[1], actual_cwrapper_path);
+ strcat (newargz[1], "$objdir");
+ strcat (newargz[1], "/");
+ strcat (newargz[1], shwrapper_name);
+EOF
+
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[1], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (shwrapper_name);
+ XFREE (actual_cwrapper_path);
+
+ /* always write in binary mode */
+ if ((shwrapper = fopen (newargz[1], FOPEN_WB)) == 0)
+ {
+ lt_fatal ("Could not open %s for writing", newargz[1]);
+ }
+ fprintf (shwrapper, "%s", script_text);
+ fclose (shwrapper);
+
+ make_executable (newargz[1]);
+
+ for (i = 1; i < argc; i++)
+ newargz[i + 1] = xstrdup (argv[i]);
+ newargz[argc + 1] = NULL;
+
+ for (i = 0; i < argc + 1; i++)
+ {
+ LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, newargz[i]));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<EOF
+ /* execv doesn't actually work on mingw as expected on unix */
+ rval = _spawnv (_P_WAIT, "$lt_newargv0", (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"$lt_newargv0\": errno = %d\n", errno));
+ return 127;
+ }
+ return rval;
+}
+EOF
+ ;;
+ *)
+ cat <<EOF
+ execv ("$lt_newargv0", newargz);
+ return rval; /* =127, but avoids unused variable warning */
+}
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal ("Memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ int tmp_len;
+ char *concat_name;
+
+ LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n",
+ wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = q - p;
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n",
+ tmp_pathspec));
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ char *errstr = strerror (errno);
+ lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr);
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal ("Could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (strcmp (str, pat) == 0)
+ *str = '\0';
+ }
+ return str;
+}
+
+static void
+lt_error_core (int exit_status, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s: %s: ", program_name, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+ va_end (ap);
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $opt_debug
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # which system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll which has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=no
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module="${wl}-single_module"
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg="$1"
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ dlfiles|dlprefiles)
+ if test "$preload" = no; then
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=yes
+ fi
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test "$dlself" = no; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test "$prev" = dlprefiles; then
+ dlself=yes
+ elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ else
+ dlprefiles="$dlprefiles $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols="$arg"
+ test -f "$arg" \
+ || func_fatal_error "symbol file \`$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex="$arg"
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) deplibs="$deplibs $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir="$arg"
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# moreargs="$moreargs $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file \`$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex="$arg"
+ prev=
+ continue
+ ;;
+ release)
+ release="-$arg"
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test "$prev" = rpath; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) rpath="$rpath $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) xrpath="$xrpath $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds="$arg"
+ prev=
+ continue
+ ;;
+ weak)
+ weak_libs="$weak_libs $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg="$arg"
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test "X$arg" = "X-export-symbols"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname '-L' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of \`$dir'"
+ dir="$absdir"
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "*) ;;
+ *)
+ deplibs="$deplibs -L$dir"
+ lib_search_path="$lib_search_path $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+ testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ *) dllsearchpath="$dllsearchpath:$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ deplibs="$deplibs System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test "X$arg" = "X-lc" && continue
+ ;;
+ esac
+ elif test "X$arg" = "X-lc_r"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ deplibs="$deplibs $arg"
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module="${wl}-multi_module"
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "\`-no-install' is ignored for $host"
+ func_warning "assuming \`-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $wl$func_quote_for_eval_result"
+ linker_flags="$linker_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ # -64, -mips[0-9] enable 64-bit mode on the SGI compiler
+ # -r[0-9][0-9]* specifies the processor on the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler
+ # +DA*, +DD* enable 64-bit mode on the HP compiler
+ # -q* pass through compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* pass through architecture-specific
+ # compiler args for GCC
+ # -F/path gives path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC
+ # @file GCC response files
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ compiler_flags="$compiler_flags $arg"
+ continue
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ *.$objext)
+ # A standard object.
+ objs="$objs $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ deplibs="$deplibs $arg"
+ old_deplibs="$old_deplibs $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ if test "$prev" = dlfiles; then
+ # This library was specified with -dlopen.
+ dlfiles="$dlfiles $arg"
+ prev=
+ elif test "$prev" = dlprefiles; then
+ # The library was specified with -dlpreopen.
+ dlprefiles="$dlprefiles $arg"
+ prev=
+ else
+ deplibs="$deplibs $arg"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prevarg' option requires an argument"
+
+ if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname="$func_basename_result"
+ libobjs_save="$libobjs"
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ func_dirname "$output" "/" ""
+ output_objdir="$func_dirname_result$objdir"
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_duplicate_deps ; then
+ case "$libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ libs="$libs $deplib"
+ done
+
+ if test "$linkmode" = lib; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;;
+ esac
+ pre_post_deps="$pre_post_deps $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=no
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test "$linkmode,$pass" = "lib,link"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs="$tmp_deplibs"
+ fi
+
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan"; then
+ libs="$deplibs"
+ deplibs=
+ fi
+ if test "$linkmode" = prog; then
+ case $pass in
+ dlopen) libs="$dlfiles" ;;
+ dlpreopen) libs="$dlprefiles" ;;
+ link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+ esac
+ fi
+ if test "$linkmode,$pass" = "lib,dlpreopen"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ case $lib in
+ *.la) func_source "$lib" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"`
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) deplibs="$deplibs $deplib" ;;
+ esac
+ done
+ done
+ libs="$dlprefiles"
+ fi
+ if test "$pass" = dlopen; then
+ # Collect dlpreopened libraries
+ save_deplibs="$deplibs"
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=no
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags $deplib"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test "$linkmode" != lib && test "$linkmode" != prog; then
+ func_warning "\`-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test "$linkmode" = lib; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib="$searchdir/lib${name}${search_ext}"
+ if test -f "$lib"; then
+ if test "$search_ext" = ".la"; then
+ found=yes
+ else
+ found=no
+ fi
+ break 2
+ fi
+ done
+ done
+ if test "$found" != yes; then
+ # deplib doesn't seem to be a libtool library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ else # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll="$l"
+ done
+ if test "X$ll" = "X$old_library" ; then # only static version available
+ found=no
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+ lib=$ladir/$old_library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ fi
+ ;; # -l
+ *.ltframework)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test "$pass" = conv && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ prog)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test "$pass" = scan; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ *)
+ func_warning "\`-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test "$pass" = link; then
+ func_stripname '-R' '' "$deplib"
+ dir=$func_stripname_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la) lib="$deplib" ;;
+ *.$libext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=no
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=yes
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=yes
+ ;;
+ esac
+ if test "$valid_a_lib" != yes; then
+ $ECHO
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because the file extensions .$libext of this argument makes me believe"
+ $ECHO "*** that it is just a static archive that I should not use here."
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test "$pass" != link; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ elif test "$linkmode" = prog; then
+ if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ newdlprefiles="$newdlprefiles $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ newdlfiles="$newdlfiles $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=yes
+ continue
+ ;;
+ esac # case $deplib
+
+ if test "$found" = yes || test -f "$lib"; then :
+ else
+ func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+ fi
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan" ||
+ { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+ test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+ test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+ fi
+
+ if test "$pass" = conv; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ convenience="$convenience $ladir/$objdir/$old_library"
+ old_convenience="$old_convenience $ladir/$objdir/$old_library"
+ elif test "$linkmode" != prog && test "$linkmode" != lib; then
+ func_fatal_error "\`$lib' is not a convenience library"
+ fi
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ for l in $old_library $library_names; do
+ linklib="$l"
+ done
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test "$pass" = dlopen; then
+ if test -z "$libdir"; then
+ func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+ fi
+ if test -z "$dlname" ||
+ test "$dlopen_support" != yes ||
+ test "$build_libtool_libs" = no; then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ dlprefiles="$dlprefiles $lib $dependency_libs"
+ else
+ newdlfiles="$newdlfiles $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of \`$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir="$ladir"
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname="$func_basename_result"
+
+ # Find the relevant object directory and library name.
+ if test "X$installed" = Xyes; then
+ if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library \`$lib' was moved."
+ dir="$ladir"
+ absdir="$abs_ladir"
+ libdir="$abs_ladir"
+ else
+ dir="$libdir"
+ absdir="$libdir"
+ fi
+ test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir="$ladir"
+ absdir="$abs_ladir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ else
+ dir="$ladir/$objdir"
+ absdir="$abs_ladir/$objdir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test "$pass" = dlpreopen; then
+ if test -z "$libdir" && test "$linkmode" = prog; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+ fi
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ newdlprefiles="$newdlprefiles $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ newdlprefiles="$newdlprefiles $dir/$dlname"
+ else
+ newdlprefiles="$newdlprefiles $dir/$linklib"
+ fi
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test "$linkmode" = lib; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test "$linkmode" = prog && test "$pass" != link; then
+ newlib_search_path="$newlib_search_path $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=no
+ if test "$link_all_deplibs" != no || test -z "$library_names" ||
+ test "$build_libtool_libs" = no; then
+ linkalldeplibs=yes
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if test "$linkalldeplibs" = yes; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test "$linkmode,$pass" = "prog,link"; then
+ if test -n "$library_names" &&
+ { { test "$prefer_static_libs" = no ||
+ test "$prefer_static_libs,$installed" = "built,yes"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+ # Make sure the rpath contains only unique directories.
+ case "$temp_rpath:" in
+ *"$absdir:"*) ;;
+ *) temp_rpath="$temp_rpath$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if test "$alldeplibs" = yes &&
+ { test "$deplibs_check_method" = pass_all ||
+ { test "$build_libtool_libs" = yes &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test "$use_static_libs" = built && test "$installed" = yes; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test "$use_static_libs" = no || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw*)
+ # No point in relinking DLLs because paths are not encoded
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=no
+ ;;
+ *)
+ if test "$installed" = no; then
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=""
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule="$dlpremoduletest"
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+ $ECHO
+ if test "$linkmode" = prog; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test "$linkmode" = lib &&
+ test "$hardcode_into_libs" = yes; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname="$dlname"
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot="$soname"
+ func_basename "$soroot"
+ soname="$func_basename_result"
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from \`$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for \`$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test "$linkmode" = prog || test "$mode" != relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test "$hardcode_direct" = no; then
+ add="$dir/$linklib"
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+ *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir="-L$dir" ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we can not
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null ; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library" ; then
+ $ECHO
+ $ECHO "*** And there doesn't seem to be a static archive available"
+ $ECHO "*** The link will probably fail, sorry"
+ else
+ add="$dir/$old_library"
+ fi
+ elif test -n "$old_library"; then
+ add="$dir/$old_library"
+ fi
+ fi
+ esac
+ elif test "$hardcode_minus_L" = no; then
+ case $host in
+ *-*-sunos*) add_shlibpath="$dir" ;;
+ esac
+ add_dir="-L$dir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = no; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$dir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$dir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test "$lib_linked" != yes; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+ esac
+ fi
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test "$hardcode_direct" != yes &&
+ test "$hardcode_minus_L" != yes &&
+ test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test "$linkmode" = prog || test "$mode" = relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$libdir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$libdir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ add="-l$name"
+ elif test "$hardcode_automatic" = yes; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib" ; then
+ add="$inst_prefix_dir$libdir/$linklib"
+ else
+ add="$libdir/$linklib"
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir="-L$libdir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ fi
+
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test "$linkmode" = prog; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test "$hardcode_direct" != unsupported; then
+ test -n "$old_library" && linklib="$old_library"
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test "$build_libtool_libs" = yes; then
+ # Not a shared library
+ if test "$deplibs_check_method" != pass_all; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ $ECHO
+ $ECHO "*** Warning: This system can not link to static lib archive $lib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have."
+ if test "$module" = yes; then
+ $ECHO "*** But as you try to build a module library, libtool will still create "
+ $ECHO "*** a static module, that should work as long as the dlopening application"
+ $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test "$linkmode" = lib; then
+ if test -n "$dependency_libs" &&
+ { test "$hardcode_into_libs" != yes ||
+ test "$build_old_libs" = yes ||
+ test "$link_static" = yes; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) xrpath="$xrpath $temp_xrpath";;
+ esac;;
+ *) temp_deplibs="$temp_deplibs $libdir";;
+ esac
+ done
+ dependency_libs="$temp_deplibs"
+ fi
+
+ newlib_search_path="$newlib_search_path $absdir"
+ # Link against this library
+ test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+
+ if test "$link_all_deplibs" != no; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) path="$deplib" ;;
+ *.la)
+ func_dirname "$deplib" "" "."
+ dir="$func_dirname_result"
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of \`$dir'"
+ absdir="$dir"
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names" ; then
+ for tmp in $deplibrary_names ; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl" ; then
+ depdepl="$absdir/$objdir/$depdepl"
+ darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+ linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path="-L$absdir/$objdir"
+ ;;
+ esac
+ else
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "\`$deplib' seems to be moved"
+
+ path="-L$absdir"
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test "$pass" = link; then
+ if test "$linkmode" = "prog"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs="$newdependency_libs"
+ if test "$pass" = dlpreopen; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test "$pass" != dlopen; then
+ if test "$pass" != conv; then
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) lib_search_path="$lib_search_path $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ fi
+
+ if test "$linkmode,$pass" != "prog,link"; then
+ vars="deplibs"
+ else
+ vars="compile_deplibs finalize_deplibs"
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs ; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=""
+ ;;
+ esac
+ if test -n "$i" ; then
+ tmp_libs="$tmp_libs $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test "$linkmode" = prog; then
+ dlfiles="$newdlfiles"
+ fi
+ if test "$linkmode" = prog || test "$linkmode" = lib; then
+ dlprefiles="$newdlprefiles"
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "\`-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs="$output"
+ objs="$objs$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form `libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test "$module" = no && \
+ func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+ if test "$need_lib_prefix" != no; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test "$deplibs_check_method" != pass_all; then
+ func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ libobjs="$libobjs $objs"
+ fi
+ fi
+
+ test "$dlself" != no && \
+ func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test "$#" -gt 1 && \
+ func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+ install_libdir="$1"
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test "$build_libtool_libs" = yes; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a `.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs="$IFS"; IFS=':'
+ set dummy $vinfo 0 0 0
+ shift
+ IFS="$save_ifs"
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to \`-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major="$1"
+ number_minor="$2"
+ number_revision="$3"
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # which has an extra 1 added just for fun
+ #
+ case $version_type in
+ darwin|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_revision"
+ ;;
+ freebsd-aout|freebsd-elf|sunos)
+ current="$number_major"
+ revision="$number_minor"
+ age="0"
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_minor"
+ lt_irix_increment=no
+ ;;
+ esac
+ ;;
+ no)
+ current="$1"
+ revision="$2"
+ age="$3"
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT \`$current' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION \`$revision' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE \`$age' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE \`$age' is greater than the current interface number \`$current'"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+
+ freebsd-aout)
+ major=".$current"
+ versuffix=".$current.$revision";
+ ;;
+
+ freebsd-elf)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ irix | nonstopux)
+ if test "X$lt_irix_increment" = "Xno"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring="$verstring_prefix$major.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test "$loop" -ne 0; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring_prefix$major.$iface:$verstring"
+ done
+
+ # Before this point, $major must not contain `.'.
+ major=.$major
+ versuffix="$major.$revision"
+ ;;
+
+ linux)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=".$current.$age.$revision"
+ verstring="$current.$age.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test "$loop" -ne 0; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring:${iface}.0"
+ done
+
+ # Make executables depend on our current version.
+ verstring="$verstring:${current}.0"
+ ;;
+
+ qnx)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ sunos)
+ major=".$current"
+ versuffix=".$current.$revision"
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 filesystems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type \`$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring="0.0"
+ ;;
+ esac
+ if test "$need_version" = no; then
+ versuffix=
+ else
+ versuffix=".0.0"
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test "$avoid_version" = yes && test "$need_version" = no; then
+ major=
+ versuffix=
+ verstring=""
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test "$allow_undefined" = yes; then
+ if test "$allow_undefined_flag" = unsupported; then
+ func_warning "undefined symbols not allowed in $host shared libraries"
+ build_libtool_libs=no
+ build_old_libs=yes
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag="$no_undefined_flag"
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" "yes"
+ libobjs="$libobjs $symfileobj"
+ test "X$libobjs" = "X " && libobjs=
+
+ if test "$mode" != relink; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+ if test "X$precious_files_regex" != "X"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ removelist="$removelist $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+ oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"`
+ # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ temp_xrpath="$temp_xrpath -R$libdir"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles="$dlfiles"
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) dlfiles="$dlfiles $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles="$dlprefiles"
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) dlprefiles="$dlprefiles $lib" ;;
+ esac
+ done
+
+ if test "$build_libtool_libs" = yes; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ deplibs="$deplibs System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test "$build_libtool_need_lc" = "yes"; then
+ deplibs="$deplibs -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=""
+ versuffix=""
+ major=""
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which I believe you do not have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use it for"
+ $ECHO "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use this one"
+ $ECHO "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ $ECHO "*** make it link in! You will probably need to install it or some"
+ $ECHO "*** library that it depends on before this library will be fully"
+ $ECHO "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib="$potent_lib"
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+ *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib="$potent_lib" # see symlink-check above in file_magic test
+ if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=""
+ tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \
+ -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ for i in $predeps $postdeps ; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"`
+ done
+ fi
+ if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' |
+ $GREP . >/dev/null; then
+ $ECHO
+ if test "X$deplibs_check_method" = "Xnone"; then
+ $ECHO "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ $ECHO "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ $ECHO "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ fi
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test "$droppeddeps" = yes; then
+ if test "$module" = yes; then
+ $ECHO
+ $ECHO "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ $ECHO "*** a static module, that should work as long as the dlopening"
+ $ECHO "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ $ECHO "*** The inter-library dependencies that have been dropped here will be"
+ $ECHO "*** automatically added whenever a program is linked with this library"
+ $ECHO "*** or is declared to -dlopen it."
+
+ if test "$allow_undefined" = no; then
+ $ECHO
+ $ECHO "*** Since this library must not contain undefined symbols,"
+ $ECHO "*** because either the platform does not support them or"
+ $ECHO "*** it was explicitly requested with -no-undefined,"
+ $ECHO "*** libtool will only create a static version of it."
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ deplibs="$new_libs"
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test "$build_libtool_libs" = yes; then
+ if test "$hardcode_into_libs" = yes; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath="$finalize_rpath"
+ test "$mode" != relink && rpath="$compile_rpath$rpath"
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ dep_rpath="$dep_rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ if test -n "$hardcode_libdir_flag_spec_ld"; then
+ eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
+ else
+ eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+ fi
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath="$finalize_shlibpath"
+ test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib="$output_objdir/$realname"
+ linknames=
+ for link
+ do
+ linknames="$linknames $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols="$output_objdir/$libname.uexp"
+ delfiles="$delfiles $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols="$export_symbols"
+ export_symbols=
+ always_export_symbols=yes
+ fi
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ func_len " $cmd"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS="$save_ifs"
+ if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ tmp_deplibs="$tmp_deplibs $test_deplib"
+ ;;
+ esac
+ done
+ deplibs="$tmp_deplibs"
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test "$compiler_needs_object" = yes &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ linker_flags="$linker_flags $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test "X$skipped_export" != "X:" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ output_la=`$ECHO "X$output" | $Xsed -e "$basename"`
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+ output=${output_objdir}/${output_la}.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ $ECHO 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ $ECHO "$obj" >> $output
+ done
+ $ECHO ')' >> $output
+ delfiles="$delfiles $output"
+ elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+ output=${output_objdir}/${output_la}.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test "$compiler_needs_object" = yes; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ $ECHO "$obj" >> $output
+ done
+ delfiles="$delfiles $output"
+ output=$firstobj\"$file_list_spec$output\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-${k}.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test "X$objlist" = X ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test "$k" -eq 1 ; then
+ # The first file doesn't have a previous command to add.
+ eval concat_cmds=\"$reload_cmds $objlist $last_robj\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-${k}.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-${k}.$objext
+ objlist=$obj
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+ fi
+ delfiles="$delfiles $output"
+
+ else
+ output=
+ fi
+
+ if ${skipped_export-false}; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ fi
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS="$save_ifs"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ if ${skipped_export-false}; then
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ fi
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test "$module" = yes || test "$export_dynamic" = yes; then
+ # On all known operating systems, these are identical.
+ dlname="$soname"
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj="$output"
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # reload_cmds runs $LD directly, so let us get rid of
+ # -Wl from whole_archive_flag_spec and hope we can get by with
+ # turning comma into space..
+ wl=
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
+ else
+ gentop="$output_objdir/${obj}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # Create the old-style object.
+ reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+ output="$obj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$build_libtool_libs" != yes; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ fi
+
+ if test -n "$pic_flag" || test "$pic_mode" != default; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output="$libobj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for programs"
+
+ test "$preload" = yes \
+ && test "$dlopen_support" = unknown \
+ && test "$dlopen_self" = unknown \
+ && test "$dlopen_self_static" = unknown && \
+ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test "$tagname" = CXX ; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ compile_command="$compile_command ${wl}-bind_at_load"
+ finalize_command="$finalize_command ${wl}-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ compile_deplibs="$new_libs"
+
+
+ compile_command="$compile_command $compile_deplibs"
+ finalize_command="$finalize_command $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+ testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ *) dllsearchpath="$dllsearchpath:$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath="$rpath"
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath="$rpath"
+
+ if test -n "$libobjs" && test "$build_old_libs" = yes; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=yes
+ case $host in
+ *cygwin* | *mingw* )
+ if test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ *)
+ if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ esac
+ if test "$wrappers_required" = no; then
+ # Replace the output file specification.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ link_command="$compile_command$compile_rpath"
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.${objext}"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+ fi
+
+ exit $exit_status
+ fi
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test "$no_install" = yes; then
+ # We don't need to create a wrapper script.
+ link_command="$compile_var$compile_command$compile_rpath"
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$hardcode_action" = relink; then
+ # Fast installation is not supported
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "\`$output' will be relinked during installation"
+ else
+ if test "$fast_install" != no; then
+ link_command="$finalize_var$compile_command$finalize_rpath"
+ if test "$fast_install" = yes; then
+ relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+ else
+ # fast_install is set to needless
+ relink_command=
+ fi
+ else
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+ fi
+ fi
+
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Quote $ECHO for shipping.
+ if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then
+ case $progpath in
+ [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";;
+ *) qecho="$SHELL `pwd`/$progpath --fallback-echo";;
+ esac
+ qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"`
+ else
+ qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource="$output_path/$objdir/lt-$output_name.c"
+ cwrapper="$output_path/$output_name.exe"
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # we should really use a build-platform specific compiler
+ # here, but OTOH, the wrappers (shell script and this C one)
+ # are only useful if you want to execute the "real" binary.
+ # Since the "real" binary is built for $host, then this
+ # wrapper might as well be built for $host, too.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host" ; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ if test "$build_libtool_libs" = convenience; then
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs="$convenience"
+ build_libtool_libs=no
+ else
+ if test "$build_libtool_libs" = module; then
+ oldobjs="$libobjs_save"
+ build_libtool_libs=no
+ else
+ oldobjs="$old_deplibs $non_pic_objects"
+ if test "$preload" = yes && test -f "$symfileobj"; then
+ oldobjs="$oldobjs $symfileobj"
+ fi
+ fi
+ addlibs="$old_convenience"
+ fi
+
+ if test -n "$addlibs"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $addlibs
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ $ECHO "copying selected object files to avoid basename conflicts..."
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase="$func_basename_result"
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ oldobjs="$oldobjs $gentop/$newobj"
+ ;;
+ *) oldobjs="$oldobjs $obj" ;;
+ esac
+ done
+ fi
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj" ; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test "X$oldobjs" = "X" ; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test "$build_old_libs" = yes && old_library="$libname.$libext"
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ if test "$hardcode_automatic" = yes ; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test "$installed" = yes; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output="$output_objdir/$outputname"i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ if test "x$EGREP" = x ; then
+ EGREP=egrep
+ fi
+ # We do not want portage's install root ($D) present. Check only for
+ # this if the .la is being installed.
+ if test "$installed" = yes && test "$D"; then
+ eval mynewdependency_lib=`echo "$libdir/$name" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ else
+ mynewdependency_lib="$libdir/$name"
+ fi
+ # Do not add duplicates
+ if test "$mynewdependency_lib"; then
+ my_little_ninja_foo_1=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
+ if test -z "$my_little_ninja_foo_1"; then
+ newdependency_libs="$newdependency_libs $mynewdependency_lib"
+ fi
+ fi
+ ;;
+ *)
+ if test "$installed" = yes; then
+ # Rather use S=WORKDIR if our version of portage supports it.
+ # This is because some ebuild (gcc) do not use $S as buildroot.
+ if test "$PWORKDIR"; then
+ S="$PWORKDIR"
+ fi
+ # We do not want portage's build root ($S) present.
+ my_little_ninja_foo_2=`echo $deplib |$EGREP -e "$S"`
+ # We do not want portage's install root ($D) present.
+ my_little_ninja_foo_3=`echo $deplib |$EGREP -e "$D"`
+ if test -n "$my_little_ninja_foo_2" && test "$S"; then
+ mynewdependency_lib=""
+ elif test -n "$my_little_ninja_foo_3" && test "$D"; then
+ eval mynewdependency_lib=`echo "$deplib" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ else
+ mynewdependency_lib="$deplib"
+ fi
+ else
+ mynewdependency_lib="$deplib"
+ fi
+ # Do not add duplicates
+ if test "$mynewdependency_lib"; then
+ my_little_ninja_foo_4=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
+ if test -z "$my_little_ninja_foo_4"; then
+ newdependency_libs="$newdependency_libs $mynewdependency_lib"
+ fi
+ fi
+ ;;
+ esac
+ done
+ dependency_libs="$newdependency_libs"
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlfiles="$newdlfiles $libdir/$name"
+ ;;
+ *) newdlfiles="$newdlfiles $lib" ;;
+ esac
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlprefiles="$newdlprefiles $libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles="$newdlprefiles"
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlfiles="$newdlfiles $abs"
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlprefiles="$newdlprefiles $abs"
+ done
+ dlprefiles="$newdlprefiles"
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+ esac
+ # Do not add duplicates
+ if test "$installed" = yes && test "$D"; then
+ install_libdir=`echo "$install_libdir" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
+ fi
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test "$installed" = no && test "$need_relink" = yes; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+{ test "$mode" = link || test "$mode" = relink; } &&
+ func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $opt_debug
+ RM="$nonopt"
+ files=
+ rmforce=
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ for arg
+ do
+ case $arg in
+ -f) RM="$RM $arg"; rmforce=yes ;;
+ -*) RM="$RM $arg" ;;
+ *) files="$files $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ origobjdir="$objdir"
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ if test "X$dir" = X.; then
+ objdir="$origobjdir"
+ else
+ objdir="$dir/$origobjdir"
+ fi
+ func_basename "$file"
+ name="$func_basename_result"
+ test "$mode" = uninstall && objdir="$dir"
+
+ # Remember objdir for removal later, being careful to avoid duplicates
+ if test "$mode" = clean; then
+ case " $rmdirs " in
+ *" $objdir "*) ;;
+ *) rmdirs="$rmdirs $objdir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif test "$rmforce" = yes; then
+ continue
+ fi
+
+ rmfiles="$file"
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ rmfiles="$rmfiles $objdir/$n"
+ done
+ test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+
+ case "$mode" in
+ clean)
+ case " $library_names " in
+ # " " in the beginning catches empty $dlname
+ *" $dlname "*) ;;
+ *) rmfiles="$rmfiles $objdir/$dlname" ;;
+ esac
+ test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" &&
+ test "$pic_object" != none; then
+ rmfiles="$rmfiles $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" &&
+ test "$non_pic_object" != none; then
+ rmfiles="$rmfiles $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test "$mode" = clean ; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ rmfiles="$rmfiles $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ rmfiles="$rmfiles $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+ if test "$fast_install" = yes && test -n "$relink_command"; then
+ rmfiles="$rmfiles $objdir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name" ; then
+ rmfiles="$rmfiles $objdir/lt-${noexename}.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+ objdir="$origobjdir"
+
+ # Try to remove the ${objdir}s in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+{ test "$mode" = uninstall || test "$mode" = clean; } &&
+ func_mode_uninstall ${1+"$@"}
+
+test -z "$mode" && {
+ help="$generic_help"
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode \`$mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/third_party/sqlite/main.mk b/third_party/sqlite/main.mk
new file mode 100755
index 0000000..a952432
--- /dev/null
+++ b/third_party/sqlite/main.mk
@@ -0,0 +1,547 @@
+###############################################################################
+# The following macros should be defined before this script is
+# invoked:
+#
+# TOP The toplevel directory of the source tree. This is the
+# directory that contains this "Makefile.in" and the
+# "configure.in" script.
+#
+# BCC C Compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+# THREADLIB Specify any extra linker options needed to make the library
+# thread safe
+#
+# OPTS Extra compiler command-line options.
+#
+# EXE The suffix to add to executable files. ".exe" for windows
+# and "" for Unix.
+#
+# TCC C Compiler and options for use in building executables that
+# will run on the target platform. This is usually the same
+# as BCC, unless you are cross-compiling.
+#
+# AR Tools used to build a static library.
+# RANLIB
+#
+# TCL_FLAGS Extra compiler options needed for programs that use the
+# TCL library.
+#
+# LIBTCL Linker options needed to link against the TCL library.
+#
+# READLINE_FLAGS Compiler options needed for programs that use the
+# readline() library.
+#
+# LIBREADLINE Linker options needed by programs using readline() must
+# link against.
+#
+# NAWK Nawk compatible awk program. Older (obsolete?) solaris
+# systems need this to avoid using the original AT&T AWK.
+#
+# Once the macros above are defined, the rest of this make script will
+# build the SQLite library and testing tools.
+################################################################################
+
+# This is how we compile
+#
+TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
+
+# Object files for the SQLite library.
+#
+LIBOBJ+= alter.o analyze.o attach.o auth.o bitvec.o btmutex.o btree.o build.o \
+ callback.o complete.o date.o delete.o \
+ expr.o fault.o func.o global.o hash.o insert.o journal.o loadext.o \
+ main.o malloc.o mem1.o mem2.o mem3.o mem4.o mem5.o mem6.o \
+ mutex.o mutex_os2.o mutex_unix.o mutex_w32.o \
+ opcodes.o os.o os_os2.o os_unix.o os_win.o \
+ pager.o parse.o pragma.o prepare.o printf.o random.o \
+ select.o status.o table.o $(TCLOBJ) tokenize.o trigger.o \
+ update.o util.o vacuum.o \
+ vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
+ where.o utf.o legacy.o vtab.o rtree.o icu.o
+
+EXTOBJ = icu.o
+LIBOBJ += fts1.o \
+ fts1_hash.o \
+ fts1_tokenizer1.o \
+ fts1_porter.o
+LIBOBJ += fts2.o \
+ fts2_hash.o \
+ fts2_icu.o \
+ fts2_porter.o \
+ fts2_tokenizer.o \
+ fts2_tokenizer1.o
+EXTOBJ += fts3.o \
+ fts3_hash.o \
+ fts3_icu.o \
+ fts3_porter.o \
+ fts3_tokenizer.o \
+ fts3_tokenizer1.o
+EXTOBJ += rtree.o
+
+# All of the source code files.
+#
+SRC = \
+ $(TOP)/src/alter.c \
+ $(TOP)/src/analyze.c \
+ $(TOP)/src/attach.c \
+ $(TOP)/src/auth.c \
+ $(TOP)/src/bitvec.c \
+ $(TOP)/src/btmutex.c \
+ $(TOP)/src/btree.c \
+ $(TOP)/src/btree.h \
+ $(TOP)/src/btreeInt.h \
+ $(TOP)/src/build.c \
+ $(TOP)/src/callback.c \
+ $(TOP)/src/complete.c \
+ $(TOP)/src/date.c \
+ $(TOP)/src/delete.c \
+ $(TOP)/src/expr.c \
+ $(TOP)/src/fault.c \
+ $(TOP)/src/func.c \
+ $(TOP)/src/global.c \
+ $(TOP)/src/hash.c \
+ $(TOP)/src/hash.h \
+ $(TOP)/src/hwtime.h \
+ $(TOP)/src/insert.c \
+ $(TOP)/src/journal.c \
+ $(TOP)/src/legacy.c \
+ $(TOP)/src/loadext.c \
+ $(TOP)/src/main.c \
+ $(TOP)/src/malloc.c \
+ $(TOP)/src/mem1.c \
+ $(TOP)/src/mem2.c \
+ $(TOP)/src/mem3.c \
+ $(TOP)/src/mem4.c \
+ $(TOP)/src/mem5.c \
+ $(TOP)/src/mem6.c \
+ $(TOP)/src/mutex.c \
+ $(TOP)/src/mutex.h \
+ $(TOP)/src/mutex_os2.c \
+ $(TOP)/src/mutex_unix.c \
+ $(TOP)/src/mutex_w32.c \
+ $(TOP)/src/os.c \
+ $(TOP)/src/os.h \
+ $(TOP)/src/os_common.h \
+ $(TOP)/src/os_os2.c \
+ $(TOP)/src/os_unix.c \
+ $(TOP)/src/os_win.c \
+ $(TOP)/src/pager.c \
+ $(TOP)/src/pager.h \
+ $(TOP)/src/parse.y \
+ $(TOP)/src/pragma.c \
+ $(TOP)/src/prepare.c \
+ $(TOP)/src/printf.c \
+ $(TOP)/src/random.c \
+ $(TOP)/src/select.c \
+ $(TOP)/src/status.c \
+ $(TOP)/src/shell.c \
+ $(TOP)/src/sqlite.h.in \
+ $(TOP)/src/sqlite3ext.h \
+ $(TOP)/src/sqliteInt.h \
+ $(TOP)/src/sqliteLimit.h \
+ $(TOP)/src/table.c \
+ $(TOP)/src/tclsqlite.c \
+ $(TOP)/src/tokenize.c \
+ $(TOP)/src/trigger.c \
+ $(TOP)/src/utf.c \
+ $(TOP)/src/update.c \
+ $(TOP)/src/util.c \
+ $(TOP)/src/vacuum.c \
+ $(TOP)/src/vdbe.c \
+ $(TOP)/src/vdbe.h \
+ $(TOP)/src/vdbeapi.c \
+ $(TOP)/src/vdbeaux.c \
+ $(TOP)/src/vdbeblob.c \
+ $(TOP)/src/vdbefifo.c \
+ $(TOP)/src/vdbemem.c \
+ $(TOP)/src/vdbeInt.h \
+ $(TOP)/src/vtab.c \
+ $(TOP)/src/where.c
+
+# Source code for extensions
+#
+SRC += \
+ $(TOP)/ext/fts1/fts1.c \
+ $(TOP)/ext/fts1/fts1.h \
+ $(TOP)/ext/fts1/fts1_hash.c \
+ $(TOP)/ext/fts1/fts1_hash.h \
+ $(TOP)/ext/fts1/fts1_porter.c \
+ $(TOP)/ext/fts1/fts1_tokenizer.h \
+ $(TOP)/ext/fts1/fts1_tokenizer1.c
+SRC += \
+ $(TOP)/ext/fts2/fts2.c \
+ $(TOP)/ext/fts2/fts2.h \
+ $(TOP)/ext/fts2/fts2_hash.c \
+ $(TOP)/ext/fts2/fts2_hash.h \
+ $(TOP)/ext/fts2/fts2_icu.c \
+ $(TOP)/ext/fts2/fts2_porter.c \
+ $(TOP)/ext/fts2/fts2_tokenizer.h \
+ $(TOP)/ext/fts2/fts2_tokenizer.c \
+ $(TOP)/ext/fts2/fts2_tokenizer1.c
+SRC += \
+ $(TOP)/ext/fts3/fts3.c \
+ $(TOP)/ext/fts3/fts3.h \
+ $(TOP)/ext/fts3/fts3_hash.c \
+ $(TOP)/ext/fts3/fts3_hash.h \
+ $(TOP)/ext/fts3/fts3_icu.c \
+ $(TOP)/ext/fts3/fts3_porter.c \
+ $(TOP)/ext/fts3/fts3_tokenizer.h \
+ $(TOP)/ext/fts3/fts3_tokenizer.c \
+ $(TOP)/ext/fts3/fts3_tokenizer1.c
+SRC += \
+ $(TOP)/ext/icu/icu.c
+SRC += \
+ $(TOP)/ext/rtree/rtree.h \
+ $(TOP)/ext/rtree/rtree.c
+
+
+# Generated source code files
+#
+SRC += \
+ keywordhash.h \
+ opcodes.c \
+ opcodes.h \
+ parse.c \
+ parse.h \
+ sqlite3.h
+
+
+# Source code to the test files.
+#
+TESTSRC = \
+ $(TOP)/src/test1.c \
+ $(TOP)/src/test2.c \
+ $(TOP)/src/test3.c \
+ $(TOP)/src/test4.c \
+ $(TOP)/src/test5.c \
+ $(TOP)/src/test6.c \
+ $(TOP)/src/test7.c \
+ $(TOP)/src/test8.c \
+ $(TOP)/src/test9.c \
+ $(TOP)/src/test_autoext.c \
+ $(TOP)/src/test_async.c \
+ $(TOP)/src/test_btree.c \
+ $(TOP)/src/test_config.c \
+ $(TOP)/src/test_devsym.c \
+ $(TOP)/src/test_func.c \
+ $(TOP)/src/test_hexio.c \
+ $(TOP)/src/test_malloc.c \
+ $(TOP)/src/test_md5.c \
+ $(TOP)/src/test_mutex.c \
+ $(TOP)/src/test_onefile.c \
+ $(TOP)/src/test_osinst.c \
+ $(TOP)/src/test_schema.c \
+ $(TOP)/src/test_server.c \
+ $(TOP)/src/test_tclvar.c \
+ $(TOP)/src/test_thread.c \
+
+TESTSRC += \
+ $(TOP)/ext/fts1/fts1.c \
+ $(TOP)/ext/fts1/fts1.h \
+ $(TOP)/ext/fts1/fts1_hash.c \
+ $(TOP)/ext/fts1/fts1_hash.h \
+ $(TOP)/ext/fts1/fts1_porter.c \
+ $(TOP)/ext/fts1/fts1_tokenizer.h \
+ $(TOP)/ext/fts1/fts1_tokenizer1.c
+TESTSRC += \
+ $(TOP)/ext/fts2/fts2.c \
+ $(TOP)/ext/fts2/fts2.h \
+ $(TOP)/ext/fts2/fts2_hash.c \
+ $(TOP)/ext/fts2/fts2_hash.h \
+ $(TOP)/ext/fts2/fts2_icu.c \
+ $(TOP)/ext/fts2/fts2_porter.c \
+ $(TOP)/ext/fts2/fts2_tokenizer.h \
+ $(TOP)/ext/fts2/fts2_tokenizer.c \
+ $(TOP)/ext/fts2/fts2_tokenizer1.c
+
+#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
+#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c
+
+TESTSRC2 = \
+ $(TOP)/src/attach.c $(TOP)/src/btree.c $(TOP)/src/build.c $(TOP)/src/date.c \
+ $(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \
+ $(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
+ $(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
+ $(TOP)/src/printf.c $(TOP)/src/random.c \
+ $(TOP)/src/select.c $(TOP)/src/tokenize.c \
+ $(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
+ $(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c
+
+# Header files used by all library source files.
+#
+HDR = \
+ $(TOP)/src/btree.h \
+ $(TOP)/src/btreeInt.h \
+ $(TOP)/src/hash.h \
+ $(TOP)/src/hwtime.h \
+ keywordhash.h \
+ $(TOP)/src/mutex.h \
+ opcodes.h \
+ $(TOP)/src/os.h \
+ $(TOP)/src/os_common.h \
+ $(TOP)/src/pager.h \
+ parse.h \
+ sqlite3.h \
+ $(TOP)/src/sqlite3ext.h \
+ $(TOP)/src/sqliteInt.h \
+ $(TOP)/src/sqliteLimit.h \
+ $(TOP)/src/vdbe.h \
+ $(TOP)/src/vdbeInt.h
+
+# Header files used by extensions
+#
+EXTHDR += \
+ $(TOP)/ext/fts1/fts1.h \
+ $(TOP)/ext/fts1/fts1_hash.h \
+ $(TOP)/ext/fts1/fts1_tokenizer.h
+EXTHDR += \
+ $(TOP)/ext/fts2/fts2.h \
+ $(TOP)/ext/fts2/fts2_hash.h \
+ $(TOP)/ext/fts2/fts2_tokenizer.h
+EXTHDR += \
+ $(TOP)/ext/fts3/fts3.h \
+ $(TOP)/ext/fts3/fts3_hash.h \
+ $(TOP)/ext/fts3/fts3_tokenizer.h
+EXTHDR += \
+ $(TOP)/ext/rtree/rtree.h
+
+# This is the default Makefile target. The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all: sqlite3.h libsqlite3.a sqlite3$(EXE)
+
+libsqlite3.a: $(LIBOBJ)
+ $(AR) libsqlite3.a $(LIBOBJ)
+ $(RANLIB) libsqlite3.a
+
+sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
+ $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \
+ $(TOP)/src/shell.c \
+ libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) -ldl
+
+objects: $(LIBOBJ_ORIG)
+
+# This target creates a directory named "tsrc" and fills it with
+# copies of all of the C source code and header files needed to
+# build on the target system. Some of the C source code and header
+# files are automatically generated. This target takes care of
+# all that automatic generation.
+#
+target_source: $(SRC)
+ rm -rf tsrc
+ mkdir tsrc
+ cp -f $(SRC) tsrc
+ rm tsrc/sqlite.h.in tsrc/parse.y
+ touch target_source
+
+sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
+ tclsh $(TOP)/tool/mksqlite3c.tcl
+ cp sqlite3.c tclsqlite3.c
+ cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
+
+fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl
+ tclsh $(TOP)/ext/fts2/mkfts2amal.tcl
+
+fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl
+ tclsh $(TOP)/ext/fts3/mkfts3amal.tcl
+
+# Rules to build the LEMON compiler generator
+#
+lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
+ $(BCC) -o lemon $(TOP)/tool/lemon.c
+ cp $(TOP)/tool/lempar.c .
+
+# Rules to build individual *.o files from generated *.c files. This
+# applies to:
+#
+# parse.o
+# opcodes.o
+#
+%.o: %.c $(HDR)
+ $(TCCX) -c $<
+
+# Rules to build individual *.o files from files in the src directory.
+#
+%.o: $(TOP)/src/%.c $(HDR)
+ $(TCCX) -c $<
+
+tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
+ $(TCCX) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c
+
+
+
+# Rules to build opcodes.c and opcodes.h
+#
+opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
+ sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
+
+opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
+ cat parse.h $(TOP)/src/vdbe.c |$(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
+
+
+# Rules to build parse.c and parse.h - the outputs of lemon.
+#
+parse.h: parse.c
+
+parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
+ cp $(TOP)/src/parse.y .
+ rm -f parse.h
+ ./lemon $(OPTS) parse.y
+ mv parse.h parse.h.temp
+ awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
+
+sqlite3.h: $(TOP)/src/sqlite.h.in
+ sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \
+ -e s/--VERSION-NUMBER--/`cat ${TOP}/VERSION | sed 's/[^0-9]/ /g' | $(NAWK) '{printf "%d%03d%03d",$$1,$$2,$$3}'`/ \
+ $(TOP)/src/sqlite.h.in >sqlite3.h
+
+keywordhash.h: $(TOP)/tool/mkkeywordhash.c
+ $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
+ ./mkkeywordhash >keywordhash.h
+
+
+
+# Rules to build the extension objects.
+#
+icu.o: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/icu/icu.c
+
+fts2.o: $(TOP)/ext/fts2/fts2.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2.c
+
+fts2_hash.o: $(TOP)/ext/fts2/fts2_hash.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_hash.c
+
+fts2_icu.o: $(TOP)/ext/fts2/fts2_icu.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_icu.c
+
+fts2_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_porter.c
+
+fts2_tokenizer.o: $(TOP)/ext/fts2/fts2_tokenizer.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c
+
+fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c
+
+fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
+
+fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c
+
+fts3_icu.o: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_icu.c
+
+fts3_porter.o: $(TOP)/ext/fts3/fts3_porter.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_porter.c
+
+fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer.c
+
+fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c
+
+rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
+
+
+
+
+fts1.o: $(TOP)/ext/fts1/fts1.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts1/fts1.c
+
+fts1_hash.o: $(TOP)/ext/fts1/fts1_hash.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts1/fts1_hash.c
+
+fts1_tokenizer1.o: $(TOP)/ext/fts1/fts1_tokenizer1.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts1/fts1_tokenizer1.c
+
+fts1_porter.o: $(TOP)/ext/fts1/fts1_porter.c $(HDR) $(EXTHDR)
+ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts1/fts1_porter.c
+
+# Rules for building test programs and for running tests
+#
+tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
+ $(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
+ $(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
+
+
+# Rules to build the 'testfixture' application.
+#
+TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
+TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
+
+testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
+ $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
+ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \
+ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite3.a
+
+amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c
+ $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
+ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
+ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
+
+fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c
+ $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
+ -DSQLITE_ENABLE_FTS3=1 \
+ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \
+ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
+
+fulltest: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/all.test
+
+soaktest: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/all.test -soak 1
+
+test: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/veryquick.test
+
+ftstest: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/fts.test
+
+fts1test: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/fts1.test
+
+fts2test: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/fts2.test
+
+sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
+ $(TOP)/tool/spaceanal.tcl
+ sed \
+ -e '/^#/d' \
+ -e 's,\\,\\\\,g' \
+ -e 's,",\\",g' \
+ -e 's,^,",' \
+ -e 's,$$,\\n",' \
+ $(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h
+ $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
+ -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_DEBUG=1 -DSQLITE_PRIVATE="" \
+ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
+ -o sqlite3_analyzer$(EXE) \
+ $(LIBTCL) $(THREADLIB)
+
+TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO)
+$(TEST_EXTENSION): $(TOP)/src/test_loadext.c
+ $(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION)
+
+extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
+ ./testfixture$(EXE) $(TOP)/test/loadext.test
+
+
+# Standard install and cleanup targets
+#
+install: sqlite3 libsqlite3.a sqlite3.h
+ mv sqlite3 /usr/bin
+ mv libsqlite3.a /usr/lib
+ mv sqlite3.h /usr/include
+
+clean:
+ rm -f *.o sqlite3 libsqlite3.a sqlite3.h opcodes.*
+ rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
+ rm -f $(PUBLISH)
+ rm -f *.da *.bb *.bbg gmon.out
+ rm -rf tsrc target_source
+ rm -f testloadext.dll libtestloadext.so
+ rm -f sqlite3.c fts?amal.c
diff --git a/third_party/sqlite/malloc.c b/third_party/sqlite/malloc.c
deleted file mode 100644
index 8a99332..0000000
--- a/third_party/sqlite/malloc.c
+++ /dev/null
@@ -1,835 +0,0 @@
-/*
-** 2001 September 15
-**
-** 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.
-**
-*************************************************************************
-** Memory allocation functions used throughout sqlite.
-**
-**
-** $Id: malloc.c,v 1.4 2007/08/08 01:04:52 drh Exp $
-*/
-#include "sqliteInt.h"
-#include "os.h"
-#include <stdarg.h>
-#include <ctype.h>
-
-/*
-** MALLOC WRAPPER ARCHITECTURE
-**
-** The sqlite code accesses dynamic memory allocation/deallocation by invoking
-** the following six APIs (which may be implemented as macros).
-**
-** sqlite3Malloc()
-** sqlite3MallocRaw()
-** sqlite3Realloc()
-** sqlite3ReallocOrFree()
-** sqlite3Free()
-** sqlite3AllocSize()
-**
-** The function sqlite3FreeX performs the same task as sqlite3Free and is
-** guaranteed to be a real function. The same holds for sqlite3MallocX
-**
-** The above APIs are implemented in terms of the functions provided in the
-** operating-system interface. The OS interface is never accessed directly
-** by code outside of this file.
-**
-** sqlite3OsMalloc()
-** sqlite3OsRealloc()
-** sqlite3OsFree()
-** sqlite3OsAllocationSize()
-**
-** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke
-** sqlite3_release_memory() if a call to sqlite3OsMalloc() or
-** sqlite3OsRealloc() fails (or if the soft-heap-limit for the thread is
-** exceeded). Function sqlite3Malloc() usually invokes
-** sqlite3MallocRaw().
-**
-** MALLOC TEST WRAPPER ARCHITECTURE
-**
-** The test wrapper provides extra test facilities to ensure the library
-** does not leak memory and handles the failure of the underlying OS level
-** allocation system correctly. It is only present if the library is
-** compiled with the SQLITE_MEMDEBUG macro set.
-**
-** * Guardposts to detect overwrites.
-** * Ability to cause a specific Malloc() or Realloc() to fail.
-** * Audit outstanding memory allocations (i.e check for leaks).
-*/
-
-#define MAX(x,y) ((x)>(y)?(x):(y))
-
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
-/*
-** Set the soft heap-size limit for the current thread. Passing a negative
-** value indicates no limit.
-*/
-void sqlite3_soft_heap_limit(int n){
- ThreadData *pTd = sqlite3ThreadData();
- if( pTd ){
- pTd->nSoftHeapLimit = n;
- }
- sqlite3ReleaseThreadData();
-}
-
-/*
-** Release memory held by SQLite instances created by the current thread.
-*/
-int sqlite3_release_memory(int n){
- return sqlite3PagerReleaseMemory(n);
-}
-#else
-/* If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, then define a version
-** of sqlite3_release_memory() to be used by other code in this file.
-** This is done for no better reason than to reduce the number of
-** pre-processor #ifndef statements.
-*/
-#define sqlite3_release_memory(x) 0 /* 0 == no memory freed */
-#endif
-
-#ifdef SQLITE_MEMDEBUG
-/*--------------------------------------------------------------------------
-** Begin code for memory allocation system test layer.
-**
-** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
-**
-** SQLITE_MEMDEBUG==1 -> Fence-posting only (thread safe)
-** SQLITE_MEMDEBUG==2 -> Fence-posting + linked list of allocations (not ts)
-** SQLITE_MEMDEBUG==3 -> Above + backtraces (not thread safe, req. glibc)
-*/
-
-/* Figure out whether or not to store backtrace() information for each malloc.
-** The backtrace() function is only used if SQLITE_MEMDEBUG is set to 2 or
-** greater and glibc is in use. If we don't want to use backtrace(), then just
-** define it as an empty macro and set the amount of space reserved to 0.
-*/
-#if defined(__GLIBC__) && SQLITE_MEMDEBUG>2
- extern int backtrace(void **, int);
- #define TESTALLOC_STACKSIZE 128
- #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*))
-#else
- #define backtrace(x, y)
- #define TESTALLOC_STACKSIZE 0
- #define TESTALLOC_STACKFRAMES 0
-#endif
-
-/*
-** Number of 32-bit guard words. This should probably be a multiple of
-** 2 since on 64-bit machines we want the value returned by sqliteMalloc()
-** to be 8-byte aligned.
-*/
-#ifndef TESTALLOC_NGUARD
-# define TESTALLOC_NGUARD 2
-#endif
-
-/*
-** Size reserved for storing file-name along with each malloc()ed blob.
-*/
-#define TESTALLOC_FILESIZE 64
-
-/*
-** Size reserved for storing the user string. Each time a Malloc() or Realloc()
-** call succeeds, up to TESTALLOC_USERSIZE bytes of the string pointed to by
-** sqlite3_malloc_id are stored along with the other test system metadata.
-*/
-#define TESTALLOC_USERSIZE 64
-const char *sqlite3_malloc_id = 0;
-
-/*
-** Blocks used by the test layer have the following format:
-**
-** <sizeof(void *) pNext pointer>
-** <sizeof(void *) pPrev pointer>
-** <TESTALLOC_NGUARD 32-bit guard words>
-** <The application level allocation>
-** <TESTALLOC_NGUARD 32-bit guard words>
-** <32-bit line number>
-** <TESTALLOC_FILESIZE bytes containing null-terminated file name>
-** <TESTALLOC_STACKSIZE bytes of backtrace() output>
-*/
-
-#define TESTALLOC_OFFSET_GUARD1(p) (sizeof(void *) * 2)
-#define TESTALLOC_OFFSET_DATA(p) ( \
- TESTALLOC_OFFSET_GUARD1(p) + sizeof(u32) * TESTALLOC_NGUARD \
-)
-#define TESTALLOC_OFFSET_GUARD2(p) ( \
- TESTALLOC_OFFSET_DATA(p) + sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD \
-)
-#define TESTALLOC_OFFSET_LINENUMBER(p) ( \
- TESTALLOC_OFFSET_GUARD2(p) + sizeof(u32) * TESTALLOC_NGUARD \
-)
-#define TESTALLOC_OFFSET_FILENAME(p) ( \
- TESTALLOC_OFFSET_LINENUMBER(p) + sizeof(u32) \
-)
-#define TESTALLOC_OFFSET_USER(p) ( \
- TESTALLOC_OFFSET_FILENAME(p) + TESTALLOC_FILESIZE \
-)
-#define TESTALLOC_OFFSET_STACK(p) ( \
- TESTALLOC_OFFSET_USER(p) + TESTALLOC_USERSIZE + 8 - \
- (TESTALLOC_OFFSET_USER(p) % 8) \
-)
-
-#define TESTALLOC_OVERHEAD ( \
- sizeof(void *)*2 + /* pPrev and pNext pointers */ \
- TESTALLOC_NGUARD*sizeof(u32)*2 + /* Guard words */ \
- sizeof(u32) + TESTALLOC_FILESIZE + /* File and line number */ \
- TESTALLOC_USERSIZE + /* User string */ \
- TESTALLOC_STACKSIZE /* backtrace() stack */ \
-)
-
-
-/*
-** For keeping track of the number of mallocs and frees. This
-** is used to check for memory leaks. The iMallocFail and iMallocReset
-** values are used to simulate malloc() failures during testing in
-** order to verify that the library correctly handles an out-of-memory
-** condition.
-*/
-int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
-int sqlite3_nFree; /* Number of sqliteFree() calls */
-int sqlite3_memUsed; /* TODO Total memory obtained from malloc */
-int sqlite3_memMax; /* TODO Mem usage high-water mark */
-int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
-int sqlite3_iMallocReset = -1; /* When iMallocFail reaches 0, set to this */
-
-void *sqlite3_pFirst = 0; /* Pointer to linked list of allocations */
-int sqlite3_nMaxAlloc = 0; /* High water mark of ThreadData.nAlloc */
-int sqlite3_mallocDisallowed = 0; /* assert() in sqlite3Malloc() if set */
-int sqlite3_isFail = 0; /* True if all malloc calls should fail */
-const char *sqlite3_zFile = 0; /* Filename to associate debug info with */
-int sqlite3_iLine = 0; /* Line number for debug info */
-int sqlite3_mallocfail_trace = 0; /* Print a msg on malloc fail if true */
-
-/*
-** Check for a simulated memory allocation failure. Return true if
-** the failure should be simulated. Return false to proceed as normal.
-*/
-int sqlite3TestMallocFail(){
- if( sqlite3_isFail ){
- return 1;
- }
- if( sqlite3_iMallocFail>=0 ){
- sqlite3_iMallocFail--;
- if( sqlite3_iMallocFail==0 ){
- sqlite3_iMallocFail = sqlite3_iMallocReset;
- sqlite3_isFail = 1;
- if( sqlite3_mallocfail_trace ){
- sqlite3DebugPrintf("###_malloc_fails_###\n");
- }
- return 1;
- }
- }
- return 0;
-}
-
-/*
-** The argument is a pointer returned by sqlite3OsMalloc() or xRealloc().
-** assert() that the first and last (TESTALLOC_NGUARD*4) bytes are set to the
-** values set by the applyGuards() function.
-*/
-static void checkGuards(u32 *p)
-{
- int i;
- char *zAlloc = (char *)p;
- char *z;
-
- /* First set of guard words */
- z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
- for(i=0; i<TESTALLOC_NGUARD; i++){
- assert(((u32 *)z)[i]==0xdead1122);
- }
-
- /* Second set of guard words */
- z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
- for(i=0; i<TESTALLOC_NGUARD; i++){
- u32 guard = 0;
- memcpy(&guard, &z[i*sizeof(u32)], sizeof(u32));
- assert(guard==0xdead3344);
- }
-}
-
-/*
-** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). The
-** first and last (TESTALLOC_NGUARD*4) bytes are set to known values for use as
-** guard-posts.
-*/
-static void applyGuards(u32 *p)
-{
- int i;
- char *z;
- char *zAlloc = (char *)p;
-
- /* First set of guard words */
- z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
- for(i=0; i<TESTALLOC_NGUARD; i++){
- ((u32 *)z)[i] = 0xdead1122;
- }
-
- /* Second set of guard words */
- z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
- for(i=0; i<TESTALLOC_NGUARD; i++){
- static const int guard = 0xdead3344;
- memcpy(&z[i*sizeof(u32)], &guard, sizeof(u32));
- }
-
- /* Line number */
- z = &((char *)z)[TESTALLOC_NGUARD*sizeof(u32)]; /* Guard words */
- z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
- memcpy(z, &sqlite3_iLine, sizeof(u32));
-
- /* File name */
- z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
- strncpy(z, sqlite3_zFile, TESTALLOC_FILESIZE);
- z[TESTALLOC_FILESIZE - 1] = '\0';
-
- /* User string */
- z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
- z[0] = 0;
- if( sqlite3_malloc_id ){
- strncpy(z, sqlite3_malloc_id, TESTALLOC_USERSIZE);
- z[TESTALLOC_USERSIZE-1] = 0;
- }
-
- /* backtrace() stack */
- z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
- backtrace((void **)z, TESTALLOC_STACKFRAMES);
-
- /* Sanity check to make sure checkGuards() is working */
- checkGuards(p);
-}
-
-/*
-** The argument is a malloc()ed pointer as returned by the test-wrapper.
-** Return a pointer to the Os level allocation.
-*/
-static void *getOsPointer(void *p)
-{
- char *z = (char *)p;
- return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]);
-}
-
-
-#if SQLITE_MEMDEBUG>1
-/*
-** The argument points to an Os level allocation. Link it into the threads list
-** of allocations.
-*/
-static void linkAlloc(void *p){
- void **pp = (void **)p;
- pp[0] = 0;
- pp[1] = sqlite3_pFirst;
- if( sqlite3_pFirst ){
- ((void **)sqlite3_pFirst)[0] = p;
- }
- sqlite3_pFirst = p;
-}
-
-/*
-** The argument points to an Os level allocation. Unlinke it from the threads
-** list of allocations.
-*/
-static void unlinkAlloc(void *p)
-{
- void **pp = (void **)p;
- if( p==sqlite3_pFirst ){
- assert(!pp[0]);
- assert(!pp[1] || ((void **)(pp[1]))[0]==p);
- sqlite3_pFirst = pp[1];
- if( sqlite3_pFirst ){
- ((void **)sqlite3_pFirst)[0] = 0;
- }
- }else{
- void **pprev = pp[0];
- void **pnext = pp[1];
- assert(pprev);
- assert(pprev[1]==p);
- pprev[1] = (void *)pnext;
- if( pnext ){
- assert(pnext[0]==p);
- pnext[0] = (void *)pprev;
- }
- }
-}
-
-/*
-** Pointer p is a pointer to an OS level allocation that has just been
-** realloc()ed. Set the list pointers that point to this entry to it's new
-** location.
-*/
-static void relinkAlloc(void *p)
-{
- void **pp = (void **)p;
- if( pp[0] ){
- ((void **)(pp[0]))[1] = p;
- }else{
- sqlite3_pFirst = p;
- }
- if( pp[1] ){
- ((void **)(pp[1]))[0] = p;
- }
-}
-#else
-#define linkAlloc(x)
-#define relinkAlloc(x)
-#define unlinkAlloc(x)
-#endif
-
-/*
-** This function sets the result of the Tcl interpreter passed as an argument
-** to a list containing an entry for each currently outstanding call made to
-** sqliteMalloc and friends by the current thread. Each list entry is itself a
-** list, consisting of the following (in order):
-**
-** * The number of bytes allocated
-** * The __FILE__ macro at the time of the sqliteMalloc() call.
-** * The __LINE__ macro ...
-** * The value of the sqlite3_malloc_id variable ...
-** * The output of backtrace() (if available) ...
-**
-** Todo: We could have a version of this function that outputs to stdout,
-** to debug memory leaks when Tcl is not available.
-*/
-#if defined(TCLSH) && defined(SQLITE_DEBUG) && SQLITE_MEMDEBUG>1
-#include <tcl.h>
-int sqlite3OutstandingMallocs(Tcl_Interp *interp){
- void *p;
- Tcl_Obj *pRes = Tcl_NewObj();
- Tcl_IncrRefCount(pRes);
-
-
- for(p=sqlite3_pFirst; p; p=((void **)p)[1]){
- Tcl_Obj *pEntry = Tcl_NewObj();
- Tcl_Obj *pStack = Tcl_NewObj();
- char *z;
- u32 iLine;
- int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD;
- char *zAlloc = (char *)p;
- int i;
-
- Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(nBytes));
-
- z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
- Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
-
- z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
- memcpy(&iLine, z, sizeof(u32));
- Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(iLine));
-
- z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
- Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
-
- z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
- for(i=0; i<TESTALLOC_STACKFRAMES; i++){
- char zHex[128];
- sqlite3_snprintf(sizeof(zHex), zHex, "%p", ((void **)z)[i]);
- Tcl_ListObjAppendElement(0, pStack, Tcl_NewStringObj(zHex, -1));
- }
-
- Tcl_ListObjAppendElement(0, pEntry, pStack);
- Tcl_ListObjAppendElement(0, pRes, pEntry);
- }
-
- Tcl_ResetResult(interp);
- Tcl_SetObjResult(interp, pRes);
- Tcl_DecrRefCount(pRes);
- return TCL_OK;
-}
-#endif
-
-/*
-** This is the test layer's wrapper around sqlite3OsMalloc().
-*/
-static void * OSMALLOC(int n){
- sqlite3OsEnterMutex();
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- sqlite3_nMaxAlloc =
- MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc);
-#endif
- assert( !sqlite3_mallocDisallowed );
- if( !sqlite3TestMallocFail() ){
- u32 *p;
- p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD);
- assert(p);
- sqlite3_nMalloc++;
- applyGuards(p);
- linkAlloc(p);
- sqlite3OsLeaveMutex();
- return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
- }
- sqlite3OsLeaveMutex();
- return 0;
-}
-
-static int OSSIZEOF(void *p){
- if( p ){
- u32 *pOs = (u32 *)getOsPointer(p);
- return sqlite3OsAllocationSize(pOs) - TESTALLOC_OVERHEAD;
- }
- return 0;
-}
-
-/*
-** This is the test layer's wrapper around sqlite3OsFree(). The argument is a
-** pointer to the space allocated for the application to use.
-*/
-static void OSFREE(void *pFree){
- u32 *p; /* Pointer to the OS-layer allocation */
- sqlite3OsEnterMutex();
- p = (u32 *)getOsPointer(pFree);
- checkGuards(p);
- unlinkAlloc(p);
- memset(pFree, 0x55, OSSIZEOF(pFree));
- sqlite3OsFree(p);
- sqlite3_nFree++;
- sqlite3OsLeaveMutex();
-}
-
-/*
-** This is the test layer's wrapper around sqlite3OsRealloc().
-*/
-static void * OSREALLOC(void *pRealloc, int n){
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- sqlite3_nMaxAlloc =
- MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc);
-#endif
- assert( !sqlite3_mallocDisallowed );
- if( !sqlite3TestMallocFail() ){
- u32 *p = (u32 *)getOsPointer(pRealloc);
- checkGuards(p);
- p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD);
- applyGuards(p);
- relinkAlloc(p);
- return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
- }
- return 0;
-}
-
-static void OSMALLOC_FAILED(){
- sqlite3_isFail = 0;
-}
-
-#else
-/* Define macros to call the sqlite3OsXXX interface directly if
-** the SQLITE_MEMDEBUG macro is not defined.
-*/
-#define OSMALLOC(x) sqlite3OsMalloc(x)
-#define OSREALLOC(x,y) sqlite3OsRealloc(x,y)
-#define OSFREE(x) sqlite3OsFree(x)
-#define OSSIZEOF(x) sqlite3OsAllocationSize(x)
-#define OSMALLOC_FAILED()
-
-#endif /* SQLITE_MEMDEBUG */
-/*
-** End code for memory allocation system test layer.
-**--------------------------------------------------------------------------*/
-
-/*
-** This routine is called when we are about to allocate n additional bytes
-** of memory. If the new allocation will put is over the soft allocation
-** limit, then invoke sqlite3_release_memory() to try to release some
-** memory before continuing with the allocation.
-**
-** This routine also makes sure that the thread-specific-data (TSD) has
-** be allocated. If it has not and can not be allocated, then return
-** false. The updateMemoryUsedCount() routine below will deallocate
-** the TSD if it ought to be.
-**
-** If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, this routine is
-** a no-op
-*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-static int enforceSoftLimit(int n){
- ThreadData *pTsd = sqlite3ThreadData();
- if( pTsd==0 ){
- return 0;
- }
- assert( pTsd->nAlloc>=0 );
- if( n>0 && pTsd->nSoftHeapLimit>0 ){
- while( pTsd->nAlloc+n>pTsd->nSoftHeapLimit && sqlite3_release_memory(n) ){}
- }
- return 1;
-}
-#else
-# define enforceSoftLimit(X) 1
-#endif
-
-/*
-** Update the count of total outstanding memory that is held in
-** thread-specific-data (TSD). If after this update the TSD is
-** no longer being used, then deallocate it.
-**
-** If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, this routine is
-** a no-op
-*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-static void updateMemoryUsedCount(int n){
- ThreadData *pTsd = sqlite3ThreadData();
- if( pTsd ){
- pTsd->nAlloc += n;
- assert( pTsd->nAlloc>=0 );
- if( pTsd->nAlloc==0 && pTsd->nSoftHeapLimit==0 ){
- sqlite3ReleaseThreadData();
- }
- }
-}
-#else
-#define updateMemoryUsedCount(x) /* no-op */
-#endif
-
-/*
-** Allocate and return N bytes of uninitialised memory by calling
-** sqlite3OsMalloc(). If the Malloc() call fails, attempt to free memory
-** by calling sqlite3_release_memory().
-*/
-void *sqlite3MallocRaw(int n, int doMemManage){
- void *p = 0;
- if( n>0 && !sqlite3MallocFailed() && (!doMemManage || enforceSoftLimit(n)) ){
- while( (p = OSMALLOC(n))==0 && sqlite3_release_memory(n) ){}
- if( !p ){
- sqlite3FailedMalloc();
- OSMALLOC_FAILED();
- }else if( doMemManage ){
- updateMemoryUsedCount(OSSIZEOF(p));
- }
- }
- return p;
-}
-
-/*
-** Resize the allocation at p to n bytes by calling sqlite3OsRealloc(). The
-** pointer to the new allocation is returned. If the Realloc() call fails,
-** attempt to free memory by calling sqlite3_release_memory().
-*/
-void *sqlite3Realloc(void *p, int n){
- if( sqlite3MallocFailed() ){
- return 0;
- }
-
- if( !p ){
- return sqlite3Malloc(n, 1);
- }else{
- void *np = 0;
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- int origSize = OSSIZEOF(p);
-#endif
- if( enforceSoftLimit(n - origSize) ){
- while( (np = OSREALLOC(p, n))==0 && sqlite3_release_memory(n) ){}
- if( !np ){
- sqlite3FailedMalloc();
- OSMALLOC_FAILED();
- }else{
- updateMemoryUsedCount(OSSIZEOF(np) - origSize);
- }
- }
- return np;
- }
-}
-
-/*
-** Free the memory pointed to by p. p must be either a NULL pointer or a
-** value returned by a previous call to sqlite3Malloc() or sqlite3Realloc().
-*/
-void sqlite3FreeX(void *p){
- if( p ){
- updateMemoryUsedCount(0 - OSSIZEOF(p));
- OSFREE(p);
- }
-}
-
-/*
-** A version of sqliteMalloc() that is always a function, not a macro.
-** Currently, this is used only to alloc to allocate the parser engine.
-*/
-void *sqlite3MallocX(int n){
- return sqliteMalloc(n);
-}
-
-/*
-** sqlite3Malloc
-** sqlite3ReallocOrFree
-**
-** These two are implemented as wrappers around sqlite3MallocRaw(),
-** sqlite3Realloc() and sqlite3Free().
-*/
-void *sqlite3Malloc(int n, int doMemManage){
- void *p = sqlite3MallocRaw(n, doMemManage);
- if( p ){
- memset(p, 0, n);
- }
- return p;
-}
-void *sqlite3ReallocOrFree(void *p, int n){
- void *pNew;
- pNew = sqlite3Realloc(p, n);
- if( !pNew ){
- sqlite3FreeX(p);
- }
- return pNew;
-}
-
-/*
-** sqlite3ThreadSafeMalloc() and sqlite3ThreadSafeFree() are used in those
-** rare scenarios where sqlite may allocate memory in one thread and free
-** it in another. They are exactly the same as sqlite3Malloc() and
-** sqlite3Free() except that:
-**
-** * The allocated memory is not included in any calculations with
-** respect to the soft-heap-limit, and
-**
-** * sqlite3ThreadSafeMalloc() must be matched with ThreadSafeFree(),
-** not sqlite3Free(). Calling sqlite3Free() on memory obtained from
-** ThreadSafeMalloc() will cause an error somewhere down the line.
-*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-void *sqlite3ThreadSafeMalloc(int n){
- (void)ENTER_MALLOC;
- return sqlite3Malloc(n, 0);
-}
-void sqlite3ThreadSafeFree(void *p){
- (void)ENTER_MALLOC;
- if( p ){
- OSFREE(p);
- }
-}
-#endif
-
-
-/*
-** Return the number of bytes allocated at location p. p must be either
-** a NULL pointer (in which case 0 is returned) or a pointer returned by
-** sqlite3Malloc(), sqlite3Realloc() or sqlite3ReallocOrFree().
-**
-** The number of bytes allocated does not include any overhead inserted by
-** any malloc() wrapper functions that may be called. So the value returned
-** is the number of bytes that were available to SQLite using pointer p,
-** regardless of how much memory was actually allocated.
-*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-int sqlite3AllocSize(void *p){
- return OSSIZEOF(p);
-}
-#endif
-
-/*
-** Make a copy of a string in memory obtained from sqliteMalloc(). These
-** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
-** is because when memory debugging is turned on, these two functions are
-** called via macros that record the current file and line number in the
-** ThreadData structure.
-*/
-char *sqlite3StrDup(const char *z){
- char *zNew;
- int n;
- if( z==0 ) return 0;
- n = strlen(z)+1;
- zNew = sqlite3MallocRaw(n, 1);
- if( zNew ) memcpy(zNew, z, n);
- return zNew;
-}
-char *sqlite3StrNDup(const char *z, int n){
- char *zNew;
- if( z==0 ) return 0;
- zNew = sqlite3MallocRaw(n+1, 1);
- if( zNew ){
- memcpy(zNew, z, n);
- zNew[n] = 0;
- }
- return zNew;
-}
-
-/*
-** Create a string from the 2nd and subsequent arguments (up to the
-** first NULL argument), store the string in memory obtained from
-** sqliteMalloc() and make the pointer indicated by the 1st argument
-** point to that string. The 1st argument must either be NULL or
-** point to memory obtained from sqliteMalloc().
-*/
-void sqlite3SetString(char **pz, ...){
- va_list ap;
- int nByte;
- const char *z;
- char *zResult;
-
- assert( pz!=0 );
- nByte = 1;
- va_start(ap, pz);
- while( (z = va_arg(ap, const char*))!=0 ){
- nByte += strlen(z);
- }
- va_end(ap);
- sqliteFree(*pz);
- *pz = zResult = sqliteMallocRaw( nByte );
- if( zResult==0 ){
- return;
- }
- *zResult = 0;
- va_start(ap, pz);
- while( (z = va_arg(ap, const char*))!=0 ){
- int n = strlen(z);
- memcpy(zResult, z, n);
- zResult += n;
- }
- zResult[0] = 0;
- va_end(ap);
-}
-
-
-/*
-** This function must be called before exiting any API function (i.e.
-** returning control to the user) that has called sqlite3Malloc or
-** sqlite3Realloc.
-**
-** The returned value is normally a copy of the second argument to this
-** function. However, if a malloc() failure has occured since the previous
-** invocation SQLITE_NOMEM is returned instead.
-**
-** If the first argument, db, is not NULL and a malloc() error has occured,
-** then the connection error-code (the value returned by sqlite3_errcode())
-** is set to SQLITE_NOMEM.
-*/
-int sqlite3MallocHasFailed = 0;
-int sqlite3ApiExit(sqlite3* db, int rc){
- if( sqlite3MallocFailed() ){
- sqlite3MallocHasFailed = 0;
- sqlite3OsLeaveMutex();
- sqlite3Error(db, SQLITE_NOMEM, 0);
- rc = SQLITE_NOMEM;
- }
- return rc & (db ? db->errMask : 0xff);
-}
-
-/*
-** Set the "malloc has failed" condition to true for this thread.
-*/
-void sqlite3FailedMalloc(){
- if( !sqlite3MallocFailed() ){
- sqlite3OsEnterMutex();
- assert( sqlite3MallocHasFailed==0 );
- sqlite3MallocHasFailed = 1;
- }
-}
-
-#ifdef SQLITE_MEMDEBUG
-/*
-** This function sets a flag in the thread-specific-data structure that will
-** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called.
-*/
-void sqlite3MallocDisallow(){
- assert( sqlite3_mallocDisallowed>=0 );
- sqlite3_mallocDisallowed++;
-}
-
-/*
-** This function clears the flag set in the thread-specific-data structure set
-** by sqlite3MallocDisallow().
-*/
-void sqlite3MallocAllow(){
- assert( sqlite3_mallocDisallowed>0 );
- sqlite3_mallocDisallowed--;
-}
-#endif
diff --git a/third_party/sqlite/mkdll.sh b/third_party/sqlite/mkdll.sh
new file mode 100755
index 0000000..ec56fcd
--- /dev/null
+++ b/third_party/sqlite/mkdll.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite into a DLL.
+#
+# Two separate DLLs are generated. "sqlite3.dll" is the core
+# library. "tclsqlite3.dll" contains the TCL bindings and is the
+# library that is loaded into TCL in order to run SQLite.
+#
+make sqlite3.c
+PATH=$PATH:/opt/mingw/bin
+TCLDIR=/home/drh/tcltk/846/win/846win
+TCLSTUBLIB=$TCLDIR/libtcl84stub.a
+OPTS='-DUSE_TCL_STUBS=1 -DBUILD_sqlite=1 -DSQLITE_OS_WIN=1'
+OPTS="$OPTS -DSQLITE_THREADSAFE=1"
+OPTS="$OPTS -DSQLITE_ENABLE_FTS3=1"
+OPTS="$OPTS -DSQLITE_ENABLE_COLUMN_METADATA=1"
+CC="i386-mingw32msvc-gcc -Os $OPTS -Itsrc -I$TCLDIR"
+NM="i386-mingw32msvc-nm"
+CMD="$CC -c sqlite3.c"
+echo $CMD
+$CMD
+CMD="$CC -c tclsqlite3.c"
+echo $CMD
+$CMD
+echo 'EXPORTS' >tclsqlite3.def
+$NM tclsqlite3.o | grep ' T ' >temp1
+grep '_Init$' temp1 >temp2
+grep '_SafeInit$' temp1 >>temp2
+grep ' T _sqlite3_' temp1 >>temp2
+echo 'EXPORTS' >tclsqlite3.def
+sed 's/^.* T _//' temp2 | sort | uniq >>tclsqlite3.def
+i386-mingw32msvc-dllwrap \
+ --def tclsqlite3.def -v --export-all \
+ --driver-name i386-mingw32msvc-gcc \
+ --dlltool-name i386-mingw32msvc-dlltool \
+ --as i386-mingw32msvc-as \
+ --target i386-mingw32 \
+ -dllname tclsqlite3.dll -lmsvcrt tclsqlite3.o $TCLSTUBLIB
+$NM sqlite3.o | grep ' T ' >temp1
+echo 'EXPORTS' >sqlite3.def
+grep ' _sqlite3_' temp1 | sed 's/^.* _//' >>sqlite3.def
+i386-mingw32msvc-dllwrap \
+ --def sqlite3.def -v --export-all \
+ --driver-name i386-mingw32msvc-gcc \
+ --dlltool-name i386-mingw32msvc-dlltool \
+ --as i386-mingw32msvc-as \
+ --target i386-mingw32 \
+ -dllname sqlite3.dll -lmsvcrt sqlite3.o
diff --git a/third_party/sqlite/mkextu.sh b/third_party/sqlite/mkextu.sh
new file mode 100755
index 0000000..1d96897
--- /dev/null
+++ b/third_party/sqlite/mkextu.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite into a shared library on Linux.
+#
+# Two separate shared libraries are generated. "sqlite3.so" is the core
+# library. "tclsqlite3.so" contains the TCL bindings and is the
+# library that is loaded into TCL in order to run SQLite.
+#
+CFLAGS=-O2 -Wall
+make fts2amal.c
+echo gcc $CFLAGS -shared fts2amal.c -o fts2.so
+gcc $CFLAGS -shared fts2amal.c -o fts2.so
+strip fts2.so
diff --git a/third_party/sqlite/mkextw.sh b/third_party/sqlite/mkextw.sh
new file mode 100755
index 0000000..2d1b6d1
--- /dev/null
+++ b/third_party/sqlite/mkextw.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite extensions into DLLs.
+#
+make fts2amal.c
+PATH=$PATH:/opt/mingw/bin
+OPTS='-DTHREADSAFE=1 -DBUILD_sqlite=1 -DSQLITE_OS_WIN=1'
+CC="i386-mingw32msvc-gcc -O2 $OPTS -Itsrc"
+NM="i386-mingw32msvc-nm"
+CMD="$CC -c fts2amal.c"
+echo $CMD
+$CMD
+echo 'EXPORTS' >fts2.def
+echo 'sqlite3_extension_init' >>fts2.def
+i386-mingw32msvc-dllwrap \
+ --def fts2.def -v --export-all \
+ --driver-name i386-mingw32msvc-gcc \
+ --dlltool-name i386-mingw32msvc-dlltool \
+ --as i386-mingw32msvc-as \
+ --target i386-mingw32 \
+ -dllname fts2.dll -lmsvcrt fts2amal.o
+zip fts2dll.zip fts2.dll fts2.def
diff --git a/third_party/sqlite/mkopcodec.awk b/third_party/sqlite/mkopcodec.awk
new file mode 100755
index 0000000..ec80953
--- /dev/null
+++ b/third_party/sqlite/mkopcodec.awk
@@ -0,0 +1,31 @@
+#!/usr/bin/awk -f
+#
+# This AWK script scans the opcodes.h file (which is itself generated by
+# another awk script) and uses the information gleaned to create the
+# opcodes.c source file.
+#
+# Opcodes.c contains strings which are the symbolic names for the various
+# opcodes used by the VDBE. These strings are used when disassembling a
+# VDBE program during tracing or as a result of the EXPLAIN keyword.
+#
+BEGIN {
+ print "/* Automatically generated. Do not edit */"
+ print "/* See the mkopcodec.awk script for details. */"
+ printf "#if !defined(SQLITE_OMIT_EXPLAIN)"
+ printf " || !defined(NDEBUG)"
+ printf " || defined(VDBE_PROFILE)"
+ print " || defined(SQLITE_DEBUG)"
+ print "const char *sqlite3OpcodeName(int i){"
+ print " static const char *const azName[] = { \"?\","
+}
+/define OP_/ {
+ sub("OP_","",$2)
+ i++
+ printf " /* %3d */ \"%s\",\n", $3, $2
+}
+END {
+ print " };"
+ print " return azName[i];"
+ print "}"
+ print "#endif"
+}
diff --git a/third_party/sqlite/mkopcodeh.awk b/third_party/sqlite/mkopcodeh.awk
new file mode 100755
index 0000000..796a7af
--- /dev/null
+++ b/third_party/sqlite/mkopcodeh.awk
@@ -0,0 +1,157 @@
+#!/usr/bin/awk -f
+#
+# Generate the file opcodes.h.
+#
+# This AWK script scans a concatenation of the parse.h output file from the
+# parser and the vdbe.c source file in order to generate the opcodes numbers
+# for all opcodes.
+#
+# The lines of the vdbe.c that we are interested in are of the form:
+#
+# case OP_aaaa: /* same as TK_bbbbb */
+#
+# The TK_ comment is optional. If it is present, then the value assigned to
+# the OP_ is the same as the TK_ value. If missing, the OP_ value is assigned
+# a small integer that is different from every other OP_ value.
+#
+# We go to the trouble of making some OP_ values the same as TK_ values
+# as an optimization. During parsing, things like expression operators
+# are coded with TK_ values such as TK_ADD, TK_DIVIDE, and so forth. Later
+# during code generation, we need to generate corresponding opcodes like
+# OP_Add and OP_Divide. By making TK_ADD==OP_Add and TK_DIVIDE==OP_Divide,
+# code to translate from one to the other is avoided. This makes the
+# code generator run (infinitesimally) faster and more importantly it makes
+# the library footprint smaller.
+#
+# This script also scans for lines of the form:
+#
+# case OP_aaaa: /* no-push */
+#
+# When the no-push comment is found on an opcode, it means that that
+# opcode does not leave a result on the stack. By identifying which
+# opcodes leave results on the stack it is possible to determine a
+# much smaller upper bound on the size of the stack. This allows
+# a smaller stack to be allocated, which is important to embedded
+# systems with limited memory space. This script generates a series
+# of "NOPUSH_MASK" defines that contain bitmaps of opcodes that leave
+# results on the stack. The NOPUSH_MASK defines are used in vdbeaux.c
+# to help determine the maximum stack size.
+#
+
+
+# Remember the TK_ values from the parse.h file
+/^#define TK_/ {
+ tk[$2] = 0+$3
+}
+
+# Scan for "case OP_aaaa:" lines in the vdbe.c file
+/^case OP_/ {
+ name = $2
+ sub(/:/,"",name)
+ sub("\r","",name)
+ op[name] = -1
+ jump[name] = 0
+ out2_prerelease[name] = 0
+ in1[name] = 0
+ in2[name] = 0
+ in3[name] = 0
+ out3[name] = 0
+ for(i=3; i<NF; i++){
+ if($i=="same" && $(i+1)=="as"){
+ sym = $(i+2)
+ sub(/,/,"",sym)
+ op[name] = tk[sym]
+ used[op[name]] = 1
+ sameas[op[name]] = sym
+ }
+ x = $i
+ sub(",","",x)
+ if(x=="jump"){
+ jump[name] = 1
+ }else if(x=="out2-prerelease"){
+ out2_prerelease[name] = 1
+ }else if(x=="in1"){
+ in1[name] = 1
+ }else if(x=="in2"){
+ in2[name] = 1
+ }else if(x=="in3"){
+ in3[name] = 1
+ }else if(x=="out3"){
+ out3[name] = 1
+ }
+ }
+}
+
+# Assign numbers to all opcodes and output the result.
+END {
+ cnt = 0
+ max = 0
+ print "/* Automatically generated. Do not edit */"
+ print "/* See the mkopcodeh.awk script for details */"
+ op["OP_Noop"] = -1;
+ op["OP_Explain"] = -1;
+ for(name in op){
+ if( op[name]<0 ){
+ cnt++
+ while( used[cnt] ) cnt++
+ op[name] = cnt
+ }
+ used[op[name]] = 1;
+ if( op[name]>max ) max = op[name]
+ printf "#define %-25s %15d", name, op[name]
+ if( sameas[op[name]] ) {
+ printf " /* same as %-12s*/", sameas[op[name]]
+ }
+ printf "\n"
+
+ }
+ seenUnused = 0;
+ for(i=1; i<max; i++){
+ if( !used[i] ){
+ if( !seenUnused ){
+ printf "\n/* The following opcode values are never used */\n"
+ seenUnused = 1
+ }
+ printf "#define %-25s %15d\n", sprintf( "OP_NotUsed_%-3d", i ), i
+ }
+ }
+
+ # Generate the bitvectors:
+ #
+ # bit 0: jump
+ # bit 1: pushes a result onto stack
+ # bit 2: output to p1. release p1 before opcode runs
+ #
+ for(i=0; i<=max; i++) bv[i] = 0;
+ for(name in op){
+ x = op[name]
+ a0 = a1 = a2 = a3 = a4 = a5 = a6 = a7 = 0
+ # a8 = a9 = a10 = a11 = a12 = a13 = a14 = a15 = 0
+ if( jump[name] ) a0 = 1;
+ if( out2_prerelease[name] ) a1 = 2;
+ if( in1[name] ) a2 = 4;
+ if( in2[name] ) a3 = 8;
+ if( in3[name] ) a4 = 16;
+ if( out3[name] ) a5 = 32;
+ # bv[x] = a0+a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15;
+ bv[x] = a0+a1+a2+a3+a4+a5+a6+a7;
+ }
+ print "\n"
+ print "/* Properties such as \"out2\" or \"jump\" that are specified in"
+ print "** comments following the \"case\" for each opcode in the vdbe.c"
+ print "** are encoded into bitvectors as follows:"
+ print "*/"
+ print "#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */"
+ print "#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */"
+ print "#define OPFLG_IN1 0x0004 /* in1: P1 is an input */"
+ print "#define OPFLG_IN2 0x0008 /* in2: P2 is an input */"
+ print "#define OPFLG_IN3 0x0010 /* in3: P3 is an input */"
+ print "#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */"
+ print "#define OPFLG_INITIALIZER {\\"
+ for(i=0; i<=max; i++){
+ if( i%8==0 ) printf("/* %3d */",i)
+ printf " 0x%02x,", bv[i]
+ if( i%8==7 ) printf("\\\n");
+ }
+ print "}"
+}
diff --git a/third_party/sqlite/mkso.sh b/third_party/sqlite/mkso.sh
new file mode 100755
index 0000000..6f2e8e2
--- /dev/null
+++ b/third_party/sqlite/mkso.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite into a shared library on Linux.
+#
+# Two separate shared libraries are generated. "sqlite3.so" is the core
+# library. "tclsqlite3.so" contains the TCL bindings and is the
+# library that is loaded into TCL in order to run SQLite.
+#
+make target_source
+cd tsrc
+rm shell.c
+TCLDIR=/home/drh/tcltk/846/linux/846linux
+TCLSTUBLIB=$TCLDIR/libtclstub8.4g.a
+OPTS='-DUSE_TCL_STUBS=1 -DNDEBUG=1 -DHAVE_DLOPEN=1'
+OPTS="$OPTS -DSQLITE_THREADSAFE=1"
+OPTS="$OPTS -DSQLITE_ENABLE_FTS3=1"
+OPTS="$OPTS -DSQLITE_ENABLE_COLUMN_METADATA=1"
+for i in *.c; do
+ if test $i != 'keywordhash.c'; then
+ CMD="cc -fPIC $OPTS -O2 -I. -I$TCLDIR -c $i"
+ echo $CMD
+ $CMD
+ fi
+done
+echo gcc -shared *.o $TCLSTUBLIB -o tclsqlite3.so
+gcc -shared *.o $TCLSTUBLIB -o tclsqlite3.so
+strip tclsqlite3.so
+rm tclsqlite.c tclsqlite.o
+echo gcc -shared *.o -o sqlite3.so
+gcc -shared *.o -o sqlite3.so
+strip sqlite3.so
+cd ..
diff --git a/third_party/sqlite/opcodes.c b/third_party/sqlite/opcodes.c
deleted file mode 100644
index 0b62359..0000000
--- a/third_party/sqlite/opcodes.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Automatically generated. Do not edit */
-/* See the mkopcodec.awk script for details. */
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
-const char *const sqlite3OpcodeNames[] = { "?",
- /* 1 */ "MemLoad",
- /* 2 */ "VNext",
- /* 3 */ "Column",
- /* 4 */ "SetCookie",
- /* 5 */ "IfMemPos",
- /* 6 */ "Sequence",
- /* 7 */ "MoveGt",
- /* 8 */ "RowKey",
- /* 9 */ "OpenWrite",
- /* 10 */ "If",
- /* 11 */ "Pop",
- /* 12 */ "VRowid",
- /* 13 */ "CollSeq",
- /* 14 */ "OpenRead",
- /* 15 */ "Expire",
- /* 16 */ "Not",
- /* 17 */ "AutoCommit",
- /* 18 */ "IntegrityCk",
- /* 19 */ "Sort",
- /* 20 */ "Function",
- /* 21 */ "Noop",
- /* 22 */ "Return",
- /* 23 */ "NewRowid",
- /* 24 */ "IfMemNeg",
- /* 25 */ "Variable",
- /* 26 */ "String",
- /* 27 */ "RealAffinity",
- /* 28 */ "VRename",
- /* 29 */ "ParseSchema",
- /* 30 */ "VOpen",
- /* 31 */ "Close",
- /* 32 */ "CreateIndex",
- /* 33 */ "IsUnique",
- /* 34 */ "NotFound",
- /* 35 */ "Int64",
- /* 36 */ "MustBeInt",
- /* 37 */ "Halt",
- /* 38 */ "Rowid",
- /* 39 */ "IdxLT",
- /* 40 */ "AddImm",
- /* 41 */ "Statement",
- /* 42 */ "RowData",
- /* 43 */ "MemMax",
- /* 44 */ "Push",
- /* 45 */ "NotExists",
- /* 46 */ "MemIncr",
- /* 47 */ "Gosub",
- /* 48 */ "Integer",
- /* 49 */ "MemInt",
- /* 50 */ "Prev",
- /* 51 */ "VColumn",
- /* 52 */ "CreateTable",
- /* 53 */ "Last",
- /* 54 */ "IncrVacuum",
- /* 55 */ "IdxRowid",
- /* 56 */ "MakeIdxRec",
- /* 57 */ "ResetCount",
- /* 58 */ "FifoWrite",
- /* 59 */ "Callback",
- /* 60 */ "Or",
- /* 61 */ "And",
- /* 62 */ "ContextPush",
- /* 63 */ "DropTrigger",
- /* 64 */ "DropIndex",
- /* 65 */ "IsNull",
- /* 66 */ "NotNull",
- /* 67 */ "Ne",
- /* 68 */ "Eq",
- /* 69 */ "Gt",
- /* 70 */ "Le",
- /* 71 */ "Lt",
- /* 72 */ "Ge",
- /* 73 */ "IdxGE",
- /* 74 */ "BitAnd",
- /* 75 */ "BitOr",
- /* 76 */ "ShiftLeft",
- /* 77 */ "ShiftRight",
- /* 78 */ "Add",
- /* 79 */ "Subtract",
- /* 80 */ "Multiply",
- /* 81 */ "Divide",
- /* 82 */ "Remainder",
- /* 83 */ "Concat",
- /* 84 */ "IdxDelete",
- /* 85 */ "Negative",
- /* 86 */ "Vacuum",
- /* 87 */ "BitNot",
- /* 88 */ "String8",
- /* 89 */ "MoveLe",
- /* 90 */ "IfNot",
- /* 91 */ "DropTable",
- /* 92 */ "MakeRecord",
- /* 93 */ "Delete",
- /* 94 */ "AggFinal",
- /* 95 */ "Dup",
- /* 96 */ "Goto",
- /* 97 */ "TableLock",
- /* 98 */ "FifoRead",
- /* 99 */ "Clear",
- /* 100 */ "IdxGT",
- /* 101 */ "MoveLt",
- /* 102 */ "VerifyCookie",
- /* 103 */ "AggStep",
- /* 104 */ "Pull",
- /* 105 */ "SetNumColumns",
- /* 106 */ "AbsValue",
- /* 107 */ "Transaction",
- /* 108 */ "VFilter",
- /* 109 */ "VDestroy",
- /* 110 */ "ContextPop",
- /* 111 */ "Next",
- /* 112 */ "IdxInsert",
- /* 113 */ "Distinct",
- /* 114 */ "Insert",
- /* 115 */ "Destroy",
- /* 116 */ "ReadCookie",
- /* 117 */ "ForceInt",
- /* 118 */ "LoadAnalysis",
- /* 119 */ "Explain",
- /* 120 */ "IfMemZero",
- /* 121 */ "OpenPseudo",
- /* 122 */ "OpenEphemeral",
- /* 123 */ "Null",
- /* 124 */ "Blob",
- /* 125 */ "Real",
- /* 126 */ "HexBlob",
- /* 127 */ "MemStore",
- /* 128 */ "Rewind",
- /* 129 */ "MoveGe",
- /* 130 */ "VBegin",
- /* 131 */ "VUpdate",
- /* 132 */ "VCreate",
- /* 133 */ "MemMove",
- /* 134 */ "MemNull",
- /* 135 */ "Found",
- /* 136 */ "NullRow",
- /* 137 */ "NotUsed_137",
- /* 138 */ "ToText",
- /* 139 */ "ToBlob",
- /* 140 */ "ToNumeric",
- /* 141 */ "ToInt",
- /* 142 */ "ToReal",
-};
-#endif
diff --git a/third_party/sqlite/opcodes.h b/third_party/sqlite/opcodes.h
deleted file mode 100644
index c4b5760..0000000
--- a/third_party/sqlite/opcodes.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/* Automatically generated. Do not edit */
-/* See the mkopcodeh.awk script for details */
-#define OP_MemLoad 1
-#define OP_VNext 2
-#define OP_HexBlob 126 /* same as TK_BLOB */
-#define OP_Column 3
-#define OP_SetCookie 4
-#define OP_IfMemPos 5
-#define OP_Real 125 /* same as TK_FLOAT */
-#define OP_Sequence 6
-#define OP_MoveGt 7
-#define OP_Ge 72 /* same as TK_GE */
-#define OP_RowKey 8
-#define OP_Eq 68 /* same as TK_EQ */
-#define OP_OpenWrite 9
-#define OP_NotNull 66 /* same as TK_NOTNULL */
-#define OP_If 10
-#define OP_ToInt 141 /* same as TK_TO_INT */
-#define OP_String8 88 /* same as TK_STRING */
-#define OP_Pop 11
-#define OP_VRowid 12
-#define OP_CollSeq 13
-#define OP_OpenRead 14
-#define OP_Expire 15
-#define OP_AutoCommit 17
-#define OP_Gt 69 /* same as TK_GT */
-#define OP_IntegrityCk 18
-#define OP_Sort 19
-#define OP_Function 20
-#define OP_And 61 /* same as TK_AND */
-#define OP_Subtract 79 /* same as TK_MINUS */
-#define OP_Noop 21
-#define OP_Return 22
-#define OP_Remainder 82 /* same as TK_REM */
-#define OP_NewRowid 23
-#define OP_Multiply 80 /* same as TK_STAR */
-#define OP_IfMemNeg 24
-#define OP_Variable 25
-#define OP_String 26
-#define OP_RealAffinity 27
-#define OP_VRename 28
-#define OP_ParseSchema 29
-#define OP_VOpen 30
-#define OP_Close 31
-#define OP_CreateIndex 32
-#define OP_IsUnique 33
-#define OP_NotFound 34
-#define OP_Int64 35
-#define OP_MustBeInt 36
-#define OP_Halt 37
-#define OP_Rowid 38
-#define OP_IdxLT 39
-#define OP_AddImm 40
-#define OP_Statement 41
-#define OP_RowData 42
-#define OP_MemMax 43
-#define OP_Push 44
-#define OP_Or 60 /* same as TK_OR */
-#define OP_NotExists 45
-#define OP_MemIncr 46
-#define OP_Gosub 47
-#define OP_Divide 81 /* same as TK_SLASH */
-#define OP_Integer 48
-#define OP_ToNumeric 140 /* same as TK_TO_NUMERIC*/
-#define OP_MemInt 49
-#define OP_Prev 50
-#define OP_Concat 83 /* same as TK_CONCAT */
-#define OP_BitAnd 74 /* same as TK_BITAND */
-#define OP_VColumn 51
-#define OP_CreateTable 52
-#define OP_Last 53
-#define OP_IsNull 65 /* same as TK_ISNULL */
-#define OP_IncrVacuum 54
-#define OP_IdxRowid 55
-#define OP_MakeIdxRec 56
-#define OP_ShiftRight 77 /* same as TK_RSHIFT */
-#define OP_ResetCount 57
-#define OP_FifoWrite 58
-#define OP_Callback 59
-#define OP_ContextPush 62
-#define OP_DropTrigger 63
-#define OP_DropIndex 64
-#define OP_IdxGE 73
-#define OP_IdxDelete 84
-#define OP_Vacuum 86
-#define OP_MoveLe 89
-#define OP_IfNot 90
-#define OP_DropTable 91
-#define OP_MakeRecord 92
-#define OP_ToBlob 139 /* same as TK_TO_BLOB */
-#define OP_Delete 93
-#define OP_AggFinal 94
-#define OP_ShiftLeft 76 /* same as TK_LSHIFT */
-#define OP_Dup 95
-#define OP_Goto 96
-#define OP_TableLock 97
-#define OP_FifoRead 98
-#define OP_Clear 99
-#define OP_IdxGT 100
-#define OP_MoveLt 101
-#define OP_Le 70 /* same as TK_LE */
-#define OP_VerifyCookie 102
-#define OP_AggStep 103
-#define OP_Pull 104
-#define OP_ToText 138 /* same as TK_TO_TEXT */
-#define OP_Not 16 /* same as TK_NOT */
-#define OP_ToReal 142 /* same as TK_TO_REAL */
-#define OP_SetNumColumns 105
-#define OP_AbsValue 106
-#define OP_Transaction 107
-#define OP_VFilter 108
-#define OP_Negative 85 /* same as TK_UMINUS */
-#define OP_Ne 67 /* same as TK_NE */
-#define OP_VDestroy 109
-#define OP_ContextPop 110
-#define OP_BitOr 75 /* same as TK_BITOR */
-#define OP_Next 111
-#define OP_IdxInsert 112
-#define OP_Distinct 113
-#define OP_Lt 71 /* same as TK_LT */
-#define OP_Insert 114
-#define OP_Destroy 115
-#define OP_ReadCookie 116
-#define OP_ForceInt 117
-#define OP_LoadAnalysis 118
-#define OP_Explain 119
-#define OP_IfMemZero 120
-#define OP_OpenPseudo 121
-#define OP_OpenEphemeral 122
-#define OP_Null 123
-#define OP_Blob 124
-#define OP_Add 78 /* same as TK_PLUS */
-#define OP_MemStore 127
-#define OP_Rewind 128
-#define OP_MoveGe 129
-#define OP_VBegin 130
-#define OP_VUpdate 131
-#define OP_BitNot 87 /* same as TK_BITNOT */
-#define OP_VCreate 132
-#define OP_MemMove 133
-#define OP_MemNull 134
-#define OP_Found 135
-#define OP_NullRow 136
-
-/* The following opcode values are never used */
-#define OP_NotUsed_137 137
-
-/* Opcodes that are guaranteed to never push a value onto the stack
-** contain a 1 their corresponding position of the following mask
-** set. See the opcodeNoPush() function in vdbeaux.c */
-#define NOPUSH_MASK_0 0xeeb4
-#define NOPUSH_MASK_1 0xf96b
-#define NOPUSH_MASK_2 0xfbb6
-#define NOPUSH_MASK_3 0xfe64
-#define NOPUSH_MASK_4 0xffff
-#define NOPUSH_MASK_5 0x6ef7
-#define NOPUSH_MASK_6 0xfbfb
-#define NOPUSH_MASK_7 0x8767
-#define NOPUSH_MASK_8 0x7d9f
-#define NOPUSH_MASK_9 0x0000
diff --git a/third_party/sqlite/os.c b/third_party/sqlite/os.c
deleted file mode 100644
index 7cd1ab9..0000000
--- a/third_party/sqlite/os.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-** 2005 November 29
-**
-** 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 contains OS interface code that is common to all
-** architectures.
-*/
-#define _SQLITE_OS_C_ 1
-#include "sqliteInt.h"
-#include "os.h"
-#undef _SQLITE_OS_C_
-
-/*
-** The following routines are convenience wrappers around methods
-** of the OsFile object. This is mostly just syntactic sugar. All
-** of this would be completely automatic if SQLite were coded using
-** C++ instead of plain old C.
-*/
-int sqlite3OsClose(OsFile **pId){
- OsFile *id;
- if( pId!=0 && (id = *pId)!=0 ){
- return id->pMethod->xClose(pId);
- }else{
- return SQLITE_OK;
- }
-}
-int sqlite3OsOpenDirectory(OsFile *id, const char *zName){
- return id->pMethod->xOpenDirectory(id, zName);
-}
-int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
- return id->pMethod->xRead(id, pBuf, amt);
-}
-int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
- return id->pMethod->xWrite(id, pBuf, amt);
-}
-int sqlite3OsSeek(OsFile *id, i64 offset){
- return id->pMethod->xSeek(id, offset);
-}
-int sqlite3OsTruncate(OsFile *id, i64 size){
- return id->pMethod->xTruncate(id, size);
-}
-int sqlite3OsSync(OsFile *id, int fullsync){
- return id->pMethod->xSync(id, fullsync);
-}
-void sqlite3OsSetFullSync(OsFile *id, int value){
- id->pMethod->xSetFullSync(id, value);
-}
-int sqlite3OsFileSize(OsFile *id, i64 *pSize){
- return id->pMethod->xFileSize(id, pSize);
-}
-int sqlite3OsLock(OsFile *id, int lockType){
- return id->pMethod->xLock(id, lockType);
-}
-int sqlite3OsUnlock(OsFile *id, int lockType){
- return id->pMethod->xUnlock(id, lockType);
-}
-int sqlite3OsCheckReservedLock(OsFile *id){
- return id->pMethod->xCheckReservedLock(id);
-}
-int sqlite3OsSectorSize(OsFile *id){
- int (*xSectorSize)(OsFile*) = id->pMethod->xSectorSize;
- return xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE;
-}
-
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
- /* These methods are currently only used for testing and debugging. */
- int sqlite3OsFileHandle(OsFile *id){
- return id->pMethod->xFileHandle(id);
- }
- int sqlite3OsLockState(OsFile *id){
- return id->pMethod->xLockState(id);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_REDEF_IO
-/*
-** A function to return a pointer to the virtual function table.
-** This routine really does not accomplish very much since the
-** virtual function table is a global variable and anybody who
-** can call this function can just as easily access the variable
-** for themselves. Nevertheless, we include this routine for
-** backwards compatibility with an earlier redefinable I/O
-** interface design.
-*/
-struct sqlite3OsVtbl *sqlite3_os_switch(void){
- return &sqlite3Os;
-}
-#endif
diff --git a/third_party/sqlite/os.h b/third_party/sqlite/os.h
deleted file mode 100644
index 76a9f2c..0000000
--- a/third_party/sqlite/os.h
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
-** 2001 September 16
-**
-** 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 header file (together with is companion C source-code file
-** "os.c") attempt to abstract the underlying operating system so that
-** the SQLite library will work on both POSIX and windows systems.
-*/
-#ifndef _SQLITE_OS_H_
-#define _SQLITE_OS_H_
-
-/*
-** Figure out if we are dealing with Unix, Windows, or some other
-** operating system.
-*/
-#if defined(OS_OTHER)
-# if OS_OTHER==1
-# undef OS_UNIX
-# define OS_UNIX 0
-# undef OS_WIN
-# define OS_WIN 0
-# undef OS_OS2
-# define OS_OS2 0
-# else
-# undef OS_OTHER
-# endif
-#endif
-#if !defined(OS_UNIX) && !defined(OS_OTHER)
-# define OS_OTHER 0
-# ifndef OS_WIN
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
-# define OS_WIN 1
-# define OS_UNIX 0
-# define OS_OS2 0
-# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
-# define OS_WIN 0
-# define OS_UNIX 0
-# define OS_OS2 1
-# else
-# define OS_WIN 0
-# define OS_UNIX 1
-# define OS_OS2 0
-# endif
-# else
-# define OS_UNIX 0
-# define OS_OS2 0
-# endif
-#else
-# ifndef OS_WIN
-# define OS_WIN 0
-# endif
-#endif
-
-
-/*
-** Define the maximum size of a temporary filename
-*/
-#if OS_WIN
-# include <windows.h>
-# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
-#elif OS_OS2
-# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
-# include <os2safe.h> /* has to be included before os2.h for linking to work */
-# endif
-# define INCL_DOSDATETIME
-# define INCL_DOSFILEMGR
-# define INCL_DOSERRORS
-# define INCL_DOSMISC
-# define INCL_DOSPROCESS
-# define INCL_DOSMODULEMGR
-# include <os2.h>
-# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
-#else
-# define SQLITE_TEMPNAME_SIZE 200
-#endif
-
-/* If the SET_FULLSYNC macro is not defined above, then make it
-** a no-op
-*/
-#ifndef SET_FULLSYNC
-# define SET_FULLSYNC(x,y)
-#endif
-
-/*
-** The default size of a disk sector
-*/
-#ifndef SQLITE_DEFAULT_SECTOR_SIZE
-# define SQLITE_DEFAULT_SECTOR_SIZE 512
-#endif
-
-/*
-** Temporary files are named starting with this prefix followed by 16 random
-** alphanumeric characters, and no file extension. They are stored in the
-** OS's standard temporary file directory, and are deleted prior to exit.
-** If sqlite is being embedded in another program, you may wish to change the
-** prefix to reflect your program's name, so that if your program exits
-** prematurely, old temporary files can be easily identified. This can be done
-** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line.
-**
-** 2006-10-31: The default prefix used to be "sqlite_". But then
-** Mcafee started using SQLite in their anti-virus product and it
-** started putting files with the "sqlite" name in the c:/temp folder.
-** This annoyed many windows users. Those users would then do a
-** Google search for "sqlite", find the telephone numbers of the
-** developers and call to wake them up at night and complain.
-** For this reason, the default name prefix is changed to be "sqlite"
-** spelled backwards. So the temp files are still identified, but
-** anybody smart enough to figure out the code is also likely smart
-** enough to know that calling the developer will not help get rid
-** of the file.
-*/
-#ifndef TEMP_FILE_PREFIX
-# define TEMP_FILE_PREFIX "etilqs_"
-#endif
-
-/*
-** Define the interfaces for Unix, Windows, and OS/2.
-*/
-#if OS_UNIX
-#define sqlite3OsOpenReadWrite sqlite3UnixOpenReadWrite
-#define sqlite3OsOpenExclusive sqlite3UnixOpenExclusive
-#define sqlite3OsOpenReadOnly sqlite3UnixOpenReadOnly
-#define sqlite3OsDelete sqlite3UnixDelete
-#define sqlite3OsFileExists sqlite3UnixFileExists
-#define sqlite3OsFullPathname sqlite3UnixFullPathname
-#define sqlite3OsIsDirWritable sqlite3UnixIsDirWritable
-#define sqlite3OsSyncDirectory sqlite3UnixSyncDirectory
-#define sqlite3OsTempFileName sqlite3UnixTempFileName
-#define sqlite3OsRandomSeed sqlite3UnixRandomSeed
-#define sqlite3OsSleep sqlite3UnixSleep
-#define sqlite3OsCurrentTime sqlite3UnixCurrentTime
-#define sqlite3OsEnterMutex sqlite3UnixEnterMutex
-#define sqlite3OsLeaveMutex sqlite3UnixLeaveMutex
-#define sqlite3OsInMutex sqlite3UnixInMutex
-#define sqlite3OsThreadSpecificData sqlite3UnixThreadSpecificData
-#define sqlite3OsMalloc sqlite3GenericMalloc
-#define sqlite3OsRealloc sqlite3GenericRealloc
-#define sqlite3OsFree sqlite3GenericFree
-#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
-#define sqlite3OsDlopen sqlite3UnixDlopen
-#define sqlite3OsDlsym sqlite3UnixDlsym
-#define sqlite3OsDlclose sqlite3UnixDlclose
-#endif
-#if OS_WIN
-#define sqlite3OsOpenReadWrite sqlite3WinOpenReadWrite
-#define sqlite3OsOpenExclusive sqlite3WinOpenExclusive
-#define sqlite3OsOpenReadOnly sqlite3WinOpenReadOnly
-#define sqlite3OsDelete sqlite3WinDelete
-#define sqlite3OsFileExists sqlite3WinFileExists
-#define sqlite3OsFullPathname sqlite3WinFullPathname
-#define sqlite3OsIsDirWritable sqlite3WinIsDirWritable
-#define sqlite3OsSyncDirectory sqlite3WinSyncDirectory
-#define sqlite3OsTempFileName sqlite3WinTempFileName
-#define sqlite3OsRandomSeed sqlite3WinRandomSeed
-#define sqlite3OsSleep sqlite3WinSleep
-#define sqlite3OsCurrentTime sqlite3WinCurrentTime
-#define sqlite3OsEnterMutex sqlite3WinEnterMutex
-#define sqlite3OsLeaveMutex sqlite3WinLeaveMutex
-#define sqlite3OsInMutex sqlite3WinInMutex
-#define sqlite3OsThreadSpecificData sqlite3WinThreadSpecificData
-#define sqlite3OsMalloc sqlite3GenericMalloc
-#define sqlite3OsRealloc sqlite3GenericRealloc
-#define sqlite3OsFree sqlite3GenericFree
-#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
-#define sqlite3OsDlopen sqlite3WinDlopen
-#define sqlite3OsDlsym sqlite3WinDlsym
-#define sqlite3OsDlclose sqlite3WinDlclose
-#endif
-#if OS_OS2
-#define sqlite3OsOpenReadWrite sqlite3Os2OpenReadWrite
-#define sqlite3OsOpenExclusive sqlite3Os2OpenExclusive
-#define sqlite3OsOpenReadOnly sqlite3Os2OpenReadOnly
-#define sqlite3OsDelete sqlite3Os2Delete
-#define sqlite3OsFileExists sqlite3Os2FileExists
-#define sqlite3OsFullPathname sqlite3Os2FullPathname
-#define sqlite3OsIsDirWritable sqlite3Os2IsDirWritable
-#define sqlite3OsSyncDirectory sqlite3Os2SyncDirectory
-#define sqlite3OsTempFileName sqlite3Os2TempFileName
-#define sqlite3OsRandomSeed sqlite3Os2RandomSeed
-#define sqlite3OsSleep sqlite3Os2Sleep
-#define sqlite3OsCurrentTime sqlite3Os2CurrentTime
-#define sqlite3OsEnterMutex sqlite3Os2EnterMutex
-#define sqlite3OsLeaveMutex sqlite3Os2LeaveMutex
-#define sqlite3OsInMutex sqlite3Os2InMutex
-#define sqlite3OsThreadSpecificData sqlite3Os2ThreadSpecificData
-#define sqlite3OsMalloc sqlite3GenericMalloc
-#define sqlite3OsRealloc sqlite3GenericRealloc
-#define sqlite3OsFree sqlite3GenericFree
-#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
-#define sqlite3OsDlopen sqlite3Os2Dlopen
-#define sqlite3OsDlsym sqlite3Os2Dlsym
-#define sqlite3OsDlclose sqlite3Os2Dlclose
-#endif
-
-
-
-
-/*
-** If using an alternative OS interface, then we must have an "os_other.h"
-** header file available for that interface. Presumably the "os_other.h"
-** header file contains #defines similar to those above.
-*/
-#if OS_OTHER
-# include "os_other.h"
-#endif
-
-
-
-/*
-** Forward declarations
-*/
-typedef struct OsFile OsFile;
-typedef struct IoMethod IoMethod;
-
-/*
-** An instance of the following structure contains pointers to all
-** methods on an OsFile object.
-*/
-struct IoMethod {
- int (*xClose)(OsFile**);
- int (*xOpenDirectory)(OsFile*, const char*);
- int (*xRead)(OsFile*, void*, int amt);
- int (*xWrite)(OsFile*, const void*, int amt);
- int (*xSeek)(OsFile*, i64 offset);
- int (*xTruncate)(OsFile*, i64 size);
- int (*xSync)(OsFile*, int);
- void (*xSetFullSync)(OsFile *id, int setting);
- int (*xFileHandle)(OsFile *id);
- int (*xFileSize)(OsFile*, i64 *pSize);
- int (*xLock)(OsFile*, int);
- int (*xUnlock)(OsFile*, int);
- int (*xLockState)(OsFile *id);
- int (*xCheckReservedLock)(OsFile *id);
- int (*xSectorSize)(OsFile *id);
-};
-
-/*
-** The OsFile object describes an open disk file in an OS-dependent way.
-** The version of OsFile defined here is a generic version. Each OS
-** implementation defines its own subclass of this structure that contains
-** additional information needed to handle file I/O. But the pMethod
-** entry (pointing to the virtual function table) always occurs first
-** so that we can always find the appropriate methods.
-*/
-struct OsFile {
- IoMethod const *pMethod;
-};
-
-/*
-** The following values may be passed as the second argument to
-** sqlite3OsLock(). The various locks exhibit the following semantics:
-**
-** SHARED: Any number of processes may hold a SHARED lock simultaneously.
-** RESERVED: A single process may hold a RESERVED lock on a file at
-** any time. Other processes may hold and obtain new SHARED locks.
-** PENDING: A single process may hold a PENDING lock on a file at
-** any one time. Existing SHARED locks may persist, but no new
-** SHARED locks may be obtained by other processes.
-** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
-**
-** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
-** process that requests an EXCLUSIVE lock may actually obtain a PENDING
-** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
-** sqlite3OsLock().
-*/
-#define NO_LOCK 0
-#define SHARED_LOCK 1
-#define RESERVED_LOCK 2
-#define PENDING_LOCK 3
-#define EXCLUSIVE_LOCK 4
-
-/*
-** File Locking Notes: (Mostly about windows but also some info for Unix)
-**
-** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
-** those functions are not available. So we use only LockFile() and
-** UnlockFile().
-**
-** LockFile() prevents not just writing but also reading by other processes.
-** A SHARED_LOCK is obtained by locking a single randomly-chosen
-** byte out of a specific range of bytes. The lock byte is obtained at
-** random so two separate readers can probably access the file at the
-** same time, unless they are unlucky and choose the same lock byte.
-** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
-** There can only be one writer. A RESERVED_LOCK is obtained by locking
-** a single byte of the file that is designated as the reserved lock byte.
-** A PENDING_LOCK is obtained by locking a designated byte different from
-** the RESERVED_LOCK byte.
-**
-** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
-** which means we can use reader/writer locks. When reader/writer locks
-** are used, the lock is placed on the same range of bytes that is used
-** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
-** will support two or more Win95 readers or two or more WinNT readers.
-** But a single Win95 reader will lock out all WinNT readers and a single
-** WinNT reader will lock out all other Win95 readers.
-**
-** The following #defines specify the range of bytes used for locking.
-** SHARED_SIZE is the number of bytes available in the pool from which
-** a random byte is selected for a shared lock. The pool of bytes for
-** shared locks begins at SHARED_FIRST.
-**
-** These #defines are available in sqlite_aux.h so that adaptors for
-** connecting SQLite to other operating systems can use the same byte
-** ranges for locking. In particular, the same locking strategy and
-** byte ranges are used for Unix. This leaves open the possiblity of having
-** clients on win95, winNT, and unix all talking to the same shared file
-** and all locking correctly. To do so would require that samba (or whatever
-** tool is being used for file sharing) implements locks correctly between
-** windows and unix. I'm guessing that isn't likely to happen, but by
-** using the same locking range we are at least open to the possibility.
-**
-** Locking in windows is manditory. For this reason, we cannot store
-** actual data in the bytes used for locking. The pager never allocates
-** the pages involved in locking therefore. SHARED_SIZE is selected so
-** that all locks will fit on a single page even at the minimum page size.
-** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
-** is set high so that we don't have to allocate an unused page except
-** for very large databases. But one should test the page skipping logic
-** by setting PENDING_BYTE low and running the entire regression suite.
-**
-** Changing the value of PENDING_BYTE results in a subtly incompatible
-** file format. Depending on how it is changed, you might not notice
-** the incompatibility right away, even running a full regression test.
-** The default location of PENDING_BYTE is the first byte past the
-** 1GB boundary.
-**
-*/
-#ifndef SQLITE_TEST
-#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */
-#else
-extern unsigned int sqlite3_pending_byte;
-#define PENDING_BYTE sqlite3_pending_byte
-#endif
-
-#define RESERVED_BYTE (PENDING_BYTE+1)
-#define SHARED_FIRST (PENDING_BYTE+2)
-#define SHARED_SIZE 510
-
-/*
-** Prototypes for operating system interface routines.
-*/
-int sqlite3OsClose(OsFile**);
-int sqlite3OsOpenDirectory(OsFile*, const char*);
-int sqlite3OsRead(OsFile*, void*, int amt);
-int sqlite3OsWrite(OsFile*, const void*, int amt);
-int sqlite3OsSeek(OsFile*, i64 offset);
-int sqlite3OsTruncate(OsFile*, i64 size);
-int sqlite3OsSync(OsFile*, int);
-void sqlite3OsSetFullSync(OsFile *id, int setting);
-int sqlite3OsFileSize(OsFile*, i64 *pSize);
-int sqlite3OsLock(OsFile*, int);
-int sqlite3OsUnlock(OsFile*, int);
-int sqlite3OsCheckReservedLock(OsFile *id);
-int sqlite3OsOpenReadWrite(const char*, OsFile**, int*);
-int sqlite3OsOpenExclusive(const char*, OsFile**, int);
-int sqlite3OsOpenReadOnly(const char*, OsFile**);
-int sqlite3OsDelete(const char*);
-int sqlite3OsFileExists(const char*);
-char *sqlite3OsFullPathname(const char*);
-int sqlite3OsIsDirWritable(char*);
-int sqlite3OsSyncDirectory(const char*);
-int sqlite3OsSectorSize(OsFile *id);
-int sqlite3OsTempFileName(char*);
-int sqlite3OsRandomSeed(char*);
-int sqlite3OsSleep(int ms);
-int sqlite3OsCurrentTime(double*);
-void sqlite3OsEnterMutex(void);
-void sqlite3OsLeaveMutex(void);
-int sqlite3OsInMutex(int);
-ThreadData *sqlite3OsThreadSpecificData(int);
-void *sqlite3OsMalloc(int);
-void *sqlite3OsRealloc(void *, int);
-void sqlite3OsFree(void *);
-int sqlite3OsAllocationSize(void *);
-void *sqlite3OsDlopen(const char*);
-void *sqlite3OsDlsym(void*, const char*);
-int sqlite3OsDlclose(void*);
-
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
- int sqlite3OsFileHandle(OsFile *id);
- int sqlite3OsLockState(OsFile *id);
-#endif
-
-/*
-** If the SQLITE_ENABLE_REDEF_IO macro is defined, then the OS-layer
-** interface routines are not called directly but are invoked using
-** pointers to functions. This allows the implementation of various
-** OS-layer interface routines to be modified at run-time. There are
-** obscure but legitimate reasons for wanting to do this. But for
-** most users, a direct call to the underlying interface is preferable
-** so the the redefinable I/O interface is turned off by default.
-*/
-#ifdef SQLITE_ENABLE_REDEF_IO
-
-/*
-** When redefinable I/O is enabled, a single global instance of the
-** following structure holds pointers to the routines that SQLite
-** uses to talk with the underlying operating system. Modify this
-** structure (before using any SQLite API!) to accomodate perculiar
-** operating system interfaces or behaviors.
-*/
-struct sqlite3OsVtbl {
- int (*xOpenReadWrite)(const char*, OsFile**, int*);
- int (*xOpenExclusive)(const char*, OsFile**, int);
- int (*xOpenReadOnly)(const char*, OsFile**);
-
- int (*xDelete)(const char*);
- int (*xFileExists)(const char*);
- char *(*xFullPathname)(const char*);
- int (*xIsDirWritable)(char*);
- int (*xSyncDirectory)(const char*);
- int (*xTempFileName)(char*);
-
- int (*xRandomSeed)(char*);
- int (*xSleep)(int ms);
- int (*xCurrentTime)(double*);
-
- void (*xEnterMutex)(void);
- void (*xLeaveMutex)(void);
- int (*xInMutex)(int);
- ThreadData *(*xThreadSpecificData)(int);
-
- void *(*xMalloc)(int);
- void *(*xRealloc)(void *, int);
- void (*xFree)(void *);
- int (*xAllocationSize)(void *);
-
- void *(*xDlopen)(const char*);
- void *(*xDlsym)(void*, const char*);
- int (*xDlclose)(void*);
-};
-
-/* Macro used to comment out routines that do not exists when there is
-** no disk I/O or extension loading
-*/
-#ifdef SQLITE_OMIT_DISKIO
-# define IF_DISKIO(X) 0
-#else
-# define IF_DISKIO(X) X
-#endif
-#ifdef SQLITE_OMIT_LOAD_EXTENSION
-# define IF_DLOPEN(X) 0
-#else
-# define IF_DLOPEN(X) X
-#endif
-
-
-#if defined(_SQLITE_OS_C_) || defined(SQLITE_AMALGAMATION)
- /*
- ** The os.c file implements the global virtual function table.
- ** We have to put this file here because the initializers
- ** (ex: sqlite3OsRandomSeed) are macros that are about to be
- ** redefined.
- */
- struct sqlite3OsVtbl sqlite3Os = {
- IF_DISKIO( sqlite3OsOpenReadWrite ),
- IF_DISKIO( sqlite3OsOpenExclusive ),
- IF_DISKIO( sqlite3OsOpenReadOnly ),
- IF_DISKIO( sqlite3OsDelete ),
- IF_DISKIO( sqlite3OsFileExists ),
- IF_DISKIO( sqlite3OsFullPathname ),
- IF_DISKIO( sqlite3OsIsDirWritable ),
- IF_DISKIO( sqlite3OsSyncDirectory ),
- IF_DISKIO( sqlite3OsTempFileName ),
- sqlite3OsRandomSeed,
- sqlite3OsSleep,
- sqlite3OsCurrentTime,
- sqlite3OsEnterMutex,
- sqlite3OsLeaveMutex,
- sqlite3OsInMutex,
- sqlite3OsThreadSpecificData,
- sqlite3OsMalloc,
- sqlite3OsRealloc,
- sqlite3OsFree,
- sqlite3OsAllocationSize,
- IF_DLOPEN( sqlite3OsDlopen ),
- IF_DLOPEN( sqlite3OsDlsym ),
- IF_DLOPEN( sqlite3OsDlclose ),
- };
-#else
- /*
- ** Files other than os.c just reference the global virtual function table.
- */
- extern struct sqlite3OsVtbl sqlite3Os;
-#endif /* _SQLITE_OS_C_ */
-
-
-/* This additional API routine is available with redefinable I/O */
-struct sqlite3OsVtbl *sqlite3_os_switch(void);
-
-
-/*
-** Redefine the OS interface to go through the virtual function table
-** rather than calling routines directly.
-*/
-#undef sqlite3OsOpenReadWrite
-#undef sqlite3OsOpenExclusive
-#undef sqlite3OsOpenReadOnly
-#undef sqlite3OsDelete
-#undef sqlite3OsFileExists
-#undef sqlite3OsFullPathname
-#undef sqlite3OsIsDirWritable
-#undef sqlite3OsSyncDirectory
-#undef sqlite3OsTempFileName
-#undef sqlite3OsRandomSeed
-#undef sqlite3OsSleep
-#undef sqlite3OsCurrentTime
-#undef sqlite3OsEnterMutex
-#undef sqlite3OsLeaveMutex
-#undef sqlite3OsInMutex
-#undef sqlite3OsThreadSpecificData
-#undef sqlite3OsMalloc
-#undef sqlite3OsRealloc
-#undef sqlite3OsFree
-#undef sqlite3OsAllocationSize
-#define sqlite3OsOpenReadWrite sqlite3Os.xOpenReadWrite
-#define sqlite3OsOpenExclusive sqlite3Os.xOpenExclusive
-#define sqlite3OsOpenReadOnly sqlite3Os.xOpenReadOnly
-#define sqlite3OsDelete sqlite3Os.xDelete
-#define sqlite3OsFileExists sqlite3Os.xFileExists
-#define sqlite3OsFullPathname sqlite3Os.xFullPathname
-#define sqlite3OsIsDirWritable sqlite3Os.xIsDirWritable
-#define sqlite3OsSyncDirectory sqlite3Os.xSyncDirectory
-#define sqlite3OsTempFileName sqlite3Os.xTempFileName
-#define sqlite3OsRandomSeed sqlite3Os.xRandomSeed
-#define sqlite3OsSleep sqlite3Os.xSleep
-#define sqlite3OsCurrentTime sqlite3Os.xCurrentTime
-#define sqlite3OsEnterMutex sqlite3Os.xEnterMutex
-#define sqlite3OsLeaveMutex sqlite3Os.xLeaveMutex
-#define sqlite3OsInMutex sqlite3Os.xInMutex
-#define sqlite3OsThreadSpecificData sqlite3Os.xThreadSpecificData
-#define sqlite3OsMalloc sqlite3Os.xMalloc
-#define sqlite3OsRealloc sqlite3Os.xRealloc
-#define sqlite3OsFree sqlite3Os.xFree
-#define sqlite3OsAllocationSize sqlite3Os.xAllocationSize
-
-#endif /* SQLITE_ENABLE_REDEF_IO */
-
-#endif /* _SQLITE_OS_H_ */
diff --git a/third_party/sqlite/os_common.h b/third_party/sqlite/os_common.h
deleted file mode 100644
index 0c54405..0000000
--- a/third_party/sqlite/os_common.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
-** 2004 May 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 contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-
-/*
- * When testing, this global variable stores the location of the
- * pending-byte in the database file.
- */
-#ifdef SQLITE_TEST
-unsigned int sqlite3_pending_byte = 0x40000000;
-#endif
-
-#ifdef SQLITE_DEBUG
-int sqlite3_os_trace = 0;
-#define OSTRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X)
-#define OSTRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y)
-#define OSTRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
-#define OSTRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
-#define OSTRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
-#define OSTRACE6(X,Y,Z,A,B,C) \
- if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
-#define OSTRACE7(X,Y,Z,A,B,C,D) \
- if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
-#else
-#define OSTRACE1(X)
-#define OSTRACE2(X,Y)
-#define OSTRACE3(X,Y,Z)
-#define OSTRACE4(X,Y,Z,A)
-#define OSTRACE5(X,Y,Z,A,B)
-#define OSTRACE6(X,Y,Z,A,B,C)
-#define OSTRACE7(X,Y,Z,A,B,C,D)
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-__inline__ unsigned long long int hwtime(void){
- unsigned long long int x;
- __asm__("rdtsc\n\t"
- "mov %%edx, %%ecx\n\t"
- :"=A" (x));
- return x;
-}
-static unsigned long long int g_start;
-static unsigned int elapse;
-#define TIMER_START g_start=hwtime()
-#define TIMER_END elapse=hwtime()-g_start
-#define TIMER_ELAPSED elapse
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED 0
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#ifdef SQLITE_TEST
-int sqlite3_io_error_hit = 0;
-int sqlite3_io_error_pending = 0;
-int sqlite3_io_error_persist = 0;
-int sqlite3_diskfull_pending = 0;
-int sqlite3_diskfull = 0;
-#define SimulateIOError(CODE) \
- if( sqlite3_io_error_pending || sqlite3_io_error_hit ) \
- if( sqlite3_io_error_pending-- == 1 \
- || (sqlite3_io_error_persist && sqlite3_io_error_hit) ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit = 1;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#ifdef SQLITE_TEST
-int sqlite3_open_file_count = 0;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif
-
-/*
-** sqlite3GenericMalloc
-** sqlite3GenericRealloc
-** sqlite3GenericOsFree
-** sqlite3GenericAllocationSize
-**
-** Implementation of the os level dynamic memory allocation interface in terms
-** of the standard malloc(), realloc() and free() found in many operating
-** systems. No rocket science here.
-**
-** There are two versions of these four functions here. The version
-** implemented here is only used if memory-management or memory-debugging is
-** enabled. This version allocates an extra 8-bytes at the beginning of each
-** block and stores the size of the allocation there.
-**
-** If neither memory-management or debugging is enabled, the second
-** set of implementations is used instead.
-*/
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || defined (SQLITE_MEMDEBUG)
-void *sqlite3GenericMalloc(int n){
- char *p = (char *)malloc(n+8);
- assert(n>0);
- assert(sizeof(int)<=8);
- if( p ){
- *(int *)p = n;
- p += 8;
- }
- return (void *)p;
-}
-void *sqlite3GenericRealloc(void *p, int n){
- char *p2 = ((char *)p - 8);
- assert(n>0);
- p2 = (char*)realloc(p2, n+8);
- if( p2 ){
- *(int *)p2 = n;
- p2 += 8;
- }
- return (void *)p2;
-}
-void sqlite3GenericFree(void *p){
- assert(p);
- free((void *)((char *)p - 8));
-}
-int sqlite3GenericAllocationSize(void *p){
- return p ? *(int *)((char *)p - 8) : 0;
-}
-#else
-void *sqlite3GenericMalloc(int n){
- char *p = (char *)malloc(n);
- return (void *)p;
-}
-void *sqlite3GenericRealloc(void *p, int n){
- assert(n>0);
- p = realloc(p, n);
- return p;
-}
-void sqlite3GenericFree(void *p){
- assert(p);
- free(p);
-}
-/* Never actually used, but needed for the linker */
-int sqlite3GenericAllocationSize(void *p){ return 0; }
-#endif
-
-/*
-** The default size of a disk sector
-*/
-#ifndef PAGER_SECTOR_SIZE
-# define PAGER_SECTOR_SIZE 512
-#endif
diff --git a/third_party/sqlite/preprocessed/README b/third_party/sqlite/preprocessed/README
new file mode 100755
index 0000000..302965d
--- /dev/null
+++ b/third_party/sqlite/preprocessed/README
@@ -0,0 +1,10 @@
+DO NOT EDIT FILES IN THIS DIRECTORY.
+
+These files are automatically generated from the sqlite originals. If
+you edit these files, your edits will be dropped in a future import of
+the sqlite code.
+
+See ../google_generate_preprocessed.sh for information on how these
+files are built.
+
+Scott Hess, April 9, 2007.
diff --git a/third_party/sqlite/keywordhash.h b/third_party/sqlite/preprocessed/keywordhash.h
index 609ff20..609ff20 100644..100755
--- a/third_party/sqlite/keywordhash.h
+++ b/third_party/sqlite/preprocessed/keywordhash.h
diff --git a/third_party/sqlite/preprocessed/opcodes.c b/third_party/sqlite/preprocessed/opcodes.c
new file mode 100755
index 0000000..5f1492d
--- /dev/null
+++ b/third_party/sqlite/preprocessed/opcodes.c
@@ -0,0 +1,151 @@
+/* Automatically generated. Do not edit */
+/* See the mkopcodec.awk script for details. */
+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+const char *sqlite3OpcodeName(int i){
+ static const char *const azName[] = { "?",
+ /* 1 */ "VNext",
+ /* 2 */ "Affinity",
+ /* 3 */ "Column",
+ /* 4 */ "SetCookie",
+ /* 5 */ "Sequence",
+ /* 6 */ "MoveGt",
+ /* 7 */ "RowKey",
+ /* 8 */ "SCopy",
+ /* 9 */ "OpenWrite",
+ /* 10 */ "If",
+ /* 11 */ "VRowid",
+ /* 12 */ "CollSeq",
+ /* 13 */ "OpenRead",
+ /* 14 */ "Expire",
+ /* 15 */ "AutoCommit",
+ /* 16 */ "Not",
+ /* 17 */ "Pagecount",
+ /* 18 */ "IntegrityCk",
+ /* 19 */ "Sort",
+ /* 20 */ "Copy",
+ /* 21 */ "Trace",
+ /* 22 */ "Function",
+ /* 23 */ "IfNeg",
+ /* 24 */ "Noop",
+ /* 25 */ "Return",
+ /* 26 */ "NewRowid",
+ /* 27 */ "Variable",
+ /* 28 */ "String",
+ /* 29 */ "RealAffinity",
+ /* 30 */ "VRename",
+ /* 31 */ "ParseSchema",
+ /* 32 */ "VOpen",
+ /* 33 */ "Close",
+ /* 34 */ "CreateIndex",
+ /* 35 */ "IsUnique",
+ /* 36 */ "NotFound",
+ /* 37 */ "Int64",
+ /* 38 */ "MustBeInt",
+ /* 39 */ "Halt",
+ /* 40 */ "Rowid",
+ /* 41 */ "IdxLT",
+ /* 42 */ "AddImm",
+ /* 43 */ "Statement",
+ /* 44 */ "RowData",
+ /* 45 */ "MemMax",
+ /* 46 */ "NotExists",
+ /* 47 */ "Gosub",
+ /* 48 */ "Integer",
+ /* 49 */ "Prev",
+ /* 50 */ "VColumn",
+ /* 51 */ "CreateTable",
+ /* 52 */ "Last",
+ /* 53 */ "IncrVacuum",
+ /* 54 */ "IdxRowid",
+ /* 55 */ "ResetCount",
+ /* 56 */ "FifoWrite",
+ /* 57 */ "ContextPush",
+ /* 58 */ "Yield",
+ /* 59 */ "DropTrigger",
+ /* 60 */ "Or",
+ /* 61 */ "And",
+ /* 62 */ "DropIndex",
+ /* 63 */ "IdxGE",
+ /* 64 */ "IdxDelete",
+ /* 65 */ "IsNull",
+ /* 66 */ "NotNull",
+ /* 67 */ "Ne",
+ /* 68 */ "Eq",
+ /* 69 */ "Gt",
+ /* 70 */ "Le",
+ /* 71 */ "Lt",
+ /* 72 */ "Ge",
+ /* 73 */ "Vacuum",
+ /* 74 */ "BitAnd",
+ /* 75 */ "BitOr",
+ /* 76 */ "ShiftLeft",
+ /* 77 */ "ShiftRight",
+ /* 78 */ "Add",
+ /* 79 */ "Subtract",
+ /* 80 */ "Multiply",
+ /* 81 */ "Divide",
+ /* 82 */ "Remainder",
+ /* 83 */ "Concat",
+ /* 84 */ "MoveLe",
+ /* 85 */ "IfNot",
+ /* 86 */ "DropTable",
+ /* 87 */ "BitNot",
+ /* 88 */ "String8",
+ /* 89 */ "MakeRecord",
+ /* 90 */ "ResultRow",
+ /* 91 */ "Delete",
+ /* 92 */ "AggFinal",
+ /* 93 */ "Compare",
+ /* 94 */ "Goto",
+ /* 95 */ "TableLock",
+ /* 96 */ "FifoRead",
+ /* 97 */ "Clear",
+ /* 98 */ "MoveLt",
+ /* 99 */ "VerifyCookie",
+ /* 100 */ "AggStep",
+ /* 101 */ "SetNumColumns",
+ /* 102 */ "Transaction",
+ /* 103 */ "VFilter",
+ /* 104 */ "VDestroy",
+ /* 105 */ "ContextPop",
+ /* 106 */ "Next",
+ /* 107 */ "IdxInsert",
+ /* 108 */ "Insert",
+ /* 109 */ "Destroy",
+ /* 110 */ "ReadCookie",
+ /* 111 */ "ForceInt",
+ /* 112 */ "LoadAnalysis",
+ /* 113 */ "Explain",
+ /* 114 */ "OpenPseudo",
+ /* 115 */ "OpenEphemeral",
+ /* 116 */ "Null",
+ /* 117 */ "Move",
+ /* 118 */ "Blob",
+ /* 119 */ "Rewind",
+ /* 120 */ "MoveGe",
+ /* 121 */ "VBegin",
+ /* 122 */ "VUpdate",
+ /* 123 */ "IfZero",
+ /* 124 */ "VCreate",
+ /* 125 */ "Real",
+ /* 126 */ "Found",
+ /* 127 */ "IfPos",
+ /* 128 */ "NullRow",
+ /* 129 */ "Jump",
+ /* 130 */ "Permutation",
+ /* 131 */ "NotUsed_131",
+ /* 132 */ "NotUsed_132",
+ /* 133 */ "NotUsed_133",
+ /* 134 */ "NotUsed_134",
+ /* 135 */ "NotUsed_135",
+ /* 136 */ "NotUsed_136",
+ /* 137 */ "NotUsed_137",
+ /* 138 */ "ToText",
+ /* 139 */ "ToBlob",
+ /* 140 */ "ToNumeric",
+ /* 141 */ "ToInt",
+ /* 142 */ "ToReal",
+ };
+ return azName[i];
+}
+#endif
diff --git a/third_party/sqlite/preprocessed/opcodes.h b/third_party/sqlite/preprocessed/opcodes.h
new file mode 100755
index 0000000..5701277
--- /dev/null
+++ b/third_party/sqlite/preprocessed/opcodes.h
@@ -0,0 +1,177 @@
+/* Automatically generated. Do not edit */
+/* See the mkopcodeh.awk script for details */
+#define OP_VNext 1
+#define OP_Affinity 2
+#define OP_Column 3
+#define OP_SetCookie 4
+#define OP_Real 125 /* same as TK_FLOAT */
+#define OP_Sequence 5
+#define OP_MoveGt 6
+#define OP_Ge 72 /* same as TK_GE */
+#define OP_RowKey 7
+#define OP_SCopy 8
+#define OP_Eq 68 /* same as TK_EQ */
+#define OP_OpenWrite 9
+#define OP_NotNull 66 /* same as TK_NOTNULL */
+#define OP_If 10
+#define OP_ToInt 141 /* same as TK_TO_INT */
+#define OP_String8 88 /* same as TK_STRING */
+#define OP_VRowid 11
+#define OP_CollSeq 12
+#define OP_OpenRead 13
+#define OP_Expire 14
+#define OP_AutoCommit 15
+#define OP_Gt 69 /* same as TK_GT */
+#define OP_Pagecount 17
+#define OP_IntegrityCk 18
+#define OP_Sort 19
+#define OP_Copy 20
+#define OP_Trace 21
+#define OP_Function 22
+#define OP_IfNeg 23
+#define OP_And 61 /* same as TK_AND */
+#define OP_Subtract 79 /* same as TK_MINUS */
+#define OP_Noop 24
+#define OP_Return 25
+#define OP_Remainder 82 /* same as TK_REM */
+#define OP_NewRowid 26
+#define OP_Multiply 80 /* same as TK_STAR */
+#define OP_Variable 27
+#define OP_String 28
+#define OP_RealAffinity 29
+#define OP_VRename 30
+#define OP_ParseSchema 31
+#define OP_VOpen 32
+#define OP_Close 33
+#define OP_CreateIndex 34
+#define OP_IsUnique 35
+#define OP_NotFound 36
+#define OP_Int64 37
+#define OP_MustBeInt 38
+#define OP_Halt 39
+#define OP_Rowid 40
+#define OP_IdxLT 41
+#define OP_AddImm 42
+#define OP_Statement 43
+#define OP_RowData 44
+#define OP_MemMax 45
+#define OP_Or 60 /* same as TK_OR */
+#define OP_NotExists 46
+#define OP_Gosub 47
+#define OP_Divide 81 /* same as TK_SLASH */
+#define OP_Integer 48
+#define OP_ToNumeric 140 /* same as TK_TO_NUMERIC*/
+#define OP_Prev 49
+#define OP_Concat 83 /* same as TK_CONCAT */
+#define OP_BitAnd 74 /* same as TK_BITAND */
+#define OP_VColumn 50
+#define OP_CreateTable 51
+#define OP_Last 52
+#define OP_IsNull 65 /* same as TK_ISNULL */
+#define OP_IncrVacuum 53
+#define OP_IdxRowid 54
+#define OP_ShiftRight 77 /* same as TK_RSHIFT */
+#define OP_ResetCount 55
+#define OP_FifoWrite 56
+#define OP_ContextPush 57
+#define OP_Yield 58
+#define OP_DropTrigger 59
+#define OP_DropIndex 62
+#define OP_IdxGE 63
+#define OP_IdxDelete 64
+#define OP_Vacuum 73
+#define OP_MoveLe 84
+#define OP_IfNot 85
+#define OP_DropTable 86
+#define OP_MakeRecord 89
+#define OP_ToBlob 139 /* same as TK_TO_BLOB */
+#define OP_ResultRow 90
+#define OP_Delete 91
+#define OP_AggFinal 92
+#define OP_Compare 93
+#define OP_ShiftLeft 76 /* same as TK_LSHIFT */
+#define OP_Goto 94
+#define OP_TableLock 95
+#define OP_FifoRead 96
+#define OP_Clear 97
+#define OP_MoveLt 98
+#define OP_Le 70 /* same as TK_LE */
+#define OP_VerifyCookie 99
+#define OP_AggStep 100
+#define OP_ToText 138 /* same as TK_TO_TEXT */
+#define OP_Not 16 /* same as TK_NOT */
+#define OP_ToReal 142 /* same as TK_TO_REAL */
+#define OP_SetNumColumns 101
+#define OP_Transaction 102
+#define OP_VFilter 103
+#define OP_Ne 67 /* same as TK_NE */
+#define OP_VDestroy 104
+#define OP_ContextPop 105
+#define OP_BitOr 75 /* same as TK_BITOR */
+#define OP_Next 106
+#define OP_IdxInsert 107
+#define OP_Lt 71 /* same as TK_LT */
+#define OP_Insert 108
+#define OP_Destroy 109
+#define OP_ReadCookie 110
+#define OP_ForceInt 111
+#define OP_LoadAnalysis 112
+#define OP_Explain 113
+#define OP_OpenPseudo 114
+#define OP_OpenEphemeral 115
+#define OP_Null 116
+#define OP_Move 117
+#define OP_Blob 118
+#define OP_Add 78 /* same as TK_PLUS */
+#define OP_Rewind 119
+#define OP_MoveGe 120
+#define OP_VBegin 121
+#define OP_VUpdate 122
+#define OP_IfZero 123
+#define OP_BitNot 87 /* same as TK_BITNOT */
+#define OP_VCreate 124
+#define OP_Found 126
+#define OP_IfPos 127
+#define OP_NullRow 128
+#define OP_Jump 129
+#define OP_Permutation 130
+
+/* The following opcode values are never used */
+#define OP_NotUsed_131 131
+#define OP_NotUsed_132 132
+#define OP_NotUsed_133 133
+#define OP_NotUsed_134 134
+#define OP_NotUsed_135 135
+#define OP_NotUsed_136 136
+#define OP_NotUsed_137 137
+
+
+/* Properties such as "out2" or "jump" that are specified in
+** comments following the "case" for each opcode in the vdbe.c
+** are encoded into bitvectors as follows:
+*/
+#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */
+#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */
+#define OPFLG_IN1 0x0004 /* in1: P1 is an input */
+#define OPFLG_IN2 0x0008 /* in2: P2 is an input */
+#define OPFLG_IN3 0x0010 /* in3: P3 is an input */
+#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */
+#define OPFLG_INITIALIZER {\
+/* 0 */ 0x00, 0x01, 0x00, 0x00, 0x10, 0x02, 0x11, 0x00,\
+/* 8 */ 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00,\
+/* 16 */ 0x04, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05,\
+/* 24 */ 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00,\
+/* 32 */ 0x00, 0x00, 0x02, 0x11, 0x11, 0x02, 0x05, 0x00,\
+/* 40 */ 0x02, 0x11, 0x04, 0x00, 0x00, 0x0c, 0x11, 0x01,\
+/* 48 */ 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\
+/* 56 */ 0x04, 0x00, 0x00, 0x00, 0x2c, 0x2c, 0x00, 0x11,\
+/* 64 */ 0x00, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
+/* 72 */ 0x15, 0x00, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\
+/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x11, 0x05, 0x00, 0x04,\
+/* 88 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\
+/* 96 */ 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01,\
+/* 104 */ 0x00, 0x00, 0x01, 0x08, 0x00, 0x02, 0x02, 0x05,\
+/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x01,\
+/* 120 */ 0x11, 0x00, 0x00, 0x05, 0x00, 0x02, 0x11, 0x05,\
+/* 128 */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 136 */ 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,}
diff --git a/third_party/sqlite/parse.c b/third_party/sqlite/preprocessed/parse.c
index 508d5bd..91c0e72 100644..100755
--- a/third_party/sqlite/parse.c
+++ b/third_party/sqlite/preprocessed/parse.c
@@ -1,13 +1,12 @@
/* Driver template for the LEMON parser generator.
** The author disclaims copyright to this source code.
*/
-/* First off, code is include which follows the "include" declaration
-** in the input file. */
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
#include <stdio.h>
-#line 56 "parse.y"
+#line 51 "parse.y"
#include "sqliteInt.h"
-#include "parse.h"
/*
** An instance of this structure holds information about the
@@ -109,11 +108,8 @@ typedef union {
TriggerStep* yy243;
struct TrigEvent yy370;
SrcList* yy373;
- Expr * yy386;
struct {int value; int mask;} yy405;
- Token yy410;
IdList* yy432;
- int yy495;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -122,16 +118,18 @@ typedef union {
#define sqlite3ParserARG_PDECL ,Parse *pParse
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
-#define YYNSTATE 586
-#define YYNRULE 311
-#define YYERRORSYMBOL 138
-#define YYERRSYMDT yy495
+#define YYNSTATE 589
+#define YYNRULE 313
#define YYFALLBACK 1
#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
-/* Next are that tables used to determine what action to take based on the
+/* The yyzerominor constant is used to initialize instances of
+** YYMINORTYPE objects to zero. */
+static const YYMINORTYPE yyzerominor;
+
+/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
** functions that take a state number and lookahead value and return an
** action integer.
@@ -179,141 +177,141 @@ typedef union {
** yy_default[] Default action for each state.
*/
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 290, 898, 122, 585, 407, 170, 2, 437, 61, 61,
- /* 10 */ 61, 61, 517, 63, 63, 63, 63, 64, 64, 65,
- /* 20 */ 65, 65, 66, 231, 445, 209, 422, 428, 68, 63,
- /* 30 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 231,
- /* 40 */ 389, 386, 394, 449, 60, 59, 295, 432, 433, 429,
- /* 50 */ 429, 62, 62, 61, 61, 61, 61, 261, 63, 63,
- /* 60 */ 63, 63, 64, 64, 65, 65, 65, 66, 231, 290,
- /* 70 */ 491, 492, 437, 487, 206, 81, 67, 417, 69, 152,
+ /* 0 */ 292, 903, 124, 588, 409, 172, 2, 418, 61, 61,
+ /* 10 */ 61, 61, 519, 63, 63, 63, 63, 64, 64, 65,
+ /* 20 */ 65, 65, 66, 210, 447, 212, 425, 431, 68, 63,
+ /* 30 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 210,
+ /* 40 */ 391, 388, 396, 451, 60, 59, 297, 435, 436, 432,
+ /* 50 */ 432, 62, 62, 61, 61, 61, 61, 263, 63, 63,
+ /* 60 */ 63, 63, 64, 64, 65, 65, 65, 66, 210, 292,
+ /* 70 */ 493, 494, 418, 489, 208, 82, 67, 420, 69, 154,
/* 80 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 90 */ 231, 67, 460, 69, 152, 422, 428, 571, 262, 58,
- /* 100 */ 64, 64, 65, 65, 65, 66, 231, 395, 396, 419,
- /* 110 */ 419, 419, 290, 60, 59, 295, 432, 433, 429, 429,
- /* 120 */ 62, 62, 61, 61, 61, 61, 315, 63, 63, 63,
- /* 130 */ 63, 64, 64, 65, 65, 65, 66, 231, 422, 428,
- /* 140 */ 93, 65, 65, 65, 66, 231, 394, 231, 412, 34,
- /* 150 */ 56, 296, 440, 441, 408, 486, 60, 59, 295, 432,
- /* 160 */ 433, 429, 429, 62, 62, 61, 61, 61, 61, 488,
+ /* 90 */ 210, 67, 462, 69, 154, 425, 431, 574, 264, 58,
+ /* 100 */ 64, 64, 65, 65, 65, 66, 210, 397, 398, 422,
+ /* 110 */ 422, 422, 292, 60, 59, 297, 435, 436, 432, 432,
+ /* 120 */ 62, 62, 61, 61, 61, 61, 317, 63, 63, 63,
+ /* 130 */ 63, 64, 64, 65, 65, 65, 66, 210, 425, 431,
+ /* 140 */ 94, 65, 65, 65, 66, 210, 396, 210, 414, 34,
+ /* 150 */ 56, 298, 442, 443, 410, 418, 60, 59, 297, 435,
+ /* 160 */ 436, 432, 432, 62, 62, 61, 61, 61, 61, 208,
/* 170 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 180 */ 231, 290, 255, 522, 293, 569, 112, 406, 520, 449,
- /* 190 */ 329, 315, 405, 20, 437, 338, 517, 394, 530, 529,
- /* 200 */ 503, 445, 209, 568, 567, 206, 528, 422, 428, 147,
- /* 210 */ 148, 395, 396, 412, 41, 208, 149, 531, 370, 487,
- /* 220 */ 259, 566, 257, 417, 290, 60, 59, 295, 432, 433,
- /* 230 */ 429, 429, 62, 62, 61, 61, 61, 61, 315, 63,
- /* 240 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 231,
- /* 250 */ 422, 428, 445, 331, 212, 419, 419, 419, 361, 437,
- /* 260 */ 412, 41, 395, 396, 364, 565, 208, 290, 60, 59,
- /* 270 */ 295, 432, 433, 429, 429, 62, 62, 61, 61, 61,
- /* 280 */ 61, 394, 63, 63, 63, 63, 64, 64, 65, 65,
- /* 290 */ 65, 66, 231, 422, 428, 489, 298, 522, 472, 66,
- /* 300 */ 231, 211, 472, 224, 409, 284, 532, 20, 447, 521,
- /* 310 */ 166, 60, 59, 295, 432, 433, 429, 429, 62, 62,
- /* 320 */ 61, 61, 61, 61, 472, 63, 63, 63, 63, 64,
- /* 330 */ 64, 65, 65, 65, 66, 231, 207, 478, 315, 76,
- /* 340 */ 290, 235, 298, 55, 482, 225, 395, 396, 179, 545,
- /* 350 */ 492, 343, 346, 347, 67, 150, 69, 152, 337, 522,
- /* 360 */ 412, 35, 348, 237, 249, 368, 422, 428, 576, 20,
- /* 370 */ 162, 116, 239, 341, 244, 342, 174, 320, 440, 441,
- /* 380 */ 412, 3, 79, 250, 60, 59, 295, 432, 433, 429,
- /* 390 */ 429, 62, 62, 61, 61, 61, 61, 172, 63, 63,
- /* 400 */ 63, 63, 64, 64, 65, 65, 65, 66, 231, 290,
- /* 410 */ 249, 548, 232, 485, 508, 351, 315, 116, 239, 341,
- /* 420 */ 244, 342, 174, 179, 315, 523, 343, 346, 347, 250,
- /* 430 */ 218, 413, 153, 462, 509, 422, 428, 348, 412, 34,
- /* 440 */ 463, 208, 175, 173, 158, 233, 412, 34, 336, 547,
- /* 450 */ 447, 321, 166, 60, 59, 295, 432, 433, 429, 429,
- /* 460 */ 62, 62, 61, 61, 61, 61, 413, 63, 63, 63,
- /* 470 */ 63, 64, 64, 65, 65, 65, 66, 231, 290, 540,
- /* 480 */ 333, 515, 502, 539, 454, 569, 300, 19, 329, 142,
- /* 490 */ 315, 388, 315, 328, 2, 360, 455, 292, 481, 371,
- /* 500 */ 267, 266, 250, 568, 422, 428, 586, 389, 386, 456,
- /* 510 */ 206, 493, 412, 49, 412, 49, 301, 583, 889, 157,
- /* 520 */ 889, 494, 60, 59, 295, 432, 433, 429, 429, 62,
- /* 530 */ 62, 61, 61, 61, 61, 199, 63, 63, 63, 63,
- /* 540 */ 64, 64, 65, 65, 65, 66, 231, 290, 315, 179,
- /* 550 */ 436, 253, 343, 346, 347, 368, 151, 580, 306, 248,
- /* 560 */ 307, 450, 75, 348, 77, 380, 208, 423, 424, 413,
- /* 570 */ 412, 27, 317, 422, 428, 438, 1, 22, 583, 888,
- /* 580 */ 394, 888, 542, 476, 318, 261, 435, 435, 426, 427,
- /* 590 */ 413, 60, 59, 295, 432, 433, 429, 429, 62, 62,
- /* 600 */ 61, 61, 61, 61, 326, 63, 63, 63, 63, 64,
- /* 610 */ 64, 65, 65, 65, 66, 231, 290, 425, 580, 372,
- /* 620 */ 219, 92, 515, 9, 334, 394, 555, 394, 454, 67,
- /* 630 */ 394, 69, 152, 397, 398, 399, 318, 234, 435, 435,
- /* 640 */ 455, 316, 422, 428, 297, 395, 396, 318, 430, 435,
- /* 650 */ 435, 579, 289, 456, 220, 325, 5, 217, 544, 290,
- /* 660 */ 60, 59, 295, 432, 433, 429, 429, 62, 62, 61,
- /* 670 */ 61, 61, 61, 393, 63, 63, 63, 63, 64, 64,
- /* 680 */ 65, 65, 65, 66, 231, 422, 428, 480, 311, 390,
- /* 690 */ 395, 396, 395, 396, 205, 395, 396, 821, 271, 515,
- /* 700 */ 248, 198, 290, 60, 59, 295, 432, 433, 429, 429,
- /* 710 */ 62, 62, 61, 61, 61, 61, 468, 63, 63, 63,
- /* 720 */ 63, 64, 64, 65, 65, 65, 66, 231, 422, 428,
- /* 730 */ 169, 158, 261, 261, 302, 413, 274, 117, 272, 261,
- /* 740 */ 515, 515, 261, 515, 190, 290, 60, 70, 295, 432,
- /* 750 */ 433, 429, 429, 62, 62, 61, 61, 61, 61, 377,
+ /* 180 */ 210, 292, 372, 524, 295, 572, 113, 408, 522, 451,
+ /* 190 */ 331, 317, 407, 20, 244, 340, 519, 396, 478, 531,
+ /* 200 */ 505, 447, 212, 571, 570, 245, 530, 425, 431, 149,
+ /* 210 */ 150, 397, 398, 414, 41, 211, 151, 533, 488, 489,
+ /* 220 */ 418, 568, 569, 420, 292, 60, 59, 297, 435, 436,
+ /* 230 */ 432, 432, 62, 62, 61, 61, 61, 61, 317, 63,
+ /* 240 */ 63, 63, 63, 64, 64, 65, 65, 65, 66, 210,
+ /* 250 */ 425, 431, 447, 333, 215, 422, 422, 422, 363, 299,
+ /* 260 */ 414, 41, 397, 398, 366, 567, 211, 292, 60, 59,
+ /* 270 */ 297, 435, 436, 432, 432, 62, 62, 61, 61, 61,
+ /* 280 */ 61, 396, 63, 63, 63, 63, 64, 64, 65, 65,
+ /* 290 */ 65, 66, 210, 425, 431, 491, 300, 524, 474, 66,
+ /* 300 */ 210, 214, 474, 229, 411, 286, 534, 20, 449, 523,
+ /* 310 */ 168, 60, 59, 297, 435, 436, 432, 432, 62, 62,
+ /* 320 */ 61, 61, 61, 61, 474, 63, 63, 63, 63, 64,
+ /* 330 */ 64, 65, 65, 65, 66, 210, 209, 480, 317, 77,
+ /* 340 */ 292, 239, 300, 55, 484, 490, 397, 398, 181, 547,
+ /* 350 */ 494, 345, 348, 349, 67, 152, 69, 154, 339, 524,
+ /* 360 */ 414, 35, 350, 241, 221, 370, 425, 431, 579, 20,
+ /* 370 */ 164, 118, 243, 343, 248, 344, 176, 322, 442, 443,
+ /* 380 */ 414, 3, 80, 252, 60, 59, 297, 435, 436, 432,
+ /* 390 */ 432, 62, 62, 61, 61, 61, 61, 174, 63, 63,
+ /* 400 */ 63, 63, 64, 64, 65, 65, 65, 66, 210, 292,
+ /* 410 */ 221, 550, 236, 487, 510, 353, 317, 118, 243, 343,
+ /* 420 */ 248, 344, 176, 181, 317, 532, 345, 348, 349, 252,
+ /* 430 */ 223, 415, 155, 464, 511, 425, 431, 350, 414, 34,
+ /* 440 */ 465, 211, 177, 175, 160, 525, 414, 34, 338, 549,
+ /* 450 */ 449, 323, 168, 60, 59, 297, 435, 436, 432, 432,
+ /* 460 */ 62, 62, 61, 61, 61, 61, 415, 63, 63, 63,
+ /* 470 */ 63, 64, 64, 65, 65, 65, 66, 210, 292, 542,
+ /* 480 */ 335, 517, 504, 541, 456, 572, 302, 19, 331, 144,
+ /* 490 */ 317, 390, 317, 330, 2, 362, 457, 294, 483, 373,
+ /* 500 */ 269, 268, 252, 571, 425, 431, 589, 391, 388, 458,
+ /* 510 */ 208, 495, 414, 49, 414, 49, 303, 586, 894, 230,
+ /* 520 */ 894, 496, 60, 59, 297, 435, 436, 432, 432, 62,
+ /* 530 */ 62, 61, 61, 61, 61, 201, 63, 63, 63, 63,
+ /* 540 */ 64, 64, 65, 65, 65, 66, 210, 292, 317, 181,
+ /* 550 */ 439, 255, 345, 348, 349, 370, 153, 583, 308, 251,
+ /* 560 */ 309, 452, 76, 350, 78, 382, 211, 426, 427, 415,
+ /* 570 */ 414, 27, 319, 425, 431, 440, 1, 22, 586, 893,
+ /* 580 */ 396, 893, 544, 478, 320, 263, 438, 438, 429, 430,
+ /* 590 */ 415, 60, 59, 297, 435, 436, 432, 432, 62, 62,
+ /* 600 */ 61, 61, 61, 61, 237, 63, 63, 63, 63, 64,
+ /* 610 */ 64, 65, 65, 65, 66, 210, 292, 428, 583, 374,
+ /* 620 */ 224, 93, 517, 9, 159, 396, 557, 396, 456, 67,
+ /* 630 */ 396, 69, 154, 399, 400, 401, 320, 328, 438, 438,
+ /* 640 */ 457, 336, 425, 431, 361, 397, 398, 320, 433, 438,
+ /* 650 */ 438, 582, 291, 458, 238, 327, 318, 222, 546, 292,
+ /* 660 */ 60, 59, 297, 435, 436, 432, 432, 62, 62, 61,
+ /* 670 */ 61, 61, 61, 225, 63, 63, 63, 63, 64, 64,
+ /* 680 */ 65, 65, 65, 66, 210, 425, 431, 482, 313, 392,
+ /* 690 */ 397, 398, 397, 398, 207, 397, 398, 825, 273, 517,
+ /* 700 */ 251, 200, 292, 60, 59, 297, 435, 436, 432, 432,
+ /* 710 */ 62, 62, 61, 61, 61, 61, 470, 63, 63, 63,
+ /* 720 */ 63, 64, 64, 65, 65, 65, 66, 210, 425, 431,
+ /* 730 */ 171, 160, 263, 263, 304, 415, 276, 395, 274, 263,
+ /* 740 */ 517, 517, 263, 517, 192, 292, 60, 70, 297, 435,
+ /* 750 */ 436, 432, 432, 62, 62, 61, 61, 61, 61, 379,
/* 760 */ 63, 63, 63, 63, 64, 64, 65, 65, 65, 66,
- /* 770 */ 231, 422, 428, 382, 557, 303, 304, 248, 413, 318,
- /* 780 */ 558, 435, 435, 559, 538, 358, 538, 385, 290, 194,
- /* 790 */ 59, 295, 432, 433, 429, 429, 62, 62, 61, 61,
- /* 800 */ 61, 61, 369, 63, 63, 63, 63, 64, 64, 65,
- /* 810 */ 65, 65, 66, 231, 422, 428, 394, 273, 248, 248,
- /* 820 */ 170, 246, 437, 413, 384, 365, 176, 177, 178, 467,
- /* 830 */ 309, 121, 154, 126, 295, 432, 433, 429, 429, 62,
- /* 840 */ 62, 61, 61, 61, 61, 315, 63, 63, 63, 63,
- /* 850 */ 64, 64, 65, 65, 65, 66, 231, 72, 322, 175,
- /* 860 */ 4, 315, 261, 315, 294, 261, 413, 412, 28, 315,
- /* 870 */ 261, 315, 319, 72, 322, 315, 4, 418, 443, 443,
- /* 880 */ 294, 395, 396, 412, 23, 412, 32, 437, 319, 324,
- /* 890 */ 327, 412, 53, 412, 52, 315, 156, 412, 97, 449,
- /* 900 */ 315, 192, 315, 275, 315, 324, 376, 469, 500, 315,
- /* 910 */ 476, 277, 476, 163, 292, 449, 315, 412, 95, 74,
- /* 920 */ 73, 467, 412, 100, 412, 101, 412, 111, 72, 313,
- /* 930 */ 314, 412, 113, 417, 446, 74, 73, 479, 412, 16,
- /* 940 */ 379, 315, 181, 465, 72, 313, 314, 72, 322, 417,
- /* 950 */ 4, 206, 315, 184, 294, 315, 497, 498, 474, 206,
- /* 960 */ 171, 339, 319, 412, 98, 419, 419, 419, 420, 421,
- /* 970 */ 11, 359, 378, 305, 412, 33, 413, 412, 96, 324,
- /* 980 */ 458, 419, 419, 419, 420, 421, 11, 413, 411, 449,
- /* 990 */ 411, 160, 410, 315, 410, 466, 221, 222, 223, 103,
- /* 1000 */ 83, 471, 315, 507, 506, 315, 620, 475, 315, 74,
- /* 1010 */ 73, 245, 203, 21, 279, 412, 24, 437, 72, 313,
- /* 1020 */ 314, 280, 315, 417, 412, 54, 505, 412, 114, 315,
- /* 1030 */ 412, 115, 504, 201, 145, 547, 240, 510, 524, 200,
- /* 1040 */ 315, 511, 202, 315, 412, 25, 315, 241, 315, 18,
- /* 1050 */ 315, 412, 36, 315, 254, 419, 419, 419, 420, 421,
- /* 1060 */ 11, 256, 412, 37, 258, 412, 26, 315, 412, 38,
- /* 1070 */ 412, 39, 412, 40, 260, 412, 42, 315, 512, 315,
- /* 1080 */ 126, 315, 437, 315, 187, 375, 276, 266, 265, 412,
- /* 1090 */ 43, 291, 315, 252, 315, 126, 206, 581, 8, 412,
- /* 1100 */ 44, 412, 29, 412, 30, 412, 45, 350, 363, 126,
- /* 1110 */ 315, 543, 315, 126, 412, 46, 412, 47, 315, 355,
- /* 1120 */ 381, 551, 315, 171, 552, 315, 90, 562, 578, 90,
- /* 1130 */ 288, 366, 412, 48, 412, 31, 582, 367, 268, 269,
- /* 1140 */ 412, 10, 270, 554, 412, 50, 564, 412, 51, 278,
- /* 1150 */ 281, 282, 575, 144, 442, 403, 323, 226, 444, 461,
- /* 1160 */ 464, 242, 503, 550, 561, 513, 161, 392, 400, 516,
- /* 1170 */ 401, 345, 402, 7, 312, 83, 227, 332, 228, 82,
- /* 1180 */ 330, 57, 408, 416, 168, 78, 459, 123, 210, 414,
- /* 1190 */ 84, 335, 340, 299, 496, 500, 490, 229, 495, 243,
- /* 1200 */ 104, 247, 499, 501, 230, 285, 415, 215, 514, 518,
- /* 1210 */ 525, 526, 519, 236, 527, 473, 238, 352, 477, 286,
- /* 1220 */ 182, 356, 533, 354, 119, 183, 185, 87, 546, 130,
- /* 1230 */ 186, 535, 188, 140, 362, 191, 553, 216, 373, 374,
- /* 1240 */ 131, 560, 308, 132, 133, 572, 577, 136, 263, 134,
- /* 1250 */ 139, 536, 573, 391, 91, 94, 404, 574, 99, 214,
- /* 1260 */ 102, 621, 622, 431, 164, 434, 165, 71, 141, 17,
- /* 1270 */ 439, 448, 451, 155, 6, 452, 470, 110, 167, 453,
- /* 1280 */ 457, 124, 13, 213, 120, 80, 12, 125, 159, 483,
- /* 1290 */ 484, 85, 310, 105, 180, 251, 106, 118, 86, 107,
- /* 1300 */ 241, 344, 349, 353, 143, 534, 127, 357, 171, 189,
- /* 1310 */ 264, 108, 287, 128, 549, 129, 193, 537, 541, 14,
- /* 1320 */ 195, 88, 196, 556, 197, 137, 138, 135, 15, 563,
- /* 1330 */ 570, 109, 283, 146, 204, 383, 387, 899, 584, 899,
- /* 1340 */ 899, 899, 899, 899, 89,
+ /* 770 */ 210, 425, 431, 384, 559, 305, 306, 251, 415, 320,
+ /* 780 */ 560, 438, 438, 561, 540, 360, 540, 387, 292, 196,
+ /* 790 */ 59, 297, 435, 436, 432, 432, 62, 62, 61, 61,
+ /* 800 */ 61, 61, 371, 63, 63, 63, 63, 64, 64, 65,
+ /* 810 */ 65, 65, 66, 210, 425, 431, 396, 275, 251, 251,
+ /* 820 */ 172, 250, 418, 415, 386, 367, 178, 179, 180, 469,
+ /* 830 */ 311, 123, 156, 5, 297, 435, 436, 432, 432, 62,
+ /* 840 */ 62, 61, 61, 61, 61, 317, 63, 63, 63, 63,
+ /* 850 */ 64, 64, 65, 65, 65, 66, 210, 72, 324, 194,
+ /* 860 */ 4, 317, 263, 317, 296, 263, 415, 414, 28, 317,
+ /* 870 */ 257, 317, 321, 72, 324, 317, 4, 119, 165, 177,
+ /* 880 */ 296, 397, 398, 414, 23, 414, 32, 418, 321, 326,
+ /* 890 */ 421, 414, 53, 414, 52, 317, 158, 414, 98, 451,
+ /* 900 */ 317, 263, 317, 277, 317, 326, 378, 471, 261, 317,
+ /* 910 */ 259, 18, 478, 445, 445, 451, 317, 414, 96, 75,
+ /* 920 */ 74, 469, 414, 101, 414, 102, 414, 112, 73, 315,
+ /* 930 */ 316, 414, 114, 420, 294, 75, 74, 481, 414, 16,
+ /* 940 */ 381, 317, 279, 467, 73, 315, 316, 72, 324, 420,
+ /* 950 */ 4, 208, 317, 183, 296, 317, 186, 128, 84, 208,
+ /* 960 */ 8, 341, 321, 414, 99, 422, 422, 422, 423, 424,
+ /* 970 */ 11, 623, 380, 307, 414, 33, 413, 414, 97, 326,
+ /* 980 */ 412, 422, 422, 422, 423, 424, 11, 415, 413, 451,
+ /* 990 */ 415, 162, 412, 317, 499, 500, 226, 227, 228, 104,
+ /* 1000 */ 448, 476, 317, 173, 507, 317, 509, 508, 317, 75,
+ /* 1010 */ 74, 329, 205, 21, 281, 414, 24, 418, 73, 315,
+ /* 1020 */ 316, 282, 317, 420, 414, 54, 460, 414, 115, 317,
+ /* 1030 */ 414, 116, 502, 203, 147, 549, 514, 468, 128, 202,
+ /* 1040 */ 317, 473, 204, 317, 414, 117, 317, 477, 317, 584,
+ /* 1050 */ 317, 414, 25, 317, 249, 422, 422, 422, 423, 424,
+ /* 1060 */ 11, 506, 414, 36, 512, 414, 37, 317, 414, 26,
+ /* 1070 */ 414, 38, 414, 39, 526, 414, 40, 317, 254, 317,
+ /* 1080 */ 128, 317, 418, 317, 256, 377, 278, 268, 585, 414,
+ /* 1090 */ 42, 293, 317, 352, 317, 128, 208, 513, 258, 414,
+ /* 1100 */ 43, 414, 44, 414, 29, 414, 30, 545, 260, 128,
+ /* 1110 */ 317, 553, 317, 173, 414, 45, 414, 46, 317, 262,
+ /* 1120 */ 383, 554, 317, 91, 564, 317, 91, 317, 581, 189,
+ /* 1130 */ 290, 357, 414, 47, 414, 48, 267, 365, 368, 369,
+ /* 1140 */ 414, 31, 270, 271, 414, 10, 272, 414, 50, 414,
+ /* 1150 */ 51, 556, 566, 280, 283, 284, 578, 146, 419, 405,
+ /* 1160 */ 231, 505, 444, 325, 516, 463, 163, 446, 552, 394,
+ /* 1170 */ 466, 563, 246, 515, 518, 520, 402, 403, 404, 7,
+ /* 1180 */ 314, 84, 232, 334, 347, 83, 332, 57, 170, 79,
+ /* 1190 */ 213, 461, 125, 85, 337, 342, 492, 502, 497, 301,
+ /* 1200 */ 498, 416, 105, 219, 247, 218, 503, 501, 233, 220,
+ /* 1210 */ 287, 234, 527, 528, 235, 529, 417, 521, 354, 288,
+ /* 1220 */ 184, 121, 185, 240, 535, 475, 242, 356, 187, 479,
+ /* 1230 */ 188, 358, 537, 88, 190, 548, 364, 193, 132, 376,
+ /* 1240 */ 555, 375, 133, 134, 135, 310, 562, 138, 136, 575,
+ /* 1250 */ 576, 577, 580, 100, 393, 406, 217, 142, 624, 625,
+ /* 1260 */ 103, 141, 265, 166, 167, 434, 71, 453, 441, 437,
+ /* 1270 */ 450, 143, 538, 157, 120, 454, 161, 472, 455, 169,
+ /* 1280 */ 459, 81, 6, 12, 13, 92, 95, 126, 216, 127,
+ /* 1290 */ 111, 485, 486, 17, 86, 346, 106, 122, 253, 107,
+ /* 1300 */ 87, 108, 182, 245, 355, 145, 351, 536, 129, 359,
+ /* 1310 */ 312, 130, 543, 173, 539, 266, 191, 109, 289, 551,
+ /* 1320 */ 195, 14, 131, 198, 197, 558, 137, 199, 139, 140,
+ /* 1330 */ 15, 565, 89, 90, 573, 110, 385, 206, 148, 389,
+ /* 1340 */ 285, 587,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 16, 139, 140, 141, 168, 21, 144, 23, 69, 70,
@@ -331,17 +329,17 @@ static const YYCODETYPE yy_lookahead[] = {
/* 120 */ 67, 68, 69, 70, 71, 72, 147, 74, 75, 76,
/* 130 */ 77, 78, 79, 80, 81, 82, 83, 84, 42, 43,
/* 140 */ 44, 80, 81, 82, 83, 84, 23, 84, 169, 170,
- /* 150 */ 19, 164, 165, 166, 23, 169, 60, 61, 62, 63,
- /* 160 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 169,
+ /* 150 */ 19, 164, 165, 166, 23, 23, 60, 61, 62, 63,
+ /* 160 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 110,
/* 170 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- /* 180 */ 84, 16, 14, 147, 150, 147, 21, 167, 168, 58,
- /* 190 */ 211, 147, 156, 157, 23, 216, 176, 23, 181, 176,
- /* 200 */ 177, 78, 79, 165, 166, 110, 183, 42, 43, 78,
- /* 210 */ 79, 88, 89, 169, 170, 228, 180, 181, 123, 88,
- /* 220 */ 52, 98, 54, 92, 16, 60, 61, 62, 63, 64,
+ /* 180 */ 84, 16, 123, 147, 150, 147, 21, 167, 168, 58,
+ /* 190 */ 211, 147, 156, 157, 92, 216, 176, 23, 147, 176,
+ /* 200 */ 177, 78, 79, 165, 166, 103, 183, 42, 43, 78,
+ /* 210 */ 79, 88, 89, 169, 170, 228, 180, 181, 169, 88,
+ /* 220 */ 88, 98, 99, 92, 16, 60, 61, 62, 63, 64,
/* 230 */ 65, 66, 67, 68, 69, 70, 71, 72, 147, 74,
/* 240 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
- /* 250 */ 42, 43, 78, 209, 210, 124, 125, 126, 224, 88,
+ /* 250 */ 42, 43, 78, 209, 210, 124, 125, 126, 224, 208,
/* 260 */ 169, 170, 88, 89, 230, 227, 228, 16, 60, 61,
/* 270 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
/* 280 */ 72, 23, 74, 75, 76, 77, 78, 79, 80, 81,
@@ -350,7 +348,7 @@ static const YYCODETYPE yy_lookahead[] = {
/* 310 */ 163, 60, 61, 62, 63, 64, 65, 66, 67, 68,
/* 320 */ 69, 70, 71, 72, 161, 74, 75, 76, 77, 78,
/* 330 */ 79, 80, 81, 82, 83, 84, 192, 200, 147, 131,
- /* 340 */ 16, 200, 16, 199, 20, 190, 88, 89, 90, 185,
+ /* 340 */ 16, 200, 16, 199, 20, 169, 88, 89, 90, 185,
/* 350 */ 186, 93, 94, 95, 217, 22, 219, 220, 147, 147,
/* 360 */ 169, 170, 104, 200, 84, 147, 42, 43, 156, 157,
/* 370 */ 90, 91, 92, 93, 94, 95, 96, 164, 165, 166,
@@ -360,14 +358,14 @@ static const YYCODETYPE yy_lookahead[] = {
/* 410 */ 84, 11, 221, 20, 30, 16, 147, 91, 92, 93,
/* 420 */ 94, 95, 96, 90, 147, 181, 93, 94, 95, 103,
/* 430 */ 212, 189, 155, 27, 50, 42, 43, 104, 169, 170,
- /* 440 */ 34, 228, 43, 201, 202, 147, 169, 170, 206, 49,
+ /* 440 */ 34, 228, 43, 201, 202, 181, 169, 170, 206, 49,
/* 450 */ 161, 162, 163, 60, 61, 62, 63, 64, 65, 66,
/* 460 */ 67, 68, 69, 70, 71, 72, 189, 74, 75, 76,
/* 470 */ 77, 78, 79, 80, 81, 82, 83, 84, 16, 25,
/* 480 */ 211, 147, 20, 29, 12, 147, 102, 19, 211, 21,
/* 490 */ 147, 141, 147, 216, 144, 41, 24, 98, 20, 99,
/* 500 */ 100, 101, 103, 165, 42, 43, 0, 1, 2, 37,
- /* 510 */ 110, 39, 169, 170, 169, 170, 182, 19, 20, 147,
+ /* 510 */ 110, 39, 169, 170, 169, 170, 182, 19, 20, 190,
/* 520 */ 22, 49, 60, 61, 62, 63, 64, 65, 66, 67,
/* 530 */ 68, 69, 70, 71, 72, 155, 74, 75, 76, 77,
/* 540 */ 78, 79, 80, 81, 82, 83, 84, 16, 147, 90,
@@ -376,14 +374,14 @@ static const YYCODETYPE yy_lookahead[] = {
/* 570 */ 169, 170, 16, 42, 43, 20, 19, 22, 19, 20,
/* 580 */ 23, 22, 18, 147, 106, 147, 108, 109, 63, 64,
/* 590 */ 189, 60, 61, 62, 63, 64, 65, 66, 67, 68,
- /* 600 */ 69, 70, 71, 72, 186, 74, 75, 76, 77, 78,
+ /* 600 */ 69, 70, 71, 72, 147, 74, 75, 76, 77, 78,
/* 610 */ 79, 80, 81, 82, 83, 84, 16, 92, 59, 55,
/* 620 */ 212, 21, 147, 19, 147, 23, 188, 23, 12, 217,
- /* 630 */ 23, 219, 220, 7, 8, 9, 106, 147, 108, 109,
+ /* 630 */ 23, 219, 220, 7, 8, 9, 106, 186, 108, 109,
/* 640 */ 24, 147, 42, 43, 208, 88, 89, 106, 92, 108,
- /* 650 */ 109, 244, 245, 37, 145, 39, 191, 182, 94, 16,
+ /* 650 */ 109, 244, 245, 37, 147, 39, 147, 182, 94, 16,
/* 660 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
- /* 670 */ 70, 71, 72, 147, 74, 75, 76, 77, 78, 79,
+ /* 670 */ 70, 71, 72, 145, 74, 75, 76, 77, 78, 79,
/* 680 */ 80, 81, 82, 83, 84, 42, 43, 80, 142, 143,
/* 690 */ 88, 89, 88, 89, 148, 88, 89, 133, 14, 147,
/* 700 */ 225, 155, 16, 60, 61, 62, 63, 64, 65, 66,
@@ -399,104 +397,104 @@ static const YYCODETYPE yy_lookahead[] = {
/* 800 */ 71, 72, 213, 74, 75, 76, 77, 78, 79, 80,
/* 810 */ 81, 82, 83, 84, 42, 43, 23, 133, 225, 225,
/* 820 */ 21, 225, 23, 189, 239, 236, 99, 100, 101, 22,
- /* 830 */ 242, 243, 155, 22, 62, 63, 64, 65, 66, 67,
+ /* 830 */ 242, 243, 155, 191, 62, 63, 64, 65, 66, 67,
/* 840 */ 68, 69, 70, 71, 72, 147, 74, 75, 76, 77,
- /* 850 */ 78, 79, 80, 81, 82, 83, 84, 16, 17, 43,
+ /* 850 */ 78, 79, 80, 81, 82, 83, 84, 16, 17, 22,
/* 860 */ 19, 147, 147, 147, 23, 147, 189, 169, 170, 147,
- /* 870 */ 147, 147, 31, 16, 17, 147, 19, 147, 124, 125,
+ /* 870 */ 14, 147, 31, 16, 17, 147, 19, 147, 19, 43,
/* 880 */ 23, 88, 89, 169, 170, 169, 170, 88, 31, 48,
/* 890 */ 147, 169, 170, 169, 170, 147, 89, 169, 170, 58,
- /* 900 */ 147, 22, 147, 188, 147, 48, 188, 114, 97, 147,
- /* 910 */ 147, 188, 147, 19, 98, 58, 147, 169, 170, 78,
+ /* 900 */ 147, 147, 147, 188, 147, 48, 188, 114, 52, 147,
+ /* 910 */ 54, 19, 147, 124, 125, 58, 147, 169, 170, 78,
/* 920 */ 79, 114, 169, 170, 169, 170, 169, 170, 87, 88,
- /* 930 */ 89, 169, 170, 92, 161, 78, 79, 80, 169, 170,
- /* 940 */ 91, 147, 155, 22, 87, 88, 89, 16, 17, 92,
- /* 950 */ 19, 110, 147, 155, 23, 147, 7, 8, 20, 110,
- /* 960 */ 22, 80, 31, 169, 170, 124, 125, 126, 127, 128,
- /* 970 */ 129, 208, 123, 208, 169, 170, 189, 169, 170, 48,
- /* 980 */ 147, 124, 125, 126, 127, 128, 129, 189, 107, 58,
- /* 990 */ 107, 5, 111, 147, 111, 203, 10, 11, 12, 13,
- /* 1000 */ 121, 147, 147, 91, 92, 147, 112, 147, 147, 78,
+ /* 930 */ 89, 169, 170, 92, 98, 78, 79, 80, 169, 170,
+ /* 940 */ 91, 147, 188, 22, 87, 88, 89, 16, 17, 92,
+ /* 950 */ 19, 110, 147, 155, 23, 147, 155, 22, 121, 110,
+ /* 960 */ 68, 80, 31, 169, 170, 124, 125, 126, 127, 128,
+ /* 970 */ 129, 112, 123, 208, 169, 170, 107, 169, 170, 48,
+ /* 980 */ 111, 124, 125, 126, 127, 128, 129, 189, 107, 58,
+ /* 990 */ 189, 5, 111, 147, 7, 8, 10, 11, 12, 13,
+ /* 1000 */ 161, 20, 147, 22, 178, 147, 91, 92, 147, 78,
/* 1010 */ 79, 147, 26, 19, 28, 169, 170, 23, 87, 88,
- /* 1020 */ 89, 35, 147, 92, 169, 170, 178, 169, 170, 147,
- /* 1030 */ 169, 170, 147, 47, 113, 49, 92, 178, 147, 53,
- /* 1040 */ 147, 178, 56, 147, 169, 170, 147, 103, 147, 19,
+ /* 1020 */ 89, 35, 147, 92, 169, 170, 147, 169, 170, 147,
+ /* 1030 */ 169, 170, 97, 47, 113, 49, 20, 203, 22, 53,
+ /* 1040 */ 147, 147, 56, 147, 169, 170, 147, 147, 147, 20,
/* 1050 */ 147, 169, 170, 147, 147, 124, 125, 126, 127, 128,
- /* 1060 */ 129, 147, 169, 170, 147, 169, 170, 147, 169, 170,
+ /* 1060 */ 129, 147, 169, 170, 178, 169, 170, 147, 169, 170,
/* 1070 */ 169, 170, 169, 170, 147, 169, 170, 147, 20, 147,
- /* 1080 */ 22, 147, 88, 147, 232, 99, 100, 101, 147, 169,
- /* 1090 */ 170, 105, 147, 20, 147, 22, 110, 20, 68, 169,
+ /* 1080 */ 22, 147, 88, 147, 147, 99, 100, 101, 59, 169,
+ /* 1090 */ 170, 105, 147, 20, 147, 22, 110, 178, 147, 169,
/* 1100 */ 170, 169, 170, 169, 170, 169, 170, 20, 147, 22,
- /* 1110 */ 147, 20, 147, 22, 169, 170, 169, 170, 147, 233,
- /* 1120 */ 134, 20, 147, 22, 20, 147, 22, 20, 20, 22,
- /* 1130 */ 22, 147, 169, 170, 169, 170, 59, 147, 147, 147,
- /* 1140 */ 169, 170, 147, 147, 169, 170, 147, 169, 170, 147,
- /* 1150 */ 147, 147, 147, 191, 229, 149, 223, 193, 229, 172,
- /* 1160 */ 172, 172, 177, 194, 194, 172, 6, 146, 146, 172,
- /* 1170 */ 146, 173, 146, 22, 154, 121, 194, 118, 195, 119,
- /* 1180 */ 116, 120, 23, 160, 112, 130, 152, 152, 222, 189,
- /* 1190 */ 98, 115, 98, 40, 179, 97, 171, 196, 171, 171,
- /* 1200 */ 19, 84, 173, 171, 197, 174, 198, 226, 160, 160,
- /* 1210 */ 171, 171, 179, 204, 171, 205, 204, 15, 205, 174,
- /* 1220 */ 151, 38, 152, 152, 60, 151, 151, 130, 184, 19,
- /* 1230 */ 152, 152, 151, 214, 152, 184, 194, 226, 152, 15,
- /* 1240 */ 187, 194, 152, 187, 187, 33, 137, 184, 234, 187,
- /* 1250 */ 214, 235, 152, 1, 237, 237, 20, 152, 159, 175,
- /* 1260 */ 175, 112, 112, 92, 112, 107, 112, 19, 19, 231,
- /* 1270 */ 20, 20, 11, 19, 117, 20, 114, 240, 22, 20,
- /* 1280 */ 20, 19, 117, 44, 243, 22, 22, 20, 112, 20,
- /* 1290 */ 20, 19, 246, 19, 96, 20, 19, 32, 19, 19,
- /* 1300 */ 103, 44, 44, 16, 21, 17, 98, 36, 22, 98,
- /* 1310 */ 133, 19, 5, 45, 1, 102, 122, 51, 45, 19,
- /* 1320 */ 113, 68, 14, 17, 115, 102, 122, 113, 19, 123,
- /* 1330 */ 20, 14, 136, 19, 135, 57, 3, 247, 4, 247,
- /* 1340 */ 247, 247, 247, 247, 68,
+ /* 1110 */ 147, 20, 147, 22, 169, 170, 169, 170, 147, 147,
+ /* 1120 */ 134, 20, 147, 22, 20, 147, 22, 147, 20, 232,
+ /* 1130 */ 22, 233, 169, 170, 169, 170, 147, 147, 147, 147,
+ /* 1140 */ 169, 170, 147, 147, 169, 170, 147, 169, 170, 169,
+ /* 1150 */ 170, 147, 147, 147, 147, 147, 147, 191, 161, 149,
+ /* 1160 */ 193, 177, 229, 223, 161, 172, 6, 229, 194, 146,
+ /* 1170 */ 172, 194, 172, 172, 172, 161, 146, 146, 146, 22,
+ /* 1180 */ 154, 121, 194, 118, 173, 119, 116, 120, 112, 130,
+ /* 1190 */ 222, 152, 152, 98, 115, 98, 171, 97, 171, 40,
+ /* 1200 */ 179, 189, 19, 84, 171, 226, 171, 173, 195, 226,
+ /* 1210 */ 174, 196, 171, 171, 197, 171, 198, 179, 15, 174,
+ /* 1220 */ 151, 60, 151, 204, 152, 205, 204, 152, 151, 205,
+ /* 1230 */ 152, 38, 152, 130, 151, 184, 152, 184, 19, 15,
+ /* 1240 */ 194, 152, 187, 187, 187, 152, 194, 184, 187, 33,
+ /* 1250 */ 152, 152, 137, 159, 1, 20, 175, 214, 112, 112,
+ /* 1260 */ 175, 214, 234, 112, 112, 92, 19, 11, 20, 107,
+ /* 1270 */ 20, 19, 235, 19, 32, 20, 112, 114, 20, 22,
+ /* 1280 */ 20, 22, 117, 22, 117, 237, 237, 19, 44, 20,
+ /* 1290 */ 240, 20, 20, 231, 19, 44, 19, 243, 20, 19,
+ /* 1300 */ 19, 19, 96, 103, 16, 21, 44, 17, 98, 36,
+ /* 1310 */ 246, 45, 45, 22, 51, 133, 98, 19, 5, 1,
+ /* 1320 */ 122, 19, 102, 14, 113, 17, 113, 115, 102, 122,
+ /* 1330 */ 19, 123, 68, 68, 20, 14, 57, 135, 19, 3,
+ /* 1340 */ 136, 4,
};
#define YY_SHIFT_USE_DFLT (-62)
-#define YY_SHIFT_MAX 387
+#define YY_SHIFT_MAX 389
static const short yy_shift_ofst[] = {
/* 0 */ 39, 841, 986, -16, 841, 931, 931, 258, 123, -36,
/* 10 */ 96, 931, 931, 931, 931, 931, -45, 400, 174, 19,
- /* 20 */ 171, -54, -54, 53, 165, 208, 251, 324, 393, 462,
+ /* 20 */ 132, -54, -54, 53, 165, 208, 251, 324, 393, 462,
/* 30 */ 531, 600, 643, 686, 643, 643, 643, 643, 643, 643,
/* 40 */ 643, 643, 643, 643, 643, 643, 643, 643, 643, 643,
/* 50 */ 643, 643, 729, 772, 772, 857, 931, 931, 931, 931,
/* 60 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
/* 70 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
/* 80 */ 931, 931, 931, 931, 931, 931, 931, 931, 931, 931,
- /* 90 */ 931, 931, 931, 931, 931, -61, -61, 6, 6, 280,
- /* 100 */ 22, 61, 399, 564, 19, 19, 19, 19, 19, 19,
- /* 110 */ 19, 216, 171, 63, -62, -62, 131, 326, 472, 472,
- /* 120 */ 498, 559, 506, 799, 19, 799, 19, 19, 19, 19,
- /* 130 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 849,
- /* 140 */ 95, -36, -36, -36, -62, -62, -62, -15, -15, 333,
- /* 150 */ 459, 478, 557, 530, 541, 616, 602, 793, 604, 607,
- /* 160 */ 626, 19, 19, 881, 19, 19, 994, 19, 19, 807,
- /* 170 */ 19, 19, 673, 807, 19, 19, 384, 384, 384, 19,
- /* 180 */ 19, 673, 19, 19, 673, 19, 454, 685, 19, 19,
- /* 190 */ 673, 19, 19, 19, 673, 19, 19, 19, 673, 673,
- /* 200 */ 19, 19, 19, 19, 19, 468, 883, 921, 754, 754,
- /* 210 */ 432, 406, 406, 406, 816, 406, 406, 811, 879, 879,
- /* 220 */ 1160, 1160, 1160, 1160, 1151, -36, 1054, 1059, 1060, 1064,
- /* 230 */ 1061, 1159, 1055, 1072, 1072, 1092, 1076, 1092, 1076, 1094,
- /* 240 */ 1094, 1153, 1094, 1098, 1094, 1181, 1117, 1159, 1117, 1159,
- /* 250 */ 1153, 1094, 1094, 1094, 1181, 1202, 1072, 1202, 1072, 1202,
- /* 260 */ 1072, 1072, 1183, 1097, 1202, 1072, 1164, 1164, 1210, 1054,
- /* 270 */ 1072, 1224, 1224, 1224, 1224, 1054, 1164, 1210, 1072, 1212,
- /* 280 */ 1212, 1072, 1072, 1109, -62, -62, -62, -62, -62, -62,
- /* 290 */ 525, 684, 727, 168, 894, 556, 555, 938, 944, 949,
- /* 300 */ 912, 1058, 1073, 1087, 1091, 1101, 1104, 1107, 1030, 1108,
- /* 310 */ 1077, 1252, 1236, 1149, 1150, 1152, 1154, 1171, 1158, 1248,
- /* 320 */ 1250, 1251, 1249, 1261, 1254, 1255, 1256, 1259, 1260, 1263,
- /* 330 */ 1157, 1264, 1165, 1263, 1162, 1262, 1267, 1176, 1269, 1270,
- /* 340 */ 1265, 1239, 1272, 1257, 1274, 1275, 1277, 1279, 1258, 1280,
- /* 350 */ 1198, 1197, 1287, 1288, 1283, 1208, 1271, 1266, 1268, 1286,
- /* 360 */ 1273, 1177, 1211, 1292, 1307, 1313, 1213, 1253, 1276, 1194,
- /* 370 */ 1300, 1207, 1308, 1209, 1306, 1214, 1223, 1204, 1309, 1206,
- /* 380 */ 1310, 1317, 1278, 1199, 1196, 1314, 1333, 1334,
+ /* 90 */ 931, 931, 931, 931, 931, 931, -61, -61, 6, 6,
+ /* 100 */ 280, 22, 61, 399, 564, 19, 19, 19, 19, 19,
+ /* 110 */ 19, 19, 216, 132, 63, -62, -62, -62, 131, 326,
+ /* 120 */ 472, 472, 498, 559, 506, 799, 19, 799, 19, 19,
+ /* 130 */ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ /* 140 */ 19, 849, 59, -36, -36, -36, -62, -62, -62, -15,
+ /* 150 */ -15, 333, 459, 478, 557, 530, 541, 616, 602, 793,
+ /* 160 */ 604, 607, 626, 19, 19, 881, 19, 19, 994, 19,
+ /* 170 */ 19, 807, 19, 19, 673, 807, 19, 19, 384, 384,
+ /* 180 */ 384, 19, 19, 673, 19, 19, 673, 19, 454, 685,
+ /* 190 */ 19, 19, 673, 19, 19, 19, 673, 19, 19, 19,
+ /* 200 */ 673, 673, 19, 19, 19, 19, 19, 468, 869, 921,
+ /* 210 */ 132, 789, 789, 432, 406, 406, 406, 836, 406, 132,
+ /* 220 */ 406, 132, 935, 837, 837, 1160, 1160, 1160, 1160, 1157,
+ /* 230 */ -36, 1060, 1065, 1066, 1070, 1067, 1059, 1076, 1076, 1095,
+ /* 240 */ 1079, 1095, 1079, 1097, 1097, 1159, 1097, 1100, 1097, 1183,
+ /* 250 */ 1119, 1119, 1159, 1097, 1097, 1097, 1183, 1203, 1076, 1203,
+ /* 260 */ 1076, 1203, 1076, 1076, 1193, 1103, 1203, 1076, 1161, 1161,
+ /* 270 */ 1219, 1060, 1076, 1224, 1224, 1224, 1224, 1060, 1161, 1219,
+ /* 280 */ 1076, 1216, 1216, 1076, 1076, 1115, -62, -62, -62, -62,
+ /* 290 */ -62, -62, 525, 684, 727, 856, 859, 556, 555, 981,
+ /* 300 */ 102, 987, 915, 1016, 1058, 1073, 1087, 1091, 1101, 1104,
+ /* 310 */ 892, 1108, 1029, 1253, 1235, 1146, 1147, 1151, 1152, 1173,
+ /* 320 */ 1162, 1247, 1248, 1250, 1252, 1256, 1254, 1255, 1257, 1258,
+ /* 330 */ 1260, 1259, 1165, 1261, 1167, 1259, 1163, 1268, 1269, 1164,
+ /* 340 */ 1271, 1272, 1242, 1244, 1275, 1251, 1277, 1278, 1280, 1281,
+ /* 350 */ 1262, 1282, 1206, 1200, 1288, 1290, 1284, 1210, 1273, 1263,
+ /* 360 */ 1266, 1291, 1267, 1182, 1218, 1298, 1313, 1318, 1220, 1264,
+ /* 370 */ 1265, 1198, 1302, 1211, 1309, 1212, 1308, 1213, 1226, 1207,
+ /* 380 */ 1311, 1208, 1314, 1321, 1279, 1202, 1204, 1319, 1336, 1337,
};
#define YY_REDUCE_USE_DFLT (-165)
-#define YY_REDUCE_MAX 289
+#define YY_REDUCE_MAX 291
static const short yy_reduce_ofst[] = {
/* 0 */ -138, 277, 546, 137, 401, -21, 44, 36, 38, 242,
/* 10 */ -141, 191, 91, 269, 343, 345, -126, 589, 338, 150,
@@ -507,87 +505,88 @@ static const short yy_reduce_ofst[] = {
/* 60 */ 724, 728, 748, 753, 755, 757, 762, 769, 794, 805,
/* 70 */ 808, 846, 855, 858, 861, 875, 882, 893, 896, 899,
/* 80 */ 901, 903, 906, 920, 930, 932, 934, 936, 945, 947,
- /* 90 */ 963, 965, 971, 975, 978, 412, 412, 412, 412, 20,
- /* 100 */ 412, 412, 23, 34, 334, 475, 552, 593, 594, 585,
- /* 110 */ 212, 412, 289, 412, 412, 412, 135, -164, -115, 164,
- /* 120 */ 407, 407, 350, 141, 436, 163, 596, -90, 763, 218,
- /* 130 */ 765, 438, 586, 592, 595, 715, 718, 408, 723, 380,
- /* 140 */ 634, 677, 787, 798, 144, 529, 588, -14, 0, 17,
- /* 150 */ 244, 155, 298, 155, 155, 418, 372, 477, 490, 494,
- /* 160 */ 509, 526, 590, 465, 494, 730, 773, 743, 833, 792,
- /* 170 */ 854, 860, 155, 792, 864, 885, 848, 859, 863, 891,
- /* 180 */ 907, 155, 914, 917, 155, 927, 852, 886, 941, 961,
- /* 190 */ 155, 984, 990, 991, 155, 992, 995, 996, 155, 155,
- /* 200 */ 999, 1002, 1003, 1004, 1005, 1006, 962, 964, 925, 929,
- /* 210 */ 933, 987, 988, 989, 985, 993, 997, 998, 969, 970,
- /* 220 */ 1021, 1022, 1024, 1026, 1020, 1000, 982, 983, 1001, 1007,
- /* 230 */ 1008, 1023, 966, 1034, 1035, 1009, 1010, 1012, 1013, 1025,
- /* 240 */ 1027, 1015, 1028, 1029, 1032, 1031, 981, 1048, 1011, 1049,
- /* 250 */ 1033, 1039, 1040, 1043, 1045, 1069, 1070, 1074, 1071, 1075,
- /* 260 */ 1078, 1079, 1014, 1016, 1081, 1082, 1044, 1051, 1019, 1042,
- /* 270 */ 1086, 1053, 1056, 1057, 1062, 1047, 1063, 1036, 1090, 1017,
- /* 280 */ 1018, 1100, 1105, 1037, 1099, 1084, 1085, 1038, 1041, 1046,
+ /* 90 */ 963, 965, 971, 975, 978, 980, 412, 412, 412, 412,
+ /* 100 */ 20, 412, 412, 23, 34, 334, 475, 552, 593, 594,
+ /* 110 */ 585, 212, 412, 289, 412, 412, 412, 412, 135, -164,
+ /* 120 */ -115, 164, 407, 407, 350, 141, 51, 163, 596, -90,
+ /* 130 */ 436, 218, 765, 438, 586, 592, 595, 715, 718, 408,
+ /* 140 */ 754, 380, 634, 677, 798, 801, 144, 529, 588, 49,
+ /* 150 */ 176, 244, 264, 329, 457, 329, 329, 451, 477, 494,
+ /* 160 */ 507, 509, 528, 590, 730, 642, 509, 743, 839, 864,
+ /* 170 */ 879, 834, 894, 900, 329, 834, 907, 914, 826, 886,
+ /* 180 */ 919, 927, 937, 329, 951, 961, 329, 972, 897, 898,
+ /* 190 */ 989, 990, 329, 991, 992, 995, 329, 996, 999, 1004,
+ /* 200 */ 329, 329, 1005, 1006, 1007, 1008, 1009, 1010, 966, 967,
+ /* 210 */ 997, 933, 938, 940, 993, 998, 1000, 984, 1001, 1003,
+ /* 220 */ 1002, 1014, 1011, 974, 977, 1023, 1030, 1031, 1032, 1026,
+ /* 230 */ 1012, 988, 1013, 1015, 1017, 1018, 968, 1039, 1040, 1019,
+ /* 240 */ 1020, 1022, 1024, 1025, 1027, 1021, 1033, 1034, 1035, 1036,
+ /* 250 */ 979, 983, 1038, 1041, 1042, 1044, 1045, 1069, 1072, 1071,
+ /* 260 */ 1075, 1077, 1078, 1080, 1028, 1037, 1083, 1084, 1051, 1053,
+ /* 270 */ 1043, 1046, 1089, 1055, 1056, 1057, 1061, 1052, 1063, 1047,
+ /* 280 */ 1093, 1048, 1049, 1098, 1099, 1050, 1094, 1081, 1085, 1062,
+ /* 290 */ 1054, 1064,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 592, 816, 897, 707, 897, 816, 897, 897, 843, 711,
- /* 10 */ 872, 814, 897, 897, 897, 897, 789, 897, 843, 897,
- /* 20 */ 623, 843, 843, 740, 897, 897, 897, 897, 897, 897,
- /* 30 */ 897, 897, 741, 897, 818, 813, 809, 811, 810, 817,
- /* 40 */ 742, 731, 738, 745, 723, 856, 747, 748, 754, 755,
- /* 50 */ 873, 871, 777, 776, 795, 897, 897, 897, 897, 897,
- /* 60 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 70 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 80 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 90 */ 897, 897, 897, 897, 897, 779, 800, 778, 788, 616,
- /* 100 */ 780, 781, 676, 611, 897, 897, 897, 897, 897, 897,
- /* 110 */ 897, 782, 897, 783, 796, 797, 897, 897, 897, 897,
- /* 120 */ 897, 897, 592, 707, 897, 707, 897, 897, 897, 897,
- /* 130 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 140 */ 897, 897, 897, 897, 701, 711, 890, 897, 897, 667,
- /* 150 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 160 */ 599, 597, 897, 699, 897, 897, 625, 897, 897, 709,
- /* 170 */ 897, 897, 714, 715, 897, 897, 897, 897, 897, 897,
- /* 180 */ 897, 613, 897, 897, 688, 897, 849, 897, 897, 897,
- /* 190 */ 863, 897, 897, 897, 861, 897, 897, 897, 690, 750,
- /* 200 */ 830, 897, 876, 878, 897, 897, 699, 708, 897, 897,
- /* 210 */ 812, 734, 734, 734, 646, 734, 734, 649, 744, 744,
- /* 220 */ 596, 596, 596, 596, 666, 897, 744, 735, 737, 727,
- /* 230 */ 739, 897, 897, 716, 716, 724, 726, 724, 726, 678,
- /* 240 */ 678, 663, 678, 649, 678, 822, 827, 897, 827, 897,
- /* 250 */ 663, 678, 678, 678, 822, 608, 716, 608, 716, 608,
- /* 260 */ 716, 716, 853, 855, 608, 716, 680, 680, 756, 744,
- /* 270 */ 716, 687, 687, 687, 687, 744, 680, 756, 716, 875,
- /* 280 */ 875, 716, 716, 883, 633, 651, 651, 858, 890, 895,
- /* 290 */ 897, 897, 897, 897, 763, 897, 897, 897, 897, 897,
- /* 300 */ 897, 897, 897, 897, 897, 897, 897, 897, 836, 897,
- /* 310 */ 897, 897, 897, 768, 764, 897, 765, 897, 693, 897,
- /* 320 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 815,
- /* 330 */ 897, 728, 897, 736, 897, 897, 897, 897, 897, 897,
- /* 340 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 350 */ 897, 897, 897, 897, 897, 897, 897, 897, 851, 852,
- /* 360 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 370 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897,
- /* 380 */ 897, 897, 882, 897, 897, 885, 593, 897, 587, 590,
- /* 390 */ 589, 591, 595, 598, 620, 621, 622, 600, 601, 602,
- /* 400 */ 603, 604, 605, 606, 612, 614, 632, 634, 618, 636,
- /* 410 */ 697, 698, 760, 691, 692, 696, 771, 762, 766, 767,
- /* 420 */ 769, 770, 784, 785, 787, 793, 799, 802, 786, 791,
- /* 430 */ 792, 794, 798, 801, 694, 695, 805, 619, 626, 627,
- /* 440 */ 630, 631, 839, 841, 840, 842, 629, 628, 772, 775,
- /* 450 */ 807, 808, 864, 865, 866, 867, 868, 803, 717, 806,
- /* 460 */ 790, 729, 732, 733, 730, 700, 710, 719, 720, 721,
- /* 470 */ 722, 705, 706, 712, 725, 758, 759, 713, 702, 703,
- /* 480 */ 704, 804, 761, 773, 774, 637, 638, 768, 639, 640,
- /* 490 */ 641, 679, 682, 683, 684, 642, 661, 664, 665, 643,
- /* 500 */ 650, 644, 645, 652, 653, 654, 657, 658, 659, 660,
- /* 510 */ 655, 656, 823, 824, 828, 826, 825, 647, 648, 662,
- /* 520 */ 635, 624, 617, 668, 671, 672, 673, 674, 675, 677,
- /* 530 */ 669, 670, 615, 607, 609, 718, 845, 854, 850, 846,
- /* 540 */ 847, 848, 610, 819, 820, 681, 752, 753, 844, 857,
- /* 550 */ 859, 757, 860, 862, 887, 685, 686, 689, 829, 869,
- /* 560 */ 743, 746, 749, 751, 831, 832, 833, 834, 837, 838,
- /* 570 */ 835, 870, 874, 877, 879, 880, 881, 884, 886, 891,
- /* 580 */ 892, 893, 896, 894, 594, 588,
+ /* 0 */ 595, 820, 902, 710, 902, 820, 902, 902, 848, 714,
+ /* 10 */ 877, 818, 902, 902, 902, 902, 792, 902, 848, 902,
+ /* 20 */ 626, 848, 848, 743, 902, 902, 902, 902, 902, 902,
+ /* 30 */ 902, 902, 744, 902, 822, 817, 813, 815, 814, 821,
+ /* 40 */ 745, 734, 741, 748, 726, 861, 750, 751, 757, 758,
+ /* 50 */ 878, 876, 780, 779, 798, 902, 902, 902, 902, 902,
+ /* 60 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 70 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 80 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 90 */ 902, 902, 902, 902, 902, 902, 782, 804, 781, 791,
+ /* 100 */ 619, 783, 784, 679, 614, 902, 902, 902, 902, 902,
+ /* 110 */ 902, 902, 785, 902, 786, 799, 800, 801, 902, 902,
+ /* 120 */ 902, 902, 902, 902, 595, 710, 902, 710, 902, 902,
+ /* 130 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 140 */ 902, 902, 902, 902, 902, 902, 704, 714, 895, 902,
+ /* 150 */ 902, 670, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 160 */ 902, 902, 602, 600, 902, 702, 902, 902, 628, 902,
+ /* 170 */ 902, 712, 902, 902, 717, 718, 902, 902, 902, 902,
+ /* 180 */ 902, 902, 902, 616, 902, 902, 691, 902, 854, 902,
+ /* 190 */ 902, 902, 868, 902, 902, 902, 866, 902, 902, 902,
+ /* 200 */ 693, 753, 834, 902, 881, 883, 902, 902, 702, 711,
+ /* 210 */ 902, 902, 902, 816, 737, 737, 737, 649, 737, 902,
+ /* 220 */ 737, 902, 652, 747, 747, 599, 599, 599, 599, 669,
+ /* 230 */ 902, 747, 738, 740, 730, 742, 902, 719, 719, 727,
+ /* 240 */ 729, 727, 729, 681, 681, 666, 681, 652, 681, 826,
+ /* 250 */ 831, 831, 666, 681, 681, 681, 826, 611, 719, 611,
+ /* 260 */ 719, 611, 719, 719, 858, 860, 611, 719, 683, 683,
+ /* 270 */ 759, 747, 719, 690, 690, 690, 690, 747, 683, 759,
+ /* 280 */ 719, 880, 880, 719, 719, 888, 636, 654, 654, 863,
+ /* 290 */ 895, 900, 902, 902, 902, 902, 766, 902, 902, 902,
+ /* 300 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 310 */ 841, 902, 902, 902, 902, 771, 767, 902, 768, 902,
+ /* 320 */ 696, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 330 */ 902, 819, 902, 731, 902, 739, 902, 902, 902, 902,
+ /* 340 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 350 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 360 */ 856, 857, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 370 */ 902, 902, 902, 902, 902, 902, 902, 902, 902, 902,
+ /* 380 */ 902, 902, 902, 902, 887, 902, 902, 890, 596, 902,
+ /* 390 */ 590, 593, 592, 594, 598, 601, 623, 624, 625, 603,
+ /* 400 */ 604, 605, 606, 607, 608, 609, 615, 617, 635, 637,
+ /* 410 */ 621, 639, 700, 701, 763, 694, 695, 699, 622, 774,
+ /* 420 */ 765, 769, 770, 772, 773, 787, 788, 790, 796, 803,
+ /* 430 */ 806, 789, 794, 795, 797, 802, 805, 697, 698, 809,
+ /* 440 */ 629, 630, 633, 634, 844, 846, 845, 847, 632, 631,
+ /* 450 */ 775, 778, 811, 812, 869, 870, 871, 872, 873, 807,
+ /* 460 */ 720, 810, 793, 732, 735, 736, 733, 703, 713, 722,
+ /* 470 */ 723, 724, 725, 708, 709, 715, 728, 761, 762, 716,
+ /* 480 */ 705, 706, 707, 808, 764, 776, 777, 640, 641, 771,
+ /* 490 */ 642, 643, 644, 682, 685, 686, 687, 645, 664, 667,
+ /* 500 */ 668, 646, 653, 647, 648, 655, 656, 657, 660, 661,
+ /* 510 */ 662, 663, 658, 659, 827, 828, 832, 830, 829, 650,
+ /* 520 */ 651, 665, 638, 627, 620, 671, 674, 675, 676, 677,
+ /* 530 */ 678, 680, 672, 673, 618, 610, 612, 721, 850, 859,
+ /* 540 */ 855, 851, 852, 853, 613, 823, 824, 684, 755, 756,
+ /* 550 */ 849, 862, 864, 760, 865, 867, 892, 688, 689, 692,
+ /* 560 */ 833, 874, 746, 749, 752, 754, 835, 836, 837, 838,
+ /* 570 */ 839, 842, 843, 840, 875, 879, 882, 884, 885, 886,
+ /* 580 */ 889, 891, 896, 897, 898, 901, 899, 597, 591,
};
#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
@@ -596,7 +595,7 @@ static const YYACTIONTYPE yy_default[] = {
**
** %fallback ID X Y Z.
**
-** appears in the grammer, then ID becomes a fallback token for X, Y,
+** appears in the grammar, then ID becomes a fallback token for X, Y,
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
** but it does not parse, the type of the token is changed to ID and
** the parse is retried before an error is thrown.
@@ -757,11 +756,11 @@ static const YYCODETYPE yyFallback[] = {
** It is sometimes called the "minor" token.
*/
struct yyStackEntry {
- int stateno; /* The state-number */
- int major; /* The major token value. This is the code
- ** number for the token at this stack level */
- YYMINORTYPE minor; /* The user-supplied minor token value. This
- ** is the value of the token */
+ YYACTIONTYPE stateno; /* The state-number */
+ YYCODETYPE major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
};
typedef struct yyStackEntry yyStackEntry;
@@ -769,6 +768,9 @@ typedef struct yyStackEntry yyStackEntry;
** the following structure */
struct yyParser {
int yyidx; /* Index of top element in stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+ int yyidxMax; /* Maximum value of yyidx */
+#endif
int yyerrcnt; /* Shifts left before out of the error */
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
#if YYSTACKDEPTH<=0
@@ -947,7 +949,7 @@ static const char *const yyRuleName[] = {
/* 59 */ "ccons ::= CHECK LP expr RP",
/* 60 */ "ccons ::= REFERENCES nm idxlist_opt refargs",
/* 61 */ "ccons ::= defer_subclause",
- /* 62 */ "ccons ::= COLLATE id",
+ /* 62 */ "ccons ::= COLLATE ids",
/* 63 */ "autoinc ::=",
/* 64 */ "autoinc ::= AUTOINCR",
/* 65 */ "refargs ::=",
@@ -1070,7 +1072,7 @@ static const char *const yyRuleName[] = {
/* 182 */ "term ::= STRING",
/* 183 */ "expr ::= REGISTER",
/* 184 */ "expr ::= VARIABLE",
- /* 185 */ "expr ::= expr COLLATE id",
+ /* 185 */ "expr ::= expr COLLATE ids",
/* 186 */ "expr ::= CAST LP expr AS typetoken RP",
/* 187 */ "expr ::= ID LP distinct exprlist RP",
/* 188 */ "expr ::= ID LP STAR RP",
@@ -1094,108 +1096,110 @@ static const char *const yyRuleName[] = {
/* 206 */ "expr ::= expr IS NULL",
/* 207 */ "expr ::= expr NOT NULL",
/* 208 */ "expr ::= expr IS NOT NULL",
- /* 209 */ "expr ::= NOT|BITNOT expr",
- /* 210 */ "expr ::= MINUS expr",
- /* 211 */ "expr ::= PLUS expr",
- /* 212 */ "between_op ::= BETWEEN",
- /* 213 */ "between_op ::= NOT BETWEEN",
- /* 214 */ "expr ::= expr between_op expr AND expr",
- /* 215 */ "in_op ::= IN",
- /* 216 */ "in_op ::= NOT IN",
- /* 217 */ "expr ::= expr in_op LP exprlist RP",
- /* 218 */ "expr ::= LP select RP",
- /* 219 */ "expr ::= expr in_op LP select RP",
- /* 220 */ "expr ::= expr in_op nm dbnm",
- /* 221 */ "expr ::= EXISTS LP select RP",
- /* 222 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 223 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 224 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 225 */ "case_else ::= ELSE expr",
- /* 226 */ "case_else ::=",
- /* 227 */ "case_operand ::= expr",
- /* 228 */ "case_operand ::=",
- /* 229 */ "exprlist ::= nexprlist",
- /* 230 */ "exprlist ::=",
- /* 231 */ "nexprlist ::= nexprlist COMMA expr",
- /* 232 */ "nexprlist ::= expr",
- /* 233 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP",
- /* 234 */ "uniqueflag ::= UNIQUE",
- /* 235 */ "uniqueflag ::=",
- /* 236 */ "idxlist_opt ::=",
- /* 237 */ "idxlist_opt ::= LP idxlist RP",
- /* 238 */ "idxlist ::= idxlist COMMA idxitem collate sortorder",
- /* 239 */ "idxlist ::= idxitem collate sortorder",
- /* 240 */ "idxitem ::= nm",
- /* 241 */ "collate ::=",
- /* 242 */ "collate ::= COLLATE id",
- /* 243 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 244 */ "cmd ::= VACUUM",
- /* 245 */ "cmd ::= VACUUM nm",
- /* 246 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 247 */ "cmd ::= PRAGMA nm dbnm EQ ON",
- /* 248 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 249 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 250 */ "cmd ::= PRAGMA nm dbnm",
- /* 251 */ "nmnum ::= plus_num",
- /* 252 */ "nmnum ::= nm",
- /* 253 */ "plus_num ::= plus_opt number",
- /* 254 */ "minus_num ::= MINUS number",
- /* 255 */ "number ::= INTEGER|FLOAT",
- /* 256 */ "plus_opt ::= PLUS",
- /* 257 */ "plus_opt ::=",
- /* 258 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END",
- /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 260 */ "trigger_time ::= BEFORE",
- /* 261 */ "trigger_time ::= AFTER",
- /* 262 */ "trigger_time ::= INSTEAD OF",
- /* 263 */ "trigger_time ::=",
- /* 264 */ "trigger_event ::= DELETE|INSERT",
- /* 265 */ "trigger_event ::= UPDATE",
- /* 266 */ "trigger_event ::= UPDATE OF inscollist",
- /* 267 */ "foreach_clause ::=",
- /* 268 */ "foreach_clause ::= FOR EACH ROW",
- /* 269 */ "when_clause ::=",
- /* 270 */ "when_clause ::= WHEN expr",
- /* 271 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 272 */ "trigger_cmd_list ::=",
- /* 273 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt",
- /* 274 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP",
- /* 275 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select",
- /* 276 */ "trigger_cmd ::= DELETE FROM nm where_opt",
- /* 277 */ "trigger_cmd ::= select",
- /* 278 */ "expr ::= RAISE LP IGNORE RP",
- /* 279 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 280 */ "raisetype ::= ROLLBACK",
- /* 281 */ "raisetype ::= ABORT",
- /* 282 */ "raisetype ::= FAIL",
- /* 283 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 284 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 285 */ "cmd ::= DETACH database_kw_opt expr",
- /* 286 */ "key_opt ::=",
- /* 287 */ "key_opt ::= KEY expr",
- /* 288 */ "database_kw_opt ::= DATABASE",
- /* 289 */ "database_kw_opt ::=",
- /* 290 */ "cmd ::= REINDEX",
- /* 291 */ "cmd ::= REINDEX nm dbnm",
- /* 292 */ "cmd ::= ANALYZE",
- /* 293 */ "cmd ::= ANALYZE nm dbnm",
- /* 294 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 295 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
- /* 296 */ "add_column_fullname ::= fullname",
- /* 297 */ "kwcolumn_opt ::=",
- /* 298 */ "kwcolumn_opt ::= COLUMNKW",
- /* 299 */ "cmd ::= create_vtab",
- /* 300 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 301 */ "create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm",
- /* 302 */ "vtabarglist ::= vtabarg",
- /* 303 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 304 */ "vtabarg ::=",
- /* 305 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 306 */ "vtabargtoken ::= ANY",
- /* 307 */ "vtabargtoken ::= lp anylist RP",
- /* 308 */ "lp ::= LP",
- /* 309 */ "anylist ::=",
- /* 310 */ "anylist ::= anylist ANY",
+ /* 209 */ "expr ::= NOT expr",
+ /* 210 */ "expr ::= BITNOT expr",
+ /* 211 */ "expr ::= MINUS expr",
+ /* 212 */ "expr ::= PLUS expr",
+ /* 213 */ "between_op ::= BETWEEN",
+ /* 214 */ "between_op ::= NOT BETWEEN",
+ /* 215 */ "expr ::= expr between_op expr AND expr",
+ /* 216 */ "in_op ::= IN",
+ /* 217 */ "in_op ::= NOT IN",
+ /* 218 */ "expr ::= expr in_op LP exprlist RP",
+ /* 219 */ "expr ::= LP select RP",
+ /* 220 */ "expr ::= expr in_op LP select RP",
+ /* 221 */ "expr ::= expr in_op nm dbnm",
+ /* 222 */ "expr ::= EXISTS LP select RP",
+ /* 223 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 224 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 225 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 226 */ "case_else ::= ELSE expr",
+ /* 227 */ "case_else ::=",
+ /* 228 */ "case_operand ::= expr",
+ /* 229 */ "case_operand ::=",
+ /* 230 */ "exprlist ::= nexprlist",
+ /* 231 */ "exprlist ::=",
+ /* 232 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 233 */ "nexprlist ::= expr",
+ /* 234 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP",
+ /* 235 */ "uniqueflag ::= UNIQUE",
+ /* 236 */ "uniqueflag ::=",
+ /* 237 */ "idxlist_opt ::=",
+ /* 238 */ "idxlist_opt ::= LP idxlist RP",
+ /* 239 */ "idxlist ::= idxlist COMMA idxitem collate sortorder",
+ /* 240 */ "idxlist ::= idxitem collate sortorder",
+ /* 241 */ "idxitem ::= nm",
+ /* 242 */ "collate ::=",
+ /* 243 */ "collate ::= COLLATE ids",
+ /* 244 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 245 */ "cmd ::= VACUUM",
+ /* 246 */ "cmd ::= VACUUM nm",
+ /* 247 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 248 */ "cmd ::= PRAGMA nm dbnm EQ ON",
+ /* 249 */ "cmd ::= PRAGMA nm dbnm EQ DELETE",
+ /* 250 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 252 */ "cmd ::= PRAGMA nm dbnm",
+ /* 253 */ "nmnum ::= plus_num",
+ /* 254 */ "nmnum ::= nm",
+ /* 255 */ "plus_num ::= plus_opt number",
+ /* 256 */ "minus_num ::= MINUS number",
+ /* 257 */ "number ::= INTEGER|FLOAT",
+ /* 258 */ "plus_opt ::= PLUS",
+ /* 259 */ "plus_opt ::=",
+ /* 260 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END",
+ /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 262 */ "trigger_time ::= BEFORE",
+ /* 263 */ "trigger_time ::= AFTER",
+ /* 264 */ "trigger_time ::= INSTEAD OF",
+ /* 265 */ "trigger_time ::=",
+ /* 266 */ "trigger_event ::= DELETE|INSERT",
+ /* 267 */ "trigger_event ::= UPDATE",
+ /* 268 */ "trigger_event ::= UPDATE OF inscollist",
+ /* 269 */ "foreach_clause ::=",
+ /* 270 */ "foreach_clause ::= FOR EACH ROW",
+ /* 271 */ "when_clause ::=",
+ /* 272 */ "when_clause ::= WHEN expr",
+ /* 273 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 274 */ "trigger_cmd_list ::=",
+ /* 275 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt",
+ /* 276 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP",
+ /* 277 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select",
+ /* 278 */ "trigger_cmd ::= DELETE FROM nm where_opt",
+ /* 279 */ "trigger_cmd ::= select",
+ /* 280 */ "expr ::= RAISE LP IGNORE RP",
+ /* 281 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 282 */ "raisetype ::= ROLLBACK",
+ /* 283 */ "raisetype ::= ABORT",
+ /* 284 */ "raisetype ::= FAIL",
+ /* 285 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 286 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 287 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 288 */ "key_opt ::=",
+ /* 289 */ "key_opt ::= KEY expr",
+ /* 290 */ "database_kw_opt ::= DATABASE",
+ /* 291 */ "database_kw_opt ::=",
+ /* 292 */ "cmd ::= REINDEX",
+ /* 293 */ "cmd ::= REINDEX nm dbnm",
+ /* 294 */ "cmd ::= ANALYZE",
+ /* 295 */ "cmd ::= ANALYZE nm dbnm",
+ /* 296 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 297 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
+ /* 298 */ "add_column_fullname ::= fullname",
+ /* 299 */ "kwcolumn_opt ::=",
+ /* 300 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 301 */ "cmd ::= create_vtab",
+ /* 302 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 303 */ "create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm",
+ /* 304 */ "vtabarglist ::= vtabarg",
+ /* 305 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 306 */ "vtabarg ::=",
+ /* 307 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 308 */ "vtabargtoken ::= ANY",
+ /* 309 */ "vtabargtoken ::= lp anylist RP",
+ /* 310 */ "lp ::= LP",
+ /* 311 */ "anylist ::=",
+ /* 312 */ "anylist ::= anylist ANY",
};
#endif /* NDEBUG */
@@ -1240,6 +1244,9 @@ void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){
pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
if( pParser ){
pParser->yyidx = -1;
+#ifdef YYTRACKMAXSTACKDEPTH
+ pParser->yyidxMax = 0;
+#endif
#if YYSTACKDEPTH<=0
yyGrowStack(pParser);
#endif
@@ -1252,7 +1259,12 @@ void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){
** "yymajor" is the symbol code, and "yypminor" is a pointer to
** the value.
*/
-static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+static void yy_destructor(
+ yyParser *yypParser, /* The parser */
+ YYCODETYPE yymajor, /* Type code for object to destroy */
+ YYMINORTYPE *yypminor /* The object to be destroyed */
+){
+ sqlite3ParserARG_FETCH;
switch( yymajor ){
/* Here is inserted the actions which take place when a
** terminal or non-terminal is destroyed. This can happen
@@ -1264,73 +1276,83 @@ static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
** which appear on the RHS of the rule, but which are not used
** inside the C code.
*/
- case 155:
- case 189:
- case 206:
+ case 155: /* select */
+ case 189: /* oneselect */
+ case 206: /* seltablist_paren */
+{
#line 374 "parse.y"
-{sqlite3SelectDelete((yypminor->yy219));}
-#line 1274 "parse.c"
- break;
- case 169:
- case 170:
- case 194:
- case 196:
- case 204:
- case 210:
- case 218:
- case 221:
- case 223:
- case 235:
-#line 627 "parse.y"
-{sqlite3ExprDelete((yypminor->yy172));}
-#line 1288 "parse.c"
+sqlite3SelectDelete(pParse->db, (yypminor->yy219));
+#line 1289 "parse.c"
+}
break;
- case 174:
- case 182:
- case 192:
- case 195:
- case 197:
- case 199:
- case 209:
- case 211:
- case 212:
- case 215:
- case 216:
- case 222:
-#line 876 "parse.y"
-{sqlite3ExprListDelete((yypminor->yy174));}
-#line 1304 "parse.c"
+ case 169: /* term */
+ case 170: /* expr */
+ case 194: /* where_opt */
+ case 196: /* having_opt */
+ case 204: /* on_opt */
+ case 210: /* sortitem */
+ case 218: /* escape */
+ case 221: /* case_operand */
+ case 223: /* case_else */
+ case 235: /* when_clause */
+ case 238: /* key_opt */
+{
+#line 634 "parse.y"
+sqlite3ExprDelete(pParse->db, (yypminor->yy172));
+#line 1306 "parse.c"
+}
break;
- case 188:
- case 193:
- case 201:
- case 202:
-#line 489 "parse.y"
-{sqlite3SrcListDelete((yypminor->yy373));}
-#line 1312 "parse.c"
+ case 174: /* idxlist_opt */
+ case 182: /* idxlist */
+ case 192: /* selcollist */
+ case 195: /* groupby_opt */
+ case 197: /* orderby_opt */
+ case 199: /* sclp */
+ case 209: /* sortlist */
+ case 211: /* nexprlist */
+ case 212: /* setlist */
+ case 215: /* itemlist */
+ case 216: /* exprlist */
+ case 222: /* case_exprlist */
+{
+#line 892 "parse.y"
+sqlite3ExprListDelete(pParse->db, (yypminor->yy174));
+#line 1324 "parse.c"
+}
break;
- case 205:
- case 208:
- case 214:
-#line 506 "parse.y"
-{sqlite3IdListDelete((yypminor->yy432));}
-#line 1319 "parse.c"
+ case 188: /* fullname */
+ case 193: /* from */
+ case 201: /* seltablist */
+ case 202: /* stl_prefix */
+{
+#line 491 "parse.y"
+sqlite3SrcListDelete(pParse->db, (yypminor->yy373));
+#line 1334 "parse.c"
+}
break;
- case 231:
- case 236:
-#line 979 "parse.y"
-{sqlite3DeleteTriggerStep((yypminor->yy243));}
-#line 1325 "parse.c"
+ case 205: /* using_opt */
+ case 208: /* inscollist */
+ case 214: /* inscollist_opt */
+{
+#line 508 "parse.y"
+sqlite3IdListDelete(pParse->db, (yypminor->yy432));
+#line 1343 "parse.c"
+}
break;
- case 233:
-#line 965 "parse.y"
-{sqlite3IdListDelete((yypminor->yy370).b);}
-#line 1330 "parse.c"
+ case 231: /* trigger_cmd_list */
+ case 236: /* trigger_cmd */
+{
+#line 998 "parse.y"
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy243));
+#line 1351 "parse.c"
+}
break;
- case 238:
-#line 1052 "parse.y"
-{sqlite3ExprDelete((yypminor->yy386));}
-#line 1335 "parse.c"
+ case 233: /* trigger_event */
+{
+#line 984 "parse.y"
+sqlite3IdListDelete(pParse->db, (yypminor->yy370).b);
+#line 1358 "parse.c"
+}
break;
default: break; /* If no destructor action specified: do nothing */
}
@@ -1357,7 +1379,7 @@ static int yy_pop_parser_stack(yyParser *pParser){
}
#endif
yymajor = yytos->major;
- yy_destructor( yymajor, &yytos->minor);
+ yy_destructor(pParser, yymajor, &yytos->minor);
pParser->yyidx--;
return yymajor;
}
@@ -1388,6 +1410,16 @@ void sqlite3ParserFree(
}
/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int sqlite3ParserStackPeak(void *p){
+ yyParser *pParser = (yyParser*)p;
+ return pParser->yyidxMax;
+}
+#endif
+
+/*
** Find the appropriate action for a parser given the terminal
** look-ahead token iLookAhead.
**
@@ -1405,9 +1437,7 @@ static int yy_find_shift_action(
if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
return yy_default[stateno];
}
- if( iLookAhead==YYNOCODE ){
- return YY_NO_ACTION;
- }
+ assert( iLookAhead!=YYNOCODE );
i += iLookAhead;
if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
if( iLookAhead>0 ){
@@ -1458,21 +1488,26 @@ static int yy_find_reduce_action(
YYCODETYPE iLookAhead /* The look-ahead token */
){
int i;
- /* int stateno = pParser->yystack[pParser->yyidx].stateno; */
-
- if( stateno>YY_REDUCE_MAX ||
- (i = yy_reduce_ofst[stateno])==YY_REDUCE_USE_DFLT ){
+#ifdef YYERRORSYMBOL
+ if( stateno>YY_REDUCE_MAX ){
return yy_default[stateno];
}
- if( iLookAhead==YYNOCODE ){
- return YY_NO_ACTION;
- }
+#else
+ assert( stateno<=YY_REDUCE_MAX );
+#endif
+ i = yy_reduce_ofst[stateno];
+ assert( i!=YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=YYNOCODE );
i += iLookAhead;
+#ifdef YYERRORSYMBOL
if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
return yy_default[stateno];
- }else{
- return yy_action[i];
}
+#else
+ assert( i>=0 && i<YY_SZ_ACTTAB );
+ assert( yy_lookahead[i]==iLookAhead );
+#endif
+ return yy_action[i];
}
/*
@@ -1489,11 +1524,11 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
-#line 44 "parse.y"
+#line 39 "parse.y"
sqlite3ErrorMsg(pParse, "parser stack overflow");
pParse->parseError = 1;
-#line 1499 "parse.c"
+#line 1536 "parse.c"
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
}
@@ -1504,10 +1539,15 @@ static void yy_shift(
yyParser *yypParser, /* The parser to be shifted */
int yyNewState, /* The new state to shift in */
int yyMajor, /* The major token to shift in */
- YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */
){
yyStackEntry *yytos;
yypParser->yyidx++;
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( yypParser->yyidx>yypParser->yyidxMax ){
+ yypParser->yyidxMax = yypParser->yyidx;
+ }
+#endif
#if YYSTACKDEPTH>0
if( yypParser->yyidx>=YYSTACKDEPTH ){
yyStackOverflow(yypParser, yypMinor);
@@ -1757,6 +1797,7 @@ static const struct {
{ 170, 2 },
{ 170, 2 },
{ 170, 2 },
+ { 170, 2 },
{ 219, 1 },
{ 219, 2 },
{ 170, 5 },
@@ -1794,6 +1835,7 @@ static const struct {
{ 143, 5 },
{ 143, 5 },
{ 143, 5 },
+ { 143, 5 },
{ 143, 6 },
{ 143, 3 },
{ 227, 1 },
@@ -1897,7 +1939,8 @@ static void yy_reduce(
** from wireshark this week. Clearly they are stressing Lemon in ways
** that it has not been previously stressed... (SQLite ticket #2172)
*/
- memset(&yygotominor, 0, sizeof(yygotominor));
+ /*memset(&yygotominor, 0, sizeof(yygotominor));*/
+ yygotominor = yyzerominor;
switch( yyruleno ){
@@ -1909,1179 +1952,1179 @@ static void yy_reduce(
** #line <lineno> <thisfile>
** break;
*/
- case 0:
- case 1:
- case 2:
- case 4:
- case 5:
- case 10:
- case 11:
- case 12:
- case 20:
- case 28:
- case 29:
- case 37:
- case 44:
- case 45:
- case 46:
- case 47:
- case 48:
- case 49:
- case 55:
- case 82:
- case 83:
- case 84:
- case 85:
- case 256:
- case 257:
- case 267:
- case 268:
- case 288:
- case 289:
- case 297:
- case 298:
- case 302:
- case 303:
- case 305:
- case 309:
-#line 97 "parse.y"
+ case 0: /* input ::= cmdlist */
+ case 1: /* cmdlist ::= cmdlist ecmd */
+ case 2: /* cmdlist ::= ecmd */
+ case 4: /* ecmd ::= SEMI */
+ case 5: /* ecmd ::= explain cmdx SEMI */
+ case 10: /* trans_opt ::= */
+ case 11: /* trans_opt ::= TRANSACTION */
+ case 12: /* trans_opt ::= TRANSACTION nm */
+ case 20: /* cmd ::= create_table create_table_args */
+ case 28: /* columnlist ::= columnlist COMMA column */
+ case 29: /* columnlist ::= column */
+ case 37: /* type ::= */
+ case 44: /* signed ::= plus_num */
+ case 45: /* signed ::= minus_num */
+ case 46: /* carglist ::= carglist carg */
+ case 47: /* carglist ::= */
+ case 48: /* carg ::= CONSTRAINT nm ccons */
+ case 49: /* carg ::= ccons */
+ case 55: /* ccons ::= NULL onconf */
+ case 82: /* conslist ::= conslist COMMA tcons */
+ case 83: /* conslist ::= conslist tcons */
+ case 84: /* conslist ::= tcons */
+ case 85: /* tcons ::= CONSTRAINT nm */
+ case 258: /* plus_opt ::= PLUS */
+ case 259: /* plus_opt ::= */
+ case 269: /* foreach_clause ::= */
+ case 270: /* foreach_clause ::= FOR EACH ROW */
+ case 290: /* database_kw_opt ::= DATABASE */
+ case 291: /* database_kw_opt ::= */
+ case 299: /* kwcolumn_opt ::= */
+ case 300: /* kwcolumn_opt ::= COLUMNKW */
+ case 304: /* vtabarglist ::= vtabarg */
+ case 305: /* vtabarglist ::= vtabarglist COMMA vtabarg */
+ case 307: /* vtabarg ::= vtabarg vtabargtoken */
+ case 311: /* anylist ::= */
+#line 91 "parse.y"
{
}
-#line 1953 "parse.c"
+#line 1998 "parse.c"
break;
- case 3:
-#line 100 "parse.y"
+ case 3: /* cmdx ::= cmd */
+#line 94 "parse.y"
{ sqlite3FinishCoding(pParse); }
-#line 1958 "parse.c"
+#line 2003 "parse.c"
break;
- case 6:
-#line 103 "parse.y"
+ case 6: /* explain ::= */
+#line 97 "parse.y"
{ sqlite3BeginParse(pParse, 0); }
-#line 1963 "parse.c"
+#line 2008 "parse.c"
break;
- case 7:
-#line 105 "parse.y"
+ case 7: /* explain ::= EXPLAIN */
+#line 99 "parse.y"
{ sqlite3BeginParse(pParse, 1); }
-#line 1968 "parse.c"
+#line 2013 "parse.c"
break;
- case 8:
-#line 106 "parse.y"
+ case 8: /* explain ::= EXPLAIN QUERY PLAN */
+#line 100 "parse.y"
{ sqlite3BeginParse(pParse, 2); }
-#line 1973 "parse.c"
+#line 2018 "parse.c"
break;
- case 9:
-#line 112 "parse.y"
+ case 9: /* cmd ::= BEGIN transtype trans_opt */
+#line 106 "parse.y"
{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy46);}
-#line 1978 "parse.c"
+#line 2023 "parse.c"
break;
- case 13:
+ case 13: /* transtype ::= */
+#line 112 "parse.y"
+{yygotominor.yy46 = TK_IMMEDIATE;}
+#line 2028 "parse.c"
+ break;
+ case 14: /* transtype ::= DEFERRED */
+ case 15: /* transtype ::= IMMEDIATE */
+ case 16: /* transtype ::= EXCLUSIVE */
+ case 107: /* multiselect_op ::= UNION */
+ case 109: /* multiselect_op ::= EXCEPT|INTERSECT */
#line 117 "parse.y"
-{yygotominor.yy46 = TK_DEFERRED;}
-#line 1983 "parse.c"
- break;
- case 14:
- case 15:
- case 16:
- case 107:
- case 109:
-#line 118 "parse.y"
{yygotominor.yy46 = yymsp[0].major;}
-#line 1992 "parse.c"
+#line 2037 "parse.c"
break;
- case 17:
- case 18:
-#line 121 "parse.y"
+ case 17: /* cmd ::= COMMIT trans_opt */
+ case 18: /* cmd ::= END trans_opt */
+#line 120 "parse.y"
{sqlite3CommitTransaction(pParse);}
-#line 1998 "parse.c"
+#line 2043 "parse.c"
break;
- case 19:
-#line 123 "parse.y"
+ case 19: /* cmd ::= ROLLBACK trans_opt */
+#line 122 "parse.y"
{sqlite3RollbackTransaction(pParse);}
-#line 2003 "parse.c"
- break;
- case 21:
-#line 128 "parse.y"
-{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy410,&yymsp[0].minor.yy410,yymsp[-4].minor.yy46,0,0,yymsp[-2].minor.yy46);
-}
-#line 2010 "parse.c"
- break;
- case 22:
- case 25:
- case 63:
- case 77:
- case 79:
- case 90:
- case 101:
- case 112:
- case 113:
- case 212:
- case 215:
-#line 132 "parse.y"
+#line 2048 "parse.c"
+ break;
+ case 21: /* create_table ::= CREATE temp TABLE ifnotexists nm dbnm */
+#line 127 "parse.y"
+{
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy46,0,0,yymsp[-2].minor.yy46);
+}
+#line 2055 "parse.c"
+ break;
+ case 22: /* ifnotexists ::= */
+ case 25: /* temp ::= */
+ case 63: /* autoinc ::= */
+ case 77: /* init_deferred_pred_opt ::= */
+ case 79: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ case 90: /* defer_subclause_opt ::= */
+ case 101: /* ifexists ::= */
+ case 112: /* distinct ::= ALL */
+ case 113: /* distinct ::= */
+ case 213: /* between_op ::= BETWEEN */
+ case 216: /* in_op ::= IN */
+#line 131 "parse.y"
{yygotominor.yy46 = 0;}
-#line 2025 "parse.c"
- break;
- case 23:
- case 24:
- case 64:
- case 78:
- case 100:
- case 111:
- case 213:
- case 216:
-#line 133 "parse.y"
+#line 2070 "parse.c"
+ break;
+ case 23: /* ifnotexists ::= IF NOT EXISTS */
+ case 24: /* temp ::= TEMP */
+ case 64: /* autoinc ::= AUTOINCR */
+ case 78: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ case 100: /* ifexists ::= IF EXISTS */
+ case 111: /* distinct ::= DISTINCT */
+ case 214: /* between_op ::= NOT BETWEEN */
+ case 217: /* in_op ::= NOT IN */
+#line 132 "parse.y"
{yygotominor.yy46 = 1;}
-#line 2037 "parse.c"
+#line 2082 "parse.c"
break;
- case 26:
-#line 139 "parse.y"
+ case 26: /* create_table_args ::= LP columnlist conslist_opt RP */
+#line 138 "parse.y"
{
- sqlite3EndTable(pParse,&yymsp[-1].minor.yy410,&yymsp[0].minor.yy0,0);
+ sqlite3EndTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0);
}
-#line 2044 "parse.c"
+#line 2089 "parse.c"
break;
- case 27:
-#line 142 "parse.y"
+ case 27: /* create_table_args ::= AS select */
+#line 141 "parse.y"
{
sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy219);
- sqlite3SelectDelete(yymsp[0].minor.yy219);
-}
-#line 2052 "parse.c"
- break;
- case 30:
-#line 154 "parse.y"
-{
- yygotominor.yy410.z = yymsp[-2].minor.yy410.z;
- yygotominor.yy410.n = (pParse->sLastToken.z-yymsp[-2].minor.yy410.z) + pParse->sLastToken.n;
-}
-#line 2060 "parse.c"
- break;
- case 31:
-#line 158 "parse.y"
-{
- sqlite3AddColumn(pParse,&yymsp[0].minor.yy410);
- yygotominor.yy410 = yymsp[0].minor.yy410;
-}
-#line 2068 "parse.c"
- break;
- case 32:
- case 33:
- case 34:
- case 35:
- case 36:
- case 255:
-#line 168 "parse.y"
-{yygotominor.yy410 = yymsp[0].minor.yy0;}
-#line 2078 "parse.c"
- break;
- case 38:
-#line 229 "parse.y"
-{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy410);}
-#line 2083 "parse.c"
- break;
- case 39:
- case 42:
- case 119:
- case 120:
- case 131:
- case 240:
- case 242:
- case 251:
- case 252:
- case 253:
- case 254:
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy219);
+}
+#line 2097 "parse.c"
+ break;
+ case 30: /* column ::= columnid type carglist */
+#line 153 "parse.y"
+{
+ yygotominor.yy0.z = yymsp[-2].minor.yy0.z;
+ yygotominor.yy0.n = (pParse->sLastToken.z-yymsp[-2].minor.yy0.z) + pParse->sLastToken.n;
+}
+#line 2105 "parse.c"
+ break;
+ case 31: /* columnid ::= nm */
+#line 157 "parse.y"
+{
+ sqlite3AddColumn(pParse,&yymsp[0].minor.yy0);
+ yygotominor.yy0 = yymsp[0].minor.yy0;
+}
+#line 2113 "parse.c"
+ break;
+ case 32: /* id ::= ID */
+ case 33: /* ids ::= ID|STRING */
+ case 34: /* nm ::= ID */
+ case 35: /* nm ::= STRING */
+ case 36: /* nm ::= JOIN_KW */
+ case 39: /* typetoken ::= typename */
+ case 42: /* typename ::= ids */
+ case 119: /* as ::= AS nm */
+ case 120: /* as ::= ids */
+ case 131: /* dbnm ::= DOT nm */
+ case 241: /* idxitem ::= nm */
+ case 243: /* collate ::= COLLATE ids */
+ case 253: /* nmnum ::= plus_num */
+ case 254: /* nmnum ::= nm */
+ case 255: /* plus_num ::= plus_opt number */
+ case 256: /* minus_num ::= MINUS number */
+ case 257: /* number ::= INTEGER|FLOAT */
+#line 167 "parse.y"
+{yygotominor.yy0 = yymsp[0].minor.yy0;}
+#line 2134 "parse.c"
+ break;
+ case 38: /* type ::= typetoken */
+#line 228 "parse.y"
+{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy0);}
+#line 2139 "parse.c"
+ break;
+ case 40: /* typetoken ::= typename LP signed RP */
#line 230 "parse.y"
-{yygotominor.yy410 = yymsp[0].minor.yy410;}
-#line 2098 "parse.c"
- break;
- case 40:
-#line 231 "parse.y"
{
- yygotominor.yy410.z = yymsp[-3].minor.yy410.z;
- yygotominor.yy410.n = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy410.z;
+ yygotominor.yy0.z = yymsp[-3].minor.yy0.z;
+ yygotominor.yy0.n = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z;
}
-#line 2106 "parse.c"
+#line 2147 "parse.c"
break;
- case 41:
-#line 235 "parse.y"
+ case 41: /* typetoken ::= typename LP signed COMMA signed RP */
+#line 234 "parse.y"
{
- yygotominor.yy410.z = yymsp[-5].minor.yy410.z;
- yygotominor.yy410.n = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy410.z;
+ yygotominor.yy0.z = yymsp[-5].minor.yy0.z;
+ yygotominor.yy0.n = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z;
}
-#line 2114 "parse.c"
+#line 2155 "parse.c"
break;
- case 43:
-#line 241 "parse.y"
-{yygotominor.yy410.z=yymsp[-1].minor.yy410.z; yygotominor.yy410.n=yymsp[0].minor.yy410.n+(yymsp[0].minor.yy410.z-yymsp[-1].minor.yy410.z);}
-#line 2119 "parse.c"
+ case 43: /* typename ::= typename ids */
+#line 240 "parse.y"
+{yygotominor.yy0.z=yymsp[-1].minor.yy0.z; yygotominor.yy0.n=yymsp[0].minor.yy0.n+(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
+#line 2160 "parse.c"
break;
- case 50:
- case 52:
-#line 252 "parse.y"
+ case 50: /* ccons ::= DEFAULT term */
+ case 52: /* ccons ::= DEFAULT PLUS term */
+#line 251 "parse.y"
{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy172);}
-#line 2125 "parse.c"
+#line 2166 "parse.c"
break;
- case 51:
-#line 253 "parse.y"
+ case 51: /* ccons ::= DEFAULT LP expr RP */
+#line 252 "parse.y"
{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy172);}
-#line 2130 "parse.c"
+#line 2171 "parse.c"
break;
- case 53:
-#line 255 "parse.y"
+ case 53: /* ccons ::= DEFAULT MINUS term */
+#line 254 "parse.y"
{
- Expr *p = sqlite3Expr(TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
sqlite3AddDefaultValue(pParse,p);
}
-#line 2138 "parse.c"
+#line 2179 "parse.c"
break;
- case 54:
-#line 259 "parse.y"
+ case 54: /* ccons ::= DEFAULT id */
+#line 258 "parse.y"
{
- Expr *p = sqlite3Expr(TK_STRING, 0, 0, &yymsp[0].minor.yy410);
+ Expr *p = sqlite3PExpr(pParse, TK_STRING, 0, 0, &yymsp[0].minor.yy0);
sqlite3AddDefaultValue(pParse,p);
}
-#line 2146 "parse.c"
+#line 2187 "parse.c"
break;
- case 56:
-#line 268 "parse.y"
+ case 56: /* ccons ::= NOT NULL onconf */
+#line 267 "parse.y"
{sqlite3AddNotNull(pParse, yymsp[0].minor.yy46);}
-#line 2151 "parse.c"
+#line 2192 "parse.c"
break;
- case 57:
-#line 270 "parse.y"
+ case 57: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
+#line 269 "parse.y"
{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy46,yymsp[-2].minor.yy46);}
-#line 2156 "parse.c"
+#line 2197 "parse.c"
break;
- case 58:
-#line 271 "parse.y"
+ case 58: /* ccons ::= UNIQUE onconf */
+#line 270 "parse.y"
{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy46,0,0,0,0);}
-#line 2161 "parse.c"
+#line 2202 "parse.c"
break;
- case 59:
-#line 272 "parse.y"
+ case 59: /* ccons ::= CHECK LP expr RP */
+#line 271 "parse.y"
{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy172);}
-#line 2166 "parse.c"
+#line 2207 "parse.c"
break;
- case 60:
-#line 274 "parse.y"
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy410,yymsp[-1].minor.yy174,yymsp[0].minor.yy46);}
-#line 2171 "parse.c"
+ case 60: /* ccons ::= REFERENCES nm idxlist_opt refargs */
+#line 273 "parse.y"
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy174,yymsp[0].minor.yy46);}
+#line 2212 "parse.c"
break;
- case 61:
-#line 275 "parse.y"
+ case 61: /* ccons ::= defer_subclause */
+#line 274 "parse.y"
{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy46);}
-#line 2176 "parse.c"
+#line 2217 "parse.c"
break;
- case 62:
-#line 276 "parse.y"
-{sqlite3AddCollateType(pParse, (char*)yymsp[0].minor.yy410.z, yymsp[0].minor.yy410.n);}
-#line 2181 "parse.c"
+ case 62: /* ccons ::= COLLATE ids */
+#line 275 "parse.y"
+{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
+#line 2222 "parse.c"
break;
- case 65:
-#line 289 "parse.y"
+ case 65: /* refargs ::= */
+#line 288 "parse.y"
{ yygotominor.yy46 = OE_Restrict * 0x010101; }
-#line 2186 "parse.c"
+#line 2227 "parse.c"
break;
- case 66:
-#line 290 "parse.y"
+ case 66: /* refargs ::= refargs refarg */
+#line 289 "parse.y"
{ yygotominor.yy46 = (yymsp[-1].minor.yy46 & yymsp[0].minor.yy405.mask) | yymsp[0].minor.yy405.value; }
-#line 2191 "parse.c"
+#line 2232 "parse.c"
break;
- case 67:
-#line 292 "parse.y"
+ case 67: /* refarg ::= MATCH nm */
+#line 291 "parse.y"
{ yygotominor.yy405.value = 0; yygotominor.yy405.mask = 0x000000; }
-#line 2196 "parse.c"
+#line 2237 "parse.c"
break;
- case 68:
-#line 293 "parse.y"
+ case 68: /* refarg ::= ON DELETE refact */
+#line 292 "parse.y"
{ yygotominor.yy405.value = yymsp[0].minor.yy46; yygotominor.yy405.mask = 0x0000ff; }
-#line 2201 "parse.c"
+#line 2242 "parse.c"
break;
- case 69:
-#line 294 "parse.y"
+ case 69: /* refarg ::= ON UPDATE refact */
+#line 293 "parse.y"
{ yygotominor.yy405.value = yymsp[0].minor.yy46<<8; yygotominor.yy405.mask = 0x00ff00; }
-#line 2206 "parse.c"
+#line 2247 "parse.c"
break;
- case 70:
-#line 295 "parse.y"
+ case 70: /* refarg ::= ON INSERT refact */
+#line 294 "parse.y"
{ yygotominor.yy405.value = yymsp[0].minor.yy46<<16; yygotominor.yy405.mask = 0xff0000; }
-#line 2211 "parse.c"
+#line 2252 "parse.c"
break;
- case 71:
-#line 297 "parse.y"
+ case 71: /* refact ::= SET NULL */
+#line 296 "parse.y"
{ yygotominor.yy46 = OE_SetNull; }
-#line 2216 "parse.c"
+#line 2257 "parse.c"
break;
- case 72:
-#line 298 "parse.y"
+ case 72: /* refact ::= SET DEFAULT */
+#line 297 "parse.y"
{ yygotominor.yy46 = OE_SetDflt; }
-#line 2221 "parse.c"
+#line 2262 "parse.c"
break;
- case 73:
-#line 299 "parse.y"
+ case 73: /* refact ::= CASCADE */
+#line 298 "parse.y"
{ yygotominor.yy46 = OE_Cascade; }
-#line 2226 "parse.c"
+#line 2267 "parse.c"
break;
- case 74:
-#line 300 "parse.y"
+ case 74: /* refact ::= RESTRICT */
+#line 299 "parse.y"
{ yygotominor.yy46 = OE_Restrict; }
-#line 2231 "parse.c"
- break;
- case 75:
- case 76:
- case 91:
- case 93:
- case 95:
- case 96:
- case 166:
-#line 302 "parse.y"
+#line 2272 "parse.c"
+ break;
+ case 75: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ case 76: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ case 91: /* defer_subclause_opt ::= defer_subclause */
+ case 93: /* onconf ::= ON CONFLICT resolvetype */
+ case 95: /* orconf ::= OR resolvetype */
+ case 96: /* resolvetype ::= raisetype */
+ case 166: /* insert_cmd ::= INSERT orconf */
+#line 301 "parse.y"
{yygotominor.yy46 = yymsp[0].minor.yy46;}
-#line 2242 "parse.c"
+#line 2283 "parse.c"
break;
- case 80:
-#line 312 "parse.y"
-{yygotominor.yy410.n = 0; yygotominor.yy410.z = 0;}
-#line 2247 "parse.c"
+ case 80: /* conslist_opt ::= */
+#line 311 "parse.y"
+{yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;}
+#line 2288 "parse.c"
break;
- case 81:
-#line 313 "parse.y"
-{yygotominor.yy410 = yymsp[-1].minor.yy0;}
-#line 2252 "parse.c"
+ case 81: /* conslist_opt ::= COMMA conslist */
+#line 312 "parse.y"
+{yygotominor.yy0 = yymsp[-1].minor.yy0;}
+#line 2293 "parse.c"
break;
- case 86:
-#line 319 "parse.y"
+ case 86: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */
+#line 318 "parse.y"
{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy174,yymsp[0].minor.yy46,yymsp[-2].minor.yy46,0);}
-#line 2257 "parse.c"
+#line 2298 "parse.c"
break;
- case 87:
-#line 321 "parse.y"
+ case 87: /* tcons ::= UNIQUE LP idxlist RP onconf */
+#line 320 "parse.y"
{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy174,yymsp[0].minor.yy46,0,0,0,0);}
-#line 2262 "parse.c"
+#line 2303 "parse.c"
break;
- case 88:
-#line 322 "parse.y"
+ case 88: /* tcons ::= CHECK LP expr RP onconf */
+#line 321 "parse.y"
{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy172);}
-#line 2267 "parse.c"
+#line 2308 "parse.c"
break;
- case 89:
-#line 324 "parse.y"
+ case 89: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */
+#line 323 "parse.y"
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy174, &yymsp[-3].minor.yy410, yymsp[-2].minor.yy174, yymsp[-1].minor.yy46);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy174, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy174, yymsp[-1].minor.yy46);
sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy46);
}
-#line 2275 "parse.c"
+#line 2316 "parse.c"
break;
- case 92:
- case 94:
-#line 338 "parse.y"
+ case 92: /* onconf ::= */
+ case 94: /* orconf ::= */
+#line 337 "parse.y"
{yygotominor.yy46 = OE_Default;}
-#line 2281 "parse.c"
+#line 2322 "parse.c"
break;
- case 97:
-#line 343 "parse.y"
+ case 97: /* resolvetype ::= IGNORE */
+#line 342 "parse.y"
{yygotominor.yy46 = OE_Ignore;}
-#line 2286 "parse.c"
+#line 2327 "parse.c"
break;
- case 98:
- case 167:
-#line 344 "parse.y"
+ case 98: /* resolvetype ::= REPLACE */
+ case 167: /* insert_cmd ::= REPLACE */
+#line 343 "parse.y"
{yygotominor.yy46 = OE_Replace;}
-#line 2292 "parse.c"
+#line 2333 "parse.c"
break;
- case 99:
-#line 348 "parse.y"
+ case 99: /* cmd ::= DROP TABLE ifexists fullname */
+#line 347 "parse.y"
{
sqlite3DropTable(pParse, yymsp[0].minor.yy373, 0, yymsp[-1].minor.yy46);
}
-#line 2299 "parse.c"
+#line 2340 "parse.c"
break;
- case 102:
-#line 358 "parse.y"
+ case 102: /* cmd ::= CREATE temp VIEW ifnotexists nm dbnm AS select */
+#line 357 "parse.y"
{
- sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy410, &yymsp[-2].minor.yy410, yymsp[0].minor.yy219, yymsp[-6].minor.yy46, yymsp[-4].minor.yy46);
+ sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy219, yymsp[-6].minor.yy46, yymsp[-4].minor.yy46);
}
-#line 2306 "parse.c"
+#line 2347 "parse.c"
break;
- case 103:
-#line 361 "parse.y"
+ case 103: /* cmd ::= DROP VIEW ifexists fullname */
+#line 360 "parse.y"
{
sqlite3DropTable(pParse, yymsp[0].minor.yy373, 1, yymsp[-1].minor.yy46);
}
-#line 2313 "parse.c"
+#line 2354 "parse.c"
break;
- case 104:
-#line 368 "parse.y"
+ case 104: /* cmd ::= select */
+#line 367 "parse.y"
{
- sqlite3Select(pParse, yymsp[0].minor.yy219, SRT_Callback, 0, 0, 0, 0, 0);
- sqlite3SelectDelete(yymsp[0].minor.yy219);
+ SelectDest dest = {SRT_Callback, 0, 0, 0, 0};
+ sqlite3Select(pParse, yymsp[0].minor.yy219, &dest, 0, 0, 0);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy219);
}
-#line 2321 "parse.c"
+#line 2363 "parse.c"
break;
- case 105:
- case 128:
+ case 105: /* select ::= oneselect */
+ case 128: /* seltablist_paren ::= select */
#line 378 "parse.y"
{yygotominor.yy219 = yymsp[0].minor.yy219;}
-#line 2327 "parse.c"
+#line 2369 "parse.c"
break;
- case 106:
+ case 106: /* select ::= select multiselect_op oneselect */
#line 380 "parse.y"
{
if( yymsp[0].minor.yy219 ){
yymsp[0].minor.yy219->op = yymsp[-1].minor.yy46;
yymsp[0].minor.yy219->pPrior = yymsp[-2].minor.yy219;
}else{
- sqlite3SelectDelete(yymsp[-2].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy219);
}
yygotominor.yy219 = yymsp[0].minor.yy219;
}
-#line 2340 "parse.c"
+#line 2382 "parse.c"
break;
- case 108:
+ case 108: /* multiselect_op ::= UNION ALL */
#line 391 "parse.y"
{yygotominor.yy46 = TK_ALL;}
-#line 2345 "parse.c"
+#line 2387 "parse.c"
break;
- case 110:
+ case 110: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
#line 395 "parse.y"
{
- yygotominor.yy219 = sqlite3SelectNew(yymsp[-6].minor.yy174,yymsp[-5].minor.yy373,yymsp[-4].minor.yy172,yymsp[-3].minor.yy174,yymsp[-2].minor.yy172,yymsp[-1].minor.yy174,yymsp[-7].minor.yy46,yymsp[0].minor.yy234.pLimit,yymsp[0].minor.yy234.pOffset);
+ yygotominor.yy219 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy174,yymsp[-5].minor.yy373,yymsp[-4].minor.yy172,yymsp[-3].minor.yy174,yymsp[-2].minor.yy172,yymsp[-1].minor.yy174,yymsp[-7].minor.yy46,yymsp[0].minor.yy234.pLimit,yymsp[0].minor.yy234.pOffset);
}
-#line 2352 "parse.c"
+#line 2394 "parse.c"
break;
- case 114:
- case 237:
+ case 114: /* sclp ::= selcollist COMMA */
+ case 238: /* idxlist_opt ::= LP idxlist RP */
#line 416 "parse.y"
{yygotominor.yy174 = yymsp[-1].minor.yy174;}
-#line 2358 "parse.c"
+#line 2400 "parse.c"
break;
- case 115:
- case 141:
- case 149:
- case 230:
- case 236:
+ case 115: /* sclp ::= */
+ case 141: /* orderby_opt ::= */
+ case 149: /* groupby_opt ::= */
+ case 231: /* exprlist ::= */
+ case 237: /* idxlist_opt ::= */
#line 417 "parse.y"
{yygotominor.yy174 = 0;}
-#line 2367 "parse.c"
+#line 2409 "parse.c"
break;
- case 116:
+ case 116: /* selcollist ::= sclp expr as */
#line 418 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-2].minor.yy174,yymsp[-1].minor.yy172,yymsp[0].minor.yy410.n?&yymsp[0].minor.yy410:0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy174,yymsp[-1].minor.yy172,yymsp[0].minor.yy0.n?&yymsp[0].minor.yy0:0);
}
-#line 2374 "parse.c"
+#line 2416 "parse.c"
break;
- case 117:
+ case 117: /* selcollist ::= sclp STAR */
#line 421 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-1].minor.yy174, sqlite3Expr(TK_ALL, 0, 0, 0), 0);
+ Expr *p = sqlite3PExpr(pParse, TK_ALL, 0, 0, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy174, p, 0);
}
-#line 2381 "parse.c"
+#line 2424 "parse.c"
break;
- case 118:
-#line 424 "parse.y"
+ case 118: /* selcollist ::= sclp nm DOT STAR */
+#line 425 "parse.y"
{
- Expr *pRight = sqlite3Expr(TK_ALL, 0, 0, 0);
- Expr *pLeft = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy410);
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-3].minor.yy174, sqlite3Expr(TK_DOT, pLeft, pRight, 0), 0);
+ Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0);
+ Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
+ Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy174, pDot, 0);
}
-#line 2390 "parse.c"
+#line 2434 "parse.c"
break;
- case 121:
-#line 436 "parse.y"
-{yygotominor.yy410.n = 0;}
-#line 2395 "parse.c"
+ case 121: /* as ::= */
+#line 438 "parse.y"
+{yygotominor.yy0.n = 0;}
+#line 2439 "parse.c"
break;
- case 122:
-#line 448 "parse.y"
-{yygotominor.yy373 = sqliteMalloc(sizeof(*yygotominor.yy373));}
-#line 2400 "parse.c"
+ case 122: /* from ::= */
+#line 450 "parse.y"
+{yygotominor.yy373 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy373));}
+#line 2444 "parse.c"
break;
- case 123:
-#line 449 "parse.y"
+ case 123: /* from ::= FROM seltablist */
+#line 451 "parse.y"
{
yygotominor.yy373 = yymsp[0].minor.yy373;
sqlite3SrcListShiftJoinType(yygotominor.yy373);
}
-#line 2408 "parse.c"
+#line 2452 "parse.c"
break;
- case 124:
-#line 457 "parse.y"
+ case 124: /* stl_prefix ::= seltablist joinop */
+#line 459 "parse.y"
{
yygotominor.yy373 = yymsp[-1].minor.yy373;
if( yygotominor.yy373 && yygotominor.yy373->nSrc>0 ) yygotominor.yy373->a[yygotominor.yy373->nSrc-1].jointype = yymsp[0].minor.yy46;
}
-#line 2416 "parse.c"
+#line 2460 "parse.c"
break;
- case 125:
-#line 461 "parse.y"
+ case 125: /* stl_prefix ::= */
+#line 463 "parse.y"
{yygotominor.yy373 = 0;}
-#line 2421 "parse.c"
+#line 2465 "parse.c"
break;
- case 126:
-#line 462 "parse.y"
+ case 126: /* seltablist ::= stl_prefix nm dbnm as on_opt using_opt */
+#line 464 "parse.y"
{
- yygotominor.yy373 = sqlite3SrcListAppendFromTerm(yymsp[-5].minor.yy373,&yymsp[-4].minor.yy410,&yymsp[-3].minor.yy410,&yymsp[-2].minor.yy410,0,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
+ yygotominor.yy373 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy373,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
}
-#line 2428 "parse.c"
+#line 2472 "parse.c"
break;
- case 127:
-#line 467 "parse.y"
+ case 127: /* seltablist ::= stl_prefix LP seltablist_paren RP as on_opt using_opt */
+#line 469 "parse.y"
{
- yygotominor.yy373 = sqlite3SrcListAppendFromTerm(yymsp[-6].minor.yy373,0,0,&yymsp[-2].minor.yy410,yymsp[-4].minor.yy219,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
+ yygotominor.yy373 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy373,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy219,yymsp[-1].minor.yy172,yymsp[0].minor.yy432);
}
-#line 2435 "parse.c"
+#line 2479 "parse.c"
break;
- case 129:
-#line 478 "parse.y"
+ case 129: /* seltablist_paren ::= seltablist */
+#line 480 "parse.y"
{
sqlite3SrcListShiftJoinType(yymsp[0].minor.yy373);
- yygotominor.yy219 = sqlite3SelectNew(0,yymsp[0].minor.yy373,0,0,0,0,0,0,0);
+ yygotominor.yy219 = sqlite3SelectNew(pParse,0,yymsp[0].minor.yy373,0,0,0,0,0,0,0);
}
-#line 2443 "parse.c"
+#line 2487 "parse.c"
break;
- case 130:
-#line 485 "parse.y"
-{yygotominor.yy410.z=0; yygotominor.yy410.n=0;}
-#line 2448 "parse.c"
+ case 130: /* dbnm ::= */
+#line 487 "parse.y"
+{yygotominor.yy0.z=0; yygotominor.yy0.n=0;}
+#line 2492 "parse.c"
break;
- case 132:
-#line 490 "parse.y"
-{yygotominor.yy373 = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy410,&yymsp[0].minor.yy410);}
-#line 2453 "parse.c"
+ case 132: /* fullname ::= nm dbnm */
+#line 492 "parse.y"
+{yygotominor.yy373 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+#line 2497 "parse.c"
break;
- case 133:
-#line 494 "parse.y"
+ case 133: /* joinop ::= COMMA|JOIN */
+#line 496 "parse.y"
{ yygotominor.yy46 = JT_INNER; }
-#line 2458 "parse.c"
+#line 2502 "parse.c"
break;
- case 134:
-#line 495 "parse.y"
+ case 134: /* joinop ::= JOIN_KW JOIN */
+#line 497 "parse.y"
{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
-#line 2463 "parse.c"
- break;
- case 135:
-#line 496 "parse.y"
-{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy410,0); }
-#line 2468 "parse.c"
+#line 2507 "parse.c"
break;
- case 136:
+ case 135: /* joinop ::= JOIN_KW nm JOIN */
#line 498 "parse.y"
-{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy410,&yymsp[-1].minor.yy410); }
-#line 2473 "parse.c"
- break;
- case 137:
- case 145:
- case 152:
- case 159:
- case 174:
- case 202:
- case 225:
- case 227:
-#line 502 "parse.y"
+{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); }
+#line 2512 "parse.c"
+ break;
+ case 136: /* joinop ::= JOIN_KW nm nm JOIN */
+#line 500 "parse.y"
+{ yygotominor.yy46 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); }
+#line 2517 "parse.c"
+ break;
+ case 137: /* on_opt ::= ON expr */
+ case 145: /* sortitem ::= expr */
+ case 152: /* having_opt ::= HAVING expr */
+ case 159: /* where_opt ::= WHERE expr */
+ case 174: /* expr ::= term */
+ case 202: /* escape ::= ESCAPE expr */
+ case 226: /* case_else ::= ELSE expr */
+ case 228: /* case_operand ::= expr */
+#line 504 "parse.y"
{yygotominor.yy172 = yymsp[0].minor.yy172;}
-#line 2485 "parse.c"
- break;
- case 138:
- case 151:
- case 158:
- case 203:
- case 226:
- case 228:
-#line 503 "parse.y"
+#line 2529 "parse.c"
+ break;
+ case 138: /* on_opt ::= */
+ case 151: /* having_opt ::= */
+ case 158: /* where_opt ::= */
+ case 203: /* escape ::= */
+ case 227: /* case_else ::= */
+ case 229: /* case_operand ::= */
+#line 505 "parse.y"
{yygotominor.yy172 = 0;}
-#line 2495 "parse.c"
+#line 2539 "parse.c"
break;
- case 139:
- case 171:
-#line 507 "parse.y"
+ case 139: /* using_opt ::= USING LP inscollist RP */
+ case 171: /* inscollist_opt ::= LP inscollist RP */
+#line 509 "parse.y"
{yygotominor.yy432 = yymsp[-1].minor.yy432;}
-#line 2501 "parse.c"
+#line 2545 "parse.c"
break;
- case 140:
- case 170:
-#line 508 "parse.y"
+ case 140: /* using_opt ::= */
+ case 170: /* inscollist_opt ::= */
+#line 510 "parse.y"
{yygotominor.yy432 = 0;}
-#line 2507 "parse.c"
+#line 2551 "parse.c"
break;
- case 142:
- case 150:
- case 229:
-#line 519 "parse.y"
+ case 142: /* orderby_opt ::= ORDER BY sortlist */
+ case 150: /* groupby_opt ::= GROUP BY nexprlist */
+ case 230: /* exprlist ::= nexprlist */
+#line 521 "parse.y"
{yygotominor.yy174 = yymsp[0].minor.yy174;}
-#line 2514 "parse.c"
+#line 2558 "parse.c"
break;
- case 143:
-#line 520 "parse.y"
+ case 143: /* sortlist ::= sortlist COMMA sortitem sortorder */
+#line 522 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-3].minor.yy174,yymsp[-1].minor.yy172,0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy174,yymsp[-1].minor.yy172,0);
if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
}
-#line 2522 "parse.c"
+#line 2566 "parse.c"
break;
- case 144:
-#line 524 "parse.y"
+ case 144: /* sortlist ::= sortitem sortorder */
+#line 526 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(0,yymsp[-1].minor.yy172,0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy172,0);
if( yygotominor.yy174 && yygotominor.yy174->a ) yygotominor.yy174->a[0].sortOrder = yymsp[0].minor.yy46;
}
-#line 2530 "parse.c"
+#line 2574 "parse.c"
break;
- case 146:
- case 148:
-#line 532 "parse.y"
+ case 146: /* sortorder ::= ASC */
+ case 148: /* sortorder ::= */
+#line 534 "parse.y"
{yygotominor.yy46 = SQLITE_SO_ASC;}
-#line 2536 "parse.c"
+#line 2580 "parse.c"
break;
- case 147:
-#line 533 "parse.y"
+ case 147: /* sortorder ::= DESC */
+#line 535 "parse.y"
{yygotominor.yy46 = SQLITE_SO_DESC;}
-#line 2541 "parse.c"
+#line 2585 "parse.c"
break;
- case 153:
-#line 559 "parse.y"
+ case 153: /* limit_opt ::= */
+#line 561 "parse.y"
{yygotominor.yy234.pLimit = 0; yygotominor.yy234.pOffset = 0;}
-#line 2546 "parse.c"
+#line 2590 "parse.c"
break;
- case 154:
-#line 560 "parse.y"
+ case 154: /* limit_opt ::= LIMIT expr */
+#line 562 "parse.y"
{yygotominor.yy234.pLimit = yymsp[0].minor.yy172; yygotominor.yy234.pOffset = 0;}
-#line 2551 "parse.c"
+#line 2595 "parse.c"
break;
- case 155:
-#line 562 "parse.y"
+ case 155: /* limit_opt ::= LIMIT expr OFFSET expr */
+#line 564 "parse.y"
{yygotominor.yy234.pLimit = yymsp[-2].minor.yy172; yygotominor.yy234.pOffset = yymsp[0].minor.yy172;}
-#line 2556 "parse.c"
+#line 2600 "parse.c"
break;
- case 156:
-#line 564 "parse.y"
+ case 156: /* limit_opt ::= LIMIT expr COMMA expr */
+#line 566 "parse.y"
{yygotominor.yy234.pOffset = yymsp[-2].minor.yy172; yygotominor.yy234.pLimit = yymsp[0].minor.yy172;}
-#line 2561 "parse.c"
+#line 2605 "parse.c"
break;
- case 157:
-#line 568 "parse.y"
+ case 157: /* cmd ::= DELETE FROM fullname where_opt */
+#line 570 "parse.y"
{sqlite3DeleteFrom(pParse,yymsp[-1].minor.yy373,yymsp[0].minor.yy172);}
-#line 2566 "parse.c"
+#line 2610 "parse.c"
break;
- case 160:
-#line 578 "parse.y"
+ case 160: /* cmd ::= UPDATE orconf fullname SET setlist where_opt */
+#line 580 "parse.y"
{
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy174,SQLITE_MAX_COLUMN,"set list");
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy174,"set list");
sqlite3Update(pParse,yymsp[-3].minor.yy373,yymsp[-1].minor.yy174,yymsp[0].minor.yy172,yymsp[-4].minor.yy46);
}
-#line 2574 "parse.c"
+#line 2618 "parse.c"
break;
- case 161:
-#line 587 "parse.y"
-{yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-4].minor.yy174,yymsp[0].minor.yy172,&yymsp[-2].minor.yy410);}
-#line 2579 "parse.c"
+ case 161: /* setlist ::= setlist COMMA nm EQ expr */
+#line 589 "parse.y"
+{yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174,yymsp[0].minor.yy172,&yymsp[-2].minor.yy0);}
+#line 2623 "parse.c"
break;
- case 162:
-#line 588 "parse.y"
-{yygotominor.yy174 = sqlite3ExprListAppend(0,yymsp[0].minor.yy172,&yymsp[-2].minor.yy410);}
-#line 2584 "parse.c"
+ case 162: /* setlist ::= nm EQ expr */
+#line 591 "parse.y"
+{yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy172,&yymsp[-2].minor.yy0);}
+#line 2628 "parse.c"
break;
- case 163:
-#line 594 "parse.y"
+ case 163: /* cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP */
+#line 597 "parse.y"
{sqlite3Insert(pParse, yymsp[-5].minor.yy373, yymsp[-1].minor.yy174, 0, yymsp[-4].minor.yy432, yymsp[-7].minor.yy46);}
-#line 2589 "parse.c"
+#line 2633 "parse.c"
break;
- case 164:
-#line 596 "parse.y"
+ case 164: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */
+#line 599 "parse.y"
{sqlite3Insert(pParse, yymsp[-2].minor.yy373, 0, yymsp[0].minor.yy219, yymsp[-1].minor.yy432, yymsp[-4].minor.yy46);}
-#line 2594 "parse.c"
+#line 2638 "parse.c"
break;
- case 165:
-#line 598 "parse.y"
+ case 165: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */
+#line 601 "parse.y"
{sqlite3Insert(pParse, yymsp[-3].minor.yy373, 0, 0, yymsp[-2].minor.yy432, yymsp[-5].minor.yy46);}
-#line 2599 "parse.c"
- break;
- case 168:
- case 231:
-#line 608 "parse.y"
-{yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-2].minor.yy174,yymsp[0].minor.yy172,0);}
-#line 2605 "parse.c"
- break;
- case 169:
- case 232:
-#line 609 "parse.y"
-{yygotominor.yy174 = sqlite3ExprListAppend(0,yymsp[0].minor.yy172,0);}
-#line 2611 "parse.c"
- break;
- case 172:
-#line 618 "parse.y"
-{yygotominor.yy432 = sqlite3IdListAppend(yymsp[-2].minor.yy432,&yymsp[0].minor.yy410);}
-#line 2616 "parse.c"
- break;
- case 173:
-#line 619 "parse.y"
-{yygotominor.yy432 = sqlite3IdListAppend(0,&yymsp[0].minor.yy410);}
-#line 2621 "parse.c"
- break;
- case 175:
-#line 630 "parse.y"
+#line 2643 "parse.c"
+ break;
+ case 168: /* itemlist ::= itemlist COMMA expr */
+ case 232: /* nexprlist ::= nexprlist COMMA expr */
+#line 612 "parse.y"
+{yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy174,yymsp[0].minor.yy172,0);}
+#line 2649 "parse.c"
+ break;
+ case 169: /* itemlist ::= expr */
+ case 233: /* nexprlist ::= expr */
+#line 614 "parse.y"
+{yygotominor.yy174 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy172,0);}
+#line 2655 "parse.c"
+ break;
+ case 172: /* inscollist ::= inscollist COMMA nm */
+#line 624 "parse.y"
+{yygotominor.yy432 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy432,&yymsp[0].minor.yy0);}
+#line 2660 "parse.c"
+ break;
+ case 173: /* inscollist ::= nm */
+#line 626 "parse.y"
+{yygotominor.yy432 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);}
+#line 2665 "parse.c"
+ break;
+ case 175: /* expr ::= LP expr RP */
+#line 637 "parse.y"
{yygotominor.yy172 = yymsp[-1].minor.yy172; sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); }
-#line 2626 "parse.c"
+#line 2670 "parse.c"
break;
- case 176:
- case 181:
- case 182:
-#line 631 "parse.y"
-{yygotominor.yy172 = sqlite3Expr(yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);}
-#line 2633 "parse.c"
+ case 176: /* term ::= NULL */
+ case 181: /* term ::= INTEGER|FLOAT|BLOB */
+ case 182: /* term ::= STRING */
+#line 638 "parse.y"
+{yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);}
+#line 2677 "parse.c"
break;
- case 177:
- case 178:
-#line 632 "parse.y"
-{yygotominor.yy172 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy0);}
-#line 2639 "parse.c"
+ case 177: /* expr ::= ID */
+ case 178: /* expr ::= JOIN_KW */
+#line 639 "parse.y"
+{yygotominor.yy172 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);}
+#line 2683 "parse.c"
break;
- case 179:
-#line 634 "parse.y"
+ case 179: /* expr ::= nm DOT nm */
+#line 641 "parse.y"
{
- Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy410);
- Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy410);
- yygotominor.yy172 = sqlite3Expr(TK_DOT, temp1, temp2, 0);
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
}
-#line 2648 "parse.c"
+#line 2692 "parse.c"
break;
- case 180:
-#line 639 "parse.y"
+ case 180: /* expr ::= nm DOT nm DOT nm */
+#line 646 "parse.y"
{
- Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-4].minor.yy410);
- Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy410);
- Expr *temp3 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy410);
- Expr *temp4 = sqlite3Expr(TK_DOT, temp2, temp3, 0);
- yygotominor.yy172 = sqlite3Expr(TK_DOT, temp1, temp4, 0);
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
+ Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
+ Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
}
-#line 2659 "parse.c"
+#line 2703 "parse.c"
break;
- case 183:
-#line 648 "parse.y"
+ case 183: /* expr ::= REGISTER */
+#line 655 "parse.y"
{yygotominor.yy172 = sqlite3RegisterExpr(pParse, &yymsp[0].minor.yy0);}
-#line 2664 "parse.c"
+#line 2708 "parse.c"
break;
- case 184:
-#line 649 "parse.y"
+ case 184: /* expr ::= VARIABLE */
+#line 656 "parse.y"
{
Token *pToken = &yymsp[0].minor.yy0;
- Expr *pExpr = yygotominor.yy172 = sqlite3Expr(TK_VARIABLE, 0, 0, pToken);
+ Expr *pExpr = yygotominor.yy172 = sqlite3PExpr(pParse, TK_VARIABLE, 0, 0, pToken);
sqlite3ExprAssignVarNumber(pParse, pExpr);
}
-#line 2673 "parse.c"
+#line 2717 "parse.c"
break;
- case 185:
-#line 654 "parse.y"
+ case 185: /* expr ::= expr COLLATE ids */
+#line 661 "parse.y"
{
- yygotominor.yy172 = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy172, &yymsp[0].minor.yy410);
+ yygotominor.yy172 = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy172, &yymsp[0].minor.yy0);
}
-#line 2680 "parse.c"
+#line 2724 "parse.c"
break;
- case 186:
-#line 658 "parse.y"
+ case 186: /* expr ::= CAST LP expr AS typetoken RP */
+#line 665 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_CAST, yymsp[-3].minor.yy172, 0, &yymsp[-1].minor.yy410);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy172, 0, &yymsp[-1].minor.yy0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0);
}
-#line 2688 "parse.c"
+#line 2732 "parse.c"
break;
- case 187:
-#line 663 "parse.y"
+ case 187: /* expr ::= ID LP distinct exprlist RP */
+#line 670 "parse.y"
{
if( yymsp[-1].minor.yy174 && yymsp[-1].minor.yy174->nExpr>SQLITE_MAX_FUNCTION_ARG ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
}
- yygotominor.yy172 = sqlite3ExprFunction(yymsp[-1].minor.yy174, &yymsp[-4].minor.yy0);
+ yygotominor.yy172 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy174, &yymsp[-4].minor.yy0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
if( yymsp[-2].minor.yy46 && yygotominor.yy172 ){
yygotominor.yy172->flags |= EP_Distinct;
}
}
-#line 2702 "parse.c"
+#line 2746 "parse.c"
break;
- case 188:
-#line 673 "parse.y"
+ case 188: /* expr ::= ID LP STAR RP */
+#line 680 "parse.y"
{
- yygotominor.yy172 = sqlite3ExprFunction(0, &yymsp[-3].minor.yy0);
+ yygotominor.yy172 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
}
-#line 2710 "parse.c"
+#line 2754 "parse.c"
break;
- case 189:
-#line 677 "parse.y"
+ case 189: /* term ::= CTIME_KW */
+#line 684 "parse.y"
{
/* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
** treated as functions that return constants */
- yygotominor.yy172 = sqlite3ExprFunction(0,&yymsp[0].minor.yy0);
+ yygotominor.yy172 = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0);
if( yygotominor.yy172 ){
yygotominor.yy172->op = TK_CONST_FUNC;
yygotominor.yy172->span = yymsp[0].minor.yy0;
}
}
-#line 2723 "parse.c"
- break;
- case 190:
- case 191:
- case 192:
- case 193:
- case 194:
- case 195:
- case 196:
- case 197:
-#line 686 "parse.y"
-{yygotominor.yy172 = sqlite3Expr(yymsp[-1].major, yymsp[-2].minor.yy172, yymsp[0].minor.yy172, 0);}
-#line 2735 "parse.c"
- break;
- case 198:
- case 200:
-#line 696 "parse.y"
+#line 2767 "parse.c"
+ break;
+ case 190: /* expr ::= expr AND expr */
+ case 191: /* expr ::= expr OR expr */
+ case 192: /* expr ::= expr LT|GT|GE|LE expr */
+ case 193: /* expr ::= expr EQ|NE expr */
+ case 194: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ case 195: /* expr ::= expr PLUS|MINUS expr */
+ case 196: /* expr ::= expr STAR|SLASH|REM expr */
+ case 197: /* expr ::= expr CONCAT expr */
+#line 693 "parse.y"
+{yygotominor.yy172 = sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy172,yymsp[0].minor.yy172,0);}
+#line 2779 "parse.c"
+ break;
+ case 198: /* likeop ::= LIKE_KW */
+ case 200: /* likeop ::= MATCH */
+#line 705 "parse.y"
{yygotominor.yy72.eOperator = yymsp[0].minor.yy0; yygotominor.yy72.not = 0;}
-#line 2741 "parse.c"
+#line 2785 "parse.c"
break;
- case 199:
- case 201:
-#line 697 "parse.y"
+ case 199: /* likeop ::= NOT LIKE_KW */
+ case 201: /* likeop ::= NOT MATCH */
+#line 706 "parse.y"
{yygotominor.yy72.eOperator = yymsp[0].minor.yy0; yygotominor.yy72.not = 1;}
-#line 2747 "parse.c"
+#line 2791 "parse.c"
break;
- case 204:
-#line 704 "parse.y"
+ case 204: /* expr ::= expr likeop expr escape */
+#line 713 "parse.y"
{
ExprList *pList;
- pList = sqlite3ExprListAppend(0, yymsp[-1].minor.yy172, 0);
- pList = sqlite3ExprListAppend(pList, yymsp[-3].minor.yy172, 0);
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-1].minor.yy172, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-3].minor.yy172, 0);
if( yymsp[0].minor.yy172 ){
- pList = sqlite3ExprListAppend(pList, yymsp[0].minor.yy172, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy172, 0);
}
- yygotominor.yy172 = sqlite3ExprFunction(pList, &yymsp[-2].minor.yy72.eOperator);
- if( yymsp[-2].minor.yy72.not ) yygotominor.yy172 = sqlite3Expr(TK_NOT, yygotominor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3ExprFunction(pParse, pList, &yymsp[-2].minor.yy72.eOperator);
+ if( yymsp[-2].minor.yy72.not ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172, &yymsp[-3].minor.yy172->span, &yymsp[-1].minor.yy172->span);
if( yygotominor.yy172 ) yygotominor.yy172->flags |= EP_InfixFunc;
}
-#line 2763 "parse.c"
+#line 2807 "parse.c"
break;
- case 205:
-#line 717 "parse.y"
+ case 205: /* expr ::= expr ISNULL|NOTNULL */
+#line 726 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(yymsp[0].major, yymsp[-1].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[0].major, yymsp[-1].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2771 "parse.c"
+#line 2815 "parse.c"
break;
- case 206:
-#line 721 "parse.y"
+ case 206: /* expr ::= expr IS NULL */
+#line 730 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_ISNULL, yymsp[-2].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_ISNULL, yymsp[-2].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2779 "parse.c"
+#line 2823 "parse.c"
break;
- case 207:
-#line 725 "parse.y"
+ case 207: /* expr ::= expr NOT NULL */
+#line 734 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_NOTNULL, yymsp[-2].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-2].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2787 "parse.c"
+#line 2831 "parse.c"
break;
- case 208:
-#line 729 "parse.y"
+ case 208: /* expr ::= expr IS NOT NULL */
+#line 738 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_NOTNULL, yymsp[-3].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOTNULL, yymsp[-3].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2795 "parse.c"
+#line 2839 "parse.c"
break;
- case 209:
-#line 733 "parse.y"
+ case 209: /* expr ::= NOT expr */
+ case 210: /* expr ::= BITNOT expr */
+#line 742 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(yymsp[-1].major, yymsp[0].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
}
-#line 2803 "parse.c"
+#line 2848 "parse.c"
break;
- case 210:
-#line 737 "parse.y"
+ case 211: /* expr ::= MINUS expr */
+#line 750 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
}
-#line 2811 "parse.c"
+#line 2856 "parse.c"
break;
- case 211:
-#line 741 "parse.y"
+ case 212: /* expr ::= PLUS expr */
+#line 754 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_UPLUS, yymsp[0].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_UPLUS, yymsp[0].minor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy172->span);
}
-#line 2819 "parse.c"
+#line 2864 "parse.c"
break;
- case 214:
-#line 748 "parse.y"
+ case 215: /* expr ::= expr between_op expr AND expr */
+#line 761 "parse.y"
{
- ExprList *pList = sqlite3ExprListAppend(0, yymsp[-2].minor.yy172, 0);
- pList = sqlite3ExprListAppend(pList, yymsp[0].minor.yy172, 0);
- yygotominor.yy172 = sqlite3Expr(TK_BETWEEN, yymsp[-4].minor.yy172, 0, 0);
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy172, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy172, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy172, 0, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->pList = pList;
}else{
- sqlite3ExprListDelete(pList);
+ sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3Expr(TK_NOT, yygotominor.yy172, 0, 0);
+ if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy172->span);
}
-#line 2835 "parse.c"
+#line 2880 "parse.c"
break;
- case 217:
-#line 764 "parse.y"
+ case 218: /* expr ::= expr in_op LP exprlist RP */
+#line 777 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy172, 0, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->pList = yymsp[-1].minor.yy174;
- sqlite3ExprSetHeight(yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3ExprListDelete(yymsp[-1].minor.yy174);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy174);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3Expr(TK_NOT, yygotominor.yy172, 0, 0);
+ if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2850 "parse.c"
+#line 2895 "parse.c"
break;
- case 218:
-#line 775 "parse.y"
+ case 219: /* expr ::= LP select RP */
+#line 788 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_SELECT, 0, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->pSelect = yymsp[-1].minor.yy219;
- sqlite3ExprSetHeight(yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3SelectDelete(yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
}
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
}
-#line 2864 "parse.c"
+#line 2909 "parse.c"
break;
- case 219:
-#line 785 "parse.y"
+ case 220: /* expr ::= expr in_op LP select RP */
+#line 798 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy172, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy172, 0, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->pSelect = yymsp[-1].minor.yy219;
- sqlite3ExprSetHeight(yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3SelectDelete(yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
}
- if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3Expr(TK_NOT, yygotominor.yy172, 0, 0);
+ if( yymsp[-3].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
sqlite3ExprSpan(yygotominor.yy172,&yymsp[-4].minor.yy172->span,&yymsp[0].minor.yy0);
}
-#line 2879 "parse.c"
+#line 2924 "parse.c"
break;
- case 220:
-#line 796 "parse.y"
+ case 221: /* expr ::= expr in_op nm dbnm */
+#line 809 "parse.y"
{
- SrcList *pSrc = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy410,&yymsp[0].minor.yy410);
- yygotominor.yy172 = sqlite3Expr(TK_IN, yymsp[-3].minor.yy172, 0, 0);
+ SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy172, 0, 0);
if( yygotominor.yy172 ){
- yygotominor.yy172->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,0,0);
- sqlite3ExprSetHeight(yygotominor.yy172);
+ yygotominor.yy172->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3SrcListDelete(pSrc);
+ sqlite3SrcListDelete(pParse->db, pSrc);
}
- if( yymsp[-2].minor.yy46 ) yygotominor.yy172 = sqlite3Expr(TK_NOT, yygotominor.yy172, 0, 0);
- sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy172->span,yymsp[0].minor.yy410.z?&yymsp[0].minor.yy410:&yymsp[-1].minor.yy410);
+ if( yymsp[-2].minor.yy46 ) yygotominor.yy172 = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy172, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy172,&yymsp[-3].minor.yy172->span,yymsp[0].minor.yy0.z?&yymsp[0].minor.yy0:&yymsp[-1].minor.yy0);
}
-#line 2895 "parse.c"
+#line 2940 "parse.c"
break;
- case 221:
-#line 808 "parse.y"
+ case 222: /* expr ::= EXISTS LP select RP */
+#line 821 "parse.y"
{
- Expr *p = yygotominor.yy172 = sqlite3Expr(TK_EXISTS, 0, 0, 0);
+ Expr *p = yygotominor.yy172 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
if( p ){
p->pSelect = yymsp[-1].minor.yy219;
sqlite3ExprSpan(p,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
- sqlite3ExprSetHeight(yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3SelectDelete(yymsp[-1].minor.yy219);
+ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy219);
}
}
-#line 2909 "parse.c"
+#line 2954 "parse.c"
break;
- case 222:
-#line 821 "parse.y"
+ case 223: /* expr ::= CASE case_operand case_exprlist case_else END */
+#line 834 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_CASE, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->pList = yymsp[-2].minor.yy174;
- sqlite3ExprSetHeight(yygotominor.yy172);
+ sqlite3ExprSetHeight(pParse, yygotominor.yy172);
}else{
- sqlite3ExprListDelete(yymsp[-2].minor.yy174);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy174);
}
sqlite3ExprSpan(yygotominor.yy172, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
}
-#line 2923 "parse.c"
+#line 2968 "parse.c"
break;
- case 223:
-#line 833 "parse.y"
+ case 224: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+#line 846 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-4].minor.yy174, yymsp[-2].minor.yy172, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(yygotominor.yy174, yymsp[0].minor.yy172, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174, yymsp[-2].minor.yy172, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yygotominor.yy174, yymsp[0].minor.yy172, 0);
}
-#line 2931 "parse.c"
+#line 2976 "parse.c"
break;
- case 224:
-#line 837 "parse.y"
+ case 225: /* case_exprlist ::= WHEN expr THEN expr */
+#line 850 "parse.y"
{
- yygotominor.yy174 = sqlite3ExprListAppend(0, yymsp[-2].minor.yy172, 0);
- yygotominor.yy174 = sqlite3ExprListAppend(yygotominor.yy174, yymsp[0].minor.yy172, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy172, 0);
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yygotominor.yy174, yymsp[0].minor.yy172, 0);
}
-#line 2939 "parse.c"
+#line 2984 "parse.c"
break;
- case 233:
-#line 864 "parse.y"
+ case 234: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */
+#line 879 "parse.y"
{
- sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy410, &yymsp[-5].minor.yy410, sqlite3SrcListAppend(0,&yymsp[-3].minor.yy410,0), yymsp[-1].minor.yy174, yymsp[-9].minor.yy46,
+ sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0,
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy174, yymsp[-9].minor.yy46,
&yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy46);
}
-#line 2947 "parse.c"
+#line 2993 "parse.c"
break;
- case 234:
- case 281:
-#line 870 "parse.y"
+ case 235: /* uniqueflag ::= UNIQUE */
+ case 283: /* raisetype ::= ABORT */
+#line 886 "parse.y"
{yygotominor.yy46 = OE_Abort;}
-#line 2953 "parse.c"
+#line 2999 "parse.c"
break;
- case 235:
-#line 871 "parse.y"
+ case 236: /* uniqueflag ::= */
+#line 887 "parse.y"
{yygotominor.yy46 = OE_None;}
-#line 2958 "parse.c"
+#line 3004 "parse.c"
break;
- case 238:
-#line 881 "parse.y"
+ case 239: /* idxlist ::= idxlist COMMA idxitem collate sortorder */
+#line 897 "parse.y"
{
Expr *p = 0;
- if( yymsp[-1].minor.yy410.n>0 ){
- p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
- if( p ) p->pColl = sqlite3LocateCollSeq(pParse, (char*)yymsp[-1].minor.yy410.z, yymsp[-1].minor.yy410.n);
+ if( yymsp[-1].minor.yy0.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0);
}
- yygotominor.yy174 = sqlite3ExprListAppend(yymsp[-4].minor.yy174, p, &yymsp[-2].minor.yy410);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy174, SQLITE_MAX_COLUMN, "index");
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy174, p, &yymsp[-2].minor.yy0);
+ sqlite3ExprListCheckLength(pParse, yygotominor.yy174, "index");
if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
}
-#line 2972 "parse.c"
+#line 3018 "parse.c"
break;
- case 239:
-#line 891 "parse.y"
+ case 240: /* idxlist ::= idxitem collate sortorder */
+#line 907 "parse.y"
{
Expr *p = 0;
- if( yymsp[-1].minor.yy410.n>0 ){
- p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
- if( p ) p->pColl = sqlite3LocateCollSeq(pParse, (char*)yymsp[-1].minor.yy410.z, yymsp[-1].minor.yy410.n);
+ if( yymsp[-1].minor.yy0.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0);
}
- yygotominor.yy174 = sqlite3ExprListAppend(0, p, &yymsp[-2].minor.yy410);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy174, SQLITE_MAX_COLUMN, "index");
+ yygotominor.yy174 = sqlite3ExprListAppend(pParse,0, p, &yymsp[-2].minor.yy0);
+ sqlite3ExprListCheckLength(pParse, yygotominor.yy174, "index");
if( yygotominor.yy174 ) yygotominor.yy174->a[yygotominor.yy174->nExpr-1].sortOrder = yymsp[0].minor.yy46;
}
-#line 2986 "parse.c"
+#line 3032 "parse.c"
break;
- case 241:
-#line 904 "parse.y"
-{yygotominor.yy410.z = 0; yygotominor.yy410.n = 0;}
-#line 2991 "parse.c"
+ case 242: /* collate ::= */
+#line 920 "parse.y"
+{yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;}
+#line 3037 "parse.c"
break;
- case 243:
-#line 910 "parse.y"
+ case 244: /* cmd ::= DROP INDEX ifexists fullname */
+#line 926 "parse.y"
{sqlite3DropIndex(pParse, yymsp[0].minor.yy373, yymsp[-1].minor.yy46);}
-#line 2996 "parse.c"
+#line 3042 "parse.c"
break;
- case 244:
- case 245:
-#line 916 "parse.y"
+ case 245: /* cmd ::= VACUUM */
+ case 246: /* cmd ::= VACUUM nm */
+#line 932 "parse.y"
{sqlite3Vacuum(pParse);}
-#line 3002 "parse.c"
+#line 3048 "parse.c"
break;
- case 246:
-#line 924 "parse.y"
-{sqlite3Pragma(pParse,&yymsp[-3].minor.yy410,&yymsp[-2].minor.yy410,&yymsp[0].minor.yy410,0);}
-#line 3007 "parse.c"
+ case 247: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 248: /* cmd ::= PRAGMA nm dbnm EQ ON */
+ case 249: /* cmd ::= PRAGMA nm dbnm EQ DELETE */
+#line 941 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
+#line 3055 "parse.c"
break;
- case 247:
-#line 925 "parse.y"
-{sqlite3Pragma(pParse,&yymsp[-3].minor.yy410,&yymsp[-2].minor.yy410,&yymsp[0].minor.yy0,0);}
-#line 3012 "parse.c"
- break;
- case 248:
-#line 926 "parse.y"
+ case 250: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+#line 944 "parse.y"
{
- sqlite3Pragma(pParse,&yymsp[-3].minor.yy410,&yymsp[-2].minor.yy410,&yymsp[0].minor.yy410,1);
+ sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);
}
-#line 3019 "parse.c"
+#line 3062 "parse.c"
break;
- case 249:
-#line 929 "parse.y"
-{sqlite3Pragma(pParse,&yymsp[-4].minor.yy410,&yymsp[-3].minor.yy410,&yymsp[-1].minor.yy410,0);}
-#line 3024 "parse.c"
+ case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+#line 947 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
+#line 3067 "parse.c"
break;
- case 250:
-#line 930 "parse.y"
-{sqlite3Pragma(pParse,&yymsp[-1].minor.yy410,&yymsp[0].minor.yy410,0,0);}
-#line 3029 "parse.c"
+ case 252: /* cmd ::= PRAGMA nm dbnm */
+#line 948 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
+#line 3072 "parse.c"
break;
- case 258:
-#line 944 "parse.y"
+ case 260: /* cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END */
+#line 963 "parse.y"
{
Token all;
- all.z = yymsp[-3].minor.yy410.z;
- all.n = (yymsp[0].minor.yy0.z - yymsp[-3].minor.yy410.z) + yymsp[0].minor.yy0.n;
+ all.z = yymsp[-3].minor.yy0.z;
+ all.n = (yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy243, &all);
}
-#line 3039 "parse.c"
+#line 3082 "parse.c"
break;
- case 259:
-#line 953 "parse.y"
+ case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+#line 972 "parse.y"
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy410, &yymsp[-6].minor.yy410, yymsp[-5].minor.yy46, yymsp[-4].minor.yy370.a, yymsp[-4].minor.yy370.b, yymsp[-2].minor.yy373, yymsp[0].minor.yy172, yymsp[-10].minor.yy46, yymsp[-8].minor.yy46);
- yygotominor.yy410 = (yymsp[-6].minor.yy410.n==0?yymsp[-7].minor.yy410:yymsp[-6].minor.yy410);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy46, yymsp[-4].minor.yy370.a, yymsp[-4].minor.yy370.b, yymsp[-2].minor.yy373, yymsp[0].minor.yy172, yymsp[-10].minor.yy46, yymsp[-8].minor.yy46);
+ yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0);
}
-#line 3047 "parse.c"
+#line 3090 "parse.c"
break;
- case 260:
- case 263:
-#line 959 "parse.y"
+ case 262: /* trigger_time ::= BEFORE */
+ case 265: /* trigger_time ::= */
+#line 978 "parse.y"
{ yygotominor.yy46 = TK_BEFORE; }
-#line 3053 "parse.c"
+#line 3096 "parse.c"
break;
- case 261:
-#line 960 "parse.y"
+ case 263: /* trigger_time ::= AFTER */
+#line 979 "parse.y"
{ yygotominor.yy46 = TK_AFTER; }
-#line 3058 "parse.c"
+#line 3101 "parse.c"
break;
- case 262:
-#line 961 "parse.y"
+ case 264: /* trigger_time ::= INSTEAD OF */
+#line 980 "parse.y"
{ yygotominor.yy46 = TK_INSTEAD;}
-#line 3063 "parse.c"
+#line 3106 "parse.c"
break;
- case 264:
- case 265:
-#line 966 "parse.y"
+ case 266: /* trigger_event ::= DELETE|INSERT */
+ case 267: /* trigger_event ::= UPDATE */
+#line 985 "parse.y"
{yygotominor.yy370.a = yymsp[0].major; yygotominor.yy370.b = 0;}
-#line 3069 "parse.c"
+#line 3112 "parse.c"
break;
- case 266:
-#line 968 "parse.y"
+ case 268: /* trigger_event ::= UPDATE OF inscollist */
+#line 987 "parse.y"
{yygotominor.yy370.a = TK_UPDATE; yygotominor.yy370.b = yymsp[0].minor.yy432;}
-#line 3074 "parse.c"
+#line 3117 "parse.c"
break;
- case 269:
-#line 975 "parse.y"
+ case 271: /* when_clause ::= */
+ case 288: /* key_opt ::= */
+#line 994 "parse.y"
{ yygotominor.yy172 = 0; }
-#line 3079 "parse.c"
+#line 3123 "parse.c"
break;
- case 270:
-#line 976 "parse.y"
+ case 272: /* when_clause ::= WHEN expr */
+ case 289: /* key_opt ::= KEY expr */
+#line 995 "parse.y"
{ yygotominor.yy172 = yymsp[0].minor.yy172; }
-#line 3084 "parse.c"
+#line 3129 "parse.c"
break;
- case 271:
-#line 980 "parse.y"
+ case 273: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+#line 999 "parse.y"
{
if( yymsp[-2].minor.yy243 ){
yymsp[-2].minor.yy243->pLast->pNext = yymsp[-1].minor.yy243;
@@ -3091,171 +3134,161 @@ static void yy_reduce(
yymsp[-2].minor.yy243->pLast = yymsp[-1].minor.yy243;
yygotominor.yy243 = yymsp[-2].minor.yy243;
}
-#line 3097 "parse.c"
+#line 3142 "parse.c"
break;
- case 272:
-#line 989 "parse.y"
+ case 274: /* trigger_cmd_list ::= */
+#line 1008 "parse.y"
{ yygotominor.yy243 = 0; }
-#line 3102 "parse.c"
- break;
- case 273:
-#line 995 "parse.y"
-{ yygotominor.yy243 = sqlite3TriggerUpdateStep(&yymsp[-3].minor.yy410, yymsp[-1].minor.yy174, yymsp[0].minor.yy172, yymsp[-4].minor.yy46); }
-#line 3107 "parse.c"
- break;
- case 274:
-#line 1000 "parse.y"
-{yygotominor.yy243 = sqlite3TriggerInsertStep(&yymsp[-5].minor.yy410, yymsp[-4].minor.yy432, yymsp[-1].minor.yy174, 0, yymsp[-7].minor.yy46);}
-#line 3112 "parse.c"
- break;
- case 275:
-#line 1003 "parse.y"
-{yygotominor.yy243 = sqlite3TriggerInsertStep(&yymsp[-2].minor.yy410, yymsp[-1].minor.yy432, 0, yymsp[0].minor.yy219, yymsp[-4].minor.yy46);}
-#line 3117 "parse.c"
- break;
- case 276:
-#line 1007 "parse.y"
-{yygotominor.yy243 = sqlite3TriggerDeleteStep(&yymsp[-1].minor.yy410, yymsp[0].minor.yy172);}
-#line 3122 "parse.c"
- break;
- case 277:
-#line 1010 "parse.y"
-{yygotominor.yy243 = sqlite3TriggerSelectStep(yymsp[0].minor.yy219); }
-#line 3127 "parse.c"
- break;
- case 278:
-#line 1013 "parse.y"
+#line 3147 "parse.c"
+ break;
+ case 275: /* trigger_cmd ::= UPDATE orconf nm SET setlist where_opt */
+#line 1014 "parse.y"
+{ yygotominor.yy243 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy174, yymsp[0].minor.yy172, yymsp[-4].minor.yy46); }
+#line 3152 "parse.c"
+ break;
+ case 276: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP */
+#line 1019 "parse.y"
+{yygotominor.yy243 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy432, yymsp[-1].minor.yy174, 0, yymsp[-7].minor.yy46);}
+#line 3157 "parse.c"
+ break;
+ case 277: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt select */
+#line 1022 "parse.y"
+{yygotominor.yy243 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy432, 0, yymsp[0].minor.yy219, yymsp[-4].minor.yy46);}
+#line 3162 "parse.c"
+ break;
+ case 278: /* trigger_cmd ::= DELETE FROM nm where_opt */
+#line 1026 "parse.y"
+{yygotominor.yy243 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-1].minor.yy0, yymsp[0].minor.yy172);}
+#line 3167 "parse.c"
+ break;
+ case 279: /* trigger_cmd ::= select */
+#line 1029 "parse.y"
+{yygotominor.yy243 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy219); }
+#line 3172 "parse.c"
+ break;
+ case 280: /* expr ::= RAISE LP IGNORE RP */
+#line 1032 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_RAISE, 0, 0, 0);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
if( yygotominor.yy172 ){
yygotominor.yy172->iColumn = OE_Ignore;
sqlite3ExprSpan(yygotominor.yy172, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0);
}
}
-#line 3138 "parse.c"
+#line 3183 "parse.c"
break;
- case 279:
-#line 1020 "parse.y"
+ case 281: /* expr ::= RAISE LP raisetype COMMA nm RP */
+#line 1039 "parse.y"
{
- yygotominor.yy172 = sqlite3Expr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy410);
+ yygotominor.yy172 = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
if( yygotominor.yy172 ) {
yygotominor.yy172->iColumn = yymsp[-3].minor.yy46;
sqlite3ExprSpan(yygotominor.yy172, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0);
}
}
-#line 3149 "parse.c"
+#line 3194 "parse.c"
break;
- case 280:
-#line 1030 "parse.y"
+ case 282: /* raisetype ::= ROLLBACK */
+#line 1049 "parse.y"
{yygotominor.yy46 = OE_Rollback;}
-#line 3154 "parse.c"
+#line 3199 "parse.c"
break;
- case 282:
-#line 1032 "parse.y"
+ case 284: /* raisetype ::= FAIL */
+#line 1051 "parse.y"
{yygotominor.yy46 = OE_Fail;}
-#line 3159 "parse.c"
+#line 3204 "parse.c"
break;
- case 283:
-#line 1037 "parse.y"
+ case 285: /* cmd ::= DROP TRIGGER ifexists fullname */
+#line 1056 "parse.y"
{
sqlite3DropTrigger(pParse,yymsp[0].minor.yy373,yymsp[-1].minor.yy46);
}
-#line 3166 "parse.c"
+#line 3211 "parse.c"
break;
- case 284:
-#line 1044 "parse.y"
+ case 286: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+#line 1063 "parse.y"
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, yymsp[0].minor.yy386);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy172, yymsp[-1].minor.yy172, yymsp[0].minor.yy172);
}
-#line 3173 "parse.c"
+#line 3218 "parse.c"
break;
- case 285:
-#line 1047 "parse.y"
+ case 287: /* cmd ::= DETACH database_kw_opt expr */
+#line 1066 "parse.y"
{
sqlite3Detach(pParse, yymsp[0].minor.yy172);
}
-#line 3180 "parse.c"
+#line 3225 "parse.c"
break;
- case 286:
-#line 1053 "parse.y"
-{ yygotominor.yy386 = 0; }
-#line 3185 "parse.c"
- break;
- case 287:
-#line 1054 "parse.y"
-{ yygotominor.yy386 = yymsp[0].minor.yy172; }
-#line 3190 "parse.c"
- break;
- case 290:
-#line 1062 "parse.y"
+ case 292: /* cmd ::= REINDEX */
+#line 1081 "parse.y"
{sqlite3Reindex(pParse, 0, 0);}
-#line 3195 "parse.c"
+#line 3230 "parse.c"
break;
- case 291:
-#line 1063 "parse.y"
-{sqlite3Reindex(pParse, &yymsp[-1].minor.yy410, &yymsp[0].minor.yy410);}
-#line 3200 "parse.c"
+ case 293: /* cmd ::= REINDEX nm dbnm */
+#line 1082 "parse.y"
+{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
+#line 3235 "parse.c"
break;
- case 292:
-#line 1068 "parse.y"
+ case 294: /* cmd ::= ANALYZE */
+#line 1087 "parse.y"
{sqlite3Analyze(pParse, 0, 0);}
-#line 3205 "parse.c"
+#line 3240 "parse.c"
break;
- case 293:
-#line 1069 "parse.y"
-{sqlite3Analyze(pParse, &yymsp[-1].minor.yy410, &yymsp[0].minor.yy410);}
-#line 3210 "parse.c"
+ case 295: /* cmd ::= ANALYZE nm dbnm */
+#line 1088 "parse.y"
+{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
+#line 3245 "parse.c"
break;
- case 294:
-#line 1074 "parse.y"
+ case 296: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+#line 1093 "parse.y"
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy373,&yymsp[0].minor.yy410);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy373,&yymsp[0].minor.yy0);
}
-#line 3217 "parse.c"
+#line 3252 "parse.c"
break;
- case 295:
-#line 1077 "parse.y"
+ case 297: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */
+#line 1096 "parse.y"
{
- sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy410);
+ sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0);
}
-#line 3224 "parse.c"
+#line 3259 "parse.c"
break;
- case 296:
-#line 1080 "parse.y"
+ case 298: /* add_column_fullname ::= fullname */
+#line 1099 "parse.y"
{
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy373);
}
-#line 3231 "parse.c"
+#line 3266 "parse.c"
break;
- case 299:
-#line 1089 "parse.y"
+ case 301: /* cmd ::= create_vtab */
+#line 1108 "parse.y"
{sqlite3VtabFinishParse(pParse,0);}
-#line 3236 "parse.c"
+#line 3271 "parse.c"
break;
- case 300:
-#line 1090 "parse.y"
+ case 302: /* cmd ::= create_vtab LP vtabarglist RP */
+#line 1109 "parse.y"
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
-#line 3241 "parse.c"
+#line 3276 "parse.c"
break;
- case 301:
-#line 1091 "parse.y"
+ case 303: /* create_vtab ::= CREATE VIRTUAL TABLE nm dbnm USING nm */
+#line 1110 "parse.y"
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy410, &yymsp[-2].minor.yy410, &yymsp[0].minor.yy410);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
-#line 3248 "parse.c"
+#line 3283 "parse.c"
break;
- case 304:
-#line 1096 "parse.y"
+ case 306: /* vtabarg ::= */
+#line 1115 "parse.y"
{sqlite3VtabArgInit(pParse);}
-#line 3253 "parse.c"
+#line 3288 "parse.c"
break;
- case 306:
- case 307:
- case 308:
- case 310:
-#line 1098 "parse.y"
+ case 308: /* vtabargtoken ::= ANY */
+ case 309: /* vtabargtoken ::= lp anylist RP */
+ case 310: /* lp ::= LP */
+ case 312: /* anylist ::= anylist ANY */
+#line 1117 "parse.y"
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
-#line 3261 "parse.c"
+#line 3296 "parse.c"
break;
};
yygoto = yyRuleInfo[yyruleno].lhs;
@@ -3279,7 +3312,8 @@ static void yy_reduce(
{
yy_shift(yypParser,yyact,yygoto,&yygotominor);
}
- }else if( yyact == YYNSTATE + YYNRULE + 1 ){
+ }else{
+ assert( yyact == YYNSTATE + YYNRULE + 1 );
yy_accept(yypParser);
}
}
@@ -3314,15 +3348,10 @@ static void yy_syntax_error(
#define TOKEN (yyminor.yy0)
#line 34 "parse.y"
- if( !pParse->parseError ){
- if( TOKEN.z[0] ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
- }else{
- sqlite3ErrorMsg(pParse, "incomplete SQL statement");
- }
- pParse->parseError = 1;
- }
-#line 3329 "parse.c"
+ assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+ pParse->parseError = 1;
+#line 3361 "parse.c"
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
@@ -3372,7 +3401,9 @@ void sqlite3Parser(
YYMINORTYPE yyminorunion;
int yyact; /* The parser action. */
int yyendofinput; /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
int yyerrorhit = 0; /* True if yymajor has invoked an error */
+#endif
yyParser *yypParser; /* The parser */
/* (re)initialize the parser, if necessary */
@@ -3380,7 +3411,8 @@ void sqlite3Parser(
if( yypParser->yyidx<0 ){
#if YYSTACKDEPTH<=0
if( yypParser->yystksz <=0 ){
- memset(&yyminorunion, 0, sizeof(yyminorunion));
+ /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
+ yyminorunion = yyzerominor;
yyStackOverflow(yypParser, &yyminorunion);
return;
}
@@ -3403,17 +3435,17 @@ void sqlite3Parser(
do{
yyact = yy_find_shift_action(yypParser,yymajor);
if( yyact<YYNSTATE ){
+ assert( !yyendofinput ); /* Impossible to shift the $ token */
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
yypParser->yyerrcnt--;
- if( yyendofinput && yypParser->yyidx>=0 ){
- yymajor = 0;
- }else{
- yymajor = YYNOCODE;
- }
+ yymajor = YYNOCODE;
}else if( yyact < YYNSTATE + YYNRULE ){
yy_reduce(yypParser,yyact-YYNSTATE);
- }else if( yyact == YY_ERROR_ACTION ){
+ }else{
+ assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
int yymx;
+#endif
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
@@ -3450,7 +3482,7 @@ void sqlite3Parser(
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
- yy_destructor(yymajor,&yyminorunion);
+ yy_destructor(yypParser, yymajor,&yyminorunion);
yymajor = YYNOCODE;
}else{
while(
@@ -3463,7 +3495,7 @@ void sqlite3Parser(
yy_pop_parser_stack(yypParser);
}
if( yypParser->yyidx < 0 || yymajor==0 ){
- yy_destructor(yymajor,&yyminorunion);
+ yy_destructor(yypParser,yymajor,&yyminorunion);
yy_parse_failed(yypParser);
yymajor = YYNOCODE;
}else if( yymx!=YYERRORSYMBOL ){
@@ -3488,15 +3520,12 @@ void sqlite3Parser(
yy_syntax_error(yypParser,yymajor,yyminorunion);
}
yypParser->yyerrcnt = 3;
- yy_destructor(yymajor,&yyminorunion);
+ yy_destructor(yypParser,yymajor,&yyminorunion);
if( yyendofinput ){
yy_parse_failed(yypParser);
}
yymajor = YYNOCODE;
#endif
- }else{
- yy_accept(yypParser);
- yymajor = YYNOCODE;
}
}while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
return;
diff --git a/third_party/sqlite/parse.h b/third_party/sqlite/preprocessed/parse.h
index ed848ec..ed848ec 100644..100755
--- a/third_party/sqlite/parse.h
+++ b/third_party/sqlite/preprocessed/parse.h
diff --git a/third_party/sqlite/preprocessed/sqlite3.h b/third_party/sqlite/preprocessed/sqlite3.h
new file mode 100755
index 0000000..8c9588b
--- /dev/null
+++ b/third_party/sqlite/preprocessed/sqlite3.h
@@ -0,0 +1,6294 @@
+/*
+** 2001 September 15
+**
+** 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 header file defines the interface that the SQLite library
+** presents to client programs. If a C-function, structure, datatype,
+** or constant definition does not appear in this file, then it is
+** not a published API of SQLite, is subject to change without
+** notice, and should not be referenced by programs that use SQLite.
+**
+** Some of the definitions that are in this file are marked as
+** "experimental". Experimental interfaces are normally new
+** features recently added to SQLite. We do not anticipate changes
+** to experimental interfaces but reserve to make minor changes if
+** experience from use "in the wild" suggest such changes are prudent.
+**
+** The official C-language API documentation for SQLite is derived
+** from comments in this file. This file is the authoritative source
+** on how SQLite interfaces are suppose to operate.
+**
+** The name of this file under configuration management is "sqlite.h.in".
+** The makefile makes some minor changes to this file (such as inserting
+** the version number) and changes its name to "sqlite3.h" as
+** part of the build process.
+**
+** @(#) $Id: sqlite.h.in,v 1.388 2008/08/06 13:40:13 danielk1977 Exp $
+*/
+#ifndef _SQLITE3_H_
+#define _SQLITE3_H_
+#include <stdarg.h> /* Needed for the definition of va_list */
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** Add the ability to override 'extern'
+*/
+#ifndef SQLITE_EXTERN
+# define SQLITE_EXTERN extern
+#endif
+
+/*
+** Ensure these symbols were not defined by some previous header file.
+*/
+#ifdef SQLITE_VERSION
+# undef SQLITE_VERSION
+#endif
+#ifdef SQLITE_VERSION_NUMBER
+# undef SQLITE_VERSION_NUMBER
+#endif
+
+/*
+** CAPI3REF: Compile-Time Library Version Numbers {H10010} <S60100>
+**
+** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in
+** the sqlite3.h file specify the version of SQLite with which
+** that header file is associated.
+**
+** The "version" of SQLite is a string of the form "X.Y.Z".
+** The phrase "alpha" or "beta" might be appended after the Z.
+** The X value is major version number always 3 in SQLite3.
+** The X value only changes when backwards compatibility is
+** broken and we intend to never break backwards compatibility.
+** The Y value is the minor version number and only changes when
+** there are major feature enhancements that are forwards compatible
+** but not backwards compatible.
+** The Z value is the release number and is incremented with
+** each release but resets back to 0 whenever Y is incremented.
+**
+** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()].
+**
+** INVARIANTS:
+**
+** {H10011} The SQLITE_VERSION #define in the sqlite3.h header file shall
+** evaluate to a string literal that is the SQLite version
+** with which the header file is associated.
+**
+** {H10014} The SQLITE_VERSION_NUMBER #define shall resolve to an integer
+** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z
+** are the major version, minor version, and release number.
+*/
+#define SQLITE_VERSION "3.6.1"
+#define SQLITE_VERSION_NUMBER 3006001
+
+/*
+** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
+** KEYWORDS: sqlite3_version
+**
+** These features provide the same information as the [SQLITE_VERSION]
+** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated
+** with the library instead of the header file. Cautious programmers might
+** include a check in their application to verify that
+** sqlite3_libversion_number() always returns the value
+** [SQLITE_VERSION_NUMBER].
+**
+** The sqlite3_libversion() function returns the same information as is
+** in the sqlite3_version[] string constant. The function is provided
+** for use in DLLs since DLL users usually do not have direct access to string
+** constants within the DLL.
+**
+** INVARIANTS:
+**
+** {H10021} The [sqlite3_libversion_number()] interface shall return
+** an integer equal to [SQLITE_VERSION_NUMBER].
+**
+** {H10022} The [sqlite3_version] string constant shall contain
+** the text of the [SQLITE_VERSION] string.
+**
+** {H10023} The [sqlite3_libversion()] function shall return
+** a pointer to the [sqlite3_version] string constant.
+*/
+SQLITE_EXTERN const char sqlite3_version[];
+const char *sqlite3_libversion(void);
+int sqlite3_libversion_number(void);
+
+/*
+** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} <S60100>
+**
+** SQLite can be compiled with or without mutexes. When
+** the [SQLITE_THREADSAFE] C preprocessor macro is true, mutexes
+** are enabled and SQLite is threadsafe. When that macro is false,
+** the mutexes are omitted. Without the mutexes, it is not safe
+** to use SQLite concurrently from more than one thread.
+**
+** Enabling mutexes incurs a measurable performance penalty.
+** So if speed is of utmost importance, it makes sense to disable
+** the mutexes. But for maximum safety, mutexes should be enabled.
+** The default behavior is for mutexes to be enabled.
+**
+** This interface can be used by a program to make sure that the
+** version of SQLite that it is linking against was compiled with
+** the desired setting of the [SQLITE_THREADSAFE] macro.
+**
+** This interface only reports on the compile-time mutex setting
+** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with
+** SQLITE_THREADSAFE=1 then mutexes are enabled by default but
+** can be fully or partially disabled using a call to [sqlite3_config()]
+** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],
+** or [SQLITE_CONFIG_MUTEX]. The return value of this function shows
+** only the default compile-time setting, not any run-time changes
+** to that setting.
+**
+** INVARIANTS:
+**
+** {H10101} The [sqlite3_threadsafe()] function shall return nonzero if
+** SQLite was compiled with the its mutexes enabled by default
+** or zero if SQLite was compiled such that mutexes are
+** permanently disabled.
+**
+** {H10102} The value returned by the [sqlite3_threadsafe()] function
+** shall not change when mutex setting are modified at
+** runtime using the [sqlite3_config()] interface and
+** especially the [SQLITE_CONFIG_SINGLETHREAD],
+** [SQLITE_CONFIG_MULTITHREAD], [SQLITE_CONFIG_SERIALIZED],
+** and [SQLITE_CONFIG_MUTEX] verbs.
+*/
+int sqlite3_threadsafe(void);
+
+/*
+** CAPI3REF: Database Connection Handle {H12000} <S40200>
+** KEYWORDS: {database connection} {database connections}
+**
+** Each open SQLite database is represented by a pointer to an instance of
+** the opaque structure named "sqlite3". It is useful to think of an sqlite3
+** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
+** is its destructor. There are many other interfaces (such as
+** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
+** [sqlite3_busy_timeout()] to name but three) that are methods on an
+** sqlite3 object.
+*/
+typedef struct sqlite3 sqlite3;
+
+/*
+** CAPI3REF: 64-Bit Integer Types {H10200} <S10110>
+** KEYWORDS: sqlite_int64 sqlite_uint64
+**
+** Because there is no cross-platform way to specify 64-bit integer types
+** SQLite includes typedefs for 64-bit signed and unsigned integers.
+**
+** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions.
+** The sqlite_int64 and sqlite_uint64 types are supported for backwards
+** compatibility only.
+**
+** INVARIANTS:
+**
+** {H10201} The [sqlite_int64] and [sqlite3_int64] type shall specify
+** a 64-bit signed integer.
+**
+** {H10202} The [sqlite_uint64] and [sqlite3_uint64] type shall specify
+** a 64-bit unsigned integer.
+*/
+#ifdef SQLITE_INT64_TYPE
+ typedef SQLITE_INT64_TYPE sqlite_int64;
+ typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
+#elif defined(_MSC_VER) || defined(__BORLANDC__)
+ typedef __int64 sqlite_int64;
+ typedef unsigned __int64 sqlite_uint64;
+#else
+ typedef long long int sqlite_int64;
+ typedef unsigned long long int sqlite_uint64;
+#endif
+typedef sqlite_int64 sqlite3_int64;
+typedef sqlite_uint64 sqlite3_uint64;
+
+/*
+** If compiling for a processor that lacks floating point support,
+** substitute integer for floating-point.
+*/
+#ifdef SQLITE_OMIT_FLOATING_POINT
+# define double sqlite3_int64
+#endif
+
+/*
+** CAPI3REF: Closing A Database Connection {H12010} <S30100><S40200>
+**
+** This routine is the destructor for the [sqlite3] object.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements]
+** and [sqlite3_blob_close | close] all [BLOB handles] associated with
+** the [sqlite3] object prior to attempting to close the object.
+** The [sqlite3_next_stmt()] interface can be used to locate all
+** [prepared statements] associated with a [database connection] if desired.
+** Typical code might look like this:
+**
+** <blockquote><pre>
+** sqlite3_stmt *pStmt;
+** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
+** &nbsp; sqlite3_finalize(pStmt);
+** }
+** </pre></blockquote>
+**
+** If [sqlite3_close()] is invoked while a transaction is open,
+** the transaction is automatically rolled back.
+**
+** INVARIANTS:
+**
+** {H12011} A successful call to [sqlite3_close(C)] shall destroy the
+** [database connection] object C.
+**
+** {H12012} A successful call to [sqlite3_close(C)] shall return SQLITE_OK.
+**
+** {H12013} A successful call to [sqlite3_close(C)] shall release all
+** memory and system resources associated with [database connection]
+** C.
+**
+** {H12014} A call to [sqlite3_close(C)] on a [database connection] C that
+** has one or more open [prepared statements] shall fail with
+** an [SQLITE_BUSY] error code.
+**
+** {H12015} A call to [sqlite3_close(C)] where C is a NULL pointer shall
+** return SQLITE_OK.
+**
+** {H12019} When [sqlite3_close(C)] is invoked on a [database connection] C
+** that has a pending transaction, the transaction shall be
+** rolled back.
+**
+** ASSUMPTIONS:
+**
+** {A12016} The C parameter to [sqlite3_close(C)] must be either a NULL
+** pointer or an [sqlite3] object pointer obtained
+** from [sqlite3_open()], [sqlite3_open16()], or
+** [sqlite3_open_v2()], and not previously closed.
+*/
+int sqlite3_close(sqlite3 *);
+
+/*
+** The type for a callback function.
+** This is legacy and deprecated. It is included for historical
+** compatibility and is not documented.
+*/
+typedef int (*sqlite3_callback)(void*,int,char**, char**);
+
+/*
+** CAPI3REF: One-Step Query Execution Interface {H12100} <S10000>
+**
+** The sqlite3_exec() interface is a convenient way of running one or more
+** SQL statements without having to write a lot of C code. The UTF-8 encoded
+** SQL statements are passed in as the second parameter to sqlite3_exec().
+** The statements are evaluated one by one until either an error or
+** an interrupt is encountered, or until they are all done. The 3rd parameter
+** is an optional callback that is invoked once for each row of any query
+** results produced by the SQL statements. The 5th parameter tells where
+** to write any error messages.
+**
+** The error message passed back through the 5th parameter is held
+** in memory obtained from [sqlite3_malloc()]. To avoid a memory leak,
+** the calling application should call [sqlite3_free()] on any error
+** message returned through the 5th parameter when it has finished using
+** the error message.
+**
+** If the SQL statement in the 2nd parameter is NULL or an empty string
+** or a string containing only whitespace and comments, then no SQL
+** statements are evaluated and the database is not changed.
+**
+** The sqlite3_exec() interface is implemented in terms of
+** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()].
+** The sqlite3_exec() routine does nothing to the database that cannot be done
+** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()].
+**
+** INVARIANTS:
+**
+** {H12101} A successful invocation of [sqlite3_exec(D,S,C,A,E)]
+** shall sequentially evaluate all of the UTF-8 encoded,
+** semicolon-separated SQL statements in the zero-terminated
+** string S within the context of the [database connection] D.
+**
+** {H12102} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL then
+** the actions of the interface shall be the same as if the
+** S parameter were an empty string.
+**
+** {H12104} The return value of [sqlite3_exec()] shall be [SQLITE_OK] if all
+** SQL statements run successfully and to completion.
+**
+** {H12105} The return value of [sqlite3_exec()] shall be an appropriate
+** non-zero [error code] if any SQL statement fails.
+**
+** {H12107} If one or more of the SQL statements handed to [sqlite3_exec()]
+** return results and the 3rd parameter is not NULL, then
+** the callback function specified by the 3rd parameter shall be
+** invoked once for each row of result.
+**
+** {H12110} If the callback returns a non-zero value then [sqlite3_exec()]
+** shall abort the SQL statement it is currently evaluating,
+** skip all subsequent SQL statements, and return [SQLITE_ABORT].
+**
+** {H12113} The [sqlite3_exec()] routine shall pass its 4th parameter through
+** as the 1st parameter of the callback.
+**
+** {H12116} The [sqlite3_exec()] routine shall set the 2nd parameter of its
+** callback to be the number of columns in the current row of
+** result.
+**
+** {H12119} The [sqlite3_exec()] routine shall set the 3rd parameter of its
+** callback to be an array of pointers to strings holding the
+** values for each column in the current result set row as
+** obtained from [sqlite3_column_text()].
+**
+** {H12122} The [sqlite3_exec()] routine shall set the 4th parameter of its
+** callback to be an array of pointers to strings holding the
+** names of result columns as obtained from [sqlite3_column_name()].
+**
+** {H12125} If the 3rd parameter to [sqlite3_exec()] is NULL then
+** [sqlite3_exec()] shall silently discard query results.
+**
+** {H12131} If an error occurs while parsing or evaluating any of the SQL
+** statements in the S parameter of [sqlite3_exec(D,S,C,A,E)] and if
+** the E parameter is not NULL, then [sqlite3_exec()] shall store
+** in *E an appropriate error message written into memory obtained
+** from [sqlite3_malloc()].
+**
+** {H12134} The [sqlite3_exec(D,S,C,A,E)] routine shall set the value of
+** *E to NULL if E is not NULL and there are no errors.
+**
+** {H12137} The [sqlite3_exec(D,S,C,A,E)] function shall set the [error code]
+** and message accessible via [sqlite3_errcode()],
+** [sqlite3_errmsg()], and [sqlite3_errmsg16()].
+**
+** {H12138} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL or an
+** empty string or contains nothing other than whitespace, comments,
+** and/or semicolons, then results of [sqlite3_errcode()],
+** [sqlite3_errmsg()], and [sqlite3_errmsg16()]
+** shall reset to indicate no errors.
+**
+** ASSUMPTIONS:
+**
+** {A12141} The first parameter to [sqlite3_exec()] must be an valid and open
+** [database connection].
+**
+** {A12142} The database connection must not be closed while
+** [sqlite3_exec()] is running.
+**
+** {A12143} The calling function should use [sqlite3_free()] to free
+** the memory that *errmsg is left pointing at once the error
+** message is no longer needed.
+**
+** {A12145} The SQL statement text in the 2nd parameter to [sqlite3_exec()]
+** must remain unchanged while [sqlite3_exec()] is running.
+*/
+int sqlite3_exec(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be evaluated */
+ int (*callback)(void*,int,char**,char**), /* Callback function */
+ void *, /* 1st argument to callback */
+ char **errmsg /* Error msg written here */
+);
+
+/*
+** CAPI3REF: Result Codes {H10210} <S10700>
+** KEYWORDS: SQLITE_OK {error code} {error codes}
+** KEYWORDS: {result code} {result codes}
+**
+** Many SQLite functions return an integer result code from the set shown
+** here in order to indicates success or failure.
+**
+** New error codes may be added in future versions of SQLite.
+**
+** See also: [SQLITE_IOERR_READ | extended result codes]
+*/
+#define SQLITE_OK 0 /* Successful result */
+/* beginning-of-error-codes */
+#define SQLITE_ERROR 1 /* SQL error or missing database */
+#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */
+#define SQLITE_PERM 3 /* Access permission denied */
+#define SQLITE_ABORT 4 /* Callback routine requested an abort */
+#define SQLITE_BUSY 5 /* The database file is locked */
+#define SQLITE_LOCKED 6 /* A table in the database is locked */
+#define SQLITE_NOMEM 7 /* A malloc() failed */
+#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
+#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
+#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
+#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
+#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */
+#define SQLITE_FULL 13 /* Insertion failed because database is full */
+#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
+#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */
+#define SQLITE_EMPTY 16 /* Database is empty */
+#define SQLITE_SCHEMA 17 /* The database schema changed */
+#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
+#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
+#define SQLITE_MISMATCH 20 /* Data type mismatch */
+#define SQLITE_MISUSE 21 /* Library used incorrectly */
+#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
+#define SQLITE_AUTH 23 /* Authorization denied */
+#define SQLITE_FORMAT 24 /* Auxiliary database format error */
+#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
+#define SQLITE_NOTADB 26 /* File opened that is not a database file */
+#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
+#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
+/* end-of-error-codes */
+
+/*
+** CAPI3REF: Extended Result Codes {H10220} <S10700>
+** KEYWORDS: {extended error code} {extended error codes}
+** KEYWORDS: {extended result code} {extended result codes}
+**
+** In its default configuration, SQLite API routines return one of 26 integer
+** [SQLITE_OK | result codes]. However, experience has shown that many of
+** these result codes are too coarse-grained. They do not provide as
+** much information about problems as programmers might like. In an effort to
+** address this, newer versions of SQLite (version 3.3.8 and later) include
+** support for additional result codes that provide more detailed information
+** about errors. The extended result codes are enabled or disabled
+** on a per database connection basis using the
+** [sqlite3_extended_result_codes()] API.
+**
+** Some of the available extended result codes are listed here.
+** One may expect the number of extended result codes will be expand
+** over time. Software that uses extended result codes should expect
+** to see new result codes in future releases of SQLite.
+**
+** The SQLITE_OK result code will never be extended. It will always
+** be exactly zero.
+**
+** INVARIANTS:
+**
+** {H10223} The symbolic name for an extended result code shall contains
+** a related primary result code as a prefix.
+**
+** {H10224} Primary result code names shall contain a single "_" character.
+**
+** {H10225} Extended result code names shall contain two or more "_" characters.
+**
+** {H10226} The numeric value of an extended result code shall contain the
+** numeric value of its corresponding primary result code in
+** its least significant 8 bits.
+*/
+#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
+#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
+#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
+#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
+#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
+#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8))
+#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8))
+#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8))
+#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8))
+#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8))
+#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8))
+#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
+#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
+#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
+
+/*
+** CAPI3REF: Flags For File Open Operations {H10230} <H11120> <H12700>
+**
+** These bit values are intended for use in the
+** 3rd parameter to the [sqlite3_open_v2()] interface and
+** in the 4th parameter to the xOpen method of the
+** [sqlite3_vfs] object.
+*/
+#define SQLITE_OPEN_READONLY 0x00000001
+#define SQLITE_OPEN_READWRITE 0x00000002
+#define SQLITE_OPEN_CREATE 0x00000004
+#define SQLITE_OPEN_DELETEONCLOSE 0x00000008
+#define SQLITE_OPEN_EXCLUSIVE 0x00000010
+#define SQLITE_OPEN_MAIN_DB 0x00000100
+#define SQLITE_OPEN_TEMP_DB 0x00000200
+#define SQLITE_OPEN_TRANSIENT_DB 0x00000400
+#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800
+#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000
+#define SQLITE_OPEN_SUBJOURNAL 0x00002000
+#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000
+#define SQLITE_OPEN_NOMUTEX 0x00008000
+
+/*
+** CAPI3REF: Device Characteristics {H10240} <H11120>
+**
+** The xDeviceCapabilities method of the [sqlite3_io_methods]
+** object returns an integer which is a vector of the these
+** bit values expressing I/O characteristics of the mass storage
+** device that holds the file that the [sqlite3_io_methods]
+** refers to.
+**
+** The SQLITE_IOCAP_ATOMIC property means that all writes of
+** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().
+*/
+#define SQLITE_IOCAP_ATOMIC 0x00000001
+#define SQLITE_IOCAP_ATOMIC512 0x00000002
+#define SQLITE_IOCAP_ATOMIC1K 0x00000004
+#define SQLITE_IOCAP_ATOMIC2K 0x00000008
+#define SQLITE_IOCAP_ATOMIC4K 0x00000010
+#define SQLITE_IOCAP_ATOMIC8K 0x00000020
+#define SQLITE_IOCAP_ATOMIC16K 0x00000040
+#define SQLITE_IOCAP_ATOMIC32K 0x00000080
+#define SQLITE_IOCAP_ATOMIC64K 0x00000100
+#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
+#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
+
+/*
+** CAPI3REF: File Locking Levels {H10250} <H11120> <H11310>
+**
+** SQLite uses one of these integer values as the second
+** argument to calls it makes to the xLock() and xUnlock() methods
+** of an [sqlite3_io_methods] object.
+*/
+#define SQLITE_LOCK_NONE 0
+#define SQLITE_LOCK_SHARED 1
+#define SQLITE_LOCK_RESERVED 2
+#define SQLITE_LOCK_PENDING 3
+#define SQLITE_LOCK_EXCLUSIVE 4
+
+/*
+** CAPI3REF: Synchronization Type Flags {H10260} <H11120>
+**
+** When SQLite invokes the xSync() method of an
+** [sqlite3_io_methods] object it uses a combination of
+** these integer values as the second argument.
+**
+** When the SQLITE_SYNC_DATAONLY flag is used, it means that the
+** sync operation only needs to flush data to mass storage. Inode
+** information need not be flushed. The SQLITE_SYNC_NORMAL flag means
+** to use normal fsync() semantics. The SQLITE_SYNC_FULL flag means
+** to use Mac OS-X style fullsync instead of fsync().
+*/
+#define SQLITE_SYNC_NORMAL 0x00002
+#define SQLITE_SYNC_FULL 0x00003
+#define SQLITE_SYNC_DATAONLY 0x00010
+
+/*
+** CAPI3REF: OS Interface Open File Handle {H11110} <S20110>
+**
+** An [sqlite3_file] object represents an open file in the OS
+** interface layer. Individual OS interface implementations will
+** want to subclass this object by appending additional fields
+** for their own use. The pMethods entry is a pointer to an
+** [sqlite3_io_methods] object that defines methods for performing
+** I/O operations on the open file.
+*/
+typedef struct sqlite3_file sqlite3_file;
+struct sqlite3_file {
+ const struct sqlite3_io_methods *pMethods; /* Methods for an open file */
+};
+
+/*
+** CAPI3REF: OS Interface File Virtual Methods Object {H11120} <S20110>
+**
+** Every file opened by the [sqlite3_vfs] xOpen method populates an
+** [sqlite3_file] object (or, more commonly, a subclass of the
+** [sqlite3_file] object) with a pointer to an instance of this object.
+** This object defines the methods used to perform various operations
+** against the open file represented by the [sqlite3_file] object.
+**
+** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
+** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
+** The second choice is a Mac OS-X style fullsync. The [SQLITE_SYNC_DATAONLY]
+** flag may be ORed in to indicate that only the data of the file
+** and not its inode needs to be synced.
+**
+** The integer values to xLock() and xUnlock() are one of
+** <ul>
+** <li> [SQLITE_LOCK_NONE],
+** <li> [SQLITE_LOCK_SHARED],
+** <li> [SQLITE_LOCK_RESERVED],
+** <li> [SQLITE_LOCK_PENDING], or
+** <li> [SQLITE_LOCK_EXCLUSIVE].
+** </ul>
+** xLock() increases the lock. xUnlock() decreases the lock.
+** The xCheckReservedLock() method checks whether any database connection,
+** either in this process or in some other process, is holding a RESERVED,
+** PENDING, or EXCLUSIVE lock on the file. It returns true
+** if such a lock exists and false otherwise.
+**
+** The xFileControl() method is a generic interface that allows custom
+** VFS implementations to directly control an open file using the
+** [sqlite3_file_control()] interface. The second "op" argument is an
+** integer opcode. The third argument is a generic pointer intended to
+** point to a structure that may contain arguments or space in which to
+** write return values. Potential uses for xFileControl() might be
+** functions to enable blocking locks with timeouts, to change the
+** locking strategy (for example to use dot-file locks), to inquire
+** about the status of a lock, or to break stale locks. The SQLite
+** core reserves all opcodes less than 100 for its own use.
+** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
+** Applications that define a custom xFileControl method should use opcodes
+** greater than 100 to avoid conflicts.
+**
+** The xSectorSize() method returns the sector size of the
+** device that underlies the file. The sector size is the
+** minimum write that can be performed without disturbing
+** other bytes in the file. The xDeviceCharacteristics()
+** method returns a bit vector describing behaviors of the
+** underlying device:
+**
+** <ul>
+** <li> [SQLITE_IOCAP_ATOMIC]
+** <li> [SQLITE_IOCAP_ATOMIC512]
+** <li> [SQLITE_IOCAP_ATOMIC1K]
+** <li> [SQLITE_IOCAP_ATOMIC2K]
+** <li> [SQLITE_IOCAP_ATOMIC4K]
+** <li> [SQLITE_IOCAP_ATOMIC8K]
+** <li> [SQLITE_IOCAP_ATOMIC16K]
+** <li> [SQLITE_IOCAP_ATOMIC32K]
+** <li> [SQLITE_IOCAP_ATOMIC64K]
+** <li> [SQLITE_IOCAP_SAFE_APPEND]
+** <li> [SQLITE_IOCAP_SEQUENTIAL]
+** </ul>
+**
+** The SQLITE_IOCAP_ATOMIC property means that all writes of
+** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().
+*/
+typedef struct sqlite3_io_methods sqlite3_io_methods;
+struct sqlite3_io_methods {
+ int iVersion;
+ int (*xClose)(sqlite3_file*);
+ int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+ int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+ int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);
+ int (*xSync)(sqlite3_file*, int flags);
+ int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
+ int (*xLock)(sqlite3_file*, int);
+ int (*xUnlock)(sqlite3_file*, int);
+ int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);
+ int (*xFileControl)(sqlite3_file*, int op, void *pArg);
+ int (*xSectorSize)(sqlite3_file*);
+ int (*xDeviceCharacteristics)(sqlite3_file*);
+ /* Additional methods may be added in future releases */
+};
+
+/*
+** CAPI3REF: Standard File Control Opcodes {H11310} <S30800>
+**
+** These integer constants are opcodes for the xFileControl method
+** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]
+** interface.
+**
+** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This
+** opcode causes the xFileControl method to write the current state of
+** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
+** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
+** into an integer that the pArg argument points to. This capability
+** is used during testing and only needs to be supported when SQLITE_TEST
+** is defined.
+*/
+#define SQLITE_FCNTL_LOCKSTATE 1
+
+/*
+** CAPI3REF: Mutex Handle {H17110} <S20130>
+**
+** The mutex module within SQLite defines [sqlite3_mutex] to be an
+** abstract type for a mutex object. The SQLite core never looks
+** at the internal representation of an [sqlite3_mutex]. It only
+** deals with pointers to the [sqlite3_mutex] object.
+**
+** Mutexes are created using [sqlite3_mutex_alloc()].
+*/
+typedef struct sqlite3_mutex sqlite3_mutex;
+
+/*
+** CAPI3REF: OS Interface Object {H11140} <S20100>
+**
+** An instance of the sqlite3_vfs object defines the interface between
+** the SQLite core and the underlying operating system. The "vfs"
+** in the name of the object stands for "virtual file system".
+**
+** The value of the iVersion field is initially 1 but may be larger in
+** future versions of SQLite. Additional fields may be appended to this
+** object when the iVersion value is increased. Note that the structure
+** of the sqlite3_vfs object changes in the transaction between
+** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
+** modified.
+**
+** The szOsFile field is the size of the subclassed [sqlite3_file]
+** structure used by this VFS. mxPathname is the maximum length of
+** a pathname in this VFS.
+**
+** Registered sqlite3_vfs objects are kept on a linked list formed by
+** the pNext pointer. The [sqlite3_vfs_register()]
+** and [sqlite3_vfs_unregister()] interfaces manage this list
+** in a thread-safe way. The [sqlite3_vfs_find()] interface
+** searches the list. Neither the application code nor the VFS
+** implementation should use the pNext pointer.
+**
+** The pNext field is the only field in the sqlite3_vfs
+** structure that SQLite will ever modify. SQLite will only access
+** or modify this field while holding a particular static mutex.
+** The application should never modify anything within the sqlite3_vfs
+** object once the object has been registered.
+**
+** The zName field holds the name of the VFS module. The name must
+** be unique across all VFS modules.
+**
+** {H11141} SQLite will guarantee that the zFilename parameter to xOpen
+** is either a NULL pointer or string obtained
+** from xFullPathname(). SQLite further guarantees that
+** the string will be valid and unchanged until xClose() is
+** called. {END} Because of the previous sentense,
+** the [sqlite3_file] can safely store a pointer to the
+** filename if it needs to remember the filename for some reason.
+** If the zFilename parameter is xOpen is a NULL pointer then xOpen
+** must invite its own temporary name for the file. Whenever the
+** xFilename parameter is NULL it will also be the case that the
+** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].
+**
+** {H11142} The flags argument to xOpen() includes all bits set in
+** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()]
+** or [sqlite3_open16()] is used, then flags includes at least
+** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. {END}
+** If xOpen() opens a file read-only then it sets *pOutFlags to
+** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set.
+**
+** {H11143} SQLite will also add one of the following flags to the xOpen()
+** call, depending on the object being opened:
+**
+** <ul>
+** <li> [SQLITE_OPEN_MAIN_DB]
+** <li> [SQLITE_OPEN_MAIN_JOURNAL]
+** <li> [SQLITE_OPEN_TEMP_DB]
+** <li> [SQLITE_OPEN_TEMP_JOURNAL]
+** <li> [SQLITE_OPEN_TRANSIENT_DB]
+** <li> [SQLITE_OPEN_SUBJOURNAL]
+** <li> [SQLITE_OPEN_MASTER_JOURNAL]
+** </ul> {END}
+**
+** The file I/O implementation can use the object type flags to
+** change the way it deals with files. For example, an application
+** that does not care about crash recovery or rollback might make
+** the open of a journal file a no-op. Writes to this journal would
+** also be no-ops, and any attempt to read the journal would return
+** SQLITE_IOERR. Or the implementation might recognize that a database
+** file will be doing page-aligned sector reads and writes in a random
+** order and set up its I/O subsystem accordingly.
+**
+** SQLite might also add one of the following flags to the xOpen method:
+**
+** <ul>
+** <li> [SQLITE_OPEN_DELETEONCLOSE]
+** <li> [SQLITE_OPEN_EXCLUSIVE]
+** </ul>
+**
+** {H11145} The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be
+** deleted when it is closed. {H11146} The [SQLITE_OPEN_DELETEONCLOSE]
+** will be set for TEMP databases, journals and for subjournals.
+**
+** {H11147} The [SQLITE_OPEN_EXCLUSIVE] flag means the file should be opened
+** for exclusive access. This flag is set for all files except
+** for the main database file.
+**
+** {H11148} At least szOsFile bytes of memory are allocated by SQLite
+** to hold the [sqlite3_file] structure passed as the third
+** argument to xOpen. {END} The xOpen method does not have to
+** allocate the structure; it should just fill it in.
+**
+** {H11149} The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
+** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
+** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
+** to test whether a file is at least readable. {END} The file can be a
+** directory.
+**
+** {H11150} SQLite will always allocate at least mxPathname+1 bytes for the
+** output buffer xFullPathname. {H11151} The exact size of the output buffer
+** is also passed as a parameter to both methods. {END} If the output buffer
+** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is
+** handled as a fatal error by SQLite, vfs implementations should endeavor
+** to prevent this by setting mxPathname to a sufficiently large value.
+**
+** The xRandomness(), xSleep(), and xCurrentTime() interfaces
+** are not strictly a part of the filesystem, but they are
+** included in the VFS structure for completeness.
+** The xRandomness() function attempts to return nBytes bytes
+** of good-quality randomness into zOut. The return value is
+** the actual number of bytes of randomness obtained.
+** The xSleep() method causes the calling thread to sleep for at
+** least the number of microseconds given. The xCurrentTime()
+** method returns a Julian Day Number for the current date and time.
+*/
+typedef struct sqlite3_vfs sqlite3_vfs;
+struct sqlite3_vfs {
+ int iVersion; /* Structure version number */
+ int szOsFile; /* Size of subclassed sqlite3_file */
+ int mxPathname; /* Maximum file pathname length */
+ sqlite3_vfs *pNext; /* Next registered VFS */
+ const char *zName; /* Name of this virtual file system */
+ void *pAppData; /* Pointer to application-specific data */
+ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int flags, int *pOutFlags);
+ int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
+ int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
+ int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
+ void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
+ void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
+ void *(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol);
+ void (*xDlClose)(sqlite3_vfs*, void*);
+ int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
+ int (*xSleep)(sqlite3_vfs*, int microseconds);
+ int (*xCurrentTime)(sqlite3_vfs*, double*);
+ int (*xGetLastError)(sqlite3_vfs*, int, char *);
+ /* New fields may be appended in figure versions. The iVersion
+ ** value will increment whenever this happens. */
+};
+
+/*
+** CAPI3REF: Flags for the xAccess VFS method {H11190} <H11140>
+**
+** {H11191} These integer constants can be used as the third parameter to
+** the xAccess method of an [sqlite3_vfs] object. {END} They determine
+** what kind of permissions the xAccess method is looking for.
+** {H11192} With SQLITE_ACCESS_EXISTS, the xAccess method
+** simply checks whether the file exists.
+** {H11193} With SQLITE_ACCESS_READWRITE, the xAccess method
+** checks whether the file is both readable and writable.
+** {H11194} With SQLITE_ACCESS_READ, the xAccess method
+** checks whether the file is readable.
+*/
+#define SQLITE_ACCESS_EXISTS 0
+#define SQLITE_ACCESS_READWRITE 1
+#define SQLITE_ACCESS_READ 2
+
+/*
+** CAPI3REF: Initialize The SQLite Library {H10130} <S20000><S30100>
+**
+** The sqlite3_initialize() routine initializes the
+** SQLite library. The sqlite3_shutdown() routine
+** deallocates any resources that were allocated by sqlite3_initialize().
+**
+** A call to sqlite3_initialize() is an "effective" call if it is
+** the first time sqlite3_initialize() is invoked during the lifetime of
+** the process, or if it is the first time sqlite3_initialize() is invoked
+** following a call to sqlite3_shutdown(). Only an effective call
+** of sqlite3_initialize() does any initialization. All other calls
+** are harmless no-ops.
+**
+** Among other things, sqlite3_initialize() shall invoke
+** sqlite3_os_init(). Similarly, sqlite3_shutdown()
+** shall invoke sqlite3_os_end().
+**
+** The sqlite3_initialize() routine returns SQLITE_OK on success.
+** If for some reason, sqlite3_initialize() is unable to initialize
+** the library (perhaps it is unable to allocate a needed resource such
+** as a mutex) it returns an [error code] other than SQLITE_OK.
+**
+** The sqlite3_initialize() routine is called internally by many other
+** SQLite interfaces so that an application usually does not need to
+** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
+** calls sqlite3_initialize() so the SQLite library will be automatically
+** initialized when [sqlite3_open()] is called if it has not be initialized
+** already. However, if SQLite is compiled with the SQLITE_OMIT_AUTOINIT
+** compile-time option, then the automatic calls to sqlite3_initialize()
+** are omitted and the application must call sqlite3_initialize() directly
+** prior to using any other SQLite interface. For maximum portability,
+** it is recommended that applications always invoke sqlite3_initialize()
+** directly prior to using any other SQLite interface. Future releases
+** of SQLite may require this. In other words, the behavior exhibited
+** when SQLite is compiled with SQLITE_OMIT_AUTOINIT might become the
+** default behavior in some future release of SQLite.
+**
+** The sqlite3_os_init() routine does operating-system specific
+** initialization of the SQLite library. The sqlite3_os_end()
+** routine undoes the effect of sqlite3_os_init(). Typical tasks
+** performed by these routines include allocation or deallocation
+** of static resources, initialization of global variables,
+** setting up a default [sqlite3_vfs] module, or setting up
+** a default configuration using [sqlite3_config()].
+**
+** The application should never invoke either sqlite3_os_init()
+** or sqlite3_os_end() directly. The application should only invoke
+** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init()
+** interface is called automatically by sqlite3_initialize() and
+** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate
+** implementations for sqlite3_os_init() and sqlite3_os_end()
+** are built into SQLite when it is compiled for unix, windows, or os/2.
+** When built for other platforms (using the SQLITE_OS_OTHER=1 compile-time
+** option) the application must supply a suitable implementation for
+** sqlite3_os_init() and sqlite3_os_end(). An application-supplied
+** implementation of sqlite3_os_init() or sqlite3_os_end()
+** must return SQLITE_OK on success and some other [error code] upon
+** failure.
+*/
+int sqlite3_initialize(void);
+int sqlite3_shutdown(void);
+int sqlite3_os_init(void);
+int sqlite3_os_end(void);
+
+/*
+** CAPI3REF: Configuring The SQLite Library {H10145} <S20000><S30200>
+** EXPERIMENTAL
+**
+** The sqlite3_config() interface is used to make global configuration
+** changes to SQLite in order to tune SQLite to the specific needs of
+** the application. The default configuration is recommended for most
+** applications and so this routine is usually not necessary. It is
+** provided to support rare applications with unusual needs.
+**
+** The sqlite3_config() interface is not threadsafe. The application
+** must insure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** may only be invoked prior to library initialization using
+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
+** Note, however, that sqlite3_config() can be called as part of the
+** implementation of an application-defined [sqlite3_os_init()].
+**
+** The first argument to sqlite3_config() is an integer
+** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines
+** what property of SQLite is to be configured. Subsequent arguments
+** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option]
+** in the first argument.
+**
+** When a configuration option is set, sqlite3_config() returns SQLITE_OK.
+** If the option is unknown or SQLite is unable to set the option
+** then this routine returns a non-zero [error code].
+*/
+int sqlite3_config(int, ...);
+
+/*
+** CAPI3REF: Configure database connections {H10180} <S20000>
+** EXPERIMENTAL
+**
+** The sqlite3_db_config() interface is used to make configuration
+** changes to a [database connection]. The interface is similar to
+** [sqlite3_config()] except that the changes apply to a single
+** [database connection] (specified in the first argument). The
+** sqlite3_db_config() interface can only be used immediately after
+** the database connection is created using [sqlite3_open()],
+** [sqlite3_open16()], or [sqlite3_open_v2()].
+**
+** The second argument to sqlite3_db_config(D,V,...) is the
+** configuration verb - an integer code that indicates what
+** aspect of the [database connection] is being configured.
+** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE].
+** New verbs are likely to be added in future releases of SQLite.
+** Additional arguments depend on the verb.
+*/
+int sqlite3_db_config(sqlite3*, int op, ...);
+
+/*
+** CAPI3REF: Memory Allocation Routines {H10155} <S20120>
+** EXPERIMENTAL
+**
+** An instance of this object defines the interface between SQLite
+** and low-level memory allocation routines.
+**
+** This object is used in only one place in the SQLite interface.
+** A pointer to an instance of this object is the argument to
+** [sqlite3_config()] when the configuration option is
+** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object
+** and passing it to [sqlite3_config()] during configuration, an
+** application can specify an alternative memory allocation subsystem
+** for SQLite to use for all of its dynamic memory needs.
+**
+** Note that SQLite comes with a built-in memory allocator that is
+** perfectly adequate for the overwhelming majority of applications
+** and that this object is only useful to a tiny minority of applications
+** with specialized memory allocation requirements. This object is
+** also used during testing of SQLite in order to specify an alternative
+** memory allocator that simulates memory out-of-memory conditions in
+** order to verify that SQLite recovers gracefully from such
+** conditions.
+**
+** The xMalloc, xFree, and xRealloc methods must work like the
+** malloc(), free(), and realloc() functions from the standard library.
+**
+** xSize should return the allocated size of a memory allocation
+** previously obtained from xMalloc or xRealloc. The allocated size
+** is always at least as big as the requested size but may be larger.
+**
+** The xRoundup method returns what would be the allocated size of
+** a memory allocation given a particular requested size. Most memory
+** allocators round up memory allocations at least to the next multiple
+** of 8. Some allocators round up to a larger multiple or to a power of 2.
+**
+** The xInit method initializes the memory allocator. (For example,
+** it might allocate any require mutexes or initialize internal data
+** structures. The xShutdown method is invoked (indirectly) by
+** [sqlite3_shutdown()] and should deallocate any resources acquired
+** by xInit. The pAppData pointer is used as the only parameter to
+** xInit and xShutdown.
+*/
+typedef struct sqlite3_mem_methods sqlite3_mem_methods;
+struct sqlite3_mem_methods {
+ void *(*xMalloc)(int); /* Memory allocation function */
+ void (*xFree)(void*); /* Free a prior allocation */
+ void *(*xRealloc)(void*,int); /* Resize an allocation */
+ int (*xSize)(void*); /* Return the size of an allocation */
+ int (*xRoundup)(int); /* Round up request size to allocation size */
+ int (*xInit)(void*); /* Initialize the memory allocator */
+ void (*xShutdown)(void*); /* Deinitialize the memory allocator */
+ void *pAppData; /* Argument to xInit() and xShutdown() */
+};
+
+/*
+** CAPI3REF: Configuration Options {H10160} <S20000>
+** EXPERIMENTAL
+**
+** These constants are the available integer configuration options that
+** can be passed as the first argument to the [sqlite3_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued. Applications
+** should check the return code from [sqlite3_config()] to make sure that
+** the call worked. The [sqlite3_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
+** <dd>There are no arguments to this option. This option disables
+** all mutexing and puts SQLite into a mode where it can only be used
+** by a single thread.</dd>
+**
+** <dt>SQLITE_CONFIG_MULTITHREAD</dt>
+** <dd>There are no arguments to this option. This option disables
+** mutexing on [database connection] and [prepared statement] objects.
+** The application is responsible for serializing access to
+** [database connections] and [prepared statements]. But other mutexes
+** are enabled so that SQLite will be safe to use in a multi-threaded
+** environment.</dd>
+**
+** <dt>SQLITE_CONFIG_SERIALIZED</dt>
+** <dd>There are no arguments to this option. This option enables
+** all mutexes including the recursive
+** mutexes on [database connection] and [prepared statement] objects.
+** In this mode (which is the default when SQLite is compiled with
+** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access
+** to [database connections] and [prepared statements] so that the
+** application is free to use the same [database connection] or the
+** same [prepared statement] in different threads at the same time.
+**
+** <p>This configuration option merely sets the default mutex
+** behavior to serialize access to [database connections]. Individual
+** [database connections] can override this setting
+** using the [SQLITE_OPEN_NOMUTEX] flag to [sqlite3_open_v2()].</p></dd>
+**
+** <dt>SQLITE_CONFIG_MALLOC</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mem_methods] structure. The argument specifies
+** alternative low-level memory allocation routines to be used in place of
+** the memory allocation routines built into SQLite.</dd>
+**
+** <dt>SQLITE_CONFIG_GETMALLOC</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
+** structure is filled with the currently defined memory allocation routines.
+** This option can be used to overload the default memory allocation
+** routines with a wrapper that simulations memory allocation failure or
+** tracks memory usage, for example.</dd>
+**
+** <dt>SQLITE_CONFIG_MEMSTATUS</dt>
+** <dd>This option takes single argument of type int, interpreted as a
+** boolean, which enables or disables the collection of memory allocation
+** statistics. When disabled, the following SQLite interfaces become
+** non-operational:
+** <ul>
+** <li> [sqlite3_memory_used()]
+** <li> [sqlite3_memory_highwater()]
+** <li> [sqlite3_soft_heap_limit()]
+** <li> [sqlite3_status()]
+** </ul>
+** </dd>
+**
+** <dt>SQLITE_CONFIG_SCRATCH</dt>
+** <dd>This option specifies a static memory buffer that SQLite can use for
+** scratch memory. There are three arguments: A pointer to the memory, the
+** size of each scratch buffer (sz), and the number of buffers (N). The sz
+** argument must be a multiple of 16. The sz parameter should be a few bytes
+** larger than the actual scratch space required due internal overhead.
+** The first
+** argument should point to an allocation of at least sz*N bytes of memory.
+** SQLite will use no more than one scratch buffer at once per thread, so
+** N should be set to the expected maximum number of threads. The sz
+** parameter should be 6 times the size of the largest database page size.
+** Scratch buffers are used as part of the btree balance operation. If
+** The btree balancer needs additional memory beyond what is provided by
+** scratch buffers or if no scratch buffer space is specified, then SQLite
+** goes to [sqlite3_malloc()] to obtain the memory it needs.</dd>
+**
+** <dt>SQLITE_CONFIG_PAGECACHE</dt>
+** <dd>This option specifies a static memory buffer that SQLite can use for
+** the database page cache. There are three arguments: A pointer to the
+** memory, the size of each page buffer (sz), and the number of pages (N).
+** The sz argument must be a power of two between 512 and 32768. The first
+** argument should point to an allocation of at least sz*N bytes of memory.
+** SQLite will use the memory provided by the first argument to satisfy its
+** memory needs for the first N pages that it adds to cache. If additional
+** page cache memory is needed beyond what is provided by this option, then
+** SQLite goes to [sqlite3_malloc()] for the additional storage space.
+** The implementation might use one or more of the N buffers to hold
+** memory accounting information. </dd>
+**
+** <dt>SQLITE_CONFIG_HEAP</dt>
+** <dd>This option specifies a static memory buffer that SQLite will use
+** for all of its dynamic memory allocation needs beyond those provided
+** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
+** There are three arguments: A pointer to the memory, the number of
+** bytes in the memory buffer, and the minimum allocation size. If
+** the first pointer (the memory pointer) is NULL, then SQLite reverts
+** to using its default memory allocator (the system malloc() implementation),
+** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. If the
+** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
+** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
+** allocator is engaged to handle all of SQLites memory allocation needs.</dd>
+**
+** <dt>SQLITE_CONFIG_MUTEX</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mutex_methods] structure. The argument specifies
+** alternative low-level mutex routines to be used in place
+** the mutex routines built into SQLite.</dd>
+**
+** <dt>SQLITE_CONFIG_GETMUTEX</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mutex_methods] structure. The
+** [sqlite3_mutex_methods]
+** structure is filled with the currently defined mutex routines.
+** This option can be used to overload the default mutex allocation
+** routines with a wrapper used to track mutex usage for performance
+** profiling or testing, for example.</dd>
+**
+** <dt>SQLITE_CONFIG_LOOKASIDE</dt>
+** <dd>This option takes two arguments that determine the default
+** memory allcation lookaside optimization. The first argument is the
+** size of each lookaside buffer slot and the second is the number of
+** slots allocated to each database connection.</dd>
+**
+** </dl>
+*/
+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */
+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_CHUNKALLOC 12 /* int threshold */
+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
+
+/*
+** CAPI3REF: Configuration Options {H10170} <S20000>
+** EXPERIMENTAL
+**
+** These constants are the available integer configuration options that
+** can be passed as the second argument to the [sqlite3_db_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued. Applications
+** should check the return code from [sqlite3_db_config()] to make sure that
+** the call worked. The [sqlite3_db_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
+** <dd>This option takes three additional arguments that determine the
+** [lookaside memory allocator] configuration for the [database connection].
+** The first argument (the third parameter to [sqlite3_db_config()] is a
+** pointer to a memory buffer to use for lookaside memory. The first
+** argument may be NULL in which case SQLite will allocate the lookaside
+** buffer itself using [sqlite3_malloc()]. The second argument is the
+** size of each lookaside buffer slot and the third argument is the number of
+** slots. The size of the buffer in the first argument must be greater than
+** or equal to the product of the second and third arguments.</dd>
+**
+** </dl>
+*/
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+
+
+/*
+** CAPI3REF: Enable Or Disable Extended Result Codes {H12200} <S10700>
+**
+** The sqlite3_extended_result_codes() routine enables or disables the
+** [extended result codes] feature of SQLite. The extended result
+** codes are disabled by default for historical compatibility considerations.
+**
+** INVARIANTS:
+**
+** {H12201} Each new [database connection] shall have the
+** [extended result codes] feature disabled by default.
+**
+** {H12202} The [sqlite3_extended_result_codes(D,F)] interface shall enable
+** [extended result codes] for the [database connection] D
+** if the F parameter is true, or disable them if F is false.
+*/
+int sqlite3_extended_result_codes(sqlite3*, int onoff);
+
+/*
+** CAPI3REF: Last Insert Rowid {H12220} <S10700>
+**
+** Each entry in an SQLite table has a unique 64-bit signed
+** integer key called the "rowid". The rowid is always available
+** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
+** names are not also used by explicitly declared columns. If
+** the table has a column of type INTEGER PRIMARY KEY then that column
+** is another alias for the rowid.
+**
+** This routine returns the rowid of the most recent
+** successful INSERT into the database from the [database connection]
+** in the first argument. If no successful INSERTs
+** have ever occurred on that database connection, zero is returned.
+**
+** If an INSERT occurs within a trigger, then the rowid of the inserted
+** row is returned by this routine as long as the trigger is running.
+** But once the trigger terminates, the value returned by this routine
+** reverts to the last value inserted before the trigger fired.
+**
+** An INSERT that fails due to a constraint violation is not a
+** successful INSERT and does not change the value returned by this
+** routine. Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK,
+** and INSERT OR ABORT make no changes to the return value of this
+** routine when their insertion fails. When INSERT OR REPLACE
+** encounters a constraint violation, it does not fail. The
+** INSERT continues to completion after deleting rows that caused
+** the constraint problem so INSERT OR REPLACE will always change
+** the return value of this interface.
+**
+** For the purposes of this routine, an INSERT is considered to
+** be successful even if it is subsequently rolled back.
+**
+** INVARIANTS:
+**
+** {H12221} The [sqlite3_last_insert_rowid()] function returns the rowid
+** of the most recent successful INSERT performed on the same
+** [database connection] and within the same or higher level
+** trigger context, or zero if there have been no qualifying inserts.
+**
+** {H12223} The [sqlite3_last_insert_rowid()] function returns the
+** same value when called from the same trigger context
+** immediately before and after a ROLLBACK.
+**
+** ASSUMPTIONS:
+**
+** {A12232} If a separate thread performs a new INSERT on the same
+** database connection while the [sqlite3_last_insert_rowid()]
+** function is running and thus changes the last insert rowid,
+** then the value returned by [sqlite3_last_insert_rowid()] is
+** unpredictable and might not equal either the old or the new
+** last insert rowid.
+*/
+sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
+
+/*
+** CAPI3REF: Count The Number Of Rows Modified {H12240} <S10600>
+**
+** This function returns the number of database rows that were changed
+** or inserted or deleted by the most recently completed SQL statement
+** on the [database connection] specified by the first parameter.
+** Only changes that are directly specified by the INSERT, UPDATE,
+** or DELETE statement are counted. Auxiliary changes caused by
+** triggers are not counted. Use the [sqlite3_total_changes()] function
+** to find the total number of changes including changes caused by triggers.
+**
+** A "row change" is a change to a single row of a single table
+** caused by an INSERT, DELETE, or UPDATE statement. Rows that
+** are changed as side effects of REPLACE constraint resolution,
+** rollback, ABORT processing, DROP TABLE, or by any other
+** mechanisms do not count as direct row changes.
+**
+** A "trigger context" is a scope of execution that begins and
+** ends with the script of a trigger. Most SQL statements are
+** evaluated outside of any trigger. This is the "top level"
+** trigger context. If a trigger fires from the top level, a
+** new trigger context is entered for the duration of that one
+** trigger. Subtriggers create subcontexts for their duration.
+**
+** Calling [sqlite3_exec()] or [sqlite3_step()] recursively does
+** not create a new trigger context.
+**
+** This function returns the number of direct row changes in the
+** most recent INSERT, UPDATE, or DELETE statement within the same
+** trigger context.
+**
+** Thus, when called from the top level, this function returns the
+** number of changes in the most recent INSERT, UPDATE, or DELETE
+** that also occurred at the top level. Within the body of a trigger,
+** the sqlite3_changes() interface can be called to find the number of
+** changes in the most recently completed INSERT, UPDATE, or DELETE
+** statement within the body of the same trigger.
+** However, the number returned does not include changes
+** caused by subtriggers since those have their own context.
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements from the table.) Because of this
+** optimization, the deletions in "DELETE FROM table" are not row changes and
+** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
+** functions, regardless of the number of elements that were originally
+** in the table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+**
+** INVARIANTS:
+**
+** {H12241} The [sqlite3_changes()] function shall return the number of
+** row changes caused by the most recent INSERT, UPDATE,
+** or DELETE statement on the same database connection and
+** within the same or higher trigger context, or zero if there have
+** not been any qualifying row changes.
+**
+** {H12243} Statements of the form "DELETE FROM tablename" with no
+** WHERE clause shall cause subsequent calls to
+** [sqlite3_changes()] to return zero, regardless of the
+** number of rows originally in the table.
+**
+** ASSUMPTIONS:
+**
+** {A12252} If a separate thread makes changes on the same database connection
+** while [sqlite3_changes()] is running then the value returned
+** is unpredictable and not meaningful.
+*/
+int sqlite3_changes(sqlite3*);
+
+/*
+** CAPI3REF: Total Number Of Rows Modified {H12260} <S10600>
+**
+** This function returns the number of row changes caused by INSERT,
+** UPDATE or DELETE statements since the [database connection] was opened.
+** The count includes all changes from all trigger contexts. However,
+** the count does not include changes used to implement REPLACE constraints,
+** do rollbacks or ABORT processing, or DROP table processing.
+** The changes are counted as soon as the statement that makes them is
+** completed (when the statement handle is passed to [sqlite3_reset()] or
+** [sqlite3_finalize()]).
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements from the table.) Because of this
+** optimization, the deletions in "DELETE FROM table" are not row changes and
+** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
+** functions, regardless of the number of elements that were originally
+** in the table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+**
+** See also the [sqlite3_changes()] interface.
+**
+** INVARIANTS:
+**
+** {H12261} The [sqlite3_total_changes()] returns the total number
+** of row changes caused by INSERT, UPDATE, and/or DELETE
+** statements on the same [database connection], in any
+** trigger context, since the database connection was created.
+**
+** {H12263} Statements of the form "DELETE FROM tablename" with no
+** WHERE clause shall not change the value returned
+** by [sqlite3_total_changes()].
+**
+** ASSUMPTIONS:
+**
+** {A12264} If a separate thread makes changes on the same database connection
+** while [sqlite3_total_changes()] is running then the value
+** returned is unpredictable and not meaningful.
+*/
+int sqlite3_total_changes(sqlite3*);
+
+/*
+** CAPI3REF: Interrupt A Long-Running Query {H12270} <S30500>
+**
+** This function causes any pending database operation to abort and
+** return at its earliest opportunity. This routine is typically
+** called in response to a user action such as pressing "Cancel"
+** or Ctrl-C where the user wants a long query operation to halt
+** immediately.
+**
+** It is safe to call this routine from a thread different from the
+** thread that is currently running the database operation. But it
+** is not safe to call this routine with a [database connection] that
+** is closed or might close before sqlite3_interrupt() returns.
+**
+** If an SQL operation is very nearly finished at the time when
+** sqlite3_interrupt() is called, then it might not have an opportunity
+** to be interrupted and might continue to completion.
+**
+** An SQL operation that is interrupted will return [SQLITE_INTERRUPT].
+** If the interrupted SQL operation is an INSERT, UPDATE, or DELETE
+** that is inside an explicit transaction, then the entire transaction
+** will be rolled back automatically.
+**
+** A call to sqlite3_interrupt() has no effect on SQL statements
+** that are started after sqlite3_interrupt() returns.
+**
+** INVARIANTS:
+**
+** {H12271} The [sqlite3_interrupt()] interface will force all running
+** SQL statements associated with the same database connection
+** to halt after processing at most one additional row of data.
+**
+** {H12272} Any SQL statement that is interrupted by [sqlite3_interrupt()]
+** will return [SQLITE_INTERRUPT].
+**
+** ASSUMPTIONS:
+**
+** {A12279} If the database connection closes while [sqlite3_interrupt()]
+** is running then bad things will likely happen.
+*/
+void sqlite3_interrupt(sqlite3*);
+
+/*
+** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} <S70200>
+**
+** These routines are useful for command-line input to determine if the
+** currently entered text seems to form complete a SQL statement or
+** if additional input is needed before sending the text into
+** SQLite for parsing. These routines return true if the input string
+** appears to be a complete SQL statement. A statement is judged to be
+** complete if it ends with a semicolon token and is not a fragment of a
+** CREATE TRIGGER statement. Semicolons that are embedded within
+** string literals or quoted identifier names or comments are not
+** independent tokens (they are part of the token in which they are
+** embedded) and thus do not count as a statement terminator.
+**
+** These routines do not parse the SQL statements thus
+** will not detect syntactically incorrect SQL.
+**
+** INVARIANTS:
+**
+** {H10511} A successful evaluation of [sqlite3_complete()] or
+** [sqlite3_complete16()] functions shall
+** return a numeric 1 if and only if the last non-whitespace
+** token in their input is a semicolon that is not in between
+** the BEGIN and END of a CREATE TRIGGER statement.
+**
+** {H10512} If a memory allocation error occurs during an invocation
+** of [sqlite3_complete()] or [sqlite3_complete16()] then the
+** routine shall return [SQLITE_NOMEM].
+**
+** ASSUMPTIONS:
+**
+** {A10512} The input to [sqlite3_complete()] must be a zero-terminated
+** UTF-8 string.
+**
+** {A10513} The input to [sqlite3_complete16()] must be a zero-terminated
+** UTF-16 string in native byte order.
+*/
+int sqlite3_complete(const char *sql);
+int sqlite3_complete16(const void *sql);
+
+/*
+** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors {H12310} <S40400>
+**
+** This routine sets a callback function that might be invoked whenever
+** an attempt is made to open a database table that another thread
+** or process has locked.
+**
+** If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]
+** is returned immediately upon encountering the lock. If the busy callback
+** is not NULL, then the callback will be invoked with two arguments.
+**
+** The first argument to the handler is a copy of the void* pointer which
+** is the third argument to sqlite3_busy_handler(). The second argument to
+** the handler callback is the number of times that the busy handler has
+** been invoked for this locking event. If the
+** busy callback returns 0, then no additional attempts are made to
+** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.
+** If the callback returns non-zero, then another attempt
+** is made to open the database for reading and the cycle repeats.
+**
+** The presence of a busy handler does not guarantee that it will be invoked
+** when there is lock contention. If SQLite determines that invoking the busy
+** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
+** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler.
+** Consider a scenario where one process is holding a read lock that
+** it is trying to promote to a reserved lock and
+** a second process is holding a reserved lock that it is trying
+** to promote to an exclusive lock. The first process cannot proceed
+** because it is blocked by the second and the second process cannot
+** proceed because it is blocked by the first. If both processes
+** invoke the busy handlers, neither will make any progress. Therefore,
+** SQLite returns [SQLITE_BUSY] for the first process, hoping that this
+** will induce the first process to release its read lock and allow
+** the second process to proceed.
+**
+** The default busy callback is NULL.
+**
+** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED]
+** when SQLite is in the middle of a large transaction where all the
+** changes will not fit into the in-memory cache. SQLite will
+** already hold a RESERVED lock on the database file, but it needs
+** to promote this lock to EXCLUSIVE so that it can spill cache
+** pages into the database file without harm to concurrent
+** readers. If it is unable to promote the lock, then the in-memory
+** cache will be left in an inconsistent state and so the error
+** code is promoted from the relatively benign [SQLITE_BUSY] to
+** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion
+** forces an automatic rollback of the changes. See the
+** <a href="/cvstrac/wiki?p=CorruptionFollowingBusyError">
+** CorruptionFollowingBusyError</a> wiki page for a discussion of why
+** this is important.
+**
+** There can only be a single busy handler defined for each
+** [database connection]. Setting a new busy handler clears any
+** previously set handler. Note that calling [sqlite3_busy_timeout()]
+** will also set or clear the busy handler.
+**
+** INVARIANTS:
+**
+** {H12311} The [sqlite3_busy_handler(D,C,A)] function shall replace
+** busy callback in the [database connection] D with a new
+** a new busy handler C and application data pointer A.
+**
+** {H12312} Newly created [database connections] shall have a busy
+** handler of NULL.
+**
+** {H12314} When two or more [database connections] share a
+** [sqlite3_enable_shared_cache | common cache],
+** the busy handler for the database connection currently using
+** the cache shall be invoked when the cache encounters a lock.
+**
+** {H12316} If a busy handler callback returns zero, then the SQLite interface
+** that provoked the locking event shall return [SQLITE_BUSY].
+**
+** {H12318} SQLite shall invokes the busy handler with two arguments which
+** are a copy of the pointer supplied by the 3rd parameter to
+** [sqlite3_busy_handler()] and a count of the number of prior
+** invocations of the busy handler for the same locking event.
+**
+** ASSUMPTIONS:
+**
+** {A12319} A busy handler must not close the database connection
+** or [prepared statement] that invoked the busy handler.
+*/
+int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+
+/*
+** CAPI3REF: Set A Busy Timeout {H12340} <S40410>
+**
+** This routine sets a [sqlite3_busy_handler | busy handler] that sleeps
+** for a specified amount of time when a table is locked. The handler
+** will sleep multiple times until at least "ms" milliseconds of sleeping
+** have accumulated. {H12343} After "ms" milliseconds of sleeping,
+** the handler returns 0 which causes [sqlite3_step()] to return
+** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
+**
+** Calling this routine with an argument less than or equal to zero
+** turns off all busy handlers.
+**
+** There can only be a single busy handler for a particular
+** [database connection] any any given moment. If another busy handler
+** was defined (using [sqlite3_busy_handler()]) prior to calling
+** this routine, that other busy handler is cleared.
+**
+** INVARIANTS:
+**
+** {H12341} The [sqlite3_busy_timeout()] function shall override any prior
+** [sqlite3_busy_timeout()] or [sqlite3_busy_handler()] setting
+** on the same [database connection].
+**
+** {H12343} If the 2nd parameter to [sqlite3_busy_timeout()] is less than
+** or equal to zero, then the busy handler shall be cleared so that
+** all subsequent locking events immediately return [SQLITE_BUSY].
+**
+** {H12344} If the 2nd parameter to [sqlite3_busy_timeout()] is a positive
+** number N, then a busy handler shall be set that repeatedly calls
+** the xSleep() method in the [sqlite3_vfs | VFS interface] until
+** either the lock clears or until the cumulative sleep time
+** reported back by xSleep() exceeds N milliseconds.
+*/
+int sqlite3_busy_timeout(sqlite3*, int ms);
+
+/*
+** CAPI3REF: Convenience Routines For Running Queries {H12370} <S10000>
+**
+** Definition: A <b>result table</b> is memory data structure created by the
+** [sqlite3_get_table()] interface. A result table records the
+** complete query results from one or more queries.
+**
+** The table conceptually has a number of rows and columns. But
+** these numbers are not part of the result table itself. These
+** numbers are obtained separately. Let N be the number of rows
+** and M be the number of columns.
+**
+** A result table is an array of pointers to zero-terminated UTF-8 strings.
+** There are (N+1)*M elements in the array. The first M pointers point
+** to zero-terminated strings that contain the names of the columns.
+** The remaining entries all point to query results. NULL values result
+** in NULL pointers. All other values are in their UTF-8 zero-terminated
+** string representation as returned by [sqlite3_column_text()].
+**
+** A result table might consist of one or more memory allocations.
+** It is not safe to pass a result table directly to [sqlite3_free()].
+** A result table should be deallocated using [sqlite3_free_table()].
+**
+** As an example of the result table format, suppose a query result
+** is as follows:
+**
+** <blockquote><pre>
+** Name | Age
+** -----------------------
+** Alice | 43
+** Bob | 28
+** Cindy | 21
+** </pre></blockquote>
+**
+** There are two column (M==2) and three rows (N==3). Thus the
+** result table has 8 entries. Suppose the result table is stored
+** in an array names azResult. Then azResult holds this content:
+**
+** <blockquote><pre>
+** azResult&#91;0] = "Name";
+** azResult&#91;1] = "Age";
+** azResult&#91;2] = "Alice";
+** azResult&#91;3] = "43";
+** azResult&#91;4] = "Bob";
+** azResult&#91;5] = "28";
+** azResult&#91;6] = "Cindy";
+** azResult&#91;7] = "21";
+** </pre></blockquote>
+**
+** The sqlite3_get_table() function evaluates one or more
+** semicolon-separated SQL statements in the zero-terminated UTF-8
+** string of its 2nd parameter. It returns a result table to the
+** pointer given in its 3rd parameter.
+**
+** After the calling function has finished using the result, it should
+** pass the pointer to the result table to sqlite3_free_table() in order to
+** release the memory that was malloced. Because of the way the
+** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling
+** function must not try to call [sqlite3_free()] directly. Only
+** [sqlite3_free_table()] is able to release the memory properly and safely.
+**
+** The sqlite3_get_table() interface is implemented as a wrapper around
+** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access
+** to any internal data structures of SQLite. It uses only the public
+** interface defined here. As a consequence, errors that occur in the
+** wrapper layer outside of the internal [sqlite3_exec()] call are not
+** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()].
+**
+** INVARIANTS:
+**
+** {H12371} If a [sqlite3_get_table()] fails a memory allocation, then
+** it shall free the result table under construction, abort the
+** query in process, skip any subsequent queries, set the
+** *pazResult output pointer to NULL and return [SQLITE_NOMEM].
+**
+** {H12373} If the pnColumn parameter to [sqlite3_get_table()] is not NULL
+** then a successful invocation of [sqlite3_get_table()] shall
+** write the number of columns in the
+** result set of the query into *pnColumn.
+**
+** {H12374} If the pnRow parameter to [sqlite3_get_table()] is not NULL
+** then a successful invocation of [sqlite3_get_table()] shall
+** writes the number of rows in the
+** result set of the query into *pnRow.
+**
+** {H12376} A successful invocation of [sqlite3_get_table()] that computes
+** N rows of result with C columns per row shall make *pazResult
+** point to an array of pointers to (N+1)*C strings where the first
+** C strings are column names as obtained from
+** [sqlite3_column_name()] and the rest are column result values
+** obtained from [sqlite3_column_text()].
+**
+** {H12379} The values in the pazResult array returned by [sqlite3_get_table()]
+** shall remain valid until cleared by [sqlite3_free_table()].
+**
+** {H12382} When an error occurs during evaluation of [sqlite3_get_table()]
+** the function shall set *pazResult to NULL, write an error message
+** into memory obtained from [sqlite3_malloc()], make
+** **pzErrmsg point to that error message, and return a
+** appropriate [error code].
+*/
+int sqlite3_get_table(
+ sqlite3 *db, /* An open database */
+ const char *zSql, /* SQL to be evaluated */
+ char ***pazResult, /* Results of the query */
+ int *pnRow, /* Number of result rows written here */
+ int *pnColumn, /* Number of result columns written here */
+ char **pzErrmsg /* Error msg written here */
+);
+void sqlite3_free_table(char **result);
+
+/*
+** CAPI3REF: Formatted String Printing Functions {H17400} <S70000><S20000>
+**
+** These routines are workalikes of the "printf()" family of functions
+** from the standard C library.
+**
+** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
+** results into memory obtained from [sqlite3_malloc()].
+** The strings returned by these two routines should be
+** released by [sqlite3_free()]. Both routines return a
+** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
+** memory to hold the resulting string.
+**
+** In sqlite3_snprintf() routine is similar to "snprintf()" from
+** the standard C library. The result is written into the
+** buffer supplied as the second parameter whose size is given by
+** the first parameter. Note that the order of the
+** first two parameters is reversed from snprintf(). This is an
+** historical accident that cannot be fixed without breaking
+** backwards compatibility. Note also that sqlite3_snprintf()
+** returns a pointer to its buffer instead of the number of
+** characters actually written into the buffer. We admit that
+** the number of characters written would be a more useful return
+** value but we cannot change the implementation of sqlite3_snprintf()
+** now without breaking compatibility.
+**
+** As long as the buffer size is greater than zero, sqlite3_snprintf()
+** guarantees that the buffer is always zero-terminated. The first
+** parameter "n" is the total size of the buffer, including space for
+** the zero terminator. So the longest string that can be completely
+** written will be n-1 characters.
+**
+** These routines all implement some additional formatting
+** options that are useful for constructing SQL statements.
+** All of the usual printf() formatting options apply. In addition, there
+** is are "%q", "%Q", and "%z" options.
+**
+** The %q option works like %s in that it substitutes a null-terminated
+** string from the argument list. But %q also doubles every '\'' character.
+** %q is designed for use inside a string literal. By doubling each '\''
+** character it escapes that character and allows it to be inserted into
+** the string.
+**
+** For example, assume the string variable zText contains text as follows:
+**
+** <blockquote><pre>
+** char *zText = "It's a happy day!";
+** </pre></blockquote>
+**
+** One can use this text in an SQL statement as follows:
+**
+** <blockquote><pre>
+** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
+** sqlite3_exec(db, zSQL, 0, 0, 0);
+** sqlite3_free(zSQL);
+** </pre></blockquote>
+**
+** Because the %q format string is used, the '\'' character in zText
+** is escaped and the SQL generated is as follows:
+**
+** <blockquote><pre>
+** INSERT INTO table1 VALUES('It''s a happy day!')
+** </pre></blockquote>
+**
+** This is correct. Had we used %s instead of %q, the generated SQL
+** would have looked like this:
+**
+** <blockquote><pre>
+** INSERT INTO table1 VALUES('It's a happy day!');
+** </pre></blockquote>
+**
+** This second example is an SQL syntax error. As a general rule you should
+** always use %q instead of %s when inserting text into a string literal.
+**
+** The %Q option works like %q except it also adds single quotes around
+** the outside of the total string. Additionally, if the parameter in the
+** argument list is a NULL pointer, %Q substitutes the text "NULL" (without
+** single quotes) in place of the %Q option. So, for example, one could say:
+**
+** <blockquote><pre>
+** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
+** sqlite3_exec(db, zSQL, 0, 0, 0);
+** sqlite3_free(zSQL);
+** </pre></blockquote>
+**
+** The code above will render a correct SQL statement in the zSQL
+** variable even if the zText variable is a NULL pointer.
+**
+** The "%z" formatting option works exactly like "%s" with the
+** addition that after the string has been read and copied into
+** the result, [sqlite3_free()] is called on the input string. {END}
+**
+** INVARIANTS:
+**
+** {H17403} The [sqlite3_mprintf()] and [sqlite3_vmprintf()] interfaces
+** return either pointers to zero-terminated UTF-8 strings held in
+** memory obtained from [sqlite3_malloc()] or NULL pointers if
+** a call to [sqlite3_malloc()] fails.
+**
+** {H17406} The [sqlite3_snprintf()] interface writes a zero-terminated
+** UTF-8 string into the buffer pointed to by the second parameter
+** provided that the first parameter is greater than zero.
+**
+** {H17407} The [sqlite3_snprintf()] interface does not write slots of
+** its output buffer (the second parameter) outside the range
+** of 0 through N-1 (where N is the first parameter)
+** regardless of the length of the string
+** requested by the format specification.
+*/
+char *sqlite3_mprintf(const char*,...);
+char *sqlite3_vmprintf(const char*, va_list);
+char *sqlite3_snprintf(int,char*,const char*, ...);
+
+/*
+** CAPI3REF: Memory Allocation Subsystem {H17300} <S20000>
+**
+** The SQLite core uses these three routines for all of its own
+** internal memory allocation needs. "Core" in the previous sentence
+** does not include operating-system specific VFS implementation. The
+** Windows VFS uses native malloc() and free() for some operations.
+**
+** The sqlite3_malloc() routine returns a pointer to a block
+** of memory at least N bytes in length, where N is the parameter.
+** If sqlite3_malloc() is unable to obtain sufficient free
+** memory, it returns a NULL pointer. If the parameter N to
+** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
+** a NULL pointer.
+**
+** Calling sqlite3_free() with a pointer previously returned
+** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
+** that it might be reused. The sqlite3_free() routine is
+** a no-op if is called with a NULL pointer. Passing a NULL pointer
+** to sqlite3_free() is harmless. After being freed, memory
+** should neither be read nor written. Even reading previously freed
+** memory might result in a segmentation fault or other severe error.
+** Memory corruption, a segmentation fault, or other severe error
+** might result if sqlite3_free() is called with a non-NULL pointer that
+** was not obtained from sqlite3_malloc() or sqlite3_free().
+**
+** The sqlite3_realloc() interface attempts to resize a
+** prior memory allocation to be at least N bytes, where N is the
+** second parameter. The memory allocation to be resized is the first
+** parameter. If the first parameter to sqlite3_realloc()
+** is a NULL pointer then its behavior is identical to calling
+** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
+** If the second parameter to sqlite3_realloc() is zero or
+** negative then the behavior is exactly the same as calling
+** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
+** sqlite3_realloc() returns a pointer to a memory allocation
+** of at least N bytes in size or NULL if sufficient memory is unavailable.
+** If M is the size of the prior allocation, then min(N,M) bytes
+** of the prior allocation are copied into the beginning of buffer returned
+** by sqlite3_realloc() and the prior allocation is freed.
+** If sqlite3_realloc() returns NULL, then the prior allocation
+** is not freed.
+**
+** The memory returned by sqlite3_malloc() and sqlite3_realloc()
+** is always aligned to at least an 8 byte boundary. {END}
+**
+** The default implementation of the memory allocation subsystem uses
+** the malloc(), realloc() and free() provided by the standard C library.
+** {H17382} However, if SQLite is compiled with the
+** SQLITE_MEMORY_SIZE=<i>NNN</i> C preprocessor macro (where <i>NNN</i>
+** is an integer), then SQLite create a static array of at least
+** <i>NNN</i> bytes in size and uses that array for all of its dynamic
+** memory allocation needs. {END} Additional memory allocator options
+** may be added in future releases.
+**
+** In SQLite version 3.5.0 and 3.5.1, it was possible to define
+** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
+** implementation of these routines to be omitted. That capability
+** is no longer provided. Only built-in memory allocators can be used.
+**
+** The Windows OS interface layer calls
+** the system malloc() and free() directly when converting
+** filenames between the UTF-8 encoding used by SQLite
+** and whatever filename encoding is used by the particular Windows
+** installation. Memory allocation errors are detected, but
+** they are reported back as [SQLITE_CANTOPEN] or
+** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
+**
+** INVARIANTS:
+**
+** {H17303} The [sqlite3_malloc(N)] interface returns either a pointer to
+** a newly checked-out block of at least N bytes of memory
+** that is 8-byte aligned, or it returns NULL if it is unable
+** to fulfill the request.
+**
+** {H17304} The [sqlite3_malloc(N)] interface returns a NULL pointer if
+** N is less than or equal to zero.
+**
+** {H17305} The [sqlite3_free(P)] interface releases memory previously
+** returned from [sqlite3_malloc()] or [sqlite3_realloc()],
+** making it available for reuse.
+**
+** {H17306} A call to [sqlite3_free(NULL)] is a harmless no-op.
+**
+** {H17310} A call to [sqlite3_realloc(0,N)] is equivalent to a call
+** to [sqlite3_malloc(N)].
+**
+** {H17312} A call to [sqlite3_realloc(P,0)] is equivalent to a call
+** to [sqlite3_free(P)].
+**
+** {H17315} The SQLite core uses [sqlite3_malloc()], [sqlite3_realloc()],
+** and [sqlite3_free()] for all of its memory allocation and
+** deallocation needs.
+**
+** {H17318} The [sqlite3_realloc(P,N)] interface returns either a pointer
+** to a block of checked-out memory of at least N bytes in size
+** that is 8-byte aligned, or a NULL pointer.
+**
+** {H17321} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first
+** copies the first K bytes of content from P into the newly
+** allocated block, where K is the lesser of N and the size of
+** the buffer P.
+**
+** {H17322} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first
+** releases the buffer P.
+**
+** {H17323} When [sqlite3_realloc(P,N)] returns NULL, the buffer P is
+** not modified or released.
+**
+** ASSUMPTIONS:
+**
+** {A17350} The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
+** must be either NULL or else pointers obtained from a prior
+** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
+** not yet been released.
+**
+** {A17351} The application must not read or write any part of
+** a block of memory after it has been released using
+** [sqlite3_free()] or [sqlite3_realloc()].
+*/
+void *sqlite3_malloc(int);
+void *sqlite3_realloc(void*, int);
+void sqlite3_free(void*);
+
+/*
+** CAPI3REF: Memory Allocator Statistics {H17370} <S30210>
+**
+** SQLite provides these two interfaces for reporting on the status
+** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()]
+** routines, which form the built-in memory allocation subsystem.
+**
+** INVARIANTS:
+**
+** {H17371} The [sqlite3_memory_used()] routine returns the number of bytes
+** of memory currently outstanding (malloced but not freed).
+**
+** {H17373} The [sqlite3_memory_highwater()] routine returns the maximum
+** value of [sqlite3_memory_used()] since the high-water mark
+** was last reset.
+**
+** {H17374} The values returned by [sqlite3_memory_used()] and
+** [sqlite3_memory_highwater()] include any overhead
+** added by SQLite in its implementation of [sqlite3_malloc()],
+** but not overhead added by the any underlying system library
+** routines that [sqlite3_malloc()] may call.
+**
+** {H17375} The memory high-water mark is reset to the current value of
+** [sqlite3_memory_used()] if and only if the parameter to
+** [sqlite3_memory_highwater()] is true. The value returned
+** by [sqlite3_memory_highwater(1)] is the high-water mark
+** prior to the reset.
+*/
+sqlite3_int64 sqlite3_memory_used(void);
+sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
+
+/*
+** CAPI3REF: Pseudo-Random Number Generator {H17390} <S20000>
+**
+** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
+** select random ROWIDs when inserting new records into a table that
+** already uses the largest possible ROWID. The PRNG is also used for
+** the build-in random() and randomblob() SQL functions. This interface allows
+** applications to access the same PRNG for other purposes.
+**
+** A call to this routine stores N bytes of randomness into buffer P.
+**
+** The first time this routine is invoked (either internally or by
+** the application) the PRNG is seeded using randomness obtained
+** from the xRandomness method of the default [sqlite3_vfs] object.
+** On all subsequent invocations, the pseudo-randomness is generated
+** internally and without recourse to the [sqlite3_vfs] xRandomness
+** method.
+**
+** INVARIANTS:
+**
+** {H17392} The [sqlite3_randomness(N,P)] interface writes N bytes of
+** high-quality pseudo-randomness into buffer P.
+*/
+void sqlite3_randomness(int N, void *P);
+
+/*
+** CAPI3REF: Compile-Time Authorization Callbacks {H12500} <S70100>
+**
+** This routine registers a authorizer callback with a particular
+** [database connection], supplied in the first argument.
+** The authorizer callback is invoked as SQL statements are being compiled
+** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
+** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. At various
+** points during the compilation process, as logic is being created
+** to perform various actions, the authorizer callback is invoked to
+** see if those actions are allowed. The authorizer callback should
+** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
+** specific action but allow the SQL statement to continue to be
+** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
+** rejected with an error. If the authorizer callback returns
+** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
+** then the [sqlite3_prepare_v2()] or equivalent call that triggered
+** the authorizer will fail with an error message.
+**
+** When the callback returns [SQLITE_OK], that means the operation
+** requested is ok. When the callback returns [SQLITE_DENY], the
+** [sqlite3_prepare_v2()] or equivalent call that triggered the
+** authorizer will fail with an error message explaining that
+** access is denied. If the authorizer code is [SQLITE_READ]
+** and the callback returns [SQLITE_IGNORE] then the
+** [prepared statement] statement is constructed to substitute
+** a NULL value in place of the table column that would have
+** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
+** return can be used to deny an untrusted user access to individual
+** columns of a table.
+**
+** The first parameter to the authorizer callback is a copy of the third
+** parameter to the sqlite3_set_authorizer() interface. The second parameter
+** to the callback is an integer [SQLITE_COPY | action code] that specifies
+** the particular action to be authorized. The third through sixth parameters
+** to the callback are zero-terminated strings that contain additional
+** details about the action to be authorized.
+**
+** An authorizer is used when [sqlite3_prepare | preparing]
+** SQL statements from an untrusted source, to ensure that the SQL statements
+** do not try to access data they are not allowed to see, or that they do not
+** try to execute malicious statements that damage the database. For
+** example, an application may allow a user to enter arbitrary
+** SQL queries for evaluation by a database. But the application does
+** not want the user to be able to make arbitrary changes to the
+** database. An authorizer could then be put in place while the
+** user-entered SQL is being [sqlite3_prepare | prepared] that
+** disallows everything except [SELECT] statements.
+**
+** Applications that need to process SQL from untrusted sources
+** might also consider lowering resource limits using [sqlite3_limit()]
+** and limiting database size using the [max_page_count] [PRAGMA]
+** in addition to using an authorizer.
+**
+** Only a single authorizer can be in place on a database connection
+** at a time. Each call to sqlite3_set_authorizer overrides the
+** previous call. Disable the authorizer by installing a NULL callback.
+** The authorizer is disabled by default.
+**
+** Note that the authorizer callback is invoked only during
+** [sqlite3_prepare()] or its variants. Authorization is not
+** performed during statement evaluation in [sqlite3_step()].
+**
+** INVARIANTS:
+**
+** {H12501} The [sqlite3_set_authorizer(D,...)] interface registers a
+** authorizer callback with database connection D.
+**
+** {H12502} The authorizer callback is invoked as SQL statements are
+** being compiled.
+**
+** {H12503} If the authorizer callback returns any value other than
+** [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY], then
+** the [sqlite3_prepare_v2()] or equivalent call that caused
+** the authorizer callback to run shall fail with an
+** [SQLITE_ERROR] error code and an appropriate error message.
+**
+** {H12504} When the authorizer callback returns [SQLITE_OK], the operation
+** described is processed normally.
+**
+** {H12505} When the authorizer callback returns [SQLITE_DENY], the
+** [sqlite3_prepare_v2()] or equivalent call that caused the
+** authorizer callback to run shall fail
+** with an [SQLITE_ERROR] error code and an error message
+** explaining that access is denied.
+**
+** {H12506} If the authorizer code (the 2nd parameter to the authorizer
+** callback) is [SQLITE_READ] and the authorizer callback returns
+** [SQLITE_IGNORE], then the prepared statement is constructed to
+** insert a NULL value in place of the table column that would have
+** been read if [SQLITE_OK] had been returned.
+**
+** {H12507} If the authorizer code (the 2nd parameter to the authorizer
+** callback) is anything other than [SQLITE_READ], then
+** a return of [SQLITE_IGNORE] has the same effect as [SQLITE_DENY].
+**
+** {H12510} The first parameter to the authorizer callback is a copy of
+** the third parameter to the [sqlite3_set_authorizer()] interface.
+**
+** {H12511} The second parameter to the callback is an integer
+** [SQLITE_COPY | action code] that specifies the particular action
+** to be authorized.
+**
+** {H12512} The third through sixth parameters to the callback are
+** zero-terminated strings that contain
+** additional details about the action to be authorized.
+**
+** {H12520} Each call to [sqlite3_set_authorizer()] overrides
+** any previously installed authorizer.
+**
+** {H12521} A NULL authorizer means that no authorization
+** callback is invoked.
+**
+** {H12522} The default authorizer is NULL.
+*/
+int sqlite3_set_authorizer(
+ sqlite3*,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pUserData
+);
+
+/*
+** CAPI3REF: Authorizer Return Codes {H12590} <H12500>
+**
+** The [sqlite3_set_authorizer | authorizer callback function] must
+** return either [SQLITE_OK] or one of these two constants in order
+** to signal SQLite whether or not the action is permitted. See the
+** [sqlite3_set_authorizer | authorizer documentation] for additional
+** information.
+*/
+#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
+#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
+
+/*
+** CAPI3REF: Authorizer Action Codes {H12550} <H12500>
+**
+** The [sqlite3_set_authorizer()] interface registers a callback function
+** that is invoked to authorize certain SQL statement actions. The
+** second parameter to the callback is an integer code that specifies
+** what action is being authorized. These are the integer action codes that
+** the authorizer callback may be passed.
+**
+** These action code values signify what kind of operation is to be
+** authorized. The 3rd and 4th parameters to the authorization
+** callback function will be parameters or NULL depending on which of these
+** codes is used as the second parameter. The 5th parameter to the
+** authorizer callback is the name of the database ("main", "temp",
+** etc.) if applicable. The 6th parameter to the authorizer callback
+** is the name of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** top-level SQL code.
+**
+** INVARIANTS:
+**
+** {H12551} The second parameter to an
+** [sqlite3_set_authorizer | authorizer callback] is always an integer
+** [SQLITE_COPY | authorizer code] that specifies what action
+** is being authorized.
+**
+** {H12552} The 3rd and 4th parameters to the
+** [sqlite3_set_authorizer | authorization callback]
+** will be parameters or NULL depending on which
+** [SQLITE_COPY | authorizer code] is used as the second parameter.
+**
+** {H12553} The 5th parameter to the
+** [sqlite3_set_authorizer | authorizer callback] is the name
+** of the database (example: "main", "temp", etc.) if applicable.
+**
+** {H12554} The 6th parameter to the
+** [sqlite3_set_authorizer | authorizer callback] is the name
+** of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** top-level SQL code.
+*/
+/******************************************* 3rd ************ 4th ***********/
+#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
+#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
+#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
+#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
+#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
+#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
+#define SQLITE_DELETE 9 /* Table Name NULL */
+#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
+#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
+#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
+#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
+#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
+#define SQLITE_DROP_VIEW 17 /* View Name NULL */
+#define SQLITE_INSERT 18 /* Table Name NULL */
+#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
+#define SQLITE_READ 20 /* Table Name Column Name */
+#define SQLITE_SELECT 21 /* NULL NULL */
+#define SQLITE_TRANSACTION 22 /* NULL NULL */
+#define SQLITE_UPDATE 23 /* Table Name Column Name */
+#define SQLITE_ATTACH 24 /* Filename NULL */
+#define SQLITE_DETACH 25 /* Database Name NULL */
+#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */
+#define SQLITE_REINDEX 27 /* Index Name NULL */
+#define SQLITE_ANALYZE 28 /* Table Name NULL */
+#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */
+#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */
+#define SQLITE_FUNCTION 31 /* Function Name NULL */
+#define SQLITE_COPY 0 /* No longer used */
+
+/*
+** CAPI3REF: Tracing And Profiling Functions {H12280} <S60400>
+** EXPERIMENTAL
+**
+** These routines register callback functions that can be used for
+** tracing and profiling the execution of SQL statements.
+**
+** The callback function registered by sqlite3_trace() is invoked at
+** various times when an SQL statement is being run by [sqlite3_step()].
+** The callback returns a UTF-8 rendering of the SQL statement text
+** as the statement first begins executing. Additional callbacks occur
+** as each triggered subprogram is entered. The callbacks for triggers
+** contain a UTF-8 SQL comment that identifies the trigger.
+**
+** The callback function registered by sqlite3_profile() is invoked
+** as each SQL statement finishes. The profile callback contains
+** the original statement text and an estimate of wall-clock time
+** of how long that statement took to run.
+**
+** INVARIANTS:
+**
+** {H12281} The callback function registered by [sqlite3_trace()] is
+** whenever an SQL statement first begins to execute and
+** whenever a trigger subprogram first begins to run.
+**
+** {H12282} Each call to [sqlite3_trace()] overrides the previously
+** registered trace callback.
+**
+** {H12283} A NULL trace callback disables tracing.
+**
+** {H12284} The first argument to the trace callback is a copy of
+** the pointer which was the 3rd argument to [sqlite3_trace()].
+**
+** {H12285} The second argument to the trace callback is a
+** zero-terminated UTF-8 string containing the original text
+** of the SQL statement as it was passed into [sqlite3_prepare_v2()]
+** or the equivalent, or an SQL comment indicating the beginning
+** of a trigger subprogram.
+**
+** {H12287} The callback function registered by [sqlite3_profile()] is invoked
+** as each SQL statement finishes.
+**
+** {H12288} The first parameter to the profile callback is a copy of
+** the 3rd parameter to [sqlite3_profile()].
+**
+** {H12289} The second parameter to the profile callback is a
+** zero-terminated UTF-8 string that contains the complete text of
+** the SQL statement as it was processed by [sqlite3_prepare_v2()]
+** or the equivalent.
+**
+** {H12290} The third parameter to the profile callback is an estimate
+** of the number of nanoseconds of wall-clock time required to
+** run the SQL statement from start to finish.
+*/
+void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+void *sqlite3_profile(sqlite3*,
+ void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
+
+/*
+** CAPI3REF: Query Progress Callbacks {H12910} <S60400>
+**
+** This routine configures a callback function - the
+** progress callback - that is invoked periodically during long
+** running calls to [sqlite3_exec()], [sqlite3_step()] and
+** [sqlite3_get_table()]. An example use for this
+** interface is to keep a GUI updated during a large query.
+**
+** If the progress callback returns non-zero, the operation is
+** interrupted. This feature can be used to implement a
+** "Cancel" button on a GUI dialog box.
+**
+** INVARIANTS:
+**
+** {H12911} The callback function registered by sqlite3_progress_handler()
+** is invoked periodically during long running calls to
+** [sqlite3_step()].
+**
+** {H12912} The progress callback is invoked once for every N virtual
+** machine opcodes, where N is the second argument to
+** the [sqlite3_progress_handler()] call that registered
+** the callback. If N is less than 1, sqlite3_progress_handler()
+** acts as if a NULL progress handler had been specified.
+**
+** {H12913} The progress callback itself is identified by the third
+** argument to sqlite3_progress_handler().
+**
+** {H12914} The fourth argument to sqlite3_progress_handler() is a
+** void pointer passed to the progress callback
+** function each time it is invoked.
+**
+** {H12915} If a call to [sqlite3_step()] results in fewer than N opcodes
+** being executed, then the progress callback is never invoked.
+**
+** {H12916} Every call to [sqlite3_progress_handler()]
+** overwrites any previously registered progress handler.
+**
+** {H12917} If the progress handler callback is NULL then no progress
+** handler is invoked.
+**
+** {H12918} If the progress callback returns a result other than 0, then
+** the behavior is a if [sqlite3_interrupt()] had been called.
+** <S30500>
+*/
+void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
+
+/*
+** CAPI3REF: Opening A New Database Connection {H12700} <S40200>
+**
+** These routines open an SQLite database file whose name is given by the
+** filename argument. The filename argument is interpreted as UTF-8 for
+** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
+** order for sqlite3_open16(). A [database connection] handle is usually
+** returned in *ppDb, even if an error occurs. The only exception is that
+** if SQLite is unable to allocate memory to hold the [sqlite3] object,
+** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]
+** object. If the database is opened (and/or created) successfully, then
+** [SQLITE_OK] is returned. Otherwise an [error code] is returned. The
+** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain
+** an English language description of the error.
+**
+** The default encoding for the database will be UTF-8 if
+** sqlite3_open() or sqlite3_open_v2() is called and
+** UTF-16 in the native byte order if sqlite3_open16() is used.
+**
+** Whether or not an error occurs when it is opened, resources
+** associated with the [database connection] handle should be released by
+** passing it to [sqlite3_close()] when it is no longer required.
+**
+** The sqlite3_open_v2() interface works like sqlite3_open()
+** except that it accepts two additional parameters for additional control
+** over the new database connection. The flags parameter can take one of
+** the following three values, optionally combined with the
+** [SQLITE_OPEN_NOMUTEX] flag:
+**
+** <dl>
+** <dt>[SQLITE_OPEN_READONLY]</dt>
+** <dd>The database is opened in read-only mode. If the database does not
+** already exist, an error is returned.</dd>
+**
+** <dt>[SQLITE_OPEN_READWRITE]</dt>
+** <dd>The database is opened for reading and writing if possible, or reading
+** only if the file is write protected by the operating system. In either
+** case the database must already exist, otherwise an error is returned.</dd>
+**
+** <dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
+** <dd>The database is opened for reading and writing, and is creates it if
+** it does not already exist. This is the behavior that is always used for
+** sqlite3_open() and sqlite3_open16().</dd>
+** </dl>
+**
+** If the 3rd parameter to sqlite3_open_v2() is not one of the
+** combinations shown above or one of the combinations shown above combined
+** with the [SQLITE_OPEN_NOMUTEX] flag, then the behavior is undefined.
+**
+** If the [SQLITE_OPEN_NOMUTEX] flag is set, then mutexes on the
+** opened [database connection] are disabled and the appliation must
+** insure that access to the [database connection] and its associated
+** [prepared statements] is serialized. The [SQLITE_OPEN_NOMUTEX] flag
+** is the default behavior is SQLite is configured using the
+** [SQLITE_CONFIG_MULTITHREAD] or [SQLITE_CONFIG_SINGLETHREAD] options
+** to [sqlite3_config()]. The [SQLITE_OPEN_NOMUTEX] flag only makes a
+** difference when SQLite is in its default [SQLITE_CONFIG_SERIALIZED] mode.
+**
+** If the filename is ":memory:", then a private, temporary in-memory database
+** is created for the connection. This in-memory database will vanish when
+** the database connection is closed. Future versions of SQLite might
+** make use of additional special filenames that begin with the ":" character.
+** It is recommended that when a database filename actually does begin with
+** a ":" character you should prefix the filename with a pathname such as
+** "./" to avoid ambiguity.
+**
+** If the filename is an empty string, then a private, temporary
+** on-disk database will be created. This private database will be
+** automatically deleted as soon as the database connection is closed.
+**
+** The fourth parameter to sqlite3_open_v2() is the name of the
+** [sqlite3_vfs] object that defines the operating system interface that
+** the new database connection should use. If the fourth parameter is
+** a NULL pointer then the default [sqlite3_vfs] object is used.
+**
+** <b>Note to Windows users:</b> The encoding used for the filename argument
+** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever
+** codepage is currently defined. Filenames containing international
+** characters must be converted to UTF-8 prior to passing them into
+** sqlite3_open() or sqlite3_open_v2().
+**
+** INVARIANTS:
+**
+** {H12701} The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces create a new
+** [database connection] associated with
+** the database file given in their first parameter.
+**
+** {H12702} The filename argument is interpreted as UTF-8
+** for [sqlite3_open()] and [sqlite3_open_v2()] and as UTF-16
+** in the native byte order for [sqlite3_open16()].
+**
+** {H12703} A successful invocation of [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()] writes a pointer to a new
+** [database connection] into *ppDb.
+**
+** {H12704} The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces return [SQLITE_OK] upon success,
+** or an appropriate [error code] on failure.
+**
+** {H12706} The default text encoding for a new database created using
+** [sqlite3_open()] or [sqlite3_open_v2()] will be UTF-8.
+**
+** {H12707} The default text encoding for a new database created using
+** [sqlite3_open16()] will be UTF-16.
+**
+** {H12709} The [sqlite3_open(F,D)] interface is equivalent to
+** [sqlite3_open_v2(F,D,G,0)] where the G parameter is
+** [SQLITE_OPEN_READWRITE]|[SQLITE_OPEN_CREATE].
+**
+** {H12711} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_READONLY] then the database is opened
+** for reading only.
+**
+** {H12712} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_READWRITE] then the database is opened
+** reading and writing if possible, or for reading only if the
+** file is write protected by the operating system.
+**
+** {H12713} If the G parameter to [sqlite3_open_v2(F,D,G,V)] omits the
+** bit value [SQLITE_OPEN_CREATE] and the database does not
+** previously exist, an error is returned.
+**
+** {H12714} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_CREATE] and the database does not
+** previously exist, then an attempt is made to create and
+** initialize the database.
+**
+** {H12717} If the filename argument to [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()] is ":memory:", then an private,
+** ephemeral, in-memory database is created for the connection.
+** <todo>Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required
+** in sqlite3_open_v2()?</todo>
+**
+** {H12719} If the filename is NULL or an empty string, then a private,
+** ephemeral on-disk database will be created.
+** <todo>Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required
+** in sqlite3_open_v2()?</todo>
+**
+** {H12721} The [database connection] created by [sqlite3_open_v2(F,D,G,V)]
+** will use the [sqlite3_vfs] object identified by the V parameter,
+** or the default [sqlite3_vfs] object if V is a NULL pointer.
+**
+** {H12723} Two [database connections] will share a common cache if both were
+** opened with the same VFS while [shared cache mode] was enabled and
+** if both filenames compare equal using memcmp() after having been
+** processed by the [sqlite3_vfs | xFullPathname] method of the VFS.
+*/
+int sqlite3_open(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open16(
+ const void *filename, /* Database filename (UTF-16) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open_v2(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb, /* OUT: SQLite db handle */
+ int flags, /* Flags */
+ const char *zVfs /* Name of VFS module to use */
+);
+
+/*
+** CAPI3REF: Error Codes And Messages {H12800} <S60200>
+**
+** The sqlite3_errcode() interface returns the numeric [result code] or
+** [extended result code] for the most recent failed sqlite3_* API call
+** associated with a [database connection]. If a prior API call failed
+** but the most recent API call succeeded, the return value from
+** sqlite3_errcode() is undefined.
+**
+** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
+** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** Memory to hold the error message string is managed internally.
+** The application does not need to worry about freeing the result.
+** However, the error string might be overwritten or deallocated by
+** subsequent calls to other SQLite interface functions.
+**
+** If an interface fails with SQLITE_MISUSE, that means the interface
+** was invoked incorrectly by the application. In that case, the
+** error code and message may or may not be set.
+**
+** INVARIANTS:
+**
+** {H12801} The [sqlite3_errcode(D)] interface returns the numeric
+** [result code] or [extended result code] for the most recently
+** failed interface call associated with the [database connection] D.
+**
+** {H12803} The [sqlite3_errmsg(D)] and [sqlite3_errmsg16(D)]
+** interfaces return English-language text that describes
+** the error in the mostly recently failed interface call,
+** encoded as either UTF-8 or UTF-16 respectively.
+**
+** {H12807} The strings returned by [sqlite3_errmsg()] and [sqlite3_errmsg16()]
+** are valid until the next SQLite interface call.
+**
+** {H12808} Calls to API routines that do not return an error code
+** (example: [sqlite3_data_count()]) do not
+** change the error code or message returned by
+** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()].
+**
+** {H12809} Interfaces that are not associated with a specific
+** [database connection] (examples:
+** [sqlite3_mprintf()] or [sqlite3_enable_shared_cache()]
+** do not change the values returned by
+** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()].
+*/
+int sqlite3_errcode(sqlite3 *db);
+const char *sqlite3_errmsg(sqlite3*);
+const void *sqlite3_errmsg16(sqlite3*);
+
+/*
+** CAPI3REF: SQL Statement Object {H13000} <H13010>
+** KEYWORDS: {prepared statement} {prepared statements}
+**
+** An instance of this object represents a single SQL statement.
+** This object is variously known as a "prepared statement" or a
+** "compiled SQL statement" or simply as a "statement".
+**
+** The life of a statement object goes something like this:
+**
+** <ol>
+** <li> Create the object using [sqlite3_prepare_v2()] or a related
+** function.
+** <li> Bind values to [host parameters] using the sqlite3_bind_*()
+** interfaces.
+** <li> Run the SQL by calling [sqlite3_step()] one or more times.
+** <li> Reset the statement using [sqlite3_reset()] then go back
+** to step 2. Do this zero or more times.
+** <li> Destroy the object using [sqlite3_finalize()].
+** </ol>
+**
+** Refer to documentation on individual methods above for additional
+** information.
+*/
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+/*
+** CAPI3REF: Run-time Limits {H12760} <S20600>
+**
+** This interface allows the size of various constructs to be limited
+** on a connection by connection basis. The first parameter is the
+** [database connection] whose limit is to be set or queried. The
+** second parameter is one of the [limit categories] that define a
+** class of constructs to be size limited. The third parameter is the
+** new limit for that construct. The function returns the old limit.
+**
+** If the new limit is a negative number, the limit is unchanged.
+** For the limit category of SQLITE_LIMIT_XYZ there is a hard upper
+** bound set by a compile-time C preprocessor macro named SQLITE_MAX_XYZ.
+** (The "_LIMIT_" in the name is changed to "_MAX_".)
+** Attempts to increase a limit above its hard upper bound are
+** silently truncated to the hard upper limit.
+**
+** Run time limits are intended for use in applications that manage
+** both their own internal database and also databases that are controlled
+** by untrusted external sources. An example application might be a
+** webbrowser that has its own databases for storing history and
+** separate databases controlled by JavaScript applications downloaded
+** off the Internet. The internal databases can be given the
+** large, default limits. Databases managed by external sources can
+** be given much smaller limits designed to prevent a denial of service
+** attack. Developers might also want to use the [sqlite3_set_authorizer()]
+** interface to further control untrusted SQL. The size of the database
+** created by an untrusted script can be contained using the
+** [max_page_count] [PRAGMA].
+**
+** New run-time limit categories may be added in future releases.
+**
+** INVARIANTS:
+**
+** {H12762} A successful call to [sqlite3_limit(D,C,V)] where V is
+** positive changes the limit on the size of construct C in the
+** [database connection] D to the lesser of V and the hard upper
+** bound on the size of C that is set at compile-time.
+**
+** {H12766} A successful call to [sqlite3_limit(D,C,V)] where V is negative
+** leaves the state of the [database connection] D unchanged.
+**
+** {H12769} A successful call to [sqlite3_limit(D,C,V)] returns the
+** value of the limit on the size of construct C in the
+** [database connection] D as it was prior to the call.
+*/
+int sqlite3_limit(sqlite3*, int id, int newVal);
+
+/*
+** CAPI3REF: Run-Time Limit Categories {H12790} <H12760>
+** KEYWORDS: {limit category} {limit categories}
+**
+** These constants define various aspects of a [database connection]
+** that can be limited in size by calls to [sqlite3_limit()].
+** The meanings of the various limits are as follows:
+**
+** <dl>
+** <dt>SQLITE_LIMIT_LENGTH</dt>
+** <dd>The maximum size of any string or BLOB or table row.<dd>
+**
+** <dt>SQLITE_LIMIT_SQL_LENGTH</dt>
+** <dd>The maximum length of an SQL statement.</dd>
+**
+** <dt>SQLITE_LIMIT_COLUMN</dt>
+** <dd>The maximum number of columns in a table definition or in the
+** result set of a SELECT or the maximum number of columns in an index
+** or in an ORDER BY or GROUP BY clause.</dd>
+**
+** <dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
+** <dd>The maximum depth of the parse tree on any expression.</dd>
+**
+** <dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
+** <dd>The maximum number of terms in a compound SELECT statement.</dd>
+**
+** <dt>SQLITE_LIMIT_VDBE_OP</dt>
+** <dd>The maximum number of instructions in a virtual machine program
+** used to implement an SQL statement.</dd>
+**
+** <dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
+** <dd>The maximum number of arguments on a function.</dd>
+**
+** <dt>SQLITE_LIMIT_ATTACHED</dt>
+** <dd>The maximum number of attached databases.</dd>
+**
+** <dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>
+** <dd>The maximum length of the pattern argument to the LIKE or
+** GLOB operators.</dd>
+**
+** <dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>
+** <dd>The maximum number of variables in an SQL statement that can
+** be bound.</dd>
+** </dl>
+*/
+#define SQLITE_LIMIT_LENGTH 0
+#define SQLITE_LIMIT_SQL_LENGTH 1
+#define SQLITE_LIMIT_COLUMN 2
+#define SQLITE_LIMIT_EXPR_DEPTH 3
+#define SQLITE_LIMIT_COMPOUND_SELECT 4
+#define SQLITE_LIMIT_VDBE_OP 5
+#define SQLITE_LIMIT_FUNCTION_ARG 6
+#define SQLITE_LIMIT_ATTACHED 7
+#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
+#define SQLITE_LIMIT_VARIABLE_NUMBER 9
+
+/*
+** CAPI3REF: Compiling An SQL Statement {H13010} <S10000>
+** KEYWORDS: {SQL statement compiler}
+**
+** To execute an SQL query, it must first be compiled into a byte-code
+** program using one of these routines.
+**
+** The first argument, "db", is a [database connection] obtained from a
+** prior call to [sqlite3_open()], [sqlite3_open_v2()] or [sqlite3_open16()].
+**
+** The second argument, "zSql", is the statement to be compiled, encoded
+** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2()
+** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
+** use UTF-16.
+**
+** If the nByte argument is less than zero, then zSql is read up to the
+** first zero terminator. If nByte is non-negative, then it is the maximum
+** number of bytes read from zSql. When nByte is non-negative, the
+** zSql string ends at either the first '\000' or '\u0000' character or
+** the nByte-th byte, whichever comes first. If the caller knows
+** that the supplied string is nul-terminated, then there is a small
+** performance advantage to be gained by passing an nByte parameter that
+** is equal to the number of bytes in the input string <i>including</i>
+** the nul-terminator bytes.
+**
+** *pzTail is made to point to the first byte past the end of the
+** first SQL statement in zSql. These routines only compile the first
+** statement in zSql, so *pzTail is left pointing to what remains
+** uncompiled.
+**
+** *ppStmt is left pointing to a compiled [prepared statement] that can be
+** executed using [sqlite3_step()]. If there is an error, *ppStmt is set
+** to NULL. If the input text contains no SQL (if the input is an empty
+** string or a comment) then *ppStmt is set to NULL.
+** {A13018} The calling procedure is responsible for deleting the compiled
+** SQL statement using [sqlite3_finalize()] after it has finished with it.
+**
+** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned.
+**
+** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
+** recommended for all new programs. The two older interfaces are retained
+** for backwards compatibility, but their use is discouraged.
+** In the "v2" interfaces, the prepared statement
+** that is returned (the [sqlite3_stmt] object) contains a copy of the
+** original SQL text. This causes the [sqlite3_step()] interface to
+** behave a differently in two ways:
+**
+** <ol>
+** <li>
+** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
+** always used to do, [sqlite3_step()] will automatically recompile the SQL
+** statement and try to run it again. If the schema has changed in
+** a way that makes the statement no longer valid, [sqlite3_step()] will still
+** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is
+** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the
+** error go away. Note: use [sqlite3_errmsg()] to find the text
+** of the parsing error that results in an [SQLITE_SCHEMA] return.
+** </li>
+**
+** <li>
+** When an error occurs, [sqlite3_step()] will return one of the detailed
+** [error codes] or [extended error codes]. The legacy behavior was that
+** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code
+** and you would have to make a second call to [sqlite3_reset()] in order
+** to find the underlying cause of the problem. With the "v2" prepare
+** interfaces, the underlying reason for the error is returned immediately.
+** </li>
+** </ol>
+**
+** INVARIANTS:
+**
+** {H13011} The [sqlite3_prepare(db,zSql,...)] and
+** [sqlite3_prepare_v2(db,zSql,...)] interfaces interpret the
+** text in their zSql parameter as UTF-8.
+**
+** {H13012} The [sqlite3_prepare16(db,zSql,...)] and
+** [sqlite3_prepare16_v2(db,zSql,...)] interfaces interpret the
+** text in their zSql parameter as UTF-16 in the native byte order.
+**
+** {H13013} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)]
+** and its variants is less than zero, the SQL text is
+** read from zSql is read up to the first zero terminator.
+**
+** {H13014} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)]
+** and its variants is non-negative, then at most nBytes bytes of
+** SQL text is read from zSql.
+**
+** {H13015} In [sqlite3_prepare_v2(db,zSql,N,P,pzTail)] and its variants
+** if the zSql input text contains more than one SQL statement
+** and pzTail is not NULL, then *pzTail is made to point to the
+** first byte past the end of the first SQL statement in zSql.
+** <todo>What does *pzTail point to if there is one statement?</todo>
+**
+** {H13016} A successful call to [sqlite3_prepare_v2(db,zSql,N,ppStmt,...)]
+** or one of its variants writes into *ppStmt a pointer to a new
+** [prepared statement] or a pointer to NULL if zSql contains
+** nothing other than whitespace or comments.
+**
+** {H13019} The [sqlite3_prepare_v2()] interface and its variants return
+** [SQLITE_OK] or an appropriate [error code] upon failure.
+**
+** {H13021} Before [sqlite3_prepare(db,zSql,nByte,ppStmt,pzTail)] or its
+** variants returns an error (any value other than [SQLITE_OK]),
+** they first set *ppStmt to NULL.
+*/
+int sqlite3_prepare(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare_v2(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16_v2(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+
+/*
+** CAPIREF: Retrieving Statement SQL {H13100} <H13000>
+**
+** This interface can be used to retrieve a saved copy of the original
+** SQL text used to create a [prepared statement] if that statement was
+** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+**
+** INVARIANTS:
+**
+** {H13101} If the [prepared statement] passed as the argument to
+** [sqlite3_sql()] was compiled using either [sqlite3_prepare_v2()] or
+** [sqlite3_prepare16_v2()], then [sqlite3_sql()] returns
+** a pointer to a zero-terminated string containing a UTF-8 rendering
+** of the original SQL statement.
+**
+** {H13102} If the [prepared statement] passed as the argument to
+** [sqlite3_sql()] was compiled using either [sqlite3_prepare()] or
+** [sqlite3_prepare16()], then [sqlite3_sql()] returns a NULL pointer.
+**
+** {H13103} The string returned by [sqlite3_sql(S)] is valid until the
+** [prepared statement] S is deleted using [sqlite3_finalize(S)].
+*/
+const char *sqlite3_sql(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Dynamically Typed Value Object {H15000} <S20200>
+** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
+**
+** SQLite uses the sqlite3_value object to represent all values
+** that can be stored in a database table. SQLite uses dynamic typing
+** for the values it stores. Values stored in sqlite3_value objects
+** can be integers, floating point values, strings, BLOBs, or NULL.
+**
+** An sqlite3_value object may be either "protected" or "unprotected".
+** Some interfaces require a protected sqlite3_value. Other interfaces
+** will accept either a protected or an unprotected sqlite3_value.
+** Every interface that accepts sqlite3_value arguments specifies
+** whether or not it requires a protected sqlite3_value.
+**
+** The terms "protected" and "unprotected" refer to whether or not
+** a mutex is held. A internal mutex is held for a protected
+** sqlite3_value object but no mutex is held for an unprotected
+** sqlite3_value object. If SQLite is compiled to be single-threaded
+** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
+** or if SQLite is run in one of reduced mutex modes
+** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
+** then there is no distinction between protected and unprotected
+** sqlite3_value objects and they can be used interchangeably. However,
+** for maximum code portability it is recommended that applications
+** still make the distinction between between protected and unprotected
+** sqlite3_value objects even when not strictly required.
+**
+** The sqlite3_value objects that are passed as parameters into the
+** implementation of [application-defined SQL functions] are protected.
+** The sqlite3_value object returned by
+** [sqlite3_column_value()] is unprotected.
+** Unprotected sqlite3_value objects may only be used with
+** [sqlite3_result_value()] and [sqlite3_bind_value()].
+** The [sqlite3_value_blob | sqlite3_value_type()] family of
+** interfaces require protected sqlite3_value objects.
+*/
+typedef struct Mem sqlite3_value;
+
+/*
+** CAPI3REF: SQL Function Context Object {H16001} <S20200>
+**
+** The context in which an SQL function executes is stored in an
+** sqlite3_context object. A pointer to an sqlite3_context object
+** is always first parameter to [application-defined SQL functions].
+** The application-defined SQL function implementation will pass this
+** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
+** [sqlite3_aggregate_context()], [sqlite3_user_data()],
+** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()],
+** and/or [sqlite3_set_auxdata()].
+*/
+typedef struct sqlite3_context sqlite3_context;
+
+/*
+** CAPI3REF: Binding Values To Prepared Statements {H13500} <S70300>
+** KEYWORDS: {host parameter} {host parameters} {host parameter name}
+** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
+**
+** In the SQL strings input to [sqlite3_prepare_v2()] and its variants,
+** literals may be replaced by a parameter in one of these forms:
+**
+** <ul>
+** <li> ?
+** <li> ?NNN
+** <li> :VVV
+** <li> @VVV
+** <li> $VVV
+** </ul>
+**
+** In the parameter forms shown above NNN is an integer literal,
+** and VVV is an alpha-numeric parameter name. The values of these
+** parameters (also called "host parameter names" or "SQL parameters")
+** can be set using the sqlite3_bind_*() routines defined here.
+**
+** The first argument to the sqlite3_bind_*() routines is always
+** a pointer to the [sqlite3_stmt] object returned from
+** [sqlite3_prepare_v2()] or its variants.
+**
+** The second argument is the index of the SQL parameter to be set.
+** The leftmost SQL parameter has an index of 1. When the same named
+** SQL parameter is used more than once, second and subsequent
+** occurrences have the same index as the first occurrence.
+** The index for named parameters can be looked up using the
+** [sqlite3_bind_parameter_index()] API if desired. The index
+** for "?NNN" parameters is the value of NNN.
+** The NNN value must be between 1 and the [sqlite3_limit()]
+** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
+**
+** The third argument is the value to bind to the parameter.
+**
+** In those routines that have a fourth argument, its value is the
+** number of bytes in the parameter. To be clear: the value is the
+** number of <u>bytes</u> in the value, not the number of characters.
+** If the fourth parameter is negative, the length of the string is
+** the number of bytes up to the first zero terminator.
+**
+** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
+** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+** string after SQLite has finished with it. If the fifth argument is
+** the special value [SQLITE_STATIC], then SQLite assumes that the
+** information is in static, unmanaged space and does not need to be freed.
+** If the fifth argument has the value [SQLITE_TRANSIENT], then
+** SQLite makes its own private copy of the data immediately, before
+** the sqlite3_bind_*() routine returns.
+**
+** The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
+** is filled with zeroes. A zeroblob uses a fixed amount of memory
+** (just an integer to hold its size) while it is being processed.
+** Zeroblobs are intended to serve as placeholders for BLOBs whose
+** content is later written using
+** [sqlite3_blob_open | incremental BLOB I/O] routines.
+** A negative value for the zeroblob results in a zero-length BLOB.
+**
+** The sqlite3_bind_*() routines must be called after
+** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and
+** before [sqlite3_step()].
+** Bindings are not cleared by the [sqlite3_reset()] routine.
+** Unbound parameters are interpreted as NULL.
+**
+** These routines return [SQLITE_OK] on success or an error code if
+** anything goes wrong. [SQLITE_RANGE] is returned if the parameter
+** index is out of range. [SQLITE_NOMEM] is returned if malloc() fails.
+** [SQLITE_MISUSE] might be returned if these routines are called on a
+** virtual machine that is the wrong state or which has already been finalized.
+** Detection of misuse is unreliable. Applications should not depend
+** on SQLITE_MISUSE returns. SQLITE_MISUSE is intended to indicate a
+** a logic error in the application. Future versions of SQLite might
+** panic rather than return SQLITE_MISUSE.
+**
+** See also: [sqlite3_bind_parameter_count()],
+** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13506} The [SQL statement compiler] recognizes tokens of the forms
+** "?", "?NNN", "$VVV", ":VVV", and "@VVV" as SQL parameters,
+** where NNN is any sequence of one or more digits
+** and where VVV is any sequence of one or more alphanumeric
+** characters or "::" optionally followed by a string containing
+** no spaces and contained within parentheses.
+**
+** {H13509} The initial value of an SQL parameter is NULL.
+**
+** {H13512} The index of an "?" SQL parameter is one larger than the
+** largest index of SQL parameter to the left, or 1 if
+** the "?" is the leftmost SQL parameter.
+**
+** {H13515} The index of an "?NNN" SQL parameter is the integer NNN.
+**
+** {H13518} The index of an ":VVV", "$VVV", or "@VVV" SQL parameter is
+** the same as the index of leftmost occurrences of the same
+** parameter, or one more than the largest index over all
+** parameters to the left if this is the first occurrence
+** of this parameter, or 1 if this is the leftmost parameter.
+**
+** {H13521} The [SQL statement compiler] fails with an [SQLITE_RANGE]
+** error if the index of an SQL parameter is less than 1
+** or greater than the compile-time SQLITE_MAX_VARIABLE_NUMBER
+** parameter.
+**
+** {H13524} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,V,...)]
+** associate the value V with all SQL parameters having an
+** index of N in the [prepared statement] S.
+**
+** {H13527} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,...)]
+** override prior calls with the same values of S and N.
+**
+** {H13530} Bindings established by [sqlite3_bind_text | sqlite3_bind(S,...)]
+** persist across calls to [sqlite3_reset(S)].
+**
+** {H13533} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds the first L
+** bytes of the BLOB or string pointed to by V, when L
+** is non-negative.
+**
+** {H13536} In calls to [sqlite3_bind_text(S,N,V,L,D)] or
+** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds characters
+** from V through the first zero character when L is negative.
+**
+** {H13539} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special
+** constant [SQLITE_STATIC], SQLite assumes that the value V
+** is held in static unmanaged space that will not change
+** during the lifetime of the binding.
+**
+** {H13542} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special
+** constant [SQLITE_TRANSIENT], the routine makes a
+** private copy of the value V before it returns.
+**
+** {H13545} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is a pointer to
+** a function, SQLite invokes that function to destroy the
+** value V after it has finished using the value V.
+**
+** {H13548} In calls to [sqlite3_bind_zeroblob(S,N,V,L)] the value bound
+** is a BLOB of L bytes, or a zero-length BLOB if L is negative.
+**
+** {H13551} In calls to [sqlite3_bind_value(S,N,V)] the V argument may
+** be either a [protected sqlite3_value] object or an
+** [unprotected sqlite3_value] object.
+*/
+int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+int sqlite3_bind_double(sqlite3_stmt*, int, double);
+int sqlite3_bind_int(sqlite3_stmt*, int, int);
+int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
+int sqlite3_bind_null(sqlite3_stmt*, int);
+int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+
+/*
+** CAPI3REF: Number Of SQL Parameters {H13600} <S70300>
+**
+** This routine can be used to find the number of [SQL parameters]
+** in a [prepared statement]. SQL parameters are tokens of the
+** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as
+** placeholders for values that are [sqlite3_bind_blob | bound]
+** to the parameters at a later time.
+**
+** This routine actually returns the index of the largest (rightmost)
+** parameter. For all forms except ?NNN, this will correspond to the
+** number of unique parameters. If parameters of the ?NNN are used,
+** there may be gaps in the list.
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_name()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13601} The [sqlite3_bind_parameter_count(S)] interface returns
+** the largest index of all SQL parameters in the
+** [prepared statement] S, or 0 if S contains no SQL parameters.
+*/
+int sqlite3_bind_parameter_count(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Name Of A Host Parameter {H13620} <S70300>
+**
+** This routine returns a pointer to the name of the n-th
+** [SQL parameter] in a [prepared statement].
+** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** respectively.
+** In other words, the initial ":" or "$" or "@" or "?"
+** is included as part of the name.
+** Parameters of the form "?" without a following integer have no name
+** and are also referred to as "anonymous parameters".
+**
+** The first host parameter has an index of 1, not 0.
+**
+** If the value n is out of range or if the n-th parameter is
+** nameless, then NULL is returned. The returned string is
+** always in UTF-8 encoding even if the named parameter was
+** originally specified as UTF-16 in [sqlite3_prepare16()] or
+** [sqlite3_prepare16_v2()].
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_count()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13621} The [sqlite3_bind_parameter_name(S,N)] interface returns
+** a UTF-8 rendering of the name of the SQL parameter in
+** the [prepared statement] S having index N, or
+** NULL if there is no SQL parameter with index N or if the
+** parameter with index N is an anonymous parameter "?".
+*/
+const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
+
+/*
+** CAPI3REF: Index Of A Parameter With A Given Name {H13640} <S70300>
+**
+** Return the index of an SQL parameter given its name. The
+** index value returned is suitable for use as the second
+** parameter to [sqlite3_bind_blob|sqlite3_bind()]. A zero
+** is returned if no matching parameter is found. The parameter
+** name must be given in UTF-8 even if the original statement
+** was prepared from UTF-16 text using [sqlite3_prepare16_v2()].
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_count()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13641} The [sqlite3_bind_parameter_index(S,N)] interface returns
+** the index of SQL parameter in the [prepared statement]
+** S whose name matches the UTF-8 string N, or 0 if there is
+** no match.
+*/
+int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
+
+/*
+** CAPI3REF: Reset All Bindings On A Prepared Statement {H13660} <S70300>
+**
+** Contrary to the intuition of many, [sqlite3_reset()] does not reset
+** the [sqlite3_bind_blob | bindings] on a [prepared statement].
+** Use this routine to reset all host parameters to NULL.
+**
+** INVARIANTS:
+**
+** {H13661} The [sqlite3_clear_bindings(S)] interface resets all SQL
+** parameter bindings in the [prepared statement] S back to NULL.
+*/
+int sqlite3_clear_bindings(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Number Of Columns In A Result Set {H13710} <S10700>
+**
+** Return the number of columns in the result set returned by the
+** [prepared statement]. This routine returns 0 if pStmt is an SQL
+** statement that does not return data (for example an [UPDATE]).
+**
+** INVARIANTS:
+**
+** {H13711} The [sqlite3_column_count(S)] interface returns the number of
+** columns in the result set generated by the [prepared statement] S,
+** or 0 if S does not generate a result set.
+*/
+int sqlite3_column_count(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Column Names In A Result Set {H13720} <S10700>
+**
+** These routines return the name assigned to a particular column
+** in the result set of a [SELECT] statement. The sqlite3_column_name()
+** interface returns a pointer to a zero-terminated UTF-8 string
+** and sqlite3_column_name16() returns a pointer to a zero-terminated
+** UTF-16 string. The first parameter is the [prepared statement]
+** that implements the [SELECT] statement. The second parameter is the
+** column number. The leftmost column is number 0.
+**
+** The returned string pointer is valid until either the [prepared statement]
+** is destroyed by [sqlite3_finalize()] or until the next call to
+** sqlite3_column_name() or sqlite3_column_name16() on the same column.
+**
+** If sqlite3_malloc() fails during the processing of either routine
+** (for example during a conversion from UTF-8 to UTF-16) then a
+** NULL pointer is returned.
+**
+** The name of a result column is the value of the "AS" clause for
+** that column, if there is an AS clause. If there is no AS clause
+** then the name of the column is unspecified and may change from
+** one release of SQLite to the next.
+**
+** INVARIANTS:
+**
+** {H13721} A successful invocation of the [sqlite3_column_name(S,N)]
+** interface returns the name of the Nth column (where 0 is
+** the leftmost column) for the result set of the
+** [prepared statement] S as a zero-terminated UTF-8 string.
+**
+** {H13723} A successful invocation of the [sqlite3_column_name16(S,N)]
+** interface returns the name of the Nth column (where 0 is
+** the leftmost column) for the result set of the
+** [prepared statement] S as a zero-terminated UTF-16 string
+** in the native byte order.
+**
+** {H13724} The [sqlite3_column_name()] and [sqlite3_column_name16()]
+** interfaces return a NULL pointer if they are unable to
+** allocate memory to hold their normal return strings.
+**
+** {H13725} If the N parameter to [sqlite3_column_name(S,N)] or
+** [sqlite3_column_name16(S,N)] is out of range, then the
+** interfaces return a NULL pointer.
+**
+** {H13726} The strings returned by [sqlite3_column_name(S,N)] and
+** [sqlite3_column_name16(S,N)] are valid until the next
+** call to either routine with the same S and N parameters
+** or until [sqlite3_finalize(S)] is called.
+**
+** {H13727} When a result column of a [SELECT] statement contains
+** an AS clause, the name of that column is the identifier
+** to the right of the AS keyword.
+*/
+const char *sqlite3_column_name(sqlite3_stmt*, int N);
+const void *sqlite3_column_name16(sqlite3_stmt*, int N);
+
+/*
+** CAPI3REF: Source Of Data In A Query Result {H13740} <S10700>
+**
+** These routines provide a means to determine what column of what
+** table in which database a result of a [SELECT] statement comes from.
+** The name of the database or table or column can be returned as
+** either a UTF-8 or UTF-16 string. The _database_ routines return
+** the database name, the _table_ routines return the table name, and
+** the origin_ routines return the column name.
+** The returned string is valid until the [prepared statement] is destroyed
+** using [sqlite3_finalize()] or until the same information is requested
+** again in a different encoding.
+**
+** The names returned are the original un-aliased names of the
+** database, table, and column.
+**
+** The first argument to the following calls is a [prepared statement].
+** These functions return information about the Nth column returned by
+** the statement, where N is the second function argument.
+**
+** If the Nth column returned by the statement is an expression or
+** subquery and is not a column value, then all of these functions return
+** NULL. These routine might also return NULL if a memory allocation error
+** occurs. Otherwise, they return the name of the attached database, table
+** and column that query result column was extracted from.
+**
+** As with all other SQLite APIs, those postfixed with "16" return
+** UTF-16 encoded strings, the other functions return UTF-8. {END}
+**
+** These APIs are only available if the library was compiled with the
+** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+**
+** {A13751}
+** If two or more threads call one or more of these routines against the same
+** prepared statement and column at the same time then the results are
+** undefined.
+**
+** INVARIANTS:
+**
+** {H13741} The [sqlite3_column_database_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the database from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13742} The [sqlite3_column_database_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the database
+** from which the Nth result column of the [prepared statement] S is
+** extracted, or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13743} The [sqlite3_column_table_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the table from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13744} The [sqlite3_column_table_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the table
+** from which the Nth result column of the [prepared statement] S is
+** extracted, or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13745} The [sqlite3_column_origin_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the table column from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13746} The [sqlite3_column_origin_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the table
+** column from which the Nth result column of the
+** [prepared statement] S is extracted, or NULL if the Nth column
+** of S is a general expression or if unable to allocate memory
+** to store the name.
+**
+** {H13748} The return values from
+** [sqlite3_column_database_name | column metadata interfaces]
+** are valid for the lifetime of the [prepared statement]
+** or until the encoding is changed by another metadata
+** interface call for the same prepared statement and column.
+**
+** ASSUMPTIONS:
+**
+** {A13751} If two or more threads call one or more
+** [sqlite3_column_database_name | column metadata interfaces]
+** for the same [prepared statement] and result column
+** at the same time then the results are undefined.
+*/
+const char *sqlite3_column_database_name(sqlite3_stmt*,int);
+const void *sqlite3_column_database_name16(sqlite3_stmt*,int);
+const char *sqlite3_column_table_name(sqlite3_stmt*,int);
+const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
+const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
+const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
+
+/*
+** CAPI3REF: Declared Datatype Of A Query Result {H13760} <S10700>
+**
+** The first parameter is a [prepared statement].
+** If this statement is a [SELECT] statement and the Nth column of the
+** returned result set of that [SELECT] is a table column (not an
+** expression or subquery) then the declared type of the table
+** column is returned. If the Nth column of the result set is an
+** expression or subquery, then a NULL pointer is returned.
+** The returned string is always UTF-8 encoded. {END}
+**
+** For example, given the database schema:
+**
+** CREATE TABLE t1(c1 VARIANT);
+**
+** and the following statement to be compiled:
+**
+** SELECT c1 + 1, c1 FROM t1;
+**
+** this routine would return the string "VARIANT" for the second result
+** column (i==1), and a NULL pointer for the first result column (i==0).
+**
+** SQLite uses dynamic run-time typing. So just because a column
+** is declared to contain a particular type does not mean that the
+** data stored in that column is of the declared type. SQLite is
+** strongly typed, but the typing is dynamic not static. Type
+** is associated with individual values, not with the containers
+** used to hold those values.
+**
+** INVARIANTS:
+**
+** {H13761} A successful call to [sqlite3_column_decltype(S,N)] returns a
+** zero-terminated UTF-8 string containing the declared datatype
+** of the table column that appears as the Nth column (numbered
+** from 0) of the result set to the [prepared statement] S.
+**
+** {H13762} A successful call to [sqlite3_column_decltype16(S,N)]
+** returns a zero-terminated UTF-16 native byte order string
+** containing the declared datatype of the table column that appears
+** as the Nth column (numbered from 0) of the result set to the
+** [prepared statement] S.
+**
+** {H13763} If N is less than 0 or N is greater than or equal to
+** the number of columns in the [prepared statement] S,
+** or if the Nth column of S is an expression or subquery rather
+** than a table column, or if a memory allocation failure
+** occurs during encoding conversions, then
+** calls to [sqlite3_column_decltype(S,N)] or
+** [sqlite3_column_decltype16(S,N)] return NULL.
+*/
+const char *sqlite3_column_decltype(sqlite3_stmt*,int);
+const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
+
+/*
+** CAPI3REF: Evaluate An SQL Statement {H13200} <S10000>
+**
+** After a [prepared statement] has been prepared using either
+** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy
+** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function
+** must be called one or more times to evaluate the statement.
+**
+** The details of the behavior of the sqlite3_step() interface depend
+** on whether the statement was prepared using the newer "v2" interface
+** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy
+** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the
+** new "v2" interface is recommended for new applications but the legacy
+** interface will continue to be supported.
+**
+** In the legacy interface, the return value will be either [SQLITE_BUSY],
+** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
+** With the "v2" interface, any of the other [result codes] or
+** [extended result codes] might be returned as well.
+**
+** [SQLITE_BUSY] means that the database engine was unable to acquire the
+** database locks it needs to do its job. If the statement is a [COMMIT]
+** or occurs outside of an explicit transaction, then you can retry the
+** statement. If the statement is not a [COMMIT] and occurs within a
+** explicit transaction then you should rollback the transaction before
+** continuing.
+**
+** [SQLITE_DONE] means that the statement has finished executing
+** successfully. sqlite3_step() should not be called again on this virtual
+** machine without first calling [sqlite3_reset()] to reset the virtual
+** machine back to its initial state.
+**
+** If the SQL statement being executed returns any data, then [SQLITE_ROW]
+** is returned each time a new row of data is ready for processing by the
+** caller. The values may be accessed using the [column access functions].
+** sqlite3_step() is called again to retrieve the next row of data.
+**
+** [SQLITE_ERROR] means that a run-time error (such as a constraint
+** violation) has occurred. sqlite3_step() should not be called again on
+** the VM. More information may be found by calling [sqlite3_errmsg()].
+** With the legacy interface, a more specific error code (for example,
+** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)
+** can be obtained by calling [sqlite3_reset()] on the
+** [prepared statement]. In the "v2" interface,
+** the more specific error code is returned directly by sqlite3_step().
+**
+** [SQLITE_MISUSE] means that the this routine was called inappropriately.
+** Perhaps it was called on a [prepared statement] that has
+** already been [sqlite3_finalize | finalized] or on one that had
+** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could
+** be the case that the same database connection is being used by two or
+** more threads at the same moment in time.
+**
+** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()
+** API always returns a generic error code, [SQLITE_ERROR], following any
+** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call
+** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the
+** specific [error codes] that better describes the error.
+** We admit that this is a goofy design. The problem has been fixed
+** with the "v2" interface. If you prepare all of your SQL statements
+** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead
+** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,
+** then the more specific [error codes] are returned directly
+** by sqlite3_step(). The use of the "v2" interface is recommended.
+**
+** INVARIANTS:
+**
+** {H13202} If the [prepared statement] S is ready to be run, then
+** [sqlite3_step(S)] advances that prepared statement until
+** completion or until it is ready to return another row of the
+** result set, or until an [sqlite3_interrupt | interrupt]
+** or a run-time error occurs.
+**
+** {H15304} When a call to [sqlite3_step(S)] causes the [prepared statement]
+** S to run to completion, the function returns [SQLITE_DONE].
+**
+** {H15306} When a call to [sqlite3_step(S)] stops because it is ready to
+** return another row of the result set, it returns [SQLITE_ROW].
+**
+** {H15308} If a call to [sqlite3_step(S)] encounters an
+** [sqlite3_interrupt | interrupt] or a run-time error,
+** it returns an appropriate error code that is not one of
+** [SQLITE_OK], [SQLITE_ROW], or [SQLITE_DONE].
+**
+** {H15310} If an [sqlite3_interrupt | interrupt] or a run-time error
+** occurs during a call to [sqlite3_step(S)]
+** for a [prepared statement] S created using
+** legacy interfaces [sqlite3_prepare()] or
+** [sqlite3_prepare16()], then the function returns either
+** [SQLITE_ERROR], [SQLITE_BUSY], or [SQLITE_MISUSE].
+*/
+int sqlite3_step(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Number of columns in a result set {H13770} <S10700>
+**
+** Returns the number of values in the current row of the result set.
+**
+** INVARIANTS:
+**
+** {H13771} After a call to [sqlite3_step(S)] that returns [SQLITE_ROW],
+** the [sqlite3_data_count(S)] routine will return the same value
+** as the [sqlite3_column_count(S)] function.
+**
+** {H13772} After [sqlite3_step(S)] has returned any value other than
+** [SQLITE_ROW] or before [sqlite3_step(S)] has been called on the
+** [prepared statement] for the first time since it was
+** [sqlite3_prepare | prepared] or [sqlite3_reset | reset],
+** the [sqlite3_data_count(S)] routine returns zero.
+*/
+int sqlite3_data_count(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Fundamental Datatypes {H10265} <S10110><S10120>
+** KEYWORDS: SQLITE_TEXT
+**
+** {H10266} Every value in SQLite has one of five fundamental datatypes:
+**
+** <ul>
+** <li> 64-bit signed integer
+** <li> 64-bit IEEE floating point number
+** <li> string
+** <li> BLOB
+** <li> NULL
+** </ul> {END}
+**
+** These constants are codes for each of those types.
+**
+** Note that the SQLITE_TEXT constant was also used in SQLite version 2
+** for a completely different meaning. Software that links against both
+** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not
+** SQLITE_TEXT.
+*/
+#define SQLITE_INTEGER 1
+#define SQLITE_FLOAT 2
+#define SQLITE_BLOB 4
+#define SQLITE_NULL 5
+#ifdef SQLITE_TEXT
+# undef SQLITE_TEXT
+#else
+# define SQLITE_TEXT 3
+#endif
+#define SQLITE3_TEXT 3
+
+/*
+** CAPI3REF: Result Values From A Query {H13800} <S10700>
+** KEYWORDS: {column access functions}
+**
+** These routines form the "result set query" interface.
+**
+** These routines return information about a single column of the current
+** result row of a query. In every case the first argument is a pointer
+** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*]
+** that was returned from [sqlite3_prepare_v2()] or one of its variants)
+** and the second argument is the index of the column for which information
+** should be returned. The leftmost column of the result set has the index 0.
+**
+** If the SQL statement does not currently point to a valid row, or if the
+** column index is out of range, the result is undefined.
+** These routines may only be called when the most recent call to
+** [sqlite3_step()] has returned [SQLITE_ROW] and neither
+** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently.
+** If any of these routines are called after [sqlite3_reset()] or
+** [sqlite3_finalize()] or after [sqlite3_step()] has returned
+** something other than [SQLITE_ROW], the results are undefined.
+** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()]
+** are called from a different thread while any of these routines
+** are pending, then the results are undefined.
+**
+** The sqlite3_column_type() routine returns the
+** [SQLITE_INTEGER | datatype code] for the initial data type
+** of the result column. The returned value is one of [SQLITE_INTEGER],
+** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value
+** returned by sqlite3_column_type() is only meaningful if no type
+** conversions have occurred as described below. After a type conversion,
+** the value returned by sqlite3_column_type() is undefined. Future
+** versions of SQLite may change the behavior of sqlite3_column_type()
+** following a type conversion.
+**
+** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()
+** routine returns the number of bytes in that BLOB or string.
+** If the result is a UTF-16 string, then sqlite3_column_bytes() converts
+** the string to UTF-8 and then returns the number of bytes.
+** If the result is a numeric value then sqlite3_column_bytes() uses
+** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns
+** the number of bytes in that string.
+** The value returned does not include the zero terminator at the end
+** of the string. For clarity: the value returned is the number of
+** bytes in the string, not the number of characters.
+**
+** Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
+** even empty strings, are always zero terminated. The return
+** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary
+** pointer, possibly even a NULL pointer.
+**
+** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes()
+** but leaves the result in UTF-16 in native byte order instead of UTF-8.
+** The zero terminator is not included in this count.
+**
+** The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
+** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** If the [unprotected sqlite3_value] object returned by
+** [sqlite3_column_value()] is used in any other way, including calls
+** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
+** or [sqlite3_value_bytes()], then the behavior is undefined.
+**
+** These routines attempt to convert the value where appropriate. For
+** example, if the internal representation is FLOAT and a text result
+** is requested, [sqlite3_snprintf()] is used internally to perform the
+** conversion automatically. The following table details the conversions
+** that are applied:
+**
+** <blockquote>
+** <table border="1">
+** <tr><th> Internal<br>Type <th> Requested<br>Type <th> Conversion
+**
+** <tr><td> NULL <td> INTEGER <td> Result is 0
+** <tr><td> NULL <td> FLOAT <td> Result is 0.0
+** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
+** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
+** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
+** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
+** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT
+** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
+** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
+** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
+** <tr><td> TEXT <td> INTEGER <td> Use atoi()
+** <tr><td> TEXT <td> FLOAT <td> Use atof()
+** <tr><td> TEXT <td> BLOB <td> No change
+** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
+** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
+** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
+** </table>
+** </blockquote>
+**
+** The table above makes reference to standard C library functions atoi()
+** and atof(). SQLite does not really use these functions. It has its
+** own equivalent internal routines. The atoi() and atof() names are
+** used in the table for brevity and because they are familiar to most
+** C programmers.
+**
+** Note that when type conversions occur, pointers returned by prior
+** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
+** sqlite3_column_text16() may be invalidated.
+** Type conversions and pointer invalidations might occur
+** in the following cases:
+**
+** <ul>
+** <li> The initial content is a BLOB and sqlite3_column_text() or
+** sqlite3_column_text16() is called. A zero-terminator might
+** need to be added to the string.</li>
+** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or
+** sqlite3_column_text16() is called. The content must be converted
+** to UTF-16.</li>
+** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or
+** sqlite3_column_text() is called. The content must be converted
+** to UTF-8.</li>
+** </ul>
+**
+** Conversions between UTF-16be and UTF-16le are always done in place and do
+** not invalidate a prior pointer, though of course the content of the buffer
+** that the prior pointer points to will have been modified. Other kinds
+** of conversion are done in place when it is possible, but sometimes they
+** are not possible and in those cases prior pointers are invalidated.
+**
+** The safest and easiest to remember policy is to invoke these routines
+** in one of the following ways:
+**
+** <ul>
+** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>
+** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>
+** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>
+** </ul>
+**
+** In other words, you should call sqlite3_column_text(),
+** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result
+** into the desired format, then invoke sqlite3_column_bytes() or
+** sqlite3_column_bytes16() to find the size of the result. Do not mix calls
+** to sqlite3_column_text() or sqlite3_column_blob() with calls to
+** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16()
+** with calls to sqlite3_column_bytes().
+**
+** The pointers returned are valid until a type conversion occurs as
+** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
+** [sqlite3_finalize()] is called. The memory space used to hold strings
+** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
+** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
+** [sqlite3_free()].
+**
+** If a memory allocation error occurs during the evaluation of any
+** of these routines, a default value is returned. The default value
+** is either the integer 0, the floating point number 0.0, or a NULL
+** pointer. Subsequent calls to [sqlite3_errcode()] will return
+** [SQLITE_NOMEM].
+**
+** INVARIANTS:
+**
+** {H13803} The [sqlite3_column_blob(S,N)] interface converts the
+** Nth column in the current row of the result set for
+** the [prepared statement] S into a BLOB and then returns a
+** pointer to the converted value.
+**
+** {H13806} The [sqlite3_column_bytes(S,N)] interface returns the
+** number of bytes in the BLOB or string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_column_blob(S,N)] or
+** [sqlite3_column_text(S,N)].
+**
+** {H13809} The [sqlite3_column_bytes16(S,N)] interface returns the
+** number of bytes in the string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_column_text16(S,N)].
+**
+** {H13812} The [sqlite3_column_double(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a floating point value and
+** returns a copy of that value.
+**
+** {H13815} The [sqlite3_column_int(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a 64-bit signed integer and
+** returns the lower 32 bits of that integer.
+**
+** {H13818} The [sqlite3_column_int64(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a 64-bit signed integer and
+** returns a copy of that integer.
+**
+** {H13821} The [sqlite3_column_text(S,N)] interface converts the
+** Nth column in the current row of the result set for
+** the [prepared statement] S into a zero-terminated UTF-8
+** string and returns a pointer to that string.
+**
+** {H13824} The [sqlite3_column_text16(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a zero-terminated 2-byte
+** aligned UTF-16 native byte order string and returns
+** a pointer to that string.
+**
+** {H13827} The [sqlite3_column_type(S,N)] interface returns
+** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT],
+** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for
+** the Nth column in the current row of the result set for
+** the [prepared statement] S.
+**
+** {H13830} The [sqlite3_column_value(S,N)] interface returns a
+** pointer to an [unprotected sqlite3_value] object for the
+** Nth column in the current row of the result set for
+** the [prepared statement] S.
+*/
+const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
+double sqlite3_column_double(sqlite3_stmt*, int iCol);
+int sqlite3_column_int(sqlite3_stmt*, int iCol);
+sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
+const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
+int sqlite3_column_type(sqlite3_stmt*, int iCol);
+sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
+
+/*
+** CAPI3REF: Destroy A Prepared Statement Object {H13300} <S70300><S30100>
+**
+** The sqlite3_finalize() function is called to delete a [prepared statement].
+** If the statement was executed successfully or not executed at all, then
+** SQLITE_OK is returned. If execution of the statement failed then an
+** [error code] or [extended error code] is returned.
+**
+** This routine can be called at any point during the execution of the
+** [prepared statement]. If the virtual machine has not
+** completed execution when this routine is called, that is like
+** encountering an error or an [sqlite3_interrupt | interrupt].
+** Incomplete updates may be rolled back and transactions canceled,
+** depending on the circumstances, and the
+** [error code] returned will be [SQLITE_ABORT].
+**
+** INVARIANTS:
+**
+** {H11302} The [sqlite3_finalize(S)] interface destroys the
+** [prepared statement] S and releases all
+** memory and file resources held by that object.
+**
+** {H11304} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S returned an error,
+** then [sqlite3_finalize(S)] returns that same error.
+*/
+int sqlite3_finalize(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Reset A Prepared Statement Object {H13330} <S70300>
+**
+** The sqlite3_reset() function is called to reset a [prepared statement]
+** object back to its initial state, ready to be re-executed.
+** Any SQL statement variables that had values bound to them using
+** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values.
+** Use [sqlite3_clear_bindings()] to reset the bindings.
+**
+** {H11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S
+** back to the beginning of its program.
+**
+** {H11334} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],
+** or if [sqlite3_step(S)] has never before been called on S,
+** then [sqlite3_reset(S)] returns [SQLITE_OK].
+**
+** {H11336} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S indicated an error, then
+** [sqlite3_reset(S)] returns an appropriate [error code].
+**
+** {H11338} The [sqlite3_reset(S)] interface does not change the values
+** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
+*/
+int sqlite3_reset(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Create Or Redefine SQL Functions {H16100} <S20200>
+** KEYWORDS: {function creation routines}
+** KEYWORDS: {application-defined SQL function}
+** KEYWORDS: {application-defined SQL functions}
+**
+** These two functions (collectively known as "function creation routines")
+** are used to add SQL functions or aggregates or to redefine the behavior
+** of existing SQL functions or aggregates. The only difference between the
+** two is that the second parameter, the name of the (scalar) function or
+** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16
+** for sqlite3_create_function16().
+**
+** The first parameter is the [database connection] to which the SQL
+** function is to be added. If a single program uses more than one database
+** connection internally, then SQL functions must be added individually to
+** each database connection.
+**
+** The second parameter is the name of the SQL function to be created or
+** redefined. The length of the name is limited to 255 bytes, exclusive of
+** the zero-terminator. Note that the name length limit is in bytes, not
+** characters. Any attempt to create a function with a longer name
+** will result in [SQLITE_ERROR] being returned.
+**
+** The third parameter is the number of arguments that the SQL function or
+** aggregate takes. If this parameter is negative, then the SQL function or
+** aggregate may take any number of arguments.
+**
+** The fourth parameter, eTextRep, specifies what
+** [SQLITE_UTF8 | text encoding] this SQL function prefers for
+** its parameters. Any SQL function implementation should be able to work
+** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
+** more efficient with one encoding than another. It is allowed to
+** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
+** times with the same function but with different values of eTextRep.
+** When multiple implementations of the same function are available, SQLite
+** will pick the one that involves the least amount of data conversion.
+** If there is only a single implementation which does not care what text
+** encoding is used, then the fourth argument should be [SQLITE_ANY].
+**
+** The fifth parameter is an arbitrary pointer. The implementation of the
+** function can gain access to this pointer using [sqlite3_user_data()].
+**
+** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
+** pointers to C-language functions that implement the SQL function or
+** aggregate. A scalar SQL function requires an implementation of the xFunc
+** callback only, NULL pointers should be passed as the xStep and xFinal
+** parameters. An aggregate SQL function requires an implementation of xStep
+** and xFinal and NULL should be passed for xFunc. To delete an existing
+** SQL function or aggregate, pass NULL for all three function callbacks.
+**
+** It is permitted to register multiple implementations of the same
+** functions with the same name but with either differing numbers of
+** arguments or differing preferred text encodings. SQLite will use
+** the implementation most closely matches the way in which the
+** SQL function is used.
+**
+** INVARIANTS:
+**
+** {H16103} The [sqlite3_create_function16()] interface behaves exactly
+** like [sqlite3_create_function()] in every way except that it
+** interprets the zFunctionName argument as zero-terminated UTF-16
+** native byte order instead of as zero-terminated UTF-8.
+**
+** {H16106} A successful invocation of
+** the [sqlite3_create_function(D,X,N,E,...)] interface registers
+** or replaces callback functions in the [database connection] D
+** used to implement the SQL function named X with N parameters
+** and having a preferred text encoding of E.
+**
+** {H16109} A successful call to [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** replaces the P, F, S, and L values from any prior calls with
+** the same D, X, N, and E values.
+**
+** {H16112} The [sqlite3_create_function(D,X,...)] interface fails with
+** a return code of [SQLITE_ERROR] if the SQL function name X is
+** longer than 255 bytes exclusive of the zero terminator.
+**
+** {H16118} Either F must be NULL and S and L are non-NULL or else F
+** is non-NULL and S and L are NULL, otherwise
+** [sqlite3_create_function(D,X,N,E,P,F,S,L)] returns [SQLITE_ERROR].
+**
+** {H16121} The [sqlite3_create_function(D,...)] interface fails with an
+** error code of [SQLITE_BUSY] if there exist [prepared statements]
+** associated with the [database connection] D.
+**
+** {H16124} The [sqlite3_create_function(D,X,N,...)] interface fails with an
+** error code of [SQLITE_ERROR] if parameter N (specifying the number
+** of arguments to the SQL function being registered) is less
+** than -1 or greater than 127.
+**
+** {H16127} When N is non-negative, the [sqlite3_create_function(D,X,N,...)]
+** interface causes callbacks to be invoked for the SQL function
+** named X when the number of arguments to the SQL function is
+** exactly N.
+**
+** {H16130} When N is -1, the [sqlite3_create_function(D,X,N,...)]
+** interface causes callbacks to be invoked for the SQL function
+** named X with any number of arguments.
+**
+** {H16133} When calls to [sqlite3_create_function(D,X,N,...)]
+** specify multiple implementations of the same function X
+** and when one implementation has N>=0 and the other has N=(-1)
+** the implementation with a non-zero N is preferred.
+**
+** {H16136} When calls to [sqlite3_create_function(D,X,N,E,...)]
+** specify multiple implementations of the same function X with
+** the same number of arguments N but with different
+** encodings E, then the implementation where E matches the
+** database encoding is preferred.
+**
+** {H16139} For an aggregate SQL function created using
+** [sqlite3_create_function(D,X,N,E,P,0,S,L)] the finalizer
+** function L will always be invoked exactly once if the
+** step function S is called one or more times.
+**
+** {H16142} When SQLite invokes either the xFunc or xStep function of
+** an application-defined SQL function or aggregate created
+** by [sqlite3_create_function()] or [sqlite3_create_function16()],
+** then the array of [sqlite3_value] objects passed as the
+** third parameter are always [protected sqlite3_value] objects.
+*/
+int sqlite3_create_function(
+ sqlite3 *db,
+ const char *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pApp,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+int sqlite3_create_function16(
+ sqlite3 *db,
+ const void *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pApp,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+
+/*
+** CAPI3REF: Text Encodings {H10267} <S50200> <H16100>
+**
+** These constant define integer codes that represent the various
+** text encodings supported by SQLite.
+*/
+#define SQLITE_UTF8 1
+#define SQLITE_UTF16LE 2
+#define SQLITE_UTF16BE 3
+#define SQLITE_UTF16 4 /* Use native byte order */
+#define SQLITE_ANY 5 /* sqlite3_create_function only */
+#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
+
+/*
+** CAPI3REF: Deprecated Functions
+** DEPRECATED
+**
+** These functions are [deprecated]. In order to maintain
+** backwards compatibility with older code, these functions continue
+** to be supported. However, new applications should avoid
+** the use of these functions. To help encourage people to avoid
+** using these functions, we are not going to tell you want they do.
+*/
+int sqlite3_aggregate_count(sqlite3_context*);
+int sqlite3_expired(sqlite3_stmt*);
+int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
+int sqlite3_global_recover(void);
+void sqlite3_thread_cleanup(void);
+int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+
+/*
+** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} <S20200>
+**
+** The C-language implementation of SQL functions and aggregates uses
+** this set of interface routines to access the parameter values on
+** the function or aggregate.
+**
+** The xFunc (for scalar functions) or xStep (for aggregates) parameters
+** to [sqlite3_create_function()] and [sqlite3_create_function16()]
+** define callbacks that implement the SQL functions and aggregates.
+** The 4th parameter to these callbacks is an array of pointers to
+** [protected sqlite3_value] objects. There is one [sqlite3_value] object for
+** each parameter to the SQL function. These routines are used to
+** extract values from the [sqlite3_value] objects.
+**
+** These routines work only with [protected sqlite3_value] objects.
+** Any attempt to use these routines on an [unprotected sqlite3_value]
+** object results in undefined behavior.
+**
+** These routines work just like the corresponding [column access functions]
+** except that these routines take a single [protected sqlite3_value] object
+** pointer instead of a [sqlite3_stmt*] pointer and an integer column number.
+**
+** The sqlite3_value_text16() interface extracts a UTF-16 string
+** in the native byte-order of the host machine. The
+** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
+** extract UTF-16 strings as big-endian and little-endian respectively.
+**
+** The sqlite3_value_numeric_type() interface attempts to apply
+** numeric affinity to the value. This means that an attempt is
+** made to convert the value to an integer or floating point. If
+** such a conversion is possible without loss of information (in other
+** words, if the value is a string that looks like a number)
+** then the conversion is performed. Otherwise no conversion occurs.
+** The [SQLITE_INTEGER | datatype] after conversion is returned.
+**
+** Please pay particular attention to the fact that the pointer returned
+** from [sqlite3_value_blob()], [sqlite3_value_text()], or
+** [sqlite3_value_text16()] can be invalidated by a subsequent call to
+** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()],
+** or [sqlite3_value_text16()].
+**
+** These routines must be called from the same thread as
+** the SQL function that supplied the [sqlite3_value*] parameters.
+**
+** INVARIANTS:
+**
+** {H15103} The [sqlite3_value_blob(V)] interface converts the
+** [protected sqlite3_value] object V into a BLOB and then
+** returns a pointer to the converted value.
+**
+** {H15106} The [sqlite3_value_bytes(V)] interface returns the
+** number of bytes in the BLOB or string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_value_blob(V)] or
+** [sqlite3_value_text(V)].
+**
+** {H15109} The [sqlite3_value_bytes16(V)] interface returns the
+** number of bytes in the string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_value_text16(V)],
+** [sqlite3_value_text16be(V)], or [sqlite3_value_text16le(V)].
+**
+** {H15112} The [sqlite3_value_double(V)] interface converts the
+** [protected sqlite3_value] object V into a floating point value and
+** returns a copy of that value.
+**
+** {H15115} The [sqlite3_value_int(V)] interface converts the
+** [protected sqlite3_value] object V into a 64-bit signed integer and
+** returns the lower 32 bits of that integer.
+**
+** {H15118} The [sqlite3_value_int64(V)] interface converts the
+** [protected sqlite3_value] object V into a 64-bit signed integer and
+** returns a copy of that integer.
+**
+** {H15121} The [sqlite3_value_text(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated UTF-8
+** string and returns a pointer to that string.
+**
+** {H15124} The [sqlite3_value_text16(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 native byte order
+** string and returns a pointer to that string.
+**
+** {H15127} The [sqlite3_value_text16be(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 big-endian
+** string and returns a pointer to that string.
+**
+** {H15130} The [sqlite3_value_text16le(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 little-endian
+** string and returns a pointer to that string.
+**
+** {H15133} The [sqlite3_value_type(V)] interface returns
+** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT],
+** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for
+** the [sqlite3_value] object V.
+**
+** {H15136} The [sqlite3_value_numeric_type(V)] interface converts
+** the [protected sqlite3_value] object V into either an integer or
+** a floating point value if it can do so without loss of
+** information, and returns one of [SQLITE_NULL],
+** [SQLITE_INTEGER], [SQLITE_FLOAT], [SQLITE_TEXT], or
+** [SQLITE_BLOB] as appropriate for the
+** [protected sqlite3_value] object V after the conversion attempt.
+*/
+const void *sqlite3_value_blob(sqlite3_value*);
+int sqlite3_value_bytes(sqlite3_value*);
+int sqlite3_value_bytes16(sqlite3_value*);
+double sqlite3_value_double(sqlite3_value*);
+int sqlite3_value_int(sqlite3_value*);
+sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
+const unsigned char *sqlite3_value_text(sqlite3_value*);
+const void *sqlite3_value_text16(sqlite3_value*);
+const void *sqlite3_value_text16le(sqlite3_value*);
+const void *sqlite3_value_text16be(sqlite3_value*);
+int sqlite3_value_type(sqlite3_value*);
+int sqlite3_value_numeric_type(sqlite3_value*);
+
+/*
+** CAPI3REF: Obtain Aggregate Function Context {H16210} <S20200>
+**
+** The implementation of aggregate SQL functions use this routine to allocate
+** a structure for storing their state.
+**
+** The first time the sqlite3_aggregate_context() routine is called for a
+** particular aggregate, SQLite allocates nBytes of memory, zeroes out that
+** memory, and returns a pointer to it. On second and subsequent calls to
+** sqlite3_aggregate_context() for the same aggregate function index,
+** the same buffer is returned. The implementation of the aggregate can use
+** the returned buffer to accumulate data.
+**
+** SQLite automatically frees the allocated buffer when the aggregate
+** query concludes.
+**
+** The first parameter should be a copy of the
+** [sqlite3_context | SQL function context] that is the first parameter
+** to the callback routine that implements the aggregate function.
+**
+** This routine must be called from the same thread in which
+** the aggregate SQL function is running.
+**
+** INVARIANTS:
+**
+** {H16211} The first invocation of [sqlite3_aggregate_context(C,N)] for
+** a particular instance of an aggregate function (for a particular
+** context C) causes SQLite to allocate N bytes of memory,
+** zero that memory, and return a pointer to the allocated memory.
+**
+** {H16213} If a memory allocation error occurs during
+** [sqlite3_aggregate_context(C,N)] then the function returns 0.
+**
+** {H16215} Second and subsequent invocations of
+** [sqlite3_aggregate_context(C,N)] for the same context pointer C
+** ignore the N parameter and return a pointer to the same
+** block of memory returned by the first invocation.
+**
+** {H16217} The memory allocated by [sqlite3_aggregate_context(C,N)] is
+** automatically freed on the next call to [sqlite3_reset()]
+** or [sqlite3_finalize()] for the [prepared statement] containing
+** the aggregate function associated with context C.
+*/
+void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
+
+/*
+** CAPI3REF: User Data For Functions {H16240} <S20200>
+**
+** The sqlite3_user_data() interface returns a copy of
+** the pointer that was the pUserData parameter (the 5th parameter)
+** of the [sqlite3_create_function()]
+** and [sqlite3_create_function16()] routines that originally
+** registered the application defined function. {END}
+**
+** This routine must be called from the same thread in which
+** the application-defined function is running.
+**
+** INVARIANTS:
+**
+** {H16243} The [sqlite3_user_data(C)] interface returns a copy of the
+** P pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that
+** registered the SQL function associated with [sqlite3_context] C.
+*/
+void *sqlite3_user_data(sqlite3_context*);
+
+/*
+** CAPI3REF: Database Connection For Functions {H16250} <S60600><S20200>
+**
+** The sqlite3_context_db_handle() interface returns a copy of
+** the pointer to the [database connection] (the 1st parameter)
+** of the [sqlite3_create_function()]
+** and [sqlite3_create_function16()] routines that originally
+** registered the application defined function.
+**
+** INVARIANTS:
+**
+** {H16253} The [sqlite3_context_db_handle(C)] interface returns a copy of the
+** D pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that
+** registered the SQL function associated with [sqlite3_context] C.
+*/
+sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
+
+/*
+** CAPI3REF: Function Auxiliary Data {H16270} <S20200>
+**
+** The following two functions may be used by scalar SQL functions to
+** associate metadata with argument values. If the same value is passed to
+** multiple invocations of the same SQL function during query execution, under
+** some circumstances the associated metadata may be preserved. This may
+** be used, for example, to add a regular-expression matching scalar
+** function. The compiled version of the regular expression is stored as
+** metadata associated with the SQL value passed as the regular expression
+** pattern. The compiled regular expression can be reused on multiple
+** invocations of the same function so that the original pattern string
+** does not need to be recompiled on each invocation.
+**
+** The sqlite3_get_auxdata() interface returns a pointer to the metadata
+** associated by the sqlite3_set_auxdata() function with the Nth argument
+** value to the application-defined function. If no metadata has been ever
+** been set for the Nth argument of the function, or if the corresponding
+** function parameter has changed since the meta-data was set,
+** then sqlite3_get_auxdata() returns a NULL pointer.
+**
+** The sqlite3_set_auxdata() interface saves the metadata
+** pointed to by its 3rd parameter as the metadata for the N-th
+** argument of the application-defined function. Subsequent
+** calls to sqlite3_get_auxdata() might return this data, if it has
+** not been destroyed.
+** If it is not NULL, SQLite will invoke the destructor
+** function given by the 4th parameter to sqlite3_set_auxdata() on
+** the metadata when the corresponding function parameter changes
+** or when the SQL statement completes, whichever comes first.
+**
+** SQLite is free to call the destructor and drop metadata on any
+** parameter of any function at any time. The only guarantee is that
+** the destructor will be called before the metadata is dropped.
+**
+** In practice, metadata is preserved between function calls for
+** expressions that are constant at compile time. This includes literal
+** values and SQL variables.
+**
+** These routines must be called from the same thread in which
+** the SQL function is running.
+**
+** INVARIANTS:
+**
+** {H16272} The [sqlite3_get_auxdata(C,N)] interface returns a pointer
+** to metadata associated with the Nth parameter of the SQL function
+** whose context is C, or NULL if there is no metadata associated
+** with that parameter.
+**
+** {H16274} The [sqlite3_set_auxdata(C,N,P,D)] interface assigns a metadata
+** pointer P to the Nth parameter of the SQL function with context C.
+**
+** {H16276} SQLite will invoke the destructor D with a single argument
+** which is the metadata pointer P following a call to
+** [sqlite3_set_auxdata(C,N,P,D)] when SQLite ceases to hold
+** the metadata.
+**
+** {H16277} SQLite ceases to hold metadata for an SQL function parameter
+** when the value of that parameter changes.
+**
+** {H16278} When [sqlite3_set_auxdata(C,N,P,D)] is invoked, the destructor
+** is called for any prior metadata associated with the same function
+** context C and parameter N.
+**
+** {H16279} SQLite will call destructors for any metadata it is holding
+** in a particular [prepared statement] S when either
+** [sqlite3_reset(S)] or [sqlite3_finalize(S)] is called.
+*/
+void *sqlite3_get_auxdata(sqlite3_context*, int N);
+void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+
+
+/*
+** CAPI3REF: Constants Defining Special Destructor Behavior {H10280} <S30100>
+**
+** These are special values for the destructor that is passed in as the
+** final argument to routines like [sqlite3_result_blob()]. If the destructor
+** argument is SQLITE_STATIC, it means that the content pointer is constant
+** and will never change. It does not need to be destroyed. The
+** SQLITE_TRANSIENT value means that the content will likely change in
+** the near future and that SQLite should make its own private copy of
+** the content before returning.
+**
+** The typedef is necessary to work around problems in certain
+** C++ compilers. See ticket #2191.
+*/
+typedef void (*sqlite3_destructor_type)(void*);
+#define SQLITE_STATIC ((sqlite3_destructor_type)0)
+#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
+
+/*
+** CAPI3REF: Setting The Result Of An SQL Function {H16400} <S20200>
+**
+** These routines are used by the xFunc or xFinal callbacks that
+** implement SQL functions and aggregates. See
+** [sqlite3_create_function()] and [sqlite3_create_function16()]
+** for additional information.
+**
+** These functions work very much like the [parameter binding] family of
+** functions used to bind values to host parameters in prepared statements.
+** Refer to the [SQL parameter] documentation for additional information.
+**
+** The sqlite3_result_blob() interface sets the result from
+** an application-defined function to be the BLOB whose content is pointed
+** to by the second parameter and which is N bytes long where N is the
+** third parameter.
+**
+** The sqlite3_result_zeroblob() interfaces set the result of
+** the application-defined function to be a BLOB containing all zero
+** bytes and N bytes in size, where N is the value of the 2nd parameter.
+**
+** The sqlite3_result_double() interface sets the result from
+** an application-defined function to be a floating point value specified
+** by its 2nd argument.
+**
+** The sqlite3_result_error() and sqlite3_result_error16() functions
+** cause the implemented SQL function to throw an exception.
+** SQLite uses the string pointed to by the
+** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
+** as the text of an error message. SQLite interprets the error
+** message string from sqlite3_result_error() as UTF-8. SQLite
+** interprets the string from sqlite3_result_error16() as UTF-16 in native
+** byte order. If the third parameter to sqlite3_result_error()
+** or sqlite3_result_error16() is negative then SQLite takes as the error
+** message all text up through the first zero character.
+** If the third parameter to sqlite3_result_error() or
+** sqlite3_result_error16() is non-negative then SQLite takes that many
+** bytes (not characters) from the 2nd parameter as the error message.
+** The sqlite3_result_error() and sqlite3_result_error16()
+** routines make a private copy of the error message text before
+** they return. Hence, the calling function can deallocate or
+** modify the text after they return without harm.
+** The sqlite3_result_error_code() function changes the error code
+** returned by SQLite as a result of an error in a function. By default,
+** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error()
+** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
+**
+** The sqlite3_result_toobig() interface causes SQLite to throw an error
+** indicating that a string or BLOB is to long to represent.
+**
+** The sqlite3_result_nomem() interface causes SQLite to throw an error
+** indicating that a memory allocation failed.
+**
+** The sqlite3_result_int() interface sets the return value
+** of the application-defined function to be the 32-bit signed integer
+** value given in the 2nd argument.
+** The sqlite3_result_int64() interface sets the return value
+** of the application-defined function to be the 64-bit signed integer
+** value given in the 2nd argument.
+**
+** The sqlite3_result_null() interface sets the return value
+** of the application-defined function to be NULL.
+**
+** The sqlite3_result_text(), sqlite3_result_text16(),
+** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces
+** set the return value of the application-defined function to be
+** a text string which is represented as UTF-8, UTF-16 native byte order,
+** UTF-16 little endian, or UTF-16 big endian, respectively.
+** SQLite takes the text result from the application from
+** the 2nd parameter of the sqlite3_result_text* interfaces.
+** If the 3rd parameter to the sqlite3_result_text* interfaces
+** is negative, then SQLite takes result text from the 2nd parameter
+** through the first zero character.
+** If the 3rd parameter to the sqlite3_result_text* interfaces
+** is non-negative, then as many bytes (not characters) of the text
+** pointed to by the 2nd parameter are taken as the application-defined
+** function result.
+** If the 4th parameter to the sqlite3_result_text* interfaces
+** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
+** function as the destructor on the text or BLOB result when it has
+** finished using that result.
+** If the 4th parameter to the sqlite3_result_text* interfaces or
+** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite
+** assumes that the text or BLOB result is in constant space and does not
+** copy the it or call a destructor when it has finished using that result.
+** If the 4th parameter to the sqlite3_result_text* interfaces
+** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
+** then SQLite makes a copy of the result into space obtained from
+** from [sqlite3_malloc()] before it returns.
+**
+** The sqlite3_result_value() interface sets the result of
+** the application-defined function to be a copy the
+** [unprotected sqlite3_value] object specified by the 2nd parameter. The
+** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
+** so that the [sqlite3_value] specified in the parameter may change or
+** be deallocated after sqlite3_result_value() returns without harm.
+** A [protected sqlite3_value] object may always be used where an
+** [unprotected sqlite3_value] object is required, so either
+** kind of [sqlite3_value] object can be used with this interface.
+**
+** If these routines are called from within the different thread
+** than the one containing the application-defined function that received
+** the [sqlite3_context] pointer, the results are undefined.
+**
+** INVARIANTS:
+**
+** {H16403} The default return value from any SQL function is NULL.
+**
+** {H16406} The [sqlite3_result_blob(C,V,N,D)] interface changes the
+** return value of function C to be a BLOB that is N bytes
+** in length and with content pointed to by V.
+**
+** {H16409} The [sqlite3_result_double(C,V)] interface changes the
+** return value of function C to be the floating point value V.
+**
+** {H16412} The [sqlite3_result_error(C,V,N)] interface changes the return
+** value of function C to be an exception with error code
+** [SQLITE_ERROR] and a UTF-8 error message copied from V up to the
+** first zero byte or until N bytes are read if N is positive.
+**
+** {H16415} The [sqlite3_result_error16(C,V,N)] interface changes the return
+** value of function C to be an exception with error code
+** [SQLITE_ERROR] and a UTF-16 native byte order error message
+** copied from V up to the first zero terminator or until N bytes
+** are read if N is positive.
+**
+** {H16418} The [sqlite3_result_error_toobig(C)] interface changes the return
+** value of the function C to be an exception with error code
+** [SQLITE_TOOBIG] and an appropriate error message.
+**
+** {H16421} The [sqlite3_result_error_nomem(C)] interface changes the return
+** value of the function C to be an exception with error code
+** [SQLITE_NOMEM] and an appropriate error message.
+**
+** {H16424} The [sqlite3_result_error_code(C,E)] interface changes the return
+** value of the function C to be an exception with error code E.
+** The error message text is unchanged.
+**
+** {H16427} The [sqlite3_result_int(C,V)] interface changes the
+** return value of function C to be the 32-bit integer value V.
+**
+** {H16430} The [sqlite3_result_int64(C,V)] interface changes the
+** return value of function C to be the 64-bit integer value V.
+**
+** {H16433} The [sqlite3_result_null(C)] interface changes the
+** return value of function C to be NULL.
+**
+** {H16436} The [sqlite3_result_text(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-8 string
+** V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16439} The [sqlite3_result_text16(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 native byte order
+** string V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16442} The [sqlite3_result_text16be(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 big-endian
+** string V up to the first zero if N is negative
+** or the first N bytes or V if N is non-negative.
+**
+** {H16445} The [sqlite3_result_text16le(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 little-endian
+** string V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16448} The [sqlite3_result_value(C,V)] interface changes the
+** return value of function C to be the [unprotected sqlite3_value]
+** object V.
+**
+** {H16451} The [sqlite3_result_zeroblob(C,N)] interface changes the
+** return value of function C to be an N-byte BLOB of all zeros.
+**
+** {H16454} The [sqlite3_result_error()] and [sqlite3_result_error16()]
+** interfaces make a copy of their error message strings before
+** returning.
+**
+** {H16457} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is the constant [SQLITE_STATIC]
+** then no destructor is ever called on the pointer V and SQLite
+** assumes that V is immutable.
+**
+** {H16460} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is the constant
+** [SQLITE_TRANSIENT] then the interfaces makes a copy of the
+** content of V and retains the copy.
+**
+** {H16463} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is some value other than
+** the constants [SQLITE_STATIC] and [SQLITE_TRANSIENT] then
+** SQLite will invoke the destructor D with V as its only argument
+** when it has finished with the V value.
+*/
+void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_double(sqlite3_context*, double);
+void sqlite3_result_error(sqlite3_context*, const char*, int);
+void sqlite3_result_error16(sqlite3_context*, const void*, int);
+void sqlite3_result_error_toobig(sqlite3_context*);
+void sqlite3_result_error_nomem(sqlite3_context*);
+void sqlite3_result_error_code(sqlite3_context*, int);
+void sqlite3_result_int(sqlite3_context*, int);
+void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
+void sqlite3_result_null(sqlite3_context*);
+void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
+void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+void sqlite3_result_zeroblob(sqlite3_context*, int n);
+
+/*
+** CAPI3REF: Define New Collating Sequences {H16600} <S20300>
+**
+** These functions are used to add new collation sequences to the
+** [database connection] specified as the first argument.
+**
+** The name of the new collation sequence is specified as a UTF-8 string
+** for sqlite3_create_collation() and sqlite3_create_collation_v2()
+** and a UTF-16 string for sqlite3_create_collation16(). In all cases
+** the name is passed as the second function argument.
+**
+** The third argument may be one of the constants [SQLITE_UTF8],
+** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied
+** routine expects to be passed pointers to strings encoded using UTF-8,
+** UTF-16 little-endian, or UTF-16 big-endian, respectively. The
+** third argument might also be [SQLITE_UTF16_ALIGNED] to indicate that
+** the routine expects pointers to 16-bit word aligned strings
+** of UTF-16 in the native byte order of the host computer.
+**
+** A pointer to the user supplied routine must be passed as the fifth
+** argument. If it is NULL, this is the same as deleting the collation
+** sequence (so that SQLite cannot call it anymore).
+** Each time the application supplied function is invoked, it is passed
+** as its first parameter a copy of the void* passed as the fourth argument
+** to sqlite3_create_collation() or sqlite3_create_collation16().
+**
+** The remaining arguments to the application-supplied routine are two strings,
+** each represented by a (length, data) pair and encoded in the encoding
+** that was passed as the third argument when the collation sequence was
+** registered. {END} The application defined collation routine should
+** return negative, zero or positive if the first string is less than,
+** equal to, or greater than the second string. i.e. (STRING1 - STRING2).
+**
+** The sqlite3_create_collation_v2() works like sqlite3_create_collation()
+** except that it takes an extra argument which is a destructor for
+** the collation. The destructor is called when the collation is
+** destroyed and is passed a copy of the fourth parameter void* pointer
+** of the sqlite3_create_collation_v2().
+** Collations are destroyed when they are overridden by later calls to the
+** collation creation functions or when the [database connection] is closed
+** using [sqlite3_close()].
+**
+** INVARIANTS:
+**
+** {H16603} A successful call to the
+** [sqlite3_create_collation_v2(B,X,E,P,F,D)] interface
+** registers function F as the comparison function used to
+** implement collation X on the [database connection] B for
+** databases having encoding E.
+**
+** {H16604} SQLite understands the X parameter to
+** [sqlite3_create_collation_v2(B,X,E,P,F,D)] as a zero-terminated
+** UTF-8 string in which case is ignored for ASCII characters and
+** is significant for non-ASCII characters.
+**
+** {H16606} Successive calls to [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** with the same values for B, X, and E, override prior values
+** of P, F, and D.
+**
+** {H16609} If the destructor D in [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** is not NULL then it is called with argument P when the
+** collating function is dropped by SQLite.
+**
+** {H16612} A collating function is dropped when it is overloaded.
+**
+** {H16615} A collating function is dropped when the database connection
+** is closed using [sqlite3_close()].
+**
+** {H16618} The pointer P in [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** is passed through as the first parameter to the comparison
+** function F for all subsequent invocations of F.
+**
+** {H16621} A call to [sqlite3_create_collation(B,X,E,P,F)] is exactly
+** the same as a call to [sqlite3_create_collation_v2()] with
+** the same parameters and a NULL destructor.
+**
+** {H16624} Following a [sqlite3_create_collation_v2(B,X,E,P,F,D)],
+** SQLite uses the comparison function F for all text comparison
+** operations on the [database connection] B on text values that
+** use the collating sequence named X.
+**
+** {H16627} The [sqlite3_create_collation16(B,X,E,P,F)] works the same
+** as [sqlite3_create_collation(B,X,E,P,F)] except that the
+** collation name X is understood as UTF-16 in native byte order
+** instead of UTF-8.
+**
+** {H16630} When multiple comparison functions are available for the same
+** collating sequence, SQLite chooses the one whose text encoding
+** requires the least amount of conversion from the default
+** text encoding of the database.
+*/
+int sqlite3_create_collation(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+int sqlite3_create_collation_v2(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*),
+ void(*xDestroy)(void*)
+);
+int sqlite3_create_collation16(
+ sqlite3*,
+ const void *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+
+/*
+** CAPI3REF: Collation Needed Callbacks {H16700} <S20300>
+**
+** To avoid having to register all collation sequences before a database
+** can be used, a single callback function may be registered with the
+** [database connection] to be called whenever an undefined collation
+** sequence is required.
+**
+** If the function is registered using the sqlite3_collation_needed() API,
+** then it is passed the names of undefined collation sequences as strings
+** encoded in UTF-8. {H16703} If sqlite3_collation_needed16() is used,
+** the names are passed as UTF-16 in machine native byte order.
+** A call to either function replaces any existing callback.
+**
+** When the callback is invoked, the first argument passed is a copy
+** of the second argument to sqlite3_collation_needed() or
+** sqlite3_collation_needed16(). The second argument is the database
+** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE],
+** or [SQLITE_UTF16LE], indicating the most desirable form of the collation
+** sequence function required. The fourth parameter is the name of the
+** required collation sequence.
+**
+** The callback function should register the desired collation using
+** [sqlite3_create_collation()], [sqlite3_create_collation16()], or
+** [sqlite3_create_collation_v2()].
+**
+** INVARIANTS:
+**
+** {H16702} A successful call to [sqlite3_collation_needed(D,P,F)]
+** or [sqlite3_collation_needed16(D,P,F)] causes
+** the [database connection] D to invoke callback F with first
+** parameter P whenever it needs a comparison function for a
+** collating sequence that it does not know about.
+**
+** {H16704} Each successful call to [sqlite3_collation_needed()] or
+** [sqlite3_collation_needed16()] overrides the callback registered
+** on the same [database connection] by prior calls to either
+** interface.
+**
+** {H16706} The name of the requested collating function passed in the
+** 4th parameter to the callback is in UTF-8 if the callback
+** was registered using [sqlite3_collation_needed()] and
+** is in UTF-16 native byte order if the callback was
+** registered using [sqlite3_collation_needed16()].
+*/
+int sqlite3_collation_needed(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const char*)
+);
+int sqlite3_collation_needed16(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const void*)
+);
+
+/*
+** Specify the key for an encrypted database. This routine should be
+** called right after sqlite3_open().
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_key(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The key */
+);
+
+/*
+** Change the key on an open database. If the current database is not
+** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
+** database is decrypted.
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_rekey(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The new key */
+);
+
+/*
+** CAPI3REF: Suspend Execution For A Short Time {H10530} <S40410>
+**
+** The sqlite3_sleep() function causes the current thread to suspend execution
+** for at least a number of milliseconds specified in its parameter.
+**
+** If the operating system does not support sleep requests with
+** millisecond time resolution, then the time will be rounded up to
+** the nearest second. The number of milliseconds of sleep actually
+** requested from the operating system is returned.
+**
+** SQLite implements this interface by calling the xSleep()
+** method of the default [sqlite3_vfs] object.
+**
+** INVARIANTS:
+**
+** {H10533} The [sqlite3_sleep(M)] interface invokes the xSleep
+** method of the default [sqlite3_vfs|VFS] in order to
+** suspend execution of the current thread for at least
+** M milliseconds.
+**
+** {H10536} The [sqlite3_sleep(M)] interface returns the number of
+** milliseconds of sleep actually requested of the operating
+** system, which might be larger than the parameter M.
+*/
+int sqlite3_sleep(int);
+
+/*
+** CAPI3REF: Name Of The Folder Holding Temporary Files {H10310} <S20000>
+**
+** If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all temporary files
+** created by SQLite will be placed in that directory. If this variable
+** is a NULL pointer, then SQLite performs a search for an appropriate
+** temporary file directory.
+**
+** It is not safe to modify this variable once a [database connection]
+** has been opened. It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been call and remain unchanged thereafter.
+*/
+SQLITE_EXTERN char *sqlite3_temp_directory;
+
+/*
+** CAPI3REF: Test For Auto-Commit Mode {H12930} <S60200>
+** KEYWORDS: {autocommit mode}
+**
+** The sqlite3_get_autocommit() interface returns non-zero or
+** zero if the given database connection is or is not in autocommit mode,
+** respectively. Autocommit mode is on by default.
+** Autocommit mode is disabled by a [BEGIN] statement.
+** Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK].
+**
+** If certain kinds of errors occur on a statement within a multi-statement
+** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR],
+** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the
+** transaction might be rolled back automatically. The only way to
+** find out whether SQLite automatically rolled back the transaction after
+** an error is to use this function.
+**
+** INVARIANTS:
+**
+** {H12931} The [sqlite3_get_autocommit(D)] interface returns non-zero or
+** zero if the [database connection] D is or is not in autocommit
+** mode, respectively.
+**
+** {H12932} Autocommit mode is on by default.
+**
+** {H12933} Autocommit mode is disabled by a successful [BEGIN] statement.
+**
+** {H12934} Autocommit mode is enabled by a successful [COMMIT] or [ROLLBACK]
+** statement.
+**
+** ASSUMPTIONS:
+**
+** {A12936} If another thread changes the autocommit status of the database
+** connection while this routine is running, then the return value
+** is undefined.
+*/
+int sqlite3_get_autocommit(sqlite3*);
+
+/*
+** CAPI3REF: Find The Database Handle Of A Prepared Statement {H13120} <S60600>
+**
+** The sqlite3_db_handle interface returns the [database connection] handle
+** to which a [prepared statement] belongs. The database handle returned by
+** sqlite3_db_handle is the same database handle that was the first argument
+** to the [sqlite3_prepare_v2()] call (or its variants) that was used to
+** create the statement in the first place.
+**
+** INVARIANTS:
+**
+** {H13123} The [sqlite3_db_handle(S)] interface returns a pointer
+** to the [database connection] associated with the
+** [prepared statement] S.
+*/
+sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Find the next prepared statement {H13140} <S60600>
+**
+** This interface returns a pointer to the next [prepared statement] after
+** pStmt associated with the [database connection] pDb. If pStmt is NULL
+** then this interface returns a pointer to the first prepared statement
+** associated with the database connection pDb. If no prepared statement
+** satisfies the conditions of this routine, it returns NULL.
+**
+** INVARIANTS:
+**
+** {H13143} If D is a [database connection] that holds one or more
+** unfinalized [prepared statements] and S is a NULL pointer,
+** then [sqlite3_next_stmt(D, S)] routine shall return a pointer
+** to one of the prepared statements associated with D.
+**
+** {H13146} If D is a [database connection] that holds no unfinalized
+** [prepared statements] and S is a NULL pointer, then
+** [sqlite3_next_stmt(D, S)] routine shall return a NULL pointer.
+**
+** {H13149} If S is a [prepared statement] in the [database connection] D
+** and S is not the last prepared statement in D, then
+** [sqlite3_next_stmt(D, S)] routine shall return a pointer
+** to the next prepared statement in D after S.
+**
+** {H13152} If S is the last [prepared statement] in the
+** [database connection] D then the [sqlite3_next_stmt(D, S)]
+** routine shall return a NULL pointer.
+**
+** ASSUMPTIONS:
+**
+** {A13154} The [database connection] pointer D in a call to
+** [sqlite3_next_stmt(D,S)] must refer to an open database
+** connection and in particular must not be a NULL pointer.
+*/
+sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Commit And Rollback Notification Callbacks {H12950} <S60400>
+**
+** The sqlite3_commit_hook() interface registers a callback
+** function to be invoked whenever a transaction is committed.
+** Any callback set by a previous call to sqlite3_commit_hook()
+** for the same database connection is overridden.
+** The sqlite3_rollback_hook() interface registers a callback
+** function to be invoked whenever a transaction is committed.
+** Any callback set by a previous call to sqlite3_commit_hook()
+** for the same database connection is overridden.
+** The pArg argument is passed through to the callback.
+** If the callback on a commit hook function returns non-zero,
+** then the commit is converted into a rollback.
+**
+** If another function was previously registered, its
+** pArg value is returned. Otherwise NULL is returned.
+**
+** Registering a NULL function disables the callback.
+**
+** For the purposes of this API, a transaction is said to have been
+** rolled back if an explicit "ROLLBACK" statement is executed, or
+** an error or constraint causes an implicit rollback to occur.
+** The rollback callback is not invoked if a transaction is
+** automatically rolled back because the database connection is closed.
+** The rollback callback is not invoked if a transaction is
+** rolled back because a commit callback returned non-zero.
+** <todo> Check on this </todo>
+**
+** INVARIANTS:
+**
+** {H12951} The [sqlite3_commit_hook(D,F,P)] interface registers the
+** callback function F to be invoked with argument P whenever
+** a transaction commits on the [database connection] D.
+**
+** {H12952} The [sqlite3_commit_hook(D,F,P)] interface returns the P argument
+** from the previous call with the same [database connection] D,
+** or NULL on the first call for a particular database connection D.
+**
+** {H12953} Each call to [sqlite3_commit_hook()] overwrites the callback
+** registered by prior calls.
+**
+** {H12954} If the F argument to [sqlite3_commit_hook(D,F,P)] is NULL
+** then the commit hook callback is canceled and no callback
+** is invoked when a transaction commits.
+**
+** {H12955} If the commit callback returns non-zero then the commit is
+** converted into a rollback.
+**
+** {H12961} The [sqlite3_rollback_hook(D,F,P)] interface registers the
+** callback function F to be invoked with argument P whenever
+** a transaction rolls back on the [database connection] D.
+**
+** {H12962} The [sqlite3_rollback_hook(D,F,P)] interface returns the P
+** argument from the previous call with the same
+** [database connection] D, or NULL on the first call
+** for a particular database connection D.
+**
+** {H12963} Each call to [sqlite3_rollback_hook()] overwrites the callback
+** registered by prior calls.
+**
+** {H12964} If the F argument to [sqlite3_rollback_hook(D,F,P)] is NULL
+** then the rollback hook callback is canceled and no callback
+** is invoked when a transaction rolls back.
+*/
+void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
+void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
+
+/*
+** CAPI3REF: Data Change Notification Callbacks {H12970} <S60400>
+**
+** The sqlite3_update_hook() interface registers a callback function
+** with the [database connection] identified by the first argument
+** to be invoked whenever a row is updated, inserted or deleted.
+** Any callback set by a previous call to this function
+** for the same database connection is overridden.
+**
+** The second argument is a pointer to the function to invoke when a
+** row is updated, inserted or deleted.
+** The first argument to the callback is a copy of the third argument
+** to sqlite3_update_hook().
+** The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
+** or [SQLITE_UPDATE], depending on the operation that caused the callback
+** to be invoked.
+** The third and fourth arguments to the callback contain pointers to the
+** database and table name containing the affected row.
+** The final callback parameter is the rowid of the row. In the case of
+** an update, this is the rowid after the update takes place.
+**
+** The update hook is not invoked when internal system tables are
+** modified (i.e. sqlite_master and sqlite_sequence).
+**
+** If another function was previously registered, its pArg value
+** is returned. Otherwise NULL is returned.
+**
+** INVARIANTS:
+**
+** {H12971} The [sqlite3_update_hook(D,F,P)] interface causes the callback
+** function F to be invoked with first parameter P whenever
+** a table row is modified, inserted, or deleted on
+** the [database connection] D.
+**
+** {H12973} The [sqlite3_update_hook(D,F,P)] interface returns the value
+** of P for the previous call on the same [database connection] D,
+** or NULL for the first call.
+**
+** {H12975} If the update hook callback F in [sqlite3_update_hook(D,F,P)]
+** is NULL then the no update callbacks are made.
+**
+** {H12977} Each call to [sqlite3_update_hook(D,F,P)] overrides prior calls
+** to the same interface on the same [database connection] D.
+**
+** {H12979} The update hook callback is not invoked when internal system
+** tables such as sqlite_master and sqlite_sequence are modified.
+**
+** {H12981} The second parameter to the update callback
+** is one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
+** depending on the operation that caused the callback to be invoked.
+**
+** {H12983} The third and fourth arguments to the callback contain pointers
+** to zero-terminated UTF-8 strings which are the names of the
+** database and table that is being updated.
+
+** {H12985} The final callback parameter is the rowid of the row after
+** the change occurs.
+*/
+void *sqlite3_update_hook(
+ sqlite3*,
+ void(*)(void *,int ,char const *,char const *,sqlite3_int64),
+ void*
+);
+
+/*
+** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} <S30900>
+** KEYWORDS: {shared cache} {shared cache mode}
+**
+** This routine enables or disables the sharing of the database cache
+** and schema data structures between [database connection | connections]
+** to the same database. Sharing is enabled if the argument is true
+** and disabled if the argument is false.
+**
+** Cache sharing is enabled and disabled for an entire process. {END}
+** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** sharing was enabled or disabled for each thread separately.
+**
+** The cache sharing mode set by this interface effects all subsequent
+** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()].
+** Existing database connections continue use the sharing mode
+** that was in effect at the time they were opened.
+**
+** Virtual tables cannot be used with a shared cache. When shared
+** cache is enabled, the [sqlite3_create_module()] API used to register
+** virtual tables will always return an error.
+**
+** This routine returns [SQLITE_OK] if shared cache was enabled or disabled
+** successfully. An [error code] is returned otherwise.
+**
+** Shared cache is disabled by default. But this might change in
+** future releases of SQLite. Applications that care about shared
+** cache setting should set it explicitly.
+**
+** INVARIANTS:
+**
+** {H10331} A successful invocation of [sqlite3_enable_shared_cache(B)]
+** will enable or disable shared cache mode for any subsequently
+** created [database connection] in the same process.
+**
+** {H10336} When shared cache is enabled, the [sqlite3_create_module()]
+** interface will always return an error.
+**
+** {H10337} The [sqlite3_enable_shared_cache(B)] interface returns
+** [SQLITE_OK] if shared cache was enabled or disabled successfully.
+**
+** {H10339} Shared cache is disabled by default.
+*/
+int sqlite3_enable_shared_cache(int);
+
+/*
+** CAPI3REF: Attempt To Free Heap Memory {H17340} <S30220>
+**
+** The sqlite3_release_memory() interface attempts to free N bytes
+** of heap memory by deallocating non-essential memory allocations
+** held by the database library. {END} Memory used to cache database
+** pages to improve performance is an example of non-essential memory.
+** sqlite3_release_memory() returns the number of bytes actually freed,
+** which might be more or less than the amount requested.
+**
+** INVARIANTS:
+**
+** {H17341} The [sqlite3_release_memory(N)] interface attempts to
+** free N bytes of heap memory by deallocating non-essential
+** memory allocations held by the database library.
+**
+** {H16342} The [sqlite3_release_memory(N)] returns the number
+** of bytes actually freed, which might be more or less
+** than the amount requested.
+*/
+int sqlite3_release_memory(int);
+
+/*
+** CAPI3REF: Impose A Limit On Heap Size {H17350} <S30220>
+**
+** The sqlite3_soft_heap_limit() interface places a "soft" limit
+** on the amount of heap memory that may be allocated by SQLite.
+** If an internal allocation is requested that would exceed the
+** soft heap limit, [sqlite3_release_memory()] is invoked one or
+** more times to free up some space before the allocation is performed.
+**
+** The limit is called "soft", because if [sqlite3_release_memory()]
+** cannot free sufficient memory to prevent the limit from being exceeded,
+** the memory is allocated anyway and the current operation proceeds.
+**
+** A negative or zero value for N means that there is no soft heap limit and
+** [sqlite3_release_memory()] will only be called when memory is exhausted.
+** The default value for the soft heap limit is zero.
+**
+** SQLite makes a best effort to honor the soft heap limit.
+** But if the soft heap limit cannot be honored, execution will
+** continue without error or notification. This is why the limit is
+** called a "soft" limit. It is advisory only.
+**
+** Prior to SQLite version 3.5.0, this routine only constrained the memory
+** allocated by a single thread - the same thread in which this routine
+** runs. Beginning with SQLite version 3.5.0, the soft heap limit is
+** applied to all threads. The value specified for the soft heap limit
+** is an upper bound on the total memory allocation for all threads. In
+** version 3.5.0 there is no mechanism for limiting the heap usage for
+** individual threads.
+**
+** INVARIANTS:
+**
+** {H16351} The [sqlite3_soft_heap_limit(N)] interface places a soft limit
+** of N bytes on the amount of heap memory that may be allocated
+** using [sqlite3_malloc()] or [sqlite3_realloc()] at any point
+** in time.
+**
+** {H16352} If a call to [sqlite3_malloc()] or [sqlite3_realloc()] would
+** cause the total amount of allocated memory to exceed the
+** soft heap limit, then [sqlite3_release_memory()] is invoked
+** in an attempt to reduce the memory usage prior to proceeding
+** with the memory allocation attempt.
+**
+** {H16353} Calls to [sqlite3_malloc()] or [sqlite3_realloc()] that trigger
+** attempts to reduce memory usage through the soft heap limit
+** mechanism continue even if the attempt to reduce memory
+** usage is unsuccessful.
+**
+** {H16354} A negative or zero value for N in a call to
+** [sqlite3_soft_heap_limit(N)] means that there is no soft
+** heap limit and [sqlite3_release_memory()] will only be
+** called when memory is completely exhausted.
+**
+** {H16355} The default value for the soft heap limit is zero.
+**
+** {H16358} Each call to [sqlite3_soft_heap_limit(N)] overrides the
+** values set by all prior calls.
+*/
+void sqlite3_soft_heap_limit(int);
+
+/*
+** CAPI3REF: Extract Metadata About A Column Of A Table {H12850} <S60300>
+**
+** This routine returns metadata about a specific column of a specific
+** database table accessible using the [database connection] handle
+** passed as the first function argument.
+**
+** The column is identified by the second, third and fourth parameters to
+** this function. The second parameter is either the name of the database
+** (i.e. "main", "temp" or an attached database) containing the specified
+** table or NULL. If it is NULL, then all attached databases are searched
+** for the table using the same algorithm used by the database engine to
+** resolve unqualified table references.
+**
+** The third and fourth parameters to this function are the table and column
+** name of the desired column, respectively. Neither of these parameters
+** may be NULL.
+**
+** Metadata is returned by writing to the memory locations passed as the 5th
+** and subsequent parameters to this function. Any of these arguments may be
+** NULL, in which case the corresponding element of metadata is omitted.
+**
+** <blockquote>
+** <table border="1">
+** <tr><th> Parameter <th> Output<br>Type <th> Description
+**
+** <tr><td> 5th <td> const char* <td> Data type
+** <tr><td> 6th <td> const char* <td> Name of default collation sequence
+** <tr><td> 7th <td> int <td> True if column has a NOT NULL constraint
+** <tr><td> 8th <td> int <td> True if column is part of the PRIMARY KEY
+** <tr><td> 9th <td> int <td> True if column is AUTOINCREMENT
+** </table>
+** </blockquote>
+**
+** The memory pointed to by the character pointers returned for the
+** declaration type and collation sequence is valid only until the next
+** call to any SQLite API function.
+**
+** If the specified table is actually a view, an [error code] is returned.
+**
+** If the specified column is "rowid", "oid" or "_rowid_" and an
+** INTEGER PRIMARY KEY column has been explicitly declared, then the output
+** parameters are set for the explicitly declared column. If there is no
+** explicitly declared INTEGER PRIMARY KEY column, then the output
+** parameters are set as follows:
+**
+** <pre>
+** data type: "INTEGER"
+** collation sequence: "BINARY"
+** not null: 0
+** primary key: 1
+** auto increment: 0
+** </pre>
+**
+** This function may load one or more schemas from database files. If an
+** error occurs during this process, or if the requested table or column
+** cannot be found, an [error code] is returned and an error message left
+** in the [database connection] (to be retrieved using sqlite3_errmsg()).
+**
+** This API is only available if the library was compiled with the
+** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+*/
+int sqlite3_table_column_metadata(
+ sqlite3 *db, /* Connection handle */
+ const char *zDbName, /* Database name or NULL */
+ const char *zTableName, /* Table name */
+ const char *zColumnName, /* Column name */
+ char const **pzDataType, /* OUTPUT: Declared data type */
+ char const **pzCollSeq, /* OUTPUT: Collation sequence name */
+ int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
+ int *pPrimaryKey, /* OUTPUT: True if column part of PK */
+ int *pAutoinc /* OUTPUT: True if column is auto-increment */
+);
+
+/*
+** CAPI3REF: Load An Extension {H12600} <S20500>
+**
+** This interface loads an SQLite extension library from the named file.
+**
+** {H12601} The sqlite3_load_extension() interface attempts to load an
+** SQLite extension library contained in the file zFile.
+**
+** {H12602} The entry point is zProc.
+**
+** {H12603} zProc may be 0, in which case the name of the entry point
+** defaults to "sqlite3_extension_init".
+**
+** {H12604} The sqlite3_load_extension() interface shall return
+** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
+**
+** {H12605} If an error occurs and pzErrMsg is not 0, then the
+** [sqlite3_load_extension()] interface shall attempt to
+** fill *pzErrMsg with error message text stored in memory
+** obtained from [sqlite3_malloc()]. {END} The calling function
+** should free this memory by calling [sqlite3_free()].
+**
+** {H12606} Extension loading must be enabled using
+** [sqlite3_enable_load_extension()] prior to calling this API,
+** otherwise an error will be returned.
+*/
+int sqlite3_load_extension(
+ sqlite3 *db, /* Load the extension into this database connection */
+ const char *zFile, /* Name of the shared library containing extension */
+ const char *zProc, /* Entry point. Derived from zFile if 0 */
+ char **pzErrMsg /* Put error message here if not 0 */
+);
+
+/*
+** CAPI3REF: Enable Or Disable Extension Loading {H12620} <S20500>
+**
+** So as not to open security holes in older applications that are
+** unprepared to deal with extension loading, and as a means of disabling
+** extension loading while evaluating user-entered SQL, the following API
+** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
+**
+** Extension loading is off by default. See ticket #1863.
+**
+** {H12621} Call the sqlite3_enable_load_extension() routine with onoff==1
+** to turn extension loading on and call it with onoff==0 to turn
+** it back off again.
+**
+** {H12622} Extension loading is off by default.
+*/
+int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
+
+/*
+** CAPI3REF: Automatically Load An Extensions {H12640} <S20500>
+**
+** This API can be invoked at program startup in order to register
+** one or more statically linked extensions that will be available
+** to all new [database connections]. {END}
+**
+** This routine stores a pointer to the extension in an array that is
+** obtained from [sqlite3_malloc()]. If you run a memory leak checker
+** on your program and it reports a leak because of this array, invoke
+** [sqlite3_reset_auto_extension()] prior to shutdown to free the memory.
+**
+** {H12641} This function registers an extension entry point that is
+** automatically invoked whenever a new [database connection]
+** is opened using [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()].
+**
+** {H12642} Duplicate extensions are detected so calling this routine
+** multiple times with the same extension is harmless.
+**
+** {H12643} This routine stores a pointer to the extension in an array
+** that is obtained from [sqlite3_malloc()].
+**
+** {H12644} Automatic extensions apply across all threads.
+*/
+int sqlite3_auto_extension(void *xEntryPoint);
+
+/*
+** CAPI3REF: Reset Automatic Extension Loading {H12660} <S20500>
+**
+** This function disables all previously registered automatic
+** extensions. {END} It undoes the effect of all prior
+** [sqlite3_auto_extension()] calls.
+**
+** {H12661} This function disables all previously registered
+** automatic extensions.
+**
+** {H12662} This function disables automatic extensions in all threads.
+*/
+void sqlite3_reset_auto_extension(void);
+
+/*
+****** EXPERIMENTAL - subject to change without notice **************
+**
+** The interface to the virtual-table mechanism is currently considered
+** to be experimental. The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+*/
+
+/*
+** Structures used by the virtual table interface
+*/
+typedef struct sqlite3_vtab sqlite3_vtab;
+typedef struct sqlite3_index_info sqlite3_index_info;
+typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
+typedef struct sqlite3_module sqlite3_module;
+
+/*
+** CAPI3REF: Virtual Table Object {H18000} <S20400>
+** KEYWORDS: sqlite3_module
+** EXPERIMENTAL
+**
+** A module is a class of virtual tables. Each module is defined
+** by an instance of the following structure. This structure consists
+** mostly of methods for the module.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_module {
+ int iVersion;
+ int (*xCreate)(sqlite3*, void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab, char**);
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab, char**);
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+ int (*xClose)(sqlite3_vtab_cursor*);
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+ int (*xNext)(sqlite3_vtab_cursor*);
+ int (*xEof)(sqlite3_vtab_cursor*);
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
+ int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid);
+ int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *);
+ int (*xBegin)(sqlite3_vtab *pVTab);
+ int (*xSync)(sqlite3_vtab *pVTab);
+ int (*xCommit)(sqlite3_vtab *pVTab);
+ int (*xRollback)(sqlite3_vtab *pVTab);
+ int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg);
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+};
+
+/*
+** CAPI3REF: Virtual Table Indexing Information {H18100} <S20400>
+** KEYWORDS: sqlite3_index_info
+** EXPERIMENTAL
+**
+** The sqlite3_index_info structure and its substructures is used to
+** pass information into and receive the reply from the xBestIndex
+** method of an sqlite3_module. The fields under **Inputs** are the
+** inputs to xBestIndex and are read-only. xBestIndex inserts its
+** results into the **Outputs** fields.
+**
+** The aConstraint[] array records WHERE clause constraints of the form:
+**
+** <pre>column OP expr</pre>
+**
+** where OP is =, &lt;, &lt;=, &gt;, or &gt;=. The particular operator is
+** stored in aConstraint[].op. The index of the column is stored in
+** aConstraint[].iColumn. aConstraint[].usable is TRUE if the
+** expr on the right-hand side can be evaluated (and thus the constraint
+** is usable) and false if it cannot.
+**
+** The optimizer automatically inverts terms of the form "expr OP column"
+** and makes other simplifications to the WHERE clause in an attempt to
+** get as many WHERE clause terms into the form shown above as possible.
+** The aConstraint[] array only reports WHERE clause terms in the correct
+** form that refer to the particular virtual table being queried.
+**
+** Information about the ORDER BY clause is stored in aOrderBy[].
+** Each term of aOrderBy records a column of the ORDER BY clause.
+**
+** The xBestIndex method must fill aConstraintUsage[] with information
+** about what parameters to pass to xFilter. If argvIndex>0 then
+** the right-hand side of the corresponding aConstraint[] is evaluated
+** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit
+** is true, then the constraint is assumed to be fully handled by the
+** virtual table and is not checked again by SQLite.
+**
+** The idxNum and idxPtr values are recorded and passed into xFilter.
+** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true.
+**
+** The orderByConsumed means that output from xFilter will occur in
+** the correct order to satisfy the ORDER BY clause so that no separate
+** sorting step is required.
+**
+** The estimatedCost value is an estimate of the cost of doing the
+** particular lookup. A full scan of a table with N entries should have
+** a cost of N. A binary search of a table of N entries should have a
+** cost of approximately log(N).
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_index_info {
+ /* Inputs */
+ int nConstraint; /* Number of entries in aConstraint */
+ struct sqlite3_index_constraint {
+ int iColumn; /* Column on left-hand side of constraint */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *aConstraint; /* Table of WHERE clause constraints */
+ int nOrderBy; /* Number of terms in the ORDER BY clause */
+ struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+};
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+
+/*
+** CAPI3REF: Register A Virtual Table Implementation {H18200} <S20400>
+** EXPERIMENTAL
+**
+** This routine is used to register a new module name with a
+** [database connection]. Module names must be registered before
+** creating new virtual tables on the module, or before using
+** preexisting virtual tables of the module.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+int sqlite3_create_module(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *, /* Methods for the module */
+ void * /* Client data for xCreate/xConnect */
+);
+
+/*
+** CAPI3REF: Register A Virtual Table Implementation {H18210} <S20400>
+** EXPERIMENTAL
+**
+** This routine is identical to the [sqlite3_create_module()] method above,
+** except that it allows a destructor function to be specified. It is
+** even more experimental than the rest of the virtual tables API.
+*/
+int sqlite3_create_module_v2(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *, /* Methods for the module */
+ void *, /* Client data for xCreate/xConnect */
+ void(*xDestroy)(void*) /* Module destructor function */
+);
+
+/*
+** CAPI3REF: Virtual Table Instance Object {H18010} <S20400>
+** KEYWORDS: sqlite3_vtab
+** EXPERIMENTAL
+**
+** Every module implementation uses a subclass of the following structure
+** to describe a particular instance of the module. Each subclass will
+** be tailored to the specific needs of the module implementation.
+** The purpose of this superclass is to define certain fields that are
+** common to all module implementations.
+**
+** Virtual tables methods can set an error message by assigning a
+** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should
+** take care that any prior string is freed by a call to [sqlite3_free()]
+** prior to assigning a new string to zErrMsg. After the error message
+** is delivered up to the client application, the string will be automatically
+** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note
+** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field
+** since virtual tables are commonly implemented in loadable extensions which
+** do not have access to sqlite3MPrintf() or sqlite3Free().
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_vtab {
+ const sqlite3_module *pModule; /* The module for this virtual table */
+ int nRef; /* Used internally */
+ char *zErrMsg; /* Error message from sqlite3_mprintf() */
+ /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Virtual Table Cursor Object {H18020} <S20400>
+** KEYWORDS: sqlite3_vtab_cursor
+** EXPERIMENTAL
+**
+** Every module implementation uses a subclass of the following structure
+** to describe cursors that point into the virtual table and are used
+** to loop through the virtual table. Cursors are created using the
+** xOpen method of the module. Each module implementation will define
+** the content of a cursor structure to suit its own needs.
+**
+** This superclass exists in order to define fields of the cursor that
+** are common to all implementations.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_vtab_cursor {
+ sqlite3_vtab *pVtab; /* Virtual table of this cursor */
+ /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} <S20400>
+** EXPERIMENTAL
+**
+** The xCreate and xConnect methods of a module use the following API
+** to declare the format (the names and datatypes of the columns) of
+** the virtual tables they implement.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
+
+/*
+** CAPI3REF: Overload A Function For A Virtual Table {H18300} <S20400>
+** EXPERIMENTAL
+**
+** Virtual tables can provide alternative implementations of functions
+** using the xFindFunction method. But global versions of those functions
+** must exist in order to be overloaded.
+**
+** This API makes sure a global version of a function with a particular
+** name and number of parameters exists. If no such function exists
+** before this API is called, a new function is created. The implementation
+** of the new function always causes an exception to be thrown. So
+** the new function is not good for anything by itself. Its only
+** purpose is to be a placeholder function that can be overloaded
+** by virtual tables.
+**
+** This API should be considered part of the virtual table interface,
+** which is experimental and subject to change.
+*/
+int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
+
+/*
+** The interface to the virtual-table mechanism defined above (back up
+** to a comment remarkably similar to this one) is currently considered
+** to be experimental. The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+**
+****** EXPERIMENTAL - subject to change without notice **************
+*/
+
+/*
+** CAPI3REF: A Handle To An Open BLOB {H17800} <S30230>
+** KEYWORDS: {BLOB handle} {BLOB handles}
+**
+** An instance of this object represents an open BLOB on which
+** [sqlite3_blob_open | incremental BLOB I/O] can be performed.
+** Objects of this type are created by [sqlite3_blob_open()]
+** and destroyed by [sqlite3_blob_close()].
+** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces
+** can be used to read or write small subsections of the BLOB.
+** The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes.
+*/
+typedef struct sqlite3_blob sqlite3_blob;
+
+/*
+** CAPI3REF: Open A BLOB For Incremental I/O {H17810} <S30230>
+**
+** This interfaces opens a [BLOB handle | handle] to the BLOB located
+** in row iRow, column zColumn, table zTable in database zDb;
+** in other words, the same BLOB that would be selected by:
+**
+** <pre>
+** SELECT zColumn FROM zDb.zTable WHERE rowid = iRow;
+** </pre> {END}
+**
+** If the flags parameter is non-zero, the the BLOB is opened for read
+** and write access. If it is zero, the BLOB is opened for read access.
+**
+** Note that the database name is not the filename that contains
+** the database but rather the symbolic name of the database that
+** is assigned when the database is connected using [ATTACH].
+** For the main database file, the database name is "main".
+** For TEMP tables, the database name is "temp".
+**
+** On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
+** to *ppBlob. Otherwise an [error code] is returned and any value written
+** to *ppBlob should not be used by the caller.
+** This function sets the [database connection] error code and message
+** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()].
+**
+** If the row that a BLOB handle points to is modified by an
+** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
+** then the BLOB handle is marked as "expired".
+** This is true if any column of the row is changed, even a column
+** other than the one the BLOB handle is open on.
+** Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for
+** a expired BLOB handle fail with an return code of [SQLITE_ABORT].
+** Changes written into a BLOB prior to the BLOB expiring are not
+** rollback by the expiration of the BLOB. Such changes will eventually
+** commit if the transaction continues to completion.
+**
+** INVARIANTS:
+**
+** {H17813} A successful invocation of the [sqlite3_blob_open(D,B,T,C,R,F,P)]
+** interface shall open an [sqlite3_blob] object P on the BLOB
+** in column C of the table T in the database B on
+** the [database connection] D.
+**
+** {H17814} A successful invocation of [sqlite3_blob_open(D,...)] shall start
+** a new transaction on the [database connection] D if that
+** connection is not already in a transaction.
+**
+** {H17816} The [sqlite3_blob_open(D,B,T,C,R,F,P)] interface shall open
+** the BLOB for read and write access if and only if the F
+** parameter is non-zero.
+**
+** {H17819} The [sqlite3_blob_open()] interface shall return [SQLITE_OK] on
+** success and an appropriate [error code] on failure.
+**
+** {H17821} If an error occurs during evaluation of [sqlite3_blob_open(D,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error.
+**
+** {H17824} If any column in the row that a [sqlite3_blob] has open is
+** changed by a separate [UPDATE] or [DELETE] statement or by
+** an [ON CONFLICT] side effect, then the [sqlite3_blob] shall
+** be marked as invalid.
+*/
+int sqlite3_blob_open(
+ sqlite3*,
+ const char *zDb,
+ const char *zTable,
+ const char *zColumn,
+ sqlite3_int64 iRow,
+ int flags,
+ sqlite3_blob **ppBlob
+);
+
+/*
+** CAPI3REF: Close A BLOB Handle {H17830} <S30230>
+**
+** Closes an open [BLOB handle].
+**
+** Closing a BLOB shall cause the current transaction to commit
+** if there are no other BLOBs, no pending prepared statements, and the
+** database connection is in [autocommit mode].
+** If any writes were made to the BLOB, they might be held in cache
+** until the close operation if they will fit. {END}
+**
+** Closing the BLOB often forces the changes
+** out to disk and so if any I/O errors occur, they will likely occur
+** at the time when the BLOB is closed. {H17833} Any errors that occur during
+** closing are reported as a non-zero return value.
+**
+** The BLOB is closed unconditionally. Even if this routine returns
+** an error code, the BLOB is still closed.
+**
+** INVARIANTS:
+**
+** {H17833} The [sqlite3_blob_close(P)] interface closes an [sqlite3_blob]
+** object P previously opened using [sqlite3_blob_open()].
+**
+** {H17836} Closing an [sqlite3_blob] object using
+** [sqlite3_blob_close()] shall cause the current transaction to
+** commit if there are no other open [sqlite3_blob] objects
+** or [prepared statements] on the same [database connection] and
+** the database connection is in [autocommit mode].
+**
+** {H17839} The [sqlite3_blob_close(P)] interfaces shall close the
+** [sqlite3_blob] object P unconditionally, even if
+** [sqlite3_blob_close(P)] returns something other than [SQLITE_OK].
+*/
+int sqlite3_blob_close(sqlite3_blob *);
+
+/*
+** CAPI3REF: Return The Size Of An Open BLOB {H17840} <S30230>
+**
+** Returns the size in bytes of the BLOB accessible via the open
+** []BLOB handle] in its only argument.
+**
+** INVARIANTS:
+**
+** {H17843} The [sqlite3_blob_bytes(P)] interface returns the size
+** in bytes of the BLOB that the [sqlite3_blob] object P
+** refers to.
+*/
+int sqlite3_blob_bytes(sqlite3_blob *);
+
+/*
+** CAPI3REF: Read Data From A BLOB Incrementally {H17850} <S30230>
+**
+** This function is used to read data from an open [BLOB handle] into a
+** caller-supplied buffer. N bytes of data are copied into buffer Z
+** from the open BLOB, starting at offset iOffset.
+**
+** If offset iOffset is less than N bytes from the end of the BLOB,
+** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is
+** less than zero, [SQLITE_ERROR] is returned and no data is read.
+**
+** An attempt to read from an expired [BLOB handle] fails with an
+** error code of [SQLITE_ABORT].
+**
+** On success, SQLITE_OK is returned.
+** Otherwise, an [error code] or an [extended error code] is returned.
+**
+** INVARIANTS:
+**
+** {H17853} A successful invocation of [sqlite3_blob_read(P,Z,N,X)]
+** shall reads N bytes of data out of the BLOB referenced by
+** [BLOB handle] P beginning at offset X and store those bytes
+** into buffer Z.
+**
+** {H17856} In [sqlite3_blob_read(P,Z,N,X)] if the size of the BLOB
+** is less than N+X bytes, then the function shall leave the
+** Z buffer unchanged and return [SQLITE_ERROR].
+**
+** {H17859} In [sqlite3_blob_read(P,Z,N,X)] if X or N is less than zero
+** then the function shall leave the Z buffer unchanged
+** and return [SQLITE_ERROR].
+**
+** {H17862} The [sqlite3_blob_read(P,Z,N,X)] interface shall return [SQLITE_OK]
+** if N bytes are successfully read into buffer Z.
+**
+** {H17863} If the [BLOB handle] P is expired and X and N are within bounds
+** then [sqlite3_blob_read(P,Z,N,X)] shall leave the Z buffer
+** unchanged and return [SQLITE_ABORT].
+**
+** {H17865} If the requested read could not be completed,
+** the [sqlite3_blob_read(P,Z,N,X)] interface shall return an
+** appropriate [error code] or [extended error code].
+**
+** {H17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error, where D is the
+** [database connection] that was used to open the [BLOB handle] P.
+*/
+int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
+
+/*
+** CAPI3REF: Write Data Into A BLOB Incrementally {H17870} <S30230>
+**
+** This function is used to write data into an open [BLOB handle] from a
+** caller-supplied buffer. N bytes of data are copied from the buffer Z
+** into the open BLOB, starting at offset iOffset.
+**
+** If the [BLOB handle] passed as the first argument was not opened for
+** writing (the flags parameter to [sqlite3_blob_open()] was zero),
+** this function returns [SQLITE_READONLY].
+**
+** This function may only modify the contents of the BLOB; it is
+** not possible to increase the size of a BLOB using this API.
+** If offset iOffset is less than N bytes from the end of the BLOB,
+** [SQLITE_ERROR] is returned and no data is written. If N is
+** less than zero [SQLITE_ERROR] is returned and no data is written.
+**
+** An attempt to write to an expired [BLOB handle] fails with an
+** error code of [SQLITE_ABORT]. Writes to the BLOB that occurred
+** before the [BLOB handle] expired are not rolled back by the
+** expiration of the handle, though of course those changes might
+** have been overwritten by the statement that expired the BLOB handle
+** or by other independent statements.
+**
+** On success, SQLITE_OK is returned.
+** Otherwise, an [error code] or an [extended error code] is returned.
+**
+** INVARIANTS:
+**
+** {H17873} A successful invocation of [sqlite3_blob_write(P,Z,N,X)]
+** shall write N bytes of data from buffer Z into the BLOB
+** referenced by [BLOB handle] P beginning at offset X into
+** the BLOB.
+**
+** {H17874} In the absence of other overridding changes, the changes
+** written to a BLOB by [sqlite3_blob_write()] shall
+** remain in effect after the associated [BLOB handle] expires.
+**
+** {H17875} If the [BLOB handle] P was opened for reading only then
+** an invocation of [sqlite3_blob_write(P,Z,N,X)] shall leave
+** the referenced BLOB unchanged and return [SQLITE_READONLY].
+**
+** {H17876} If the size of the BLOB referenced by [BLOB handle] P is
+** less than N+X bytes then [sqlite3_blob_write(P,Z,N,X)] shall
+** leave the BLOB unchanged and return [SQLITE_ERROR].
+**
+** {H17877} If the [BLOB handle] P is expired and X and N are within bounds
+** then [sqlite3_blob_read(P,Z,N,X)] shall leave the BLOB
+** unchanged and return [SQLITE_ABORT].
+**
+** {H17879} If X or N are less than zero then [sqlite3_blob_write(P,Z,N,X)]
+** shall leave the BLOB referenced by [BLOB handle] P unchanged
+** and return [SQLITE_ERROR].
+**
+** {H17882} The [sqlite3_blob_write(P,Z,N,X)] interface shall return
+** [SQLITE_OK] if N bytes where successfully written into the BLOB.
+**
+** {H17885} If the requested write could not be completed,
+** the [sqlite3_blob_write(P,Z,N,X)] interface shall return an
+** appropriate [error code] or [extended error code].
+**
+** {H17888} If an error occurs during evaluation of [sqlite3_blob_write(D,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error.
+*/
+int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
+
+/* Begin preload-cache.patch for Chromium */
+/*
+** Preload the databases into the pager cache, up to the maximum size of the
+** pager cache.
+**
+** For a database to be loaded successfully, the pager must be active. That is,
+** there must be an open statement on that database. See sqlite3pager_loadall
+**
+** There might be many databases attached to the given connection. We iterate
+** them all and try to load them. If none are loadable successfully, we return
+** an error. Otherwise, we return OK.
+*/
+int sqlite3Preload(sqlite3 *db);
+/* End preload-cache.patch for Chromium */
+
+/*
+** CAPI3REF: Virtual File System Objects {H11200} <S20100>
+**
+** A virtual filesystem (VFS) is an [sqlite3_vfs] object
+** that SQLite uses to interact
+** with the underlying operating system. Most SQLite builds come with a
+** single default VFS that is appropriate for the host computer.
+** New VFSes can be registered and existing VFSes can be unregistered.
+** The following interfaces are provided.
+**
+** The sqlite3_vfs_find() interface returns a pointer to a VFS given its name.
+** Names are case sensitive.
+** Names are zero-terminated UTF-8 strings.
+** If there is no match, a NULL pointer is returned.
+** If zVfsName is NULL then the default VFS is returned.
+**
+** New VFSes are registered with sqlite3_vfs_register().
+** Each new VFS becomes the default VFS if the makeDflt flag is set.
+** The same VFS can be registered multiple times without injury.
+** To make an existing VFS into the default VFS, register it again
+** with the makeDflt flag set. If two different VFSes with the
+** same name are registered, the behavior is undefined. If a
+** VFS is registered with a name that is NULL or an empty string,
+** then the behavior is undefined.
+**
+** Unregister a VFS with the sqlite3_vfs_unregister() interface.
+** If the default VFS is unregistered, another VFS is chosen as
+** the default. The choice for the new VFS is arbitrary.
+**
+** INVARIANTS:
+**
+** {H11203} The [sqlite3_vfs_find(N)] interface returns a pointer to the
+** registered [sqlite3_vfs] object whose name exactly matches
+** the zero-terminated UTF-8 string N, or it returns NULL if
+** there is no match.
+**
+** {H11206} If the N parameter to [sqlite3_vfs_find(N)] is NULL then
+** the function returns a pointer to the default [sqlite3_vfs]
+** object if there is one, or NULL if there is no default
+** [sqlite3_vfs] object.
+**
+** {H11209} The [sqlite3_vfs_register(P,F)] interface registers the
+** well-formed [sqlite3_vfs] object P using the name given
+** by the zName field of the object.
+**
+** {H11212} Using the [sqlite3_vfs_register(P,F)] interface to register
+** the same [sqlite3_vfs] object multiple times is a harmless no-op.
+**
+** {H11215} The [sqlite3_vfs_register(P,F)] interface makes the [sqlite3_vfs]
+** object P the default [sqlite3_vfs] object if F is non-zero.
+**
+** {H11218} The [sqlite3_vfs_unregister(P)] interface unregisters the
+** [sqlite3_vfs] object P so that it is no longer returned by
+** subsequent calls to [sqlite3_vfs_find()].
+*/
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
+int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
+int sqlite3_vfs_unregister(sqlite3_vfs*);
+
+/*
+** CAPI3REF: Mutexes {H17000} <S20000>
+**
+** The SQLite core uses these routines for thread
+** synchronization. Though they are intended for internal
+** use by SQLite, code that links against SQLite is
+** permitted to use any of these routines.
+**
+** The SQLite source code contains multiple implementations
+** of these mutex routines. An appropriate implementation
+** is selected automatically at compile-time. The following
+** implementations are available in the SQLite core:
+**
+** <ul>
+** <li> SQLITE_MUTEX_OS2
+** <li> SQLITE_MUTEX_PTHREAD
+** <li> SQLITE_MUTEX_W32
+** <li> SQLITE_MUTEX_NOOP
+** </ul>
+**
+** The SQLITE_MUTEX_NOOP implementation is a set of routines
+** that does no real locking and is appropriate for use in
+** a single-threaded application. The SQLITE_MUTEX_OS2,
+** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
+** are appropriate for use on OS/2, Unix, and Windows.
+**
+** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
+** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
+** implementation is included with the library. In this case the
+** application must supply a custom mutex implementation using the
+** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
+** before calling sqlite3_initialize() or any other public sqlite3_
+** function that calls sqlite3_initialize().
+**
+** {H17011} The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. {H17012} If it returns NULL
+** that means that a mutex could not be allocated. {H17013} SQLite
+** will unwind its stack and return an error. {H17014} The argument
+** to sqlite3_mutex_alloc() is one of these integer constants:
+**
+** <ul>
+** <li> SQLITE_MUTEX_FAST
+** <li> SQLITE_MUTEX_RECURSIVE
+** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MEM
+** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_PRNG
+** <li> SQLITE_MUTEX_STATIC_LRU
+** <li> SQLITE_MUTEX_STATIC_LRU2
+** </ul>
+**
+** {H17015} The first two constants cause sqlite3_mutex_alloc() to create
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END}
+** The mutex implementation does not need to make a distinction
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
+** not want to. {H17016} But SQLite will only request a recursive mutex in
+** cases where it really needs one. {END} If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to SQLITE_MUTEX_FAST.
+**
+** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return
+** a pointer to a static preexisting mutex. {END} Four static mutexes are
+** used by the current version of SQLite. Future versions of SQLite
+** may add additional static mutexes. Static mutexes are for internal
+** use by SQLite only. Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
+** SQLITE_MUTEX_RECURSIVE.
+**
+** {H17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
+** returns a different mutex on every call. {H17034} But for the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+**
+** {H17019} The sqlite3_mutex_free() routine deallocates a previously
+** allocated dynamic mutex. {H17020} SQLite is careful to deallocate every
+** dynamic mutex that it allocates. {A17021} The dynamic mutexes must not be in
+** use when they are deallocated. {A17022} Attempting to deallocate a static
+** mutex results in undefined behavior. {H17023} SQLite never deallocates
+** a static mutex. {END}
+**
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. {H17024} If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. {H17025} The sqlite3_mutex_try() interface returns [SQLITE_OK]
+** upon successful entry. {H17026} Mutexes created using
+** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread.
+** {H17027} In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. {A17028} If the same thread tries to enter any other
+** kind of mutex more than once, the behavior is undefined.
+** {H17029} SQLite will never exhibit
+** such behavior in its own use of mutexes.
+**
+** Some systems (for example, Windows 95) do not support the operation
+** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
+** will always return SQLITE_BUSY. {H17030} The SQLite core only ever uses
+** sqlite3_mutex_try() as an optimization so this is acceptable behavior.
+**
+** {H17031} The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. {A17032} The behavior
+** is undefined if the mutex is not currently entered by the
+** calling thread or is not currently allocated. {H17033} SQLite will
+** never do either. {END}
+**
+** If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
+** sqlite3_mutex_leave() is a NULL pointer, then all three routines
+** behave as no-ops.
+**
+** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
+*/
+sqlite3_mutex *sqlite3_mutex_alloc(int);
+void sqlite3_mutex_free(sqlite3_mutex*);
+void sqlite3_mutex_enter(sqlite3_mutex*);
+int sqlite3_mutex_try(sqlite3_mutex*);
+void sqlite3_mutex_leave(sqlite3_mutex*);
+
+/*
+** CAPI3REF: Mutex Methods Object {H17120} <S20130>
+** EXPERIMENTAL
+**
+** An instance of this structure defines the low-level routines
+** used to allocate and use mutexes.
+**
+** Usually, the default mutex implementations provided by SQLite are
+** sufficient, however the user has the option of substituting a custom
+** implementation for specialized deployments or systems for which SQLite
+** does not provide a suitable implementation. In this case, the user
+** creates and populates an instance of this structure to pass
+** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.
+** Additionally, an instance of this structure can be used as an
+** output variable when querying the system for the current mutex
+** implementation, using the [SQLITE_CONFIG_GETMUTEX] option.
+**
+** The xMutexInit method defined by this structure is invoked as
+** part of system initialization by the sqlite3_initialize() function.
+** {H17001} The xMutexInit routine shall be called by SQLite once for each
+** effective call to [sqlite3_initialize()].
+**
+** The xMutexEnd method defined by this structure is invoked as
+** part of system shutdown by the sqlite3_shutdown() function. The
+** implementation of this method is expected to release all outstanding
+** resources obtained by the mutex methods implementation, especially
+** those obtained by the xMutexInit method. {H17003} The xMutexEnd()
+** interface shall be invoked once for each call to [sqlite3_shutdown()].
+**
+** The remaining seven methods defined by this structure (xMutexAlloc,
+** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and
+** xMutexNotheld) implement the following interfaces (respectively):
+**
+** <ul>
+** <li> [sqlite3_mutex_alloc()] </li>
+** <li> [sqlite3_mutex_free()] </li>
+** <li> [sqlite3_mutex_enter()] </li>
+** <li> [sqlite3_mutex_try()] </li>
+** <li> [sqlite3_mutex_leave()] </li>
+** <li> [sqlite3_mutex_held()] </li>
+** <li> [sqlite3_mutex_notheld()] </li>
+** </ul>
+**
+** The only difference is that the public sqlite3_XXX functions enumerated
+** above silently ignore any invocations that pass a NULL pointer instead
+** of a valid mutex handle. The implementations of the methods defined
+** by this structure are not required to handle this case, the results
+** of passing a NULL pointer instead of a valid mutex handle are undefined
+** (i.e. it is acceptable to provide an implementation that segfaults if
+** it is passed a NULL pointer).
+*/
+typedef struct sqlite3_mutex_methods sqlite3_mutex_methods;
+struct sqlite3_mutex_methods {
+ int (*xMutexInit)(void);
+ int (*xMutexEnd)(void);
+ sqlite3_mutex *(*xMutexAlloc)(int);
+ void (*xMutexFree)(sqlite3_mutex *);
+ void (*xMutexEnter)(sqlite3_mutex *);
+ int (*xMutexTry)(sqlite3_mutex *);
+ void (*xMutexLeave)(sqlite3_mutex *);
+ int (*xMutexHeld)(sqlite3_mutex *);
+ int (*xMutexNotheld)(sqlite3_mutex *);
+};
+
+/*
+** CAPI3REF: Mutex Verification Routines {H17080} <S20130> <S30800>
+**
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
+** are intended for use inside assert() statements. {H17081} The SQLite core
+** never uses these routines except inside an assert() and applications
+** are advised to follow the lead of the core. {H17082} The core only
+** provides implementations for these routines when it is compiled
+** with the SQLITE_DEBUG flag. {A17087} External mutex implementations
+** are only required to provide these routines if SQLITE_DEBUG is
+** defined and if NDEBUG is not defined.
+**
+** {H17083} These routines should return true if the mutex in their argument
+** is held or not held, respectively, by the calling thread.
+**
+** {X17084} The implementation is not required to provided versions of these
+** routines that actually work. If the implementation does not provide working
+** versions of these routines, it should at least provide stubs that always
+** return true so that one does not get spurious assertion failures.
+**
+** {H17085} If the argument to sqlite3_mutex_held() is a NULL pointer then
+** the routine should return 1. {END} This seems counter-intuitive since
+** clearly the mutex cannot be held if it does not exist. But the
+** the reason the mutex does not exist is because the build is not
+** using mutexes. And we do not want the assert() containing the
+** call to sqlite3_mutex_held() to fail, so a non-zero return is
+** the appropriate thing to do. {H17086} The sqlite3_mutex_notheld()
+** interface should also return 1 when given a NULL pointer.
+*/
+int sqlite3_mutex_held(sqlite3_mutex*);
+int sqlite3_mutex_notheld(sqlite3_mutex*);
+
+/*
+** CAPI3REF: Mutex Types {H17001} <H17000>
+**
+** The [sqlite3_mutex_alloc()] interface takes a single argument
+** which is one of these integer constants.
+**
+** The set of static mutexes may change from one SQLite release to the
+** next. Applications that override the built-in mutex logic must be
+** prepared to accommodate additional static mutexes.
+*/
+#define SQLITE_MUTEX_FAST 0
+#define SQLITE_MUTEX_RECURSIVE 1
+#define SQLITE_MUTEX_STATIC_MASTER 2
+#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
+#define SQLITE_MUTEX_STATIC_MEM2 4 /* sqlite3_release_memory() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
+#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */
+
+/*
+** CAPI3REF: Low-Level Control Of Database Files {H11300} <S30800>
+**
+** {H11301} The [sqlite3_file_control()] interface makes a direct call to the
+** xFileControl method for the [sqlite3_io_methods] object associated
+** with a particular database identified by the second argument. {H11302} The
+** name of the database is the name assigned to the database by the
+** <a href="lang_attach.html">ATTACH</a> SQL command that opened the
+** database. {H11303} To control the main database file, use the name "main"
+** or a NULL pointer. {H11304} The third and fourth parameters to this routine
+** are passed directly through to the second and third parameters of
+** the xFileControl method. {H11305} The return value of the xFileControl
+** method becomes the return value of this routine.
+**
+** {H11306} If the second parameter (zDbName) does not match the name of any
+** open database file, then SQLITE_ERROR is returned. {H11307} This error
+** code is not remembered and will not be recalled by [sqlite3_errcode()]
+** or [sqlite3_errmsg()]. {A11308} The underlying xFileControl method might
+** also return SQLITE_ERROR. {A11309} There is no way to distinguish between
+** an incorrect zDbName and an SQLITE_ERROR return from the underlying
+** xFileControl method. {END}
+**
+** See also: [SQLITE_FCNTL_LOCKSTATE]
+*/
+int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
+
+/*
+** CAPI3REF: Testing Interface {H11400} <S30800>
+**
+** The sqlite3_test_control() interface is used to read out internal
+** state of SQLite and to inject faults into SQLite for testing
+** purposes. The first parameter is an operation code that determines
+** the number, meaning, and operation of all subsequent parameters.
+**
+** This interface is not for use by applications. It exists solely
+** for verifying the correct operation of the SQLite library. Depending
+** on how the SQLite library is compiled, this interface might not exist.
+**
+** The details of the operation codes, their meanings, the parameters
+** they take, and what they do are all subject to change without notice.
+** Unlike most of the SQLite API, this function is not guaranteed to
+** operate consistently from one release to the next.
+*/
+int sqlite3_test_control(int op, ...);
+
+/*
+** CAPI3REF: Testing Interface Operation Codes {H11410} <H11400>
+**
+** These constants are the valid operation code parameters used
+** as the first argument to [sqlite3_test_control()].
+**
+** These parameters and their meanings are subject to change
+** without notice. These values are for testing purposes only.
+** Applications should not use any of these parameters or the
+** [sqlite3_test_control()] interface.
+*/
+#define SQLITE_TESTCTRL_PRNG_SAVE 5
+#define SQLITE_TESTCTRL_PRNG_RESTORE 6
+#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_BITVEC_TEST 8
+#define SQLITE_TESTCTRL_FAULT_INSTALL 9
+#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
+
+/*
+** CAPI3REF: SQLite Runtime Status {H17200} <S60200>
+** EXPERIMENTAL
+**
+** This interface is used to retrieve runtime status information
+** about the preformance of SQLite, and optionally to reset various
+** highwater marks. The first argument is an integer code for
+** the specific parameter to measure. Recognized integer codes
+** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].
+** The current value of the parameter is returned into *pCurrent.
+** The highest recorded value is returned in *pHighwater. If the
+** resetFlag is true, then the highest record value is reset after
+** *pHighwater is written. Some parameters do not record the highest
+** value. For those parameters
+** nothing is written into *pHighwater and the resetFlag is ignored.
+** Other parameters record only the highwater mark and not the current
+** value. For these latter parameters nothing is written into *pCurrent.
+**
+** This routine returns SQLITE_OK on success and a non-zero
+** [error code] on failure.
+**
+** This routine is threadsafe but is not atomic. This routine can
+** called while other threads are running the same or different SQLite
+** interfaces. However the values returned in *pCurrent and
+** *pHighwater reflect the status of SQLite at different points in time
+** and it is possible that another thread might change the parameter
+** in between the times when *pCurrent and *pHighwater are written.
+**
+** See also: [sqlite3_db_status()]
+*/
+int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+
+/*
+** CAPI3REF: Database Connection Status {H17201} <S60200>
+** EXPERIMENTAL
+**
+** This interface is used to retrieve runtime status information
+** about a single [database connection]. The first argument is the
+** database connection object to be interrogated. The second argument
+** is the parameter to interrogate. Currently, the only allowed value
+** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED].
+** Additional options will likely appear in future releases of SQLite.
+**
+** The current value of the request parameter is written into *pCur
+** and the highest instantaneous value is written into *pHiwtr. If
+** the resetFlg is true, then the highest instantaneous value is
+** reset back down to the current value.
+**
+** See also: [sqlite3_status()].
+*/
+int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
+
+/*
+** CAPI3REF: Status Parameters {H17250} <H17200>
+** EXPERIMENTAL
+**
+** These integer constants designate various run-time status parameters
+** that can be returned by [sqlite3_status()].
+**
+** <dl>
+** <dt>SQLITE_STATUS_MEMORY_USED</dt>
+** <dd>This parameter is the current amount of memory checked out
+** using [sqlite3_malloc()], either directly or indirectly. The
+** figure includes calls made to [sqlite3_malloc()] by the application
+** and internal memory usage by the SQLite library. Scratch memory
+** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
+** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
+** this parameter. The amount returned is the sum of the allocation
+** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>
+**
+** <dt>SQLITE_STATUS_MALLOC_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
+** internal equivalents). Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_USED</dt>
+** <dd>This parameter returns the number of pages used out of the
+** [pagecache memory allocator] that was configured using
+** [SQLITE_CONFIG_PAGECACHE]. The
+** value returned is in pages, not in bytes.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of page cache
+** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE]
+** buffer and where forced to overflow to [sqlite3_malloc()]. The
+** returned value includes allocations that overflowed because they
+** where too large (they were larger than the "sz" parameter to
+** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
+** no space was left in the page cache.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [pagecache memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_USED</dt>
+** <dd>This parameter returns the number of allocations used out of the
+** [scratch memory allocator] configured using
+** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
+** in bytes. Since a single thread may only have one scratch allocation
+** outstanding at time, this parameter also reports the number of threads
+** using scratch memory at the same time.</dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of scratch memory
+** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH]
+** buffer and where forced to overflow to [sqlite3_malloc()]. The values
+** returned include overflows because the requested allocation was too
+** larger (that is, because the requested allocation was larger than the
+** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer
+** slots were available.
+** </dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [scratch memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_PARSER_STACK</dt>
+** <dd>This parameter records the deepest parser stack. It is only
+** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>
+** </dl>
+**
+** New status parameters may be added from time to time.
+*/
+#define SQLITE_STATUS_MEMORY_USED 0
+#define SQLITE_STATUS_PAGECACHE_USED 1
+#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2
+#define SQLITE_STATUS_SCRATCH_USED 3
+#define SQLITE_STATUS_SCRATCH_OVERFLOW 4
+#define SQLITE_STATUS_MALLOC_SIZE 5
+#define SQLITE_STATUS_PARSER_STACK 6
+#define SQLITE_STATUS_PAGECACHE_SIZE 7
+#define SQLITE_STATUS_SCRATCH_SIZE 8
+
+/*
+** CAPI3REF: Status Parameters for database connections {H17275} <H17200>
+** EXPERIMENTAL
+**
+** Status verbs for [sqlite3_db_status()].
+**
+** <dl>
+** <dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
+** <dd>This parameter returns the number of lookaside memory slots currently
+** checked out.</dd>
+** </dl>
+*/
+#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
+
+/*
+** Undo the hack that converts floating point types to integer for
+** builds on processors without floating point support.
+*/
+#ifdef SQLITE_OMIT_FLOATING_POINT
+# undef double
+#endif
+
+#ifdef __cplusplus
+} /* End of the 'extern "C"' block */
+#endif
+#endif
diff --git a/third_party/sqlite/publish.sh b/third_party/sqlite/publish.sh
new file mode 100755
index 0000000..727a99e
--- /dev/null
+++ b/third_party/sqlite/publish.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite and package everything up
+# so that it is ready to move to the SQLite website.
+#
+
+# Set srcdir to the name of the directory that contains the publish.sh
+# script.
+#
+srcdir=`echo "$0" | sed 's%\(^.*\)/[^/][^/]*$%\1%'`
+
+# Get the makefile.
+#
+cp $srcdir/Makefile.linux-gcc ./Makefile
+chmod +x $srcdir/install-sh
+
+# Get the current version number - needed to help build filenames
+#
+VERS=`cat $srcdir/VERSION`
+VERSW=`sed 's/\./_/g' $srcdir/VERSION`
+echo "VERSIONS: $VERS $VERSW"
+
+# Start by building an sqlite shell for linux.
+#
+make clean
+make sqlite3.c
+CFLAGS="-Os -DSQLITE_ENABLE_FTS3=0 -DSQLITE_ENABLE_RTREE=0"
+CFLAGS="$CFLAGS -DSQLITE_THREADSAFE=0"
+echo '***** '"COMPILING sqlite3-$VERS.bin..."
+gcc $CFLAGS -Itsrc sqlite3.c tsrc/shell.c -o sqlite3 -ldl
+strip sqlite3
+mv sqlite3 sqlite3-$VERS.bin
+gzip sqlite3-$VERS.bin
+chmod 644 sqlite3-$VERS.bin.gz
+mv sqlite3-$VERS.bin.gz doc
+
+# Build a source archive useful for windows.
+#
+make target_source
+cd tsrc
+echo '***** BUILDING preprocessed source archives'
+rm fts[12]* icu*
+rm -f ../doc/sqlite-source-$VERSW.zip
+zip ../doc/sqlite-source-$VERSW.zip *
+cd ..
+cp tsrc/sqlite3.h tsrc/sqlite3ext.h .
+pwd
+zip doc/sqlite-amalgamation-$VERSW.zip sqlite3.c sqlite3.h sqlite3ext.h
+
+# Build the sqlite.so and tclsqlite.so shared libraries
+# under Linux
+#
+TCLDIR=/home/drh/tcltk/846/linux/846linux
+TCLSTUBLIB=$TCLDIR/libtclstub8.4g.a
+CFLAGS="-Os -DSQLITE_ENABLE_FTS3=3 -DSQLITE_ENABLE_RTREE=1"
+CFLAGS="$CFLAGS -DHAVE_LOCALTIME_R=1 -DHAVE_GMTIME_R=1"
+CFLAGS="$CFLAGS -DSQLITE_ENABLE_COLUMN_METADATA=1"
+echo '***** BUILDING shared libraries for linux'
+gcc $CFLAGS -shared tclsqlite3.c $TCLSTUBLIB -o tclsqlite3.so -lpthread
+strip tclsqlite3.so
+chmod 644 tclsqlite3.so
+mv tclsqlite3.so tclsqlite-$VERS.so
+gzip tclsqlite-$VERS.so
+mv tclsqlite-$VERS.so.gz doc
+gcc $CFLAGS -shared sqlite3.c -o sqlite3.so -lpthread
+strip sqlite3.so
+chmod 644 sqlite3.so
+mv sqlite3.so sqlite-$VERS.so
+gzip sqlite-$VERS.so
+mv sqlite-$VERS.so.gz doc
+
+
+# Build the tclsqlite3.dll and sqlite3.dll shared libraries.
+#
+. $srcdir/mkdll.sh
+echo '***** PACKAGING shared libraries for windows'
+echo zip doc/tclsqlite-$VERSW.zip tclsqlite3.dll
+zip doc/tclsqlite-$VERSW.zip tclsqlite3.dll
+echo zip doc/sqlitedll-$VERSW.zip sqlite3.dll sqlite3.def
+zip doc/sqlitedll-$VERSW.zip sqlite3.dll sqlite3.def
+
+# Build the sqlite.exe executable for windows.
+#
+OPTS='-DSTATIC_BUILD=1 -DNDEBUG=1 -DSQLITE_THREADSAFE=0'
+OPTS="$OPTS -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1"
+i386-mingw32msvc-gcc -Os $OPTS -Itsrc -I$TCLDIR sqlite3.c tsrc/shell.c \
+ -o sqlite3.exe
+zip doc/sqlite-$VERSW.zip sqlite3.exe
+
+# Construct a tarball of the source tree
+#
+echo '***** BUILDING source archive'
+ORIGIN=`pwd`
+cd $srcdir
+cd ..
+mv sqlite sqlite-$VERS
+EXCLUDE=`find sqlite-$VERS -print | egrep '(CVS|www/|art/|doc/|contrib/|_FOSSIL_|manifest)' | sed 's,^, --exclude ,'`
+echo "tar czf $ORIGIN/doc/sqlite-$VERS.tar.gz $EXCLUDE sqlite-$VERS"
+tar czf $ORIGIN/doc/sqlite-$VERS.tar.gz $EXCLUDE sqlite-$VERS
+mv sqlite-$VERS sqlite
+cd $ORIGIN
+
+#
+# Build RPMS (binary) and Source RPM
+#
+
+# Make sure we are properly setup to build RPMs
+#
+echo "%HOME %{expand:%%(cd; pwd)}" > $HOME/.rpmmacros
+echo "%_topdir %{HOME}/rpm" >> $HOME/.rpmmacros
+mkdir $HOME/rpm
+mkdir $HOME/rpm/BUILD
+mkdir $HOME/rpm/SOURCES
+mkdir $HOME/rpm/RPMS
+mkdir $HOME/rpm/SRPMS
+mkdir $HOME/rpm/SPECS
+
+# create the spec file from the template
+sed s/SQLITE_VERSION/$VERS/g $srcdir/spec.template > $HOME/rpm/SPECS/sqlite.spec
+
+# copy the source tarball to the rpm directory
+cp doc/sqlite-$VERS.tar.gz $HOME/rpm/SOURCES/.
+
+# build all the rpms
+rpm -ba $HOME/rpm/SPECS/sqlite.spec >& rpm-$vers.log
+
+# copy the RPMs into the build directory.
+mv $HOME/rpm/RPMS/i386/sqlite*-$vers*.rpm doc
+mv $HOME/rpm/SRPMS/sqlite-$vers*.rpm doc
+
+# Build the website
+#
+#cp $srcdir/../historical/* doc
+#make doc
+#cd doc
+#chmod 644 *.gz
diff --git a/third_party/sqlite/publish_osx.sh b/third_party/sqlite/publish_osx.sh
new file mode 100755
index 0000000..508b623
--- /dev/null
+++ b/third_party/sqlite/publish_osx.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# This script is used to compile SQLite and package everything up
+# so that it is ready to move to the SQLite website.
+#
+
+# Set srcdir to the name of the directory that contains the publish.sh
+# script.
+#
+srcdir=`echo "$0" | sed 's%\(^.*\)/[^/][^/]*$%\1%'`
+
+# Get the makefile.
+#
+cp $srcdir/Makefile.linux-gcc ./Makefile
+chmod +x $srcdir/install-sh
+
+# Get the current version number - needed to help build filenames
+#
+VERS=`cat $srcdir/VERSION`
+VERSW=`sed 's/\./_/g' $srcdir/VERSION`
+echo "VERSIONS: $VERS $VERSW"
+
+# Start by building an sqlite shell for linux.
+#
+make clean
+make sqlite3.c
+CFLAGS="-Os -DSQLITE_ENABLE_FTS3=1 -DSQLITE_THREADSAFE=0"
+NAME=sqlite3-$VERS-osx-x86.bin
+echo '***** '"COMPILING $NAME..."
+gcc $CFLAGS -Itsrc sqlite3.c tsrc/shell.c -o $NAME -ldl
+strip $NAME
+chmod 644 $NAME
+gzip $NAME
+mkdir -p doc
+mv $NAME.gz doc
diff --git a/third_party/sqlite/spec.template b/third_party/sqlite/spec.template
new file mode 100755
index 0000000..6cc7ab2
--- /dev/null
+++ b/third_party/sqlite/spec.template
@@ -0,0 +1,67 @@
+%define name sqlite
+%define version SQLITE_VERSION
+%define release 1
+
+Name: %{name}
+Summary: SQLite is a C library that implements an embeddable SQL database engine
+Version: %{version}
+Release: %{release}
+Source: %{name}-%{version}.tar.gz
+Group: System/Libraries
+URL: http://www.sqlite.org/
+License: Public Domain
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+SQLite is a software library that implements a self-contained, serverless,
+zero-configuration, transactional SQL database engine.
+Programs that link with the SQLite library can have SQL database access
+without running a separate RDBMS process. The distribution comes with a
+standalone command-line access program (sqlite) that can be used to
+administer an SQLite database and which serves as an example of how to
+use the SQLite library.
+
+%package -n %{name}-devel
+Summary: Header files and libraries for developing apps which will use sqlite
+Group: Development/C
+Requires: %{name} = %{version}-%{release}
+
+%description -n %{name}-devel
+The sqlite-devel package contains the header files and libraries needed
+to develop programs that use the SQLite database library.
+
+%prep
+%setup -q -n %{name}
+
+%build
+CFLAGS="%optflags -DNDEBUG=1" CXXFLAGS="%optflags -DNDEBUG=1" ./configure --prefix=%{_prefix}
+
+make
+make doc
+
+%install
+install -d $RPM_BUILD_ROOT/%{_prefix}
+install -d $RPM_BUILD_ROOT/%{_prefix}/bin
+install -d $RPM_BUILD_ROOT/%{_prefix}/include
+install -d $RPM_BUILD_ROOT/%{_prefix}/lib
+make install prefix=$RPM_BUILD_ROOT/%{_prefix}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%clean
+rm -fr $RPM_BUILD_ROOT
+
+%files
+%defattr(-, root, root)
+%{_libdir}/*.so*
+%{_bindir}/*
+
+%files -n %{name}-devel
+%defattr(-, root, root)
+%{_libdir}/pkgconfig/sqlite3.pc
+%{_libdir}/*.a
+%{_libdir}/*.la
+%{_includedir}/*
+%doc doc/*
diff --git a/third_party/sqlite/sqlite.pc.in b/third_party/sqlite/sqlite.pc.in
new file mode 100755
index 0000000..50b6c38
--- /dev/null
+++ b/third_party/sqlite/sqlite.pc.in
@@ -0,0 +1,13 @@
+# Package Information for pkg-config
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: SQLite
+Description: SQL database engine
+Version: @VERSION@
+Libs: -L${libdir} -lsqlite
+Libs.private: @LIBS@
+Cflags: -I${includedir}
diff --git a/third_party/sqlite/sqlite.vcproj b/third_party/sqlite/sqlite.vcproj
index cc3a14b..8cb3551 100644
--- a/third_party/sqlite/sqlite.vcproj
+++ b/third_party/sqlite/sqlite.vcproj
@@ -37,6 +37,7 @@
/>
<Tool
Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)..\third_party\sqlite\src&quot;;&quot;$(SolutionDir)..\third_party\sqlite\preprocessed&quot;"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -88,6 +89,7 @@
/>
<Tool
Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)..\third_party\sqlite\src&quot;;&quot;$(SolutionDir)..\third_party\sqlite\preprocessed&quot;"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -139,6 +141,7 @@
/>
<Tool
Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)..\third_party\sqlite\src&quot;;&quot;$(SolutionDir)..\third_party\sqlite\preprocessed&quot;"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -182,195 +185,247 @@
</References>
<Files>
<File
- RelativePath=".\alter.c"
+ RelativePath=".\src\alter.c"
>
</File>
<File
- RelativePath=".\analyze.c"
+ RelativePath=".\src\analyze.c"
>
</File>
<File
- RelativePath=".\attach.c"
+ RelativePath=".\src\attach.c"
>
</File>
<File
- RelativePath=".\auth.c"
+ RelativePath=".\src\auth.c"
>
</File>
<File
- RelativePath=".\btree.c"
+ RelativePath=".\src\bitvec.c"
>
</File>
<File
- RelativePath=".\btree.h"
+ RelativePath=".\src\btmutex.c"
>
</File>
<File
- RelativePath=".\btreeInt.h"
+ RelativePath=".\src\btree.c"
>
</File>
<File
- RelativePath=".\build.c"
+ RelativePath=".\src\btree.h"
>
</File>
<File
- RelativePath=".\callback.c"
+ RelativePath=".\src\btreeInt.h"
>
</File>
<File
- RelativePath=".\complete.c"
+ RelativePath=".\src\build.c"
>
</File>
<File
- RelativePath=".\date.c"
+ RelativePath=".\src\callback.c"
>
</File>
<File
- RelativePath=".\delete.c"
+ RelativePath=".\src\complete.c"
>
</File>
<File
- RelativePath=".\expr.c"
+ RelativePath=".\src\date.c"
>
</File>
<File
- RelativePath=".\fts2.c"
+ RelativePath=".\src\delete.c"
>
</File>
<File
- RelativePath=".\fts2.h"
+ RelativePath=".\src\expr.c"
>
</File>
<File
- RelativePath=".\fts2_hash.c"
+ RelativePath=".\src\fault.c"
>
</File>
<File
- RelativePath=".\fts2_hash.h"
+ RelativePath=".\ext\fts2\fts2.c"
>
</File>
<File
- RelativePath=".\fts2_icu.c"
+ RelativePath=".\ext\fts2\fts2.h"
>
</File>
<File
- RelativePath=".\fts2_porter.c"
+ RelativePath=".\ext\fts2\fts2_hash.c"
>
</File>
<File
- RelativePath=".\fts2_tokenizer.c"
+ RelativePath=".\ext\fts2\fts2_hash.h"
>
</File>
<File
- RelativePath=".\fts2_tokenizer.h"
+ RelativePath=".\ext\fts2\fts2_icu.c"
>
</File>
<File
- RelativePath=".\fts2_tokenizer1.c"
+ RelativePath=".\ext\fts2\fts2_porter.c"
>
</File>
<File
- RelativePath=".\func.c"
+ RelativePath=".\ext\fts2\fts2_tokenizer.c"
>
</File>
<File
- RelativePath=".\hash.c"
+ RelativePath=".\ext\fts2\fts2_tokenizer.h"
>
</File>
<File
- RelativePath=".\hash.h"
+ RelativePath=".\ext\fts2\fts2_tokenizer1.c"
>
</File>
<File
- RelativePath=".\icu.c"
+ RelativePath=".\src\func.c"
>
</File>
<File
- RelativePath=".\insert.c"
+ RelativePath=".\src\global.c"
>
</File>
<File
- RelativePath=".\keywordhash.h"
+ RelativePath=".\src\hash.c"
>
</File>
<File
- RelativePath=".\legacy.c"
+ RelativePath=".\src\hash.h"
>
</File>
<File
- RelativePath=".\limits.h"
+ RelativePath=".\ext\icu\icu.c"
>
</File>
<File
- RelativePath=".\loadext.c"
+ RelativePath=".\src\insert.c"
>
</File>
<File
- RelativePath=".\main.c"
+ RelativePath=".\preprocessed\keywordhash.h"
>
</File>
<File
- RelativePath=".\malloc.c"
+ RelativePath=".\src\legacy.c"
>
</File>
<File
- RelativePath=".\opcodes.c"
+ RelativePath=".\src\loadext.c"
>
</File>
<File
- RelativePath=".\opcodes.h"
+ RelativePath=".\src\main.c"
>
</File>
<File
- RelativePath=".\os.c"
+ RelativePath=".\src\malloc.c"
>
</File>
<File
- RelativePath=".\os.h"
+ RelativePath=".\src\mem1.c"
>
</File>
<File
- RelativePath=".\os_win.c"
+ RelativePath=".\src\mem2.c"
>
</File>
<File
- RelativePath=".\pager.c"
+ RelativePath=".\src\mem3.c"
>
</File>
<File
- RelativePath=".\pager.h"
+ RelativePath=".\src\mem4.c"
>
</File>
<File
- RelativePath=".\parse.c"
+ RelativePath=".\src\mem5.c"
>
</File>
<File
- RelativePath=".\parse.h"
+ RelativePath=".\src\mem6.c"
>
</File>
<File
- RelativePath=".\pragma.c"
+ RelativePath=".\src\mutex.c"
>
</File>
<File
- RelativePath=".\prepare.c"
+ RelativePath=".\src\mutex.h"
>
</File>
<File
- RelativePath=".\printf.c"
+ RelativePath=".\src\mutex_w32.c"
>
</File>
<File
- RelativePath=".\random.c"
+ RelativePath=".\preprocessed\opcodes.c"
>
</File>
<File
- RelativePath=".\select.c"
+ RelativePath=".\preprocessed\opcodes.h"
>
</File>
<File
- RelativePath=".\shell.c"
+ RelativePath=".\src\os.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\os.h"
+ >
+ </File>
+ <File
+ RelativePath=".\src\os_common.h"
+ >
+ </File>
+ <File
+ RelativePath=".\src\os_win.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\pager.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\pager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\preprocessed\parse.c"
+ >
+ </File>
+ <File
+ RelativePath=".\preprocessed\parse.h"
+ >
+ </File>
+ <File
+ RelativePath=".\src\pragma.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\prepare.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\printf.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\random.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\select.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\shell.c"
>
<FileConfiguration
Name="Debug|Win32"
@@ -390,87 +445,95 @@
</FileConfiguration>
</File>
<File
- RelativePath=".\shell_icu.c"
+ RelativePath=".\src\shell_icu.c"
+ >
+ </File>
+ <File
+ RelativePath=".\preprocessed\sqlite3.h"
+ >
+ </File>
+ <File
+ RelativePath=".\src\sqlite3ext.h"
>
</File>
<File
- RelativePath=".\sqlite3.h"
+ RelativePath=".\src\sqliteInt.h"
>
</File>
<File
- RelativePath=".\sqlite3ext.h"
+ RelativePath=".\src\sqliteLimit.h"
>
</File>
<File
- RelativePath=".\sqliteInt.h"
+ RelativePath=".\src\status.c"
>
</File>
<File
- RelativePath=".\sqliteLimit.h"
+ RelativePath=".\src\table.c"
>
</File>
<File
- RelativePath=".\table.c"
+ RelativePath=".\src\tokenize.c"
>
</File>
<File
- RelativePath=".\tokenize.c"
+ RelativePath=".\src\trigger.c"
>
</File>
<File
- RelativePath=".\trigger.c"
+ RelativePath=".\src\update.c"
>
</File>
<File
- RelativePath=".\update.c"
+ RelativePath=".\src\utf.c"
>
</File>
<File
- RelativePath=".\utf.c"
+ RelativePath=".\src\util.c"
>
</File>
<File
- RelativePath=".\util.c"
+ RelativePath=".\src\vacuum.c"
>
</File>
<File
- RelativePath=".\vacuum.c"
+ RelativePath=".\src\vdbe.c"
>
</File>
<File
- RelativePath=".\vdbe.c"
+ RelativePath=".\src\vdbe.h"
>
</File>
<File
- RelativePath=".\vdbe.h"
+ RelativePath=".\src\vdbeapi.c"
>
</File>
<File
- RelativePath=".\vdbeapi.c"
+ RelativePath=".\src\vdbeaux.c"
>
</File>
<File
- RelativePath=".\vdbeaux.c"
+ RelativePath=".\src\vdbeblob.c"
>
</File>
<File
- RelativePath=".\vdbefifo.c"
+ RelativePath=".\src\vdbefifo.c"
>
</File>
<File
- RelativePath=".\vdbeInt.h"
+ RelativePath=".\src\vdbeInt.h"
>
</File>
<File
- RelativePath=".\vdbemem.c"
+ RelativePath=".\src\vdbemem.c"
>
</File>
<File
- RelativePath=".\vtab.c"
+ RelativePath=".\src\vtab.c"
>
</File>
<File
- RelativePath=".\where.c"
+ RelativePath=".\src\where.c"
>
</File>
</Files>
diff --git a/third_party/sqlite/sqlite3.1 b/third_party/sqlite/sqlite3.1
new file mode 100755
index 0000000..785995b
--- /dev/null
+++ b/third_party/sqlite/sqlite3.1
@@ -0,0 +1,229 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH SQLITE3 1 "Mon Apr 15 23:49:17 2002"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp <n> insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+.B sqlite3
+\- A command line interface for SQLite version 3
+
+.SH SYNOPSIS
+.B sqlite3
+.RI [ options ]
+.RI [ databasefile ]
+.RI [ SQL ]
+
+.SH SUMMARY
+.PP
+.B sqlite3
+is a terminal-based front-end to the SQLite library that can evaluate
+queries interactively and display the results in multiple formats.
+.B sqlite3
+can also be used within shell scripts and other applications to provide
+batch processing features.
+
+.SH DESCRIPTION
+To start a
+.B sqlite3
+interactive session, invoke the
+.B sqlite3
+command and optionally provide the name of a database file. If the
+database file does not exist, it will be created. If the database file
+does exist, it will be opened.
+
+For example, to create a new database file named "mydata.db", create
+a table named "memos" and insert a couple of records into that table:
+.sp
+$
+.B sqlite3 mydata.db
+.br
+SQLite version 3.1.3
+.br
+Enter ".help" for instructions
+.br
+sqlite>
+.B create table memos(text, priority INTEGER);
+.br
+sqlite>
+.B insert into memos values('deliver project description', 10);
+.br
+sqlite>
+.B insert into memos values('lunch with Christine', 100);
+.br
+sqlite>
+.B select * from memos;
+.br
+deliver project description|10
+.br
+lunch with Christine|100
+.br
+sqlite>
+.sp
+
+If no database name is supplied, the ATTACH sql command can be used
+to attach to existing or create new database files. ATTACH can also
+be used to attach to multiple databases within the same interactive
+session. This is useful for migrating data between databases,
+possibly changing the schema along the way.
+
+Optionally, a SQL statement or set of SQL statements can be supplied as
+a single argument. Multiple statements should be separated by
+semi-colons.
+
+For example:
+.sp
+$
+.B sqlite3 -line mydata.db 'select * from memos where priority > 20;'
+.br
+ text = lunch with Christine
+.br
+priority = 100
+.br
+.sp
+
+.SS SQLITE META-COMMANDS
+.PP
+The interactive interpreter offers a set of meta-commands that can be
+used to control the output format, examine the currently attached
+database files, or perform administrative operations upon the
+attached databases (such as rebuilding indices). Meta-commands are
+always prefixed with a dot (.).
+
+A list of available meta-commands can be viewed at any time by issuing
+the '.help' command. For example:
+.sp
+sqlite>
+.B .help
+.nf
+.cc |
+.databases List names and files of attached databases
+.dump ?TABLE? ... Dump the database in an SQL text format
+.echo ON|OFF Turn command echo on or off
+.exit Exit this program
+.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
+.header(s) ON|OFF Turn display of headers on or off
+.help Show this message
+.import FILE TABLE Import data from FILE into TABLE
+.indices TABLE Show names of all indices on TABLE
+.mode MODE ?TABLE? Set output mode where MODE is one of:
+ csv Comma-separated values
+ column Left-aligned columns. (See .width)
+ html HTML <table> code
+ insert SQL insert statements for TABLE
+ line One value per line
+ list Values delimited by .separator string
+ tabs Tab-separated values
+ tcl TCL list elements
+.nullvalue STRING Print STRING in place of NULL values
+.output FILENAME Send output to FILENAME
+.output stdout Send output to the screen
+.prompt MAIN CONTINUE Replace the standard prompts
+.quit Exit this program
+.read FILENAME Execute SQL in FILENAME
+.schema ?TABLE? Show the CREATE statements
+.separator STRING Change separator used by output mode and .import
+.show Show the current values for various settings
+.tables ?PATTERN? List names of tables matching a LIKE pattern
+.timeout MS Try opening locked tables for MS milliseconds
+.width NUM NUM ... Set column widths for "column" mode
+sqlite>
+|cc .
+.sp
+.fi
+
+.SH OPTIONS
+.B sqlite3
+has the following options:
+.TP
+.BI \-init\ file
+Read and execute commands from
+.I file
+, which can contain a mix of SQL statements and meta-commands.
+.TP
+.B \-echo
+Print commands before execution.
+.TP
+.B \-[no]header
+Turn headers on or off.
+.TP
+.B \-column
+Query results will be displayed in a table like form, using
+whitespace characters to separate the columns and align the
+output.
+.TP
+.B \-html
+Query results will be output as simple HTML tables.
+.TP
+.B \-line
+Query results will be displayed with one value per line, rows
+separated by a blank line. Designed to be easily parsed by
+scripts or other programs
+.TP
+.B \-list
+Query results will be displayed with the separator (|, by default)
+character between each field value. The default.
+.TP
+.BI \-separator\ separator
+Set output field separator. Default is '|'.
+.TP
+.BI \-nullvalue\ string
+Set string used to represent NULL values. Default is ''
+(empty string).
+.TP
+.B \-version
+Show SQLite version.
+.TP
+.B \-help
+Show help on options and exit.
+
+
+.SH INIT FILE
+.B sqlite3
+reads an initialization file to set the configuration of the
+interactive environment. Throughout initialization, any previously
+specified setting can be overridden. The sequence of initialization is
+as follows:
+
+o The default configuration is established as follows:
+
+.sp
+.nf
+.cc |
+mode = LIST
+separator = "|"
+main prompt = "sqlite> "
+continue prompt = " ...> "
+|cc .
+.sp
+.fi
+
+o If the file
+.B ~/.sqliterc
+exists, it is processed first.
+can be found in the user's home directory, it is
+read and processed. It should generally only contain meta-commands.
+
+o If the -init option is present, the specified file is processed.
+
+o All other command line options are processed.
+
+.SH SEE ALSO
+http://www.sqlite.org/
+.br
+The sqlite-doc package
+.SH AUTHOR
+This manual page was originally written by Andreas Rottmann
+<rotty@debian.org>, for the Debian GNU/Linux system (but may be used
+by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com>.
diff --git a/third_party/sqlite/sqlite3.def b/third_party/sqlite/sqlite3.def
new file mode 100755
index 0000000..8a826ca
--- /dev/null
+++ b/third_party/sqlite/sqlite3.def
@@ -0,0 +1,150 @@
+EXPORTS
+sqlite3_aggregate_context
+sqlite3_aggregate_count
+sqlite3_auto_extension
+sqlite3_bind_blob
+sqlite3_bind_double
+sqlite3_bind_int
+sqlite3_bind_int64
+sqlite3_bind_null
+sqlite3_bind_parameter_count
+sqlite3_bind_parameter_index
+sqlite3_bind_parameter_name
+sqlite3_bind_text
+sqlite3_bind_text16
+sqlite3_bind_value
+sqlite3_bind_zeroblob
+sqlite3_blob_bytes
+sqlite3_blob_close
+sqlite3_blob_open
+sqlite3_blob_read
+sqlite3_blob_write
+sqlite3_busy_handler
+sqlite3_busy_timeout
+sqlite3_changes
+sqlite3_clear_bindings
+sqlite3_close
+sqlite3_collation_needed
+sqlite3_collation_needed16
+sqlite3_column_blob
+sqlite3_column_bytes
+sqlite3_column_bytes16
+sqlite3_column_count
+sqlite3_column_decltype
+sqlite3_column_decltype16
+sqlite3_column_double
+sqlite3_column_int
+sqlite3_column_int64
+sqlite3_column_name
+sqlite3_column_name16
+sqlite3_column_text
+sqlite3_column_text16
+sqlite3_column_type
+sqlite3_column_value
+sqlite3_commit_hook
+sqlite3_complete
+sqlite3_complete16
+sqlite3_create_collation
+sqlite3_create_collation16
+sqlite3_create_collation_v2
+sqlite3_create_function
+sqlite3_create_function16
+sqlite3_create_module
+sqlite3_create_module_v2
+sqlite3_data_count
+sqlite3_db_handle
+sqlite3_declare_vtab
+sqlite3_enable_load_extension
+sqlite3_enable_shared_cache
+sqlite3_errcode
+sqlite3_errmsg
+sqlite3_errmsg16
+sqlite3_exec
+sqlite3_expired
+sqlite3_extended_result_codes
+sqlite3_file_control
+sqlite3_finalize
+sqlite3_free
+sqlite3_free_table
+sqlite3_get_autocommit
+sqlite3_get_auxdata
+sqlite3_get_table
+sqlite3_global_recover
+sqlite3_interrupt
+sqlite3_last_insert_rowid
+sqlite3_libversion
+sqlite3_libversion_number
+sqlite3_load_extension
+sqlite3_malloc
+sqlite3_memory_alarm
+sqlite3_memory_highwater
+sqlite3_memory_used
+sqlite3_mprintf
+sqlite3_mutex_alloc
+sqlite3_mutex_enter
+sqlite3_mutex_free
+sqlite3_mutex_leave
+sqlite3_mutex_try
+sqlite3_open
+sqlite3_open16
+sqlite3_open_v2
+sqlite3_overload_function
+sqlite3_prepare
+sqlite3_prepare16
+sqlite3_prepare16_v2
+sqlite3_prepare_v2
+sqlite3_profile
+sqlite3_progress_handler
+sqlite3_randomness
+sqlite3_realloc
+sqlite3_release_memory
+sqlite3_reset
+sqlite3_reset_auto_extension
+sqlite3_result_blob
+sqlite3_result_double
+sqlite3_result_error
+sqlite3_result_error16
+sqlite3_result_error_code
+sqlite3_result_error_nomem
+sqlite3_result_error_toobig
+sqlite3_result_int
+sqlite3_result_int64
+sqlite3_result_null
+sqlite3_result_text
+sqlite3_result_text16
+sqlite3_result_text16be
+sqlite3_result_text16le
+sqlite3_result_value
+sqlite3_result_zeroblob
+sqlite3_rollback_hook
+sqlite3_set_authorizer
+sqlite3_set_auxdata
+sqlite3_sleep
+sqlite3_snprintf
+sqlite3_soft_heap_limit
+sqlite3_sql
+sqlite3_step
+sqlite3_test_control
+sqlite3_thread_cleanup
+sqlite3_threadsafe
+sqlite3_total_changes
+sqlite3_trace
+sqlite3_transfer_bindings
+sqlite3_update_hook
+sqlite3_user_data
+sqlite3_value_blob
+sqlite3_value_bytes
+sqlite3_value_bytes16
+sqlite3_value_double
+sqlite3_value_int
+sqlite3_value_int64
+sqlite3_value_numeric_type
+sqlite3_value_text
+sqlite3_value_text16
+sqlite3_value_text16be
+sqlite3_value_text16le
+sqlite3_value_type
+sqlite3_vfs_find
+sqlite3_vfs_register
+sqlite3_vfs_unregister
+sqlite3_vmprintf
diff --git a/third_party/sqlite/sqlite3.h b/third_party/sqlite/sqlite3.h
deleted file mode 100644
index ac41cd7..0000000
--- a/third_party/sqlite/sqlite3.h
+++ /dev/null
@@ -1,2722 +0,0 @@
-/*
-** 2001 September 15
-**
-** 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 header file defines the interface that the SQLite library
-** presents to client programs. If a C-function, structure, datatype,
-** or constant definition does not appear in this file, then it is
-** not a published API of SQLite, is subject to change without
-** notice, and should not be referenced by programs that use SQLite.
-**
-** Some of the definitions that are in this file are marked as
-** "experimental". Experimental interfaces are normally new
-** features recently added to SQLite. We do not anticipate changes
-** to experimental interfaces but reserve to make minor changes if
-** experience from use "in the wild" suggest such changes are prudent.
-**
-** The official C-language API documentation for SQLite is derived
-** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
-**
-** The name of this file under configuration management is "sqlite.h.in".
-** The makefile makes some minor changes to this file (such as inserting
-** the version number) and changes its name to "sqlite3.h" as
-** part of the build process.
-**
-** @(#) $Id: sqlite.h.in,v 1.219 2007/08/08 12:11:21 drh Exp $
-*/
-#ifndef _SQLITE3_H_
-#define _SQLITE3_H_
-#include <stdarg.h> /* Needed for the definition of va_list */
-
-/*
-** Make sure we can call this stuff from C++.
-*/
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
-** Add the ability to override 'extern'
-*/
-#ifndef SQLITE_EXTERN
-# define SQLITE_EXTERN extern
-#endif
-
-/*
-** Make sure these symbols where not defined by some previous header
-** file.
-*/
-#ifdef SQLITE_VERSION
-# undef SQLITE_VERSION
-#endif
-#ifdef SQLITE_VERSION_NUMBER
-# undef SQLITE_VERSION_NUMBER
-#endif
-
-/*
-** CAPI3REF: Compile-Time Library Version Numbers
-**
-** The version of the SQLite library is contained in the sqlite3.h
-** header file in a #define named SQLITE_VERSION. The SQLITE_VERSION
-** macro resolves to a string constant.
-**
-** The format of the version string is "X.Y.Z", where
-** X is the major version number, Y is the minor version number and Z
-** is the release number. The X.Y.Z might be followed by "alpha" or "beta".
-** For example "3.1.1beta".
-**
-** The X value is always 3 in SQLite. The X value only changes when
-** backwards compatibility is broken and we intend to never break
-** backwards compatibility. The Y value only changes when
-** there are major feature enhancements that are forwards compatible
-** but not backwards compatible. The Z value is incremented with
-** each release but resets back to 0 when Y is incremented.
-**
-** The SQLITE_VERSION_NUMBER is an integer with the value
-** (X*1000000 + Y*1000 + Z). For example, for version "3.1.1beta",
-** SQLITE_VERSION_NUMBER is set to 3001001. To detect if they are using
-** version 3.1.1 or greater at compile time, programs may use the test
-** (SQLITE_VERSION_NUMBER>=3001001).
-**
-** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()].
-*/
-#define SQLITE_VERSION "3.4.2"
-#define SQLITE_VERSION_NUMBER 3004002
-
-/*
-** CAPI3REF: Run-Time Library Version Numbers
-**
-** These routines return values equivalent to the header constants
-** [SQLITE_VERSION] and [SQLITE_VERSION_NUMBER]. The values returned
-** by this routines should only be different from the header values
-** if you compile your program using an sqlite3.h header from a
-** different version of SQLite that the version of the library you
-** link against.
-**
-** The sqlite3_version[] string constant contains the text of the
-** [SQLITE_VERSION] string. The sqlite3_libversion() function returns
-** a poiner to the sqlite3_version[] string constant. The function
-** is provided for DLL users who can only access functions and not
-** constants within the DLL.
-*/
-SQLITE_EXTERN const char sqlite3_version[];
-const char *sqlite3_libversion(void);
-int sqlite3_libversion_number(void);
-
-/*
-** CAPI3REF: Database Connection Handle
-**
-** Each open SQLite database is represented by pointer to an instance of the
-** opaque structure named "sqlite3". It is useful to think of an sqlite3
-** pointer as an object. The [sqlite3_open] interface is its constructor
-** and [sqlite3_close] is its destructor. There are many other interfaces
-** (such as [sqlite3_prepare_v2], [sqlite3_create_function], and
-** [sqlite3_busy_timeout] to name but three) that are methods on this
-** object.
-*/
-typedef struct sqlite3 sqlite3;
-
-
-/*
-** CAPI3REF: 64-Bit Integer Types
-**
-** Some compilers do not support the "long long" datatype. So we have
-** to do compiler-specific typedefs for 64-bit signed and unsigned integers.
-**
-** Many SQLite interface functions require a 64-bit integer arguments.
-** Those interfaces are declared using this typedef.
-*/
-#ifdef SQLITE_INT64_TYPE
- typedef SQLITE_INT64_TYPE sqlite_int64;
- typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
-#elif defined(_MSC_VER) || defined(__BORLANDC__)
- typedef __int64 sqlite_int64;
- typedef unsigned __int64 sqlite_uint64;
-#else
- typedef long long int sqlite_int64;
- typedef unsigned long long int sqlite_uint64;
-#endif
-
-/*
-** If compiling for a processor that lacks floating point support,
-** substitute integer for floating-point
-*/
-#ifdef SQLITE_OMIT_FLOATING_POINT
-# define double sqlite_int64
-#endif
-
-/*
-** CAPI3REF: Closing A Database Connection
-**
-** Call this function with a pointer to a structure that was previously
-** returned from [sqlite3_open()] and the corresponding database will by
-** closed.
-**
-** All SQL statements prepared using [sqlite3_prepare_v2()] or
-** [sqlite3_prepare16_v2()] must be destroyed using [sqlite3_finalize()]
-** before this routine is called. Otherwise, SQLITE_BUSY is returned and the
-** database connection remains open.
-*/
-int sqlite3_close(sqlite3 *);
-
-/*
-** The type for a callback function.
-** This is legacy and deprecated. It is included for historical
-** compatibility and is not documented.
-*/
-typedef int (*sqlite3_callback)(void*,int,char**, char**);
-
-/*
-** CAPI3REF: One-Step Query Execution Interface
-**
-** This interface is used to do a one-time evaluatation of zero
-** or more SQL statements. UTF-8 text of the SQL statements to
-** be evaluted is passed in as the second parameter. The statements
-** are prepared one by one using [sqlite3_prepare()], evaluated
-** using [sqlite3_step()], then destroyed using [sqlite3_finalize()].
-**
-** If one or more of the SQL statements are queries, then
-** the callback function specified by the 3rd parameter is
-** invoked once for each row of the query result. This callback
-** should normally return 0. If the callback returns a non-zero
-** value then the query is aborted, all subsequent SQL statements
-** are skipped and the sqlite3_exec() function returns the SQLITE_ABORT.
-**
-** The 4th parameter to this interface is an arbitrary pointer that is
-** passed through to the callback function as its first parameter.
-**
-** The 2nd parameter to the callback function is the number of
-** columns in the query result. The 3rd parameter to the callback
-** is an array of strings holding the values for each column
-** as extracted using [sqlite3_column_text()].
-** The 4th parameter to the callback is an array of strings
-** obtained using [sqlite3_column_name()] and holding
-** the names of each column.
-**
-** The callback function may be NULL, even for queries. A NULL
-** callback is not an error. It just means that no callback
-** will be invoked.
-**
-** If an error occurs while parsing or evaluating the SQL (but
-** not while executing the callback) then an appropriate error
-** message is written into memory obtained from [sqlite3_malloc()] and
-** *errmsg is made to point to that message. The calling function
-** is responsible for freeing the memory that holds the error
-** message. Use [sqlite3_free()] for this. If errmsg==NULL,
-** then no error message is ever written.
-**
-** The return value is is SQLITE_OK if there are no errors and
-** some other [SQLITE_OK | return code] if there is an error.
-** The particular return value depends on the type of error.
-**
-*/
-int sqlite3_exec(
- sqlite3*, /* An open database */
- const char *sql, /* SQL to be evaluted */
- int (*callback)(void*,int,char**,char**), /* Callback function */
- void *, /* 1st argument to callback */
- char **errmsg /* Error msg written here */
-);
-
-/*
-** CAPI3REF: Result Codes
-** KEYWORDS: SQLITE_OK
-**
-** Many SQLite functions return an integer result code from the set shown
-** above in order to indicates success or failure.
-**
-** The result codes above are the only ones returned by SQLite in its
-** default configuration. However, the [sqlite3_extended_result_codes()]
-** API can be used to set a database connectoin to return more detailed
-** result codes.
-**
-** See also: [SQLITE_IOERR_READ | extended result codes]
-**
-*/
-#define SQLITE_OK 0 /* Successful result */
-/* beginning-of-error-codes */
-#define SQLITE_ERROR 1 /* SQL error or missing database */
-#define SQLITE_INTERNAL 2 /* NOT USED. Internal logic error in SQLite */
-#define SQLITE_PERM 3 /* Access permission denied */
-#define SQLITE_ABORT 4 /* Callback routine requested an abort */
-#define SQLITE_BUSY 5 /* The database file is locked */
-#define SQLITE_LOCKED 6 /* A table in the database is locked */
-#define SQLITE_NOMEM 7 /* A malloc() failed */
-#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
-#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
-#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
-#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
-#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */
-#define SQLITE_FULL 13 /* Insertion failed because database is full */
-#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
-#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */
-#define SQLITE_EMPTY 16 /* Database is empty */
-#define SQLITE_SCHEMA 17 /* The database schema changed */
-#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
-#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
-#define SQLITE_MISMATCH 20 /* Data type mismatch */
-#define SQLITE_MISUSE 21 /* Library used incorrectly */
-#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
-#define SQLITE_AUTH 23 /* Authorization denied */
-#define SQLITE_FORMAT 24 /* Auxiliary database format error */
-#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
-#define SQLITE_NOTADB 26 /* File opened that is not a database file */
-#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
-#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
-/* end-of-error-codes */
-
-/*
-** CAPI3REF: Extended Result Codes
-**
-** In its default configuration, SQLite API routines return one of 26 integer
-** result codes described at result-codes. However, experience has shown that
-** many of these result codes are too course-grained. They do not provide as
-** much information about problems as users might like. In an effort to
-** address this, newer versions of SQLite (version 3.3.8 and later) include
-** support for additional result codes that provide more detailed information
-** about errors. The extended result codes are enabled (or disabled) for
-** each database
-** connection using the [sqlite3_extended_result_codes()] API.
-**
-** Some of the available extended result codes are listed above.
-** We expect the number of extended result codes will be expand
-** over time. Software that uses extended result codes should expect
-** to see new result codes in future releases of SQLite.
-**
-** The symbolic name for an extended result code always contains a related
-** primary result code as a prefix. Primary result codes contain a single
-** "_" character. Extended result codes contain two or more "_" characters.
-** The numeric value of an extended result code can be converted to its
-** corresponding primary result code by masking off the lower 8 bytes.
-**
-** The SQLITE_OK result code will never be extended. It will always
-** be exactly zero.
-*/
-#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
-#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
-#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
-#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
-#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
-#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8))
-#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8))
-#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8))
-#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8))
-#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8))
-#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8))
-
-/*
-** CAPI3REF: Enable Or Disable Extended Result Codes
-**
-** This routine enables or disables the
-** [SQLITE_IOERR_READ | extended result codes] feature.
-** By default, SQLite API routines return one of only 26 integer
-** [SQLITE_OK | result codes]. When extended result codes
-** are enabled by this routine, the repetoire of result codes can be
-** much larger and can (hopefully) provide more detailed information
-** about the cause of an error.
-**
-** The second argument is a boolean value that turns extended result
-** codes on and off. Extended result codes are off by default for
-** backwards compatibility with older versions of SQLite.
-*/
-int sqlite3_extended_result_codes(sqlite3*, int onoff);
-
-/*
-** CAPI3REF: Last Insert Rowid
-**
-** Each entry in an SQLite table has a unique 64-bit signed integer key
-** called the "rowid". The rowid is always available as an undeclared
-** column named ROWID, OID, or _ROWID_. If the table has a column of
-** type INTEGER PRIMARY KEY then that column is another an alias for the
-** rowid.
-**
-** This routine returns the rowid of the most recent INSERT into
-** the database from the database connection given in the first
-** argument. If no inserts have ever occurred on this database
-** connection, zero is returned.
-**
-** If an INSERT occurs within a trigger, then the rowid of the
-** inserted row is returned by this routine as long as the trigger
-** is running. But once the trigger terminates, the value returned
-** by this routine reverts to the last value inserted before the
-** trigger fired.
-*/
-sqlite_int64 sqlite3_last_insert_rowid(sqlite3*);
-
-/*
-** CAPI3REF: Count The Number Of Rows Modified
-**
-** This function returns the number of database rows that were changed
-** (or inserted or deleted) by the most recent SQL statement. Only
-** changes that are directly specified by the INSERT, UPDATE, or
-** DELETE statement are counted. Auxiliary changes caused by
-** triggers are not counted. Use the [sqlite3_total_changes()] function
-** to find the total number of changes including changes caused by triggers.
-**
-** Within the body of a trigger, the sqlite3_changes() interface can be
-** called to find the number of
-** changes in the most recently completed INSERT, UPDATE, or DELETE
-** statement within the body of the trigger.
-**
-** All changes are counted, even if they were later undone by a
-** ROLLBACK or ABORT. Except, changes associated with creating and
-** dropping tables are not counted.
-**
-** If a callback invokes [sqlite3_exec()] or [sqlite3_step()] recursively,
-** then the changes in the inner, recursive call are counted together
-** with the changes in the outer call.
-**
-** SQLite implements the command "DELETE FROM table" without a WHERE clause
-** by dropping and recreating the table. (This is much faster than going
-** through and deleting individual elements from the table.) Because of
-** this optimization, the change count for "DELETE FROM table" will be
-** zero regardless of the number of elements that were originally in the
-** table. To get an accurate count of the number of rows deleted, use
-** "DELETE FROM table WHERE 1" instead.
-*/
-int sqlite3_changes(sqlite3*);
-
-/*
-** CAPI3REF: Total Number Of Rows Modified
-***
-** This function returns the number of database rows that have been
-** modified by INSERT, UPDATE or DELETE statements since the database handle
-** was opened. This includes UPDATE, INSERT and DELETE statements executed
-** as part of trigger programs. All changes are counted as soon as the
-** statement that makes them is completed (when the statement handle is
-** passed to [sqlite3_reset()] or [sqlite_finalise()]).
-**
-** See also the [sqlite3_change()] interface.
-**
-** SQLite implements the command "DELETE FROM table" without a WHERE clause
-** by dropping and recreating the table. (This is much faster than going
-** through and deleting individual elements form the table.) Because of
-** this optimization, the change count for "DELETE FROM table" will be
-** zero regardless of the number of elements that were originally in the
-** table. To get an accurate count of the number of rows deleted, use
-** "DELETE FROM table WHERE 1" instead.
-*/
-int sqlite3_total_changes(sqlite3*);
-
-/*
-** CAPI3REF: Interrupt A Long-Running Query
-**
-** This function causes any pending database operation to abort and
-** return at its earliest opportunity. This routine is typically
-** called in response to a user action such as pressing "Cancel"
-** or Ctrl-C where the user wants a long query operation to halt
-** immediately.
-**
-** It is safe to call this routine from a thread different from the
-** thread that is currently running the database operation.
-**
-** The SQL operation that is interrupted will return [SQLITE_INTERRUPT].
-** If an interrupted operation was an update that is inside an
-** explicit transaction, then the entire transaction will be rolled
-** back automatically.
-*/
-void sqlite3_interrupt(sqlite3*);
-
-/*
-** CAPI3REF: Determine If An SQL Statement Is Complete
-**
-** These functions return true if the given input string comprises
-** one or more complete SQL statements. For the sqlite3_complete() call,
-** the parameter must be a nul-terminated UTF-8 string. For
-** sqlite3_complete16(), a nul-terminated machine byte order UTF-16 string
-** is required.
-**
-** These routines are useful for command-line input to determine if the
-** currently entered text forms one or more complete SQL statements or
-** if additional input is needed before sending the statements into
-** SQLite for parsing. The algorithm is simple. If the
-** last token other than spaces and comments is a semicolon, then return
-** true. Actually, the algorithm is a little more complicated than that
-** in order to deal with triggers, but the basic idea is the same: the
-** statement is not complete unless it ends in a semicolon.
-*/
-int sqlite3_complete(const char *sql);
-int sqlite3_complete16(const void *sql);
-
-/*
-** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
-**
-** This routine identifies a callback function that might be invoked
-** whenever an attempt is made to open a database table
-** that another thread or process has locked.
-** If the busy callback is NULL, then [SQLITE_BUSY]
-** (or sometimes [SQLITE_IOERR_BLOCKED])
-** is returned immediately upon encountering the lock.
-** If the busy callback is not NULL, then the
-** callback will be invoked with two arguments. The
-** first argument to the handler is a copy of the void* pointer which
-** is the third argument to this routine. The second argument to
-** the handler is the number of times that the busy handler has
-** been invoked for this locking event. If the
-** busy callback returns 0, then no additional attempts are made to
-** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.
-** If the callback returns non-zero, then another attempt is made to open the
-** database for reading and the cycle repeats.
-**
-** The presence of a busy handler does not guarantee that
-** it will be invoked when there is lock contention.
-** If SQLite determines that invoking the busy handler could result in
-** a deadlock, it will return [SQLITE_BUSY] instead.
-** Consider a scenario where one process is holding a read lock that
-** it is trying to promote to a reserved lock and
-** a second process is holding a reserved lock that it is trying
-** to promote to an exclusive lock. The first process cannot proceed
-** because it is blocked by the second and the second process cannot
-** proceed because it is blocked by the first. If both processes
-** invoke the busy handlers, neither will make any progress. Therefore,
-** SQLite returns [SQLITE_BUSY] for the first process, hoping that this
-** will induce the first process to release its read lock and allow
-** the second process to proceed.
-**
-** The default busy callback is NULL.
-**
-** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] when
-** SQLite is in the middle of a large transaction where all the
-** changes will not fit into the in-memory cache. SQLite will
-** already hold a RESERVED lock on the database file, but it needs
-** to promote this lock to EXCLUSIVE so that it can spill cache
-** pages into the database file without harm to concurrent
-** readers. If it is unable to promote the lock, then the in-memory
-** cache will be left in an inconsistent state and so the error
-** code is promoted from the relatively benign [SQLITE_BUSY] to
-** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion
-** forces an automatic rollback of the changes. See the
-** <a href="http://www.sqlite.org/cvstrac/wiki?p=CorruptionFollowingBusyError">
-** CorruptionFollowingBusyError</a> wiki page for a discussion of why
-** this is important.
-**
-** Sqlite is re-entrant, so the busy handler may start a new query.
-** (It is not clear why anyone would every want to do this, but it
-** is allowed, in theory.) But the busy handler may not close the
-** database. Closing the database from a busy handler will delete
-** data structures out from under the executing query and will
-** probably result in a segmentation fault or other runtime error.
-**
-** There can only be a single busy handler defined for each database
-** connection. Setting a new busy handler clears any previous one.
-** Note that calling [sqlite3_busy_timeout()] will also set or clear
-** the busy handler.
-*/
-int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
-
-/*
-** CAPI3REF: Set A Busy Timeout
-**
-** This routine sets a busy handler that sleeps for a while when a
-** table is locked. The handler will sleep multiple times until
-** at least "ms" milliseconds of sleeping have been done. After
-** "ms" milliseconds of sleeping, the handler returns 0 which
-** causes [sqlite3_step()] to return [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
-**
-** Calling this routine with an argument less than or equal to zero
-** turns off all busy handlers.
-**
-** There can only be a single busy handler for a particular database
-** connection. If another busy handler was defined
-** (using [sqlite3_busy_handler()]) prior to calling
-** this routine, that other busy handler is cleared.
-*/
-int sqlite3_busy_timeout(sqlite3*, int ms);
-
-/*
-** CAPI3REF: Convenience Routines For Running Queries
-**
-** This next routine is a convenience wrapper around [sqlite3_exec()].
-** Instead of invoking a user-supplied callback for each row of the
-** result, this routine remembers each row of the result in memory
-** obtained from [sqlite3_malloc()], then returns all of the result after the
-** query has finished.
-**
-** As an example, suppose the query result where this table:
-**
-** <pre>
-** Name | Age
-** -----------------------
-** Alice | 43
-** Bob | 28
-** Cindy | 21
-** </pre>
-**
-** If the 3rd argument were &azResult then after the function returns
-** azResult will contain the following data:
-**
-** <pre>
-** azResult[0] = "Name";
-** azResult[1] = "Age";
-** azResult[2] = "Alice";
-** azResult[3] = "43";
-** azResult[4] = "Bob";
-** azResult[5] = "28";
-** azResult[6] = "Cindy";
-** azResult[7] = "21";
-** </pre>
-**
-** Notice that there is an extra row of data containing the column
-** headers. But the *nrow return value is still 3. *ncolumn is
-** set to 2. In general, the number of values inserted into azResult
-** will be ((*nrow) + 1)*(*ncolumn).
-**
-** After the calling function has finished using the result, it should
-** pass the result data pointer to sqlite3_free_table() in order to
-** release the memory that was malloc-ed. Because of the way the
-** [sqlite3_malloc()] happens, the calling function must not try to call
-** [sqlite3_free()] directly. Only [sqlite3_free_table()] is able to release
-** the memory properly and safely.
-**
-** The return value of this routine is the same as from [sqlite3_exec()].
-*/
-int sqlite3_get_table(
- sqlite3*, /* An open database */
- const char *sql, /* SQL to be executed */
- char ***resultp, /* Result written to a char *[] that this points to */
- int *nrow, /* Number of result rows written here */
- int *ncolumn, /* Number of result columns written here */
- char **errmsg /* Error msg written here */
-);
-void sqlite3_free_table(char **result);
-
-/*
-** CAPI3REF: Formatted String Printing Functions
-**
-** These routines are workalikes of the "printf()" family of functions
-** from the standard C library.
-**
-** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
-** results into memory obtained from [sqlite_malloc()].
-** The strings returned by these two routines should be
-** released by [sqlite3_free()]. Both routines return a
-** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
-** memory to hold the resulting string.
-**
-** In sqlite3_snprintf() routine is similar to "snprintf()" from
-** the standard C library. The result is written into the
-** buffer supplied as the second parameter whose size is given by
-** the first parameter. Note that the order of the
-** first two parameters is reversed from snprintf(). This is an
-** historical accident that cannot be fixed without breaking
-** backwards compatibility. Note also that sqlite3_snprintf()
-** returns a pointer to its buffer instead of the number of
-** characters actually written into the buffer. We admit that
-** the number of characters written would be a more useful return
-** value but we cannot change the implementation of sqlite3_snprintf()
-** now without breaking compatibility.
-**
-** As long as the buffer size is greater than zero, sqlite3_snprintf()
-** guarantees that the buffer is always zero-terminated. The first
-** parameter "n" is the total size of the buffer, including space for
-** the zero terminator. So the longest string that can be completely
-** written will be n-1 characters.
-**
-** These routines all implement some additional formatting
-** options that are useful for constructing SQL statements.
-** All of the usual printf formatting options apply. In addition, there
-** is are "%q" and "%Q" options.
-**
-** The %q option works like %s in that it substitutes a null-terminated
-** string from the argument list. But %q also doubles every '\'' character.
-** %q is designed for use inside a string literal. By doubling each '\''
-** character it escapes that character and allows it to be inserted into
-** the string.
-**
-** For example, so some string variable contains text as follows:
-**
-** <blockquote><pre>
-** char *zText = "It's a happy day!";
-** </pre></blockquote>
-**
-** One can use this text in an SQL statement as follows:
-**
-** <blockquote><pre>
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
-** sqlite3_exec(db, zSQL, 0, 0, 0);
-** sqlite3_free(zSQL);
-** </pre></blockquote>
-**
-** Because the %q format string is used, the '\'' character in zText
-** is escaped and the SQL generated is as follows:
-**
-** <blockquote><pre>
-** INSERT INTO table1 VALUES('It''s a happy day!')
-** </pre></blockquote>
-**
-** This is correct. Had we used %s instead of %q, the generated SQL
-** would have looked like this:
-**
-** <blockquote><pre>
-** INSERT INTO table1 VALUES('It's a happy day!');
-** </pre></blockquote>
-**
-** This second example is an SQL syntax error. As a general rule you
-** should always use %q instead of %s when inserting text into a string
-** literal.
-**
-** The %Q option works like %q except it also adds single quotes around
-** the outside of the total string. Or if the parameter in the argument
-** list is a NULL pointer, %Q substitutes the text "NULL" (without single
-** quotes) in place of the %Q option. So, for example, one could say:
-**
-** <blockquote><pre>
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
-** sqlite3_exec(db, zSQL, 0, 0, 0);
-** sqlite3_free(zSQL);
-** </pre></blockquote>
-**
-** The code above will render a correct SQL statement in the zSQL
-** variable even if the zText variable is a NULL pointer.
-*/
-char *sqlite3_mprintf(const char*,...);
-char *sqlite3_vmprintf(const char*, va_list);
-char *sqlite3_snprintf(int,char*,const char*, ...);
-
-/*
-** CAPI3REF: Memory Allocation Functions
-**
-** SQLite uses its own memory allocator. On some installations, this
-** memory allocator is identical to the standard malloc()/realloc()/free()
-** and can be used interchangable. On others, the implementations are
-** different. For maximum portability, it is best not to mix calls
-** to the standard malloc/realloc/free with the sqlite versions.
-*/
-void *sqlite3_malloc(int);
-void *sqlite3_realloc(void*, int);
-void sqlite3_free(void*);
-
-/*
-** CAPI3REF: Compile-Time Authorization Callbacks
-***
-** This routine registers a authorizer callback with the SQLite library.
-** The authorizer callback is invoked as SQL statements are being compiled
-** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
-** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. At various
-** points during the compilation process, as logic is being created
-** to perform various actions, the authorizer callback is invoked to
-** see if those actions are allowed. The authorizer callback should
-** return SQLITE_OK to allow the action, [SQLITE_IGNORE] to disallow the
-** specific action but allow the SQL statement to continue to be
-** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
-** rejected with an error.
-**
-** Depending on the action, the [SQLITE_IGNORE] and [SQLITE_DENY] return
-** codes might mean something different or they might mean the same
-** thing. If the action is, for example, to perform a delete opertion,
-** then [SQLITE_IGNORE] and [SQLITE_DENY] both cause the statement compilation
-** to fail with an error. But if the action is to read a specific column
-** from a specific table, then [SQLITE_DENY] will cause the entire
-** statement to fail but [SQLITE_IGNORE] will cause a NULL value to be
-** read instead of the actual column value.
-**
-** The first parameter to the authorizer callback is a copy of
-** the third parameter to the sqlite3_set_authorizer() interface.
-** The second parameter to the callback is an integer
-** [SQLITE_COPY | action code] that specifies the particular action
-** to be authorized. The available action codes are
-** [SQLITE_COPY | documented separately]. The third through sixth
-** parameters to the callback are strings that contain additional
-** details about the action to be authorized.
-**
-** An authorizer is used when preparing SQL statements from an untrusted
-** source, to ensure that the SQL statements do not try to access data
-** that they are not allowed to see, or that they do not try to
-** execute malicious statements that damage the database. For
-** example, an application may allow a user to enter arbitrary
-** SQL queries for evaluation by a database. But the application does
-** not want the user to be able to make arbitrary changes to the
-** database. An authorizer could then be put in place while the
-** user-entered SQL is being prepared that disallows everything
-** except SELECT statements.
-**
-** Only a single authorizer can be in place on a database connection
-** at a time. Each call to sqlite3_set_authorizer overrides the
-** previous call. A NULL authorizer means that no authorization
-** callback is invoked. The default authorizer is NULL.
-**
-** Note that the authorizer callback is invoked only during
-** [sqlite3_prepare()] or its variants. Authorization is not
-** performed during statement evaluation in [sqlite3_step()].
-*/
-int sqlite3_set_authorizer(
- sqlite3*,
- int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
- void *pUserData
-);
-
-/*
-** CAPI3REF: Authorizer Return Codes
-**
-** The [sqlite3_set_authorizer | authorizer callback function] must
-** return either [SQLITE_OK] or one of these two constants in order
-** to signal SQLite whether or not the action is permitted. See the
-** [sqlite3_set_authorizer | authorizer documentation] for additional
-** information.
-*/
-#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
-#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
-
-/*
-** CAPI3REF: Authorizer Action Codes
-**
-** The [sqlite3_set_authorizer()] interface registers a callback function
-** that is invoked to authorizer certain SQL statement actions. The
-** second parameter to the callback is an integer code that specifies
-** what action is being authorized. These are the integer action codes that
-** the authorizer callback may be passed.
-**
-** These action code values signify what kind of operation is to be
-** authorized. The 3rd and 4th parameters to the authorization callback
-** function will be parameters or NULL depending on which of these
-** codes is used as the second parameter. The 5th parameter to the
-** authorizer callback is the name of the database ("main", "temp",
-** etc.) if applicable. The 6th parameter to the authorizer callback
-** is the name of the inner-most trigger or view that is responsible for
-** the access attempt or NULL if this access attempt is directly from
-** top-level SQL code.
-*/
-/******************************************* 3rd ************ 4th ***********/
-#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
-#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
-#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
-#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
-#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
-#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
-#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
-#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
-#define SQLITE_DELETE 9 /* Table Name NULL */
-#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
-#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
-#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
-#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
-#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
-#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
-#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
-#define SQLITE_DROP_VIEW 17 /* View Name NULL */
-#define SQLITE_INSERT 18 /* Table Name NULL */
-#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
-#define SQLITE_READ 20 /* Table Name Column Name */
-#define SQLITE_SELECT 21 /* NULL NULL */
-#define SQLITE_TRANSACTION 22 /* NULL NULL */
-#define SQLITE_UPDATE 23 /* Table Name Column Name */
-#define SQLITE_ATTACH 24 /* Filename NULL */
-#define SQLITE_DETACH 25 /* Database Name NULL */
-#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */
-#define SQLITE_REINDEX 27 /* Index Name NULL */
-#define SQLITE_ANALYZE 28 /* Table Name NULL */
-#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */
-#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */
-#define SQLITE_FUNCTION 31 /* Function Name NULL */
-#define SQLITE_COPY 0 /* No longer used */
-
-/*
-** CAPI3REF: Tracing And Profiling Functions
-**
-** These routines register callback functions that can be used for
-** tracing and profiling the execution of SQL statements.
-** The callback function registered by sqlite3_trace() is invoked
-** at the first [sqlite3_step()] for the evaluation of an SQL statement.
-** The callback function registered by sqlite3_profile() is invoked
-** as each SQL statement finishes and includes
-** information on how long that statement ran.
-**
-** The sqlite3_profile() API is currently considered experimental and
-** is subject to change.
-*/
-void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
-void *sqlite3_profile(sqlite3*,
- void(*xProfile)(void*,const char*,sqlite_uint64), void*);
-
-/*
-** CAPI3REF: Query Progress Callbacks
-**
-** This routine configures a callback function - the progress callback - that
-** is invoked periodically during long running calls to [sqlite3_exec()],
-** [sqlite3_step()] and [sqlite3_get_table()]. An example use for this
-** interface is to keep a GUI updated during a large query.
-**
-** The progress callback is invoked once for every N virtual machine opcodes,
-** where N is the second argument to this function. The progress callback
-** itself is identified by the third argument to this function. The fourth
-** argument to this function is a void pointer passed to the progress callback
-** function each time it is invoked.
-**
-** If a call to [sqlite3_exec()], [sqlite3_step()], or [sqlite3_get_table()]
-** results in fewer than N opcodes being executed, then the progress
-** callback is never invoked.
-**
-** Only a single progress callback function may be registered for each
-** open database connection. Every call to sqlite3_progress_handler()
-** overwrites the results of the previous call.
-** To remove the progress callback altogether, pass NULL as the third
-** argument to this function.
-**
-** If the progress callback returns a result other than 0, then the current
-** query is immediately terminated and any database changes rolled back.
-** The containing [sqlite3_exec()], [sqlite3_step()], or
-** [sqlite3_get_table()] call returns SQLITE_INTERRUPT. This feature
-** can be used, for example, to implement the "Cancel" button on a
-** progress dialog box in a GUI.
-*/
-void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
-
-/*
-** CAPI3REF: Opening A New Database Connection
-**
-** Open the sqlite database file "filename". The "filename" is UTF-8
-** encoded for sqlite3_open() and UTF-16 encoded in the native byte order
-** for sqlite3_open16(). An [sqlite3*] handle is returned in *ppDb, even
-** if an error occurs. If the database is opened (or created) successfully,
-** then SQLITE_OK is returned. Otherwise an error code is returned. The
-** sqlite3_errmsg() or sqlite3_errmsg16() routines can be used to obtain
-** an English language description of the error.
-**
-** If the database file does not exist, then a new database will be created
-** as needed. The default encoding for the database will be UTF-8 if
-** sqlite3_open() is called and UTF-16 if sqlite3_open16 is used.
-**
-** Whether or not an error occurs when it is opened, resources associated
-** with the [sqlite3*] handle should be released by passing it to
-** sqlite3_close() when it is no longer required.
-**
-** Note to windows users: The encoding used for the filename argument
-** of sqlite3_open() must be UTF-8, not whatever codepage is currently
-** defined. Filenames containing international characters must be converted
-** to UTF-8 prior to passing them into sqlite3_open().
-*/
-int sqlite3_open(
- const char *filename, /* Database filename (UTF-8) */
- sqlite3 **ppDb /* OUT: SQLite db handle */
-);
-int sqlite3_open16(
- const void *filename, /* Database filename (UTF-16) */
- sqlite3 **ppDb /* OUT: SQLite db handle */
-);
-
-/*
-** CAPI3REF: Error Codes And Messages
-**
-** The sqlite3_errcode() interface returns the numeric
-** [SQLITE_OK | result code] or [SQLITE_IOERR_READ | extended result code]
-** for the most recent failed sqlite3_* API call associated
-** with [sqlite3] handle 'db'. If a prior API call failed but the
-** most recent API call succeeded, the return value from sqlite3_errcode()
-** is undefined.
-**
-** The sqlite3_errmsg() and sqlite3_errmsg16() return English-langauge
-** text that describes the error, as either UTF8 or UTF16 respectively.
-** Memory to hold the error message string is managed internally. The
-** string may be overwritten or deallocated by subsequent calls to SQLite
-** interface functions.
-**
-** Calls to many sqlite3_* functions set the error code and string returned
-** by [sqlite3_errcode()], [sqlite3_errmsg()], and [sqlite3_errmsg16()]
-** (overwriting the previous values). Note that calls to [sqlite3_errcode()],
-** [sqlite3_errmsg()], and [sqlite3_errmsg16()] themselves do not affect the
-** results of future invocations. Calls to API routines that do not return
-** an error code (examples: [sqlite3_data_count()] or [sqlite3_mprintf()]) do
-** not change the error code returned by this routine.
-**
-** Assuming no other intervening sqlite3_* API calls are made, the error
-** code returned by this function is associated with the same error as
-** the strings returned by [sqlite3_errmsg()] and [sqlite3_errmsg16()].
-*/
-int sqlite3_errcode(sqlite3 *db);
-const char *sqlite3_errmsg(sqlite3*);
-const void *sqlite3_errmsg16(sqlite3*);
-
-/*
-** CAPI3REF: SQL Statement Object
-**
-** Instance of this object represent single SQL statements. This
-** is variously known as a "prepared statement" or a
-** "compiled SQL statement" or simply as a "statement".
-**
-** The life of a statement object goes something like this:
-**
-** <ol>
-** <li> Create the object using [sqlite3_prepare_v2()] or a related
-** function.
-** <li> Bind values to host parameters using
-** [sqlite3_bind_blob | sqlite3_bind_* interfaces].
-** <li> Run the SQL by calling [sqlite3_step()] one or more times.
-** <li> Reset the statement using [sqlite3_reset()] then go back
-** to step 2. Do this zero or more times.
-** <li> Destroy the object using [sqlite3_finalize()].
-** </ol>
-**
-** Refer to documentation on individual methods above for additional
-** information.
-*/
-typedef struct sqlite3_stmt sqlite3_stmt;
-
-/*
-** CAPI3REF: Compiling An SQL Statement
-**
-** To execute an SQL query, it must first be compiled into a byte-code
-** program using one of these routines.
-**
-** The first argument "db" is an [sqlite3 | SQLite database handle]
-** obtained from a prior call to [sqlite3_open()] or [sqlite3_open16()].
-** The second argument "zSql" is the statement to be compiled, encoded
-** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2()
-** interfaces uses UTF-8 and sqlite3_prepare16() and sqlite3_prepare16_v2()
-** use UTF-16.
-**
-** If the nByte argument is less
-** than zero, then zSql is read up to the first zero terminator. If
-** nByte is non-negative, then it is the maximum number of
-** bytes read from zSql. When nByte is non-negative, the
-** zSql string ends at either the first '\000' character or
-** until the nByte-th byte, whichever comes first.
-**
-** *pzTail is made to point to the first byte past the end of the first
-** SQL statement in zSql. This routine only compiles the first statement
-** in zSql, so *pzTail is left pointing to what remains uncompiled.
-**
-** *ppStmt is left pointing to a compiled
-** [sqlite3_stmt | SQL statement structure] that can be
-** executed using [sqlite3_step()]. Or if there is an error, *ppStmt may be
-** set to NULL. If the input text contained no SQL (if the input is and
-** empty string or a comment) then *ppStmt is set to NULL. The calling
-** procedure is responsible for deleting the compiled SQL statement
-** using [sqlite3_finalize()] after it has finished with it.
-**
-** On success, [SQLITE_OK] is returned. Otherwise an
-** [SQLITE_ERROR | error code] is returned.
-**
-** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
-** recommended for all new programs. The two older interfaces are retained
-** for backwards compatibility, but their use is discouraged.
-** In the "v2" interfaces, the prepared statement
-** that is returned (the [sqlite3_stmt] object) contains a copy of the
-** original SQL text. This causes the [sqlite3_step()] interface to
-** behave a differently in two ways:
-**
-** <ol>
-** <li>
-** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
-** always used to do, [sqlite3_step()] will automatically recompile the SQL
-** statement and try to run it again. If the schema has changed in a way
-** that makes the statement no longer valid, [sqlite3_step()] will still
-** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is
-** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the
-** error go away. Note: use [sqlite3_errmsg()] to find the text of the parsing
-** error that results in an [SQLITE_SCHEMA] return.
-** </li>
-**
-** <li>
-** When an error occurs,
-** [sqlite3_step()] will return one of the detailed
-** [SQLITE_ERROR | result codes] or
-** [SQLITE_IOERR_READ | extended result codes] such as directly.
-** The legacy behavior was that [sqlite3_step()] would only return a generic
-** [SQLITE_ERROR] result code and you would have to make a second call to
-** [sqlite3_reset()] in order to find the underlying cause of the problem.
-** With the "v2" prepare interfaces, the underlying reason for the error is
-** returned immediately.
-** </li>
-** </ol>
-*/
-int sqlite3_prepare(
- sqlite3 *db, /* Database handle */
- const char *zSql, /* SQL statement, UTF-8 encoded */
- int nByte, /* Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /* OUT: Statement handle */
- const char **pzTail /* OUT: Pointer to unused portion of zSql */
-);
-int sqlite3_prepare_v2(
- sqlite3 *db, /* Database handle */
- const char *zSql, /* SQL statement, UTF-8 encoded */
- int nByte, /* Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /* OUT: Statement handle */
- const char **pzTail /* OUT: Pointer to unused portion of zSql */
-);
-int sqlite3_prepare16(
- sqlite3 *db, /* Database handle */
- const void *zSql, /* SQL statement, UTF-16 encoded */
- int nByte, /* Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /* OUT: Statement handle */
- const void **pzTail /* OUT: Pointer to unused portion of zSql */
-);
-int sqlite3_prepare16_v2(
- sqlite3 *db, /* Database handle */
- const void *zSql, /* SQL statement, UTF-16 encoded */
- int nByte, /* Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /* OUT: Statement handle */
- const void **pzTail /* OUT: Pointer to unused portion of zSql */
-);
-
-/*
-** CAPI3REF: Dynamically Typed Value Object
-**
-** SQLite uses dynamic typing for the values it stores. Values can
-** be integers, floating point values, strings, BLOBs, or NULL. When
-** passing around values internally, each value is represented as
-** an instance of the sqlite3_value object.
-*/
-typedef struct Mem sqlite3_value;
-
-/*
-** CAPI3REF: SQL Function Context Object
-**
-** The context in which an SQL function executes is stored in an
-** sqlite3_context object. A pointer to such an object is the
-** first parameter to user-defined SQL functions.
-*/
-typedef struct sqlite3_context sqlite3_context;
-
-/*
-** CAPI3REF: Binding Values To Prepared Statements
-**
-** In the SQL strings input to [sqlite3_prepare_v2()] and its variants,
-** one or more literals can be replace by a parameter in one of these
-** forms:
-**
-** <ul>
-** <li> ?
-** <li> ?NNN
-** <li> :AAA
-** <li> @AAA
-** <li> $VVV
-** </ul>
-**
-** In the parameter forms shown above NNN is an integer literal,
-** AAA is an alphanumeric identifier and VVV is a variable name according
-** to the syntax rules of the TCL programming language.
-** The values of these parameters (also called "host parameter names")
-** can be set using the sqlite3_bind_*() routines defined here.
-**
-** The first argument to the sqlite3_bind_*() routines always is a pointer
-** to the [sqlite3_stmt] object returned from [sqlite3_prepare_v2()] or
-** its variants. The second
-** argument is the index of the parameter to be set. The first parameter has
-** an index of 1. When the same named parameter is used more than once, second
-** and subsequent
-** occurrences have the same index as the first occurrence. The index for
-** named parameters can be looked up using the
-** [sqlite3_bind_parameter_name()] API if desired. The index for "?NNN"
-** parametes is the value of NNN.
-** The NNN value must be between 1 and the compile-time
-** parameter SQLITE_MAX_VARIABLE_NUMBER (default value: 999).
-** See <a href="limits.html">limits.html</a> for additional information.
-**
-** The third argument is the value to bind to the parameter.
-**
-** In those
-** routines that have a fourth argument, its value is the number of bytes
-** in the parameter. To be clear: the value is the number of bytes in the
-** string, not the number of characters. The number
-** of bytes does not include the zero-terminator at the end of strings.
-** If the fourth parameter is negative, the length of the string is
-** number of bytes up to the first zero terminator.
-**
-** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
-** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
-** text after SQLite has finished with it. If the fifth argument is the
-** special value [SQLITE_STATIC], then the library assumes that the information
-** is in static, unmanaged space and does not need to be freed. If the
-** fifth argument has the value [SQLITE_TRANSIENT], then SQLite makes its
-** own private copy of the data immediately, before the sqlite3_bind_*()
-** routine returns.
-**
-** The sqlite3_bind_zeroblob() routine binds a BLOB of length n that
-** is filled with zeros. A zeroblob uses a fixed amount of memory
-** (just an integer to hold it size) while it is being processed.
-** Zeroblobs are intended to serve as place-holders for BLOBs whose
-** content is later written using
-** [sqlite3_blob_open | increment BLOB I/O] routines.
-**
-** The sqlite3_bind_*() routines must be called after
-** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and
-** before [sqlite3_step()].
-** Bindings are not cleared by the [sqlite3_reset()] routine.
-** Unbound parameters are interpreted as NULL.
-**
-** These routines return [SQLITE_OK] on success or an error code if
-** anything goes wrong. [SQLITE_RANGE] is returned if the parameter
-** index is out of range. [SQLITE_NOMEM] is returned if malloc fails.
-** [SQLITE_MISUSE] is returned if these routines are called on a virtual
-** machine that is the wrong state or which has already been finalized.
-*/
-int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
-int sqlite3_bind_double(sqlite3_stmt*, int, double);
-int sqlite3_bind_int(sqlite3_stmt*, int, int);
-int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite_int64);
-int sqlite3_bind_null(sqlite3_stmt*, int);
-int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
-int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
-int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
-int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
-
-/*
-** CAPI3REF: Number Of Host Parameters
-**
-** Return the largest host parameter index in the precompiled statement given
-** as the argument. When the host parameters are of the forms like ":AAA"
-** or "?", then they are assigned sequential increasing numbers beginning
-** with one, so the value returned is the number of parameters. However
-** if the same host parameter name is used multiple times, each occurrance
-** is given the same number, so the value returned in that case is the number
-** of unique host parameter names. If host parameters of the form "?NNN"
-** are used (where NNN is an integer) then there might be gaps in the
-** numbering and the value returned by this interface is the index of the
-** host parameter with the largest index value.
-*/
-int sqlite3_bind_parameter_count(sqlite3_stmt*);
-
-/*
-** CAPI3REF: Name Of A Host Parameter
-**
-** This routine returns a pointer to the name of the n-th parameter in a
-** [sqlite3_stmt | prepared statement].
-** Host parameters of the form ":AAA" or "@AAA" or "$VVV" have a name
-** which is the string ":AAA" or "@AAA" or "$VVV".
-** In other words, the initial ":" or "$" or "@"
-** is included as part of the name.
-** Parameters of the form "?" or "?NNN" have no name.
-**
-** The first bound parameter has an index of 1, not 0.
-**
-** If the value n is out of range or if the n-th parameter is nameless,
-** then NULL is returned. The returned string is always in the
-** UTF-8 encoding even if the named parameter was originally specified
-** as UTF-16 in [sqlite3_prepare16()] or [sqlite3_prepare16_v2()].
-*/
-const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
-
-/*
-** CAPI3REF: Index Of A Parameter With A Given Name
-**
-** This routine returns the index of a host parameter with the given name.
-** The name must match exactly. If no parameter with the given name is
-** found, return 0. Parameter names must be UTF8.
-*/
-int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
-
-/*
-** CAPI3REF: Reset All Bindings On A Prepared Statement
-**
-** Contrary to the intuition of many, [sqlite3_reset()] does not
-** reset the [sqlite3_bind_blob | bindings] on a
-** [sqlite3_stmt | prepared statement]. Use this routine to
-** reset all host parameters to NULL.
-*/
-int sqlite3_clear_bindings(sqlite3_stmt*);
-
-/*
-** CAPI3REF: Number Of Columns In A Result Set
-**
-** Return the number of columns in the result set returned by the
-** [sqlite3_stmt | compiled SQL statement]. This routine returns 0
-** if pStmt is an SQL statement that does not return data (for
-** example an UPDATE).
-*/
-int sqlite3_column_count(sqlite3_stmt *pStmt);
-
-/*
-** CAPI3REF: Column Names In A Result Set
-**
-** These routines return the name assigned to a particular column
-** in the result set of a SELECT statement. The sqlite3_column_name()
-** interface returns a pointer to a UTF8 string and sqlite3_column_name16()
-** returns a pointer to a UTF16 string. The first parameter is the
-** [sqlite_stmt | prepared statement] that implements the SELECT statement.
-** The second parameter is the column number. The left-most column is
-** number 0.
-**
-** The returned string pointer is valid until either the
-** [sqlite_stmt | prepared statement] is destroyed by [sqlite3_finalize()]
-** or until the next call sqlite3_column_name() or sqlite3_column_name16()
-** on the same column.
-*/
-const char *sqlite3_column_name(sqlite3_stmt*, int N);
-const void *sqlite3_column_name16(sqlite3_stmt*, int N);
-
-/*
-** CAPI3REF: Source Of Data In A Query Result
-**
-** These routines provide a means to determine what column of what
-** table in which database a result of a SELECT statement comes from.
-** The name of the database or table or column can be returned as
-** either a UTF8 or UTF16 string. The _database_ routines return
-** the database name, the _table_ routines return the table name, and
-** the origin_ routines return the column name.
-** The returned string is valid until
-** the [sqlite3_stmt | prepared statement] is destroyed using
-** [sqlite3_finalize()] or until the same information is requested
-** again in a different encoding.
-**
-** The names returned are the original un-aliased names of the
-** database, table, and column.
-**
-** The first argument to the following calls is a
-** [sqlite3_stmt | compiled SQL statement].
-** These functions return information about the Nth column returned by
-** the statement, where N is the second function argument.
-**
-** If the Nth column returned by the statement is an expression
-** or subquery and is not a column value, then all of these functions
-** return NULL. Otherwise, they return the
-** name of the attached database, table and column that query result
-** column was extracted from.
-**
-** As with all other SQLite APIs, those postfixed with "16" return UTF-16
-** encoded strings, the other functions return UTF-8.
-**
-** These APIs are only available if the library was compiled with the
-** SQLITE_ENABLE_COLUMN_METADATA preprocessor symbol defined.
-*/
-const char *sqlite3_column_database_name(sqlite3_stmt*,int);
-const void *sqlite3_column_database_name16(sqlite3_stmt*,int);
-const char *sqlite3_column_table_name(sqlite3_stmt*,int);
-const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
-const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
-const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
-
-/*
-** CAPI3REF: Declared Datatype Of A Query Result
-**
-** The first parameter is a [sqlite3_stmt | compiled SQL statement].
-** If this statement is a SELECT statement and the Nth column of the
-** returned result set of that SELECT is a table column (not an
-** expression or subquery) then the declared type of the table
-** column is returned. If the Nth column of the result set is an
-** expression or subquery, then a NULL pointer is returned.
-** The returned string is always UTF-8 encoded. For example, in
-** the database schema:
-**
-** CREATE TABLE t1(c1 VARIANT);
-**
-** And the following statement compiled:
-**
-** SELECT c1 + 1, c1 FROM t1;
-**
-** Then this routine would return the string "VARIANT" for the second
-** result column (i==1), and a NULL pointer for the first result column
-** (i==0).
-**
-** SQLite uses dynamic run-time typing. So just because a column
-** is declared to contain a particular type does not mean that the
-** data stored in that column is of the declared type. SQLite is
-** strongly typed, but the typing is dynamic not static. Type
-** is associated with individual values, not with the containers
-** used to hold those values.
-*/
-const char *sqlite3_column_decltype(sqlite3_stmt *, int i);
-const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
-
-/*
-** CAPI3REF: Evaluate An SQL Statement
-**
-** After an [sqlite3_stmt | SQL statement] has been prepared with a call
-** to either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or to one of
-** the legacy interfaces [sqlite3_prepare()] or [sqlite3_prepare16()],
-** then this function must be called one or more times to evaluate the
-** statement.
-**
-** The details of the behavior of this sqlite3_step() interface depend
-** on whether the statement was prepared using the newer "v2" interface
-** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy
-** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the
-** new "v2" interface is recommended for new applications but the legacy
-** interface will continue to be supported.
-**
-** In the lagacy interface, the return value will be either [SQLITE_BUSY],
-** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
-** With the "v2" interface, any of the other [SQLITE_OK | result code]
-** or [SQLITE_IOERR_READ | extended result code] might be returned as
-** well.
-**
-** [SQLITE_BUSY] means that the database engine was unable to acquire the
-** database locks it needs to do its job. If the statement is a COMMIT
-** or occurs outside of an explicit transaction, then you can retry the
-** statement. If the statement is not a COMMIT and occurs within a
-** explicit transaction then you should rollback the transaction before
-** continuing.
-**
-** [SQLITE_DONE] means that the statement has finished executing
-** successfully. sqlite3_step() should not be called again on this virtual
-** machine without first calling [sqlite3_reset()] to reset the virtual
-** machine back to its initial state.
-**
-** If the SQL statement being executed returns any data, then
-** [SQLITE_ROW] is returned each time a new row of data is ready
-** for processing by the caller. The values may be accessed using
-** the [sqlite3_column_int | column access functions].
-** sqlite3_step() is called again to retrieve the next row of data.
-**
-** [SQLITE_ERROR] means that a run-time error (such as a constraint
-** violation) has occurred. sqlite3_step() should not be called again on
-** the VM. More information may be found by calling [sqlite3_errmsg()].
-** With the legacy interface, a more specific error code (example:
-** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)
-** can be obtained by calling [sqlite3_reset()] on the
-** [sqlite_stmt | prepared statement]. In the "v2" interface,
-** the more specific error code is returned directly by sqlite3_step().
-**
-** [SQLITE_MISUSE] means that the this routine was called inappropriately.
-** Perhaps it was called on a [sqlite_stmt | prepared statement] that has
-** already been [sqlite3_finalize | finalized] or on one that had
-** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could
-** be the case that the same database connection is being used by two or
-** more threads at the same moment in time.
-**
-** <b>Goofy Interface Alert:</b>
-** In the legacy interface,
-** the sqlite3_step() API always returns a generic error code,
-** [SQLITE_ERROR], following any error other than [SQLITE_BUSY]
-** and [SQLITE_MISUSE]. You must call [sqlite3_reset()] or
-** [sqlite3_finalize()] in order to find one of the specific
-** [SQLITE_ERROR | result codes] that better describes the error.
-** We admit that this is a goofy design. The problem has been fixed
-** with the "v2" interface. If you prepare all of your SQL statements
-** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead
-** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()], then the
-** more specific [SQLITE_ERROR | result codes] are returned directly
-** by sqlite3_step(). The use of the "v2" interface is recommended.
-*/
-int sqlite3_step(sqlite3_stmt*);
-
-/*
-** CAPI3REF:
-**
-** Return the number of values in the current row of the result set.
-**
-** After a call to [sqlite3_step()] that returns [SQLITE_ROW], this routine
-** will return the same value as the [sqlite3_column_count()] function.
-** After [sqlite3_step()] has returned an [SQLITE_DONE], [SQLITE_BUSY], or
-** a [SQLITE_ERROR | error code], or before [sqlite3_step()] has been
-** called on the [sqlite_stmt | prepared statement] for the first time,
-** this routine returns zero.
-*/
-int sqlite3_data_count(sqlite3_stmt *pStmt);
-
-/*
-** CAPI3REF: Fundamental Datatypes
-**
-** Every value in SQLite has one of five fundamental datatypes:
-**
-** <ul>
-** <li> 64-bit signed integer
-** <li> 64-bit IEEE floating point number
-** <li> string
-** <li> BLOB
-** <li> NULL
-** </ul>
-**
-** These constants are codes for each of those types.
-**
-** Note that the SQLITE_TEXT constant was also used in SQLite version 2
-** for a completely different meaning. Software that links against both
-** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT not
-** SQLITE_TEXT.
-*/
-#define SQLITE_INTEGER 1
-#define SQLITE_FLOAT 2
-#define SQLITE_BLOB 4
-#define SQLITE_NULL 5
-#ifdef SQLITE_TEXT
-# undef SQLITE_TEXT
-#else
-# define SQLITE_TEXT 3
-#endif
-#define SQLITE3_TEXT 3
-
-/*
-** CAPI3REF: Results Values From A Query
-**
-** These routines return information about the information
-** in a single column of the current result row of a query. In every
-** case the first argument is a pointer to the
-** [sqlite3_stmt | SQL statement] that is being
-** evaluate (the [sqlite_stmt*] that was returned from
-** [sqlite3_prepare_v2()] or one of its variants) and
-** the second argument is the index of the column for which information
-** should be returned. The left-most column has an index of 0.
-**
-** If the SQL statement is not currently point to a valid row, or if the
-** the column index is out of range, the result is undefined.
-**
-** The sqlite3_column_type() routine returns
-** [SQLITE_INTEGER | datatype code] for the initial data type
-** of the result column. The returned value is one of [SQLITE_INTEGER],
-** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value
-** returned by sqlite3_column_type() is only meaningful if no type
-** conversions have occurred as described below. After a type conversion,
-** the value returned by sqlite3_column_type() is undefined. Future
-** versions of SQLite may change the behavior of sqlite3_column_type()
-** following a type conversion.
-**
-** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()
-** routine returns the number of bytes in that BLOB or string.
-** If the result is a UTF-16 string, then sqlite3_column_bytes() converts
-** the string to UTF-8 and then returns the number of bytes.
-** If the result is a numeric value then sqlite3_column_bytes() uses
-** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns
-** the number of bytes in that string.
-** The value returned does not include the zero terminator at the end
-** of the string. For clarity: the value returned is the number of
-** bytes in the string, not the number of characters.
-**
-** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes()
-** but leaves the result in UTF-16 instead of UTF-8.
-** The zero terminator is not included in this count.
-**
-** These routines attempt to convert the value where appropriate. For
-** example, if the internal representation is FLOAT and a text result
-** is requested, [sqlite3_snprintf()] is used internally to do the conversion
-** automatically. The following table details the conversions that
-** are applied:
-**
-** <blockquote>
-** <table border="1">
-** <tr><th> Internal <th> Requested <th>
-** <tr><th> Type <th> Type <th> Conversion
-**
-** <tr><td> NULL <td> INTEGER <td> Result is 0
-** <tr><td> NULL <td> FLOAT <td> Result is 0.0
-** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
-** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
-** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
-** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
-** <tr><td> INTEGER <td> BLOB <td> Same as for INTEGER->TEXT
-** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
-** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
-** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
-** <tr><td> TEXT <td> INTEGER <td> Use atoi()
-** <tr><td> TEXT <td> FLOAT <td> Use atof()
-** <tr><td> TEXT <td> BLOB <td> No change
-** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
-** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
-** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
-** </table>
-** </blockquote>
-**
-** The table above makes reference to standard C library functions atoi()
-** and atof(). SQLite does not really use these functions. It has its
-** on equavalent internal routines. The atoi() and atof() names are
-** used in the table for brevity and because they are familiar to most
-** C programmers.
-**
-** Note that when type conversions occur, pointers returned by prior
-** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
-** sqlite3_column_text16() may be invalidated.
-** Type conversions and pointer invalidations might occur
-** in the following cases:
-**
-** <ul>
-** <li><p> The initial content is a BLOB and sqlite3_column_text()
-** or sqlite3_column_text16() is called. A zero-terminator might
-** need to be added to the string.</p></li>
-**
-** <li><p> The initial content is UTF-8 text and sqlite3_column_bytes16() or
-** sqlite3_column_text16() is called. The content must be converted
-** to UTF-16.</p></li>
-**
-** <li><p> The initial content is UTF-16 text and sqlite3_column_bytes() or
-** sqlite3_column_text() is called. The content must be converted
-** to UTF-8.</p></li>
-** </ul>
-**
-** Conversions between UTF-16be and UTF-16le are always done in place and do
-** not invalidate a prior pointer, though of course the content of the buffer
-** that the prior pointer points to will have been modified. Other kinds
-** of conversion are done in place when it is possible, but sometime it is
-** not possible and in those cases prior pointers are invalidated.
-**
-** The safest and easiest to remember policy is to invoke these routines
-** in one of the following ways:
-**
-** <ul>
-** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>
-** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>
-** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>
-** </ul>
-**
-** In other words, you should call sqlite3_column_text(), sqlite3_column_blob(),
-** or sqlite3_column_text16() first to force the result into the desired
-** format, then invoke sqlite3_column_bytes() or sqlite3_column_bytes16() to
-** find the size of the result. Do not mix call to sqlite3_column_text() or
-** sqlite3_column_blob() with calls to sqlite3_column_bytes16(). And do not
-** mix calls to sqlite3_column_text16() with calls to sqlite3_column_bytes().
-*/
-const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
-int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
-int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
-double sqlite3_column_double(sqlite3_stmt*, int iCol);
-int sqlite3_column_int(sqlite3_stmt*, int iCol);
-sqlite_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
-const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
-const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
-int sqlite3_column_type(sqlite3_stmt*, int iCol);
-sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
-
-/*
-** CAPI3REF: Destroy A Prepared Statement Object
-**
-** The sqlite3_finalize() function is called to delete a
-** [sqlite3_stmt | compiled SQL statement]. If the statement was
-** executed successfully, or not executed at all, then SQLITE_OK is returned.
-** If execution of the statement failed then an
-** [SQLITE_ERROR | error code] or [SQLITE_IOERR_READ | extended error code]
-** is returned.
-**
-** This routine can be called at any point during the execution of the
-** [sqlite3_stmt | virtual machine]. If the virtual machine has not
-** completed execution when this routine is called, that is like
-** encountering an error or an interrupt. (See [sqlite3_interrupt()].)
-** Incomplete updates may be rolled back and transactions cancelled,
-** depending on the circumstances, and the
-** [SQLITE_ERROR | result code] returned will be [SQLITE_ABORT].
-*/
-int sqlite3_finalize(sqlite3_stmt *pStmt);
-
-/*
-** CAPI3REF: Reset A Prepared Statement Object
-**
-** The sqlite3_reset() function is called to reset a
-** [sqlite_stmt | compiled SQL statement] object.
-** back to it's initial state, ready to be re-executed.
-** Any SQL statement variables that had values bound to them using
-** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values.
-** Use [sqlite3_clear_bindings()] to reset the bindings.
-*/
-int sqlite3_reset(sqlite3_stmt *pStmt);
-
-/*
-** CAPI3REF: Create Or Redefine SQL Functions
-**
-** The following two functions are used to add SQL functions or aggregates
-** or to redefine the behavior of existing SQL functions or aggregates. The
-** difference only between the two is that the second parameter, the
-** name of the (scalar) function or aggregate, is encoded in UTF-8 for
-** sqlite3_create_function() and UTF-16 for sqlite3_create_function16().
-**
-** The first argument is the [sqlite3 | database handle] that holds the
-** SQL function or aggregate is to be added or redefined. If a single
-** program uses more than one database handle internally, then SQL
-** functions or aggregates must be added individually to each database
-** handle with which they will be used.
-**
-** The second parameter is the name of the SQL function to be created
-** or redefined.
-** The length of the name is limited to 255 bytes, exclusive of the
-** zero-terminator. Note that the name length limit is in bytes, not
-** characters. Any attempt to create a function with a longer name
-** will result in an SQLITE_ERROR error.
-**
-** The third parameter is the number of arguments that the SQL function or
-** aggregate takes. If this parameter is negative, then the SQL function or
-** aggregate may take any number of arguments.
-**
-** The fourth parameter, eTextRep, specifies what
-** [SQLITE_UTF8 | text encoding] this SQL function prefers for
-** its parameters. Any SQL function implementation should be able to work
-** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
-** more efficient with one encoding than another. It is allowed to
-** invoke sqlite_create_function() or sqlite3_create_function16() multiple
-** times with the same function but with different values of eTextRep.
-** When multiple implementations of the same function are available, SQLite
-** will pick the one that involves the least amount of data conversion.
-** If there is only a single implementation which does not care what
-** text encoding is used, then the fourth argument should be
-** [SQLITE_ANY].
-**
-** The fifth parameter is an arbitrary pointer. The implementation
-** of the function can gain access to this pointer using
-** [sqlite_user_data()].
-**
-** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
-** pointers to C-language functions that implement the SQL
-** function or aggregate. A scalar SQL function requires an implementation of
-** the xFunc callback only, NULL pointers should be passed as the xStep
-** and xFinal parameters. An aggregate SQL function requires an implementation
-** of xStep and xFinal and NULL should be passed for xFunc. To delete an
-** existing SQL function or aggregate, pass NULL for all three function
-** callback.
-**
-** It is permitted to register multiple implementations of the same
-** functions with the same name but with either differing numbers of
-** arguments or differing perferred text encodings. SQLite will use
-** the implementation most closely matches the way in which the
-** SQL function is used.
-*/
-int sqlite3_create_function(
- sqlite3 *,
- const char *zFunctionName,
- int nArg,
- int eTextRep,
- void*,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
- void (*xStep)(sqlite3_context*,int,sqlite3_value**),
- void (*xFinal)(sqlite3_context*)
-);
-int sqlite3_create_function16(
- sqlite3*,
- const void *zFunctionName,
- int nArg,
- int eTextRep,
- void*,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
- void (*xStep)(sqlite3_context*,int,sqlite3_value**),
- void (*xFinal)(sqlite3_context*)
-);
-
-/*
-** CAPI3REF: Text Encodings
-**
-** These constant define integer codes that represent the various
-** text encodings supported by SQLite.
-*/
-#define SQLITE_UTF8 1
-#define SQLITE_UTF16LE 2
-#define SQLITE_UTF16BE 3
-#define SQLITE_UTF16 4 /* Use native byte order */
-#define SQLITE_ANY 5 /* sqlite3_create_function only */
-#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
-
-/*
-** CAPI3REF: Obsolete Functions
-**
-** These functions are all now obsolete. In order to maintain
-** backwards compatibility with older code, we continue to support
-** these functions. However, new development projects should avoid
-** the use of these functions. To help encourage people to avoid
-** using these functions, we are not going to tell you want they do.
-*/
-int sqlite3_aggregate_count(sqlite3_context*);
-int sqlite3_expired(sqlite3_stmt*);
-int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
-int sqlite3_global_recover(void);
-
-
-/*
-** CAPI3REF: Obtaining SQL Function Parameter Values
-**
-** The C-language implementation of SQL functions and aggregates uses
-** this set of interface routines to access the parameter values on
-** the function or aggregate.
-**
-** The xFunc (for scalar functions) or xStep (for aggregates) parameters
-** to [sqlite3_create_function()] and [sqlite3_create_function16()]
-** define callbacks that implement the SQL functions and aggregates.
-** The 4th parameter to these callbacks is an array of pointers to
-** [sqlite3_value] objects. There is one [sqlite3_value] object for
-** each parameter to the SQL function. These routines are used to
-** extract values from the [sqlite3_value] objects.
-**
-** These routines work just like the corresponding
-** [sqlite3_column_blob | sqlite3_column_* routines] except that
-** these routines take a single [sqlite3_value*] pointer instead
-** of an [sqlite3_stmt*] pointer and an integer column number.
-**
-** The sqlite3_value_text16() interface extracts a UTF16 string
-** in the native byte-order of the host machine. The
-** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
-** extract UTF16 strings as big-endian and little-endian respectively.
-**
-** The sqlite3_value_numeric_type() interface attempts to apply
-** numeric affinity to the value. This means that an attempt is
-** made to convert the value to an integer or floating point. If
-** such a conversion is possible without loss of information (in order
-** words if the value is original a string that looks like a number)
-** then it is done. Otherwise no conversion occurs. The
-** [SQLITE_INTEGER | datatype] after conversion is returned.
-**
-** Please pay particular attention to the fact that the pointer that
-** is returned from [sqlite3_value_blob()], [sqlite3_value_text()], or
-** [sqlite3_value_text16()] can be invalidated by a subsequent call to
-** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite_value_text()],
-** or [sqlite3_value_text16()].
-*/
-const void *sqlite3_value_blob(sqlite3_value*);
-int sqlite3_value_bytes(sqlite3_value*);
-int sqlite3_value_bytes16(sqlite3_value*);
-double sqlite3_value_double(sqlite3_value*);
-int sqlite3_value_int(sqlite3_value*);
-sqlite_int64 sqlite3_value_int64(sqlite3_value*);
-const unsigned char *sqlite3_value_text(sqlite3_value*);
-const void *sqlite3_value_text16(sqlite3_value*);
-const void *sqlite3_value_text16le(sqlite3_value*);
-const void *sqlite3_value_text16be(sqlite3_value*);
-int sqlite3_value_type(sqlite3_value*);
-int sqlite3_value_numeric_type(sqlite3_value*);
-
-/*
-** CAPI3REF: Obtain Aggregate Function Context
-**
-** The implementation of aggregate SQL functions use this routine to allocate
-** a structure for storing their state. The first time this routine
-** is called for a particular aggregate, a new structure of size nBytes
-** is allocated, zeroed, and returned. On subsequent calls (for the
-** same aggregate instance) the same buffer is returned. The implementation
-** of the aggregate can use the returned buffer to accumulate data.
-**
-** The buffer allocated is freed automatically by SQLite whan the aggregate
-** query concludes.
-**
-** The first parameter should be a copy of the
-** [sqlite3_context | SQL function context] that is the first
-** parameter to the callback routine that implements the aggregate
-** function.
-*/
-void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
-
-/*
-** CAPI3REF: User Data For Functions
-**
-** The pUserData parameter to the [sqlite3_create_function()]
-** and [sqlite3_create_function16()] routines
-** used to register user functions is available to
-** the implementation of the function using this call.
-*/
-void *sqlite3_user_data(sqlite3_context*);
-
-/*
-** CAPI3REF: Function Auxiliary Data
-**
-** The following two functions may be used by scalar SQL functions to
-** associate meta-data with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated meta-data may be preserved. This may
-** be used, for example, to add a regular-expression matching scalar
-** function. The compiled version of the regular expression is stored as
-** meta-data associated with the SQL value passed as the regular expression
-** pattern. The compiled regular expression can be reused on multiple
-** invocations of the same function so that the original pattern string
-** does not need to be recompiled on each invocation.
-**
-** The sqlite3_get_auxdata() interface returns a pointer to the meta-data
-** associated with the Nth argument value to the current SQL function
-** call, where N is the second parameter. If no meta-data has been set for
-** that value, then a NULL pointer is returned.
-**
-** The sqlite3_set_auxdata() is used to associate meta-data with an SQL
-** function argument. The third parameter is a pointer to the meta-data
-** to be associated with the Nth user function argument value. The fourth
-** parameter specifies a destructor that will be called on the meta-
-** data pointer to release it when it is no longer required. If the
-** destructor is NULL, it is not invoked.
-**
-** In practice, meta-data is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and SQL variables.
-*/
-void *sqlite3_get_auxdata(sqlite3_context*, int);
-void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
-
-
-/*
-** CAPI3REF: Constants Defining Special Destructor Behavior
-**
-** These are special value for the destructor that is passed in as the
-** final argument to routines like [sqlite3_result_blob()]. If the destructor
-** argument is SQLITE_STATIC, it means that the content pointer is constant
-** and will never change. It does not need to be destroyed. The
-** SQLITE_TRANSIENT value means that the content will likely change in
-** the near future and that SQLite should make its own private copy of
-** the content before returning.
-**
-** The typedef is necessary to work around problems in certain
-** C++ compilers. See ticket #2191.
-*/
-typedef void (*sqlite3_destructor_type)(void*);
-#define SQLITE_STATIC ((sqlite3_destructor_type)0)
-#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
-
-/*
-** CAPI3REF: Setting The Result Of An SQL Function
-**
-** These routines are used by the xFunc or xFinal callbacks that
-** implement SQL functions and aggregates. See
-** [sqlite3_create_function()] and [sqlite3_create_function16()]
-** for additional information.
-**
-** These functions work very much like the
-** [sqlite3_bind_blob | sqlite3_bind_*] family of functions used
-** to bind values to host parameters in prepared statements.
-** Refer to the
-** [sqlite3_bind_blob | sqlite3_bind_* documentation] for
-** additional information.
-**
-** The sqlite3_result_error() and sqlite3_result_error16() functions
-** cause the implemented SQL function to throw an exception. The
-** parameter to sqlite3_result_error() or sqlite3_result_error16()
-** is the text of an error message.
-**
-** The sqlite3_result_toobig() cause the function implementation
-** to throw and error indicating that a string or BLOB is to long
-** to represent.
-*/
-void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
-void sqlite3_result_double(sqlite3_context*, double);
-void sqlite3_result_error(sqlite3_context*, const char*, int);
-void sqlite3_result_error16(sqlite3_context*, const void*, int);
-void sqlite3_result_error_toobig(sqlite3_context*);
-void sqlite3_result_int(sqlite3_context*, int);
-void sqlite3_result_int64(sqlite3_context*, sqlite_int64);
-void sqlite3_result_null(sqlite3_context*);
-void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
-void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
-void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
-void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
-void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
-void sqlite3_result_zeroblob(sqlite3_context*, int n);
-
-/*
-** CAPI3REF: Define New Collating Sequences
-**
-** These functions are used to add new collation sequences to the
-** [sqlite3*] handle specified as the first argument.
-**
-** The name of the new collation sequence is specified as a UTF-8 string
-** for sqlite3_create_collation() and sqlite3_create_collation_v2()
-** and a UTF-16 string for sqlite3_create_collation16(). In all cases
-** the name is passed as the second function argument.
-**
-** The third argument must be one of the constants [SQLITE_UTF8],
-** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied
-** routine expects to be passed pointers to strings encoded using UTF-8,
-** UTF-16 little-endian or UTF-16 big-endian respectively.
-**
-** A pointer to the user supplied routine must be passed as the fifth
-** argument. If it is NULL, this is the same as deleting the collation
-** sequence (so that SQLite cannot call it anymore). Each time the user
-** supplied function is invoked, it is passed a copy of the void* passed as
-** the fourth argument to sqlite3_create_collation() or
-** sqlite3_create_collation16() as its first parameter.
-**
-** The remaining arguments to the user-supplied routine are two strings,
-** each represented by a [length, data] pair and encoded in the encoding
-** that was passed as the third argument when the collation sequence was
-** registered. The user routine should return negative, zero or positive if
-** the first string is less than, equal to, or greater than the second
-** string. i.e. (STRING1 - STRING2).
-**
-** The sqlite3_create_collation_v2() works like sqlite3_create_collation()
-** excapt that it takes an extra argument which is a destructor for
-** the collation. The destructor is called when the collation is
-** destroyed and is passed a copy of the fourth parameter void* pointer
-** of the sqlite3_create_collation_v2(). Collations are destroyed when
-** they are overridden by later calls to the collation creation functions
-** or when the [sqlite3*] database handle is closed using [sqlite3_close()].
-**
-** The sqlite3_create_collation_v2() interface is experimental and
-** subject to change in future releases. The other collation creation
-** functions are stable.
-*/
-int sqlite3_create_collation(
- sqlite3*,
- const char *zName,
- int eTextRep,
- void*,
- int(*xCompare)(void*,int,const void*,int,const void*)
-);
-int sqlite3_create_collation_v2(
- sqlite3*,
- const char *zName,
- int eTextRep,
- void*,
- int(*xCompare)(void*,int,const void*,int,const void*),
- void(*xDestroy)(void*)
-);
-int sqlite3_create_collation16(
- sqlite3*,
- const char *zName,
- int eTextRep,
- void*,
- int(*xCompare)(void*,int,const void*,int,const void*)
-);
-
-/*
-** CAPI3REF: Collation Needed Callbacks
-**
-** To avoid having to register all collation sequences before a database
-** can be used, a single callback function may be registered with the
-** database handle to be called whenever an undefined collation sequence is
-** required.
-**
-** If the function is registered using the sqlite3_collation_needed() API,
-** then it is passed the names of undefined collation sequences as strings
-** encoded in UTF-8. If sqlite3_collation_needed16() is used, the names
-** are passed as UTF-16 in machine native byte order. A call to either
-** function replaces any existing callback.
-**
-** When the callback is invoked, the first argument passed is a copy
-** of the second argument to sqlite3_collation_needed() or
-** sqlite3_collation_needed16(). The second argument is the database
-** handle. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], or
-** [SQLITE_UTF16LE], indicating the most desirable form of the collation
-** sequence function required. The fourth parameter is the name of the
-** required collation sequence.
-**
-** The callback function should register the desired collation using
-** [sqlite3_create_collation()], [sqlite3_create_collation16()], or
-** [sqlite3_create_collation_v2()].
-*/
-int sqlite3_collation_needed(
- sqlite3*,
- void*,
- void(*)(void*,sqlite3*,int eTextRep,const char*)
-);
-int sqlite3_collation_needed16(
- sqlite3*,
- void*,
- void(*)(void*,sqlite3*,int eTextRep,const void*)
-);
-
-/*
-** Specify the key for an encrypted database. This routine should be
-** called right after sqlite3_open().
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-int sqlite3_key(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The key */
-);
-
-/*
-** Change the key on an open database. If the current database is not
-** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
-** database is decrypted.
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-int sqlite3_rekey(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The new key */
-);
-
-/*
-** CAPI3REF: Suspend Execution For A Short Time
-**
-** This function causes the current thread to suspend execution
-** a number of milliseconds specified in its parameter.
-**
-** If the operating system does not support sleep requests with
-** millisecond time resolution, then the time will be rounded up to
-** the nearest second. The number of milliseconds of sleep actually
-** requested from the operating system is returned.
-*/
-int sqlite3_sleep(int);
-
-/*
-** CAPI3REF: Name Of The Folder Holding Temporary Files
-**
-** If this global variable is made to point to a string which is
-** the name of a folder (a.ka. directory), then all temporary files
-** created by SQLite will be placed in that directory. If this variable
-** is NULL pointer, then SQLite does a search for an appropriate temporary
-** file directory.
-**
-** Once [sqlite3_open()] has been called, changing this variable will
-** invalidate the current temporary database, if any. Generally speaking,
-** it is not safe to invoke this routine after [sqlite3_open()] has
-** been called.
-*/
-SQLITE_EXTERN char *sqlite3_temp_directory;
-
-/*
-** CAPI3REF: Test To See If The Databse Is In Auto-Commit Mode
-**
-** Test to see whether or not the database connection is in autocommit
-** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on
-** by default. Autocommit is disabled by a BEGIN statement and reenabled
-** by the next COMMIT or ROLLBACK.
-*/
-int sqlite3_get_autocommit(sqlite3*);
-
-/*
-** CAPI3REF: Find The Database Handle Associated With A Prepared Statement
-**
-** Return the [sqlite3*] database handle to which a
-** [sqlite3_stmt | prepared statement] belongs.
-** This is the same database handle that was
-** the first argument to the [sqlite3_prepare_v2()] or its variants
-** that was used to create the statement in the first place.
-*/
-sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
-
-
-/*
-** CAPI3REF: Commit And Rollback Notification Callbacks
-**
-** These routines
-** register callback functions to be invoked whenever a transaction
-** is committed or rolled back. The pArg argument is passed through
-** to the callback. If the callback on a commit hook function
-** returns non-zero, then the commit is converted into a rollback.
-**
-** If another function was previously registered, its pArg value is returned.
-** Otherwise NULL is returned.
-**
-** Registering a NULL function disables the callback.
-**
-** For the purposes of this API, a transaction is said to have been
-** rolled back if an explicit "ROLLBACK" statement is executed, or
-** an error or constraint causes an implicit rollback to occur. The
-** callback is not invoked if a transaction is automatically rolled
-** back because the database connection is closed.
-**
-** These are experimental interfaces and are subject to change.
-*/
-void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
-void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
-
-/*
-** CAPI3REF: Data Change Notification Callbacks
-**
-** Register a callback function with the database connection identified by the
-** first argument to be invoked whenever a row is updated, inserted or deleted.
-** Any callback set by a previous call to this function for the same
-** database connection is overridden.
-**
-** The second argument is a pointer to the function to invoke when a
-** row is updated, inserted or deleted. The first argument to the callback is
-** a copy of the third argument to sqlite3_update_hook(). The second callback
-** argument is one of SQLITE_INSERT, SQLITE_DELETE or SQLITE_UPDATE, depending
-** on the operation that caused the callback to be invoked. The third and
-** fourth arguments to the callback contain pointers to the database and
-** table name containing the affected row. The final callback parameter is
-** the rowid of the row. In the case of an update, this is the rowid after
-** the update takes place.
-**
-** The update hook is not invoked when internal system tables are
-** modified (i.e. sqlite_master and sqlite_sequence).
-**
-** If another function was previously registered, its pArg value is returned.
-** Otherwise NULL is returned.
-*/
-void *sqlite3_update_hook(
- sqlite3*,
- void(*)(void *,int ,char const *,char const *,sqlite_int64),
- void*
-);
-
-/*
-** CAPI3REF: Enable Or Disable Shared Pager Cache
-**
-** This routine enables or disables the sharing of the database cache
-** and schema data structures between connections to the same database.
-** Sharing is enabled if the argument is true and disabled if the argument
-** is false.
-**
-** Cache sharing is enabled and disabled on a thread-by-thread basis.
-** Each call to this routine enables or disables cache sharing only for
-** connections created in the same thread in which this routine is called.
-** There is no mechanism for sharing cache between database connections
-** running in different threads.
-**
-** Sharing must be disabled prior to shutting down a thread or else
-** the thread will leak memory. Call this routine with an argument of
-** 0 to turn off sharing. Or use the sqlite3_thread_cleanup() API.
-**
-** This routine must not be called when any database connections
-** are active in the current thread. Enabling or disabling shared
-** cache while there are active database connections will result
-** in memory corruption.
-**
-** When the shared cache is enabled, the
-** following routines must always be called from the same thread:
-** [sqlite3_open()], [sqlite3_prepare_v2()], [sqlite3_step()],
-** [sqlite3_reset()], [sqlite3_finalize()], and [sqlite3_close()].
-** This is due to the fact that the shared cache makes use of
-** thread-specific storage so that it will be available for sharing
-** with other connections.
-**
-** Virtual tables cannot be used with a shared cache. When shared
-** cache is enabled, the sqlite3_create_module() API used to register
-** virtual tables will always return an error.
-**
-** This routine returns [SQLITE_OK] if shared cache was
-** enabled or disabled successfully. An [SQLITE_ERROR | error code]
-** is returned otherwise.
-**
-** Shared cache is disabled by default for backward compatibility.
-*/
-int sqlite3_enable_shared_cache(int);
-
-/*
-** CAPI3REF: Attempt To Free Heap Memory
-**
-** Attempt to free N bytes of heap memory by deallocating non-essential
-** memory allocations held by the database library (example: memory
-** used to cache database pages to improve performance).
-**
-** This function is not a part of standard builds. It is only created
-** if SQLite is compiled with the SQLITE_ENABLE_MEMORY_MANAGEMENT macro.
-*/
-int sqlite3_release_memory(int);
-
-/*
-** CAPI3REF: Impose A Limit On Heap Size
-**
-** Place a "soft" limit on the amount of heap memory that may be allocated by
-** SQLite within the current thread. If an internal allocation is requested
-** that would exceed the specified limit, [sqlite3_release_memory()] is invoked
-** one or more times to free up some space before the allocation is made.
-**
-** The limit is called "soft", because if [sqlite3_release_memory()] cannot free
-** sufficient memory to prevent the limit from being exceeded, the memory is
-** allocated anyway and the current operation proceeds.
-**
-** Prior to shutting down a thread sqlite3_soft_heap_limit() must be set to
-** zero (the default) or else the thread will leak memory. Alternatively, use
-** the [sqlite3_thread_cleanup()] API.
-**
-** A negative or zero value for N means that there is no soft heap limit and
-** [sqlite3_release_memory()] will only be called when memory is exhaused.
-** The default value for the soft heap limit is zero.
-**
-** SQLite makes a best effort to honor the soft heap limit. But if it
-** is unable to reduce memory usage below the soft limit, execution will
-** continue without error or notification. This is why the limit is
-** called a "soft" limit. It is advisory only.
-**
-** This function is only available if the library was compiled with the
-** SQLITE_ENABLE_MEMORY_MANAGEMENT option set.
-** memory-management has been enabled.
-*/
-void sqlite3_soft_heap_limit(int);
-
-/*
-** CAPI3REF: Clean Up Thread Local Storage
-**
-** This routine makes sure that all thread-local storage has been
-** deallocated for the current thread.
-**
-** This routine is not technically necessary. All thread-local storage
-** will be automatically deallocated once memory-management and
-** shared-cache are disabled and the soft heap limit has been set
-** to zero. This routine is provided as a convenience for users who
-** want to make absolutely sure they have not forgotten something
-** prior to killing off a thread.
-*/
-void sqlite3_thread_cleanup(void);
-
-/*
-** CAPI3REF: Extract Metadata About A Column Of A Table
-**
-** This routine
-** returns meta-data about a specific column of a specific database
-** table accessible using the connection handle passed as the first function
-** argument.
-**
-** The column is identified by the second, third and fourth parameters to
-** this function. The second parameter is either the name of the database
-** (i.e. "main", "temp" or an attached database) containing the specified
-** table or NULL. If it is NULL, then all attached databases are searched
-** for the table using the same algorithm as the database engine uses to
-** resolve unqualified table references.
-**
-** The third and fourth parameters to this function are the table and column
-** name of the desired column, respectively. Neither of these parameters
-** may be NULL.
-**
-** Meta information is returned by writing to the memory locations passed as
-** the 5th and subsequent parameters to this function. Any of these
-** arguments may be NULL, in which case the corresponding element of meta
-** information is ommitted.
-**
-** <pre>
-** Parameter Output Type Description
-** -----------------------------------
-**
-** 5th const char* Data type
-** 6th const char* Name of the default collation sequence
-** 7th int True if the column has a NOT NULL constraint
-** 8th int True if the column is part of the PRIMARY KEY
-** 9th int True if the column is AUTOINCREMENT
-** </pre>
-**
-**
-** The memory pointed to by the character pointers returned for the
-** declaration type and collation sequence is valid only until the next
-** call to any sqlite API function.
-**
-** If the specified table is actually a view, then an error is returned.
-**
-** If the specified column is "rowid", "oid" or "_rowid_" and an
-** INTEGER PRIMARY KEY column has been explicitly declared, then the output
-** parameters are set for the explicitly declared column. If there is no
-** explicitly declared IPK column, then the output parameters are set as
-** follows:
-**
-** <pre>
-** data type: "INTEGER"
-** collation sequence: "BINARY"
-** not null: 0
-** primary key: 1
-** auto increment: 0
-** </pre>
-**
-** This function may load one or more schemas from database files. If an
-** error occurs during this process, or if the requested table or column
-** cannot be found, an SQLITE error code is returned and an error message
-** left in the database handle (to be retrieved using sqlite3_errmsg()).
-**
-** This API is only available if the library was compiled with the
-** SQLITE_ENABLE_COLUMN_METADATA preprocessor symbol defined.
-*/
-int sqlite3_table_column_metadata(
- sqlite3 *db, /* Connection handle */
- const char *zDbName, /* Database name or NULL */
- const char *zTableName, /* Table name */
- const char *zColumnName, /* Column name */
- char const **pzDataType, /* OUTPUT: Declared data type */
- char const **pzCollSeq, /* OUTPUT: Collation sequence name */
- int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
- int *pPrimaryKey, /* OUTPUT: True if column part of PK */
- int *pAutoinc /* OUTPUT: True if colums is auto-increment */
-);
-
-/*
-** CAPI3REF: Load An Extension
-**
-** Attempt to load an SQLite extension library contained in the file
-** zFile. The entry point is zProc. zProc may be 0 in which case the
-** name of the entry point defaults to "sqlite3_extension_init".
-**
-** Return [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
-**
-** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with
-** error message text. The calling function should free this memory
-** by calling [sqlite3_free()].
-**
-** Extension loading must be enabled using [sqlite3_enable_load_extension()]
-** prior to calling this API or an error will be returned.
-*/
-int sqlite3_load_extension(
- sqlite3 *db, /* Load the extension into this database connection */
- const char *zFile, /* Name of the shared library containing extension */
- const char *zProc, /* Entry point. Derived from zFile if 0 */
- char **pzErrMsg /* Put error message here if not 0 */
-);
-
-/*
-** CAPI3REF: Enable Or Disable Extension Loading
-**
-** So as not to open security holes in older applications that are
-** unprepared to deal with extension loading, and as a means of disabling
-** extension loading while evaluating user-entered SQL, the following
-** API is provided to turn the [sqlite3_load_extension()] mechanism on and
-** off. It is off by default. See ticket #1863.
-**
-** Call this routine with onoff==1 to turn extension loading on
-** and call it with onoff==0 to turn it back off again.
-*/
-int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
-
-/*
-** CAPI3REF: Make Arrangements To Automatically Load An Extension
-**
-** Register an extension entry point that is automatically invoked
-** whenever a new database connection is opened using
-** [sqlite3_open()] or [sqlite3_open16()].
-**
-** This API can be invoked at program startup in order to register
-** one or more statically linked extensions that will be available
-** to all new database connections.
-**
-** Duplicate extensions are detected so calling this routine multiple
-** times with the same extension is harmless.
-**
-** This routine stores a pointer to the extension in an array
-** that is obtained from malloc(). If you run a memory leak
-** checker on your program and it reports a leak because of this
-** array, then invoke [sqlite3_automatic_extension_reset()] prior
-** to shutdown to free the memory.
-**
-** Automatic extensions apply across all threads.
-**
-** This interface is experimental and is subject to change or
-** removal in future releases of SQLite.
-*/
-int sqlite3_auto_extension(void *xEntryPoint);
-
-
-/*
-** CAPI3REF: Reset Automatic Extension Loading
-**
-** Disable all previously registered automatic extensions. This
-** routine undoes the effect of all prior [sqlite3_automatic_extension()]
-** calls.
-**
-** This call disabled automatic extensions in all threads.
-**
-** This interface is experimental and is subject to change or
-** removal in future releases of SQLite.
-*/
-void sqlite3_reset_auto_extension(void);
-
-
-/*
-****** EXPERIMENTAL - subject to change without notice **************
-**
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stablizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
-** Structures used by the virtual table interface
-*/
-typedef struct sqlite3_vtab sqlite3_vtab;
-typedef struct sqlite3_index_info sqlite3_index_info;
-typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
-typedef struct sqlite3_module sqlite3_module;
-
-/*
-** A module is a class of virtual tables. Each module is defined
-** by an instance of the following structure. This structure consists
-** mostly of methods for the module.
-*/
-struct sqlite3_module {
- int iVersion;
- int (*xCreate)(sqlite3*, void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVTab, char**);
- int (*xConnect)(sqlite3*, void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVTab, char**);
- int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
- int (*xDisconnect)(sqlite3_vtab *pVTab);
- int (*xDestroy)(sqlite3_vtab *pVTab);
- int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
- int (*xClose)(sqlite3_vtab_cursor*);
- int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
- int argc, sqlite3_value **argv);
- int (*xNext)(sqlite3_vtab_cursor*);
- int (*xEof)(sqlite3_vtab_cursor*);
- int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
- int (*xRowid)(sqlite3_vtab_cursor*, sqlite_int64 *pRowid);
- int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite_int64 *);
- int (*xBegin)(sqlite3_vtab *pVTab);
- int (*xSync)(sqlite3_vtab *pVTab);
- int (*xCommit)(sqlite3_vtab *pVTab);
- int (*xRollback)(sqlite3_vtab *pVTab);
- int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
- void **ppArg);
-
- int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
-};
-
-/*
-** The sqlite3_index_info structure and its substructures is used to
-** pass information into and receive the reply from the xBestIndex
-** method of an sqlite3_module. The fields under **Inputs** are the
-** inputs to xBestIndex and are read-only. xBestIndex inserts its
-** results into the **Outputs** fields.
-**
-** The aConstraint[] array records WHERE clause constraints of the
-** form:
-**
-** column OP expr
-**
-** Where OP is =, <, <=, >, or >=. The particular operator is stored
-** in aConstraint[].op. The index of the column is stored in
-** aConstraint[].iColumn. aConstraint[].usable is TRUE if the
-** expr on the right-hand side can be evaluated (and thus the constraint
-** is usable) and false if it cannot.
-**
-** The optimizer automatically inverts terms of the form "expr OP column"
-** and makes other simplificatinos to the WHERE clause in an attempt to
-** get as many WHERE clause terms into the form shown above as possible.
-** The aConstraint[] array only reports WHERE clause terms in the correct
-** form that refer to the particular virtual table being queried.
-**
-** Information about the ORDER BY clause is stored in aOrderBy[].
-** Each term of aOrderBy records a column of the ORDER BY clause.
-**
-** The xBestIndex method must fill aConstraintUsage[] with information
-** about what parameters to pass to xFilter. If argvIndex>0 then
-** the right-hand side of the corresponding aConstraint[] is evaluated
-** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit
-** is true, then the constraint is assumed to be fully handled by the
-** virtual table and is not checked again by SQLite.
-**
-** The idxNum and idxPtr values are recorded and passed into xFilter.
-** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true.
-**
-** The orderByConsumed means that output from xFilter will occur in
-** the correct order to satisfy the ORDER BY clause so that no separate
-** sorting step is required.
-**
-** The estimatedCost value is an estimate of the cost of doing the
-** particular lookup. A full scan of a table with N entries should have
-** a cost of N. A binary search of a table of N entries should have a
-** cost of approximately log(N).
-*/
-struct sqlite3_index_info {
- /* Inputs */
- const int nConstraint; /* Number of entries in aConstraint */
- const struct sqlite3_index_constraint {
- int iColumn; /* Column on left-hand side of constraint */
- unsigned char op; /* Constraint operator */
- unsigned char usable; /* True if this constraint is usable */
- int iTermOffset; /* Used internally - xBestIndex should ignore */
- } *const aConstraint; /* Table of WHERE clause constraints */
- const int nOrderBy; /* Number of terms in the ORDER BY clause */
- const struct sqlite3_index_orderby {
- int iColumn; /* Column number */
- unsigned char desc; /* True for DESC. False for ASC. */
- } *const aOrderBy; /* The ORDER BY clause */
-
- /* Outputs */
- struct sqlite3_index_constraint_usage {
- int argvIndex; /* if >0, constraint is part of argv to xFilter */
- unsigned char omit; /* Do not code a test for this constraint */
- } *const aConstraintUsage;
- int idxNum; /* Number used to identify the index */
- char *idxStr; /* String, possibly obtained from sqlite3_malloc */
- int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
- int orderByConsumed; /* True if output is already ordered */
- double estimatedCost; /* Estimated cost of using this index */
-};
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
-
-/*
-** This routine is used to register a new module name with an SQLite
-** connection. Module names must be registered before creating new
-** virtual tables on the module, or before using preexisting virtual
-** tables of the module.
-*/
-int sqlite3_create_module(
- sqlite3 *db, /* SQLite connection to register module with */
- const char *zName, /* Name of the module */
- const sqlite3_module *, /* Methods for the module */
- void * /* Client data for xCreate/xConnect */
-);
-
-/*
-** This routine is identical to the sqlite3_create_module() method above,
-** except that it allows a destructor function to be specified. It is
-** even more experimental than the rest of the virtual tables API.
-*/
-int sqlite3_create_module_v2(
- sqlite3 *db, /* SQLite connection to register module with */
- const char *zName, /* Name of the module */
- const sqlite3_module *, /* Methods for the module */
- void *, /* Client data for xCreate/xConnect */
- void(*xDestroy)(void*) /* Module destructor function */
-);
-
-/*
-** Every module implementation uses a subclass of the following structure
-** to describe a particular instance of the module. Each subclass will
-** be taylored to the specific needs of the module implementation. The
-** purpose of this superclass is to define certain fields that are common
-** to all module implementations.
-**
-** Virtual tables methods can set an error message by assigning a
-** string obtained from sqlite3_mprintf() to zErrMsg. The method should
-** take care that any prior string is freed by a call to sqlite3_free()
-** prior to assigning a new string to zErrMsg. After the error message
-** is delivered up to the client application, the string will be automatically
-** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note
-** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field
-** since virtual tables are commonly implemented in loadable extensions which
-** do not have access to sqlite3MPrintf() or sqlite3Free().
-*/
-struct sqlite3_vtab {
- const sqlite3_module *pModule; /* The module for this virtual table */
- int nRef; /* Used internally */
- char *zErrMsg; /* Error message from sqlite3_mprintf() */
- /* Virtual table implementations will typically add additional fields */
-};
-
-/* Every module implementation uses a subclass of the following structure
-** to describe cursors that point into the virtual table and are used
-** to loop through the virtual table. Cursors are created using the
-** xOpen method of the module. Each module implementation will define
-** the content of a cursor structure to suit its own needs.
-**
-** This superclass exists in order to define fields of the cursor that
-** are common to all implementations.
-*/
-struct sqlite3_vtab_cursor {
- sqlite3_vtab *pVtab; /* Virtual table of this cursor */
- /* Virtual table implementations will typically add additional fields */
-};
-
-/*
-** The xCreate and xConnect methods of a module use the following API
-** to declare the format (the names and datatypes of the columns) of
-** the virtual tables they implement.
-*/
-int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
-
-/*
-** Virtual tables can provide alternative implementations of functions
-** using the xFindFunction method. But global versions of those functions
-** must exist in order to be overloaded.
-**
-** This API makes sure a global version of a function with a particular
-** name and number of parameters exists. If no such function exists
-** before this API is called, a new function is created. The implementation
-** of the new function always causes an exception to be thrown. So
-** the new function is not good for anything by itself. Its only
-** purpose is to be a place-holder function that can be overloaded
-** by virtual tables.
-**
-** This API should be considered part of the virtual table interface,
-** which is experimental and subject to change.
-*/
-int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
-
-/*
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stablizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-**
-****** EXPERIMENTAL - subject to change without notice **************
-*/
-
-/*
-** CAPI3REF: A Handle To An Open BLOB
-**
-** An instance of the following opaque structure is used to
-** represent an blob-handle. A blob-handle is created by
-** [sqlite3_blob_open()] and destroyed by [sqlite3_blob_close()].
-** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces
-** can be used to read or write small subsections of the blob.
-** The [sqltie3_blob_size()] interface returns the size of the
-** blob in bytes.
-*/
-typedef struct sqlite3_blob sqlite3_blob;
-
-/*
-** CAPI3REF: Open A BLOB For Incremental I/O
-**
-** Open a handle to the blob located in row iRow,, column zColumn,
-** table zTable in database zDb. i.e. the same blob that would
-** be selected by:
-**
-** <pre>
-** SELECT zColumn FROM zDb.zTable WHERE rowid = iRow;
-** </pre>
-**
-** If the flags parameter is non-zero, the blob is opened for
-** read and write access. If it is zero, the blob is opened for read
-** access.
-**
-** On success, [SQLITE_OK] is returned and the new
-** [sqlite3_blob | blob handle] is written to *ppBlob.
-** Otherwise an error code is returned and
-** any value written to *ppBlob should not be used by the caller.
-** This function sets the database-handle error code and message
-** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()].
-*/
-int sqlite3_blob_open(
- sqlite3*,
- const char *zDb,
- const char *zTable,
- const char *zColumn,
- sqlite_int64 iRow,
- int flags,
- sqlite3_blob **ppBlob
-);
-
-/*
-** CAPI3REF: Close A BLOB Handle
-**
-** Close an open [sqlite3_blob | blob handle].
-*/
-int sqlite3_blob_close(sqlite3_blob *);
-
-/*
-** CAPI3REF: Return The Size Of An Open BLOB
-**
-** Return the size in bytes of the blob accessible via the open
-** [sqlite3_blob | blob-handle] passed as an argument.
-*/
-int sqlite3_blob_bytes(sqlite3_blob *);
-
-/*
-** CAPI3REF: Read Data From A BLOB Incrementally
-**
-** This function is used to read data from an open
-** [sqlite3_blob | blob-handle] into a caller supplied buffer.
-** n bytes of data are copied into buffer
-** z from the open blob, starting at offset iOffset.
-**
-** On success, SQLITE_OK is returned. Otherwise, an
-** [SQLITE_ERROR | SQLite error code] or an
-** [SQLITE_IOERR_READ | extended error code] is returned.
-*/
-int sqlite3_blob_read(sqlite3_blob *, void *z, int n, int iOffset);
-
-/*
-** CAPI3REF: Write Data Into A BLOB Incrementally
-**
-** This function is used to write data into an open
-** [sqlite3_blob | blob-handle] from a user supplied buffer.
-** n bytes of data are copied from the buffer
-** pointed to by z into the open blob, starting at offset iOffset.
-**
-** If the [sqlite3_blob | blob-handle] passed as the first argument
-** was not opened for writing (the flags parameter to [sqlite3_blob_open()]
-*** was zero), this function returns [SQLITE_READONLY].
-**
-** This function may only modify the contents of the blob, it is
-** not possible to increase the size of a blob using this API. If
-** offset iOffset is less than n bytes from the end of the blob,
-** [SQLITE_ERROR] is returned and no data is written.
-**
-** On success, SQLITE_OK is returned. Otherwise, an
-** [SQLITE_ERROR | SQLite error code] or an
-** [SQLITE_IOERR_READ | extended error code] is returned.
-*/
-int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
-
-/*
-** Preload the databases into the pager cache, up to the maximum size of the
-** pager cache.
-**
-** For a database to be loaded successfully, the pager must be active. That is,
-** there must be an open statement on that database. See sqlite3pager_loadall
-**
-** There might be many databases attached to the given connection. We iterate
-** them all and try to load them. If none are loadable successfully, we return
-** an error. Otherwise, we return OK.
-*/
-int sqlite3Preload(sqlite3 *db);
-
-/*
-** Undo the hack that converts floating point types to integer for
-** builds on processors without floating point support.
-*/
-#ifdef SQLITE_OMIT_FLOATING_POINT
-# undef double
-#endif
-
-#ifdef __cplusplus
-} /* End of the 'extern "C"' block */
-#endif
-#endif
diff --git a/third_party/sqlite/sqlite3.pc.in b/third_party/sqlite/sqlite3.pc.in
new file mode 100755
index 0000000..50bf996
--- /dev/null
+++ b/third_party/sqlite/sqlite3.pc.in
@@ -0,0 +1,13 @@
+# Package Information for pkg-config
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: SQLite
+Description: SQL database engine
+Version: @VERSION@
+Libs: -L${libdir} -lsqlite3
+Libs.private: @LIBS@
+Cflags: -I${includedir}
diff --git a/third_party/sqlite/alter.c b/third_party/sqlite/src/alter.c
index b8c767a..ba929be 100644..100755
--- a/third_party/sqlite/alter.c
+++ b/third_party/sqlite/src/alter.c
@@ -12,7 +12,7 @@
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
**
-** $Id: alter.c,v 1.27 2007/06/27 17:09:24 danielk1977 Exp $
+** $Id: alter.c,v 1.47 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -51,9 +51,11 @@ static void renameTableFunc(
int len = 0;
char *zRet;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+
/* The principle used to locate the table name in the CREATE TABLE
- ** statement is that the table name is the first token that is immediatedly
- ** followed by a left parenthesis - TK_LP - or "USING" TK_USING.
+ ** statement is that the table name is the first non-space token that
+ ** is immediately followed by a TK_LP or TK_USING token.
*/
if( zSql ){
do {
@@ -67,18 +69,18 @@ static void renameTableFunc(
tname.n = len;
/* Advance zCsr to the next token. Store that token type in 'token',
- ** and it's length in 'len' (to be used next iteration of this loop).
+ ** and its length in 'len' (to be used next iteration of this loop).
*/
do {
zCsr += len;
len = sqlite3GetToken(zCsr, &token);
- } while( token==TK_SPACE );
+ } while( token==TK_SPACE || token==TK_COMMENT );
assert( len>0 );
} while( token!=TK_LP && token!=TK_USING );
- zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql,
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", tname.z - zSql, zSql,
zTableName, tname.z+tname.n);
- sqlite3_result_text(context, zRet, -1, sqlite3FreeX);
+ sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
@@ -105,6 +107,8 @@ static void renameTriggerFunc(
int len = 0;
char *zRet;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+
/* The principle used to locate the table name in the CREATE TRIGGER
** statement is that the table name is the first token that is immediatedly
** preceded by either TK_ON or TK_DOT and immediatedly followed by one
@@ -123,7 +127,7 @@ static void renameTriggerFunc(
tname.n = len;
/* Advance zCsr to the next token. Store that token type in 'token',
- ** and it's length in 'len' (to be used next iteration of this loop).
+ ** and its length in 'len' (to be used next iteration of this loop).
*/
do {
zCsr += len;
@@ -149,9 +153,9 @@ static void renameTriggerFunc(
/* Variable tname now contains the token that is the old table-name
** in the CREATE TRIGGER statement.
*/
- zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql,
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", tname.z - zSql, zSql,
zTableName, tname.z+tname.n);
- sqlite3_result_text(context, zRet, -1, sqlite3FreeX);
+ sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
#endif /* !SQLITE_OMIT_TRIGGER */
@@ -160,22 +164,12 @@ static void renameTriggerFunc(
** Register built-in functions used to help implement ALTER TABLE
*/
void sqlite3AlterFunctions(sqlite3 *db){
- static const struct {
- char *zName;
- signed char nArg;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
- } aFuncs[] = {
- { "sqlite_rename_table", 2, renameTableFunc},
+ sqlite3CreateFunc(db, "sqlite_rename_table", 2, SQLITE_UTF8, 0,
+ renameTableFunc, 0, 0);
#ifndef SQLITE_OMIT_TRIGGER
- { "sqlite_rename_trigger", 2, renameTriggerFunc},
+ sqlite3CreateFunc(db, "sqlite_rename_trigger", 2, SQLITE_UTF8, 0,
+ renameTriggerFunc, 0, 0);
#endif
- };
- int i;
-
- for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
- sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
- SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0);
- }
}
/*
@@ -196,14 +190,15 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
** expression being built up in zWhere.
*/
if( pTab->pSchema!=pTempSchema ){
+ sqlite3 *db = pParse->db;
for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){
- zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
+ zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name);
}else{
tmp = zWhere;
- zWhere = sqlite3MPrintf("%s OR name=%Q", zWhere, pTrig->name);
- sqliteFree(tmp);
+ zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->name);
+ sqlite3DbFree(db, tmp);
}
}
}
@@ -229,6 +224,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
v = sqlite3GetVdbe(pParse);
if( !v ) return;
+ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
assert( iDb>=0 );
@@ -237,24 +233,24 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
assert( iTrigDb==iDb || iTrigDb==1 );
- sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0);
+ sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0);
}
#endif
/* Drop the table and index from the internal schema */
- sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
/* Reload the table, index and permanent trigger schemas. */
- zWhere = sqlite3MPrintf("tbl_name=%Q", zName);
+ zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
if( !zWhere ) return;
- sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
#ifndef SQLITE_OMIT_TRIGGER
/* Now, if the table is not stored in the temp database, reload any temp
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
*/
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
- sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zWhere, P3_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC);
}
#endif
}
@@ -281,16 +277,17 @@ void sqlite3AlterRenameTable(
#endif
int isVirtualRename = 0; /* True if this is a v-table with an xRename() */
- if( sqlite3MallocFailed() ) goto exit_rename_table;
+ if( db->mallocFailed ) goto exit_rename_table;
assert( pSrc->nSrc==1 );
+ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
- pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zName;
/* Get a NULL terminated version of the new table name. */
- zName = sqlite3NameFromToken(pName);
+ zName = sqlite3NameFromToken(db, pName);
if( !zName ) goto exit_rename_table;
/* Check that a table or index named 'zName' does not already exist
@@ -313,6 +310,13 @@ void sqlite3AlterRenameTable(
goto exit_rename_table;
}
+#ifndef SQLITE_OMIT_VIEW
+ if( pTab->pSelect ){
+ sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName);
+ goto exit_rename_table;
+ }
+#endif
+
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Invoke the authorization callback. */
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
@@ -339,7 +343,7 @@ void sqlite3AlterRenameTable(
goto exit_rename_table;
}
sqlite3BeginWriteOperation(pParse, isVirtualRename, iDb);
- sqlite3ChangeCookie(db, v, iDb);
+ sqlite3ChangeCookie(pParse, iDb);
/* If this is a virtual table, invoke the xRename() function if
** one is defined. The xRename() callback will modify the names
@@ -348,8 +352,9 @@ void sqlite3AlterRenameTable(
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( isVirtualRename ){
- sqlite3VdbeOp3(v, OP_String8, 0, 0, zName, 0);
- sqlite3VdbeOp3(v, OP_VRename, 0, 0, (const char*)pTab->pVtab, P3_VTAB);
+ int i = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0);
+ sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pTab->pVtab, P4_VTAB);
}
#endif
@@ -371,7 +376,7 @@ void sqlite3AlterRenameTable(
"name = CASE "
"WHEN type='table' THEN %Q "
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
- "'sqlite_autoindex_' || %Q || substr(name,%d+18,10) "
+ "'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END "
"WHERE tbl_name=%Q AND "
"(type='table' OR type='index' OR type='trigger');",
@@ -388,7 +393,7 @@ void sqlite3AlterRenameTable(
*/
if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){
sqlite3NestedParse(pParse,
- "UPDATE %Q.sqlite_sequence set name = %Q WHERE name = %Q",
+ "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
zDb, zName, pTab->zName);
}
#endif
@@ -404,7 +409,7 @@ void sqlite3AlterRenameTable(
"sql = sqlite_rename_trigger(sql, %Q), "
"tbl_name = %Q "
"WHERE %s;", zName, zName, zWhere);
- sqliteFree(zWhere);
+ sqlite3DbFree(db, zWhere);
}
#endif
@@ -412,8 +417,8 @@ void sqlite3AlterRenameTable(
reloadTableSchema(pParse, pTab, zName);
exit_rename_table:
- sqlite3SrcListDelete(pSrc);
- sqliteFree(zName);
+ sqlite3SrcListDelete(db, pSrc);
+ sqlite3DbFree(db, zName);
}
@@ -434,17 +439,20 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
char *zCol; /* Null-terminated column definition */
Column *pCol; /* The new column */
Expr *pDflt; /* Default value for the new column */
+ sqlite3 *db; /* The database connection; */
if( pParse->nErr ) return;
pNew = pParse->pNewTable;
assert( pNew );
- iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema);
- zDb = pParse->db->aDb[iDb].zName;
+ db = pParse->db;
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
+ zDb = db->aDb[iDb].zName;
zTab = pNew->zName;
pCol = &pNew->aCol[pNew->nCol-1];
pDflt = pCol->pDflt;
- pTab = sqlite3FindTable(pParse->db, zTab, zDb);
+ pTab = sqlite3FindTable(db, zTab, zDb);
assert( pTab );
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -485,8 +493,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
*/
if( pDflt ){
sqlite3_value *pVal;
- if( sqlite3ValueFromExpr(pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
- /* malloc() has failed */
+ if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
+ db->mallocFailed = 1;
return;
}
if( !pVal ){
@@ -497,20 +505,20 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
}
/* Modify the CREATE TABLE statement. */
- zCol = sqliteStrNDup((char*)pColDef->z, pColDef->n);
+ zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
if( zCol ){
char *zEnd = &zCol[pColDef->n-1];
while( (zEnd>zCol && *zEnd==';') || isspace(*(unsigned char *)zEnd) ){
*zEnd-- = '\0';
}
sqlite3NestedParse(pParse,
- "UPDATE %Q.%s SET "
- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d,length(sql)) "
+ "UPDATE \"%w\".%s SET "
+ "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
"WHERE type = 'table' AND name = %Q",
zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1,
zTab
);
- sqliteFree(zCol);
+ sqlite3DbFree(db, zCol);
}
/* If the default value of the new column is NULL, then set the file
@@ -545,11 +553,13 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
int iDb;
int i;
int nAlloc;
+ sqlite3 *db = pParse->db;
/* Look up the table being altered. */
assert( pParse->pNewTable==0 );
- if( sqlite3MallocFailed() ) goto exit_begin_add_column;
- pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ if( db->mallocFailed ) goto exit_begin_add_column;
+ pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
if( !pTab ) goto exit_begin_add_column;
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -566,33 +576,35 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
}
assert( pTab->addColOffset>0 );
- iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
/* Put a copy of the Table struct in Parse.pNewTable for the
** sqlite3AddColumn() function and friends to modify.
*/
- pNew = (Table *)sqliteMalloc(sizeof(Table));
+ pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
if( !pNew ) goto exit_begin_add_column;
pParse->pNewTable = pNew;
pNew->nRef = 1;
+ pNew->db = db;
pNew->nCol = pTab->nCol;
assert( pNew->nCol>0 );
nAlloc = (((pNew->nCol-1)/8)*8)+8;
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
- pNew->aCol = (Column *)sqliteMalloc(sizeof(Column)*nAlloc);
- pNew->zName = sqliteStrDup(pTab->zName);
+ pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
+ pNew->zName = sqlite3DbStrDup(db, pTab->zName);
if( !pNew->aCol || !pNew->zName ){
+ db->mallocFailed = 1;
goto exit_begin_add_column;
}
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
for(i=0; i<pNew->nCol; i++){
Column *pCol = &pNew->aCol[i];
- pCol->zName = sqliteStrDup(pCol->zName);
+ pCol->zName = sqlite3DbStrDup(db, pCol->zName);
pCol->zColl = 0;
pCol->zType = 0;
pCol->pDflt = 0;
}
- pNew->pSchema = pParse->db->aDb[iDb].pSchema;
+ pNew->pSchema = db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
pNew->nRef = 1;
@@ -600,10 +612,10 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
sqlite3BeginWriteOperation(pParse, 0, iDb);
v = sqlite3GetVdbe(pParse);
if( !v ) goto exit_begin_add_column;
- sqlite3ChangeCookie(pParse->db, v, iDb);
+ sqlite3ChangeCookie(pParse, iDb);
exit_begin_add_column:
- sqlite3SrcListDelete(pSrc);
+ sqlite3SrcListDelete(db, pSrc);
return;
}
#endif /* SQLITE_ALTER_TABLE */
diff --git a/third_party/sqlite/analyze.c b/third_party/sqlite/src/analyze.c
index b835aeb..2c57d7f 100644..100755
--- a/third_party/sqlite/analyze.c
+++ b/third_party/sqlite/src/analyze.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code associated with the ANALYZE command.
**
-** @(#) $Id: analyze.c,v 1.19 2007/06/20 13:37:31 drh Exp $
+** @(#) $Id: analyze.c,v 1.43 2008/07/28 19:34:53 drh Exp $
*/
#ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h"
@@ -33,21 +33,25 @@ static void openStatTable(
sqlite3 *db = pParse->db;
Db *pDb;
int iRootPage;
+ int createStat1 = 0;
Table *pStat;
Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ assert( sqlite3VdbeDb(v)==db );
pDb = &db->aDb[iDb];
if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){
/* The sqlite_stat1 tables does not exist. Create it.
** Note that a side-effect of the CREATE TABLE statement is to leave
- ** the rootpage of the new table on the top of the stack. This is
+ ** the rootpage of the new table in register pParse->regRoot. This is
** important because the OpenWrite opcode below will be needing it. */
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)",
pDb->zName
);
- iRootPage = 0; /* Cause rootpage to be taken from top of stack */
+ iRootPage = pParse->regRoot;
+ createStat1 = 1; /* Cause rootpage to be taken from top of stack */
}else if( zWhere ){
/* The sqlite_stat1 table exists. Delete all entries associated with
** the table zWhere. */
@@ -59,7 +63,7 @@ static void openStatTable(
}else{
/* The sqlite_stat1 table already exists. Delete all rows. */
iRootPage = pStat->tnum;
- sqlite3VdbeAddOp(v, OP_Clear, pStat->tnum, iDb);
+ sqlite3VdbeAddOp2(v, OP_Clear, pStat->tnum, iDb);
}
/* Open the sqlite_stat1 table for writing. Unless it was created
@@ -67,12 +71,12 @@ static void openStatTable(
** If this vdbe did create the sqlite_stat1 table, then it must have
** already obtained a schema-lock, making the write-lock redundant.
*/
- if( iRootPage>0 ){
+ if( !createStat1 ){
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
}
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- sqlite3VdbeAddOp(v, OP_OpenWrite, iStatCur, iRootPage);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, iStatCur, 3);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
+ sqlite3VdbeChangeP5(v, createStat1);
}
/*
@@ -100,7 +104,7 @@ static void analyzeOneTable(
/* Do no analysis for tables that have no indices */
return;
}
-
+ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
assert( iDb>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -116,19 +120,27 @@ static void analyzeOneTable(
iIdxCur = pParse->nTab;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
+ int regFields; /* Register block for building records */
+ int regRec; /* Register holding completed record */
+ int regTemp; /* Temporary use register */
+ int regCol; /* Content of a column from the table being analyzed */
+ int regRowid; /* Rowid for the inserted record */
+ int regF2;
/* Open a cursor to the index to be analyzed
*/
assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- VdbeComment((v, "# %s", pIdx->zName));
- sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
- (char *)pKey, P3_KEYINFO_HANDOFF);
nCol = pIdx->nColumn;
- if( iMem+nCol*2>=pParse->nMem ){
- pParse->nMem = iMem+nCol*2+1;
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1);
+ sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
+ (char *)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pIdx->zName));
+ regFields = iMem+nCol*2;
+ regTemp = regRowid = regCol = regFields+3;
+ regRec = regCol+1;
+ if( regRec>pParse->nMem ){
+ pParse->nMem = regRec;
}
- sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, nCol+1);
/* Memory cells are used as follows:
**
@@ -144,33 +156,33 @@ static void analyzeOneTable(
** are initialized to NULL.
*/
for(i=0; i<=nCol; i++){
- sqlite3VdbeAddOp(v, OP_MemInt, 0, iMem+i);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i);
}
for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp(v, OP_MemNull, iMem+nCol+i+1, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
}
/* Do the analysis.
*/
endOfLoop = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp(v, OP_Rewind, iIdxCur, endOfLoop);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
topOfLoop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, iMem);
+ sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp(v, OP_Column, iIdxCur, i);
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem+nCol+i+1, 0);
- sqlite3VdbeAddOp(v, OP_Ne, 0x100, 0);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
+ sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1);
+ /**** TODO: add collating sequence *****/
+ sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
- sqlite3VdbeAddOp(v, OP_Goto, 0, endOfLoop);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
- addr = sqlite3VdbeAddOp(v, OP_MemIncr, 1, iMem+i+1);
- sqlite3VdbeChangeP2(v, topOfLoop + 3*i + 3, addr);
- sqlite3VdbeAddOp(v, OP_Column, iIdxCur, i);
- sqlite3VdbeAddOp(v, OP_MemStore, iMem+nCol+i+1, 1);
+ sqlite3VdbeJumpHere(v, topOfLoop + 2*(i + 1));
+ sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
sqlite3VdbeResolveLabel(v, endOfLoop);
- sqlite3VdbeAddOp(v, OP_Next, iIdxCur, topOfLoop);
- sqlite3VdbeAddOp(v, OP_Close, iIdxCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
+ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
/* Store the results.
**
@@ -190,29 +202,24 @@ static void analyzeOneTable(
** If K>0 then it is always the case the D>0 so division by zero
** is never possible.
*/
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
- addr = sqlite3VdbeAddOp(v, OP_IfNot, 0, 0);
- sqlite3VdbeAddOp(v, OP_NewRowid, iStatCur, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0);
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, " ", 0);
+ addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regFields, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regFields+1, 0, pIdx->zName, 0);
+ regF2 = regFields+2;
+ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regF2);
for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem+i+1, 0);
- sqlite3VdbeAddOp(v, OP_Add, 0, 0);
- sqlite3VdbeAddOp(v, OP_AddImm, -1, 0);
- sqlite3VdbeAddOp(v, OP_MemLoad, iMem+i+1, 0);
- sqlite3VdbeAddOp(v, OP_Divide, 0, 0);
- sqlite3VdbeAddOp(v, OP_ToInt, 0, 0);
- if( i==nCol-1 ){
- sqlite3VdbeAddOp(v, OP_Concat, nCol*2-1, 0);
- }else{
- sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
- }
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
+ sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regF2, regF2);
+ sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
+ sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
+ sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
+ sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
+ sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regF2, regF2);
}
- sqlite3VdbeOp3(v, OP_MakeRecord, 3, 0, "aaa", 0);
- sqlite3VdbeAddOp(v, OP_Insert, iStatCur, OPFLAG_APPEND);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regFields, 3, regRec, "aaa", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeJumpHere(v, addr);
}
}
@@ -224,7 +231,7 @@ static void analyzeOneTable(
static void loadAnalysis(Parse *pParse, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_LoadAnalysis, iDb, 0);
+ sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb);
}
}
@@ -241,7 +248,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, 0);
- iMem = pParse->nMem;
+ iMem = pParse->nMem+1;
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, iStatCur, iMem);
@@ -258,11 +265,12 @@ static void analyzeTable(Parse *pParse, Table *pTab){
int iStatCur;
assert( pTab!=0 );
+ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, pTab->zName);
- analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem);
+ analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1);
loadAnalysis(pParse, iDb);
}
@@ -288,6 +296,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
/* Read the database schema. If an error occurs, leave an error message
** and code in pParse and return NULL. */
+ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
return;
}
@@ -304,11 +313,13 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
if( iDb>=0 ){
analyzeDatabase(pParse, iDb);
}else{
- z = sqlite3NameFromToken(pName1);
- pTab = sqlite3LocateTable(pParse, z, 0);
- sqliteFree(z);
- if( pTab ){
- analyzeTable(pParse, pTab);
+ z = sqlite3NameFromToken(db, pName1);
+ if( z ){
+ pTab = sqlite3LocateTable(pParse, 0, z, 0);
+ sqlite3DbFree(db, z);
+ if( pTab ){
+ analyzeTable(pParse, pTab);
+ }
}
}
}else{
@@ -316,10 +327,10 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
if( iDb>=0 ){
zDb = db->aDb[iDb].zName;
- z = sqlite3NameFromToken(pTableName);
+ z = sqlite3NameFromToken(db, pTableName);
if( z ){
- pTab = sqlite3LocateTable(pParse, z, zDb);
- sqliteFree(z);
+ pTab = sqlite3LocateTable(pParse, 0, z, zDb);
+ sqlite3DbFree(db, z);
if( pTab ){
analyzeTable(pParse, pTab);
}
@@ -382,6 +393,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
char *zSql;
int rc;
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 );
+ assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
+
/* Clear any prior statistics */
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
@@ -397,12 +412,12 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
- zSql = sqlite3MPrintf("SELECT idx, stat FROM %Q.sqlite_stat1",
+ zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1",
sInfo.zDatabase);
- sqlite3SafetyOff(db);
+ (void)sqlite3SafetyOff(db);
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
- sqlite3SafetyOn(db);
- sqliteFree(zSql);
+ (void)sqlite3SafetyOn(db);
+ sqlite3DbFree(db, zSql);
return rc;
}
diff --git a/third_party/sqlite/attach.c b/third_party/sqlite/src/attach.c
index e3645d9..b8668f5 100644..100755
--- a/third_party/sqlite/attach.c
+++ b/third_party/sqlite/src/attach.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
-** $Id: attach.c,v 1.60 2007/05/09 20:31:30 drh Exp $
+** $Id: attach.c,v 1.77 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
@@ -69,12 +69,12 @@ static void attachFunc(
){
int i;
int rc = 0;
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
const char *zName;
const char *zFile;
Db *aNew;
- char zErr[128];
char *zErrDyn = 0;
+ char zErr[128];
zFile = (const char *)sqlite3_value_text(argv[0]);
zName = (const char *)sqlite3_value_text(argv[1]);
@@ -87,10 +87,10 @@ static void attachFunc(
** * Transaction currently open
** * Specified database name already being used.
*/
- if( db->nDb>=SQLITE_MAX_ATTACHED+2 ){
+ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
sqlite3_snprintf(
sizeof(zErr), zErr, "too many attached databases - max %d",
- SQLITE_MAX_ATTACHED
+ db->aLimit[SQLITE_LIMIT_ATTACHED]
);
goto attach_error;
}
@@ -102,7 +102,8 @@ static void attachFunc(
for(i=0; i<db->nDb; i++){
char *z = db->aDb[i].zName;
if( z && zName && sqlite3StrICmp(z, zName)==0 ){
- sqlite3_snprintf(sizeof(zErr), zErr, "database %s is already in use", zName);
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "database %s is already in use", zName);
goto attach_error;
}
}
@@ -111,16 +112,12 @@ static void attachFunc(
** hash tables.
*/
if( db->aDb==db->aDbStatic ){
- aNew = sqliteMalloc( sizeof(db->aDb[0])*3 );
- if( aNew==0 ){
- return;
- }
+ aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 );
+ if( aNew==0 ) return;
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
}else{
- aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
- if( aNew==0 ){
- return;
- }
+ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
+ if( aNew==0 ) return;
}
db->aDb = aNew;
aNew = &db->aDb[db->nDb++];
@@ -130,9 +127,12 @@ static void attachFunc(
** it to obtain the database schema. At this point the schema may
** or may not be initialised.
*/
- rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE, &aNew->pBt);
+ rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE,
+ db->openFlags | SQLITE_OPEN_MAIN_DB,
+ &aNew->pBt);
if( rc==SQLITE_OK ){
- aNew->pSchema = sqlite3SchemaGet(aNew->pBt);
+ Pager *pPager;
+ aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
if( !aNew->pSchema ){
rc = SQLITE_NOMEM;
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
@@ -140,9 +140,11 @@ static void attachFunc(
"attached databases must use the same text encoding as main database");
goto attach_error;
}
- sqlite3PagerLockingMode(sqlite3BtreePager(aNew->pBt), db->dfltLockMode);
+ pPager = sqlite3BtreePager(aNew->pBt);
+ sqlite3PagerLockingMode(pPager, db->dfltLockMode);
+ sqlite3PagerJournalMode(pPager, db->dfltJournalMode);
}
- aNew->zName = sqliteStrDup(zName);
+ aNew->zName = sqlite3DbStrDup(db, zName);
aNew->safety_level = 3;
#if SQLITE_HAS_CODEC
@@ -155,7 +157,7 @@ static void attachFunc(
switch( t ){
case SQLITE_INTEGER:
case SQLITE_FLOAT:
- zErrDyn = sqliteStrDup("Invalid key value");
+ zErrDyn = sqlite3DbStrDup(db, "Invalid key value");
rc = SQLITE_ERROR;
break;
@@ -181,9 +183,11 @@ static void attachFunc(
** we found it.
*/
if( rc==SQLITE_OK ){
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
+ sqlite3BtreeEnterAll(db);
rc = sqlite3Init(db, &zErrDyn);
- sqlite3SafetyOff(db);
+ sqlite3BtreeLeaveAll(db);
+ (void)sqlite3SafetyOff(db);
}
if( rc ){
int iDb = db->nDb - 1;
@@ -195,8 +199,8 @@ static void attachFunc(
}
sqlite3ResetInternalSchema(db, 0);
db->nDb = iDb;
- if( rc==SQLITE_NOMEM ){
- sqlite3FailedMalloc();
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
+ db->mallocFailed = 1;
sqlite3_snprintf(sizeof(zErr),zErr, "out of memory");
}else{
sqlite3_snprintf(sizeof(zErr),zErr, "unable to open database: %s", zFile);
@@ -210,11 +214,12 @@ attach_error:
/* Return an error if we get here */
if( zErrDyn ){
sqlite3_result_error(context, zErrDyn, -1);
- sqliteFree(zErrDyn);
+ sqlite3DbFree(db, zErrDyn);
}else{
zErr[sizeof(zErr)-1] = 0;
sqlite3_result_error(context, zErr, -1);
}
+ if( rc ) sqlite3_result_error_code(context, rc);
}
/*
@@ -231,7 +236,7 @@ static void detachFunc(
sqlite3_value **argv
){
const char *zName = (const char *)sqlite3_value_text(argv[0]);
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
int i;
Db *pDb = 0;
char zErr[128];
@@ -290,16 +295,17 @@ static void codeAttach(
Vdbe *v;
FuncDef *pFunc;
sqlite3* db = pParse->db;
+ int regArgs;
#ifndef SQLITE_OMIT_AUTHORIZATION
- assert( sqlite3MallocFailed() || pAuthArg );
+ assert( db->mallocFailed || pAuthArg );
if( pAuthArg ){
- char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
+ char *zAuthArg = sqlite3NameFromToken(db, &pAuthArg->span);
if( !zAuthArg ){
goto attach_end;
}
rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
- sqliteFree(zAuthArg);
+ sqlite3DbFree(db, zAuthArg);
if(rc!=SQLITE_OK ){
goto attach_end;
}
@@ -319,27 +325,29 @@ static void codeAttach(
}
v = sqlite3GetVdbe(pParse);
- sqlite3ExprCode(pParse, pFilename);
- sqlite3ExprCode(pParse, pDbname);
- sqlite3ExprCode(pParse, pKey);
+ regArgs = sqlite3GetTempRange(pParse, 4);
+ sqlite3ExprCode(pParse, pFilename, regArgs);
+ sqlite3ExprCode(pParse, pDbname, regArgs+1);
+ sqlite3ExprCode(pParse, pKey, regArgs+2);
- assert( v || sqlite3MallocFailed() );
+ assert( v || db->mallocFailed );
if( v ){
- sqlite3VdbeAddOp(v, OP_Function, 0, nFunc);
+ sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-nFunc, regArgs+3);
+ sqlite3VdbeChangeP5(v, nFunc);
pFunc = sqlite3FindFunction(db, zFunc, strlen(zFunc), nFunc, SQLITE_UTF8,0);
- sqlite3VdbeChangeP3(v, -1, (char *)pFunc, P3_FUNCDEF);
+ sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF);
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
** statement only). For DETACH, set it to false (expire all existing
** statements).
*/
- sqlite3VdbeAddOp(v, OP_Expire, (type==SQLITE_ATTACH), 0);
+ sqlite3VdbeAddOp1(v, OP_Expire, (type==SQLITE_ATTACH));
}
attach_end:
- sqlite3ExprDelete(pFilename);
- sqlite3ExprDelete(pDbname);
- sqlite3ExprDelete(pKey);
+ sqlite3ExprDelete(db, pFilename);
+ sqlite3ExprDelete(db, pDbname);
+ sqlite3ExprDelete(db, pKey);
}
/*
@@ -367,8 +375,8 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
void sqlite3AttachFunctions(sqlite3 *db){
#ifndef SQLITE_OMIT_ATTACH
static const int enc = SQLITE_UTF8;
- sqlite3CreateFunc(db, "sqlite_attach", 3, enc, db, attachFunc, 0, 0);
- sqlite3CreateFunc(db, "sqlite_detach", 1, enc, db, detachFunc, 0, 0);
+ sqlite3CreateFunc(db, "sqlite_attach", 3, enc, 0, attachFunc, 0, 0);
+ sqlite3CreateFunc(db, "sqlite_detach", 1, enc, 0, detachFunc, 0, 0);
#endif
}
@@ -424,7 +432,7 @@ int sqlite3FixSrcList(
zDb = pFix->zDb;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
if( pItem->zDatabase==0 ){
- pItem->zDatabase = sqliteStrDup(zDb);
+ pItem->zDatabase = sqlite3DbStrDup(pFix->pParse->db, zDb);
}else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){
sqlite3ErrorMsg(pFix->pParse,
"%s %T cannot reference objects in database %s",
diff --git a/third_party/sqlite/auth.c b/third_party/sqlite/src/auth.c
index 79940c2..5630c23 100644..100755
--- a/third_party/sqlite/auth.c
+++ b/third_party/sqlite/src/auth.c
@@ -14,7 +14,7 @@
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
**
-** $Id: auth.c,v 1.26 2007/05/14 11:34:47 drh Exp $
+** $Id: auth.c,v 1.29 2007/09/18 15:55:07 drh Exp $
*/
#include "sqliteInt.h"
@@ -74,9 +74,11 @@ int sqlite3_set_authorizer(
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pArg
){
+ sqlite3_mutex_enter(db->mutex);
db->xAuth = xAuth;
db->pAuthArg = pArg;
sqlite3ExpirePreparedStatements(db);
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -103,11 +105,12 @@ static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
void sqlite3AuthRead(
Parse *pParse, /* The parser context */
Expr *pExpr, /* The expression to check authorization on */
+ Schema *pSchema, /* The schema of the expression */
SrcList *pTabList /* All table that pExpr might refer to */
){
sqlite3 *db = pParse->db;
int rc;
- Table *pTab; /* The table being read */
+ Table *pTab = 0; /* The table being read */
const char *zCol; /* Name of the column of the table */
int iSrc; /* Index in pTabList->a[] of table being read */
const char *zDBase; /* Name of database being accessed */
@@ -116,7 +119,7 @@ void sqlite3AuthRead(
if( db->xAuth==0 ) return;
if( pExpr->op!=TK_COLUMN ) return;
- iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema);
+ iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
/* An attempt to read a column out of a subquery or other
** temporary table. */
@@ -133,8 +136,6 @@ void sqlite3AuthRead(
*/
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
pTab = pStack->pTab;
- }else{
- return;
}
if( pTab==0 ) return;
if( pExpr->iColumn>=0 ){
diff --git a/third_party/sqlite/src/bitvec.c b/third_party/sqlite/src/bitvec.c
new file mode 100755
index 0000000..e911315
--- /dev/null
+++ b/third_party/sqlite/src/bitvec.c
@@ -0,0 +1,325 @@
+/*
+** 2008 February 16
+**
+** 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 an object that represents a fixed-length
+** bitmap. Bits are numbered starting with 1.
+**
+** A bitmap is used to record what pages a database file have been
+** journalled during a transaction. Usually only a few pages are
+** journalled. So the bitmap is usually sparse and has low cardinality.
+** But sometimes (for example when during a DROP of a large table) most
+** or all of the pages get journalled. In those cases, the bitmap becomes
+** dense. The algorithm needs to handle both cases well.
+**
+** The size of the bitmap is fixed when the object is created.
+**
+** All bits are clear when the bitmap is created. Individual bits
+** may be set or cleared one at a time.
+**
+** Test operations are about 100 times more common that set operations.
+** Clear operations are exceedingly rare. There are usually between
+** 5 and 500 set operations per Bitvec object, though the number of sets can
+** sometimes grow into tens of thousands or larger. The size of the
+** Bitvec object is the number of pages in the database file at the
+** start of a transaction, and is thus usually less than a few thousand,
+** but can be as large as 2 billion for a really big database.
+**
+** @(#) $Id: bitvec.c,v 1.6 2008/06/20 14:59:51 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+#define BITVEC_SZ 512
+/* Round the union size down to the nearest pointer boundary, since that's how
+** it will be aligned within the Bitvec struct. */
+#define BITVEC_USIZE (((BITVEC_SZ-12)/sizeof(Bitvec*))*sizeof(Bitvec*))
+#define BITVEC_NCHAR BITVEC_USIZE
+#define BITVEC_NBIT (BITVEC_NCHAR*8)
+#define BITVEC_NINT (BITVEC_USIZE/4)
+#define BITVEC_MXHASH (BITVEC_NINT/2)
+#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
+
+#define BITVEC_HASH(X) (((X)*37)%BITVEC_NINT)
+
+/*
+** A bitmap is an instance of the following structure.
+**
+** This bitmap records the existance of zero or more bits
+** with values between 1 and iSize, inclusive.
+**
+** There are three possible representations of the bitmap.
+** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight
+** bitmap. The least significant bit is bit 1.
+**
+** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is
+** a hash table that will hold up to BITVEC_MXHASH distinct values.
+**
+** Otherwise, the value i is redirected into one of BITVEC_NPTR
+** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap
+** handles up to iDivisor separate values of i. apSub[0] holds
+** values between 1 and iDivisor. apSub[1] holds values between
+** iDivisor+1 and 2*iDivisor. apSub[N] holds values between
+** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized
+** to hold deal with values between 1 and iDivisor.
+*/
+struct Bitvec {
+ u32 iSize; /* Maximum bit index */
+ u32 nSet; /* Number of bits that are set */
+ u32 iDivisor; /* Number of bits handled by each apSub[] entry */
+ union {
+ u8 aBitmap[BITVEC_NCHAR]; /* Bitmap representation */
+ u32 aHash[BITVEC_NINT]; /* Hash table representation */
+ Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
+ } u;
+};
+
+/*
+** Create a new bitmap object able to handle bits between 0 and iSize,
+** inclusive. Return a pointer to the new object. Return NULL if
+** malloc fails.
+*/
+Bitvec *sqlite3BitvecCreate(u32 iSize){
+ Bitvec *p;
+ assert( sizeof(*p)==BITVEC_SZ );
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+ p->iSize = iSize;
+ }
+ return p;
+}
+
+/*
+** Check to see if the i-th bit is set. Return true or false.
+** If p is NULL (if the bitmap has not been created) or if
+** i is out of range, then return false.
+*/
+int sqlite3BitvecTest(Bitvec *p, u32 i){
+ if( p==0 ) return 0;
+ if( i>p->iSize || i==0 ) return 0;
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ return (p->u.aBitmap[i/8] & (1<<(i&7)))!=0;
+ }
+ if( p->iDivisor>0 ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ return sqlite3BitvecTest(p->u.apSub[bin], i);
+ }else{
+ u32 h = BITVEC_HASH(i);
+ while( p->u.aHash[h] ){
+ if( p->u.aHash[h]==i ) return 1;
+ h++;
+ if( h>=BITVEC_NINT ) h = 0;
+ }
+ return 0;
+ }
+}
+
+/*
+** Set the i-th bit. Return 0 on success and an error code if
+** anything goes wrong.
+*/
+int sqlite3BitvecSet(Bitvec *p, u32 i){
+ u32 h;
+ assert( p!=0 );
+ assert( i>0 );
+ assert( i<=p->iSize );
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ p->u.aBitmap[i/8] |= 1 << (i&7);
+ return SQLITE_OK;
+ }
+ if( p->iDivisor ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ if( p->u.apSub[bin]==0 ){
+ sqlite3BeginBenignMalloc();
+ p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
+ sqlite3EndBenignMalloc();
+ if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
+ }
+ return sqlite3BitvecSet(p->u.apSub[bin], i);
+ }
+ h = BITVEC_HASH(i);
+ while( p->u.aHash[h] ){
+ if( p->u.aHash[h]==i ) return SQLITE_OK;
+ h++;
+ if( h==BITVEC_NINT ) h = 0;
+ }
+ p->nSet++;
+ if( p->nSet>=BITVEC_MXHASH ){
+ int j, rc;
+ u32 aiValues[BITVEC_NINT];
+ memcpy(aiValues, p->u.aHash, sizeof(aiValues));
+ memset(p->u.apSub, 0, sizeof(p->u.apSub[0])*BITVEC_NPTR);
+ p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
+ rc = sqlite3BitvecSet(p, i);
+ for(j=0; j<BITVEC_NINT; j++){
+ if( aiValues[j] ) rc |= sqlite3BitvecSet(p, aiValues[j]);
+ }
+ return rc;
+ }
+ p->u.aHash[h] = i;
+ return SQLITE_OK;
+}
+
+/*
+** Clear the i-th bit. Return 0 on success and an error code if
+** anything goes wrong.
+*/
+void sqlite3BitvecClear(Bitvec *p, u32 i){
+ assert( p!=0 );
+ assert( i>0 );
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ p->u.aBitmap[i/8] &= ~(1 << (i&7));
+ }else if( p->iDivisor ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ if( p->u.apSub[bin] ){
+ sqlite3BitvecClear(p->u.apSub[bin], i);
+ }
+ }else{
+ int j;
+ u32 aiValues[BITVEC_NINT];
+ memcpy(aiValues, p->u.aHash, sizeof(aiValues));
+ memset(p->u.aHash, 0, sizeof(p->u.aHash[0])*BITVEC_NINT);
+ p->nSet = 0;
+ for(j=0; j<BITVEC_NINT; j++){
+ if( aiValues[j] && aiValues[j]!=i ){
+ sqlite3BitvecSet(p, aiValues[j]);
+ }
+ }
+ }
+}
+
+/*
+** Destroy a bitmap object. Reclaim all memory used.
+*/
+void sqlite3BitvecDestroy(Bitvec *p){
+ if( p==0 ) return;
+ if( p->iDivisor ){
+ int i;
+ for(i=0; i<BITVEC_NPTR; i++){
+ sqlite3BitvecDestroy(p->u.apSub[i]);
+ }
+ }
+ sqlite3_free(p);
+}
+
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+/*
+** Let V[] be an array of unsigned characters sufficient to hold
+** up to N bits. Let I be an integer between 0 and N. 0<=I<N.
+** Then the following macros can be used to set, clear, or test
+** individual bits within V.
+*/
+#define SETBIT(V,I) V[I>>3] |= (1<<(I&7))
+#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7))
+#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
+
+/*
+** This routine runs an extensive test of the Bitvec code.
+**
+** The input is an array of integers that acts as a program
+** to test the Bitvec. The integers are opcodes followed
+** by 0, 1, or 3 operands, depending on the opcode. Another
+** opcode follows immediately after the last operand.
+**
+** There are 6 opcodes numbered from 0 through 5. 0 is the
+** "halt" opcode and causes the test to end.
+**
+** 0 Halt and return the number of errors
+** 1 N S X Set N bits beginning with S and incrementing by X
+** 2 N S X Clear N bits beginning with S and incrementing by X
+** 3 N Set N randomly chosen bits
+** 4 N Clear N randomly chosen bits
+** 5 N S X Set N bits from S increment X in array only, not in bitvec
+**
+** The opcodes 1 through 4 perform set and clear operations are performed
+** on both a Bitvec object and on a linear array of bits obtained from malloc.
+** Opcode 5 works on the linear array only, not on the Bitvec.
+** Opcode 5 is used to deliberately induce a fault in order to
+** confirm that error detection works.
+**
+** At the conclusion of the test the linear array is compared
+** against the Bitvec object. If there are any differences,
+** an error is returned. If they are the same, zero is returned.
+**
+** If a memory allocation error occurs, return -1.
+*/
+int sqlite3BitvecBuiltinTest(int sz, int *aOp){
+ Bitvec *pBitvec = 0;
+ unsigned char *pV = 0;
+ int rc = -1;
+ int i, nx, pc, op;
+
+ /* Allocate the Bitvec to be tested and a linear array of
+ ** bits to act as the reference */
+ pBitvec = sqlite3BitvecCreate( sz );
+ pV = sqlite3_malloc( (sz+7)/8 + 1 );
+ if( pBitvec==0 || pV==0 ) goto bitvec_end;
+ memset(pV, 0, (sz+7)/8 + 1);
+
+ /* Run the program */
+ pc = 0;
+ while( (op = aOp[pc])!=0 ){
+ switch( op ){
+ case 1:
+ case 2:
+ case 5: {
+ nx = 4;
+ i = aOp[pc+2] - 1;
+ aOp[pc+2] += aOp[pc+3];
+ break;
+ }
+ case 3:
+ case 4:
+ default: {
+ nx = 2;
+ sqlite3_randomness(sizeof(i), &i);
+ break;
+ }
+ }
+ if( (--aOp[pc+1]) > 0 ) nx = 0;
+ pc += nx;
+ i = (i & 0x7fffffff)%sz;
+ if( (op & 1)!=0 ){
+ SETBIT(pV, (i+1));
+ if( op!=5 ){
+ if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end;
+ }
+ }else{
+ CLEARBIT(pV, (i+1));
+ sqlite3BitvecClear(pBitvec, i+1);
+ }
+ }
+
+ /* Test to make sure the linear array exactly matches the
+ ** Bitvec object. Start with the assumption that they do
+ ** match (rc==0). Change rc to non-zero if a discrepancy
+ ** is found.
+ */
+ rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
+ + sqlite3BitvecTest(pBitvec, 0);
+ for(i=1; i<=sz; i++){
+ if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
+ rc = i;
+ break;
+ }
+ }
+
+ /* Free allocated structure */
+bitvec_end:
+ sqlite3_free(pV);
+ sqlite3BitvecDestroy(pBitvec);
+ return rc;
+}
+#endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --git a/third_party/sqlite/src/btmutex.c b/third_party/sqlite/src/btmutex.c
new file mode 100755
index 0000000..bf63617
--- /dev/null
+++ b/third_party/sqlite/src/btmutex.c
@@ -0,0 +1,317 @@
+/*
+** 2007 August 27
+**
+** 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.
+**
+*************************************************************************
+**
+** $Id: btmutex.c,v 1.10 2008/07/14 19:39:17 drh Exp $
+**
+** This file contains code used to implement mutexes on Btree objects.
+** This code really belongs in btree.c. But btree.c is getting too
+** big and we want to break it down some. This packaged seemed like
+** a good breakout.
+*/
+#include "btreeInt.h"
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
+
+
+/*
+** Enter a mutex on the given BTree object.
+**
+** If the object is not sharable, then no mutex is ever required
+** and this routine is a no-op. The underlying mutex is non-recursive.
+** But we keep a reference count in Btree.wantToLock so the behavior
+** of this interface is recursive.
+**
+** To avoid deadlocks, multiple Btrees are locked in the same order
+** by all database connections. The p->pNext is a list of other
+** Btrees belonging to the same database connection as the p Btree
+** which need to be locked after p. If we cannot get a lock on
+** p, then first unlock all of the others on p->pNext, then wait
+** for the lock to become available on p, then relock all of the
+** subsequent Btrees that desire a lock.
+*/
+void sqlite3BtreeEnter(Btree *p){
+ Btree *pLater;
+
+ /* Some basic sanity checking on the Btree. The list of Btrees
+ ** connected by pNext and pPrev should be in sorted order by
+ ** Btree.pBt value. All elements of the list should belong to
+ ** the same connection. Only shared Btrees are on the list. */
+ assert( p->pNext==0 || p->pNext->pBt>p->pBt );
+ assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
+ assert( p->pNext==0 || p->pNext->db==p->db );
+ assert( p->pPrev==0 || p->pPrev->db==p->db );
+ assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
+
+ /* Check for locking consistency */
+ assert( !p->locked || p->wantToLock>0 );
+ assert( p->sharable || p->wantToLock==0 );
+
+ /* We should already hold a lock on the database connection */
+ assert( sqlite3_mutex_held(p->db->mutex) );
+
+ if( !p->sharable ) return;
+ p->wantToLock++;
+ if( p->locked ) return;
+
+#ifndef SQLITE_MUTEX_NOOP
+ /* In most cases, we should be able to acquire the lock we
+ ** want without having to go throught the ascending lock
+ ** procedure that follows. Just be sure not to block.
+ */
+ if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
+ p->locked = 1;
+ return;
+ }
+
+ /* To avoid deadlock, first release all locks with a larger
+ ** BtShared address. Then acquire our lock. Then reacquire
+ ** the other BtShared locks that we used to hold in ascending
+ ** order.
+ */
+ for(pLater=p->pNext; pLater; pLater=pLater->pNext){
+ assert( pLater->sharable );
+ assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
+ assert( !pLater->locked || pLater->wantToLock>0 );
+ if( pLater->locked ){
+ sqlite3_mutex_leave(pLater->pBt->mutex);
+ pLater->locked = 0;
+ }
+ }
+ sqlite3_mutex_enter(p->pBt->mutex);
+ p->locked = 1;
+ for(pLater=p->pNext; pLater; pLater=pLater->pNext){
+ if( pLater->wantToLock ){
+ sqlite3_mutex_enter(pLater->pBt->mutex);
+ pLater->locked = 1;
+ }
+ }
+#endif /* SQLITE_MUTEX_NOOP */
+}
+
+/*
+** Exit the recursive mutex on a Btree.
+*/
+void sqlite3BtreeLeave(Btree *p){
+ if( p->sharable ){
+ assert( p->wantToLock>0 );
+ p->wantToLock--;
+ if( p->wantToLock==0 ){
+ assert( p->locked );
+ sqlite3_mutex_leave(p->pBt->mutex);
+ p->locked = 0;
+ }
+ }
+}
+
+#ifndef NDEBUG
+/*
+** Return true if the BtShared mutex is held on the btree.
+**
+** This routine makes no determination one why or another if the
+** database connection mutex is held.
+**
+** This routine is used only from within assert() statements.
+*/
+int sqlite3BtreeHoldsMutex(Btree *p){
+ return (p->sharable==0 ||
+ (p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex)));
+}
+#endif
+
+
+#ifndef SQLITE_OMIT_INCRBLOB
+/*
+** Enter and leave a mutex on a Btree given a cursor owned by that
+** Btree. These entry points are used by incremental I/O and can be
+** omitted if that module is not used.
+*/
+void sqlite3BtreeEnterCursor(BtCursor *pCur){
+ sqlite3BtreeEnter(pCur->pBtree);
+}
+void sqlite3BtreeLeaveCursor(BtCursor *pCur){
+ sqlite3BtreeLeave(pCur->pBtree);
+}
+#endif /* SQLITE_OMIT_INCRBLOB */
+
+
+/*
+** Enter the mutex on every Btree associated with a database
+** connection. This is needed (for example) prior to parsing
+** a statement since we will be comparing table and column names
+** against all schemas and we do not want those schemas being
+** reset out from under us.
+**
+** There is a corresponding leave-all procedures.
+**
+** Enter the mutexes in accending order by BtShared pointer address
+** to avoid the possibility of deadlock when two threads with
+** two or more btrees in common both try to lock all their btrees
+** at the same instant.
+*/
+void sqlite3BtreeEnterAll(sqlite3 *db){
+ int i;
+ Btree *p, *pLater;
+ assert( sqlite3_mutex_held(db->mutex) );
+ for(i=0; i<db->nDb; i++){
+ p = db->aDb[i].pBt;
+ if( p && p->sharable ){
+ p->wantToLock++;
+ if( !p->locked ){
+ assert( p->wantToLock==1 );
+ while( p->pPrev ) p = p->pPrev;
+ while( p->locked && p->pNext ) p = p->pNext;
+ for(pLater = p->pNext; pLater; pLater=pLater->pNext){
+ if( pLater->locked ){
+ sqlite3_mutex_leave(pLater->pBt->mutex);
+ pLater->locked = 0;
+ }
+ }
+ while( p ){
+ sqlite3_mutex_enter(p->pBt->mutex);
+ p->locked++;
+ p = p->pNext;
+ }
+ }
+ }
+ }
+}
+void sqlite3BtreeLeaveAll(sqlite3 *db){
+ int i;
+ Btree *p;
+ assert( sqlite3_mutex_held(db->mutex) );
+ for(i=0; i<db->nDb; i++){
+ p = db->aDb[i].pBt;
+ if( p && p->sharable ){
+ assert( p->wantToLock>0 );
+ p->wantToLock--;
+ if( p->wantToLock==0 ){
+ assert( p->locked );
+ sqlite3_mutex_leave(p->pBt->mutex);
+ p->locked = 0;
+ }
+ }
+ }
+}
+
+#ifndef NDEBUG
+/*
+** Return true if the current thread holds the database connection
+** mutex and all required BtShared mutexes.
+**
+** This routine is used inside assert() statements only.
+*/
+int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
+ int i;
+ if( !sqlite3_mutex_held(db->mutex) ){
+ return 0;
+ }
+ for(i=0; i<db->nDb; i++){
+ Btree *p;
+ p = db->aDb[i].pBt;
+ if( p && p->sharable &&
+ (p->wantToLock==0 || !sqlite3_mutex_held(p->pBt->mutex)) ){
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif /* NDEBUG */
+
+/*
+** Add a new Btree pointer to a BtreeMutexArray.
+** if the pointer can possibly be shared with
+** another database connection.
+**
+** The pointers are kept in sorted order by pBtree->pBt. That
+** way when we go to enter all the mutexes, we can enter them
+** in order without every having to backup and retry and without
+** worrying about deadlock.
+**
+** The number of shared btrees will always be small (usually 0 or 1)
+** so an insertion sort is an adequate algorithm here.
+*/
+void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
+ int i, j;
+ BtShared *pBt;
+ if( pBtree==0 || pBtree->sharable==0 ) return;
+#ifndef NDEBUG
+ {
+ for(i=0; i<pArray->nMutex; i++){
+ assert( pArray->aBtree[i]!=pBtree );
+ }
+ }
+#endif
+ assert( pArray->nMutex>=0 );
+ assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 );
+ pBt = pBtree->pBt;
+ for(i=0; i<pArray->nMutex; i++){
+ assert( pArray->aBtree[i]!=pBtree );
+ if( pArray->aBtree[i]->pBt>pBt ){
+ for(j=pArray->nMutex; j>i; j--){
+ pArray->aBtree[j] = pArray->aBtree[j-1];
+ }
+ pArray->aBtree[i] = pBtree;
+ pArray->nMutex++;
+ return;
+ }
+ }
+ pArray->aBtree[pArray->nMutex++] = pBtree;
+}
+
+/*
+** Enter the mutex of every btree in the array. This routine is
+** called at the beginning of sqlite3VdbeExec(). The mutexes are
+** exited at the end of the same function.
+*/
+void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
+ int i;
+ for(i=0; i<pArray->nMutex; i++){
+ Btree *p = pArray->aBtree[i];
+ /* Some basic sanity checking */
+ assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
+ assert( !p->locked || p->wantToLock>0 );
+
+ /* We should already hold a lock on the database connection */
+ assert( sqlite3_mutex_held(p->db->mutex) );
+
+ p->wantToLock++;
+ if( !p->locked && p->sharable ){
+ sqlite3_mutex_enter(p->pBt->mutex);
+ p->locked = 1;
+ }
+ }
+}
+
+/*
+** Leave the mutex of every btree in the group.
+*/
+void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
+ int i;
+ for(i=0; i<pArray->nMutex; i++){
+ Btree *p = pArray->aBtree[i];
+ /* Some basic sanity checking */
+ assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
+ assert( p->locked || !p->sharable );
+ assert( p->wantToLock>0 );
+
+ /* We should already hold a lock on the database connection */
+ assert( sqlite3_mutex_held(p->db->mutex) );
+
+ p->wantToLock--;
+ if( p->wantToLock==0 && p->locked ){
+ sqlite3_mutex_leave(p->pBt->mutex);
+ p->locked = 0;
+ }
+ }
+}
+
+
+#endif /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */
diff --git a/third_party/sqlite/btree.c b/third_party/sqlite/src/btree.c
index 1e3a43e..dc71523 100644..100755
--- a/third_party/sqlite/btree.c
+++ b/third_party/sqlite/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.396 2007/08/13 14:56:44 drh Exp $
+** $Id: btree.c,v 1.495 2008/08/02 17:36:46 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
@@ -23,19 +23,61 @@
*/
static const char zMagicHeader[] = SQLITE_FILE_HEADER;
+/*
+** The header string that appears at the beginning of a SQLite
+** database which has been poisoned.
+*/
+static const char zPoisonHeader[] = "SQLite poison 3";
/*
** Set this global variable to 1 to enable tracing using the TRACE
** macro.
*/
-#if SQLITE_TEST
-int sqlite3_btree_trace=0; /* True to enable tracing */
+#if 0
+int sqlite3BtreeTrace=0; /* True to enable tracing */
+# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
+#else
+# define TRACE(X)
+#endif
+
+
+
+#ifndef SQLITE_OMIT_SHARED_CACHE
+/*
+** A flag to indicate whether or not shared cache is enabled. Also,
+** a list of BtShared objects that are eligible for participation
+** in shared cache. The variables have file scope during normal builds,
+** but the test harness needs to access these variables so we make them
+** global for test builds.
+*/
+#ifdef SQLITE_TEST
+BtShared *sqlite3SharedCacheList = 0;
+int sqlite3SharedCacheEnabled = 0;
+#else
+static BtShared *sqlite3SharedCacheList = 0;
+static int sqlite3SharedCacheEnabled = 0;
+#endif
+#endif /* SQLITE_OMIT_SHARED_CACHE */
+
+#ifndef SQLITE_OMIT_SHARED_CACHE
+/*
+** Enable or disable the shared pager and schema features.
+**
+** This routine has no effect on existing database connections.
+** The shared cache setting effects only future calls to
+** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2().
+*/
+int sqlite3_enable_shared_cache(int enable){
+ sqlite3SharedCacheEnabled = enable;
+ return SQLITE_OK;
+}
#endif
+
/*
** Forward declaration
*/
-static int checkReadLocks(Btree*,Pgno,BtCursor*);
+static int checkReadLocks(Btree*, Pgno, BtCursor*, i64);
#ifdef SQLITE_OMIT_SHARED_CACHE
@@ -50,8 +92,9 @@ static int checkReadLocks(Btree*,Pgno,BtCursor*);
#define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK
#define unlockAllTables(a)
-#else
+#endif
+#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Query to see if btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
@@ -62,11 +105,22 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
BtShared *pBt = p->pBt;
BtLock *pIter;
+ assert( sqlite3BtreeHoldsMutex(p) );
+ assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
+ assert( p->db!=0 );
+
/* This is a no-op if the shared-cache is not enabled */
- if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){
+ if( !p->sharable ){
return SQLITE_OK;
}
+ /* If some other connection is holding an exclusive lock, the
+ ** requested lock may not be obtained.
+ */
+ if( pBt->pExclusive && pBt->pExclusive!=p ){
+ return SQLITE_LOCKED;
+ }
+
/* This (along with lockTable()) is where the ReadUncommitted flag is
** dealt with. If the caller is querying for a read-lock and the flag is
** set, it is unconditionally granted - even if there are write-locks
@@ -82,8 +136,7 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
** write-cursor does not change.
*/
if(
- !p->pSqlite ||
- 0==(p->pSqlite->flags&SQLITE_ReadUncommitted) ||
+ 0==(p->db->flags&SQLITE_ReadUncommitted) ||
eLock==WRITE_LOCK ||
iTab==MASTER_ROOT
){
@@ -96,7 +149,9 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
}
return SQLITE_OK;
}
+#endif /* !SQLITE_OMIT_SHARED_CACHE */
+#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Add a lock on the table with root-page iTable to the shared-btree used
** by Btree handle p. Parameter eLock must be either READ_LOCK or
@@ -110,8 +165,12 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ assert( sqlite3BtreeHoldsMutex(p) );
+ assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
+ assert( p->db!=0 );
+
/* This is a no-op if the shared-cache is not enabled */
- if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){
+ if( !p->sharable ){
return SQLITE_OK;
}
@@ -123,8 +182,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
** the ReadUncommitted flag.
*/
if(
- (p->pSqlite) &&
- (p->pSqlite->flags&SQLITE_ReadUncommitted) &&
+ (p->db->flags&SQLITE_ReadUncommitted) &&
(eLock==READ_LOCK) &&
iTable!=MASTER_ROOT
){
@@ -143,7 +201,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
** with table iTable, allocate one and link it into the list.
*/
if( !pLock ){
- pLock = (BtLock *)sqliteMalloc(sizeof(BtLock));
+ pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
if( !pLock ){
return SQLITE_NOMEM;
}
@@ -164,40 +222,56 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
return SQLITE_OK;
}
+#endif /* !SQLITE_OMIT_SHARED_CACHE */
+#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Release all the table locks (locks obtained via calls to the lockTable()
** procedure) held by Btree handle p.
*/
static void unlockAllTables(Btree *p){
- BtLock **ppIter = &p->pBt->pLock;
+ BtShared *pBt = p->pBt;
+ BtLock **ppIter = &pBt->pLock;
- /* If the shared-cache extension is not enabled, there should be no
- ** locks in the BtShared.pLock list, making this procedure a no-op. Assert
- ** that this is the case.
- */
- assert( sqlite3ThreadDataReadOnly()->useSharedData || 0==*ppIter );
+ assert( sqlite3BtreeHoldsMutex(p) );
+ assert( p->sharable || 0==*ppIter );
while( *ppIter ){
BtLock *pLock = *ppIter;
+ assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree );
if( pLock->pBtree==p ){
*ppIter = pLock->pNext;
- sqliteFree(pLock);
+ sqlite3_free(pLock);
}else{
ppIter = &pLock->pNext;
}
}
+
+ if( pBt->pExclusive==p ){
+ pBt->pExclusive = 0;
+ }
}
#endif /* SQLITE_OMIT_SHARED_CACHE */
static void releasePage(MemPage *pPage); /* Forward reference */
+/*
+** Verify that the cursor holds a mutex on the BtShared
+*/
+#ifndef NDEBUG
+static int cursorHoldsMutex(BtCursor *p){
+ return sqlite3_mutex_held(p->pBt->mutex);
+}
+#endif
+
+
#ifndef SQLITE_OMIT_INCRBLOB
/*
** Invalidate the overflow page-list cache for cursor pCur, if any.
*/
static void invalidateOverflowCache(BtCursor *pCur){
- sqliteFree(pCur->aOverflow);
+ assert( cursorHoldsMutex(pCur) );
+ sqlite3_free(pCur->aOverflow);
pCur->aOverflow = 0;
}
@@ -207,6 +281,7 @@ static void invalidateOverflowCache(BtCursor *pCur){
*/
static void invalidateAllOverflowCache(BtShared *pBt){
BtCursor *p;
+ assert( sqlite3_mutex_held(pBt->mutex) );
for(p=pBt->pCursor; p; p=p->pNext){
invalidateOverflowCache(p);
}
@@ -225,6 +300,7 @@ static int saveCursorPosition(BtCursor *pCur){
assert( CURSOR_VALID==pCur->eState );
assert( 0==pCur->pKey );
+ assert( cursorHoldsMutex(pCur) );
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
@@ -235,13 +311,13 @@ static int saveCursorPosition(BtCursor *pCur){
** data.
*/
if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
- void *pKey = sqliteMalloc(pCur->nKey);
+ void *pKey = sqlite3Malloc(pCur->nKey);
if( pKey ){
rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
if( rc==SQLITE_OK ){
pCur->pKey = pKey;
}else{
- sqliteFree(pKey);
+ sqlite3_free(pKey);
}
}else{
rc = SQLITE_NOMEM;
@@ -266,6 +342,8 @@ static int saveCursorPosition(BtCursor *pCur){
*/
static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
BtCursor *p;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( pExcept==0 || pExcept->pBt==pBt );
for(p=pBt->pCursor; p; p=p->pNext){
if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
p->eState==CURSOR_VALID ){
@@ -282,7 +360,8 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
** Clear the current cursor position.
*/
static void clearCursorPosition(BtCursor *pCur){
- sqliteFree(pCur->pKey);
+ assert( cursorHoldsMutex(pCur) );
+ sqlite3_free(pCur->pKey);
pCur->pKey = 0;
pCur->eState = CURSOR_INVALID;
}
@@ -291,36 +370,55 @@ static void clearCursorPosition(BtCursor *pCur){
** Restore the cursor to the position it was in (or as close to as possible)
** when saveCursorPosition() was called. Note that this call deletes the
** saved position info stored by saveCursorPosition(), so there can be
-** at most one effective restoreOrClearCursorPosition() call after each
+** at most one effective restoreCursorPosition() call after each
** saveCursorPosition().
-**
-** If the second argument argument - doSeek - is false, then instead of
-** returning the cursor to it's saved position, any saved position is deleted
-** and the cursor state set to CURSOR_INVALID.
*/
-int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
+int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur){
int rc;
- assert( pCur->eState==CURSOR_REQUIRESEEK );
-#ifndef SQLITE_OMIT_INCRBLOB
- if( pCur->isIncrblobHandle ){
- return SQLITE_ABORT;
+ assert( cursorHoldsMutex(pCur) );
+ assert( pCur->eState>=CURSOR_REQUIRESEEK );
+ if( pCur->eState==CURSOR_FAULT ){
+ return pCur->skip;
}
-#endif
pCur->eState = CURSOR_INVALID;
- rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip);
+ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, 0, pCur->nKey, 0, &pCur->skip);
if( rc==SQLITE_OK ){
- sqliteFree(pCur->pKey);
+ sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
}
return rc;
}
-#define restoreOrClearCursorPosition(p) \
- (p->eState==CURSOR_REQUIRESEEK ? \
- sqlite3BtreeRestoreOrClearCursorPosition(p) : \
+#define restoreCursorPosition(p) \
+ (p->eState>=CURSOR_REQUIRESEEK ? \
+ sqlite3BtreeRestoreCursorPosition(p) : \
SQLITE_OK)
+/*
+** Determine whether or not a cursor has moved from the position it
+** was last placed at. Cursor can move when the row they are pointing
+** at is deleted out from under them.
+**
+** This routine returns an error code if something goes wrong. The
+** integer *pHasMoved is set to one if the cursor has moved and 0 if not.
+*/
+int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
+ int rc;
+
+ rc = restoreCursorPosition(pCur);
+ if( rc ){
+ *pHasMoved = 1;
+ return rc;
+ }
+ if( pCur->eState!=CURSOR_VALID || pCur->skip!=0 ){
+ *pHasMoved = 1;
+ }else{
+ *pHasMoved = 0;
+ }
+ return SQLITE_OK;
+}
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
@@ -328,9 +426,11 @@ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
** input page number.
*/
static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
- int nPagesPerMapPage = (pBt->usableSize/5)+1;
- int iPtrMap = (pgno-2)/nPagesPerMapPage;
- int ret = (iPtrMap*nPagesPerMapPage) + 2;
+ int nPagesPerMapPage, iPtrMap, ret;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ nPagesPerMapPage = (pBt->usableSize/5)+1;
+ iPtrMap = (pgno-2)/nPagesPerMapPage;
+ ret = (iPtrMap*nPagesPerMapPage) + 2;
if( ret==PENDING_BYTE_PAGE(pBt) ){
ret++;
}
@@ -351,6 +451,7 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
int offset; /* Offset in pointer map page */
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
/* The master-journal page number must never be used as a pointer map page */
assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
@@ -363,7 +464,7 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
if( rc!=SQLITE_OK ){
return rc;
}
- offset = PTRMAP_PTROFFSET(pBt, key);
+ offset = PTRMAP_PTROFFSET(iPtrmap, key);
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
@@ -393,6 +494,8 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
int offset; /* Offset of entry in pointer map */
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+
iPtrmap = PTRMAP_PAGENO(pBt, key);
rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
if( rc!=0 ){
@@ -400,7 +503,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
}
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
- offset = PTRMAP_PTROFFSET(pBt, key);
+ offset = PTRMAP_PTROFFSET(iPtrmap, key);
assert( pEType!=0 );
*pEType = pPtrmap[offset];
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
@@ -410,7 +513,11 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
return SQLITE_OK;
}
-#endif /* SQLITE_OMIT_AUTOVACUUM */
+#else /* if defined SQLITE_OMIT_AUTOVACUUM */
+ #define ptrmapPut(w,x,y,z) SQLITE_OK
+ #define ptrmapGet(w,x,y,z) SQLITE_OK
+ #define ptrmapPutOvfl(y,z) SQLITE_OK
+#endif
/*
** Given a btree page and a cell index (0 means the first cell on
@@ -419,22 +526,16 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
**
** This routine works only for pages that do not contain overflow cells.
*/
-#define findCell(pPage, iCell) \
- ((pPage)->aData + get2byte(&(pPage)->aData[(pPage)->cellOffset+2*(iCell)]))
-#ifdef SQLITE_TEST
-u8 *sqlite3BtreeFindCell(MemPage *pPage, int iCell){
- assert( iCell>=0 );
- assert( iCell<get2byte(&pPage->aData[pPage->hdrOffset+3]) );
- return findCell(pPage, iCell);
-}
-#endif
+#define findCell(P,I) \
+ ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
/*
-** This a more complex version of sqlite3BtreeFindCell() that works for
+** This a more complex version of findCell() that works for
** pages that do contain overflow cells. See insert
*/
static u8 *findOverflowCell(MemPage *pPage, int iCell){
int i;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
for(i=pPage->nOverflow-1; i>=0; i--){
int k;
struct _OvflCell *pOvfl;
@@ -467,35 +568,36 @@ void sqlite3BtreeParseCellPtr(
int n; /* Number bytes in cell content header */
u32 nPayload; /* Number of bytes of cell payload */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
pInfo->pCell = pCell;
assert( pPage->leaf==0 || pPage->leaf==1 );
n = pPage->childPtrSize;
assert( n==4-4*pPage->leaf );
- if( pPage->hasData ){
- n += getVarint32(&pCell[n], &nPayload);
- }else{
- nPayload = 0;
- }
- pInfo->nData = nPayload;
if( pPage->intKey ){
- n += getVarint(&pCell[n], (u64 *)&pInfo->nKey);
+ if( pPage->hasData ){
+ n += getVarint32(&pCell[n], nPayload);
+ }else{
+ nPayload = 0;
+ }
+ n += getVarint(&pCell[n], (u64*)&pInfo->nKey);
+ pInfo->nData = nPayload;
}else{
- u32 x;
- n += getVarint32(&pCell[n], &x);
- pInfo->nKey = x;
- nPayload += x;
+ pInfo->nData = 0;
+ n += getVarint32(&pCell[n], nPayload);
+ pInfo->nKey = nPayload;
}
pInfo->nPayload = nPayload;
pInfo->nHeader = n;
- if( nPayload<=pPage->maxLocal ){
+ if( likely(nPayload<=pPage->maxLocal) ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
int nSize; /* Total size of cell content in bytes */
+ nSize = nPayload + n;
pInfo->nLocal = nPayload;
pInfo->iOverflow = 0;
- nSize = nPayload + n;
- if( nSize<4 ){
+ if( (nSize & ~3)==0 ){
nSize = 4; /* Minimum cell size is 4 */
}
pInfo->nSize = nSize;
@@ -542,13 +644,13 @@ void sqlite3BtreeParseCell(
** the space used by the cell pointer.
*/
#ifndef NDEBUG
-static int cellSize(MemPage *pPage, int iCell){
+static u16 cellSize(MemPage *pPage, int iCell){
CellInfo info;
sqlite3BtreeParseCell(pPage, iCell, &info);
return info.nSize;
}
#endif
-static int cellSizePtr(MemPage *pPage, u8 *pCell){
+static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
CellInfo info;
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
return info.nSize;
@@ -561,14 +663,13 @@ static int cellSizePtr(MemPage *pPage, u8 *pCell){
** for the overflow page.
*/
static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){
- if( pCell ){
- CellInfo info;
- sqlite3BtreeParseCellPtr(pPage, pCell, &info);
- assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
- if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
- Pgno ovfl = get4byte(&pCell[info.iOverflow]);
- return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno);
- }
+ CellInfo info;
+ assert( pCell!=0 );
+ sqlite3BtreeParseCellPtr(pPage, pCell, &info);
+ assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
+ if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
+ Pgno ovfl = get4byte(&pCell[info.iOverflow]);
+ return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno);
}
return SQLITE_OK;
}
@@ -579,6 +680,7 @@ static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){
*/
static int ptrmapPutOvfl(MemPage *pPage, int iCell){
u8 *pCell;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pCell = findOverflowCell(pPage, iCell);
return ptrmapPutOvflPtr(pPage, pCell);
}
@@ -591,7 +693,7 @@ static int ptrmapPutOvfl(MemPage *pPage, int iCell){
** big FreeBlk that occurs in between the header and cell
** pointer array and the cell content area.
*/
-static int defragmentPage(MemPage *pPage){
+static void defragmentPage(MemPage *pPage){
int i; /* Loop counter */
int pc; /* Address of a i-th cell */
int addr; /* Offset of first byte after cell pointer array */
@@ -608,8 +710,8 @@ static int defragmentPage(MemPage *pPage){
assert( pPage->pBt!=0 );
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
- temp = sqliteMalloc( pPage->pBt->pageSize );
- if( temp==0 ) return SQLITE_NOMEM;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
data = pPage->aData;
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
@@ -636,16 +738,14 @@ static int defragmentPage(MemPage *pPage){
data[hdr+7] = 0;
addr = cellOffset+2*nCell;
memset(&data[addr], 0, brk-addr);
- sqliteFree(temp);
- return SQLITE_OK;
}
/*
** Allocate nByte bytes of space on a page.
**
** Return the index into pPage->aData[] of the first byte of
-** the new allocation. Or return 0 if there is not enough free
-** space on the page to satisfy the allocation request.
+** the new allocation. The caller guarantees that there is enough
+** space. This routine will never fail.
**
** If the page contains nBytes of free space but does not contain
** nBytes of contiguous free space, then this routine automatically
@@ -664,8 +764,10 @@ static int allocateSpace(MemPage *pPage, int nByte){
data = pPage->aData;
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt );
- if( nByte<4 ) nByte = 4;
- if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( nByte>=0 ); /* Minimum cell size is 4 */
+ assert( pPage->nFree>=nByte );
+ assert( pPage->nOverflow==0 );
pPage->nFree -= nByte;
hdr = pPage->hdrOffset;
@@ -697,7 +799,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
nCell = get2byte(&data[hdr+3]);
cellOffset = pPage->cellOffset;
if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){
- if( defragmentPage(pPage) ) return 0;
+ defragmentPage(pPage);
top = get2byte(&data[hdr+5]);
}
top -= nByte;
@@ -722,7 +824,8 @@ static void freeSpace(MemPage *pPage, int start, int size){
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
assert( (start + size)<=pPage->pBt->usableSize );
- if( size<4 ) size = 4;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( size>=0 ); /* Minimum cell size is 4 */
#ifdef SQLITE_SECURE_DELETE
/* Overwrite deleted information with zeros when the SECURE_DELETE
@@ -777,26 +880,38 @@ static void freeSpace(MemPage *pPage, int start, int size){
/*
** Decode the flags byte (the first byte of the header) for a page
** and initialize fields of the MemPage structure accordingly.
+**
+** Only the following combinations are supported. Anything different
+** indicates a corrupt database files:
+**
+** PTF_ZERODATA
+** PTF_ZERODATA | PTF_LEAF
+** PTF_LEAFDATA | PTF_INTKEY
+** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF
*/
-static void decodeFlags(MemPage *pPage, int flagByte){
+static int decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
- pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0;
- pPage->zeroData = (flagByte & PTF_ZERODATA)!=0;
- pPage->leaf = (flagByte & PTF_LEAF)!=0;
- pPage->childPtrSize = 4*(pPage->leaf==0);
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ pPage->leaf = flagByte>>3; assert( PTF_LEAF == 1<<3 );
+ flagByte &= ~PTF_LEAF;
+ pPage->childPtrSize = 4-4*pPage->leaf;
pBt = pPage->pBt;
- if( flagByte & PTF_LEAFDATA ){
- pPage->leafData = 1;
+ if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
+ pPage->intKey = 1;
+ pPage->hasData = pPage->leaf;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
- }else{
- pPage->leafData = 0;
+ }else if( flagByte==PTF_ZERODATA ){
+ pPage->intKey = 0;
+ pPage->hasData = 0;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
}
- pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData));
+ return SQLITE_OK;
}
/*
@@ -828,8 +943,10 @@ int sqlite3BtreeInitPage(
pBt = pPage->pBt;
assert( pBt!=0 );
assert( pParent==0 || pParent->pBt==pBt );
+ assert( sqlite3_mutex_held(pBt->mutex) );
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
- assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
+ assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
+ assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
/* The parent page should never change unless the file is corrupt */
return SQLITE_CORRUPT_BKPT;
@@ -841,7 +958,9 @@ int sqlite3BtreeInitPage(
}
hdr = pPage->hdrOffset;
data = pPage->aData;
- decodeFlags(pPage, data[hdr]);
+ if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
+ assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
+ pPage->maskPage = pBt->pageSize - 1;
pPage->nOverflow = 0;
pPage->idxShift = 0;
usableSize = pBt->usableSize;
@@ -881,6 +1000,26 @@ int sqlite3BtreeInitPage(
return SQLITE_CORRUPT_BKPT;
}
+#if 0
+ /* Check that all the offsets in the cell offset array are within range.
+ **
+ ** Omitting this consistency check and using the pPage->maskPage mask
+ ** to prevent overrunning the page buffer in findCell() results in a
+ ** 2.5% performance gain.
+ */
+ {
+ u8 *pOff; /* Iterator used to check all cell offsets are in range */
+ u8 *pEnd; /* Pointer to end of cell offset array */
+ u8 mask; /* Mask of bits that must be zero in MSB of cell offsets */
+ mask = ~(((u8)(pBt->pageSize>>8))-1);
+ pEnd = &data[cellOffset + pPage->nCell*2];
+ for(pOff=&data[cellOffset]; pOff!=pEnd && !((*pOff)&mask); pOff+=2);
+ if( pOff!=pEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+#endif
+
pPage->isInit = 1;
return SQLITE_OK;
}
@@ -896,9 +1035,11 @@ static void zeroPage(MemPage *pPage, int flags){
int first;
assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
- assert( &data[pBt->pageSize] == (unsigned char*)pPage );
+ assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
+ assert( sqlite3PagerGetData(pPage->pDbPage) == data );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
- memset(&data[hdr], 0, pBt->usableSize - hdr);
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ /*memset(&data[hdr], 0, pBt->usableSize - hdr);*/
data[hdr] = flags;
first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
memset(&data[hdr+1], 0, 4);
@@ -909,6 +1050,8 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->hdrOffset = hdr;
pPage->cellOffset = first;
pPage->nOverflow = 0;
+ assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
+ pPage->maskPage = pBt->pageSize - 1;
pPage->idxShift = 0;
pPage->nCell = 0;
pPage->isInit = 1;
@@ -935,6 +1078,7 @@ int sqlite3BtreeGetPage(
MemPage *pPage;
DbPage *pDbPage;
+ assert( sqlite3_mutex_held(pBt->mutex) );
rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
if( rc ) return rc;
pPage = (MemPage *)sqlite3PagerGetExtra(pDbPage);
@@ -959,12 +1103,17 @@ static int getAndInitPage(
MemPage *pParent /* Parent of the page */
){
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno==0 ){
return SQLITE_CORRUPT_BKPT;
}
rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0);
if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
rc = sqlite3BtreeInitPage(*ppPage, pParent);
+ if( rc!=SQLITE_OK ){
+ releasePage(*ppPage);
+ *ppPage = 0;
+ }
}
return rc;
}
@@ -977,7 +1126,9 @@ static void releasePage(MemPage *pPage){
if( pPage ){
assert( pPage->aData );
assert( pPage->pBt );
- assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );
+ assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
+ assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
sqlite3PagerUnref(pPage->pDbPage);
}
}
@@ -991,8 +1142,10 @@ static void pageDestructor(DbPage *pData, int pageSize){
MemPage *pPage;
assert( (pageSize & 7)==0 );
pPage = (MemPage *)sqlite3PagerGetExtra(pData);
+ assert( pPage->isInit==0 || sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->pParent ){
MemPage *pParent = pPage->pParent;
+ assert( pParent->pBt==pPage->pBt );
pPage->pParent = 0;
releasePage(pParent);
}
@@ -1012,32 +1165,44 @@ static void pageReinit(DbPage *pData, int pageSize){
assert( (pageSize & 7)==0 );
pPage = (MemPage *)sqlite3PagerGetExtra(pData);
if( pPage->isInit ){
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pPage->isInit = 0;
sqlite3BtreeInitPage(pPage, pPage->pParent);
}
}
/*
+** Invoke the busy handler for a btree.
+*/
+static int sqlite3BtreeInvokeBusyHandler(void *pArg, int n){
+ BtShared *pBt = (BtShared*)pArg;
+ assert( pBt->db );
+ assert( sqlite3_mutex_held(pBt->db->mutex) );
+ return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
+}
+
+/*
** Open a database file.
**
** zFilename is the name of the database file. If zFilename is NULL
** a new database with a random name is created. This randomly named
** database file will be deleted when sqlite3BtreeClose() is called.
+** If zFilename is ":memory:" then an in-memory database is created
+** that is automatically destroyed when it is closed.
*/
int sqlite3BtreeOpen(
const char *zFilename, /* Name of the file containing the BTree database */
- sqlite3 *pSqlite, /* Associated database handle */
+ sqlite3 *db, /* Associated database handle */
Btree **ppBtree, /* Pointer to new Btree object written here */
- int flags /* Options */
+ int flags, /* Options */
+ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
){
- BtShared *pBt; /* Shared part of btree structure */
+ sqlite3_vfs *pVfs; /* The VFS to use for this btree */
+ BtShared *pBt = 0; /* Shared part of btree structure */
Btree *p; /* Handle to return */
int rc = SQLITE_OK;
int nReserve;
unsigned char zDbHeader[100];
-#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
- const ThreadData *pTsdro;
-#endif
/* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if
@@ -1052,113 +1217,182 @@ int sqlite3BtreeOpen(
#endif
#endif
- p = sqliteMalloc(sizeof(Btree));
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ pVfs = db->pVfs;
+ p = sqlite3MallocZero(sizeof(Btree));
if( !p ){
return SQLITE_NOMEM;
}
p->inTrans = TRANS_NONE;
- p->pSqlite = pSqlite;
+ p->db = db;
- /* Try to find an existing Btree structure opened on zFilename. */
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
- pTsdro = sqlite3ThreadDataReadOnly();
- if( pTsdro->useSharedData && zFilename && !isMemdb ){
- char *zFullPathname = sqlite3OsFullPathname(zFilename);
- if( !zFullPathname ){
- sqliteFree(p);
- return SQLITE_NOMEM;
- }
- for(pBt=pTsdro->pBtree; pBt; pBt=pBt->pNext){
- assert( pBt->nRef>0 );
- if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager)) ){
- p->pBt = pBt;
- *ppBtree = p;
- pBt->nRef++;
- sqliteFree(zFullPathname);
- return SQLITE_OK;
+ /*
+ ** If this Btree is a candidate for shared cache, try to find an
+ ** existing BtShared object that we can share with
+ */
+ if( isMemdb==0
+ && (db->flags & SQLITE_Vtab)==0
+ && zFilename && zFilename[0]
+ ){
+ if( sqlite3SharedCacheEnabled ){
+ int nFullPathname = pVfs->mxPathname+1;
+ char *zFullPathname = sqlite3Malloc(nFullPathname);
+ sqlite3_mutex *mutexShared;
+ p->sharable = 1;
+ db->flags |= SQLITE_SharedCache;
+ if( !zFullPathname ){
+ sqlite3_free(p);
+ return SQLITE_NOMEM;
}
+ sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
+ mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(mutexShared);
+ for(pBt=sqlite3SharedCacheList; pBt; pBt=pBt->pNext){
+ assert( pBt->nRef>0 );
+ if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
+ && sqlite3PagerVfs(pBt->pPager)==pVfs ){
+ p->pBt = pBt;
+ pBt->nRef++;
+ break;
+ }
+ }
+ sqlite3_mutex_leave(mutexShared);
+ sqlite3_free(zFullPathname);
+ }
+#ifdef SQLITE_DEBUG
+ else{
+ /* In debug mode, we mark all persistent databases as sharable
+ ** even when they are not. This exercises the locking code and
+ ** gives more opportunity for asserts(sqlite3_mutex_held())
+ ** statements to find locking problems.
+ */
+ p->sharable = 1;
}
- sqliteFree(zFullPathname);
+#endif
}
#endif
-
- /*
- ** The following asserts make sure that structures used by the btree are
- ** the right size. This is to guard against size changes that result
- ** when compiling on a different architecture.
- */
- assert( sizeof(i64)==8 || sizeof(i64)==4 );
- assert( sizeof(u64)==8 || sizeof(u64)==4 );
- assert( sizeof(u32)==4 );
- assert( sizeof(u16)==2 );
- assert( sizeof(Pgno)==4 );
-
- pBt = sqliteMalloc( sizeof(*pBt) );
if( pBt==0 ){
- rc = SQLITE_NOMEM;
- goto btree_open_out;
- }
- rc = sqlite3PagerOpen(&pBt->pPager, zFilename, EXTRA_SIZE, flags);
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
- }
- if( rc!=SQLITE_OK ){
- goto btree_open_out;
- }
- p->pBt = pBt;
-
- sqlite3PagerSetDestructor(pBt->pPager, pageDestructor);
- sqlite3PagerSetReiniter(pBt->pPager, pageReinit);
- pBt->pCursor = 0;
- pBt->pPage1 = 0;
- pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
- pBt->pageSize = get2byte(&zDbHeader[16]);
- if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
- || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
- pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE;
- pBt->maxEmbedFrac = 64; /* 25% */
- pBt->minEmbedFrac = 32; /* 12.5% */
- pBt->minLeafFrac = 32; /* 12.5% */
-#ifndef SQLITE_OMIT_AUTOVACUUM
- /* If the magic name ":memory:" will create an in-memory database, then
- ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if
- ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if
- ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a
- ** regular file-name. In this case the auto-vacuum applies as per normal.
+ /*
+ ** The following asserts make sure that structures used by the btree are
+ ** the right size. This is to guard against size changes that result
+ ** when compiling on a different architecture.
*/
- if( zFilename && !isMemdb ){
- pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0);
- pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0);
+ assert( sizeof(i64)==8 || sizeof(i64)==4 );
+ assert( sizeof(u64)==8 || sizeof(u64)==4 );
+ assert( sizeof(u32)==4 );
+ assert( sizeof(u16)==2 );
+ assert( sizeof(Pgno)==4 );
+
+ pBt = sqlite3MallocZero( sizeof(*pBt) );
+ if( pBt==0 ){
+ rc = SQLITE_NOMEM;
+ goto btree_open_out;
}
+ pBt->busyHdr.xFunc = sqlite3BtreeInvokeBusyHandler;
+ pBt->busyHdr.pArg = pBt;
+ rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
+ EXTRA_SIZE, flags, vfsFlags);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
+ }
+ if( rc!=SQLITE_OK ){
+ goto btree_open_out;
+ }
+ sqlite3PagerSetBusyhandler(pBt->pPager, &pBt->busyHdr);
+ p->pBt = pBt;
+
+ sqlite3PagerSetDestructor(pBt->pPager, pageDestructor);
+ sqlite3PagerSetReiniter(pBt->pPager, pageReinit);
+ pBt->pCursor = 0;
+ pBt->pPage1 = 0;
+ pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
+ pBt->pageSize = get2byte(&zDbHeader[16]);
+ if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
+ || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
+ pBt->pageSize = 0;
+ sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ /* If the magic name ":memory:" will create an in-memory database, then
+ ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if
+ ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if
+ ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a
+ ** regular file-name. In this case the auto-vacuum applies as per normal.
+ */
+ if( zFilename && !isMemdb ){
+ pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0);
+ pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0);
+ }
#endif
- nReserve = 0;
- }else{
- nReserve = zDbHeader[20];
- pBt->maxEmbedFrac = zDbHeader[21];
- pBt->minEmbedFrac = zDbHeader[22];
- pBt->minLeafFrac = zDbHeader[23];
- pBt->pageSizeFixed = 1;
+ nReserve = 0;
+ }else{
+ nReserve = zDbHeader[20];
+ pBt->pageSizeFixed = 1;
#ifndef SQLITE_OMIT_AUTOVACUUM
- pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);
- pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
+ pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);
+ pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
+#endif
+ }
+ pBt->usableSize = pBt->pageSize - nReserve;
+ assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
+ sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
+
+#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
+ /* Add the new BtShared object to the linked list sharable BtShareds.
+ */
+ if( p->sharable ){
+ sqlite3_mutex *mutexShared;
+ pBt->nRef = 1;
+ mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ if( SQLITE_THREADSAFE && sqlite3Config.bCoreMutex ){
+ pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
+ if( pBt->mutex==0 ){
+ rc = SQLITE_NOMEM;
+ db->mallocFailed = 0;
+ goto btree_open_out;
+ }
+ }
+ sqlite3_mutex_enter(mutexShared);
+ pBt->pNext = sqlite3SharedCacheList;
+ sqlite3SharedCacheList = pBt;
+ sqlite3_mutex_leave(mutexShared);
+ }
#endif
}
- pBt->usableSize = pBt->pageSize - nReserve;
- assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
- sqlite3PagerSetPagesize(pBt->pPager, pBt->pageSize);
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
- /* Add the new btree to the linked list starting at ThreadData.pBtree.
- ** There is no chance that a malloc() may fail inside of the
- ** sqlite3ThreadData() call, as the ThreadData structure must have already
- ** been allocated for pTsdro->useSharedData to be non-zero.
+ /* If the new Btree uses a sharable pBtShared, then link the new
+ ** Btree into the list of all sharable Btrees for the same connection.
+ ** The list is kept in ascending order by pBt address.
*/
- if( pTsdro->useSharedData && zFilename && !isMemdb ){
- pBt->pNext = pTsdro->pBtree;
- sqlite3ThreadData()->pBtree = pBt;
+ if( p->sharable ){
+ int i;
+ Btree *pSib;
+ for(i=0; i<db->nDb; i++){
+ if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){
+ while( pSib->pPrev ){ pSib = pSib->pPrev; }
+ if( p->pBt<pSib->pBt ){
+ p->pNext = pSib;
+ p->pPrev = 0;
+ pSib->pPrev = p;
+ }else{
+ while( pSib->pNext && pSib->pNext->pBt<p->pBt ){
+ pSib = pSib->pNext;
+ }
+ p->pNext = pSib->pNext;
+ p->pPrev = pSib;
+ if( p->pNext ){
+ p->pNext->pPrev = p;
+ }
+ pSib->pNext = p;
+ }
+ break;
+ }
+ }
}
#endif
- pBt->nRef = 1;
*ppBtree = p;
btree_open_out:
@@ -1166,25 +1400,82 @@ btree_open_out:
if( pBt && pBt->pPager ){
sqlite3PagerClose(pBt->pPager);
}
- sqliteFree(pBt);
- sqliteFree(p);
+ sqlite3_free(pBt);
+ sqlite3_free(p);
*ppBtree = 0;
}
return rc;
}
/*
+** Decrement the BtShared.nRef counter. When it reaches zero,
+** remove the BtShared structure from the sharing list. Return
+** true if the BtShared.nRef counter reaches zero and return
+** false if it is still positive.
+*/
+static int removeFromSharingList(BtShared *pBt){
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ sqlite3_mutex *pMaster;
+ BtShared *pList;
+ int removed = 0;
+
+ assert( sqlite3_mutex_notheld(pBt->mutex) );
+ pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMaster);
+ pBt->nRef--;
+ if( pBt->nRef<=0 ){
+ if( sqlite3SharedCacheList==pBt ){
+ sqlite3SharedCacheList = pBt->pNext;
+ }else{
+ pList = sqlite3SharedCacheList;
+ while( ALWAYS(pList) && pList->pNext!=pBt ){
+ pList=pList->pNext;
+ }
+ if( ALWAYS(pList) ){
+ pList->pNext = pBt->pNext;
+ }
+ }
+ if( SQLITE_THREADSAFE ){
+ sqlite3_mutex_free(pBt->mutex);
+ }
+ removed = 1;
+ }
+ sqlite3_mutex_leave(pMaster);
+ return removed;
+#else
+ return 1;
+#endif
+}
+
+/*
+** Make sure pBt->pTmpSpace points to an allocation of
+** MX_CELL_SIZE(pBt) bytes.
+*/
+static void allocateTempSpace(BtShared *pBt){
+ if( !pBt->pTmpSpace ){
+ pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+ }
+}
+
+/*
+** Free the pBt->pTmpSpace allocation
+*/
+static void freeTempSpace(BtShared *pBt){
+ sqlite3PageFree( pBt->pTmpSpace);
+ pBt->pTmpSpace = 0;
+}
+
+/*
** Close an open database and invalidate all cursors.
*/
int sqlite3BtreeClose(Btree *p){
BtShared *pBt = p->pBt;
BtCursor *pCur;
-#ifndef SQLITE_OMIT_SHARED_CACHE
- ThreadData *pTsd;
-#endif
-
/* Close all cursors opened via this handle. */
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
pCur = pBt->pCursor;
while( pCur ){
BtCursor *pTmp = pCur;
@@ -1199,55 +1490,37 @@ int sqlite3BtreeClose(Btree *p){
** this handle.
*/
sqlite3BtreeRollback(p);
- sqliteFree(p);
+ sqlite3BtreeLeave(p);
-#ifndef SQLITE_OMIT_SHARED_CACHE
/* If there are still other outstanding references to the shared-btree
** structure, return now. The remainder of this procedure cleans
** up the shared-btree.
*/
- assert( pBt->nRef>0 );
- pBt->nRef--;
- if( pBt->nRef ){
- return SQLITE_OK;
- }
-
- /* Remove the shared-btree from the thread wide list. Call
- ** ThreadDataReadOnly() and then cast away the const property of the
- ** pointer to avoid allocating thread data if it is not really required.
- */
- pTsd = (ThreadData *)sqlite3ThreadDataReadOnly();
- if( pTsd->pBtree==pBt ){
- assert( pTsd==sqlite3ThreadData() );
- pTsd->pBtree = pBt->pNext;
- }else{
- BtShared *pPrev;
- for(pPrev=pTsd->pBtree; pPrev && pPrev->pNext!=pBt; pPrev=pPrev->pNext){}
- if( pPrev ){
- assert( pTsd==sqlite3ThreadData() );
- pPrev->pNext = pBt->pNext;
+ assert( p->wantToLock==0 && p->locked==0 );
+ if( !p->sharable || removeFromSharingList(pBt) ){
+ /* The pBt is no longer on the sharing list, so we can access
+ ** it without having to hold the mutex.
+ **
+ ** Clean out and delete the BtShared object.
+ */
+ assert( !pBt->pCursor );
+ sqlite3PagerClose(pBt->pPager);
+ if( pBt->xFreeSchema && pBt->pSchema ){
+ pBt->xFreeSchema(pBt->pSchema);
}
+ sqlite3_free(pBt->pSchema);
+ freeTempSpace(pBt);
+ sqlite3_free(pBt);
}
-#endif
- /* Close the pager and free the shared-btree structure */
- assert( !pBt->pCursor );
- sqlite3PagerClose(pBt->pPager);
- if( pBt->xFreeSchema && pBt->pSchema ){
- pBt->xFreeSchema(pBt->pSchema);
- }
- sqliteFree(pBt->pSchema);
- sqliteFree(pBt);
- return SQLITE_OK;
-}
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ assert( p->wantToLock==0 );
+ assert( p->locked==0 );
+ if( p->pPrev ) p->pPrev->pNext = p->pNext;
+ if( p->pNext ) p->pNext->pPrev = p->pPrev;
+#endif
-/*
-** Change the busy handler callback function.
-*/
-int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
- BtShared *pBt = p->pBt;
- pBt->pBusyHandler = pHandler;
- sqlite3PagerSetBusyhandler(pBt->pPager, pHandler);
+ sqlite3_free(p);
return SQLITE_OK;
}
@@ -1268,7 +1541,10 @@ int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
*/
int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
sqlite3PagerSetCachesize(pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -1283,7 +1559,10 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
#endif
@@ -1294,8 +1573,13 @@ int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
*/
int sqlite3BtreeSyncDisabled(Btree *p){
BtShared *pBt = p->pBt;
+ int rc;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
assert( pBt && pBt->pPager );
- return sqlite3PagerNosync(pBt->pPager);
+ rc = sqlite3PagerNosync(pBt->pPager);
+ sqlite3BtreeLeave(p);
+ return rc;
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
@@ -1315,8 +1599,11 @@ int sqlite3BtreeSyncDisabled(Btree *p){
** bytes per page is left unchanged.
*/
int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
+ int rc = SQLITE_OK;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
if( pBt->pageSizeFixed ){
+ sqlite3BtreeLeave(p);
return SQLITE_READONLY;
}
if( nReserve<0 ){
@@ -1326,10 +1613,13 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pPage1 && !pBt->pCursor );
- pBt->pageSize = sqlite3PagerSetPagesize(pBt->pPager, pageSize);
+ pBt->pageSize = pageSize;
+ freeTempSpace(pBt);
+ rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
}
pBt->usableSize = pBt->pageSize - nReserve;
- return SQLITE_OK;
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -1339,7 +1629,11 @@ int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
int sqlite3BtreeGetReserve(Btree *p){
- return p->pBt->pageSize - p->pBt->usableSize;
+ int n;
+ sqlite3BtreeEnter(p);
+ n = p->pBt->pageSize - p->pBt->usableSize;
+ sqlite3BtreeLeave(p);
+ return n;
}
/*
@@ -1348,7 +1642,11 @@ int sqlite3BtreeGetReserve(Btree *p){
** Regardless of the value of mxPage, return the maximum page count.
*/
int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
- return sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+ int n;
+ sqlite3BtreeEnter(p);
+ n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
+ return n;
}
#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */
@@ -1363,12 +1661,17 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
return SQLITE_READONLY;
#else
BtShared *pBt = p->pBt;
+ int rc = SQLITE_OK;
int av = (autoVacuum?1:0);
+
+ sqlite3BtreeEnter(p);
if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){
- return SQLITE_READONLY;
+ rc = SQLITE_READONLY;
+ }else{
+ pBt->autoVacuum = av;
}
- pBt->autoVacuum = av;
- return SQLITE_OK;
+ sqlite3BtreeLeave(p);
+ return rc;
#endif
}
@@ -1380,11 +1683,15 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
#ifdef SQLITE_OMIT_AUTOVACUUM
return BTREE_AUTOVACUUM_NONE;
#else
- return (
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = (
(!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE:
(!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL:
BTREE_AUTOVACUUM_INCR
);
+ sqlite3BtreeLeave(p);
+ return rc;
#endif
}
@@ -1399,19 +1706,26 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
** is returned if we run out of memory.
*/
static int lockBtree(BtShared *pBt){
- int rc, pageSize;
+ int rc;
MemPage *pPage1;
+ int nPage;
+
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pBt->pPage1 ) return SQLITE_OK;
rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
-
/* Do some checking to help insure the file we opened really is
** a valid database file.
*/
- rc = SQLITE_NOTADB;
- if( sqlite3PagerPagecount(pBt->pPager)>0 ){
+ rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
+ if( rc!=SQLITE_OK ){
+ goto page1_init_failed;
+ }else if( nPage>0 ){
+ int pageSize;
+ int usableSize;
u8 *page1 = pPage1->aData;
+ rc = SQLITE_NOTADB;
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
}
@@ -1421,19 +1735,42 @@ static int lockBtree(BtShared *pBt){
if( page1[19]>1 ){
goto page1_init_failed;
}
+
+ /* The maximum embedded fraction must be exactly 25%. And the minimum
+ ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
+ ** The original design allowed these amounts to vary, but as of
+ ** version 3.6.0, we require them to be fixed.
+ */
+ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
+ goto page1_init_failed;
+ }
pageSize = get2byte(&page1[16]);
- if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ){
+ if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ||
+ (SQLITE_MAX_PAGE_SIZE<32768 && pageSize>SQLITE_MAX_PAGE_SIZE)
+ ){
goto page1_init_failed;
}
assert( (pageSize & 7)==0 );
- pBt->pageSize = pageSize;
- pBt->usableSize = pageSize - page1[20];
- if( pBt->usableSize<500 ){
+ usableSize = pageSize - page1[20];
+ if( pageSize!=pBt->pageSize ){
+ /* After reading the first page of the database assuming a page size
+ ** of BtShared.pageSize, we have discovered that the page-size is
+ ** actually pageSize. Unlock the database, leave pBt->pPage1 at
+ ** zero and return SQLITE_OK. The caller will call this function
+ ** again with the correct page-size.
+ */
+ releasePage(pPage1);
+ pBt->usableSize = usableSize;
+ pBt->pageSize = pageSize;
+ freeTempSpace(pBt);
+ sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize);
+ return SQLITE_OK;
+ }
+ if( usableSize<500 ){
goto page1_init_failed;
}
- pBt->maxEmbedFrac = page1[21];
- pBt->minEmbedFrac = page1[22];
- pBt->minLeafFrac = page1[23];
+ pBt->pageSize = pageSize;
+ pBt->usableSize = usableSize;
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
@@ -1453,13 +1790,10 @@ static int lockBtree(BtShared *pBt){
** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
** page pointer.
*/
- pBt->maxLocal = (pBt->usableSize-12)*pBt->maxEmbedFrac/255 - 23;
- pBt->minLocal = (pBt->usableSize-12)*pBt->minEmbedFrac/255 - 23;
+ pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23;
+ pBt->minLocal = (pBt->usableSize-12)*32/255 - 23;
pBt->maxLeaf = pBt->usableSize - 35;
- pBt->minLeaf = (pBt->usableSize-12)*pBt->minLeafFrac/255 - 23;
- if( pBt->minLocal>pBt->maxLocal || pBt->maxLocal<0 ){
- goto page1_init_failed;
- }
+ pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23;
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
pBt->pPage1 = pPage1;
return SQLITE_OK;
@@ -1476,6 +1810,8 @@ page1_init_failed:
*/
static int lockBtreeWithRetry(Btree *pRef){
int rc = SQLITE_OK;
+
+ assert( sqlite3BtreeHoldsMutex(pRef) );
if( pRef->inTrans==TRANS_NONE ){
u8 inTransaction = pRef->pBt->inTransaction;
btreeIntegrity(pRef);
@@ -1502,14 +1838,18 @@ static int lockBtreeWithRetry(Btree *pRef){
** If there is a transaction in progress, this routine is a no-op.
*/
static void unlockBtreeIfUnused(BtShared *pBt){
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
+ assert( pBt->pPage1->aData );
+#if 0
if( pBt->pPage1->aData==0 ){
MemPage *pPage = pBt->pPage1;
- pPage->aData = &((u8*)pPage)[-pBt->pageSize];
+ pPage->aData = sqlite3PagerGetData(pPage->pDbPage);
pPage->pBt = pBt;
pPage->pgno = 1;
}
+#endif
releasePage(pBt->pPage1);
}
pBt->pPage1 = 0;
@@ -1525,7 +1865,13 @@ static int newDatabase(BtShared *pBt){
MemPage *pP1;
unsigned char *data;
int rc;
- if( sqlite3PagerPagecount(pBt->pPager)>0 ) return SQLITE_OK;
+ int nPage;
+
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
+ if( rc!=SQLITE_OK || nPage>0 ){
+ return rc;
+ }
pP1 = pBt->pPage1;
assert( pP1!=0 );
data = pP1->aData;
@@ -1533,13 +1879,14 @@ static int newDatabase(BtShared *pBt){
if( rc ) return rc;
memcpy(data, zMagicHeader, sizeof(zMagicHeader));
assert( sizeof(zMagicHeader)==16 );
+ assert( sizeof(zMagicHeader)==sizeof(zPoisonHeader) );
put2byte(&data[16], pBt->pageSize);
data[18] = 1;
data[19] = 1;
data[20] = pBt->pageSize - pBt->usableSize;
- data[21] = pBt->maxEmbedFrac;
- data[22] = pBt->minEmbedFrac;
- data[23] = pBt->minLeafFrac;
+ data[21] = 64;
+ data[22] = 32;
+ data[23] = 32;
memset(&data[24], 0, 100-24);
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
pBt->pageSizeFixed = 1;
@@ -1591,6 +1938,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
BtShared *pBt = p->pBt;
int rc = SQLITE_OK;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
btreeIntegrity(p);
/* If the btree is already in a write-transaction, or it
@@ -1598,12 +1947,13 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
** is requested, this is a no-op.
*/
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
- return SQLITE_OK;
+ goto trans_begun;
}
/* Write transactions are not possible on a read-only database */
if( pBt->readOnly && wrflag ){
- return SQLITE_READONLY;
+ rc = SQLITE_READONLY;
+ goto trans_begun;
}
/* If another database handle has already opened a write transaction
@@ -1611,12 +1961,27 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
** requested, return SQLITE_BUSY.
*/
if( pBt->inTransaction==TRANS_WRITE && wrflag ){
- return SQLITE_BUSY;
+ rc = SQLITE_BUSY;
+ goto trans_begun;
}
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ if( wrflag>1 ){
+ BtLock *pIter;
+ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
+ if( pIter->pBtree!=p ){
+ rc = SQLITE_BUSY;
+ goto trans_begun;
+ }
+ }
+ }
+#endif
+
do {
if( pBt->pPage1==0 ){
- rc = lockBtree(pBt);
+ do{
+ rc = lockBtree(pBt);
+ }while( pBt->pPage1==0 && rc==SQLITE_OK );
}
if( rc==SQLITE_OK && wrflag ){
@@ -1636,7 +2001,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
unlockBtreeIfUnused(pBt);
}
}while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
- sqlite3InvokeBusyHandler(pBt->pBusyHandler) );
+ sqlite3BtreeInvokeBusyHandler(pBt, 0) );
if( rc==SQLITE_OK ){
if( p->inTrans==TRANS_NONE ){
@@ -1646,12 +2011,33 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans>pBt->inTransaction ){
pBt->inTransaction = p->inTrans;
}
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ if( wrflag>1 ){
+ assert( !pBt->pExclusive );
+ pBt->pExclusive = p;
+ }
+#endif
}
+
+trans_begun:
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return rc;
}
+/*
+** Return the size of the database file in pages. Or return -1 if
+** there is any kind of error.
+*/
+static int pagerPagecount(Pager *pPager){
+ int rc;
+ int nPage;
+ rc = sqlite3PagerPagecount(pPager, &nPage);
+ return (rc==SQLITE_OK?nPage:-1);
+}
+
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
@@ -1667,6 +2053,7 @@ static int setChildPtrmaps(MemPage *pPage){
int isInitOrig = pPage->isInit;
Pgno pgno = pPage->pgno;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
rc = sqlite3BtreeInitPage(pPage, pPage->pParent);
if( rc!=SQLITE_OK ){
goto set_child_ptrmaps_out;
@@ -1684,7 +2071,7 @@ static int setChildPtrmaps(MemPage *pPage){
if( !pPage->leaf ){
Pgno childPgno = get4byte(pCell);
rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno);
- if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out;
+ if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out;
}
}
@@ -1714,6 +2101,7 @@ set_child_ptrmaps_out:
** overflow page in the list.
*/
static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( eType==PTRMAP_OVERFLOW2 ){
/* The pointer is always the first 4 bytes of the page in this case. */
if( get4byte(pPage->aData)!=iFrom ){
@@ -1770,7 +2158,8 @@ static int relocatePage(
MemPage *pDbPage, /* Open page to move */
u8 eType, /* Pointer map 'type' entry for pDbPage */
Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */
- Pgno iFreePage /* The location to move pDbPage to */
+ Pgno iFreePage, /* The location to move pDbPage to */
+ int isCommit
){
MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */
Pgno iDbPage = pDbPage->pgno;
@@ -1779,11 +2168,13 @@ static int relocatePage(
assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( pDbPage->pBt==pBt );
- /* Move page iDbPage from it's current location to page number iFreePage */
+ /* Move page iDbPage from its current location to page number iFreePage */
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
iDbPage, iFreePage, iPtrPage, eType));
- rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage);
+ rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -1857,9 +2248,10 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
Pgno iLastPg; /* Last page in the database */
Pgno nFreeList; /* Number of pages still on the free-list */
+ assert( sqlite3_mutex_held(pBt->mutex) );
iLastPg = pBt->nTrunc;
if( iLastPg==0 ){
- iLastPg = sqlite3PagerPagecount(pBt->pPager);
+ iLastPg = pagerPagecount(pBt->pPager);
}
if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){
@@ -1924,14 +2316,13 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
assert( iFreePg<iLastPg );
rc = sqlite3PagerWrite(pLastPg->pDbPage);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg);
+ if( rc==SQLITE_OK ){
+ rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
+ }
releasePage(pLastPg);
if( rc!=SQLITE_OK ){
return rc;
- }
+ }
}
}
@@ -1951,13 +2342,20 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
** SQLITE_OK is returned. Otherwise an SQLite error code.
*/
int sqlite3BtreeIncrVacuum(Btree *p){
+ int rc;
BtShared *pBt = p->pBt;
+
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
if( !pBt->autoVacuum ){
- return SQLITE_DONE;
+ rc = SQLITE_DONE;
+ }else{
+ invalidateAllOverflowCache(pBt);
+ rc = incrVacuumStep(pBt, 0);
}
- invalidateAllOverflowCache(pBt);
- return incrVacuumStep(pBt, 0);
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -1976,6 +2374,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
int nRef = sqlite3PagerRefcount(pPager);
#endif
+ assert( sqlite3_mutex_held(pBt->mutex) );
invalidateAllOverflowCache(pBt);
assert(pBt->autoVacuum);
if( !pBt->incrVacuum ){
@@ -1985,7 +2384,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
Pgno nFree;
Pgno nPtrmap;
const int pgsz = pBt->pageSize;
- Pgno nOrig = sqlite3PagerPagecount(pBt->pPager);
+ int nOrig = pagerPagecount(pBt->pPager);
if( PTRMAP_ISPAGE(pBt, nOrig) ){
return SQLITE_CORRUPT_BKPT;
@@ -2010,7 +2409,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
if( rc==SQLITE_DONE ){
assert(nFin==0 || pBt->nTrunc==0 || nFin<=pBt->nTrunc);
rc = SQLITE_OK;
- if( pBt->nTrunc ){
+ if( pBt->nTrunc && nFin ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0);
@@ -2063,15 +2462,19 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
if( p->inTrans==TRANS_WRITE ){
BtShared *pBt = p->pBt;
Pgno nTrunc = 0;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
rc = autoVacuumCommit(pBt, &nTrunc);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
}
#endif
- rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc);
+ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc, 0);
+ sqlite3BtreeLeave(p);
}
return rc;
}
@@ -2093,6 +2496,8 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
int sqlite3BtreeCommitPhaseTwo(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
btreeIntegrity(p);
/* If the handle has a write-transaction open, commit the shared-btrees
@@ -2104,6 +2509,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
pBt->inTransaction = TRANS_READ;
@@ -2130,6 +2536,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
unlockBtreeIfUnused(pBt);
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -2138,10 +2545,12 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
*/
int sqlite3BtreeCommit(Btree *p){
int rc;
+ sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeCommitPhaseTwo(p);
}
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2150,18 +2559,51 @@ int sqlite3BtreeCommit(Btree *p){
** Return the number of write-cursors open on this handle. This is for use
** in assert() expressions, so it is only compiled if NDEBUG is not
** defined.
+**
+** For the purposes of this routine, a write-cursor is any cursor that
+** is capable of writing to the databse. That means the cursor was
+** originally opened for writing and the cursor has not be disabled
+** by having its state changed to CURSOR_FAULT.
*/
static int countWriteCursors(BtShared *pBt){
BtCursor *pCur;
int r = 0;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->wrFlag ) r++;
+ if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++;
}
return r;
}
#endif
/*
+** This routine sets the state to CURSOR_FAULT and the error
+** code to errCode for every cursor on BtShared that pBtree
+** references.
+**
+** Every cursor is tripped, including cursors that belong
+** to other database connections that happen to be sharing
+** the cache with pBtree.
+**
+** This routine gets called when a rollback occurs.
+** All cursors using the same cache must be tripped
+** to prevent them from trying to use the btree after
+** the rollback. The rollback may have deleted tables
+** or moved root pages, so it is not sufficient to
+** save the state of the cursor. The cursor must be
+** invalidated.
+*/
+void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
+ BtCursor *p;
+ sqlite3BtreeEnter(pBtree);
+ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+ clearCursorPosition(p);
+ p->eState = CURSOR_FAULT;
+ p->skip = errCode;
+ }
+ sqlite3BtreeLeave(pBtree);
+}
+
+/*
** Rollback the transaction in progress. All cursors will be
** invalided by this operation. Any attempt to use a cursor
** that was open at the beginning of this operation will result
@@ -2175,6 +2617,8 @@ int sqlite3BtreeRollback(Btree *p){
BtShared *pBt = p->pBt;
MemPage *pPage1;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
rc = saveAllCursors(pBt, 0, 0);
#ifndef SQLITE_OMIT_SHARED_CACHE
if( rc!=SQLITE_OK ){
@@ -2185,12 +2629,7 @@ int sqlite3BtreeRollback(Btree *p){
** we cannot simply return the error to the caller. Instead, abort
** all queries that may be using any of the cursors that failed to save.
*/
- while( pBt->pCursor ){
- sqlite3 *db = pBt->pCursor->pBtree->pSqlite;
- if( db ){
- sqlite3AbortOtherActiveVdbes(db, 0);
- }
- }
+ sqlite3BtreeTripAllCursors(p, rc);
}
#endif
btreeIntegrity(p);
@@ -2232,6 +2671,7 @@ int sqlite3BtreeRollback(Btree *p){
unlockBtreeIfUnused(pBt);
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2253,12 +2693,16 @@ int sqlite3BtreeRollback(Btree *p){
int sqlite3BtreeBeginStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else{
+ assert( pBt->inTransaction==TRANS_WRITE );
+ rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
+ pBt->inStmt = 1;
}
- assert( pBt->inTransaction==TRANS_WRITE );
- rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
- pBt->inStmt = 1;
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2270,12 +2714,15 @@ int sqlite3BtreeBeginStmt(Btree *p){
int sqlite3BtreeCommitStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
if( pBt->inStmt && !pBt->readOnly ){
rc = sqlite3PagerStmtCommit(pBt->pPager);
}else{
rc = SQLITE_OK;
}
pBt->inStmt = 0;
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2290,34 +2737,17 @@ int sqlite3BtreeCommitStmt(Btree *p){
int sqlite3BtreeRollbackStmt(Btree *p){
int rc = SQLITE_OK;
BtShared *pBt = p->pBt;
- sqlite3MallocDisallow();
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
if( pBt->inStmt && !pBt->readOnly ){
rc = sqlite3PagerStmtRollback(pBt->pPager);
- assert( countWriteCursors(pBt)==0 );
pBt->inStmt = 0;
}
- sqlite3MallocAllow();
+ sqlite3BtreeLeave(p);
return rc;
}
/*
-** Default key comparison function to be used if no comparison function
-** is specified on the sqlite3BtreeCursor() call.
-*/
-static int dfltCompare(
- void *NotUsed, /* User data is not used */
- int n1, const void *p1, /* First key to compare */
- int n2, const void *p2 /* Second key to compare */
-){
- int c;
- c = memcmp(p1, p2, n1<n2 ? n1 : n2);
- if( c==0 ){
- c = n1 - n2;
- }
- return c;
-}
-
-/*
** Create a new cursor for the BTree whose root is on the page
** iTable. The act of acquiring a cursor gets a read lock on
** the database file.
@@ -2343,31 +2773,23 @@ static int dfltCompare(
** No checking is done to make sure that page iTable really is the
** root page of a b-tree. If it is not, then the cursor acquired
** will not work correctly.
-**
-** The comparison function must be logically the same for every cursor
-** on a particular table. Changing the comparison function will result
-** in incorrect operations. If the comparison function is NULL, a
-** default comparison function is used. The comparison function is
-** always ignored for INTKEY tables.
*/
-int sqlite3BtreeCursor(
- Btree *p, /* The btree */
- int iTable, /* Root page of table to open */
- int wrFlag, /* 1 to write. 0 read-only */
- int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */
- void *pArg, /* First arg to xCompare() */
- BtCursor **ppCur /* Write new cursor here */
+static int btreeCursor(
+ Btree *p, /* The btree */
+ int iTable, /* Root page of table to open */
+ int wrFlag, /* 1 to write. 0 read-only */
+ struct KeyInfo *pKeyInfo, /* First arg to comparison function */
+ BtCursor *pCur /* Space for new cursor */
){
int rc;
- BtCursor *pCur;
BtShared *pBt = p->pBt;
- *ppCur = 0;
+ assert( sqlite3BtreeHoldsMutex(p) );
if( wrFlag ){
if( pBt->readOnly ){
return SQLITE_READONLY;
}
- if( checkReadLocks(p, iTable, 0) ){
+ if( checkReadLocks(p, iTable, 0, 0) ){
return SQLITE_LOCKED;
}
}
@@ -2381,13 +2803,8 @@ int sqlite3BtreeCursor(
return SQLITE_READONLY;
}
}
- pCur = sqliteMalloc( sizeof(*pCur) );
- if( pCur==0 ){
- rc = SQLITE_NOMEM;
- goto create_cursor_exception;
- }
pCur->pgnoRoot = (Pgno)iTable;
- if( iTable==1 && sqlite3PagerPagecount(pBt->pPager)==0 ){
+ if( iTable==1 && pagerPagecount(pBt->pPager)==0 ){
rc = SQLITE_EMPTY;
goto create_cursor_exception;
}
@@ -2400,9 +2817,9 @@ int sqlite3BtreeCursor(
** variables, link the cursor into the BtShared list and set *ppCur (the
** output argument to this function).
*/
- pCur->xCompare = xCmp ? xCmp : dfltCompare;
- pCur->pArg = pArg;
+ pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
+ pCur->pBt = pBt;
pCur->wrFlag = wrFlag;
pCur->pNext = pBt->pCursor;
if( pCur->pNext ){
@@ -2410,37 +2827,59 @@ int sqlite3BtreeCursor(
}
pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID;
- *ppCur = pCur;
return SQLITE_OK;
+
create_cursor_exception:
- if( pCur ){
- releasePage(pCur->pPage);
- sqliteFree(pCur);
- }
+ releasePage(pCur->pPage);
unlockBtreeIfUnused(pBt);
return rc;
}
+int sqlite3BtreeCursor(
+ Btree *p, /* The btree */
+ int iTable, /* Root page of table to open */
+ int wrFlag, /* 1 to write. 0 read-only */
+ struct KeyInfo *pKeyInfo, /* First arg to xCompare() */
+ BtCursor *pCur /* Write new cursor here */
+){
+ int rc;
+ sqlite3BtreeEnter(p);
+ p->pBt->db = p->db;
+ rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
+int sqlite3BtreeCursorSize(){
+ return sizeof(BtCursor);
+}
+
+
/*
** Close a cursor. The read lock on the database file is released
** when the last cursor is closed.
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
- BtShared *pBt = pCur->pBtree->pBt;
- clearCursorPosition(pCur);
- if( pCur->pPrev ){
- pCur->pPrev->pNext = pCur->pNext;
- }else{
- pBt->pCursor = pCur->pNext;
- }
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur->pPrev;
+ Btree *pBtree = pCur->pBtree;
+ if( pBtree ){
+ BtShared *pBt = pCur->pBt;
+ sqlite3BtreeEnter(pBtree);
+ pBt->db = pBtree->db;
+ clearCursorPosition(pCur);
+ if( pCur->pPrev ){
+ pCur->pPrev->pNext = pCur->pNext;
+ }else{
+ pBt->pCursor = pCur->pNext;
+ }
+ if( pCur->pNext ){
+ pCur->pNext->pPrev = pCur->pPrev;
+ }
+ releasePage(pCur->pPage);
+ unlockBtreeIfUnused(pBt);
+ invalidateOverflowCache(pCur);
+ /* sqlite3_free(pCur); */
+ sqlite3BtreeLeave(pBtree);
}
- releasePage(pCur->pPage);
- unlockBtreeIfUnused(pBt);
- invalidateOverflowCache(pCur);
- sqliteFree(pCur);
return SQLITE_OK;
}
@@ -2449,6 +2888,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
** The temporary cursor is not on the cursor list for the Btree.
*/
void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
+ assert( cursorHoldsMutex(pCur) );
memcpy(pTempCur, pCur, sizeof(*pCur));
pTempCur->pNext = 0;
pTempCur->pPrev = 0;
@@ -2462,6 +2902,7 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
** function above.
*/
void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
+ assert( cursorHoldsMutex(pCur) );
if( pCur->pPage ){
sqlite3PagerUnref(pCur->pPage->pDbPage);
}
@@ -2497,6 +2938,7 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
static void getCellInfo(BtCursor *pCur){
if( pCur->info.nSize==0 ){
sqlite3BtreeParseCell(pCur->pPage, pCur->idx, &pCur->info);
+ pCur->validNKey = 1;
}else{
assertCellInfo(pCur);
}
@@ -2506,6 +2948,7 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
#define getCellInfo(pCur) \
if( pCur->info.nSize==0 ){ \
sqlite3BtreeParseCell(pCur->pPage, pCur->idx, &pCur->info); \
+ pCur->validNKey = 1; \
}else{ \
assertCellInfo(pCur); \
}
@@ -2520,7 +2963,10 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2541,7 +2987,10 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2565,7 +3014,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
**
** Unless pPgnoNext is NULL, the page number of the next overflow
** page in the linked list is written to *pPgnoNext. If page ovfl
-** is the last page in it's linked list, *pPgnoNext is set to zero.
+** is the last page in its linked list, *pPgnoNext is set to zero.
**
** If ppPage is not NULL, *ppPage is set to the MemPage* handle
** for page ovfl. The underlying pager page may have been requested
@@ -2581,6 +3030,7 @@ static int getOverflowPage(
Pgno next = 0;
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
/* One of these must not be NULL. Otherwise, why call this function? */
assert(ppPage || pPgnoNext);
@@ -2607,7 +3057,7 @@ static int getOverflowPage(
iGuess++;
}
- if( iGuess<=sqlite3PagerPagecount(pBt->pPager) ){
+ if( iGuess<=pagerPagecount(pBt->pPager) ){
rc = ptrmapGet(pBt, iGuess, &eType, &pgno);
if( rc!=SQLITE_OK ){
return rc;
@@ -2713,13 +3163,14 @@ static int accessPayload(
int rc = SQLITE_OK;
u32 nKey;
int iIdx = 0;
- MemPage *pPage = pCur->pPage; /* Btree page of current cursor entry */
- BtShared *pBt = pCur->pBtree->pBt; /* Btree this cursor belongs to */
+ MemPage *pPage = pCur->pPage; /* Btree page of current cursor entry */
+ BtShared *pBt; /* Btree this cursor belongs to */
assert( pPage );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
assert( offset>=0 );
+ assert( cursorHoldsMutex(pCur) );
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
@@ -2747,6 +3198,7 @@ static int accessPayload(
offset -= pCur->info.nLocal;
}
+ pBt = pCur->pBt;
if( rc==SQLITE_OK && amt>0 ){
const int ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */
Pgno nextPage;
@@ -2763,7 +3215,7 @@ static int accessPayload(
*/
if( pCur->isIncrblobHandle && !pCur->aOverflow ){
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
- pCur->aOverflow = (Pgno *)sqliteMalloc(sizeof(Pgno)*nOvfl);
+ pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl);
if( nOvfl && !pCur->aOverflow ){
rc = SQLITE_NOMEM;
}
@@ -2843,7 +3295,10 @@ static int accessPayload(
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
@@ -2867,7 +3322,16 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+#ifndef SQLITE_OMIT_INCRBLOB
+ if ( pCur->eState==CURSOR_INVALID ){
+ return SQLITE_ABORT;
+ }
+#endif
+
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
@@ -2908,6 +3372,7 @@ static const unsigned char *fetchPayload(
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->eState==CURSOR_VALID );
+ assert( cursorHoldsMutex(pCur) );
pPage = pCur->pPage;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
@@ -2938,18 +3403,23 @@ static const unsigned char *fetchPayload(
** b-tree page. Write the number of available bytes into *pAmt.
**
** The pointer returned is ephemeral. The key/data may move
-** or be destroyed on the next call to any Btree routine.
+** or be destroyed on the next call to any Btree routine,
+** including calls from other threads against the same cache.
+** Hence, a mutex on the BtShared should be held prior to calling
+** this routine.
**
** These routines is used to get quick access to key and data
** in the common case where no overflow pages are used.
*/
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
+ assert( cursorHoldsMutex(pCur) );
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 0);
}
return 0;
}
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
+ assert( cursorHoldsMutex(pCur) );
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 1);
}
@@ -2965,8 +3435,9 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
int rc;
MemPage *pNewPage;
MemPage *pOldPage;
- BtShared *pBt = pCur->pBtree->pBt;
+ BtShared *pBt = pCur->pBt;
+ assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
@@ -2977,6 +3448,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
pCur->pPage = pNewPage;
pCur->idx = 0;
pCur->info.nSize = 0;
+ pCur->validNKey = 0;
if( pNewPage->nCell<1 ){
return SQLITE_CORRUPT_BKPT;
}
@@ -2993,7 +3465,10 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
** 1 is pointing to.
*/
int sqlite3BtreeIsRootPage(MemPage *pPage){
- MemPage *pParent = pPage->pParent;
+ MemPage *pParent;
+
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ pParent = pPage->pParent;
if( pParent==0 ) return 1;
if( pParent->pgno>1 ) return 0;
if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
@@ -3013,6 +3488,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
MemPage *pPage;
int idxParent;
+ assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
assert( pPage!=0 );
@@ -3024,6 +3500,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
releasePage(pPage);
pCur->pPage = pParent;
pCur->info.nSize = 0;
+ pCur->validNKey = 0;
assert( pParent->idxShift==0 );
pCur->idx = idxParent;
}
@@ -3034,9 +3511,17 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
- if( pCur->eState==CURSOR_REQUIRESEEK ){
+ assert( cursorHoldsMutex(pCur) );
+ assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
+ assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
+ assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
+ if( pCur->eState>=CURSOR_REQUIRESEEK ){
+ if( pCur->eState==CURSOR_FAULT ){
+ return pCur->skip;
+ }
clearCursorPosition(pCur);
}
pRoot = pCur->pPage;
@@ -3054,6 +3539,8 @@ static int moveToRoot(BtCursor *pCur){
}
pCur->idx = 0;
pCur->info.nSize = 0;
+ pCur->atLast = 0;
+ pCur->validNKey = 0;
if( pRoot->nCell==0 && !pRoot->leaf ){
Pgno subpage;
assert( pRoot->pgno==1 );
@@ -3075,17 +3562,17 @@ static int moveToRoot(BtCursor *pCur){
*/
static int moveToLeftmost(BtCursor *pCur){
Pgno pgno;
- int rc;
+ int rc = SQLITE_OK;
MemPage *pPage;
+ assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
- while( !(pPage = pCur->pPage)->leaf ){
+ while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
pgno = get4byte(findCell(pPage, pCur->idx));
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -3100,18 +3587,21 @@ static int moveToLeftmost(BtCursor *pCur){
*/
static int moveToRightmost(BtCursor *pCur){
Pgno pgno;
- int rc;
+ int rc = SQLITE_OK;
MemPage *pPage;
+ assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
- while( !(pPage = pCur->pPage)->leaf ){
+ while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->idx = pPage->nCell;
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
}
- pCur->idx = pPage->nCell - 1;
- pCur->info.nSize = 0;
+ if( rc==SQLITE_OK ){
+ pCur->idx = pPage->nCell - 1;
+ pCur->info.nSize = 0;
+ pCur->validNKey = 0;
+ }
return SQLITE_OK;
}
@@ -3121,16 +3611,21 @@ static int moveToRightmost(BtCursor *pCur){
*/
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
+
+ assert( cursorHoldsMutex(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
rc = moveToRoot(pCur);
- if( rc ) return rc;
- if( pCur->eState==CURSOR_INVALID ){
- assert( pCur->pPage->nCell==0 );
- *pRes = 1;
- return SQLITE_OK;
+ if( rc==SQLITE_OK ){
+ if( pCur->eState==CURSOR_INVALID ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ rc = SQLITE_OK;
+ }else{
+ assert( pCur->pPage->nCell>0 );
+ *pRes = 0;
+ rc = moveToLeftmost(pCur);
+ }
}
- assert( pCur->pPage->nCell>0 );
- *pRes = 0;
- rc = moveToLeftmost(pCur);
return rc;
}
@@ -3140,26 +3635,32 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
*/
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
+
+ assert( cursorHoldsMutex(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
rc = moveToRoot(pCur);
- if( rc ) return rc;
- if( CURSOR_INVALID==pCur->eState ){
- assert( pCur->pPage->nCell==0 );
- *pRes = 1;
- return SQLITE_OK;
+ if( rc==SQLITE_OK ){
+ if( CURSOR_INVALID==pCur->eState ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ }else{
+ assert( pCur->eState==CURSOR_VALID );
+ *pRes = 0;
+ rc = moveToRightmost(pCur);
+ getCellInfo(pCur);
+ pCur->atLast = rc==SQLITE_OK;
+ }
}
- assert( pCur->eState==CURSOR_VALID );
- *pRes = 0;
- rc = moveToRightmost(pCur);
return rc;
}
-/* Move the cursor so that it points to an entry near pKey/nKey.
-** Return a success code.
+/* Move the cursor so that it points to an entry near the key
+** specified by pKey/nKey/pUnKey. Return a success code.
**
-** For INTKEY tables, only the nKey parameter is used. pKey is
-** ignored. For other tables, nKey is the number of bytes of data
-** in pKey. The comparison function specified when the cursor was
-** created is used to compare keys.
+** For INTKEY tables, only the nKey parameter is used. pKey
+** and pUnKey must be NULL. For index tables, either pUnKey
+** must point to a key that has already been unpacked, or else
+** pKey/nKey describes a blob containing the key.
**
** If an exact match is not found, then the cursor is always
** left pointing at a leaf page which would hold the entry if it
@@ -3179,17 +3680,40 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
**
** *pRes>0 The cursor is left pointing at an entry that
** is larger than pKey.
+**
*/
int sqlite3BtreeMoveto(
BtCursor *pCur, /* The cursor to be moved */
const void *pKey, /* The key content for indices. Not used by tables */
+ UnpackedRecord *pUnKey,/* Unpacked version of pKey */
i64 nKey, /* Size of pKey. Or the key for tables */
int biasRight, /* If true, bias the search to the high end */
int *pRes /* Search result flag */
){
int rc;
+ char aSpace[200];
+
+ assert( cursorHoldsMutex(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
+
+ /* If the cursor is already positioned at the point we are trying
+ ** to move to, then just return without doing any work */
+ if( pCur->eState==CURSOR_VALID && pCur->validNKey && pCur->pPage->intKey ){
+ if( pCur->info.nKey==nKey ){
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ if( pCur->atLast && pCur->info.nKey<nKey ){
+ *pRes = -1;
+ return SQLITE_OK;
+ }
+ }
+
+
rc = moveToRoot(pCur);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
assert( pCur->pPage );
assert( pCur->pPage->isInit );
if( pCur->eState==CURSOR_INVALID ){
@@ -3197,6 +3721,24 @@ int sqlite3BtreeMoveto(
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
}
+ if( pCur->pPage->intKey ){
+ /* We are given an SQL table to search. The key is the integer
+ ** rowid contained in nKey. pKey and pUnKey should both be NULL */
+ assert( pUnKey==0 );
+ assert( pKey==0 );
+ }else if( pUnKey==0 ){
+ /* We are to search an SQL index using a key encoded as a blob.
+ ** The blob is found at pKey and is nKey bytes in length. Unpack
+ ** this key so that we can use it. */
+ assert( pKey!=0 );
+ pUnKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, nKey, pKey,
+ aSpace, sizeof(aSpace));
+ if( pUnKey==0 ) return SQLITE_NOMEM;
+ }else{
+ /* We are to search an SQL index using a key that is already unpacked
+ ** and handed to us in pUnKey. */
+ assert( pKey==0 );
+ }
for(;;){
int lwr, upr;
Pgno chldPg;
@@ -3204,8 +3746,9 @@ int sqlite3BtreeMoveto(
int c = -1; /* pRes return if table is empty must be -1 */
lwr = 0;
upr = pPage->nCell-1;
- if( !pPage->intKey && pKey==0 ){
- return SQLITE_CORRUPT_BKPT;
+ if( !pPage->intKey && pUnKey==0 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto moveto_finish;
}
if( biasRight ){
pCur->idx = upr;
@@ -3216,44 +3759,51 @@ int sqlite3BtreeMoveto(
void *pCellKey;
i64 nCellKey;
pCur->info.nSize = 0;
+ pCur->validNKey = 1;
if( pPage->intKey ){
u8 *pCell;
pCell = findCell(pPage, pCur->idx) + pPage->childPtrSize;
if( pPage->hasData ){
u32 dummy;
- pCell += getVarint32(pCell, &dummy);
+ pCell += getVarint32(pCell, dummy);
}
- getVarint(pCell, (u64 *)&nCellKey);
- if( nCellKey<nKey ){
+ getVarint(pCell, (u64*)&nCellKey);
+ if( nCellKey==nKey ){
+ c = 0;
+ }else if( nCellKey<nKey ){
c = -1;
- }else if( nCellKey>nKey ){
- c = +1;
}else{
- c = 0;
+ assert( nCellKey>nKey );
+ c = +1;
}
}else{
int available;
pCellKey = (void *)fetchPayload(pCur, &available, 0);
nCellKey = pCur->info.nKey;
if( available>=nCellKey ){
- c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
+ c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
}else{
- pCellKey = sqliteMallocRaw( nCellKey );
- if( pCellKey==0 ) return SQLITE_NOMEM;
+ pCellKey = sqlite3Malloc( nCellKey );
+ if( pCellKey==0 ){
+ rc = SQLITE_NOMEM;
+ goto moveto_finish;
+ }
rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
- c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
- sqliteFree(pCellKey);
- if( rc ) return rc;
+ c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
+ sqlite3_free(pCellKey);
+ if( rc ) goto moveto_finish;
}
}
if( c==0 ){
- if( pPage->leafData && !pPage->leaf ){
+ pCur->info.nKey = nCellKey;
+ if( pPage->intKey && !pPage->leaf ){
lwr = pCur->idx;
upr = lwr - 1;
break;
}else{
if( pRes ) *pRes = 0;
- return SQLITE_OK;
+ rc = SQLITE_OK;
+ goto moveto_finish;
}
}
if( c<0 ){
@@ -3262,6 +3812,7 @@ int sqlite3BtreeMoveto(
upr = pCur->idx-1;
}
if( lwr>upr ){
+ pCur->info.nKey = nCellKey;
break;
}
pCur->idx = (lwr+upr)/2;
@@ -3278,18 +3829,25 @@ int sqlite3BtreeMoveto(
if( chldPg==0 ){
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
if( pRes ) *pRes = c;
- return SQLITE_OK;
+ rc = SQLITE_OK;
+ goto moveto_finish;
}
pCur->idx = lwr;
pCur->info.nSize = 0;
+ pCur->validNKey = 0;
rc = moveToChild(pCur, chldPg);
- if( rc ){
- return rc;
- }
+ if( rc ) goto moveto_finish;
}
- /* NOT REACHED */
+moveto_finish:
+ if( pKey ){
+ /* If we created our own unpacked key at the top of this
+ ** procedure, then destroy that key before returning. */
+ sqlite3VdbeDeleteUnpackedRecord(pUnKey);
+ }
+ return rc;
}
+
/*
** Return TRUE if the cursor is not pointing at an entry of the table.
**
@@ -3306,6 +3864,14 @@ int sqlite3BtreeEof(BtCursor *pCur){
}
/*
+** Return the database connection handle for a cursor.
+*/
+sqlite3 *sqlite3BtreeCursorDb(const BtCursor *pCur){
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
+ return pCur->pBtree->db;
+}
+
+/*
** Advance the cursor to the next entry in the database. If
** successful then set *pRes=0. If the cursor
** was already pointing to the last entry in the database before
@@ -3315,7 +3881,8 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
- rc = restoreOrClearCursorPosition(pCur);
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -3337,6 +3904,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
pCur->idx++;
pCur->info.nSize = 0;
+ pCur->validNKey = 0;
if( pCur->idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
@@ -3355,7 +3923,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
pPage = pCur->pPage;
}while( pCur->idx>=pPage->nCell );
*pRes = 0;
- if( pPage->leafData ){
+ if( pPage->intKey ){
rc = sqlite3BtreeNext(pCur, pRes);
}else{
rc = SQLITE_OK;
@@ -3370,6 +3938,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
return rc;
}
+
/*
** Step the cursor to the back to the previous entry in the database. If
** successful then set *pRes=0. If the cursor
@@ -3381,10 +3950,12 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
Pgno pgno;
MemPage *pPage;
- rc = restoreOrClearCursorPosition(pCur);
+ assert( cursorHoldsMutex(pCur) );
+ rc = restoreCursorPosition(pCur);
if( rc!=SQLITE_OK ){
return rc;
}
+ pCur->atLast = 0;
if( CURSOR_INVALID==pCur->eState ){
*pRes = 1;
return SQLITE_OK;
@@ -3402,7 +3973,9 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
if( !pPage->leaf ){
pgno = get4byte( findCell(pPage, pCur->idx) );
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
rc = moveToRightmost(pCur);
}else{
while( pCur->idx==0 ){
@@ -3416,7 +3989,8 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}
pCur->idx--;
pCur->info.nSize = 0;
- if( pPage->leafData && !pPage->leaf ){
+ pCur->validNKey = 0;
+ if( pPage->intKey && !pPage->leaf ){
rc = sqlite3BtreePrevious(pCur, pRes);
}else{
rc = SQLITE_OK;
@@ -3461,6 +4035,7 @@ static int allocateBtreePage(
MemPage *pTrunk = 0;
MemPage *pPrevTrunk = 0;
+ assert( sqlite3_mutex_held(pBt->mutex) );
pPage1 = pBt->pPage1;
n = get4byte(&pPage1->aData[36]);
if( n>0 ){
@@ -3473,7 +4048,7 @@ static int allocateBtreePage(
** the entire-list will be searched for that page.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( exact && nearby<=sqlite3PagerPagecount(pBt->pPager) ){
+ if( exact && nearby<=pagerPagecount(pBt->pPager) ){
u8 eType;
assert( nearby>0 );
assert( pBt->autoVacuum );
@@ -3525,7 +4100,7 @@ static int allocateBtreePage(
*ppPage = pTrunk;
pTrunk = 0;
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
- }else if( k>pBt->usableSize/4 - 8 ){
+ }else if( k>pBt->usableSize/4 - 2 ){
/* Value of k is out of range. Database corruption */
rc = SQLITE_CORRUPT_BKPT;
goto end_allocate_page;
@@ -3608,10 +4183,13 @@ static int allocateBtreePage(
iPage = get4byte(&aData[8+closest*4]);
if( !searchList || iPage==nearby ){
+ int nPage;
*pPgno = iPage;
- if( *pPgno>sqlite3PagerPagecount(pBt->pPager) ){
+ nPage = pagerPagecount(pBt->pPager);
+ if( *pPgno>nPage ){
/* Free page off the end of the file */
- return SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_BKPT;
+ goto end_allocate_page;
}
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
": %d more free pages\n",
@@ -3637,7 +4215,8 @@ static int allocateBtreePage(
}else{
/* There are no pages on the freelist, so create a new page at the
** end of the file */
- *pPgno = sqlite3PagerPagecount(pBt->pPager) + 1;
+ int nPage = pagerPagecount(pBt->pPager);
+ *pPgno = nPage + 1;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->nTrunc ){
@@ -3658,6 +4237,7 @@ static int allocateBtreePage(
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno));
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
(*pPgno)++;
+ if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; }
}
if( pBt->nTrunc ){
pBt->nTrunc = *pPgno;
@@ -3693,6 +4273,7 @@ static int freePage(MemPage *pPage){
int rc, n, k;
/* Prepare the page for freeing */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->pgno>1 );
pPage->isInit = 0;
releasePage(pPage->pParent);
@@ -3713,15 +4294,13 @@ static int freePage(MemPage *pPage){
memset(pPage->aData, 0, pPage->pBt->pageSize);
#endif
-#ifndef SQLITE_OMIT_AUTOVACUUM
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
rc = ptrmapPut(pBt, pPage->pgno, PTRMAP_FREEPAGE, 0);
if( rc ) return rc;
}
-#endif
if( n==0 ){
/* This is the first free page */
@@ -3739,14 +4318,29 @@ static int freePage(MemPage *pPage){
k = get4byte(&pTrunk->aData[4]);
if( k>=pBt->usableSize/4 - 8 ){
/* The trunk is full. Turn the page being freed into a new
- ** trunk page with no leaves. */
+ ** trunk page with no leaves.
+ **
+ ** Note that the trunk page is not really full until it contains
+ ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have
+ ** coded. But due to a coding error in versions of SQLite prior to
+ ** 3.6.0, databases with freelist trunk pages holding more than
+ ** usableSize/4 - 8 entries will be reported as corrupt. In order
+ ** to maintain backwards compatibility with older versions of SQLite,
+ ** we will contain to restrict the number of entries to usableSize/4 - 8
+ ** for now. At some point in the future (once everyone has upgraded
+ ** to 3.6.0 or later) we should consider fixing the conditional above
+ ** to read "usableSize/4-2" instead of "usableSize/4-8".
+ */
rc = sqlite3PagerWrite(pPage->pDbPage);
- if( rc ) return rc;
- put4byte(pPage->aData, pTrunk->pgno);
- put4byte(&pPage->aData[4], 0);
- put4byte(&pPage1->aData[32], pPage->pgno);
- TRACE(("FREE-PAGE: %d new trunk page replacing %d\n",
- pPage->pgno, pTrunk->pgno));
+ if( rc==SQLITE_OK ){
+ put4byte(pPage->aData, pTrunk->pgno);
+ put4byte(&pPage->aData[4], 0);
+ put4byte(&pPage1->aData[32], pPage->pgno);
+ TRACE(("FREE-PAGE: %d new trunk page replacing %d\n",
+ pPage->pgno, pTrunk->pgno));
+ }
+ }else if( k<0 ){
+ rc = SQLITE_CORRUPT;
}else{
/* Add the newly freed page as a leaf on the current trunk */
rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -3775,6 +4369,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
int nOvfl;
int ovflPageSize;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
@@ -3785,7 +4380,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
assert( ovflPgno==0 || nOvfl>0 );
while( nOvfl-- ){
MemPage *pOvfl;
- if( ovflPgno==0 || ovflPgno>sqlite3PagerPagecount(pBt->pPager) ){
+ if( ovflPgno==0 || ovflPgno>pagerPagecount(pBt->pPager) ){
return SQLITE_CORRUPT_BKPT;
}
@@ -3831,6 +4426,8 @@ static int fillInCell(
int nHeader;
CellInfo info;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
/* Fill in the header. */
nHeader = 0;
if( !pPage->leaf ){
@@ -3934,15 +4531,27 @@ static int fillInCell(
return SQLITE_OK;
}
+
/*
** Change the MemPage.pParent pointer on the page whose number is
** given in the second argument so that MemPage.pParent holds the
** pointer in the third argument.
-*/
-static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
+**
+** If the final argument, updatePtrmap, is non-zero and the database
+** is an auto-vacuum database, then the pointer-map entry for pgno
+** is updated.
+*/
+static int reparentPage(
+ BtShared *pBt, /* B-Tree structure */
+ Pgno pgno, /* Page number of child being adopted */
+ MemPage *pNewParent, /* New parent of pgno */
+ int idx, /* Index of child page pgno in pNewParent */
+ int updatePtrmap /* If true, update pointer-map for pgno */
+){
MemPage *pThis;
DbPage *pDbPage;
+ assert( sqlite3_mutex_held(pBt->mutex) );
assert( pNewParent!=0 );
if( pgno==0 ) return SQLITE_OK;
assert( pBt->pPager!=0 );
@@ -3950,7 +4559,7 @@ static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
if( pDbPage ){
pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage);
if( pThis->isInit ){
- assert( pThis->aData==(sqlite3PagerGetData(pDbPage)) );
+ assert( pThis->aData==sqlite3PagerGetData(pDbPage) );
if( pThis->pParent!=pNewParent ){
if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage);
pThis->pParent = pNewParent;
@@ -3961,11 +4570,26 @@ static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
sqlite3PagerUnref(pDbPage);
}
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM && updatePtrmap ){
return ptrmapPut(pBt, pgno, PTRMAP_BTREE, pNewParent->pgno);
}
+
+#ifndef NDEBUG
+ /* If the updatePtrmap flag was clear, assert that the entry in the
+ ** pointer-map is already correct.
+ */
+ if( ISAUTOVACUUM ){
+ pDbPage = sqlite3PagerLookup(pBt->pPager,PTRMAP_PAGENO(pBt,pgno));
+ if( pDbPage ){
+ u8 eType;
+ Pgno ii;
+ int rc = ptrmapGet(pBt, pgno, &eType, &ii);
+ assert( rc==SQLITE_OK && ii==pNewParent->pgno && eType==PTRMAP_BTREE );
+ sqlite3PagerUnref(pDbPage);
+ }
+ }
#endif
+
return SQLITE_OK;
}
@@ -3980,24 +4604,24 @@ static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
**
** This routine gets called after you memcpy() one page into
** another.
+**
+** If updatePtrmap is true, then the pointer-map entries for all child
+** pages of pPage are updated.
*/
-static int reparentChildPages(MemPage *pPage){
- int i;
- BtShared *pBt = pPage->pBt;
+static int reparentChildPages(MemPage *pPage, int updatePtrmap){
int rc = SQLITE_OK;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ if( !pPage->leaf ){
+ int i;
+ BtShared *pBt = pPage->pBt;
+ Pgno iRight = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- if( pPage->leaf ) return SQLITE_OK;
-
- for(i=0; i<pPage->nCell; i++){
- u8 *pCell = findCell(pPage, i);
- if( !pPage->leaf ){
- rc = reparentPage(pBt, get4byte(pCell), pPage, i);
+ for(i=0; i<pPage->nCell; i++){
+ u8 *pCell = findCell(pPage, i);
+ rc = reparentPage(pBt, get4byte(pCell), pPage, i, updatePtrmap);
if( rc!=SQLITE_OK ) return rc;
}
- }
- if( !pPage->leaf ){
- rc = reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]),
- pPage, i);
+ rc = reparentPage(pBt, iRight, pPage, i, updatePtrmap);
pPage->idxShift = 0;
}
return rc;
@@ -4020,6 +4644,7 @@ static void dropCell(MemPage *pPage, int idx, int sz){
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
ptr = &data[pPage->cellOffset + 2*idx];
pc = get2byte(ptr);
@@ -4072,7 +4697,7 @@ static int insertCell(
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( sz==cellSizePtr(pPage, pCell) );
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
@@ -4084,6 +4709,11 @@ static int insertCell(
pPage->aOvfl[j].idx = i;
pPage->nFree = 0;
}else{
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
hdr = pPage->hdrOffset;
top = get2byte(&data[hdr+5]);
@@ -4091,8 +4721,7 @@ static int insertCell(
end = cellOffset + 2*pPage->nCell + 2;
ins = cellOffset + 2*i;
if( end > top - sz ){
- int rc = defragmentPage(pPage);
- if( rc!=SQLITE_OK ) return rc;
+ defragmentPage(pPage);
top = get2byte(&data[hdr+5]);
assert( end + sz <= top );
}
@@ -4119,7 +4748,7 @@ static int insertCell(
assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
- int rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
+ rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
if( rc!=SQLITE_OK ) return rc;
}
}
@@ -4137,7 +4766,7 @@ static void assemblePage(
MemPage *pPage, /* The page to be assemblied */
int nCell, /* The number of cells to add to this page */
u8 **apCell, /* Pointers to cell bodies */
- int *aSize /* Sizes of the cells */
+ u16 *aSize /* Sizes of the cells */
){
int i; /* Loop counter */
int totalSize; /* Total size of all cells */
@@ -4147,6 +4776,7 @@ static void assemblePage(
u8 *data; /* Data for the page */
assert( pPage->nOverflow==0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
totalSize = 0;
for(i=0; i<nCell; i++){
totalSize += aSize[i];
@@ -4214,13 +4844,15 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
MemPage *pNew;
Pgno pgnoNew;
u8 *pCell;
- int szCell;
+ u16 szCell;
CellInfo info;
BtShared *pBt = pPage->pBt;
int parentIdx = pParent->nCell; /* pParent new divider cell index */
int parentSize; /* Size of new divider cell */
u8 parentCell[64]; /* Space for the new divider cell */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
/* Allocate a new page. Insert the overflow cell from pPage
** into it. Then remove the overflow cell from pPage.
*/
@@ -4241,28 +4873,32 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
/* pPage is currently the right-child of pParent. Change this
** so that the right-child is the new page allocated above and
** pPage is the next-to-right child.
+ **
+ ** Ignore the return value of the call to fillInCell(). fillInCell()
+ ** may only return other than SQLITE_OK if it is required to allocate
+ ** one or more overflow pages. Since an internal table B-Tree cell
+ ** may never spill over onto an overflow page (it is a maximum of
+ ** 13 bytes in size), it is not neccessary to check the return code.
+ **
+ ** Similarly, the insertCell() function cannot fail if the page
+ ** being inserted into is already writable and the cell does not
+ ** contain an overflow pointer. So ignore this return code too.
*/
assert( pPage->nCell>0 );
pCell = findCell(pPage, pPage->nCell-1);
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
- rc = fillInCell(pParent, parentCell, 0, info.nKey, 0, 0, 0, &parentSize);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ fillInCell(pParent, parentCell, 0, info.nKey, 0, 0, 0, &parentSize);
assert( parentSize<64 );
- rc = insertCell(pParent, parentIdx, parentCell, parentSize, 0, 4);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ assert( sqlite3PagerIswriteable(pParent->pDbPage) );
+ insertCell(pParent, parentIdx, parentCell, parentSize, 0, 4);
put4byte(findOverflowCell(pParent,parentIdx), pPage->pgno);
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew);
-#ifndef SQLITE_OMIT_AUTOVACUUM
/* If this is an auto-vacuum database, update the pointer map
** with entries for the new page, and any pointer from the
** cell on the page to an overflow page.
*/
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
rc = ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno);
if( rc==SQLITE_OK ){
rc = ptrmapPutOvfl(pNew, 0);
@@ -4272,7 +4908,6 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
return rc;
}
}
-#endif
/* Release the reference to the new page and balance the parent page,
** in case the divider cell inserted caused it to become overfull.
@@ -4328,7 +4963,9 @@ static int balance_nonroot(MemPage *pPage){
int usableSpace; /* Bytes in pPage beyond the header */
int pageFlags; /* Value of pPage->aData[0] */
int subtotal; /* Subtotal of bytes in cells on one page */
- int iSpace = 0; /* First unused byte of aSpace[] */
+ int iSpace1 = 0; /* First unused byte of aSpace1[] */
+ int iSpace2 = 0; /* First unused byte of aSpace2[] */
+ int szScratch; /* Size of scratch memory requested */
MemPage *apOld[NB]; /* pPage and up to two siblings */
Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */
MemPage *apCopy[NB]; /* Private copies of apOld[] pages */
@@ -4338,24 +4975,26 @@ static int balance_nonroot(MemPage *pPage){
int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
int szNew[NB+2]; /* Combined size of cells place on i-th page */
u8 **apCell = 0; /* All cells begin balanced */
- int *szCell; /* Local size of all cells in apCell[] */
- u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
- u8 *aSpace; /* Space to hold copies of dividers cells */
-#ifndef SQLITE_OMIT_AUTOVACUUM
+ u16 *szCell; /* Local size of all cells in apCell[] */
+ u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
+ u8 *aSpace1; /* Space for copies of dividers cells before balance */
+ u8 *aSpace2 = 0; /* Space for overflow dividers cells after balance */
u8 *aFrom = 0;
-#endif
+
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
/*
** Find the parent page.
*/
assert( pPage->isInit );
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) || pPage->nOverflow==1 );
pBt = pPage->pBt;
pParent = pPage->pParent;
assert( pParent );
if( SQLITE_OK!=(rc = sqlite3PagerWrite(pParent->pDbPage)) ){
return rc;
}
+
TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
#ifndef SQLITE_OMIT_QUICKBALANCE
@@ -4369,12 +5008,12 @@ static int balance_nonroot(MemPage *pPage){
*/
if( pPage->leaf &&
pPage->intKey &&
- pPage->leafData &&
pPage->nOverflow==1 &&
pPage->aOvfl[0].idx==pPage->nCell &&
pPage->pParent->pgno!=1 &&
get4byte(&pParent->aData[pParent->hdrOffset+8])==pPage->pgno
){
+ assert( pPage->intKey );
/*
** TODO: Check the siblings to the left of pPage. It may be that
** they are not full and no new page is required.
@@ -4383,6 +5022,10 @@ static int balance_nonroot(MemPage *pPage){
}
#endif
+ if( SQLITE_OK!=(rc = sqlite3PagerWrite(pPage->pDbPage)) ){
+ return rc;
+ }
+
/*
** Find the cell in the parent page whose left child points back
** to pPage. The "idx" variable is the index of that cell. If pPage
@@ -4445,38 +5088,41 @@ static int balance_nonroot(MemPage *pPage){
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
}
- /* Make nMaxCells a multiple of 2 in order to preserve 8-byte
+ /* Make nMaxCells a multiple of 4 in order to preserve 8-byte
** alignment */
- nMaxCells = (nMaxCells + 1)&~1;
+ nMaxCells = (nMaxCells + 3)&~3;
/*
** Allocate space for memory structures
*/
- apCell = sqliteMallocRaw(
- nMaxCells*sizeof(u8*) /* apCell */
- + nMaxCells*sizeof(int) /* szCell */
- + ROUND8(sizeof(MemPage))*NB /* aCopy */
- + pBt->pageSize*(5+NB) /* aSpace */
- + (ISAUTOVACUUM ? nMaxCells : 0) /* aFrom */
- );
+ szScratch =
+ nMaxCells*sizeof(u8*) /* apCell */
+ + nMaxCells*sizeof(u16) /* szCell */
+ + (ROUND8(sizeof(MemPage))+pBt->pageSize)*NB /* aCopy */
+ + pBt->pageSize /* aSpace1 */
+ + (ISAUTOVACUUM ? nMaxCells : 0); /* aFrom */
+ apCell = sqlite3ScratchMalloc( szScratch );
if( apCell==0 ){
rc = SQLITE_NOMEM;
goto balance_cleanup;
}
- szCell = (int*)&apCell[nMaxCells];
+ szCell = (u16*)&apCell[nMaxCells];
aCopy[0] = (u8*)&szCell[nMaxCells];
assert( ((aCopy[0] - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */
for(i=1; i<NB; i++){
aCopy[i] = &aCopy[i-1][pBt->pageSize+ROUND8(sizeof(MemPage))];
assert( ((aCopy[i] - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */
}
- aSpace = &aCopy[NB-1][pBt->pageSize+ROUND8(sizeof(MemPage))];
- assert( ((aSpace - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- aFrom = &aSpace[5*pBt->pageSize];
+ aSpace1 = &aCopy[NB-1][pBt->pageSize+ROUND8(sizeof(MemPage))];
+ assert( ((aSpace1 - (u8*)apCell) & 7)==0 ); /* 8-byte alignment required */
+ if( ISAUTOVACUUM ){
+ aFrom = &aSpace1[pBt->pageSize];
+ }
+ aSpace2 = sqlite3PageMalloc(pBt->pageSize);
+ if( aSpace2==0 ){
+ rc = SQLITE_NOMEM;
+ goto balance_cleanup;
}
-#endif
/*
** Make copies of the content of pPage and its siblings into aOld[].
@@ -4485,23 +5131,21 @@ static int balance_nonroot(MemPage *pPage){
** process of being overwritten.
*/
for(i=0; i<nOld; i++){
- MemPage *p = apCopy[i] = (MemPage*)&aCopy[i][pBt->pageSize];
- p->aData = &((u8*)p)[-pBt->pageSize];
- memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage));
- /* The memcpy() above changes the value of p->aData so we have to
- ** set it again. */
- p->aData = &((u8*)p)[-pBt->pageSize];
+ MemPage *p = apCopy[i] = (MemPage*)aCopy[i];
+ memcpy(p, apOld[i], sizeof(MemPage));
+ p->aData = (void*)&p[1];
+ memcpy(p->aData, apOld[i]->aData, pBt->pageSize);
}
/*
** Load pointers to all cells on sibling pages and the divider cells
** into the local apCell[] array. Make copies of the divider cells
- ** into space obtained form aSpace[] and remove the the divider Cells
+ ** into space obtained form aSpace1[] and remove the the divider Cells
** from pParent.
**
** If the siblings are on leaf pages, then the child pointers of the
** divider cells are stripped from the cells before they are copied
- ** into aSpace[]. In this way, all cells in apCell[] are without
+ ** into aSpace1[]. In this way, all cells in apCell[] are without
** child pointers. If siblings are not leaves, then all cell in
** apCell[] include child pointers. Either way, all cells in apCell[]
** are alike.
@@ -4511,7 +5155,7 @@ static int balance_nonroot(MemPage *pPage){
*/
nCell = 0;
leafCorrection = pPage->leaf*4;
- leafData = pPage->leafData && pPage->leaf;
+ leafData = pPage->hasData;
for(i=0; i<nOld; i++){
MemPage *pOld = apCopy[i];
int limit = pOld->nCell+pOld->nOverflow;
@@ -4519,8 +5163,7 @@ static int balance_nonroot(MemPage *pPage){
assert( nCell<nMaxCells );
apCell[nCell] = findOverflowCell(pOld, j);
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
int a;
aFrom[nCell] = i;
for(a=0; a<pOld->nOverflow; a++){
@@ -4530,11 +5173,10 @@ static int balance_nonroot(MemPage *pPage){
}
}
}
-#endif
nCell++;
}
if( i<nOld-1 ){
- int sz = cellSizePtr(pParent, apDiv[i]);
+ u16 sz = cellSizePtr(pParent, apDiv[i]);
if( leafData ){
/* With the LEAFDATA flag, pParent cells hold only INTKEYs that
** are duplicates of keys on the child pages. We need to remove
@@ -4546,16 +5188,15 @@ static int balance_nonroot(MemPage *pPage){
u8 *pTemp;
assert( nCell<nMaxCells );
szCell[nCell] = sz;
- pTemp = &aSpace[iSpace];
- iSpace += sz;
- assert( iSpace<=pBt->pageSize*5 );
+ pTemp = &aSpace1[iSpace1];
+ iSpace1 += sz;
+ assert( sz<=pBt->pageSize/4 );
+ assert( iSpace1<=pBt->pageSize );
memcpy(pTemp, apDiv[i], sz);
apCell[nCell] = pTemp+leafCorrection;
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
aFrom[nCell] = 0xFF;
}
-#endif
dropCell(pParent, nxDiv, sz);
szCell[nCell] -= leafCorrection;
assert( get4byte(pTemp)==pgnoOld[i] );
@@ -4666,7 +5307,6 @@ static int balance_nonroot(MemPage *pPage){
apNew[i] = pNew;
nNew++;
}
- zeroPage(pNew, pageFlags);
}
/* Free any old pages that were not reused as new pages.
@@ -4733,28 +5373,30 @@ static int balance_nonroot(MemPage *pPage){
MemPage *pNew = apNew[i];
assert( j<nMaxCells );
assert( pNew->pgno==pgnoNew[i] );
+ zeroPage(pNew, pageFlags);
assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]);
assert( pNew->nCell>0 || (nNew==1 && cntNew[0]==0) );
assert( pNew->nOverflow==0 );
-#ifndef SQLITE_OMIT_AUTOVACUUM
/* If this is an auto-vacuum database, update the pointer map entries
** that point to the siblings that were rearranged. These can be: left
** children of cells, the right-child of the page, or overflow pages
** pointed to by cells.
*/
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
for(k=j; k<cntNew[i]; k++){
assert( k<nMaxCells );
if( aFrom[k]==0xFF || apCopy[aFrom[k]]->pgno!=pNew->pgno ){
rc = ptrmapPutOvfl(pNew, k-j);
+ if( rc==SQLITE_OK && leafCorrection==0 ){
+ rc = ptrmapPut(pBt, get4byte(apCell[k]), PTRMAP_BTREE, pNew->pgno);
+ }
if( rc!=SQLITE_OK ){
goto balance_cleanup;
}
}
}
}
-#endif
j = cntNew[i];
@@ -4769,9 +5411,17 @@ static int balance_nonroot(MemPage *pPage){
assert( j<nMaxCells );
pCell = apCell[j];
sz = szCell[j] + leafCorrection;
+ pTemp = &aSpace2[iSpace2];
if( !pNew->leaf ){
memcpy(&pNew->aData[8], pCell, 4);
- pTemp = 0;
+ if( ISAUTOVACUUM
+ && (aFrom[j]==0xFF || apCopy[aFrom[j]]->pgno!=pNew->pgno)
+ ){
+ rc = ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno);
+ if( rc!=SQLITE_OK ){
+ goto balance_cleanup;
+ }
+ }
}else if( leafData ){
/* If the tree is a leaf-data tree, and the siblings are leaves,
** then there is no divider cell in apCell[]. Instead, the divider
@@ -4781,21 +5431,16 @@ static int balance_nonroot(MemPage *pPage){
CellInfo info;
j--;
sqlite3BtreeParseCellPtr(pNew, apCell[j], &info);
- pCell = &aSpace[iSpace];
+ pCell = pTemp;
fillInCell(pParent, pCell, 0, info.nKey, 0, 0, 0, &sz);
- iSpace += sz;
- assert( iSpace<=pBt->pageSize*5 );
pTemp = 0;
}else{
pCell -= 4;
- pTemp = &aSpace[iSpace];
- iSpace += sz;
- assert( iSpace<=pBt->pageSize*5 );
/* Obscure case for non-leaf-data trees: If the cell at pCell was
- ** previously stored on a leaf node, and it's reported size was 4
+ ** previously stored on a leaf node, and its reported size was 4
** bytes, then it may actually be smaller than this
** (see sqlite3BtreeParseCellPtr(), 4 bytes is the minimum size of
- ** any cell). But it's important to pass the correct size to
+ ** any cell). But it is important to pass the correct size to
** insertCell(), so reparse the cell now.
**
** Note that this can never happen in an SQLite data file, as all
@@ -4807,30 +5452,47 @@ static int balance_nonroot(MemPage *pPage){
sz = cellSizePtr(pParent, pCell);
}
}
+ iSpace2 += sz;
+ assert( sz<=pBt->pageSize/4 );
+ assert( iSpace2<=pBt->pageSize );
rc = insertCell(pParent, nxDiv, pCell, sz, pTemp, 4);
if( rc!=SQLITE_OK ) goto balance_cleanup;
put4byte(findOverflowCell(pParent,nxDiv), pNew->pgno);
-#ifndef SQLITE_OMIT_AUTOVACUUM
+
/* If this is an auto-vacuum database, and not a leaf-data tree,
** then update the pointer map with an entry for the overflow page
** that the cell just inserted points to (if any).
*/
- if( pBt->autoVacuum && !leafData ){
+ if( ISAUTOVACUUM && !leafData ){
rc = ptrmapPutOvfl(pParent, nxDiv);
if( rc!=SQLITE_OK ){
goto balance_cleanup;
}
}
-#endif
j++;
nxDiv++;
}
+
+ /* Set the pointer-map entry for the new sibling page. */
+ if( ISAUTOVACUUM ){
+ rc = ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno);
+ if( rc!=SQLITE_OK ){
+ goto balance_cleanup;
+ }
+ }
}
assert( j==nCell );
assert( nOld>0 );
assert( nNew>0 );
if( (pageFlags & PTF_LEAF)==0 ){
- memcpy(&apNew[nNew-1]->aData[8], &apCopy[nOld-1]->aData[8], 4);
+ u8 *zChild = &apCopy[nOld-1]->aData[8];
+ memcpy(&apNew[nNew-1]->aData[8], zChild, 4);
+ if( ISAUTOVACUUM ){
+ rc = ptrmapPut(pBt, get4byte(zChild), PTRMAP_BTREE, apNew[nNew-1]->pgno);
+ if( rc!=SQLITE_OK ){
+ goto balance_cleanup;
+ }
+ }
}
if( nxDiv==pParent->nCell+pParent->nOverflow ){
/* Right-most sibling is the right-most child of pParent */
@@ -4845,10 +5507,10 @@ static int balance_nonroot(MemPage *pPage){
** Reparent children of all cells.
*/
for(i=0; i<nNew; i++){
- rc = reparentChildPages(apNew[i]);
+ rc = reparentChildPages(apNew[i], 0);
if( rc!=SQLITE_OK ) goto balance_cleanup;
}
- rc = reparentChildPages(pParent);
+ rc = reparentChildPages(pParent, 0);
if( rc!=SQLITE_OK ) goto balance_cleanup;
/*
@@ -4857,13 +5519,16 @@ static int balance_nonroot(MemPage *pPage){
** But the parent page will always be initialized.
*/
assert( pParent->isInit );
+ sqlite3ScratchFree(apCell);
+ apCell = 0;
rc = balance(pParent, 0);
/*
** Cleanup before returning.
*/
balance_cleanup:
- sqliteFree(apCell);
+ sqlite3PageFree(aSpace2);
+ sqlite3ScratchFree(apCell);
for(i=0; i<nOld; i++){
releasePage(apOld[i]);
}
@@ -4888,15 +5553,16 @@ static int balance_shallower(MemPage *pPage){
BtShared *pBt; /* The main BTree structure */
int mxCellPerPage; /* Maximum number of cells per page */
u8 **apCell; /* All cells from pages being balanced */
- int *szCell; /* Local size of all cells */
+ u16 *szCell; /* Local size of all cells */
assert( pPage->pParent==0 );
assert( pPage->nCell==0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pBt = pPage->pBt;
mxCellPerPage = MX_CELL(pBt);
- apCell = sqliteMallocRaw( mxCellPerPage*(sizeof(u8*)+sizeof(int)) );
+ apCell = sqlite3Malloc( mxCellPerPage*(sizeof(u8*)+sizeof(u16)) );
if( apCell==0 ) return SQLITE_NOMEM;
- szCell = (int*)&apCell[mxCellPerPage];
+ szCell = (u16*)&apCell[mxCellPerPage];
if( pPage->leaf ){
/* The table is completely empty */
TRACE(("BALANCE: empty table %d\n", pPage->pgno));
@@ -4915,7 +5581,7 @@ static int balance_shallower(MemPage *pPage){
*/
pgnoChild = get4byte(&pPage->aData[pPage->hdrOffset+8]);
assert( pgnoChild>0 );
- assert( pgnoChild<=sqlite3PagerPagecount(pPage->pBt->pPager) );
+ assert( pgnoChild<=pagerPagecount(pPage->pBt->pPager) );
rc = sqlite3BtreeGetPage(pPage->pBt, pgnoChild, &pChild, 0);
if( rc ) goto end_shallow_balance;
if( pPage->pgno==1 ){
@@ -4952,10 +5618,9 @@ static int balance_shallower(MemPage *pPage){
TRACE(("BALANCE: transfer child %d into root %d\n",
pChild->pgno, pPage->pgno));
}
- rc = reparentChildPages(pPage);
+ rc = reparentChildPages(pPage, 1);
assert( pPage->nOverflow==0 );
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
int i;
for(i=0; i<pPage->nCell; i++){
rc = ptrmapPutOvfl(pPage, i);
@@ -4964,11 +5629,10 @@ static int balance_shallower(MemPage *pPage){
}
}
}
-#endif
releasePage(pChild);
}
end_shallow_balance:
- sqliteFree(apCell);
+ sqlite3_free(apCell);
return rc;
}
@@ -4996,6 +5660,7 @@ static int balance_deeper(MemPage *pPage){
assert( pPage->pParent==0 );
assert( pPage->nOverflow>0 );
pBt = pPage->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
rc = allocateBtreePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
if( rc ) return rc;
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
@@ -5006,7 +5671,7 @@ static int balance_deeper(MemPage *pPage){
cdata = pChild->aData;
memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
memcpy(&cdata[brk], &data[brk], usableSize-brk);
- assert( pChild->isInit==0 );
+ if( pChild->isInit ) return SQLITE_CORRUPT;
rc = sqlite3BtreeInitPage(pChild, pPage);
if( rc ) goto balancedeeper_out;
memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0]));
@@ -5018,20 +5683,21 @@ static int balance_deeper(MemPage *pPage){
zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
+ if( ISAUTOVACUUM ){
int i;
rc = ptrmapPut(pBt, pChild->pgno, PTRMAP_BTREE, pPage->pgno);
if( rc ) goto balancedeeper_out;
for(i=0; i<pChild->nCell; i++){
rc = ptrmapPutOvfl(pChild, i);
if( rc!=SQLITE_OK ){
- return rc;
+ goto balancedeeper_out;
}
}
+ rc = reparentChildPages(pChild, 1);
+ }
+ if( rc==SQLITE_OK ){
+ rc = balance_nonroot(pChild);
}
-#endif
- rc = balance_nonroot(pChild);
balancedeeper_out:
releasePage(pChild);
@@ -5044,8 +5710,10 @@ balancedeeper_out:
*/
static int balance(MemPage *pPage, int insert){
int rc = SQLITE_OK;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->pParent==0 ){
- if( pPage->nOverflow>0 ){
+ rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( rc==SQLITE_OK && pPage->nOverflow>0 ){
rc = balance_deeper(pPage);
}
if( rc==SQLITE_OK && pPage->nCell==0 ){
@@ -5068,30 +5736,62 @@ static int balance(MemPage *pPage, int insert){
** is not in the ReadUncommmitted state, then this routine returns
** SQLITE_LOCKED.
**
-** In addition to checking for read-locks (where a read-lock
-** means a cursor opened with wrFlag==0) this routine also moves
-** all write cursors so that they are pointing to the
-** first Cell on the root page. This is necessary because an insert
-** or delete might change the number of cells on a page or delete
-** a page entirely and we do not want to leave any cursors
-** pointing to non-existant pages or cells.
-*/
-static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
+** As well as cursors with wrFlag==0, cursors with wrFlag==1 and
+** isIncrblobHandle==1 are also considered 'read' cursors. Incremental
+** blob cursors are used for both reading and writing.
+**
+** When pgnoRoot is the root page of an intkey table, this function is also
+** responsible for invalidating incremental blob cursors when the table row
+** on which they are opened is deleted or modified. Cursors are invalidated
+** according to the following rules:
+**
+** 1) When BtreeClearTable() is called to completely delete the contents
+** of a B-Tree table, pExclude is set to zero and parameter iRow is
+** set to non-zero. In this case all incremental blob cursors open
+** on the table rooted at pgnoRoot are invalidated.
+**
+** 2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to
+** modify a table row via an SQL statement, pExclude is set to the
+** write cursor used to do the modification and parameter iRow is set
+** to the integer row id of the B-Tree entry being modified. Unless
+** pExclude is itself an incremental blob cursor, then all incremental
+** blob cursors open on row iRow of the B-Tree are invalidated.
+**
+** 3) If both pExclude and iRow are set to zero, no incremental blob
+** cursors are invalidated.
+*/
+static int checkReadLocks(
+ Btree *pBtree,
+ Pgno pgnoRoot,
+ BtCursor *pExclude,
+ i64 iRow
+){
BtCursor *p;
BtShared *pBt = pBtree->pBt;
- sqlite3 *db = pBtree->pSqlite;
+ sqlite3 *db = pBtree->db;
+ assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt->pCursor; p; p=p->pNext){
if( p==pExclude ) continue;
- if( p->eState!=CURSOR_VALID ) continue;
if( p->pgnoRoot!=pgnoRoot ) continue;
- if( p->wrFlag==0 ){
- sqlite3 *dbOther = p->pBtree->pSqlite;
+#ifndef SQLITE_OMIT_INCRBLOB
+ if( p->isIncrblobHandle && (
+ (!pExclude && iRow)
+ || (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow)
+ )){
+ p->eState = CURSOR_INVALID;
+ }
+#endif
+ if( p->eState!=CURSOR_VALID ) continue;
+ if( p->wrFlag==0
+#ifndef SQLITE_OMIT_INCRBLOB
+ || p->isIncrblobHandle
+#endif
+ ){
+ sqlite3 *dbOther = p->pBtree->db;
if( dbOther==0 ||
(dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){
return SQLITE_LOCKED;
}
- }else if( p->pPage->pgno!=p->pgnoRoot ){
- moveToRoot(p);
}
}
return SQLITE_OK;
@@ -5117,49 +5817,58 @@ int sqlite3BtreeInsert(
int loc;
int szNew;
MemPage *pPage;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
+ assert( cursorHoldsMutex(pCur) );
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction before doing an insert */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ return rc;
}
assert( !pBt->readOnly );
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Cursor not open for writing */
}
- if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, nKey) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
+ if( pCur->eState==CURSOR_FAULT ){
+ return pCur->skip;
+ }
/* Save the positions of any other cursors open on this table */
clearCursorPosition(pCur);
if(
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
- SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
+ SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, 0, nKey, appendBias, &loc))
){
return rc;
}
pPage = pCur->pPage;
assert( pPage->intKey || nKey>=0 );
- assert( pPage->leaf || !pPage->leafData );
+ assert( pPage->leaf || !pPage->intKey );
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
pCur->pgnoRoot, nKey, nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
- rc = sqlite3PagerWrite(pPage->pDbPage);
- if( rc ) return rc;
- newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
+ allocateTempSpace(pBt);
+ newCell = pBt->pTmpSpace;
if( newCell==0 ) return SQLITE_NOMEM;
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
assert( szNew<=MX_CELL_SIZE(pBt) );
if( loc==0 && CURSOR_VALID==pCur->eState ){
- int szOld;
+ u16 szOld;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+ rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( rc ){
+ goto end_insert;
+ }
oldCell = findCell(pPage, pCur->idx);
if( !pPage->leaf ){
memcpy(newCell, oldCell, 4);
@@ -5172,19 +5881,17 @@ int sqlite3BtreeInsert(
assert( pPage->leaf );
pCur->idx++;
pCur->info.nSize = 0;
+ pCur->validNKey = 0;
}else{
assert( pPage->leaf );
}
rc = insertCell(pPage, pCur->idx, newCell, szNew, 0, 0);
if( rc!=SQLITE_OK ) goto end_insert;
rc = balance(pPage, 1);
- /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
- /* fflush(stdout); */
if( rc==SQLITE_OK ){
moveToRoot(pCur);
}
end_insert:
- sqliteFree(newCell);
return rc;
}
@@ -5197,21 +5904,27 @@ int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pCell;
int rc;
Pgno pgnoChild = 0;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
+ assert( cursorHoldsMutex(pCur) );
assert( pPage->isInit );
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction before doing a delete */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ return rc;
}
assert( !pBt->readOnly );
+ if( pCur->eState==CURSOR_FAULT ){
+ return pCur->skip;
+ }
if( pCur->idx >= pPage->nCell ){
return SQLITE_ERROR; /* The cursor is not pointing to anything */
}
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Did not open this cursor for writing */
}
- if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, pCur->info.nKey) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
@@ -5221,14 +5934,14 @@ int sqlite3BtreeDelete(BtCursor *pCur){
** that the entry will be deleted from.
*/
if(
- (rc = restoreOrClearCursorPosition(pCur))!=0 ||
+ (rc = restoreCursorPosition(pCur))!=0 ||
(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 ||
(rc = sqlite3PagerWrite(pPage->pDbPage))!=0
){
return rc;
}
- /* Locate the cell within it's page and leave pCell pointing to the
+ /* Locate the cell within its page and leave pCell pointing to the
** data. The clearCell() call frees any overflow pages associated with the
** cell. The cell itself is still intact.
*/
@@ -5237,7 +5950,9 @@ int sqlite3BtreeDelete(BtCursor *pCur){
pgnoChild = get4byte(pCell);
}
rc = clearCell(pPage, pCell);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
if( !pPage->leaf ){
/*
@@ -5249,41 +5964,39 @@ int sqlite3BtreeDelete(BtCursor *pCur){
*/
BtCursor leafCur;
unsigned char *pNext;
- int szNext; /* The compiler warning is wrong: szNext is always
- ** initialized before use. Adding an extra initialization
- ** to silence the compiler slows down the code. */
int notUsed;
unsigned char *tempCell = 0;
- assert( !pPage->leafData );
+ assert( !pPage->intKey );
sqlite3BtreeGetTempCursor(pCur, &leafCur);
rc = sqlite3BtreeNext(&leafCur, &notUsed);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(leafCur.pPage->pDbPage);
}
if( rc==SQLITE_OK ){
+ u16 szNext;
TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
pNext = findCell(leafCur.pPage, leafCur.idx);
szNext = cellSizePtr(leafCur.pPage, pNext);
assert( MX_CELL_SIZE(pBt)>=szNext+4 );
- tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
+ allocateTempSpace(pBt);
+ tempCell = pBt->pTmpSpace;
if( tempCell==0 ){
rc = SQLITE_NOMEM;
}
+ if( rc==SQLITE_OK ){
+ rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell, 0);
+ }
+ if( rc==SQLITE_OK ){
+ put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
+ rc = balance(pPage, 0);
+ }
+ if( rc==SQLITE_OK ){
+ dropCell(leafCur.pPage, leafCur.idx, szNext);
+ rc = balance(leafCur.pPage, 0);
+ }
}
- if( rc==SQLITE_OK ){
- rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell, 0);
- }
- if( rc==SQLITE_OK ){
- put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
- rc = balance(pPage, 0);
- }
- if( rc==SQLITE_OK ){
- dropCell(leafCur.pPage, leafCur.idx, szNext);
- rc = balance(leafCur.pPage, 0);
- }
- sqliteFree(tempCell);
sqlite3BtreeReleaseTempCursor(&leafCur);
}else{
TRACE(("DELETE: table=%d delete from leaf %d\n",
@@ -5308,20 +6021,25 @@ int sqlite3BtreeDelete(BtCursor *pCur){
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
** BTREE_ZERODATA Used for SQL indices
*/
-int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+static int btreeCreateTable(Btree *p, int *piTable, int flags){
BtShared *pBt = p->pBt;
MemPage *pRoot;
Pgno pgnoRoot;
int rc;
+
+ assert( sqlite3BtreeHoldsMutex(p) );
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction first */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ return rc;
}
assert( !pBt->readOnly );
#ifdef SQLITE_OMIT_AUTOVACUUM
rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
#else
if( pBt->autoVacuum ){
Pgno pgnoMove; /* Move a page here to make room for the root-page */
@@ -5339,13 +6057,15 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
** created so far, so the new root-page is (meta[3]+1).
*/
rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
pgnoRoot++;
/* The new root-page may not be allocated on a pointer-map page, or the
** PENDING_BYTE page.
*/
- if( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) ||
+ while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) ||
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
pgnoRoot++;
}
@@ -5389,7 +6109,7 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
releasePage(pRoot);
return rc;
}
- rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove);
+ rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0);
releasePage(pRoot);
/* Obtain the page at pgnoRoot */
@@ -5432,6 +6152,14 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
*piTable = (int)pgnoRoot;
return SQLITE_OK;
}
+int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+ int rc;
+ sqlite3BtreeEnter(p);
+ p->pBt->db = p->db;
+ rc = btreeCreateTable(p, piTable, flags);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
/*
** Erase the given database page and all its children. Return
@@ -5448,7 +6176,8 @@ static int clearDatabasePage(
unsigned char *pCell;
int i;
- if( pgno>sqlite3PagerPagecount(pBt->pPager) ){
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ if( pgno>pagerPagecount(pBt->pPager) ){
return SQLITE_CORRUPT_BKPT;
}
@@ -5490,20 +6219,19 @@ cleardatabasepage_out:
int sqlite3BtreeClearTable(Btree *p, int iTable){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
if( p->inTrans!=TRANS_WRITE ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
- }
- rc = checkReadLocks(p, iTable, 0);
- if( rc ){
- return rc;
- }
-
- /* Save the position of all cursors open on this table */
- if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
- return rc;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else if( (rc = checkReadLocks(p, iTable, 0, 1))!=SQLITE_OK ){
+ /* nothing to do */
+ }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
+ /* nothing to do */
+ }else{
+ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
}
-
- return clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -5526,11 +6254,12 @@ int sqlite3BtreeClearTable(Btree *p, int iTable){
** The last root page is recorded in meta[3] and the value of
** meta[3] is updated by this procedure.
*/
-int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+static int btreeDropTable(Btree *p, int iTable, int *piMoved){
int rc;
MemPage *pPage = 0;
BtShared *pBt = p->pBt;
+ assert( sqlite3BtreeHoldsMutex(p) );
if( p->inTrans!=TRANS_WRITE ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
@@ -5588,7 +6317,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
if( rc!=SQLITE_OK ){
return rc;
}
- rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable);
+ rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
releasePage(pMove);
if( rc!=SQLITE_OK ){
return rc;
@@ -5632,6 +6361,14 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
}
return rc;
}
+int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+ int rc;
+ sqlite3BtreeEnter(p);
+ p->pBt->db = p->db;
+ rc = btreeDropTable(p, iTable, piMoved);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
/*
@@ -5650,6 +6387,9 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
unsigned char *pP1;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
+
/* Reading a meta-data value requires a read-lock on page 1 (and hence
** the sqlite_master table. We grab this lock regardless of whether or
** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
@@ -5657,12 +6397,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
*/
rc = queryTableLock(p, 1, READ_LOCK);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
assert( idx>=0 && idx<=15 );
rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage);
- if( rc ) return rc;
+ if( rc ){
+ sqlite3BtreeLeave(p);
+ return rc;
+ }
pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage);
*pMeta = get4byte(&pP1[36 + idx*4]);
sqlite3PagerUnref(pDbPage);
@@ -5676,6 +6420,7 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
/* Grab the read-lock on page 1. */
rc = lockTable(p, 1, READ_LOCK);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5688,20 +6433,27 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
unsigned char *pP1;
int rc;
assert( idx>=1 && idx<=15 );
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
if( p->inTrans!=TRANS_WRITE ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
- }
- assert( pBt->pPage1!=0 );
- pP1 = pBt->pPage1->aData;
- rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
- if( rc ) return rc;
- put4byte(&pP1[36 + idx*4], iMeta);
- if( idx==7 ){
- assert( pBt->autoVacuum || iMeta==0 );
- assert( iMeta==0 || iMeta==1 );
- pBt->incrVacuum = iMeta;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else{
+ assert( pBt->pPage1!=0 );
+ pP1 = pBt->pPage1->aData;
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+ if( rc==SQLITE_OK ){
+ put4byte(&pP1[36 + idx*4], iMeta);
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( idx==7 ){
+ assert( pBt->autoVacuum || iMeta==0 );
+ assert( iMeta==0 || iMeta==1 );
+ pBt->incrVacuum = iMeta;
+ }
+#endif
+ }
}
- return SQLITE_OK;
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -5710,9 +6462,13 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
*/
int sqlite3BtreeFlags(BtCursor *pCur){
/* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
- ** restoreOrClearCursorPosition() here.
+ ** restoreCursorPosition() here.
*/
- MemPage *pPage = pCur->pPage;
+ MemPage *pPage;
+ restoreCursorPosition(pCur);
+ pPage = pCur->pPage;
+ assert( cursorHoldsMutex(pCur) );
+ assert( pPage->pBt==pCur->pBt );
return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}
@@ -5736,23 +6492,21 @@ static void checkAppendMsg(
...
){
va_list ap;
- char *zMsg2;
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
va_start(ap, zFormat);
- zMsg2 = sqlite3VMPrintf(zFormat, ap);
+ if( pCheck->errMsg.nChar ){
+ sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
+ }
+ if( zMsg1 ){
+ sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1);
+ }
+ sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
va_end(ap);
- if( zMsg1==0 ) zMsg1 = "";
- if( pCheck->zErrMsg ){
- char *zOld = pCheck->zErrMsg;
- pCheck->zErrMsg = 0;
- sqlite3SetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0);
- sqliteFree(zOld);
- }else{
- sqlite3SetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0);
+ if( pCheck->errMsg.mallocFailed ){
+ pCheck->mallocFailed = 1;
}
- sqliteFree(zMsg2);
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -5845,7 +6599,7 @@ static void checkList(
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
}
#endif
- if( n>pCheck->pBt->usableSize/4-8 ){
+ if( n>pCheck->pBt->usableSize/4-2 ){
checkAppendMsg(pCheck, zContext,
"freelist leaf count too big on page %d", iPage);
N--;
@@ -5995,14 +6749,17 @@ static int checkTreePage(
*/
data = pPage->aData;
hdr = pPage->hdrOffset;
- hit = sqliteMalloc( usableSize );
- if( hit ){
+ hit = sqlite3PageMalloc( pBt->pageSize );
+ if( hit==0 ){
+ pCheck->mallocFailed = 1;
+ }else{
+ memset(hit, 0, usableSize );
memset(hit, 1, get2byte(&data[hdr+5]));
nCell = get2byte(&data[hdr+3]);
cellStart = hdr + 12 - 4*pPage->leaf;
for(i=0; i<nCell; i++){
int pc = get2byte(&data[cellStart+i*2]);
- int size = cellSizePtr(pPage, &data[pc]);
+ u16 size = cellSizePtr(pPage, &data[pc]);
int j;
if( (pc+size-1)>=usableSize || pc<0 ){
checkAppendMsg(pCheck, 0,
@@ -6038,7 +6795,7 @@ static int checkTreePage(
cnt, data[hdr+7], iPage);
}
}
- sqliteFree(hit);
+ sqlite3PageFree(hit);
releasePage(pPage);
return depth+1;
@@ -6051,10 +6808,10 @@ static int checkTreePage(
** an array of pages numbers were each page number is the root page of
** a table. nRoot is the number of entries in aRoot.
**
-** If everything checks out, this routine returns NULL. If something is
-** amiss, an error message is written into memory obtained from malloc()
-** and a pointer to that error message is returned. The calling function
-** is responsible for freeing the error message when it is done.
+** Write the number of error seen in *pnErr. Except for some memory
+** allocation errors, nn error message is held in memory obtained from
+** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is
+** returned.
*/
char *sqlite3BtreeIntegrityCheck(
Btree *p, /* The btree to be checked */
@@ -6067,16 +6824,22 @@ char *sqlite3BtreeIntegrityCheck(
int nRef;
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
+ char zErr[100];
+ sqlite3BtreeEnter(p);
+ pBt->db = p->db;
nRef = sqlite3PagerRefcount(pBt->pPager);
if( lockBtreeWithRetry(p)!=SQLITE_OK ){
- return sqliteStrDup("Unable to acquire a read lock on the database");
+ *pnErr = 1;
+ sqlite3BtreeLeave(p);
+ return sqlite3DbStrDup(0, "cannot acquire a read lock on the database");
}
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = sqlite3PagerPagecount(sCheck.pPager);
+ sCheck.nPage = pagerPagecount(sCheck.pPager);
sCheck.mxErr = mxErr;
sCheck.nErr = 0;
+ sCheck.mallocFailed = 0;
*pnErr = 0;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->nTrunc!=0 ){
@@ -6085,21 +6848,22 @@ char *sqlite3BtreeIntegrityCheck(
#endif
if( sCheck.nPage==0 ){
unlockBtreeIfUnused(pBt);
+ sqlite3BtreeLeave(p);
return 0;
}
- sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
+ sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
if( !sCheck.anRef ){
unlockBtreeIfUnused(pBt);
*pnErr = 1;
- return sqlite3MPrintf("Unable to malloc %d bytes",
- (sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
+ sqlite3BtreeLeave(p);
+ return 0;
}
for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
i = PENDING_BYTE_PAGE(pBt);
if( i<=sCheck.nPage ){
sCheck.anRef[i] = 1;
}
- sCheck.zErrMsg = 0;
+ sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
/* Check the integrity of the freelist
*/
@@ -6152,14 +6916,24 @@ char *sqlite3BtreeIntegrityCheck(
/* Clean up and report errors.
*/
- sqliteFree(sCheck.anRef);
+ sqlite3BtreeLeave(p);
+ sqlite3_free(sCheck.anRef);
+ if( sCheck.mallocFailed ){
+ sqlite3StrAccumReset(&sCheck.errMsg);
+ *pnErr = sCheck.nErr+1;
+ return 0;
+ }
*pnErr = sCheck.nErr;
- return sCheck.zErrMsg;
+ if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
+ return sqlite3StrAccumFinish(&sCheck.errMsg);
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
** Return the full pathname of the underlying database file.
+**
+** The pager filename is invariant as long as the pager is
+** open so it is safe to access without the BtShared mutex.
*/
const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
@@ -6168,6 +6942,9 @@ const char *sqlite3BtreeGetFilename(Btree *p){
/*
** Return the pathname of the directory that contains the database file.
+**
+** The pager directory name is invariant as long as the pager is
+** open so it is safe to access without the BtShared mutex.
*/
const char *sqlite3BtreeGetDirname(Btree *p){
assert( p->pBt->pPager!=0 );
@@ -6178,6 +6955,9 @@ const char *sqlite3BtreeGetDirname(Btree *p){
** Return the pathname of the journal file for this database. The return
** value of this routine is the same regardless of whether the journal file
** has been created or not.
+**
+** The pager journal filename is invariant as long as the pager is
+** open so it is safe to access without the BtShared mutex.
*/
const char *sqlite3BtreeGetJournalname(Btree *p){
assert( p->pBt->pPager!=0 );
@@ -6189,67 +6969,224 @@ const char *sqlite3BtreeGetJournalname(Btree *p){
** Copy the complete content of pBtFrom into pBtTo. A transaction
** must be active for both files.
**
-** The size of file pBtFrom may be reduced by this operation.
-** If anything goes wrong, the transaction on pBtFrom is rolled back.
+** The size of file pTo may be reduced by this operation.
+** If anything goes wrong, the transaction on pTo is rolled back.
+**
+** If successful, CommitPhaseOne() may be called on pTo before returning.
+** The caller should finish committing the transaction on pTo by calling
+** sqlite3BtreeCommit().
*/
-int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+static int btreeCopyFile(Btree *pTo, Btree *pFrom){
int rc = SQLITE_OK;
- Pgno i, nPage, nToPage, iSkip;
+ Pgno i;
+
+ Pgno nFromPage; /* Number of pages in pFrom */
+ Pgno nToPage; /* Number of pages in pTo */
+ Pgno nNewPage; /* Number of pages in pTo after the copy */
+
+ Pgno iSkip; /* Pending byte page in pTo */
+ int nToPageSize; /* Page size of pTo in bytes */
+ int nFromPageSize; /* Page size of pFrom in bytes */
BtShared *pBtTo = pTo->pBt;
BtShared *pBtFrom = pFrom->pBt;
+ pBtTo->db = pTo->db;
+ pBtFrom->db = pFrom->db;
+
+ nToPageSize = pBtTo->pageSize;
+ nFromPageSize = pBtFrom->pageSize;
if( pTo->inTrans!=TRANS_WRITE || pFrom->inTrans!=TRANS_WRITE ){
return SQLITE_ERROR;
}
- if( pBtTo->pCursor ) return SQLITE_BUSY;
- nToPage = sqlite3PagerPagecount(pBtTo->pPager);
- nPage = sqlite3PagerPagecount(pBtFrom->pPager);
- iSkip = PENDING_BYTE_PAGE(pBtTo);
- for(i=1; rc==SQLITE_OK && i<=nPage; i++){
- DbPage *pDbPage;
- if( i==iSkip ) continue;
- rc = sqlite3PagerGet(pBtFrom->pPager, i, &pDbPage);
- if( rc ) break;
- rc = sqlite3PagerOverwrite(pBtTo->pPager, i, sqlite3PagerGetData(pDbPage));
- sqlite3PagerUnref(pDbPage);
+ if( pBtTo->pCursor ){
+ return SQLITE_BUSY;
}
- /* If the file is shrinking, journal the pages that are being truncated
- ** so that they can be rolled back if the commit fails.
+ nToPage = pagerPagecount(pBtTo->pPager);
+ nFromPage = pagerPagecount(pBtFrom->pPager);
+ iSkip = PENDING_BYTE_PAGE(pBtTo);
+
+ /* Variable nNewPage is the number of pages required to store the
+ ** contents of pFrom using the current page-size of pTo.
*/
- for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
- DbPage *pDbPage;
- if( i==iSkip ) continue;
- rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage);
- if( rc ) break;
- rc = sqlite3PagerWrite(pDbPage);
- sqlite3PagerDontWrite(pDbPage);
- /* Yeah. It seems wierd to call DontWrite() right after Write(). But
- ** that is because the names of those procedures do not exactly
- ** represent what they do. Write() really means "put this page in the
- ** rollback journal and mark it as dirty so that it will be written
- ** to the database file later." DontWrite() undoes the second part of
- ** that and prevents the page from being written to the database. The
- ** page is still on the rollback journal, though. And that is the whole
- ** point of this loop: to put pages on the rollback journal. */
- sqlite3PagerUnref(pDbPage);
+ nNewPage = ((i64)nFromPage * (i64)nFromPageSize + (i64)nToPageSize - 1) /
+ (i64)nToPageSize;
+
+ for(i=1; rc==SQLITE_OK && (i<=nToPage || i<=nNewPage); i++){
+
+ /* Journal the original page.
+ **
+ ** iSkip is the page number of the locking page (PENDING_BYTE_PAGE)
+ ** in database *pTo (before the copy). This page is never written
+ ** into the journal file. Unless i==iSkip or the page was not
+ ** present in pTo before the copy operation, journal page i from pTo.
+ */
+ if( i!=iSkip && i<=nToPage ){
+ DbPage *pDbPage = 0;
+ rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pDbPage);
+ if( rc==SQLITE_OK && i>nFromPage ){
+ /* Yeah. It seems wierd to call DontWrite() right after Write(). But
+ ** that is because the names of those procedures do not exactly
+ ** represent what they do. Write() really means "put this page in the
+ ** rollback journal and mark it as dirty so that it will be written
+ ** to the database file later." DontWrite() undoes the second part of
+ ** that and prevents the page from being written to the database. The
+ ** page is still on the rollback journal, though. And that is the
+ ** whole point of this block: to put pages on the rollback journal.
+ */
+ sqlite3PagerDontWrite(pDbPage);
+ }
+ sqlite3PagerUnref(pDbPage);
+ }
+ }
+
+ /* Overwrite the data in page i of the target database */
+ if( rc==SQLITE_OK && i!=iSkip && i<=nNewPage ){
+
+ DbPage *pToPage = 0;
+ sqlite3_int64 iOff;
+
+ rc = sqlite3PagerGet(pBtTo->pPager, i, &pToPage);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pToPage);
+ }
+
+ for(
+ iOff=(i-1)*nToPageSize;
+ rc==SQLITE_OK && iOff<i*nToPageSize;
+ iOff += nFromPageSize
+ ){
+ DbPage *pFromPage = 0;
+ Pgno iFrom = (iOff/nFromPageSize)+1;
+
+ if( iFrom==PENDING_BYTE_PAGE(pBtFrom) ){
+ continue;
+ }
+
+ rc = sqlite3PagerGet(pBtFrom->pPager, iFrom, &pFromPage);
+ if( rc==SQLITE_OK ){
+ char *zTo = sqlite3PagerGetData(pToPage);
+ char *zFrom = sqlite3PagerGetData(pFromPage);
+ int nCopy;
+
+ if( nFromPageSize>=nToPageSize ){
+ zFrom += ((i-1)*nToPageSize - ((iFrom-1)*nFromPageSize));
+ nCopy = nToPageSize;
+ }else{
+ zTo += (((iFrom-1)*nFromPageSize) - (i-1)*nToPageSize);
+ nCopy = nFromPageSize;
+ }
+
+ memcpy(zTo, zFrom, nCopy);
+ sqlite3PagerUnref(pFromPage);
+ }
+ }
+
+ if( pToPage ) sqlite3PagerUnref(pToPage);
+ }
}
- if( !rc && nPage<nToPage ){
- rc = sqlite3PagerTruncate(pBtTo->pPager, nPage);
+
+ /* If things have worked so far, the database file may need to be
+ ** truncated. The complex part is that it may need to be truncated to
+ ** a size that is not an integer multiple of nToPageSize - the current
+ ** page size used by the pager associated with B-Tree pTo.
+ **
+ ** For example, say the page-size of pTo is 2048 bytes and the original
+ ** number of pages is 5 (10 KB file). If pFrom has a page size of 1024
+ ** bytes and 9 pages, then the file needs to be truncated to 9KB.
+ */
+ if( rc==SQLITE_OK ){
+ if( nFromPageSize!=nToPageSize ){
+ sqlite3_file *pFile = sqlite3PagerFile(pBtTo->pPager);
+ i64 iSize = (i64)nFromPageSize * (i64)nFromPage;
+ i64 iNow = (i64)((nToPage>nNewPage)?nToPage:nNewPage) * (i64)nToPageSize;
+ i64 iPending = ((i64)PENDING_BYTE_PAGE(pBtTo)-1) *(i64)nToPageSize;
+
+ assert( iSize<=iNow );
+
+ /* Commit phase one syncs the journal file associated with pTo
+ ** containing the original data. It does not sync the database file
+ ** itself. After doing this it is safe to use OsTruncate() and other
+ ** file APIs on the database file directly.
+ */
+ pBtTo->db = pTo->db;
+ rc = sqlite3PagerCommitPhaseOne(pBtTo->pPager, 0, 0, 1);
+ if( iSize<iNow && rc==SQLITE_OK ){
+ rc = sqlite3OsTruncate(pFile, iSize);
+ }
+
+ /* The loop that copied data from database pFrom to pTo did not
+ ** populate the locking page of database pTo. If the page-size of
+ ** pFrom is smaller than that of pTo, this means some data will
+ ** not have been copied.
+ **
+ ** This block copies the missing data from database pFrom to pTo
+ ** using file APIs. This is safe because at this point we know that
+ ** all of the original data from pTo has been synced into the
+ ** journal file. At this point it would be safe to do anything at
+ ** all to the database file except truncate it to zero bytes.
+ */
+ if( rc==SQLITE_OK && nFromPageSize<nToPageSize && iSize>iPending){
+ i64 iOff;
+ for(
+ iOff=iPending;
+ rc==SQLITE_OK && iOff<(iPending+nToPageSize);
+ iOff += nFromPageSize
+ ){
+ DbPage *pFromPage = 0;
+ Pgno iFrom = (iOff/nFromPageSize)+1;
+
+ if( iFrom==PENDING_BYTE_PAGE(pBtFrom) || iFrom>nFromPage ){
+ continue;
+ }
+
+ rc = sqlite3PagerGet(pBtFrom->pPager, iFrom, &pFromPage);
+ if( rc==SQLITE_OK ){
+ char *zFrom = sqlite3PagerGetData(pFromPage);
+ rc = sqlite3OsWrite(pFile, zFrom, nFromPageSize, iOff);
+ sqlite3PagerUnref(pFromPage);
+ }
+ }
+ }
+
+ /* Sync the database file */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSync(pBtTo->pPager);
+ }
+ }else{
+ rc = sqlite3PagerTruncate(pBtTo->pPager, nNewPage);
+ }
+ if( rc==SQLITE_OK ){
+ pBtTo->pageSizeFixed = 0;
+ }
}
if( rc ){
sqlite3BtreeRollback(pTo);
}
+
return rc;
}
+int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+ int rc;
+ sqlite3BtreeEnter(pTo);
+ sqlite3BtreeEnter(pFrom);
+ rc = btreeCopyFile(pTo, pFrom);
+ sqlite3BtreeLeave(pFrom);
+ sqlite3BtreeLeave(pTo);
+ return rc;
+}
+
#endif /* SQLITE_OMIT_VACUUM */
/*
** Return non-zero if a transaction is active.
*/
int sqlite3BtreeIsInTrans(Btree *p){
+ assert( p==0 || sqlite3_mutex_held(p->db->mutex) );
return (p && (p->inTrans==TRANS_WRITE));
}
@@ -6257,6 +7194,7 @@ int sqlite3BtreeIsInTrans(Btree *p){
** Return non-zero if a statement transaction is active.
*/
int sqlite3BtreeIsInStmt(Btree *p){
+ assert( sqlite3BtreeHoldsMutex(p) );
return (p->pBt && p->pBt->inStmt);
}
@@ -6264,12 +7202,13 @@ int sqlite3BtreeIsInStmt(Btree *p){
** Return non-zero if a read (or write) transaction is active.
*/
int sqlite3BtreeIsInReadTrans(Btree *p){
+ assert( sqlite3_mutex_held(p->db->mutex) );
return (p && (p->inTrans!=TRANS_NONE));
}
/*
** This function returns a pointer to a blob of memory associated with
-** a single shared-btree. The memory is used by client code for it's own
+** a single shared-btree. The memory is used by client code for its own
** purposes (for example, to store a high-level schema associated with
** the shared-btree). The btree layer manages reference counting issues.
**
@@ -6278,17 +7217,23 @@ int sqlite3BtreeIsInReadTrans(Btree *p){
** call the nBytes parameter is ignored and a pointer to the same blob
** of memory returned.
**
+** If the nBytes parameter is 0 and the blob of memory has not yet been
+** allocated, a null pointer is returned. If the blob has already been
+** allocated, it is returned as normal.
+**
** Just before the shared-btree is closed, the function passed as the
** xFree argument when the memory allocation was made is invoked on the
-** blob of allocated memory. This function should not call sqliteFree()
+** blob of allocated memory. This function should not call sqlite3_free()
** on the memory, the btree layer does that.
*/
void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
BtShared *pBt = p->pBt;
- if( !pBt->pSchema ){
- pBt->pSchema = sqliteMalloc(nBytes);
+ sqlite3BtreeEnter(p);
+ if( !pBt->pSchema && nBytes ){
+ pBt->pSchema = sqlite3MallocZero(nBytes);
pBt->xFreeSchema = xFree;
}
+ sqlite3BtreeLeave(p);
return pBt->pSchema;
}
@@ -6297,7 +7242,12 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
** handle holds an exclusive lock on the sqlite_master table.
*/
int sqlite3BtreeSchemaLocked(Btree *p){
- return (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK);
+ int rc;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ rc = (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK);
+ sqlite3BtreeLeave(p);
+ return rc;
}
@@ -6309,10 +7259,16 @@ int sqlite3BtreeSchemaLocked(Btree *p){
*/
int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
int rc = SQLITE_OK;
- u8 lockType = (isWriteLock?WRITE_LOCK:READ_LOCK);
- rc = queryTableLock(p, iTab, lockType);
- if( rc==SQLITE_OK ){
- rc = lockTable(p, iTab, lockType);
+ if( p->sharable ){
+ u8 lockType = READ_LOCK + isWriteLock;
+ assert( READ_LOCK+1==WRITE_LOCK );
+ assert( isWriteLock==0 || isWriteLock==1 );
+ sqlite3BtreeEnter(p);
+ rc = queryTableLock(p, iTab, lockType);
+ if( rc==SQLITE_OK ){
+ rc = lockTable(p, iTab, lockType);
+ }
+ sqlite3BtreeLeave(p);
}
return rc;
}
@@ -6327,9 +7283,13 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
** to change the length of the data stored.
*/
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
-
+ assert( cursorHoldsMutex(pCsr) );
+ assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
assert(pCsr->isIncrblobHandle);
- if( pCsr->eState==CURSOR_REQUIRESEEK ){
+
+ restoreCursorPosition(pCsr);
+ assert( pCsr->eState!=CURSOR_REQUIRESEEK );
+ if( pCsr->eState!=CURSOR_VALID ){
return SQLITE_ABORT;
}
@@ -6341,9 +7301,9 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
if( !pCsr->wrFlag ){
return SQLITE_READONLY;
}
- assert( !pCsr->pBtree->pBt->readOnly
- && pCsr->pBtree->pBt->inTransaction==TRANS_WRITE );
- if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr) ){
+ assert( !pCsr->pBt->readOnly
+ && pCsr->pBt->inTransaction==TRANS_WRITE );
+ if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
if( pCsr->eState==CURSOR_INVALID || !pCsr->pPage->intKey ){
@@ -6364,8 +7324,78 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
** sqlite3BtreePutData()).
*/
void sqlite3BtreeCacheOverflow(BtCursor *pCur){
+ assert( cursorHoldsMutex(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert(!pCur->isIncrblobHandle);
assert(!pCur->aOverflow);
pCur->isIncrblobHandle = 1;
}
+
+/* Poison the db so that other clients error out as quickly as
+** possible.
+*/
+int sqlite3Poison(sqlite3 *db){
+ int rc;
+ Btree *p;
+ BtShared *pBt;
+ unsigned char *pP1;
+
+ if( db == NULL) return SQLITE_OK;
+
+ /* Database 0 corrosponds to the main database. */
+ if( db->nDb<1 ) return SQLITE_OK;
+ p = db->aDb[0].pBt;
+ pBt = p->pBt;
+
+ /* If in a transaction, roll it back. Committing any changes to a
+ ** corrupt database may mess up evidence, we definitely don't want
+ ** to allow poisoning to be rolled back, and the database is anyhow
+ ** going bye-bye RSN.
+ */
+ /* TODO(shess): Figure out if this might release the lock and let
+ ** someone else get in there, which might deny us the lock a couple
+ ** lines down.
+ */
+ if( sqlite3BtreeIsInTrans(p) ) sqlite3BtreeRollback(p);
+
+ /* Start an exclusive transaction. This will check the headers, so
+ ** if someone else poisoned the database we should get an error.
+ */
+ rc = sqlite3BtreeBeginTrans(p, 2);
+ /* TODO(shess): Handle SQLITE_BUSY? */
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Copied from sqlite3BtreeUpdateMeta(). Writing the old version of
+ ** the page to the journal may be overkill, but it probably won't
+ ** hurt.
+ */
+ assert( pBt->inTrans==TRANS_WRITE );
+ assert( pBt->pPage1!=0 );
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+ if( rc ) goto err;
+
+ /* "SQLite format 3" changes to
+ ** "SQLite poison 3". Be extra paranoid about making this change.
+ */
+ if( sizeof(zMagicHeader)!=16 ||
+ sizeof(zPoisonHeader)!=sizeof(zMagicHeader) ){
+ rc = SQLITE_ERROR;
+ goto err;
+ }
+ pP1 = pBt->pPage1->aData;
+ if( memcmp(pP1, zMagicHeader, 16)!=0 ){
+ rc = SQLITE_CORRUPT;
+ goto err;
+ }
+ memcpy(pP1, zPoisonHeader, 16);
+
+ /* Push it to the database file. */
+ return sqlite3BtreeCommit(p);
+
+ err:
+ /* TODO(shess): What about errors, here? */
+ sqlite3BtreeRollback(p);
+ return rc;
+}
+
#endif
diff --git a/third_party/sqlite/btree.h b/third_party/sqlite/src/btree.h
index bf8929d..3fe01cb 100644..100755
--- a/third_party/sqlite/btree.h
+++ b/third_party/sqlite/src/btree.h
@@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
**
-** @(#) $Id: btree.h,v 1.82 2007/05/08 21:45:27 drh Exp $
+** @(#) $Id: btree.h,v 1.102 2008/07/11 21:02:54 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_
@@ -41,13 +41,26 @@
typedef struct Btree Btree;
typedef struct BtCursor BtCursor;
typedef struct BtShared BtShared;
+typedef struct BtreeMutexArray BtreeMutexArray;
+
+/*
+** This structure records all of the Btrees that need to hold
+** a mutex before we enter sqlite3VdbeExec(). The Btrees are
+** are placed in aBtree[] in order of aBtree[]->pBt. That way,
+** we can always lock and unlock them all quickly.
+*/
+struct BtreeMutexArray {
+ int nMutex;
+ Btree *aBtree[SQLITE_MAX_ATTACHED+1];
+};
int sqlite3BtreeOpen(
const char *zFilename, /* Name of database file to open */
sqlite3 *db, /* Associated database connection */
Btree **, /* Return open Btree* here */
- int flags /* Flags */
+ int flags, /* Flags */
+ int vfsFlags /* Flags passed through to VFS open */
);
/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the
@@ -59,9 +72,11 @@ int sqlite3BtreeOpen(
#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */
#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
#define BTREE_MEMORY 4 /* In-memory DB. No argument */
+#define BTREE_READONLY 8 /* Open the database in read-only mode */
+#define BTREE_READWRITE 16 /* Open for both reading and writing */
+#define BTREE_CREATE 32 /* Create the database if it does not exist */
int sqlite3BtreeClose(Btree*);
-int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*);
int sqlite3BtreeSetCacheSize(Btree*,int);
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
int sqlite3BtreeSyncDisabled(Btree*);
@@ -105,18 +120,29 @@ int sqlite3BtreeDropTable(Btree*, int, int*);
int sqlite3BtreeClearTable(Btree*, int);
int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue);
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
+void sqlite3BtreeTripAllCursors(Btree*, int);
+
+struct UnpackedRecord; /* Forward declaration. Definition in vdbeaux.c. */
int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
int iTable, /* Index of root page */
int wrFlag, /* 1 for writing. 0 for read-only */
- int(*)(void*,int,const void*,int,const void*), /* Key comparison function */
- void*, /* First argument to compare function */
- BtCursor **ppCursor /* Returned cursor */
+ struct KeyInfo*, /* First argument to compare function */
+ BtCursor *pCursor /* Space to write cursor structure */
);
+int sqlite3BtreeCursorSize(void);
int sqlite3BtreeCloseCursor(BtCursor*);
-int sqlite3BtreeMoveto(BtCursor*,const void *pKey,i64 nKey,int bias,int *pRes);
+int sqlite3BtreeMoveto(
+ BtCursor*,
+ const void *pKey,
+ struct UnpackedRecord *pUnKey,
+ i64 nKey,
+ int bias,
+ int *pRes
+);
+int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
int sqlite3BtreeDelete(BtCursor*);
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
const void *pData, int nData,
@@ -129,6 +155,7 @@ int sqlite3BtreeFlags(BtCursor*);
int sqlite3BtreePrevious(BtCursor*, int *pRes);
int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
+sqlite3 *sqlite3BtreeCursorDb(const BtCursor*);
const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
@@ -143,7 +170,50 @@ void sqlite3BtreeCacheOverflow(BtCursor *);
#ifdef SQLITE_TEST
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
void sqlite3BtreeCursorList(Btree*);
-int sqlite3BtreePageDump(Btree*, int, int recursive);
#endif
+/*
+** If we are not using shared cache, then there is no need to
+** use mutexes to access the BtShared structures. So make the
+** Enter and Leave procedures no-ops.
+*/
+#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
+ void sqlite3BtreeEnter(Btree*);
+ void sqlite3BtreeLeave(Btree*);
+#ifndef NDEBUG
+ /* This routine is used inside assert() statements only. */
+ int sqlite3BtreeHoldsMutex(Btree*);
+#endif
+ void sqlite3BtreeEnterCursor(BtCursor*);
+ void sqlite3BtreeLeaveCursor(BtCursor*);
+ void sqlite3BtreeEnterAll(sqlite3*);
+ void sqlite3BtreeLeaveAll(sqlite3*);
+#ifndef NDEBUG
+ /* This routine is used inside assert() statements only. */
+ int sqlite3BtreeHoldsAllMutexes(sqlite3*);
+#endif
+ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*);
+ void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*);
+ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*);
+#else
+# define sqlite3BtreeEnter(X)
+# define sqlite3BtreeLeave(X)
+#ifndef NDEBUG
+ /* This routine is used inside assert() statements only. */
+# define sqlite3BtreeHoldsMutex(X) 1
+#endif
+# define sqlite3BtreeEnterCursor(X)
+# define sqlite3BtreeLeaveCursor(X)
+# define sqlite3BtreeEnterAll(X)
+# define sqlite3BtreeLeaveAll(X)
+#ifndef NDEBUG
+ /* This routine is used inside assert() statements only. */
+# define sqlite3BtreeHoldsAllMutexes(X) 1
+#endif
+# define sqlite3BtreeMutexArrayEnter(X)
+# define sqlite3BtreeMutexArrayLeave(X)
+# define sqlite3BtreeMutexArrayInsert(X,Y)
+#endif
+
+
#endif /* _BTREE_H_ */
diff --git a/third_party/sqlite/btreeInt.h b/third_party/sqlite/src/btreeInt.h
index 2964ad3..663a41e 100644..100755
--- a/third_party/sqlite/btreeInt.h
+++ b/third_party/sqlite/src/btreeInt.h
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btreeInt.h,v 1.5 2007/06/15 12:06:59 drh Exp $
+** $Id: btreeInt.h,v 1.30 2008/08/01 20:10:08 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -221,10 +221,11 @@
#define MX_CELL_SIZE(pBt) (pBt->pageSize-8)
/* The maximum number of cells on a single page of the database. This
-** assumes a minimum cell size of 3 bytes. Such small cells will be
-** exceedingly rare, but they are possible.
+** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself
+** plus 2 bytes for the index to the cell in the page header). Such
+** small cells will be rare, but they are possible.
*/
-#define MX_CELL(pBt) ((pBt->pageSize-8)/3)
+#define MX_CELL(pBt) ((pBt->pageSize-8)/6)
/* Forward declarations */
typedef struct MemPage MemPage;
@@ -248,7 +249,7 @@ typedef struct BtLock BtLock;
/*
** Page type flags. An ORed combination of these flags appear as the
-** first byte of every BTree page.
+** first byte of on-disk image of every BTree page.
*/
#define PTF_INTKEY 0x01
#define PTF_ZERODATA 0x02
@@ -264,6 +265,9 @@ typedef struct BtLock BtLock;
** walk up the BTree from any leaf to the root. Care must be taken to
** unref() the parent page pointer when this page is no longer referenced.
** The pageDestructor() routine handles that chore.
+**
+** Access to all fields of this structure is controlled by the mutex
+** stored in MemPage.pBt->mutex.
*/
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
@@ -271,23 +275,22 @@ struct MemPage {
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
u8 intKey; /* True if intkey flag is set */
u8 leaf; /* True if leaf flag is set */
- u8 zeroData; /* True if table stores keys only */
- u8 leafData; /* True if tables stores data on leaves only */
u8 hasData; /* True if this page stores data */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
- u16 maxLocal; /* Copy of Btree.maxLocal or Btree.maxLeaf */
- u16 minLocal; /* Copy of Btree.minLocal or Btree.minLeaf */
+ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
+ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
u16 cellOffset; /* Index in aData of first cell pointer */
u16 idxParent; /* Index in parent of this node */
u16 nFree; /* Number of free bytes on the page */
u16 nCell; /* Number of cells on this page, local and ovfl */
+ u16 maskPage; /* Mask for page offset */
struct _OvflCell { /* Cells that will not fit on aData[] */
u8 *pCell; /* Pointers to the body of the overflow cell */
u16 idx; /* Insert this cell before idx-th non-overflow cell */
} aOvfl[5];
- BtShared *pBt; /* Pointer back to BTree structure */
- u8 *aData; /* Pointer back to the start of the page */
+ BtShared *pBt; /* Pointer to BtShared that this page is part of */
+ u8 *aData; /* Pointer to disk image of the page data */
DbPage *pDbPage; /* Pager page handle */
Pgno pgno; /* Page number for this page */
MemPage *pParent; /* The parent of this page. NULL for root */
@@ -300,11 +303,36 @@ struct MemPage {
*/
#define EXTRA_SIZE sizeof(MemPage)
-/* Btree handle */
+/* A Btree handle
+**
+** A database connection contains a pointer to an instance of
+** this object for every database file that it has open. This structure
+** is opaque to the database connection. The database connection cannot
+** see the internals of this structure and only deals with pointers to
+** this structure.
+**
+** For some database files, the same underlying database cache might be
+** shared between multiple connections. In that case, each contection
+** has it own pointer to this object. But each instance of this object
+** points to the same BtShared object. The database cache and the
+** schema associated with the database file are all contained within
+** the BtShared object.
+**
+** All fields in this structure are accessed under sqlite3.mutex.
+** The pBt pointer itself may not be changed while there exists cursors
+** in the referenced BtShared that point back to this Btree since those
+** cursors have to do go through this Btree to find their BtShared and
+** they often do so without holding sqlite3.mutex.
+*/
struct Btree {
- sqlite3 *pSqlite;
- BtShared *pBt;
- u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
+ sqlite3 *db; /* The database connection holding this btree */
+ BtShared *pBt; /* Sharable content of this btree */
+ u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
+ u8 sharable; /* True if we can share pBt with another db */
+ u8 locked; /* True if db currently has pBt locked */
+ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
+ Btree *pNext; /* List of other sharable Btrees from the same db */
+ Btree *pPrev; /* Back pointer of the same list */
};
/*
@@ -312,25 +340,36 @@ struct Btree {
**
** If the shared-data extension is enabled, there may be multiple users
** of the Btree structure. At most one of these may open a write transaction,
-** but any number may have active read transactions. Variable Btree.pDb
-** points to the handle that owns any current write-transaction.
+** but any number may have active read transactions.
*/
#define TRANS_NONE 0
#define TRANS_READ 1
#define TRANS_WRITE 2
/*
-** Everything we need to know about an open database
+** An instance of this object represents a single database file.
+**
+** A single database file can be in use as the same time by two
+** or more database connections. When two or more connections are
+** sharing the same database file, each connection has it own
+** private Btree object for the file and each of those Btrees points
+** to this one BtShared object. BtShared.nRef is the number of
+** connections currently sharing this database file.
+**
+** Fields in this structure are accessed under the BtShared.mutex
+** mutex, except for nRef and pNext which are accessed under the
+** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
+** may not be modified once it is initially set as long as nRef>0.
+** The pSchema field may be set once under BtShared.mutex and
+** thereafter is unchanged as long as nRef>0.
*/
struct BtShared {
Pager *pPager; /* The page cache */
+ sqlite3 *db; /* Database connection currently using this Btree */
BtCursor *pCursor; /* A list of all open cursors */
MemPage *pPage1; /* First page of the database */
u8 inStmt; /* True if we are in a statement subtransaction */
u8 readOnly; /* True if the underlying file is readonly */
- u8 maxEmbedFrac; /* Maximum payload as % of total page size */
- u8 minEmbedFrac; /* Minimum payload as % of total page size */
- u8 minLeafFrac; /* Minimum leaf payload as % of total page size */
u8 pageSizeFixed; /* True if the page size can no longer be changed */
#ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */
@@ -343,16 +382,19 @@ struct BtShared {
int minLocal; /* Minimum local payload in non-LEAFDATA tables */
int maxLeaf; /* Maximum local payload in a LEAFDATA table */
int minLeaf; /* Minimum local payload in a LEAFDATA table */
- BusyHandler *pBusyHandler; /* Callback for when there is lock contention */
u8 inTransaction; /* Transaction state */
- int nRef; /* Number of references to this structure */
int nTransaction; /* Number of open transactions (read + write) */
void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
+ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
+ BusyHandler busyHdr; /* The busy handler for this btree */
#ifndef SQLITE_OMIT_SHARED_CACHE
+ int nRef; /* Number of references to this structure */
+ BtShared *pNext; /* Next on a list of sharable BtShared structs */
BtLock *pLock; /* List of locks held on this shared-btree struct */
- BtShared *pNext; /* Next in ThreadData.pBtree linked list */
+ Btree *pExclusive; /* Btree with an EXCLUSIVE lock on the whole db */
#endif
+ u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
};
/*
@@ -373,20 +415,31 @@ struct CellInfo {
};
/*
-** A cursor is a pointer to a particular entry in the BTree.
+** A cursor is a pointer to a particular entry within a particular
+** b-tree within a database file.
+**
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
+**
+** When a single database file can shared by two more database connections,
+** but cursors cannot be shared. Each cursor is associated with a
+** particular database connection identified BtCursor.pBtree.db.
+**
+** Fields in this structure are accessed under the BtShared.mutex
+** found at self->pBt->mutex.
*/
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
+ BtShared *pBt; /* The BtShared this cursor points to */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
- int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */
- void *pArg; /* First arg to xCompare() */
+ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
Pgno pgnoRoot; /* The root page of this tree */
MemPage *pPage; /* Page that contains the entry */
int idx; /* Index of the entry in pPage->aCell[] */
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
+ u8 atLast; /* Cursor pointing to the last entry */
+ u8 validNKey; /* True if info.nKey is valid */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
void *pKey; /* Saved key that was cursor's last known position */
i64 nKey; /* Size of pKey, or last integer key */
@@ -412,32 +465,20 @@ struct BtCursor {
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
-** this state, restoreOrClearCursorPosition() can be called to attempt to
+** this state, restoreCursorPosition() can be called to attempt to
** seek the cursor to the saved position.
+**
+** CURSOR_FAULT:
+** A unrecoverable error (an I/O error or a malloc failure) has occurred
+** on a different connection that shares the BtShared cache with this
+** cursor. The error has left the cache in an inconsistent state.
+** Do nothing else with this cursor. Any attempt to use the cursor
+** should return the error code stored in BtCursor.skip
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
#define CURSOR_REQUIRESEEK 2
-
-/*
-** The TRACE macro will print high-level status information about the
-** btree operation when the global variable sqlite3_btree_trace is
-** enabled.
-*/
-#if SQLITE_TEST
-# define TRACE(X) if( sqlite3_btree_trace ){ printf X; fflush(stdout); }
-#else
-# define TRACE(X)
-#endif
-
-/*
-** Routines to read and write variable-length integers. These used to
-** be defined locally, but now we use the varint routines in the util.c
-** file.
-*/
-#define getVarint sqlite3GetVarint
-#define getVarint32(A,B) ((*B=*(A))<=0x7f?1:sqlite3GetVarint32(A,B))
-#define putVarint sqlite3PutVarint
+#define CURSOR_FAULT 3
/* The database page the PENDING_BYTE occupies. This page is never used.
** TODO: This macro is very similary to PAGER_MJ_PGNO() in pager.c. They
@@ -486,7 +527,7 @@ struct BtLock {
** this test.
*/
#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
-#define PTRMAP_PTROFFSET(pBt, pgno) (5*(pgno-ptrmapPageno(pBt, pgno)-1))
+#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1))
#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
/*
@@ -530,8 +571,6 @@ struct BtLock {
** of handle p (type Btree*) are internally consistent.
*/
#define btreeIntegrity(p) \
- assert( p->inTrans!=TRANS_NONE || p->pBt->nTransaction<p->pBt->nRef ); \
- assert( p->pBt->nTransaction<=p->pBt->nRef ); \
assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \
assert( p->pBt->inTransaction>=p->inTrans );
@@ -561,8 +600,9 @@ struct IntegrityCk {
int nPage; /* Number of pages in the database */
int *anRef; /* Number of times each page is referenced */
int mxErr; /* Stop accumulating errors when this reaches zero */
- char *zErrMsg; /* An error message. NULL if no errors seen. */
int nErr; /* Number of messages written to zErrMsg so far */
+ int mallocFailed; /* A memory allocation error has occurred */
+ StrAccum errMsg; /* Accumulate the error message text here */
};
/*
@@ -580,8 +620,7 @@ int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int);
int sqlite3BtreeInitPage(MemPage *pPage, MemPage *pParent);
void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*);
void sqlite3BtreeParseCell(MemPage*, int, CellInfo*);
-u8 *sqlite3BtreeFindCell(MemPage *pPage, int iCell);
-int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur);
+int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur);
void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur);
void sqlite3BtreeReleaseTempCursor(BtCursor *pCur);
int sqlite3BtreeIsRootPage(MemPage *pPage);
diff --git a/third_party/sqlite/build.c b/third_party/sqlite/src/build.c
index f34b705..a20ee69 100644..100755
--- a/third_party/sqlite/build.c
+++ b/third_party/sqlite/src/build.c
@@ -22,7 +22,7 @@
** COMMIT
** ROLLBACK
**
-** $Id: build.c,v 1.433 2007/07/02 19:31:27 drh Exp $
+** $Id: build.c,v 1.494 2008/08/06 13:47:41 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -72,7 +72,7 @@ void sqlite3TableLock(
int nBytes;
TableLock *p;
- if( 0==sqlite3ThreadDataReadOnly()->useSharedData || iDb<0 ){
+ if( iDb<0 ){
return;
}
@@ -85,13 +85,17 @@ void sqlite3TableLock(
}
nBytes = sizeof(TableLock) * (pParse->nTableLock+1);
- pParse->aTableLock = sqliteReallocOrFree(pParse->aTableLock, nBytes);
+ pParse->aTableLock =
+ sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes);
if( pParse->aTableLock ){
p = &pParse->aTableLock[pParse->nTableLock++];
p->iDb = iDb;
p->iTab = iTab;
p->isWriteLock = isWriteLock;
p->zName = zName;
+ }else{
+ pParse->nTableLock = 0;
+ pParse->db->mallocFailed = 1;
}
}
@@ -102,7 +106,6 @@ void sqlite3TableLock(
static void codeTableLocks(Parse *pParse){
int i;
Vdbe *pVdbe;
- assert( sqlite3ThreadDataReadOnly()->useSharedData || pParse->nTableLock==0 );
if( 0==(pVdbe = sqlite3GetVdbe(pParse)) ){
return;
@@ -111,10 +114,8 @@ static void codeTableLocks(Parse *pParse){
for(i=0; i<pParse->nTableLock; i++){
TableLock *p = &pParse->aTableLock[i];
int p1 = p->iDb;
- if( p->isWriteLock ){
- p1 = -1*(p1+1);
- }
- sqlite3VdbeOp3(pVdbe, OP_TableLock, p1, p->iTab, p->zName, P3_STATIC);
+ sqlite3VdbeAddOp4(pVdbe, OP_TableLock, p1, p->iTab, p->isWriteLock,
+ p->zName, P4_STATIC);
}
}
#else
@@ -135,22 +136,17 @@ void sqlite3FinishCoding(Parse *pParse){
sqlite3 *db;
Vdbe *v;
- if( sqlite3MallocFailed() ) return;
+ db = pParse->db;
+ if( db->mallocFailed ) return;
if( pParse->nested ) return;
- if( !pParse->pVdbe ){
- if( pParse->rc==SQLITE_OK && pParse->nErr ){
- pParse->rc = SQLITE_ERROR;
- return;
- }
- }
+ if( pParse->nErr ) return;
/* Begin by generating some termination code at the end of the
** vdbe program
*/
- db = pParse->db;
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_Halt);
/* The cookie mask contains one bit for each database file open.
** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
@@ -164,13 +160,18 @@ void sqlite3FinishCoding(Parse *pParse){
sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
if( (mask & pParse->cookieMask)==0 ) continue;
- sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
- sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
+ sqlite3VdbeUsesBtree(v, iDb);
+ sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
+ sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pParse->pVirtualLock ){
- char *vtab = (char *)pParse->pVirtualLock->pVtab;
- sqlite3VdbeOp3(v, OP_VBegin, 0, 0, vtab, P3_VTAB);
+ {
+ int i;
+ for(i=0; i<pParse->nVtabLock; i++){
+ char *vtab = (char *)pParse->apVtabLock[i]->pVtab;
+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
+ }
+ pParse->nVtabLock = 0;
}
#endif
@@ -179,28 +180,31 @@ void sqlite3FinishCoding(Parse *pParse){
** shared-cache feature is enabled.
*/
codeTableLocks(pParse);
- sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto);
}
#ifndef SQLITE_OMIT_TRACE
- /* Add a No-op that contains the complete text of the compiled SQL
- ** statement as its P3 argument. This does not change the functionality
- ** of the program.
- **
- ** This is used to implement sqlite3_trace().
- */
- sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql);
+ if( !db->init.busy ){
+ /* Change the P4 argument of the first opcode (which will always be
+ ** an OP_Trace) to be the complete text of the current SQL statement.
+ */
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, 0);
+ if( pOp && pOp->opcode==OP_Trace ){
+ sqlite3VdbeChangeP4(v, 0, pParse->zSql, pParse->zTail-pParse->zSql);
+ }
+ }
#endif /* SQLITE_OMIT_TRACE */
}
/* Get the VDBE program ready for execution
*/
- if( v && pParse->nErr==0 && !sqlite3MallocFailed() ){
+ if( v && pParse->nErr==0 && !db->mallocFailed ){
#ifdef SQLITE_DEBUG
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
sqlite3VdbeTrace(v, trace);
#endif
+ assert( pParse->disableColCache==0 ); /* Disables and re-enables match */
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
pParse->nTab+3, pParse->explain);
pParse->rc = SQLITE_DONE;
@@ -231,13 +235,15 @@ void sqlite3FinishCoding(Parse *pParse){
void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
va_list ap;
char *zSql;
+ char *zErrMsg = 0;
+ sqlite3 *db = pParse->db;
# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar))
char saveBuf[SAVE_SZ];
if( pParse->nErr ) return;
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
va_start(ap, zFormat);
- zSql = sqlite3VMPrintf(zFormat, ap);
+ zSql = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
if( zSql==0 ){
return; /* A malloc must have failed */
@@ -245,8 +251,9 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
pParse->nested++;
memcpy(saveBuf, &pParse->nVar, SAVE_SZ);
memset(&pParse->nVar, 0, SAVE_SZ);
- sqlite3RunParser(pParse, zSql, 0);
- sqliteFree(zSql);
+ sqlite3RunParser(pParse, zSql, &zErrMsg);
+ sqlite3DbFree(db, zErrMsg);
+ sqlite3DbFree(db, zSql);
memcpy(&pParse->nVar, saveBuf, SAVE_SZ);
pParse->nested--;
}
@@ -266,11 +273,13 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
Table *p = 0;
int i;
+ int nName;
assert( zName!=0 );
+ nName = sqlite3Strlen(db, zName) + 1;
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
- p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, strlen(zName)+1);
+ p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName);
if( p ) break;
}
return p;
@@ -286,7 +295,12 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
** routine leaves an error message in pParse->zErrMsg where
** sqlite3FindTable() does not.
*/
-Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){
+Table *sqlite3LocateTable(
+ Parse *pParse, /* context in which to report errors */
+ int isView, /* True if looking for a VIEW rather than a TABLE */
+ const char *zName, /* Name of the table we are looking for */
+ const char *zDbase /* Name of the database. Might be NULL */
+){
Table *p;
/* Read the database schema. If an error occurs, leave an error message
@@ -297,10 +311,11 @@ Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){
p = sqlite3FindTable(pParse->db, zName, zDbase);
if( p==0 ){
+ const char *zMsg = isView ? "no such view" : "no such table";
if( zDbase ){
- sqlite3ErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
+ sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
}else{
- sqlite3ErrorMsg(pParse, "no such table: %s", zName);
+ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
}
pParse->checkSchema = 1;
}
@@ -322,13 +337,14 @@ Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){
Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
Index *p = 0;
int i;
+ int nName = sqlite3Strlen(db, zName)+1;
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
Schema *pSchema = db->aDb[j].pSchema;
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
assert( pSchema || (j==1 && !db->aDb[1].pBt) );
if( pSchema ){
- p = sqlite3HashFind(&pSchema->idxHash, zName, strlen(zName)+1);
+ p = sqlite3HashFind(&pSchema->idxHash, zName, nName);
}
if( p ) break;
}
@@ -339,8 +355,9 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
** Reclaim the memory used by an index
*/
static void freeIndex(Index *p){
- sqliteFree(p->zColAff);
- sqliteFree(p);
+ sqlite3 *db = p->pTable->db;
+ sqlite3DbFree(db, p->zColAff);
+ sqlite3DbFree(db, p);
}
/*
@@ -355,7 +372,7 @@ static void sqliteDeleteIndex(Index *p){
Index *pOld;
const char *zName = p->zName;
- pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen( zName)+1, 0);
+ pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen(zName)+1, 0);
assert( pOld==0 || pOld==p );
freeIndex(p);
}
@@ -371,7 +388,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
int len;
Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
- len = strlen(zIdxName);
+ len = sqlite3Strlen(db, zIdxName);
pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0);
if( pIndex ){
if( pIndex->pTable->pIndex==pIndex ){
@@ -401,17 +418,22 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
*/
void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
int i, j;
-
assert( iDb>=0 && iDb<db->nDb );
+
+ if( iDb==0 ){
+ sqlite3BtreeEnterAll(db);
+ }
for(i=iDb; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
if( pDb->pSchema ){
+ assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt)));
sqlite3SchemaFree(pDb->pSchema);
}
if( iDb>0 ) return;
}
assert( iDb==0 );
db->flags &= ~SQLITE_InternChanges;
+ sqlite3BtreeLeaveAll(db);
/* If one or more of the auxiliary database files has been closed,
** then remove them from the auxiliary database list. We take the
@@ -429,7 +451,7 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
for(i=j=2; i<db->nDb; i++){
struct Db *pDb = &db->aDb[i];
if( pDb->pBt==0 ){
- sqliteFree(pDb->zName);
+ sqlite3DbFree(db, pDb->zName);
pDb->zName = 0;
continue;
}
@@ -442,7 +464,7 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
db->nDb = j;
if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
- sqliteFree(db->aDb);
+ sqlite3DbFree(db, db->aDb);
db->aDb = db->aDbStatic;
}
}
@@ -460,15 +482,16 @@ void sqlite3CommitInternalChanges(sqlite3 *db){
static void sqliteResetColumnNames(Table *pTable){
int i;
Column *pCol;
+ sqlite3 *db = pTable->db;
assert( pTable!=0 );
if( (pCol = pTable->aCol)!=0 ){
for(i=0; i<pTable->nCol; i++, pCol++){
- sqliteFree(pCol->zName);
- sqlite3ExprDelete(pCol->pDflt);
- sqliteFree(pCol->zType);
- sqliteFree(pCol->zColl);
+ sqlite3DbFree(db, pCol->zName);
+ sqlite3ExprDelete(db, pCol->pDflt);
+ sqlite3DbFree(db, pCol->zType);
+ sqlite3DbFree(db, pCol->zColl);
}
- sqliteFree(pTable->aCol);
+ sqlite3DbFree(db, pTable->aCol);
}
pTable->aCol = 0;
pTable->nCol = 0;
@@ -487,8 +510,10 @@ static void sqliteResetColumnNames(Table *pTable){
void sqlite3DeleteTable(Table *pTable){
Index *pIndex, *pNext;
FKey *pFKey, *pNextFKey;
+ sqlite3 *db;
if( pTable==0 ) return;
+ db = pTable->db;
/* Do not delete the table until the reference count reaches zero. */
pTable->nRef--;
@@ -513,21 +538,21 @@ void sqlite3DeleteTable(Table *pTable){
pNextFKey = pFKey->pNextFrom;
assert( sqlite3HashFind(&pTable->pSchema->aFKey,
pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
- sqliteFree(pFKey);
+ sqlite3DbFree(db, pFKey);
}
#endif
/* Delete the Table structure itself.
*/
sqliteResetColumnNames(pTable);
- sqliteFree(pTable->zName);
- sqliteFree(pTable->zColAff);
- sqlite3SelectDelete(pTable->pSelect);
+ sqlite3DbFree(db, pTable->zName);
+ sqlite3DbFree(db, pTable->zColAff);
+ sqlite3SelectDelete(db, pTable->pSelect);
#ifndef SQLITE_OMIT_CHECK
- sqlite3ExprDelete(pTable->pCheck);
+ sqlite3ExprDelete(db, pTable->pCheck);
#endif
sqlite3VtabClear(pTable);
- sqliteFree(pTable);
+ sqlite3DbFree(db, pTable);
}
/*
@@ -574,10 +599,10 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
** are not \000 terminated and are not persistent. The returned string
** is \000 terminated and is persistent.
*/
-char *sqlite3NameFromToken(Token *pName){
+char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
char *zName;
if( pName ){
- zName = sqliteStrNDup((char*)pName->z, pName->n);
+ zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n);
sqlite3Dequote(zName);
}else{
zName = 0;
@@ -592,9 +617,8 @@ char *sqlite3NameFromToken(Token *pName){
void sqlite3OpenMasterTable(Parse *p, int iDb){
Vdbe *v = sqlite3GetVdbe(p);
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- sqlite3VdbeAddOp(v, OP_OpenWrite, 0, MASTER_ROOT);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, 0, 5); /* sqlite_master has 5 columns */
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5);/* sqlite_master has 5 columns */
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
}
/*
@@ -609,7 +633,7 @@ int sqlite3FindDb(sqlite3 *db, Token *pName){
Db *pDb; /* A database whose name space is being searched */
char *zName; /* Name we are searching for */
- zName = sqlite3NameFromToken(pName);
+ zName = sqlite3NameFromToken(db, pName);
if( zName ){
n = strlen(zName);
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
@@ -618,7 +642,7 @@ int sqlite3FindDb(sqlite3 *db, Token *pName){
break;
}
}
- sqliteFree(zName);
+ sqlite3DbFree(db, zName);
}
return i;
}
@@ -741,7 +765,7 @@ void sqlite3StartTable(
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
pParse->sNameToken = *pName;
- zName = sqlite3NameFromToken(pName);
+ zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) return;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto begin_table_error;
@@ -798,8 +822,9 @@ void sqlite3StartTable(
}
}
- pTable = sqliteMalloc( sizeof(Table) );
+ pTable = sqlite3DbMallocZero(db, sizeof(Table));
if( pTable==0 ){
+ db->mallocFailed = 1;
pParse->rc = SQLITE_NOMEM;
pParse->nErr++;
goto begin_table_error;
@@ -808,6 +833,7 @@ void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
+ pTable->db = db;
if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable);
pParse->pNewTable = pTable;
@@ -830,29 +856,33 @@ void sqlite3StartTable(
** now.
*/
if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
- int lbl;
+ int j1;
int fileFormat;
+ int reg1, reg2, reg3;
sqlite3BeginWriteOperation(pParse, 0, iDb);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( isVirtual ){
- sqlite3VdbeAddOp(v, OP_VBegin, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_VBegin);
}
#endif
/* If the file format and encoding in the database have not been set,
** set them now.
*/
- sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); /* file_format */
- lbl = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp(v, OP_If, 0, lbl);
+ reg1 = pParse->regRowid = ++pParse->nMem;
+ reg2 = pParse->regRoot = ++pParse->nMem;
+ reg3 = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, 1); /* file_format */
+ sqlite3VdbeUsesBtree(v, iDb);
+ j1 = sqlite3VdbeAddOp1(v, OP_If, reg3);
fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ?
1 : SQLITE_MAX_FILE_FORMAT;
- sqlite3VdbeAddOp(v, OP_Integer, fileFormat, 0);
- sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1);
- sqlite3VdbeAddOp(v, OP_Integer, ENC(db), 0);
- sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 4);
- sqlite3VdbeResolveLabel(v, lbl);
+ sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 1, reg3);
+ sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 4, reg3);
+ sqlite3VdbeJumpHere(v, j1);
/* This just creates a place-holder record in the sqlite_master table.
** The record created does not contain anything yet. It will be replaced
@@ -864,19 +894,18 @@ void sqlite3StartTable(
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
if( isView || isVirtual ){
- sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2);
}else
#endif
{
- sqlite3VdbeAddOp(v, OP_CreateTable, iDb, 0);
+ sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
}
sqlite3OpenMasterTable(pParse, iDb);
- sqlite3VdbeAddOp(v, OP_NewRowid, 0, 0);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
- sqlite3VdbeAddOp(v, OP_Insert, 0, OPFLAG_APPEND);
- sqlite3VdbeAddOp(v, OP_Close, 0, 0);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, reg3);
+ sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+ sqlite3VdbeAddOp0(v, OP_Close);
}
/* Normal (non-error) return. */
@@ -884,7 +913,7 @@ void sqlite3StartTable(
/* If an error occurs, we jump here */
begin_table_error:
- sqliteFree(zName);
+ sqlite3DbFree(db, zName);
return;
}
@@ -914,25 +943,28 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
int i;
char *z;
Column *pCol;
+ sqlite3 *db = pParse->db;
if( (p = pParse->pNewTable)==0 ) return;
- if( p->nCol+1>SQLITE_MAX_COLUMN ){
+#if SQLITE_MAX_COLUMN
+ if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
return;
}
- z = sqlite3NameFromToken(pName);
+#endif
+ z = sqlite3NameFromToken(pParse->db, pName);
if( z==0 ) return;
for(i=0; i<p->nCol; i++){
if( STRICMP(z, p->aCol[i].zName) ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
- sqliteFree(z);
+ sqlite3DbFree(db, z);
return;
}
}
if( (p->nCol & 0x7)==0 ){
Column *aNew;
- aNew = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0]));
+ aNew = sqlite3DbRealloc(pParse->db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
if( aNew==0 ){
- sqliteFree(z);
+ sqlite3DbFree(db, z);
return;
}
p->aCol = aNew;
@@ -1039,13 +1071,15 @@ void sqlite3AddColumnType(Parse *pParse, Token *pType){
Table *p;
int i;
Column *pCol;
+ sqlite3 *db;
if( (p = pParse->pNewTable)==0 ) return;
i = p->nCol-1;
if( i<0 ) return;
pCol = &p->aCol[i];
- sqliteFree(pCol->zType);
- pCol->zType = sqlite3NameFromToken(pType);
+ db = pParse->db;
+ sqlite3DbFree(db, pCol->zType);
+ pCol->zType = sqlite3NameFromToken(db, pType);
pCol->affinity = sqlite3AffinityType(pType);
}
@@ -1062,6 +1096,7 @@ void sqlite3AddColumnType(Parse *pParse, Token *pType){
void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){
Table *p;
Column *pCol;
+ sqlite3 *db = pParse->db;
if( (p = pParse->pNewTable)!=0 ){
pCol = &(p->aCol[p->nCol-1]);
if( !sqlite3ExprIsConstantOrFunction(pExpr) ){
@@ -1069,14 +1104,14 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){
pCol->zName);
}else{
Expr *pCopy;
- sqlite3ExprDelete(pCol->pDflt);
- pCol->pDflt = pCopy = sqlite3ExprDup(pExpr);
+ sqlite3ExprDelete(db, pCol->pDflt);
+ pCol->pDflt = pCopy = sqlite3ExprDup(db, pExpr);
if( pCopy ){
- sqlite3TokenCopy(&pCopy->span, &pExpr->span);
+ sqlite3TokenCopy(db, &pCopy->span, &pExpr->span);
}
}
}
- sqlite3ExprDelete(pExpr);
+ sqlite3ExprDelete(db, pExpr);
}
/*
@@ -1149,7 +1184,7 @@ void sqlite3AddPrimaryKey(
}
primary_key_exit:
- sqlite3ExprListDelete(pList);
+ sqlite3ExprListDelete(pParse->db, pList);
return;
}
@@ -1160,32 +1195,39 @@ void sqlite3AddCheckConstraint(
Parse *pParse, /* Parsing context */
Expr *pCheckExpr /* The check expression */
){
+ sqlite3 *db = pParse->db;
#ifndef SQLITE_OMIT_CHECK
Table *pTab = pParse->pNewTable;
if( pTab && !IN_DECLARE_VTAB ){
/* The CHECK expression must be duplicated so that tokens refer
** to malloced space and not the (ephemeral) text of the CREATE TABLE
** statement */
- pTab->pCheck = sqlite3ExprAnd(pTab->pCheck, sqlite3ExprDup(pCheckExpr));
+ pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck,
+ sqlite3ExprDup(db, pCheckExpr));
}
#endif
- sqlite3ExprDelete(pCheckExpr);
+ sqlite3ExprDelete(db, pCheckExpr);
}
/*
** Set the collation function of the most recently parsed table column
** to the CollSeq given.
*/
-void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){
+void sqlite3AddCollateType(Parse *pParse, Token *pToken){
Table *p;
int i;
+ char *zColl; /* Dequoted name of collation sequence */
+ sqlite3 *db;
if( (p = pParse->pNewTable)==0 ) return;
i = p->nCol-1;
+ db = pParse->db;
+ zColl = sqlite3NameFromToken(db, pToken);
+ if( !zColl ) return;
- if( sqlite3LocateCollSeq(pParse, zType, nType) ){
+ if( sqlite3LocateCollSeq(pParse, zColl, -1) ){
Index *pIdx;
- p->aCol[i].zColl = sqliteStrNDup(zType, nType);
+ p->aCol[i].zColl = zColl;
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
** then an index may have been created on this column before the
@@ -1197,6 +1239,8 @@ void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){
pIdx->azColl[0] = p->aCol[i].zColl;
}
}
+ }else{
+ sqlite3DbFree(db, zColl);
}
}
@@ -1229,7 +1273,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
pColl = sqlite3GetCollSeq(db, pColl, zName, nName);
if( !pColl ){
if( nName<0 ){
- nName = strlen(zName);
+ nName = sqlite3Strlen(db, zName);
}
sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", nName, zName);
pColl = 0;
@@ -1256,9 +1300,13 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32. So we're safe enough.
*/
-void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){
- sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, 0);
- sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0);
+void sqlite3ChangeCookie(Parse *pParse, int iDb){
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+ sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 0, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
}
/*
@@ -1305,7 +1353,7 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
** table. Memory to hold the text of the statement is obtained
** from sqliteMalloc() and must be freed by the calling function.
*/
-static char *createTableStmt(Table *p, int isTemp){
+static char *createTableStmt(sqlite3 *db, Table *p, int isTemp){
int i, k, n;
char *zStmt;
char *zSep, *zSep2, *zEnd, *z;
@@ -1329,8 +1377,11 @@ static char *createTableStmt(Table *p, int isTemp){
zEnd = "\n)";
}
n += 35 + 6*p->nCol;
- zStmt = sqliteMallocRaw( n );
- if( zStmt==0 ) return 0;
+ zStmt = sqlite3Malloc( n );
+ if( zStmt==0 ){
+ db->mallocFailed = 1;
+ return 0;
+ }
sqlite3_snprintf(n, zStmt,
!OMIT_TEMPDB&&isTemp ? "CREATE TEMP TABLE ":"CREATE TABLE ");
k = strlen(zStmt);
@@ -1382,7 +1433,7 @@ void sqlite3EndTable(
sqlite3 *db = pParse->db;
int iDb;
- if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3MallocFailed() ) {
+ if( (pEnd==0 && pSelect==0) || pParse->nErr || db->mallocFailed ) {
return;
}
p = pParse->pNewTable;
@@ -1441,7 +1492,7 @@ void sqlite3EndTable(
v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
- sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, 0);
/* Create the rootpage for the new table and push it onto the stack.
** A view has no rootpage, so just push a zero onto the stack for
@@ -1473,13 +1524,16 @@ void sqlite3EndTable(
** be redundant.
*/
if( pSelect ){
+ SelectDest dest;
Table *pSelTab;
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0);
+
+ assert(pParse->nTab==0);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
+ sqlite3VdbeChangeP5(v, 1);
pParse->nTab = 2;
- sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0);
- sqlite3VdbeAddOp(v, OP_Close, 1, 0);
+ sqlite3SelectDestInit(&dest, SRT_Table, 1);
+ sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, 1);
if( pParse->nErr==0 ){
pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect);
if( pSelTab==0 ) return;
@@ -1494,10 +1548,12 @@ void sqlite3EndTable(
/* Compute the complete text of the CREATE statement */
if( pSelect ){
- zStmt = createTableStmt(p, p->pSchema==pParse->db->aDb[1].pSchema);
+ zStmt = createTableStmt(db, p, p->pSchema==db->aDb[1].pSchema);
}else{
n = pEnd->z - pParse->sNameToken.z + 1;
- zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z);
+ zStmt = sqlite3MPrintf(db,
+ "CREATE %s %.*s", zType2, n, pParse->sNameToken.z
+ );
}
/* A slot for the record has already been allocated in the
@@ -1508,16 +1564,18 @@ void sqlite3EndTable(
*/
sqlite3NestedParse(pParse,
"UPDATE %Q.%s "
- "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q "
- "WHERE rowid=#1",
+ "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
+ "WHERE rowid=#%d",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
zType,
p->zName,
p->zName,
- zStmt
+ pParse->regRoot,
+ zStmt,
+ pParse->regRowid
);
- sqliteFree(zStmt);
- sqlite3ChangeCookie(db, v, iDb);
+ sqlite3DbFree(db, zStmt);
+ sqlite3ChangeCookie(pParse, iDb);
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* Check to see if we need to create an sqlite_sequence table for
@@ -1535,8 +1593,8 @@ void sqlite3EndTable(
#endif
/* Reparse everything to update our internal data structures */
- sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
- sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
+ sqlite3MPrintf(db, "tbl_name='%q'",p->zName), P4_DYNAMIC);
}
@@ -1549,13 +1607,18 @@ void sqlite3EndTable(
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
+ db->mallocFailed = 1;
return;
}
#ifndef SQLITE_OMIT_FOREIGN_KEY
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ void *data;
int nTo = strlen(pFKey->zTo) + 1;
pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
- sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
+ data = sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
+ if( data==(void *)pFKey ){
+ db->mallocFailed = 1;
+ }
}
#endif
pParse->pNewTable = 0;
@@ -1597,24 +1660,25 @@ void sqlite3CreateView(
DbFixer sFix;
Token *pName;
int iDb;
+ sqlite3 *db = pParse->db;
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
- sqlite3SelectDelete(pSelect);
+ sqlite3SelectDelete(db, pSelect);
return;
}
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
if( p==0 || pParse->nErr ){
- sqlite3SelectDelete(pSelect);
+ sqlite3SelectDelete(db, pSelect);
return;
}
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
- iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+ iDb = sqlite3SchemaToIndex(db, p->pSchema);
if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
&& sqlite3FixSelect(&sFix, pSelect)
){
- sqlite3SelectDelete(pSelect);
+ sqlite3SelectDelete(db, pSelect);
return;
}
@@ -1623,12 +1687,12 @@ void sqlite3CreateView(
** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns.
*/
- p->pSelect = sqlite3SelectDup(pSelect);
- sqlite3SelectDelete(pSelect);
- if( sqlite3MallocFailed() ){
+ p->pSelect = sqlite3SelectDup(db, pSelect);
+ sqlite3SelectDelete(db, pSelect);
+ if( db->mallocFailed ){
return;
}
- if( !pParse->db->init.busy ){
+ if( !db->init.busy ){
sqlite3ViewGetColumnNames(pParse, p);
}
@@ -1663,6 +1727,8 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
Select *pSel; /* Copy of the SELECT that implements the view */
int nErr = 0; /* Number of errors encountered */
int n; /* Temporarily holds the number of cursors assigned */
+ sqlite3 *db = pParse->db; /* Database connection for malloc errors */
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
assert( pTable );
@@ -1703,12 +1769,19 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** statement that defines the view.
*/
assert( pTable->pSelect );
- pSel = sqlite3SelectDup(pTable->pSelect);
+ pSel = sqlite3SelectDup(db, pTable->pSelect);
if( pSel ){
n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ xAuth = db->xAuth;
+ db->xAuth = 0;
+ pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
+ db->xAuth = xAuth;
+#else
pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
+#endif
pParse->nTab = n;
if( pSelTab ){
assert( pTable->aCol==0 );
@@ -1722,7 +1795,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
pTable->nCol = 0;
nErr++;
}
- sqlite3SelectDelete(pSel);
+ sqlite3SelectDelete(db, pSel);
} else {
nErr++;
}
@@ -1797,20 +1870,22 @@ void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){
*/
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
- sqlite3VdbeAddOp(v, OP_Destroy, iTable, iDb);
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
#ifndef SQLITE_OMIT_AUTOVACUUM
- /* OP_Destroy pushes an integer onto the stack. If this integer
+ /* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
** location iTable. The following code modifies the sqlite_master table to
** reflect this.
**
- ** The "#0" in the SQL is a special constant that means whatever value
+ ** The "#%d" in the SQL is a special constant that means whatever value
** is on the top of the stack. See sqlite3RegisterExpr().
*/
sqlite3NestedParse(pParse,
- "UPDATE %Q.%s SET rootpage=%d WHERE #0 AND rootpage=#0",
- pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable);
+ "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
+ pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
#endif
+ sqlite3ReleaseTempReg(pParse, r1);
}
/*
@@ -1882,11 +1957,12 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
sqlite3 *db = pParse->db;
int iDb;
- if( pParse->nErr || sqlite3MallocFailed() ){
+ if( pParse->nErr || db->mallocFailed ){
goto exit_drop_table;
}
assert( pName->nSrc==1 );
- pTab = sqlite3LocateTable(pParse, pName->a[0].zName, pName->a[0].zDatabase);
+ pTab = sqlite3LocateTable(pParse, isView,
+ pName->a[0].zName, pName->a[0].zDatabase);
if( pTab==0 ){
if( noErr ){
@@ -1896,6 +1972,13 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 && iDb<db->nDb );
+
+ /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
+ ** it is initialized.
+ */
+ if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
+ goto exit_drop_table;
+ }
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code;
@@ -1913,9 +1996,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( IsVirtual(pTab) ){
- if( sqlite3ViewGetColumnNames(pParse, pTab) ){
- goto exit_drop_table;
- }
code = SQLITE_DROP_VTABLE;
zArg2 = pTab->pMod->zName;
#endif
@@ -1960,13 +2040,13 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
if( v ){
Trigger *pTrigger;
Db *pDb = &db->aDb[iDb];
- sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
Vdbe *v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_VBegin, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_VBegin);
}
}
#endif
@@ -2007,6 +2087,14 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
+
+ /* Drop any statistics from the sqlite_stat1 table, if it exists */
+ if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb->zName, pTab->zName
+ );
+ }
+
if( !isView && !IsVirtual(pTab) ){
destroyTable(pParse, pTab);
}
@@ -2015,15 +2103,15 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
** the schema cookie.
*/
if( IsVirtual(pTab) ){
- sqlite3VdbeOp3(v, OP_VDestroy, iDb, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
}
- sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
- sqlite3ChangeCookie(db, v, iDb);
+ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
+ sqlite3ChangeCookie(pParse, iDb);
}
sqliteViewResetAll(db, iDb);
exit_drop_table:
- sqlite3SrcListDelete(pName);
+ sqlite3SrcListDelete(db, pName);
}
/*
@@ -2051,6 +2139,7 @@ void sqlite3CreateForeignKey(
ExprList *pToCol, /* Columns in the other table */
int flags /* Conflict resolution algorithms. */
){
+ sqlite3 *db = pParse->db;
#ifndef SQLITE_OMIT_FOREIGN_KEY
FKey *pFKey = 0;
Table *p = pParse->pNewTable;
@@ -2085,8 +2174,10 @@ void sqlite3CreateForeignKey(
nByte += strlen(pToCol->a[i].zName) + 1;
}
}
- pFKey = sqliteMalloc( nByte );
- if( pFKey==0 ) goto fk_end;
+ pFKey = sqlite3DbMallocZero(db, nByte );
+ if( pFKey==0 ){
+ goto fk_end;
+ }
pFKey->pFrom = p;
pFKey->pNextFrom = p->pFKey;
z = (char*)&pFKey[1];
@@ -2137,10 +2228,10 @@ void sqlite3CreateForeignKey(
pFKey = 0;
fk_end:
- sqliteFree(pFKey);
+ sqlite3DbFree(db, pFKey);
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
- sqlite3ExprListDelete(pFromCol);
- sqlite3ExprListDelete(pToCol);
+ sqlite3ExprListDelete(db, pFromCol);
+ sqlite3ExprListDelete(db, pToCol);
}
/*
@@ -2165,7 +2256,7 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
** content of an index in response to a REINDEX command.
**
** if memRootPage is not negative, it means that the index is newly
-** created. The memory cell specified by memRootPage contains the
+** created. The register specified by memRootPage contains the
** root page number of the index. If memRootPage is negative, then
** the index already exists and must be cleared before being refilled and
** the root page number of the index is taken from pIndex->tnum.
@@ -2178,11 +2269,14 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
- int iDb = sqlite3SchemaToIndex(pParse->db, pIndex->pSchema);
+ int regIdxKey; /* Registers containing the index key */
+ int regRecord; /* Register holding assemblied index record */
+ sqlite3 *db = pParse->db; /* The database connection */
+ int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
- pParse->db->aDb[iDb].zName ) ){
+ db->aDb[iDb].zName ) ){
return;
}
#endif
@@ -2193,34 +2287,40 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
if( memRootPage>=0 ){
- sqlite3VdbeAddOp(v, OP_MemLoad, memRootPage, 0);
- tnum = 0;
+ tnum = memRootPage;
}else{
tnum = pIndex->tnum;
- sqlite3VdbeAddOp(v, OP_Clear, tnum, iDb);
+ sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
}
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
pKey = sqlite3IndexKeyinfo(pParse, pIndex);
- sqlite3VdbeOp3(v, OP_OpenWrite, iIdx, tnum, (char *)pKey, P3_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
+ (char *)pKey, P4_KEYINFO_HANDOFF);
+ if( memRootPage>=0 ){
+ sqlite3VdbeChangeP5(v, 1);
+ }
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
- addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
- sqlite3GenerateIndexKey(v, pIndex, iTab);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
+ regRecord = sqlite3GetTempReg(pParse);
+ regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
if( pIndex->onError!=OE_None ){
- int curaddr = sqlite3VdbeCurrentAddr(v);
- int addr2 = curaddr+4;
- sqlite3VdbeChangeP2(v, curaddr-1, addr2);
- sqlite3VdbeAddOp(v, OP_Rowid, iTab, 0);
- sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
- sqlite3VdbeAddOp(v, OP_IsUnique, iIdx, addr2);
- sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort,
- "indexed columns are not unique", P3_STATIC);
- assert( sqlite3MallocFailed() || addr2==sqlite3VdbeCurrentAddr(v) );
- }
- sqlite3VdbeAddOp(v, OP_IdxInsert, iIdx, 0);
- sqlite3VdbeAddOp(v, OP_Next, iTab, addr1+1);
+ int j1, j2;
+ int regRowid;
+
+ regRowid = regIdxKey + pIndex->nColumn;
+ j1 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdxKey, 0, pIndex->nColumn);
+ j2 = sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx,
+ 0, regRowid, SQLITE_INT_TO_PTR(regRecord), P4_INT32);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0,
+ "indexed columns are not unique", P4_STATIC);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, j2);
+ }
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
+ sqlite3ReleaseTempReg(pParse, regRecord);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp(v, OP_Close, iTab, 0);
- sqlite3VdbeAddOp(v, OP_Close, iIdx, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, iTab);
+ sqlite3VdbeAddOp1(v, OP_Close, iIdx);
}
/*
@@ -2264,7 +2364,7 @@ void sqlite3CreateIndex(
int nExtra = 0;
char *zExtra;
- if( pParse->nErr || sqlite3MallocFailed() || IN_DECLARE_VTAB ){
+ if( pParse->nErr || db->mallocFailed || IN_DECLARE_VTAB ){
goto exit_create_index;
}
@@ -2283,11 +2383,14 @@ void sqlite3CreateIndex(
#ifndef SQLITE_OMIT_TEMPDB
/* If the index name was unqualified, check if the the table
- ** is a temp table. If so, set the database to 1.
+ ** is a temp table. If so, set the database to 1. Do not do this
+ ** if initialising a database schema.
*/
- pTab = sqlite3SrcListLookup(pParse, pTblName);
- if( pName2 && pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
- iDb = 1;
+ if( !db->init.busy ){
+ pTab = sqlite3SrcListLookup(pParse, pTblName);
+ if( pName2 && pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
+ iDb = 1;
+ }
}
#endif
@@ -2298,7 +2401,7 @@ void sqlite3CreateIndex(
** sqlite3FixSrcList can never fail. */
assert(0);
}
- pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName,
+ pTab = sqlite3LocateTable(pParse, 0, pTblName->a[0].zName,
pTblName->a[0].zDatabase);
if( !pTab ) goto exit_create_index;
assert( db->aDb[iDb].pSchema==pTab->pSchema );
@@ -2342,7 +2445,7 @@ void sqlite3CreateIndex(
** own name.
*/
if( pName ){
- zName = sqlite3NameFromToken(pName);
+ zName = sqlite3NameFromToken(db, pName);
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index;
if( zName==0 ) goto exit_create_index;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
@@ -2362,14 +2465,13 @@ void sqlite3CreateIndex(
goto exit_create_index;
}
}else{
- char zBuf[30];
int n;
Index *pLoop;
for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){}
- sqlite3_snprintf(sizeof(zBuf),zBuf,"_%d",n);
- zName = 0;
- sqlite3SetString(&zName, "sqlite_autoindex_", pTab->zName, zBuf, (char*)0);
- if( zName==0 ) goto exit_create_index;
+ zName = sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName, n);
+ if( zName==0 ){
+ goto exit_create_index;
+ }
}
/* Check for authorization to create an index.
@@ -2395,7 +2497,7 @@ void sqlite3CreateIndex(
if( pList==0 ){
nullId.z = (u8*)pTab->aCol[pTab->nCol-1].zName;
nullId.n = strlen((char*)nullId.z);
- pList = sqlite3ExprListAppend(0, 0, &nullId);
+ pList = sqlite3ExprListAppend(pParse, 0, 0, &nullId);
if( pList==0 ) goto exit_create_index;
pList->a[0].sortOrder = sortOrder;
}
@@ -2415,7 +2517,7 @@ void sqlite3CreateIndex(
*/
nName = strlen(zName);
nCol = pList->nExpr;
- pIndex = sqliteMalloc(
+ pIndex = sqlite3DbMallocZero(db,
sizeof(Index) + /* Index structure */
sizeof(int)*nCol + /* Index.aiColumn */
sizeof(int)*(nCol+1) + /* Index.aiRowEst */
@@ -2424,7 +2526,9 @@ void sqlite3CreateIndex(
nName + 1 + /* Index.zName */
nExtra /* Collation sequence names */
);
- if( sqlite3MallocFailed() ) goto exit_create_index;
+ if( db->mallocFailed ){
+ goto exit_create_index;
+ }
pIndex->azColl = (char**)(&pIndex[1]);
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]);
@@ -2551,6 +2655,7 @@ void sqlite3CreateIndex(
pIndex->zName, strlen(pIndex->zName)+1, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
+ db->mallocFailed = 1;
goto exit_create_index;
}
db->flags |= SQLITE_InternChanges;
@@ -2577,7 +2682,7 @@ void sqlite3CreateIndex(
else if( db->init.busy==0 ){
Vdbe *v;
char *zStmt;
- int iMem = pParse->nMem++;
+ int iMem = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto exit_create_index;
@@ -2586,15 +2691,14 @@ void sqlite3CreateIndex(
/* Create the rootpage for the index
*/
sqlite3BeginWriteOperation(pParse, 1, iDb);
- sqlite3VdbeAddOp(v, OP_CreateIndex, iDb, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0);
+ sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);
/* Gather the complete text of the CREATE INDEX statement into
** the zStmt variable
*/
if( pStart && pEnd ){
/* A named index with an explicit CREATE INDEX statement */
- zStmt = sqlite3MPrintf("CREATE%s INDEX %.*s",
+ zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
onError==OE_None ? "" : " UNIQUE",
pEnd->z - pName->z + 1,
pName->z);
@@ -2607,24 +2711,24 @@ void sqlite3CreateIndex(
/* Add an entry in sqlite_master for this index
*/
sqlite3NestedParse(pParse,
- "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#0,%Q);",
+ "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
pIndex->zName,
pTab->zName,
+ iMem,
zStmt
);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqliteFree(zStmt);
+ sqlite3DbFree(db, zStmt);
/* Fill the index with data and reparse the schema. Code an OP_Expire
** to invalidate all pre-compiled statements.
*/
if( pTblName ){
sqlite3RefillIndex(pParse, pIndex, iMem);
- sqlite3ChangeCookie(db, v, iDb);
- sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
- sqlite3MPrintf("name='%q'", pIndex->zName), P3_DYNAMIC);
- sqlite3VdbeAddOp(v, OP_Expire, 0, 0);
+ sqlite3ChangeCookie(pParse, iDb);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
+ sqlite3MPrintf(db, "name='%q'", pIndex->zName), P4_DYNAMIC);
+ sqlite3VdbeAddOp1(v, OP_Expire, 0);
}
}
@@ -2654,9 +2758,9 @@ exit_create_index:
if( pIndex ){
freeIndex(pIndex);
}
- sqlite3ExprListDelete(pList);
- sqlite3SrcListDelete(pTblName);
- sqliteFree(zName);
+ sqlite3ExprListDelete(db, pList);
+ sqlite3SrcListDelete(db, pTblName);
+ sqlite3DbFree(db, zName);
return;
}
@@ -2668,11 +2772,17 @@ void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
Vdbe *v;
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1);
- sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0);
- sqlite3VdbeAddOp(v, OP_Ge, 0, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0);
- sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1);
+ int r1 = sqlite3GetTempReg(pParse);
+ int r2 = sqlite3GetTempReg(pParse);
+ int j1;
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, 1);
+ sqlite3VdbeUsesBtree(v, iDb);
+ sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
+ j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 1, r2);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempReg(pParse, r2);
}
}
@@ -2721,7 +2831,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
sqlite3 *db = pParse->db;
int iDb;
- if( pParse->nErr || sqlite3MallocFailed() ){
+ if( pParse->nErr || db->mallocFailed ){
goto exit_drop_index;
}
assert( pName->nSrc==1 );
@@ -2761,18 +2871,25 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
/* Generate code to remove the index and from the master table */
v = sqlite3GetVdbe(pParse);
if( v ){
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
pIndex->zName
);
- sqlite3ChangeCookie(db, v, iDb);
+ if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
+ db->aDb[iDb].zName, pIndex->zName
+ );
+ }
+ sqlite3ChangeCookie(pParse, iDb);
destroyRootPage(pParse, pIndex->tnum, iDb);
- sqlite3VdbeOp3(v, OP_DropIndex, iDb, 0, pIndex->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
}
exit_drop_index:
- sqlite3SrcListDelete(pName);
+ sqlite3SrcListDelete(db, pName);
}
/*
@@ -2791,6 +2908,7 @@ exit_drop_index:
** pointer if the array was resized.
*/
void *sqlite3ArrayAllocate(
+ sqlite3 *db, /* Connection to notify of malloc failures */
void *pArray, /* Array of objects. Might be reallocated */
int szEntry, /* Size of each object in the array */
int initSize, /* Suggested initial allocation, in elements */
@@ -2803,7 +2921,7 @@ void *sqlite3ArrayAllocate(
void *pNew;
int newSize;
newSize = (*pnAlloc)*2 + initSize;
- pNew = sqliteRealloc(pArray, newSize*szEntry);
+ pNew = sqlite3DbRealloc(db, pArray, newSize*szEntry);
if( pNew==0 ){
*pIdx = -1;
return pArray;
@@ -2824,14 +2942,15 @@ void *sqlite3ArrayAllocate(
**
** A new IdList is returned, or NULL if malloc() fails.
*/
-IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){
+IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
int i;
if( pList==0 ){
- pList = sqliteMalloc( sizeof(IdList) );
+ pList = sqlite3DbMallocZero(db, sizeof(IdList) );
if( pList==0 ) return 0;
pList->nAlloc = 0;
}
pList->a = sqlite3ArrayAllocate(
+ db,
pList->a,
sizeof(pList->a[0]),
5,
@@ -2840,24 +2959,24 @@ IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){
&i
);
if( i<0 ){
- sqlite3IdListDelete(pList);
+ sqlite3IdListDelete(db, pList);
return 0;
}
- pList->a[i].zName = sqlite3NameFromToken(pToken);
+ pList->a[i].zName = sqlite3NameFromToken(db, pToken);
return pList;
}
/*
** Delete an IdList.
*/
-void sqlite3IdListDelete(IdList *pList){
+void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
int i;
if( pList==0 ) return;
for(i=0; i<pList->nId; i++){
- sqliteFree(pList->a[i].zName);
+ sqlite3DbFree(db, pList->a[i].zName);
}
- sqliteFree(pList->a);
- sqliteFree(pList);
+ sqlite3DbFree(db, pList->a);
+ sqlite3DbFree(db, pList);
}
/*
@@ -2889,29 +3008,34 @@ int sqlite3IdListIndex(IdList *pList, const char *zName){
**
** In other words, if call like this:
**
-** sqlite3SrcListAppend(A,B,0);
+** sqlite3SrcListAppend(D,A,B,0);
**
** Then B is a table name and the database name is unspecified. If called
** like this:
**
-** sqlite3SrcListAppend(A,B,C);
+** sqlite3SrcListAppend(D,A,B,C);
**
** Then C is the table name and B is the database name.
*/
-SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
+SrcList *sqlite3SrcListAppend(
+ sqlite3 *db, /* Connection to notify of malloc failures */
+ SrcList *pList, /* Append to this SrcList. NULL creates a new SrcList */
+ Token *pTable, /* Table to append */
+ Token *pDatabase /* Database of the table */
+){
struct SrcList_item *pItem;
if( pList==0 ){
- pList = sqliteMalloc( sizeof(SrcList) );
+ pList = sqlite3DbMallocZero(db, sizeof(SrcList) );
if( pList==0 ) return 0;
pList->nAlloc = 1;
}
if( pList->nSrc>=pList->nAlloc ){
SrcList *pNew;
pList->nAlloc *= 2;
- pNew = sqliteRealloc(pList,
+ pNew = sqlite3DbRealloc(db, pList,
sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) );
if( pNew==0 ){
- sqlite3SrcListDelete(pList);
+ sqlite3SrcListDelete(db, pList);
return 0;
}
pList = pNew;
@@ -2926,8 +3050,8 @@ SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
pDatabase = pTable;
pTable = pTemp;
}
- pItem->zName = sqlite3NameFromToken(pTable);
- pItem->zDatabase = sqlite3NameFromToken(pDatabase);
+ pItem->zName = sqlite3NameFromToken(db, pTable);
+ pItem->zDatabase = sqlite3NameFromToken(db, pDatabase);
pItem->iCursor = -1;
pItem->isPopulated = 0;
pList->nSrc++;
@@ -2940,7 +3064,7 @@ SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
int i;
struct SrcList_item *pItem;
- assert(pList || sqlite3MallocFailed() );
+ assert(pList || pParse->db->mallocFailed );
if( pList ){
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
if( pItem->iCursor>=0 ) break;
@@ -2955,20 +3079,20 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
/*
** Delete an entire SrcList including all its substructure.
*/
-void sqlite3SrcListDelete(SrcList *pList){
+void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
int i;
struct SrcList_item *pItem;
if( pList==0 ) return;
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
- sqliteFree(pItem->zDatabase);
- sqliteFree(pItem->zName);
- sqliteFree(pItem->zAlias);
+ sqlite3DbFree(db, pItem->zDatabase);
+ sqlite3DbFree(db, pItem->zName);
+ sqlite3DbFree(db, pItem->zAlias);
sqlite3DeleteTable(pItem->pTab);
- sqlite3SelectDelete(pItem->pSelect);
- sqlite3ExprDelete(pItem->pOn);
- sqlite3IdListDelete(pItem->pUsing);
+ sqlite3SelectDelete(db, pItem->pSelect);
+ sqlite3ExprDelete(db, pItem->pOn);
+ sqlite3IdListDelete(db, pItem->pUsing);
}
- sqliteFree(pList);
+ sqlite3DbFree(db, pList);
}
/*
@@ -2988,6 +3112,7 @@ void sqlite3SrcListDelete(SrcList *pList){
** term added.
*/
SrcList *sqlite3SrcListAppendFromTerm(
+ Parse *pParse, /* Parsing context */
SrcList *p, /* The left part of the FROM clause already seen */
Token *pTable, /* Name of the table to add to the FROM clause */
Token *pDatabase, /* Name of the database containing pTable */
@@ -2997,16 +3122,17 @@ SrcList *sqlite3SrcListAppendFromTerm(
IdList *pUsing /* The USING clause of a join */
){
struct SrcList_item *pItem;
- p = sqlite3SrcListAppend(p, pTable, pDatabase);
+ sqlite3 *db = pParse->db;
+ p = sqlite3SrcListAppend(db, p, pTable, pDatabase);
if( p==0 || p->nSrc==0 ){
- sqlite3ExprDelete(pOn);
- sqlite3IdListDelete(pUsing);
- sqlite3SelectDelete(pSubquery);
+ sqlite3ExprDelete(db, pOn);
+ sqlite3IdListDelete(db, pUsing);
+ sqlite3SelectDelete(db, pSubquery);
return p;
}
pItem = &p->a[p->nSrc-1];
if( pAlias && pAlias->n ){
- pItem->zAlias = sqlite3NameFromToken(pAlias);
+ pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
pItem->pSelect = pSubquery;
pItem->pOn = pOn;
@@ -3048,17 +3174,18 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
int i;
if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
- if( pParse->nErr || sqlite3MallocFailed() ) return;
+ if( pParse->nErr || db->mallocFailed ) return;
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return;
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type!=TK_DEFERRED ){
for(i=0; i<db->nDb; i++){
- sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
+ sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
+ sqlite3VdbeUsesBtree(v, i);
}
}
- sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
}
/*
@@ -3069,12 +3196,12 @@ void sqlite3CommitTransaction(Parse *pParse){
Vdbe *v;
if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
- if( pParse->nErr || sqlite3MallocFailed() ) return;
+ if( pParse->nErr || db->mallocFailed ) return;
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return;
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0);
}
}
@@ -3086,12 +3213,12 @@ void sqlite3RollbackTransaction(Parse *pParse){
Vdbe *v;
if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
- if( pParse->nErr || sqlite3MallocFailed() ) return;
+ if( pParse->nErr || db->mallocFailed ) return;
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 1);
+ sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1);
}
}
@@ -3102,7 +3229,15 @@ void sqlite3RollbackTransaction(Parse *pParse){
int sqlite3OpenTempDatabase(Parse *pParse){
sqlite3 *db = pParse->db;
if( db->aDb[1].pBt==0 && !pParse->explain ){
- int rc = sqlite3BtreeFactory(db, 0, 0, SQLITE_DEFAULT_CACHE_SIZE,
+ int rc;
+ static const int flags =
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE |
+ SQLITE_OPEN_DELETEONCLOSE |
+ SQLITE_OPEN_TEMP_DB;
+
+ rc = sqlite3BtreeFactory(db, 0, 0, SQLITE_DEFAULT_CACHE_SIZE, flags,
&db->aDb[1].pBt);
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "unable to open a temporary database "
@@ -3110,16 +3245,10 @@ int sqlite3OpenTempDatabase(Parse *pParse){
pParse->rc = rc;
return 1;
}
- if( db->flags & !db->autoCommit ){
- rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
- if( rc!=SQLITE_OK ){
- sqlite3ErrorMsg(pParse, "unable to get a write lock on "
- "the temporary database file");
- pParse->rc = rc;
- return 1;
- }
- }
+ assert( (db->flags & SQLITE_InTrans)==0 || db->autoCommit );
assert( db->aDb[1].pSchema );
+ sqlite3PagerJournalMode(sqlite3BtreePager(db->aDb[1].pBt),
+ db->dfltJournalMode);
}
return 0;
}
@@ -3155,7 +3284,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
if( v==0 ) return; /* This only happens if there was a prior error */
db = pParse->db;
if( pParse->cookieGoto==0 ){
- pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0)+1;
+ pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
}
if( iDb>=0 ){
assert( iDb<db->nDb );
@@ -3196,7 +3325,7 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
sqlite3CodeVerifySchema(pParse, iDb);
pParse->writeMask |= 1<<iDb;
if( setStatement && pParse->nested==0 ){
- sqlite3VdbeAddOp(v, OP_Statement, iDb, 0);
+ sqlite3VdbeAddOp1(v, OP_Statement, iDb);
}
if( (OMIT_TEMPDB || iDb!=1) && pParse->db->aDb[1].pBt!=0 ){
sqlite3BeginWriteOperation(pParse, setStatement, 1);
@@ -3295,30 +3424,33 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
reindexDatabases(pParse, 0);
return;
}else if( pName2==0 || pName2->z==0 ){
+ char *zColl;
assert( pName1->z );
- pColl = sqlite3FindCollSeq(db, ENC(db), (char*)pName1->z, pName1->n, 0);
+ zColl = sqlite3NameFromToken(pParse->db, pName1);
+ if( !zColl ) return;
+ pColl = sqlite3FindCollSeq(db, ENC(db), zColl, -1, 0);
if( pColl ){
- char *zColl = sqliteStrNDup((const char *)pName1->z, pName1->n);
if( zColl ){
reindexDatabases(pParse, zColl);
- sqliteFree(zColl);
+ sqlite3DbFree(db, zColl);
}
return;
}
+ sqlite3DbFree(db, zColl);
}
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
if( iDb<0 ) return;
- z = sqlite3NameFromToken(pObjName);
+ z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
zDb = db->aDb[iDb].zName;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
- sqliteFree(z);
+ sqlite3DbFree(db, z);
return;
}
pIndex = sqlite3FindIndex(db, z, zDb);
- sqliteFree(z);
+ sqlite3DbFree(db, z);
if( pIndex ){
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
@@ -3333,7 +3465,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
**
** If successful, a pointer to the new structure is returned. In this case
-** the caller is responsible for calling sqliteFree() on the returned
+** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
** pointer. If an error occurs (out of memory or missing collation
** sequence), NULL is returned and the state of pParse updated to reflect
** the error.
@@ -3342,9 +3474,11 @@ KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
int i;
int nCol = pIdx->nColumn;
int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol;
- KeyInfo *pKey = (KeyInfo *)sqliteMalloc(nBytes);
+ sqlite3 *db = pParse->db;
+ KeyInfo *pKey = (KeyInfo *)sqlite3DbMallocZero(db, nBytes);
if( pKey ){
+ pKey->db = pParse->db;
pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]);
assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) );
for(i=0; i<nCol; i++){
@@ -3357,12 +3491,13 @@ KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
}
if( pParse->nErr ){
- sqliteFree(pKey);
+ sqlite3DbFree(db, pKey);
pKey = 0;
}
return pKey;
}
+/* Begin preload-cache.patch for Chromium */
/* See declaration in sqlite3.h for information */
int sqlite3Preload(sqlite3 *db)
{
@@ -3387,3 +3522,4 @@ int sqlite3Preload(sqlite3 *db)
return SQLITE_ERROR;
return SQLITE_OK;
}
+/* End preload-cache.patch for Chromium */
diff --git a/third_party/sqlite/callback.c b/third_party/sqlite/src/callback.c
index f3d4188..a77f994 100644..100755
--- a/third_party/sqlite/callback.c
+++ b/third_party/sqlite/src/callback.c
@@ -13,7 +13,7 @@
** This file contains functions used to access the internal hash tables
** of user defined functions and collation sequences.
**
-** $Id: callback.c,v 1.18 2007/05/07 09:32:45 danielk1977 Exp $
+** $Id: callback.c,v 1.26 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
@@ -25,17 +25,17 @@
*/
static void callCollNeeded(sqlite3 *db, const char *zName, int nName){
assert( !db->xCollNeeded || !db->xCollNeeded16 );
- if( nName<0 ) nName = strlen(zName);
+ if( nName<0 ) nName = sqlite3Strlen(db, zName);
if( db->xCollNeeded ){
- char *zExternal = sqliteStrNDup(zName, nName);
+ char *zExternal = sqlite3DbStrNDup(db, zName, nName);
if( !zExternal ) return;
db->xCollNeeded(db->pCollNeededArg, db, (int)ENC(db), zExternal);
- sqliteFree(zExternal);
+ sqlite3DbFree(db, zExternal);
}
#ifndef SQLITE_OMIT_UTF16
if( db->xCollNeeded16 ){
char const *zExternal;
- sqlite3_value *pTmp = sqlite3ValueNew();
+ sqlite3_value *pTmp = sqlite3ValueNew(db);
sqlite3ValueSetStr(pTmp, nName, zName, SQLITE_UTF8, SQLITE_STATIC);
zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE);
if( zExternal ){
@@ -158,11 +158,11 @@ static CollSeq *findCollSeqEntry(
int create
){
CollSeq *pColl;
- if( nName<0 ) nName = strlen(zName);
+ if( nName<0 ) nName = sqlite3Strlen(db, zName);
pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
if( 0==pColl && create ){
- pColl = sqliteMalloc( 3*sizeof(*pColl) + nName + 1 );
+ pColl = sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 );
if( pColl ){
CollSeq *pDel = 0;
pColl[0].zName = (char*)&pColl[3];
@@ -179,9 +179,10 @@ static CollSeq *findCollSeqEntry(
** return the pColl pointer to be deleted (because it wasn't added
** to the hash table).
*/
- assert( !pDel || (sqlite3MallocFailed() && pDel==pColl) );
- if( pDel ){
- sqliteFree(pDel);
+ assert( pDel==0 || pDel==pColl );
+ if( pDel!=0 ){
+ db->mallocFailed = 1;
+ sqlite3DbFree(db, pDel);
pColl = 0;
}
}
@@ -303,14 +304,15 @@ FuncDef *sqlite3FindFunction(
** new entry to the hash table and return it.
*/
if( createFlag && bestmatch<6 &&
- (pBest = sqliteMalloc(sizeof(*pBest)+nName))!=0 ){
+ (pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName))!=0 ){
pBest->nArg = nArg;
pBest->pNext = pFirst;
pBest->iPrefEnc = enc;
memcpy(pBest->zName, zName, nName);
pBest->zName[nName] = 0;
if( pBest==sqlite3HashInsert(&db->aFunc,pBest->zName,nName,(void*)pBest) ){
- sqliteFree(pBest);
+ db->mallocFailed = 1;
+ sqlite3DbFree(db, pBest);
return 0;
}
}
@@ -323,9 +325,11 @@ FuncDef *sqlite3FindFunction(
/*
** Free all resources held by the schema structure. The void* argument points
-** at a Schema struct. This function does not call sqliteFree() on the
+** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the
** pointer itself, it just cleans up subsiduary resources (i.e. the contents
** of the schema hash tables).
+**
+** The Schema.cache_size variable is not cleared.
*/
void sqlite3SchemaFree(void *p){
Hash temp1;
@@ -339,7 +343,7 @@ void sqlite3SchemaFree(void *p){
sqlite3HashClear(&pSchema->aFKey);
sqlite3HashClear(&pSchema->idxHash);
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
+ sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem));
}
sqlite3HashClear(&temp2);
sqlite3HashInit(&pSchema->tblHash, SQLITE_HASH_STRING, 0);
@@ -356,14 +360,16 @@ void sqlite3SchemaFree(void *p){
** Find and return the schema associated with a BTree. Create
** a new one if necessary.
*/
-Schema *sqlite3SchemaGet(Btree *pBt){
+Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
Schema * p;
if( pBt ){
- p = (Schema *)sqlite3BtreeSchema(pBt,sizeof(Schema),sqlite3SchemaFree);
+ p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaFree);
}else{
- p = (Schema *)sqliteMalloc(sizeof(Schema));
+ p = (Schema *)sqlite3MallocZero(sizeof(Schema));
}
- if( p && 0==p->file_format ){
+ if( !p ){
+ db->mallocFailed = 1;
+ }else if ( 0==p->file_format ){
sqlite3HashInit(&p->tblHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&p->idxHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&p->trigHash, SQLITE_HASH_STRING, 0);
diff --git a/third_party/sqlite/complete.c b/third_party/sqlite/src/complete.c
index 536ffad..8e2dbc2 100644..100755
--- a/third_party/sqlite/complete.c
+++ b/third_party/sqlite/src/complete.c
@@ -16,7 +16,7 @@
** separating it out, the code will be automatically omitted from
** static links that do not use it.
**
-** $Id: complete.c,v 1.3 2006/01/18 15:25:17 danielk1977 Exp $
+** $Id: complete.c,v 1.7 2008/06/13 18:24:27 drh Exp $
*/
#include "sqliteInt.h"
#ifndef SQLITE_OMIT_COMPLETE
@@ -24,8 +24,16 @@
/*
** This is defined in tokenize.c. We just have to import the definition.
*/
-extern const char sqlite3IsIdChar[];
-#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsIdChar[c-0x20]))
+#ifndef SQLITE_AMALGAMATION
+#ifdef SQLITE_ASCII
+extern const char sqlite3IsAsciiIdChar[];
+#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsAsciiIdChar[c-0x20]))
+#endif
+#ifdef SQLITE_EBCDIC
+extern const char sqlite3IsEbcdicIdChar[];
+#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
+#endif
+#endif /* SQLITE_AMALGAMATION */
/*
@@ -248,13 +256,19 @@ int sqlite3_complete(const char *zSql){
int sqlite3_complete16(const void *zSql){
sqlite3_value *pVal;
char const *zSql8;
- int rc = 0;
+ int rc = SQLITE_NOMEM;
- pVal = sqlite3ValueNew();
+#ifndef SQLITE_OMIT_AUTOINIT
+ rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ pVal = sqlite3ValueNew(0);
sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
if( zSql8 ){
rc = sqlite3_complete(zSql8);
+ }else{
+ rc = SQLITE_NOMEM;
}
sqlite3ValueFree(pVal);
return sqlite3ApiExit(0, rc);
diff --git a/third_party/sqlite/date.c b/third_party/sqlite/src/date.c
index bf4bc56..08ee30c 100644..100755
--- a/third_party/sqlite/date.c
+++ b/third_party/sqlite/src/date.c
@@ -16,7 +16,7 @@
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: date.c,v 1.66 2007/05/08 21:56:00 drh Exp $
+** $Id: date.c,v 1.87 2008/07/28 19:34:53 drh Exp $
**
** SQLite processes all times and dates as Julian Day numbers. The
** dates and times are stored as the number of days since noon
@@ -46,7 +46,6 @@
** Richmond, Virginia (USA)
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
@@ -55,19 +54,36 @@
#ifndef SQLITE_OMIT_DATETIME_FUNCS
/*
+** On recent Windows platforms, the localtime_s() function is available
+** as part of the "Secure CRT". It is essentially equivalent to
+** localtime_r() available under most POSIX platforms, except that the
+** order of the parameters is reversed.
+**
+** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
+**
+** If the user has not indicated to use localtime_r() or localtime_s()
+** already, check for an MSVC build environment that provides
+** localtime_s().
+*/
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
+ defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
+#define HAVE_LOCALTIME_S 1
+#endif
+
+/*
** A structure for holding a single date and time.
*/
typedef struct DateTime DateTime;
struct DateTime {
- double rJD; /* The julian day number */
- int Y, M, D; /* Year, month, and day */
- int h, m; /* Hour and minutes */
- int tz; /* Timezone offset in minutes */
- double s; /* Seconds */
- char validYMD; /* True if Y,M,D are valid */
- char validHMS; /* True if h,m,s are valid */
- char validJD; /* True if rJD is valid */
- char validTZ; /* True if tz is valid */
+ sqlite3_int64 iJD; /* The julian day number times 86400000 */
+ int Y, M, D; /* Year, month, and day */
+ int h, m; /* Hour and minutes */
+ int tz; /* Timezone offset in minutes */
+ double s; /* Seconds */
+ char validYMD; /* True if Y,M,D are valid */
+ char validHMS; /* True if h,m,s are valid */
+ char validJD; /* True if iJD is valid */
+ char validTZ; /* True if tz is valid */
};
@@ -132,23 +148,32 @@ end_getDigits:
**
** (+/-)HH:MM
**
+** Or the "zulu" notation:
+**
+** Z
+**
** If the parse is successful, write the number of minutes
-** of change in *pnMin and return 0. If a parser error occurs,
-** return 0.
+** of change in p->tz and return 0. If a parser error occurs,
+** return non-zero.
**
** A missing specifier is not considered an error.
*/
static int parseTimezone(const char *zDate, DateTime *p){
int sgn = 0;
int nHr, nMn;
+ int c;
while( isspace(*(u8*)zDate) ){ zDate++; }
p->tz = 0;
- if( *zDate=='-' ){
+ c = *zDate;
+ if( c=='-' ){
sgn = -1;
- }else if( *zDate=='+' ){
+ }else if( c=='+' ){
sgn = +1;
+ }else if( c=='Z' || c=='z' ){
+ zDate++;
+ goto zulu_time;
}else{
- return *zDate!=0;
+ return c!=0;
}
zDate++;
if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
@@ -156,6 +181,7 @@ static int parseTimezone(const char *zDate, DateTime *p){
}
zDate += 5;
p->tz = sgn*(nMn + nHr*60);
+zulu_time:
while( isspace(*(u8*)zDate) ){ zDate++; }
return *zDate!=0;
}
@@ -230,12 +256,12 @@ static void computeJD(DateTime *p){
B = 2 - A + (A/4);
X1 = 365.25*(Y+4716);
X2 = 30.6001*(M+1);
- p->rJD = X1 + X2 + D + B - 1524.5;
+ p->iJD = (X1 + X2 + D + B - 1524.5)*86400000;
p->validJD = 1;
if( p->validHMS ){
- p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0;
+ p->iJD += p->h*3600000 + p->m*60000 + p->s*1000;
if( p->validTZ ){
- p->rJD -= p->tz*60/86400.0;
+ p->iJD -= p->tz*60000;
p->validYMD = 0;
p->validHMS = 0;
p->validTZ = 0;
@@ -288,6 +314,17 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
}
/*
+** Set the time to the current time reported by the VFS
+*/
+static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
+ double r;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ sqlite3OsCurrentTime(db->pVfs, &r);
+ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
+ p->validJD = 1;
+}
+
+/*
** Attempt to parse the given string into a Julian Day Number. Return
** the number of errors.
**
@@ -303,20 +340,22 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
** as there is a time string. The time string can be omitted as long
** as there is a year and date.
*/
-static int parseDateOrTime(const char *zDate, DateTime *p){
- memset(p, 0, sizeof(*p));
+static int parseDateOrTime(
+ sqlite3_context *context,
+ const char *zDate,
+ DateTime *p
+){
if( parseYyyyMmDd(zDate,p)==0 ){
return 0;
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0){
- double r;
- sqlite3OsCurrentTime(&r);
- p->rJD = r;
- p->validJD = 1;
+ setDateTimeToCurrent(context, p);
return 0;
}else if( sqlite3IsNumber(zDate, 0, SQLITE_UTF8) ){
- getValue(zDate, &p->rJD);
+ double r;
+ getValue(zDate, &r);
+ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
p->validJD = 1;
return 0;
}
@@ -334,7 +373,7 @@ static void computeYMD(DateTime *p){
p->M = 1;
p->D = 1;
}else{
- Z = p->rJD + 0.5;
+ Z = (p->iJD + 43200000)/86400000;
A = (Z - 1867216.25)/36524.25;
A = Z + 1 + A - (A/4);
B = A + 1524;
@@ -353,12 +392,11 @@ static void computeYMD(DateTime *p){
** Compute the Hour, Minute, and Seconds from the julian day number.
*/
static void computeHMS(DateTime *p){
- int Z, s;
+ int s;
if( p->validHMS ) return;
computeJD(p);
- Z = p->rJD + 0.5;
- s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5;
- p->s = 0.001*s;
+ s = (p->iJD + 43200000) % 86400000;
+ p->s = s/1000.0;
s = p->s;
p->s -= s;
p->h = s/3600;
@@ -385,11 +423,13 @@ static void clearYMD_HMS_TZ(DateTime *p){
p->validTZ = 0;
}
+#ifndef SQLITE_OMIT_LOCALTIME
/*
-** Compute the difference (in days) between localtime and UTC (a.k.a. GMT)
+** Compute the difference (in milliseconds)
+** between localtime and UTC (a.k.a. GMT)
** for the time value p where p is in UTC.
*/
-static double localtimeOffset(DateTime *p){
+static int localtimeOffset(DateTime *p){
DateTime x, y;
time_t t;
x = *p;
@@ -408,7 +448,7 @@ static double localtimeOffset(DateTime *p){
x.tz = 0;
x.validJD = 0;
computeJD(&x);
- t = (x.rJD-2440587.5)*86400.0 + 0.5;
+ t = x.iJD/1000 - 2440587.5*86400.0;
#ifdef HAVE_LOCALTIME_R
{
struct tm sLocal;
@@ -420,10 +460,21 @@ static double localtimeOffset(DateTime *p){
y.m = sLocal.tm_min;
y.s = sLocal.tm_sec;
}
+#elif defined(HAVE_LOCALTIME_S)
+ {
+ struct tm sLocal;
+ localtime_s(&sLocal, &t);
+ y.Y = sLocal.tm_year + 1900;
+ y.M = sLocal.tm_mon + 1;
+ y.D = sLocal.tm_mday;
+ y.h = sLocal.tm_hour;
+ y.m = sLocal.tm_min;
+ y.s = sLocal.tm_sec;
+ }
#else
{
struct tm *pTm;
- sqlite3OsEnterMutex();
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
pTm = localtime(&t);
y.Y = pTm->tm_year + 1900;
y.M = pTm->tm_mon + 1;
@@ -431,7 +482,7 @@ static double localtimeOffset(DateTime *p){
y.h = pTm->tm_hour;
y.m = pTm->tm_min;
y.s = pTm->tm_sec;
- sqlite3OsLeaveMutex();
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
#endif
y.validYMD = 1;
@@ -439,8 +490,9 @@ static double localtimeOffset(DateTime *p){
y.validJD = 0;
y.validTZ = 0;
computeJD(&y);
- return y.rJD - x.rJD;
+ return y.iJD - x.iJD;
}
+#endif /* SQLITE_OMIT_LOCALTIME */
/*
** Process a modifier to a date-time stamp. The modifiers are
@@ -474,6 +526,7 @@ static int parseModifier(const char *zMod, DateTime *p){
}
z[n] = 0;
switch( z[0] ){
+#ifndef SQLITE_OMIT_LOCALTIME
case 'l': {
/* localtime
**
@@ -482,32 +535,36 @@ static int parseModifier(const char *zMod, DateTime *p){
*/
if( strcmp(z, "localtime")==0 ){
computeJD(p);
- p->rJD += localtimeOffset(p);
+ p->iJD += localtimeOffset(p);
clearYMD_HMS_TZ(p);
rc = 0;
}
break;
}
+#endif
case 'u': {
/*
** unixepoch
**
- ** Treat the current value of p->rJD as the number of
+ ** Treat the current value of p->iJD as the number of
** seconds since 1970. Convert to a real julian day number.
*/
if( strcmp(z, "unixepoch")==0 && p->validJD ){
- p->rJD = p->rJD/86400.0 + 2440587.5;
+ p->iJD = p->iJD/86400.0 + 2440587.5*86400000.0;
clearYMD_HMS_TZ(p);
rc = 0;
- }else if( strcmp(z, "utc")==0 ){
+ }
+#ifndef SQLITE_OMIT_LOCALTIME
+ else if( strcmp(z, "utc")==0 ){
double c1;
computeJD(p);
c1 = localtimeOffset(p);
- p->rJD -= c1;
+ p->iJD -= c1;
clearYMD_HMS_TZ(p);
- p->rJD += c1 - localtimeOffset(p);
+ p->iJD += c1 - localtimeOffset(p);
rc = 0;
}
+#endif
break;
}
case 'w': {
@@ -520,15 +577,14 @@ static int parseModifier(const char *zMod, DateTime *p){
*/
if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
&& (n=r)==r && n>=0 && r<7 ){
- int Z;
+ sqlite3_int64 Z;
computeYMD_HMS(p);
p->validTZ = 0;
p->validJD = 0;
computeJD(p);
- Z = p->rJD + 1.5;
- Z %= 7;
+ Z = ((p->iJD + 129600000)/86400000) % 7;
if( Z>n ) Z -= 7;
- p->rJD += n - Z;
+ p->iJD += (n - Z)*86400000;
clearYMD_HMS_TZ(p);
rc = 0;
}
@@ -584,18 +640,18 @@ static int parseModifier(const char *zMod, DateTime *p){
*/
const char *z2 = z;
DateTime tx;
- int day;
+ sqlite3_int64 day;
if( !isdigit(*(u8*)z2) ) z2++;
memset(&tx, 0, sizeof(tx));
if( parseHhMmSs(z2, &tx) ) break;
computeJD(&tx);
- tx.rJD -= 0.5;
- day = (int)tx.rJD;
- tx.rJD -= day;
- if( z[0]=='-' ) tx.rJD = -tx.rJD;
+ tx.iJD -= 43200000;
+ day = tx.iJD/86400000;
+ tx.iJD -= day*86400000;
+ if( z[0]=='-' ) tx.iJD = -tx.iJD;
computeJD(p);
clearYMD_HMS_TZ(p);
- p->rJD += tx.rJD;
+ p->iJD += tx.iJD;
rc = 0;
break;
}
@@ -607,13 +663,13 @@ static int parseModifier(const char *zMod, DateTime *p){
computeJD(p);
rc = 0;
if( n==3 && strcmp(z,"day")==0 ){
- p->rJD += r;
+ p->iJD += r*86400000.0 + 0.5;
}else if( n==4 && strcmp(z,"hour")==0 ){
- p->rJD += r/24.0;
+ p->iJD += r*(86400000.0/24.0) + 0.5;
}else if( n==6 && strcmp(z,"minute")==0 ){
- p->rJD += r/(24.0*60.0);
+ p->iJD += r*(86400000.0/(24.0*60.0)) + 0.5;
}else if( n==6 && strcmp(z,"second")==0 ){
- p->rJD += r/(24.0*60.0*60.0);
+ p->iJD += r*(86400000.0/(24.0*60.0*60.0)) + 0.5;
}else if( n==5 && strcmp(z,"month")==0 ){
int x, y;
computeYMD_HMS(p);
@@ -625,7 +681,7 @@ static int parseModifier(const char *zMod, DateTime *p){
computeJD(p);
y = r;
if( y!=r ){
- p->rJD += (r - y)*30.0;
+ p->iJD += (r - y)*30.0*86400000.0 + 0.5;
}
}else if( n==4 && strcmp(z,"year")==0 ){
computeYMD_HMS(p);
@@ -650,13 +706,31 @@ static int parseModifier(const char *zMod, DateTime *p){
** argv[1] and following are modifiers. Parse them all and write
** the resulting time into the DateTime structure p. Return 0
** on success and 1 if there are any errors.
+**
+** If there are zero parameters (if even argv[0] is undefined)
+** then assume a default value of "now" for argv[0].
*/
-static int isDate(int argc, sqlite3_value **argv, DateTime *p){
+static int isDate(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv,
+ DateTime *p
+){
int i;
const unsigned char *z;
- if( argc==0 ) return 1;
- if( (z = sqlite3_value_text(argv[0]))==0 || parseDateOrTime((char*)z, p) ){
- return 1;
+ int eType;
+ memset(p, 0, sizeof(*p));
+ if( argc==0 ){
+ setDateTimeToCurrent(context, p);
+ }else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
+ || eType==SQLITE_INTEGER ){
+ p->iJD = sqlite3_value_double(argv[0])*86400000.0 + 0.5;
+ p->validJD = 1;
+ }else{
+ z = sqlite3_value_text(argv[0]);
+ if( !z || parseDateOrTime(context, (char*)z, p) ){
+ return 1;
+ }
}
for(i=1; i<argc; i++){
if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){
@@ -683,9 +757,9 @@ static void juliandayFunc(
sqlite3_value **argv
){
DateTime x;
- if( isDate(argc, argv, &x)==0 ){
+ if( isDate(context, argc, argv, &x)==0 ){
computeJD(&x);
- sqlite3_result_double(context, x.rJD);
+ sqlite3_result_double(context, x.iJD/86400000.0);
}
}
@@ -700,7 +774,7 @@ static void datetimeFunc(
sqlite3_value **argv
){
DateTime x;
- if( isDate(argc, argv, &x)==0 ){
+ if( isDate(context, argc, argv, &x)==0 ){
char zBuf[100];
computeYMD_HMS(&x);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
@@ -720,7 +794,7 @@ static void timeFunc(
sqlite3_value **argv
){
DateTime x;
- if( isDate(argc, argv, &x)==0 ){
+ if( isDate(context, argc, argv, &x)==0 ){
char zBuf[100];
computeHMS(&x);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s);
@@ -739,7 +813,7 @@ static void dateFunc(
sqlite3_value **argv
){
DateTime x;
- if( isDate(argc, argv, &x)==0 ){
+ if( isDate(context, argc, argv, &x)==0 ){
char zBuf[100];
computeYMD(&x);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
@@ -775,9 +849,11 @@ static void strftimeFunc(
u64 n;
int i, j;
char *z;
+ sqlite3 *db;
const char *zFmt = (const char*)sqlite3_value_text(argv[0]);
char zBuf[100];
- if( zFmt==0 || isDate(argc-1, argv+1, &x) ) return;
+ if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return;
+ db = sqlite3_context_db_handle(context);
for(i=0, n=1; zFmt[i]; i++, n++){
if( zFmt[i]=='%' ){
switch( zFmt[i+1] ){
@@ -813,12 +889,15 @@ static void strftimeFunc(
}
if( n<sizeof(zBuf) ){
z = zBuf;
- }else if( n>SQLITE_MAX_LENGTH ){
+ }else if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
sqlite3_result_error_toobig(context);
return;
}else{
- z = sqliteMalloc( n );
- if( z==0 ) return;
+ z = sqlite3DbMallocRaw(db, n);
+ if( z==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
}
computeJD(&x);
computeYMD_HMS(&x);
@@ -845,10 +924,10 @@ static void strftimeFunc(
y.M = 1;
y.D = 1;
computeJD(&y);
- nDay = x.rJD - y.rJD + 0.5;
+ nDay = (x.iJD - y.iJD)/86400000.0 + 0.5;
if( zFmt[i]=='W' ){
int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
- wd = ((int)(x.rJD+0.5)) % 7;
+ wd = ((x.iJD+43200000)/86400000) % 7;
sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7);
j += 2;
}else{
@@ -858,7 +937,7 @@ static void strftimeFunc(
break;
}
case 'J': {
- sqlite3_snprintf(20, &z[j],"%.16g",x.rJD);
+ sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0);
j+=strlen(&z[j]);
break;
}
@@ -866,22 +945,20 @@ static void strftimeFunc(
case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
case 's': {
sqlite3_snprintf(30,&z[j],"%d",
- (int)((x.rJD-2440587.5)*86400.0 + 0.5));
+ (int)(x.iJD/1000.0 - 210866760000.0));
j += strlen(&z[j]);
break;
}
case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break;
- case 'w': z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break;
+ case 'w': z[j++] = (((x.iJD+129600000)/86400000) % 7) + '0'; break;
case 'Y': sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=strlen(&z[j]);break;
- case '%': z[j++] = '%'; break;
+ default: z[j++] = '%'; break;
}
}
}
z[j] = 0;
- sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
- if( z!=zBuf ){
- sqliteFree(z);
- }
+ sqlite3_result_text(context, z, -1,
+ z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC);
}
/*
@@ -894,12 +971,7 @@ static void ctimeFunc(
int argc,
sqlite3_value **argv
){
- sqlite3_value *pVal = sqlite3ValueNew();
- if( pVal ){
- sqlite3ValueSetStr(pVal, -1, "now", SQLITE_UTF8, SQLITE_STATIC);
- timeFunc(context, 1, &pVal);
- sqlite3ValueFree(pVal);
- }
+ timeFunc(context, 0, 0);
}
/*
@@ -912,12 +984,7 @@ static void cdateFunc(
int argc,
sqlite3_value **argv
){
- sqlite3_value *pVal = sqlite3ValueNew();
- if( pVal ){
- sqlite3ValueSetStr(pVal, -1, "now", SQLITE_UTF8, SQLITE_STATIC);
- dateFunc(context, 1, &pVal);
- sqlite3ValueFree(pVal);
- }
+ dateFunc(context, 0, 0);
}
/*
@@ -930,12 +997,7 @@ static void ctimestampFunc(
int argc,
sqlite3_value **argv
){
- sqlite3_value *pVal = sqlite3ValueNew();
- if( pVal ){
- sqlite3ValueSetStr(pVal, -1, "now", SQLITE_UTF8, SQLITE_STATIC);
- datetimeFunc(context, 1, &pVal);
- sqlite3ValueFree(pVal);
- }
+ datetimeFunc(context, 0, 0);
}
#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */
@@ -958,18 +1020,13 @@ static void currentTimeFunc(
){
time_t t;
char *zFormat = (char *)sqlite3_user_data(context);
+ sqlite3 *db;
+ double rT;
char zBuf[20];
- time(&t);
-#ifdef SQLITE_TEST
- {
- extern int sqlite3_current_time; /* See os_XXX.c */
- if( sqlite3_current_time ){
- t = sqlite3_current_time;
- }
- }
-#endif
-
+ db = sqlite3_context_db_handle(context);
+ sqlite3OsCurrentTime(db->pVfs, &rT);
+ t = 86400.0*(rT - 2440587.5) + 0.5;
#ifdef HAVE_GMTIME_R
{
struct tm sNow;
@@ -979,10 +1036,10 @@ static void currentTimeFunc(
#else
{
struct tm *pTm;
- sqlite3OsEnterMutex();
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
pTm = gmtime(&t);
strftime(zBuf, 20, zFormat, pTm);
- sqlite3OsLeaveMutex();
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
#endif
diff --git a/third_party/sqlite/delete.c b/third_party/sqlite/src/delete.c
index b304526..2682424 100644..100755
--- a/third_party/sqlite/delete.c
+++ b/third_party/sqlite/src/delete.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
-** $Id: delete.c,v 1.129 2007/04/16 15:06:25 danielk1977 Exp $
+** $Id: delete.c,v 1.171 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
@@ -26,7 +26,7 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
int i;
struct SrcList_item *pItem;
for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
- pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase);
+ pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
sqlite3DeleteTable(pItem->pTab);
pItem->pTab = pTab;
if( pTab ){
@@ -75,13 +75,43 @@ void sqlite3OpenTable(
v = sqlite3GetVdbe(p);
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- VdbeComment((v, "# %s", pTab->zName));
- sqlite3VdbeAddOp(v, opcode, iCur, pTab->tnum);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
+ sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
+ VdbeComment((v, "%s", pTab->zName));
}
+#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
+/*
+** Evaluate a view and store its result in an ephemeral table. The
+** pWhere argument is an optional WHERE clause that restricts the
+** set of rows in the view that are to be added to the ephemeral table.
+*/
+void sqlite3MaterializeView(
+ Parse *pParse, /* Parsing context */
+ Select *pView, /* View definition */
+ Expr *pWhere, /* Optional WHERE clause to be added */
+ int iCur /* Cursor number for ephemerial table */
+){
+ SelectDest dest;
+ Select *pDup;
+ sqlite3 *db = pParse->db;
+
+ pDup = sqlite3SelectDup(db, pView);
+ if( pWhere ){
+ SrcList *pFrom;
+
+ pWhere = sqlite3ExprDup(db, pWhere);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, 0, pDup, 0, 0);
+ pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
+ }
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
+ sqlite3Select(pParse, pDup, &dest, 0, 0, 0);
+ sqlite3SelectDelete(db, pDup);
+}
+#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
+
+
/*
** Generate code for a DELETE FROM statement.
**
@@ -113,12 +143,17 @@ void sqlite3DeleteFrom(
int isView; /* True if attempting to delete from a view */
int triggers_exist = 0; /* True if any triggers exist */
#endif
+ int iBeginAfterTrigger; /* Address of after trigger program */
+ int iEndAfterTrigger; /* Exit of after trigger program */
+ int iBeginBeforeTrigger; /* Address of before trigger program */
+ int iEndBeforeTrigger; /* Exit of before trigger program */
+ u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
sContext.pParse = 0;
- if( pParse->nErr || sqlite3MallocFailed() ){
+ db = pParse->db;
+ if( pParse->nErr || db->mallocFailed ){
goto delete_from_cleanup;
}
- db = pParse->db;
assert( pTabList->nSrc==1 );
/* Locate the table which we want to delete. This table has to be
@@ -166,15 +201,12 @@ void sqlite3DeleteFrom(
oldIdx = pParse->nTab++;
}
- /* Resolve the column names in the WHERE clause.
+ /* Assign cursor number to the table and all its indices.
*/
assert( pTabList->nSrc==1 );
iCur = pTabList->a[0].iCursor = pParse->nTab++;
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
- sNC.pSrcList = pTabList;
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
- goto delete_from_cleanup;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ pParse->nTab++;
}
/* Start the view context
@@ -192,21 +224,46 @@ void sqlite3DeleteFrom(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
+ if( triggers_exist ){
+ int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
+ int iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ addr = sqlite3VdbeMakeLabel(v);
+
+ iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
+ (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
+ -1, oldIdx, orconf, addr, &old_col_mask, 0);
+ iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
+
+ iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
+ (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
+ oldIdx, orconf, addr, &old_col_mask, 0);
+ iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
+
+ sqlite3VdbeJumpHere(v, iGoto);
+ }
+
/* If we are trying to delete from a view, realize that view into
** a ephemeral table.
*/
if( isView ){
- Select *pView = sqlite3SelectDup(pTab->pSelect);
- sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
- sqlite3SelectDelete(pView);
+ sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, iCur);
+ }
+
+ /* Resolve the column names in the WHERE clause.
+ */
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ sNC.pSrcList = pTabList;
+ if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ goto delete_from_cleanup;
}
/* Initialize the counter of the number of rows deleted, if
** we are counting rows.
*/
if( db->flags & SQLITE_CountRows ){
- memCnt = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
+ memCnt = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
}
/* Special case: A DELETE without a WHERE clause deletes everything.
@@ -217,25 +274,23 @@ void sqlite3DeleteFrom(
if( db->flags & SQLITE_CountRows ){
/* If counting rows deleted, just count the total number of
** entries in the table. */
- int endOfLoop = sqlite3VdbeMakeLabel(v);
int addr2;
if( !isView ){
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
}
- sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
- addr2 = sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
- sqlite3VdbeAddOp(v, OP_Next, iCur, addr2);
- sqlite3VdbeResolveLabel(v, endOfLoop);
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
+ addr2 = sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr2);
+ sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
if( !isView ){
- sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
+ sqlite3VdbeAddOp2(v, OP_Clear, pTab->tnum, iDb);
if( !pParse->nested ){
- sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
+ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb);
+ sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}
}
@@ -243,17 +298,19 @@ void sqlite3DeleteFrom(
** the table and pick which records to delete.
*/
else{
+ int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
+
/* Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
if( pWInfo==0 ) goto delete_from_cleanup;
/* Remember the rowid of every item to be deleted.
*/
- sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0);
+ sqlite3VdbeAddOp2(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, iRowid);
+ sqlite3VdbeAddOp1(v, OP_FifoWrite, iRowid);
if( db->flags & SQLITE_CountRows ){
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
+ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
/* End the database scan loop.
@@ -263,8 +320,8 @@ void sqlite3DeleteFrom(
/* Open the pseudo-table used to store OLD if there are triggers.
*/
if( triggers_exist ){
- sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
+ sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx);
}
/* Delete every item whose key was written to the list during the
@@ -273,52 +330,53 @@ void sqlite3DeleteFrom(
*/
end = sqlite3VdbeMakeLabel(v);
- /* This is the beginning of the delete loop when there are
- ** row triggers.
+ if( !isView ){
+ /* Open cursors for the table we are deleting from and
+ ** all its indices.
+ */
+ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+ }
+
+ /* This is the beginning of the delete loop. If a trigger encounters
+ ** an IGNORE constraint, it jumps back to here.
*/
if( triggers_exist ){
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
-
- (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
- -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
- addr);
+ sqlite3VdbeResolveLabel(v, addr);
}
+ addr = sqlite3VdbeAddOp2(v, OP_FifoRead, iRowid, end);
- if( !isView ){
- /* Open cursors for the table we are deleting from and all its
- ** indices. If there are row triggers, this happens inside the
- ** OP_FifoRead loop because the cursor have to all be closed
- ** before the trigger fires. If there are no row triggers, the
- ** cursors are opened only once on the outside the loop.
+ if( triggers_exist ){
+ int iData = ++pParse->nMem; /* For storing row data of OLD table */
+
+ /* If the record is no longer present in the table, jump to the
+ ** next iteration of the loop through the contents of the fifo.
*/
- sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid);
- /* This is the beginning of the delete loop when there are no
- ** row triggers */
- if( !triggers_exist ){
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
+ /* Populate the OLD.* pseudo-table */
+ if( old_col_mask ){
+ sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iData);
}
+ sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid);
+ /* Jump back and run the BEFORE triggers */
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
+ sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
+ }
+
+ if( !isView ){
/* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
- pParse->pVirtualLock = pTab;
- sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB);
+ const char *pVtab = (const char *)pTab->pVtab;
+ sqlite3VtabMakeWritable(pParse, pTab);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVtab, P4_VTAB);
}else
#endif
{
- sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
+ sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, pParse->nested==0);
}
}
@@ -326,27 +384,21 @@ void sqlite3DeleteFrom(
** the AFTER triggers
*/
if( triggers_exist ){
- if( !isView ){
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
- }
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
- oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
- addr);
+ /* Jump back and run the AFTER triggers */
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
+ sqlite3VdbeJumpHere(v, iEndAfterTrigger);
}
/* End of the delete loop */
- sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
sqlite3VdbeResolveLabel(v, end);
/* Close the cursors after the loop if there are no row triggers */
- if( !triggers_exist && !IsVirtual(pTab) ){
+ if( !isView && !IsVirtual(pTab) ){
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
}
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
}
@@ -356,16 +408,15 @@ void sqlite3DeleteFrom(
** invoke the callback function.
*/
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
- sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P4_STATIC);
}
delete_from_cleanup:
sqlite3AuthContextPop(&sContext);
- sqlite3SrcListDelete(pTabList);
- sqlite3ExprDelete(pWhere);
+ sqlite3SrcListDelete(db, pTabList);
+ sqlite3ExprDelete(db, pWhere);
return;
}
@@ -382,26 +433,29 @@ delete_from_cleanup:
** 2. Read/write cursors for all indices of pTab must be open as
** cursor number base+i for the i-th index.
**
-** 3. The record number of the row to be deleted must be on the top
-** of the stack.
+** 3. The record number of the row to be deleted must be stored in
+** memory cell iRowid.
**
** This routine pops the top of the stack to remove the record number
** and then generates code to remove both the table record and all index
** entries that point to that record.
*/
void sqlite3GenerateRowDelete(
- sqlite3 *db, /* The database containing the index */
- Vdbe *v, /* Generate code into this VDBE */
+ Parse *pParse, /* Parsing context */
Table *pTab, /* Table containing the row to be deleted */
int iCur, /* Cursor number for the table */
+ int iRowid, /* Memory cell that contains the rowid to delete */
int count /* Increment the row change counter */
){
int addr;
- addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0);
- sqlite3GenerateRowIndexDelete(v, pTab, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
+ Vdbe *v;
+
+ v = pParse->pVdbe;
+ addr = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowid);
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
if( count ){
- sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
+ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
}
sqlite3VdbeJumpHere(v, addr);
}
@@ -423,45 +477,68 @@ void sqlite3GenerateRowDelete(
** deleted.
*/
void sqlite3GenerateRowIndexDelete(
- Vdbe *v, /* Generate code into this VDBE */
+ Parse *pParse, /* Parsing and code generating context */
Table *pTab, /* Table containing the row to be deleted */
int iCur, /* Cursor number for the table */
- char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
+ int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
){
int i;
Index *pIdx;
+ int r1;
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
- sqlite3GenerateIndexKey(v, pIdx, iCur);
- sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
+ if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue;
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0);
+ sqlite3VdbeAddOp3(pParse->pVdbe, OP_IdxDelete, iCur+i, r1,pIdx->nColumn+1);
}
}
/*
-** Generate code that will assemble an index key and put it on the top
-** of the tack. The key with be for index pIdx which is an index on pTab.
+** Generate code that will assemble an index key and put it in register
+** regOut. The key with be for index pIdx which is an index on pTab.
** iCur is the index of a cursor open on the pTab table and pointing to
** the entry that needs indexing.
+**
+** Return a register number which is the first in a block of
+** registers that holds the elements of the index key. The
+** block of registers has already been deallocated by the time
+** this routine returns.
*/
-void sqlite3GenerateIndexKey(
- Vdbe *v, /* Generate code into this VDBE */
+int sqlite3GenerateIndexKey(
+ Parse *pParse, /* Parsing context */
Index *pIdx, /* The index for which to generate a key */
- int iCur /* Cursor number for the pIdx->pTable table */
+ int iCur, /* Cursor number for the pIdx->pTable table */
+ int regOut, /* Write the new index key to this register */
+ int doMakeRec /* Run the OP_MakeRecord instruction if true */
){
+ Vdbe *v = pParse->pVdbe;
int j;
Table *pTab = pIdx->pTable;
+ int regBase;
+ int nCol;
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- for(j=0; j<pIdx->nColumn; j++){
+ nCol = pIdx->nColumn;
+ regBase = sqlite3GetTempRange(pParse, nCol+1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol);
+ for(j=0; j<nCol; j++){
int idx = pIdx->aiColumn[j];
if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp(v, OP_Dup, j, 0);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j);
}else{
- sqlite3VdbeAddOp(v, OP_Column, iCur, idx);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j);
sqlite3ColumnDefault(v, pTab, idx);
}
}
- sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0);
- sqlite3IndexAffinityStr(v, pIdx);
+ if( doMakeRec ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
+ sqlite3IndexAffinityStr(v, pIdx);
+ sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1);
+ }
+ sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
+ return regBase;
}
+
+/* Make sure "isView" gets undefined in case this file becomes part of
+** the amalgamation - so that subsequent files do not see isView as a
+** macro. */
+#undef isView
diff --git a/third_party/sqlite/expr.c b/third_party/sqlite/src/expr.c
index b640bec..e8d8a1e 100644..100755
--- a/third_party/sqlite/expr.c
+++ b/third_party/sqlite/src/expr.c
@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.303 2007/08/07 17:13:04 drh Exp $
+** $Id: expr.c,v 1.387 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -54,13 +54,18 @@ char sqlite3ExprAffinity(Expr *pExpr){
** collating sequences.
*/
Expr *sqlite3ExprSetColl(Parse *pParse, Expr *pExpr, Token *pName){
+ char *zColl = 0; /* Dequoted name of collation sequence */
CollSeq *pColl;
- if( pExpr==0 ) return 0;
- pColl = sqlite3LocateCollSeq(pParse, (char*)pName->z, pName->n);
- if( pColl ){
- pExpr->pColl = pColl;
- pExpr->flags |= EP_ExpCollate;
+ sqlite3 *db = pParse->db;
+ zColl = sqlite3NameFromToken(db, pName);
+ if( pExpr && zColl ){
+ pColl = sqlite3LocateCollSeq(pParse, zColl, -1);
+ if( pColl ){
+ pExpr->pColl = pColl;
+ pExpr->flags |= EP_ExpCollate;
+ }
}
+ sqlite3DbFree(db, zColl);
return pExpr;
}
@@ -154,15 +159,13 @@ int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
}
/*
-** Return the P1 value that should be used for a binary comparison
+** Return the P5 value that should be used for a binary comparison
** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2.
-** If jumpIfNull is true, then set the low byte of the returned
-** P1 value to tell the opcode to jump if either expression
-** evaluates to NULL.
*/
-static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
- char aff = sqlite3ExprAffinity(pExpr2);
- return ((int)sqlite3CompareAffinity(pExpr1, aff))+(jumpIfNull?0x100:0);
+static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
+ u8 aff = (char)sqlite3ExprAffinity(pExpr2);
+ aff = sqlite3CompareAffinity(pExpr1, aff) | jumpIfNull;
+ return aff;
}
/*
@@ -200,6 +203,30 @@ CollSeq *sqlite3BinaryCompareCollSeq(
}
/*
+** Generate the operands for a comparison operation. Before
+** generating the code for each operand, set the EP_AnyAff
+** flag on the expression so that it will be able to used a
+** cached column value that has previously undergone an
+** affinity change.
+*/
+static void codeCompareOperands(
+ Parse *pParse, /* Parsing and code generating context */
+ Expr *pLeft, /* The left operand */
+ int *pRegLeft, /* Register where left operand is stored */
+ int *pFreeLeft, /* Free this register when done */
+ Expr *pRight, /* The right operand */
+ int *pRegRight, /* Register where right operand is stored */
+ int *pFreeRight /* Write temp register for right operand there */
+){
+ while( pLeft->op==TK_UPLUS ) pLeft = pLeft->pLeft;
+ pLeft->flags |= EP_AnyAff;
+ *pRegLeft = sqlite3ExprCodeTemp(pParse, pLeft, pFreeLeft);
+ while( pRight->op==TK_UPLUS ) pRight = pRight->pLeft;
+ pRight->flags |= EP_AnyAff;
+ *pRegRight = sqlite3ExprCodeTemp(pParse, pRight, pFreeRight);
+}
+
+/*
** Generate code for a comparison operator.
*/
static int codeCompare(
@@ -207,41 +234,157 @@ static int codeCompare(
Expr *pLeft, /* The left operand */
Expr *pRight, /* The right operand */
int opcode, /* The comparison opcode */
+ int in1, int in2, /* Register holding operands */
int dest, /* Jump here if true. */
int jumpIfNull /* If true, jump if either operand is NULL */
){
- int p1 = binaryCompareP1(pLeft, pRight, jumpIfNull);
- CollSeq *p3 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
- return sqlite3VdbeOp3(pParse->pVdbe, opcode, p1, dest, (void*)p3, P3_COLLSEQ);
+ int p5;
+ int addr;
+ CollSeq *p4;
+
+ p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
+ p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
+ addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
+ (void*)p4, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(pParse->pVdbe, p5);
+ if( (p5 & SQLITE_AFF_MASK)!=SQLITE_AFF_NONE ){
+ sqlite3ExprCacheAffinityChange(pParse, in1, 1);
+ sqlite3ExprCacheAffinityChange(pParse, in2, 1);
+ }
+ return addr;
+}
+
+#if SQLITE_MAX_EXPR_DEPTH>0
+/*
+** Check that argument nHeight is less than or equal to the maximum
+** expression depth allowed. If it is not, leave an error message in
+** pParse.
+*/
+static int checkExprHeight(Parse *pParse, int nHeight){
+ int rc = SQLITE_OK;
+ int mxHeight = pParse->db->aLimit[SQLITE_LIMIT_EXPR_DEPTH];
+ if( nHeight>mxHeight ){
+ sqlite3ErrorMsg(pParse,
+ "Expression tree is too large (maximum depth %d)", mxHeight
+ );
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+
+/* The following three functions, heightOfExpr(), heightOfExprList()
+** and heightOfSelect(), are used to determine the maximum height
+** of any expression tree referenced by the structure passed as the
+** first argument.
+**
+** If this maximum height is greater than the current value pointed
+** to by pnHeight, the second parameter, then set *pnHeight to that
+** value.
+*/
+static void heightOfExpr(Expr *p, int *pnHeight){
+ if( p ){
+ if( p->nHeight>*pnHeight ){
+ *pnHeight = p->nHeight;
+ }
+ }
+}
+static void heightOfExprList(ExprList *p, int *pnHeight){
+ if( p ){
+ int i;
+ for(i=0; i<p->nExpr; i++){
+ heightOfExpr(p->a[i].pExpr, pnHeight);
+ }
+ }
+}
+static void heightOfSelect(Select *p, int *pnHeight){
+ if( p ){
+ heightOfExpr(p->pWhere, pnHeight);
+ heightOfExpr(p->pHaving, pnHeight);
+ heightOfExpr(p->pLimit, pnHeight);
+ heightOfExpr(p->pOffset, pnHeight);
+ heightOfExprList(p->pEList, pnHeight);
+ heightOfExprList(p->pGroupBy, pnHeight);
+ heightOfExprList(p->pOrderBy, pnHeight);
+ heightOfSelect(p->pPrior, pnHeight);
+ }
+}
+
+/*
+** Set the Expr.nHeight variable in the structure passed as an
+** argument. An expression with no children, Expr.pList or
+** Expr.pSelect member has a height of 1. Any other expression
+** has a height equal to the maximum height of any other
+** referenced Expr plus one.
+*/
+static void exprSetHeight(Expr *p){
+ int nHeight = 0;
+ heightOfExpr(p->pLeft, &nHeight);
+ heightOfExpr(p->pRight, &nHeight);
+ heightOfExprList(p->pList, &nHeight);
+ heightOfSelect(p->pSelect, &nHeight);
+ p->nHeight = nHeight + 1;
}
/*
+** Set the Expr.nHeight variable using the exprSetHeight() function. If
+** the height is greater than the maximum allowed expression depth,
+** leave an error in pParse.
+*/
+void sqlite3ExprSetHeight(Parse *pParse, Expr *p){
+ exprSetHeight(p);
+ checkExprHeight(pParse, p->nHeight);
+}
+
+/*
+** Return the maximum height of any expression tree referenced
+** by the select statement passed as an argument.
+*/
+int sqlite3SelectExprHeight(Select *p){
+ int nHeight = 0;
+ heightOfSelect(p, &nHeight);
+ return nHeight;
+}
+#else
+ #define checkExprHeight(x,y)
+ #define exprSetHeight(y)
+#endif /* SQLITE_MAX_EXPR_DEPTH>0 */
+
+/*
** Construct a new expression node and return a pointer to it. Memory
-** for this node is obtained from sqliteMalloc(). The calling function
+** for this node is obtained from sqlite3_malloc(). The calling function
** is responsible for making sure the node eventually gets freed.
*/
-Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, const Token *pToken){
+Expr *sqlite3Expr(
+ sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */
+ int op, /* Expression opcode */
+ Expr *pLeft, /* Left operand */
+ Expr *pRight, /* Right operand */
+ const Token *pToken /* Argument token */
+){
Expr *pNew;
- pNew = sqliteMalloc( sizeof(Expr) );
+ pNew = sqlite3DbMallocZero(db, sizeof(Expr));
if( pNew==0 ){
/* When malloc fails, delete pLeft and pRight. Expressions passed to
** this function must always be allocated with sqlite3Expr() for this
** reason.
*/
- sqlite3ExprDelete(pLeft);
- sqlite3ExprDelete(pRight);
+ sqlite3ExprDelete(db, pLeft);
+ sqlite3ExprDelete(db, pRight);
return 0;
}
pNew->op = op;
pNew->pLeft = pLeft;
pNew->pRight = pRight;
pNew->iAgg = -1;
+ pNew->span.z = (u8*)"";
if( pToken ){
assert( pToken->dyn==0 );
pNew->span = pNew->token = *pToken;
}else if( pLeft ){
if( pRight ){
- sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span);
+ if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){
+ sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span);
+ }
if( pRight->flags & EP_ExpCollate ){
pNew->flags |= EP_ExpCollate;
pNew->pColl = pRight->pColl;
@@ -253,28 +396,32 @@ Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, const Token *pToken){
}
}
- sqlite3ExprSetHeight(pNew);
+ exprSetHeight(pNew);
return pNew;
}
/*
-** Works like sqlite3Expr() but frees its pLeft and pRight arguments
-** if it fails due to a malloc problem.
+** Works like sqlite3Expr() except that it takes an extra Parse*
+** argument and notifies the associated connection object if malloc fails.
*/
-Expr *sqlite3ExprOrFree(int op, Expr *pLeft, Expr *pRight, const Token *pToken){
- Expr *pNew = sqlite3Expr(op, pLeft, pRight, pToken);
- if( pNew==0 ){
- sqlite3ExprDelete(pLeft);
- sqlite3ExprDelete(pRight);
+Expr *sqlite3PExpr(
+ Parse *pParse, /* Parsing context */
+ int op, /* Expression opcode */
+ Expr *pLeft, /* Left operand */
+ Expr *pRight, /* Right operand */
+ const Token *pToken /* Argument token */
+){
+ Expr *p = sqlite3Expr(pParse->db, op, pLeft, pRight, pToken);
+ if( p ){
+ checkExprHeight(pParse, p->nHeight);
}
- return pNew;
+ return p;
}
/*
** When doing a nested parse, you can include terms in an expression
-** that look like this: #0 #1 #2 ... These terms refer to elements
-** on the stack. "#0" means the top of the stack.
-** "#1" means the next down on the stack. And so forth.
+** that look like this: #1 #2 ... These terms refer to registers
+** in the virtual machine. #N is the N-th register.
**
** This routine is called by the parser to deal with on of those terms.
** It immediately generates code to store the value in a memory location.
@@ -284,20 +431,16 @@ Expr *sqlite3ExprOrFree(int op, Expr *pLeft, Expr *pRight, const Token *pToken){
Expr *sqlite3RegisterExpr(Parse *pParse, Token *pToken){
Vdbe *v = pParse->pVdbe;
Expr *p;
- int depth;
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", pToken);
- return sqlite3Expr(TK_NULL, 0, 0, 0);
+ return sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
}
if( v==0 ) return 0;
- p = sqlite3Expr(TK_REGISTER, 0, 0, pToken);
+ p = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, pToken);
if( p==0 ){
return 0; /* Malloc failed */
}
- depth = atoi((char*)&pToken->z[1]);
- p->iTable = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_Dup, depth, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, p->iTable, 1);
+ p->iTable = atoi((char*)&pToken->z[1]);
return p;
}
@@ -305,31 +448,27 @@ Expr *sqlite3RegisterExpr(Parse *pParse, Token *pToken){
** Join two expressions using an AND operator. If either expression is
** NULL, then just return the other expression.
*/
-Expr *sqlite3ExprAnd(Expr *pLeft, Expr *pRight){
+Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){
if( pLeft==0 ){
return pRight;
}else if( pRight==0 ){
return pLeft;
}else{
- return sqlite3Expr(TK_AND, pLeft, pRight, 0);
+ return sqlite3Expr(db, TK_AND, pLeft, pRight, 0);
}
}
/*
** Set the Expr.span field of the given expression to span all
-** text between the two given tokens.
+** text between the two given tokens. Both tokens must be pointing
+** at the same string.
*/
void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
assert( pRight!=0 );
assert( pLeft!=0 );
- if( !sqlite3MallocFailed() && pRight->z && pLeft->z ){
- assert( pLeft->dyn==0 || pLeft->z[pLeft->n]==0 );
- if( pLeft->dyn==0 && pRight->dyn==0 ){
- pExpr->span.z = pLeft->z;
- pExpr->span.n = pRight->n + (pRight->z - pLeft->z);
- }else{
- pExpr->span.z = 0;
- }
+ if( pExpr ){
+ pExpr->span.z = pLeft->z;
+ pExpr->span.n = pRight->n + (pRight->z - pLeft->z);
}
}
@@ -337,12 +476,13 @@ void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
** Construct a new expression node for a function with multiple
** arguments.
*/
-Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){
+Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
Expr *pNew;
+ sqlite3 *db = pParse->db;
assert( pToken );
- pNew = sqliteMalloc( sizeof(Expr) );
+ pNew = sqlite3DbMallocZero(db, sizeof(Expr) );
if( pNew==0 ){
- sqlite3ExprListDelete(pList); /* Avoid leaking memory when malloc fails */
+ sqlite3ExprListDelete(db, pList); /* Avoid leaking memory when malloc fails */
return 0;
}
pNew->op = TK_FUNCTION;
@@ -351,7 +491,7 @@ Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){
pNew->token = *pToken;
pNew->span = pNew->token;
- sqlite3ExprSetHeight(pNew);
+ sqlite3ExprSetHeight(pParse, pNew);
return pNew;
}
@@ -373,6 +513,8 @@ Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){
*/
void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
Token *pToken;
+ sqlite3 *db = pParse->db;
+
if( pExpr==0 ) return;
pToken = &pExpr->token;
assert( pToken->n>=1 );
@@ -386,9 +528,13 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
** use it as the variable number */
int i;
pExpr->iTable = i = atoi((char*)&pToken->z[1]);
- if( i<1 || i>SQLITE_MAX_VARIABLE_NUMBER ){
+ testcase( i==0 );
+ testcase( i==1 );
+ testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
+ testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
+ if( i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
- SQLITE_MAX_VARIABLE_NUMBER);
+ db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
}
if( i>pParse->nVar ){
pParse->nVar = i;
@@ -413,16 +559,20 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
pExpr->iTable = ++pParse->nVar;
if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){
pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10;
- pParse->apVarExpr = sqliteReallocOrFree(pParse->apVarExpr,
- pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) );
+ pParse->apVarExpr =
+ sqlite3DbReallocOrFree(
+ db,
+ pParse->apVarExpr,
+ pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0])
+ );
}
- if( !sqlite3MallocFailed() ){
+ if( !db->mallocFailed ){
assert( pParse->apVarExpr!=0 );
pParse->apVarExpr[pParse->nVarExpr++] = pExpr;
}
}
}
- if( !pParse->nErr && pParse->nVar>SQLITE_MAX_VARIABLE_NUMBER ){
+ if( !pParse->nErr && pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "too many SQL variables");
}
}
@@ -430,28 +580,28 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
/*
** Recursively delete an expression tree.
*/
-void sqlite3ExprDelete(Expr *p){
+void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p==0 ) return;
- if( p->span.dyn ) sqliteFree((char*)p->span.z);
- if( p->token.dyn ) sqliteFree((char*)p->token.z);
- sqlite3ExprDelete(p->pLeft);
- sqlite3ExprDelete(p->pRight);
- sqlite3ExprListDelete(p->pList);
- sqlite3SelectDelete(p->pSelect);
- sqliteFree(p);
+ if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
+ if( p->token.dyn ) sqlite3DbFree(db, (char*)p->token.z);
+ sqlite3ExprDelete(db, p->pLeft);
+ sqlite3ExprDelete(db, p->pRight);
+ sqlite3ExprListDelete(db, p->pList);
+ sqlite3SelectDelete(db, p->pSelect);
+ sqlite3DbFree(db, p);
}
/*
** The Expr.token field might be a string literal that is quoted.
** If so, remove the quotation marks.
*/
-void sqlite3DequoteExpr(Expr *p){
+void sqlite3DequoteExpr(sqlite3 *db, Expr *p){
if( ExprHasAnyProperty(p, EP_Dequoted) ){
return;
}
ExprSetProperty(p, EP_Dequoted);
if( p->token.dyn==0 ){
- sqlite3TokenCopy(&p->token, &p->token);
+ sqlite3TokenCopy(db, &p->token, &p->token);
}
sqlite3Dequote((char*)p->token.z);
}
@@ -469,62 +619,63 @@ void sqlite3DequoteExpr(Expr *p){
**
** Any tables that the SrcList might point to are not duplicated.
*/
-Expr *sqlite3ExprDup(Expr *p){
+Expr *sqlite3ExprDup(sqlite3 *db, Expr *p){
Expr *pNew;
if( p==0 ) return 0;
- pNew = sqliteMallocRaw( sizeof(*p) );
+ pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
memcpy(pNew, p, sizeof(*pNew));
if( p->token.z!=0 ){
- pNew->token.z = (u8*)sqliteStrNDup((char*)p->token.z, p->token.n);
+ pNew->token.z = (u8*)sqlite3DbStrNDup(db, (char*)p->token.z, p->token.n);
pNew->token.dyn = 1;
}else{
assert( pNew->token.z==0 );
}
pNew->span.z = 0;
- pNew->pLeft = sqlite3ExprDup(p->pLeft);
- pNew->pRight = sqlite3ExprDup(p->pRight);
- pNew->pList = sqlite3ExprListDup(p->pList);
- pNew->pSelect = sqlite3SelectDup(p->pSelect);
+ pNew->pLeft = sqlite3ExprDup(db, p->pLeft);
+ pNew->pRight = sqlite3ExprDup(db, p->pRight);
+ pNew->pList = sqlite3ExprListDup(db, p->pList);
+ pNew->pSelect = sqlite3SelectDup(db, p->pSelect);
return pNew;
}
-void sqlite3TokenCopy(Token *pTo, Token *pFrom){
- if( pTo->dyn ) sqliteFree((char*)pTo->z);
+void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){
+ if( pTo->dyn ) sqlite3DbFree(db, (char*)pTo->z);
if( pFrom->z ){
pTo->n = pFrom->n;
- pTo->z = (u8*)sqliteStrNDup((char*)pFrom->z, pFrom->n);
+ pTo->z = (u8*)sqlite3DbStrNDup(db, (char*)pFrom->z, pFrom->n);
pTo->dyn = 1;
}else{
pTo->z = 0;
}
}
-ExprList *sqlite3ExprListDup(ExprList *p){
+ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
ExprList *pNew;
struct ExprList_item *pItem, *pOldItem;
int i;
if( p==0 ) return 0;
- pNew = sqliteMalloc( sizeof(*pNew) );
+ pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
+ pNew->iECursor = 0;
pNew->nExpr = pNew->nAlloc = p->nExpr;
- pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) );
+ pNew->a = pItem = sqlite3DbMallocRaw(db, p->nExpr*sizeof(p->a[0]) );
if( pItem==0 ){
- sqliteFree(pNew);
+ sqlite3DbFree(db, pNew);
return 0;
}
pOldItem = p->a;
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
Expr *pNewExpr, *pOldExpr;
- pItem->pExpr = pNewExpr = sqlite3ExprDup(pOldExpr = pOldItem->pExpr);
+ pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr = pOldItem->pExpr);
if( pOldExpr->span.z!=0 && pNewExpr ){
/* Always make a copy of the span for top-level expressions in the
** expression list. The logic in SELECT processing that determines
** the names of columns in the result set needs this information */
- sqlite3TokenCopy(&pNewExpr->span, &pOldExpr->span);
+ sqlite3TokenCopy(db, &pNewExpr->span, &pOldExpr->span);
}
assert( pNewExpr==0 || pNewExpr->span.z!=0
|| pOldExpr->span.z==0
- || sqlite3MallocFailed() );
- pItem->zName = sqliteStrDup(pOldItem->zName);
+ || db->mallocFailed );
+ pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->sortOrder = pOldItem->sortOrder;
pItem->isAgg = pOldItem->isAgg;
pItem->done = 0;
@@ -540,22 +691,22 @@ ExprList *sqlite3ExprListDup(ExprList *p){
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|| !defined(SQLITE_OMIT_SUBQUERY)
-SrcList *sqlite3SrcListDup(SrcList *p){
+SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
SrcList *pNew;
int i;
int nByte;
if( p==0 ) return 0;
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
- pNew = sqliteMallocRaw( nByte );
+ pNew = sqlite3DbMallocRaw(db, nByte );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
struct SrcList_item *pNewItem = &pNew->a[i];
struct SrcList_item *pOldItem = &p->a[i];
Table *pTab;
- pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase);
- pNewItem->zName = sqliteStrDup(pOldItem->zName);
- pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias);
+ pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
+ pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
+ pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
pNewItem->jointype = pOldItem->jointype;
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->isPopulated = pOldItem->isPopulated;
@@ -563,51 +714,51 @@ SrcList *sqlite3SrcListDup(SrcList *p){
if( pTab ){
pTab->nRef++;
}
- pNewItem->pSelect = sqlite3SelectDup(pOldItem->pSelect);
- pNewItem->pOn = sqlite3ExprDup(pOldItem->pOn);
- pNewItem->pUsing = sqlite3IdListDup(pOldItem->pUsing);
+ pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect);
+ pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn);
+ pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
pNewItem->colUsed = pOldItem->colUsed;
}
return pNew;
}
-IdList *sqlite3IdListDup(IdList *p){
+IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
IdList *pNew;
int i;
if( p==0 ) return 0;
- pNew = sqliteMallocRaw( sizeof(*pNew) );
+ pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nId = pNew->nAlloc = p->nId;
- pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) );
+ pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){
- sqliteFree(pNew);
+ sqlite3DbFree(db, pNew);
return 0;
}
for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i];
- pNewItem->zName = sqliteStrDup(pOldItem->zName);
+ pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->idx = pOldItem->idx;
}
return pNew;
}
-Select *sqlite3SelectDup(Select *p){
+Select *sqlite3SelectDup(sqlite3 *db, Select *p){
Select *pNew;
if( p==0 ) return 0;
- pNew = sqliteMallocRaw( sizeof(*p) );
+ pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
pNew->isDistinct = p->isDistinct;
- pNew->pEList = sqlite3ExprListDup(p->pEList);
- pNew->pSrc = sqlite3SrcListDup(p->pSrc);
- pNew->pWhere = sqlite3ExprDup(p->pWhere);
- pNew->pGroupBy = sqlite3ExprListDup(p->pGroupBy);
- pNew->pHaving = sqlite3ExprDup(p->pHaving);
- pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy);
+ pNew->pEList = sqlite3ExprListDup(db, p->pEList);
+ pNew->pSrc = sqlite3SrcListDup(db, p->pSrc);
+ pNew->pWhere = sqlite3ExprDup(db, p->pWhere);
+ pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy);
+ pNew->pHaving = sqlite3ExprDup(db, p->pHaving);
+ pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy);
pNew->op = p->op;
- pNew->pPrior = sqlite3SelectDup(p->pPrior);
- pNew->pLimit = sqlite3ExprDup(p->pLimit);
- pNew->pOffset = sqlite3ExprDup(p->pOffset);
- pNew->iLimit = -1;
- pNew->iOffset = -1;
+ pNew->pPrior = sqlite3SelectDup(db, p->pPrior);
+ pNew->pLimit = sqlite3ExprDup(db, p->pLimit);
+ pNew->pOffset = sqlite3ExprDup(db, p->pOffset);
+ pNew->iLimit = 0;
+ pNew->iOffset = 0;
pNew->isResolved = p->isResolved;
pNew->isAgg = p->isAgg;
pNew->usesEphm = 0;
@@ -619,7 +770,7 @@ Select *sqlite3SelectDup(Select *p){
return pNew;
}
#else
-Select *sqlite3SelectDup(Select *p){
+Select *sqlite3SelectDup(sqlite3 *db, Select *p){
assert( p==0 );
return 0;
}
@@ -630,9 +781,15 @@ Select *sqlite3SelectDup(Select *p){
** Add a new element to the end of an expression list. If pList is
** initially NULL, then create a new expression list.
*/
-ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
+ExprList *sqlite3ExprListAppend(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* List to which to append. Might be NULL */
+ Expr *pExpr, /* Expression to be appended */
+ Token *pName /* AS keyword for the expression */
+){
+ sqlite3 *db = pParse->db;
if( pList==0 ){
- pList = sqliteMalloc( sizeof(ExprList) );
+ pList = sqlite3DbMallocZero(db, sizeof(ExprList) );
if( pList==0 ){
goto no_mem;
}
@@ -641,7 +798,7 @@ ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
if( pList->nAlloc<=pList->nExpr ){
struct ExprList_item *a;
int n = pList->nAlloc*2 + 4;
- a = sqliteRealloc(pList->a, n*sizeof(pList->a[0]));
+ a = sqlite3DbRealloc(db, pList->a, n*sizeof(pList->a[0]));
if( a==0 ){
goto no_mem;
}
@@ -652,15 +809,15 @@ ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
if( pExpr || pName ){
struct ExprList_item *pItem = &pList->a[pList->nExpr++];
memset(pItem, 0, sizeof(*pItem));
- pItem->zName = sqlite3NameFromToken(pName);
+ pItem->zName = sqlite3NameFromToken(db, pName);
pItem->pExpr = pExpr;
}
return pList;
no_mem:
/* Avoid leaking memory if malloc has failed. */
- sqlite3ExprDelete(pExpr);
- sqlite3ExprListDelete(pList);
+ sqlite3ExprDelete(db, pExpr);
+ sqlite3ExprListDelete(db, pList);
return 0;
}
@@ -671,99 +828,36 @@ no_mem:
void sqlite3ExprListCheckLength(
Parse *pParse,
ExprList *pEList,
- int iLimit,
const char *zObject
){
- if( pEList && pEList->nExpr>iLimit ){
+ int mx = pParse->db->aLimit[SQLITE_LIMIT_COLUMN];
+ testcase( pEList && pEList->nExpr==mx );
+ testcase( pEList && pEList->nExpr==mx+1 );
+ if( pEList && pEList->nExpr>mx ){
sqlite3ErrorMsg(pParse, "too many columns in %s", zObject);
}
}
-
-#if SQLITE_MAX_EXPR_DEPTH>0
-/* The following three functions, heightOfExpr(), heightOfExprList()
-** and heightOfSelect(), are used to determine the maximum height
-** of any expression tree referenced by the structure passed as the
-** first argument.
-**
-** If this maximum height is greater than the current value pointed
-** to by pnHeight, the second parameter, then set *pnHeight to that
-** value.
-*/
-static void heightOfExpr(Expr *p, int *pnHeight){
- if( p ){
- if( p->nHeight>*pnHeight ){
- *pnHeight = p->nHeight;
- }
- }
-}
-static void heightOfExprList(ExprList *p, int *pnHeight){
- if( p ){
- int i;
- for(i=0; i<p->nExpr; i++){
- heightOfExpr(p->a[i].pExpr, pnHeight);
- }
- }
-}
-static void heightOfSelect(Select *p, int *pnHeight){
- if( p ){
- heightOfExpr(p->pWhere, pnHeight);
- heightOfExpr(p->pHaving, pnHeight);
- heightOfExpr(p->pLimit, pnHeight);
- heightOfExpr(p->pOffset, pnHeight);
- heightOfExprList(p->pEList, pnHeight);
- heightOfExprList(p->pGroupBy, pnHeight);
- heightOfExprList(p->pOrderBy, pnHeight);
- heightOfSelect(p->pPrior, pnHeight);
- }
-}
-
-/*
-** Set the Expr.nHeight variable in the structure passed as an
-** argument. An expression with no children, Expr.pList or
-** Expr.pSelect member has a height of 1. Any other expression
-** has a height equal to the maximum height of any other
-** referenced Expr plus one.
-*/
-void sqlite3ExprSetHeight(Expr *p){
- int nHeight = 0;
- heightOfExpr(p->pLeft, &nHeight);
- heightOfExpr(p->pRight, &nHeight);
- heightOfExprList(p->pList, &nHeight);
- heightOfSelect(p->pSelect, &nHeight);
- p->nHeight = nHeight + 1;
-}
-
-/*
-** Return the maximum height of any expression tree referenced
-** by the select statement passed as an argument.
-*/
-int sqlite3SelectExprHeight(Select *p){
- int nHeight = 0;
- heightOfSelect(p, &nHeight);
- return nHeight;
-}
-#endif
-
/*
** Delete an entire expression list.
*/
-void sqlite3ExprListDelete(ExprList *pList){
+void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
int i;
struct ExprList_item *pItem;
if( pList==0 ) return;
assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
assert( pList->nExpr<=pList->nAlloc );
for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
- sqlite3ExprDelete(pItem->pExpr);
- sqliteFree(pItem->zName);
+ sqlite3ExprDelete(db, pItem->pExpr);
+ sqlite3DbFree(db, pItem->zName);
}
- sqliteFree(pList->a);
- sqliteFree(pList);
+ sqlite3DbFree(db, pList->a);
+ sqlite3DbFree(db, pList);
}
/*
-** Walk an expression tree. Call xFunc for each node visited.
+** Walk an expression tree. Call xFunc for each node visited. xFunc
+** is called on the node before xFunc is called on the nodes children.
**
** The return value from xFunc determines whether the tree walk continues.
** 0 means continue walking the tree. 1 means do not walk children
@@ -857,7 +951,14 @@ static int exprNodeIsConstant(void *pArg, Expr *pExpr){
#ifndef SQLITE_OMIT_SUBQUERY
case TK_SELECT:
case TK_EXISTS:
+ testcase( pExpr->op==TK_SELECT );
+ testcase( pExpr->op==TK_EXISTS );
#endif
+ testcase( pExpr->op==TK_ID );
+ testcase( pExpr->op==TK_COLUMN );
+ testcase( pExpr->op==TK_DOT );
+ testcase( pExpr->op==TK_AGG_FUNCTION );
+ testcase( pExpr->op==TK_AGG_COLUMN );
*pN = 0;
return 2;
case TK_IN:
@@ -918,27 +1019,36 @@ int sqlite3ExprIsConstantOrFunction(Expr *p){
** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged.
*/
int sqlite3ExprIsInteger(Expr *p, int *pValue){
+ int rc = 0;
+ if( p->flags & EP_IntValue ){
+ *pValue = p->iTable;
+ return 1;
+ }
switch( p->op ){
case TK_INTEGER: {
- if( sqlite3GetInt32((char*)p->token.z, pValue) ){
- return 1;
- }
+ rc = sqlite3GetInt32((char*)p->token.z, pValue);
break;
}
case TK_UPLUS: {
- return sqlite3ExprIsInteger(p->pLeft, pValue);
+ rc = sqlite3ExprIsInteger(p->pLeft, pValue);
+ break;
}
case TK_UMINUS: {
int v;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
*pValue = -v;
- return 1;
+ rc = 1;
}
break;
}
default: break;
}
- return 0;
+ if( rc ){
+ p->op = TK_INTEGER;
+ p->flags |= EP_IntValue;
+ p->iTable = *pValue;
+ }
+ return rc;
}
/*
@@ -994,12 +1104,13 @@ static int lookupName(
struct SrcList_item *pItem; /* Use for looping over pSrcList items */
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
+ Schema *pSchema = 0; /* Schema of the expression */
assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
- zDb = sqlite3NameFromToken(pDbToken);
- zTab = sqlite3NameFromToken(pTableToken);
- zCol = sqlite3NameFromToken(pColumnToken);
- if( sqlite3MallocFailed() ){
+ zDb = sqlite3NameFromToken(db, pDbToken);
+ zTab = sqlite3NameFromToken(db, pTableToken);
+ zCol = sqlite3NameFromToken(db, pColumnToken);
+ if( db->mallocFailed ){
goto lookupname_end;
}
@@ -1032,7 +1143,7 @@ static int lookupName(
}
if( 0==(cntTab++) ){
pExpr->iTable = pItem->iCursor;
- pExpr->pSchema = pTab->pSchema;
+ pSchema = pTab->pSchema;
pMatch = pItem;
}
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
@@ -1042,7 +1153,7 @@ static int lookupName(
cnt++;
pExpr->iTable = pItem->iCursor;
pMatch = pItem;
- pExpr->pSchema = pTab->pSchema;
+ pSchema = pTab->pSchema;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
pExpr->affinity = pTab->aCol[j].affinity;
@@ -1082,21 +1193,24 @@ static int lookupName(
if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
TriggerStack *pTriggerStack = pParse->trigStack;
Table *pTab = 0;
+ u32 *piColMask;
if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
pExpr->iTable = pTriggerStack->newIdx;
assert( pTriggerStack->pTab );
pTab = pTriggerStack->pTab;
+ piColMask = &(pTriggerStack->newColMask);
}else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){
pExpr->iTable = pTriggerStack->oldIdx;
assert( pTriggerStack->pTab );
pTab = pTriggerStack->pTab;
+ piColMask = &(pTriggerStack->oldColMask);
}
if( pTab ){
int iCol;
Column *pCol = pTab->aCol;
- pExpr->pSchema = pTab->pSchema;
+ pSchema = pTab->pSchema;
cntTab++;
for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) {
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
@@ -1108,6 +1222,11 @@ static int lookupName(
pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
}
pExpr->pTab = pTab;
+ if( iCol>=0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ *piColMask |= ((u32)1<<iCol) | (iCol>=32?0xffffffff:0);
+ }
break;
}
}
@@ -1147,18 +1266,18 @@ static int lookupName(
pOrig = pEList->a[j].pExpr;
if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
- sqliteFree(zCol);
+ sqlite3DbFree(db, zCol);
return 2;
}
- pDup = sqlite3ExprDup(pOrig);
+ pDup = sqlite3ExprDup(db, pOrig);
if( pExpr->flags & EP_ExpCollate ){
pDup->pColl = pExpr->pColl;
pDup->flags |= EP_ExpCollate;
}
- if( pExpr->span.dyn ) sqliteFree((char*)pExpr->span.z);
- if( pExpr->token.dyn ) sqliteFree((char*)pExpr->token.z);
+ if( pExpr->span.dyn ) sqlite3DbFree(db, (char*)pExpr->span.z);
+ if( pExpr->token.dyn ) sqlite3DbFree(db, (char*)pExpr->token.z);
memcpy(pExpr, pDup, sizeof(*pExpr));
- sqliteFree(pDup);
+ sqlite3DbFree(db, pDup);
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
@@ -1186,7 +1305,7 @@ static int lookupName(
** fields are not changed in any context.
*/
if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
- sqliteFree(zCol);
+ sqlite3DbFree(db, zCol);
return 0;
}
@@ -1195,18 +1314,15 @@ static int lookupName(
** more matches. Either way, we have an error.
*/
if( cnt!=1 ){
- char *z = 0;
- char *zErr;
- zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s";
+ const char *zErr;
+ zErr = cnt==0 ? "no such column" : "ambiguous column name";
if( zDb ){
- sqlite3SetString(&z, zDb, ".", zTab, ".", zCol, (char*)0);
+ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
}else if( zTab ){
- sqlite3SetString(&z, zTab, ".", zCol, (char*)0);
+ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
}else{
- z = sqliteStrDup(zCol);
+ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
- sqlite3ErrorMsg(pParse, zErr, z);
- sqliteFree(z);
pTopNC->nErr++;
}
@@ -1218,6 +1334,7 @@ static int lookupName(
*/
if( pExpr->iColumn>=0 && pMatch!=0 ){
int n = pExpr->iColumn;
+ testcase( n==sizeof(Bitmask)*8-1 );
if( n>=sizeof(Bitmask)*8 ){
n = sizeof(Bitmask)*8-1;
}
@@ -1228,18 +1345,18 @@ static int lookupName(
lookupname_end:
/* Clean up and return
*/
- sqliteFree(zDb);
- sqliteFree(zTab);
- sqlite3ExprDelete(pExpr->pLeft);
+ sqlite3DbFree(db, zDb);
+ sqlite3DbFree(db, zTab);
+ sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
- sqlite3ExprDelete(pExpr->pRight);
+ sqlite3ExprDelete(db, pExpr->pRight);
pExpr->pRight = 0;
pExpr->op = TK_COLUMN;
lookupname_end_2:
- sqliteFree(zCol);
+ sqlite3DbFree(db, zCol);
if( cnt==1 ){
assert( pNC!=0 );
- sqlite3AuthRead(pParse, pExpr, pNC->pSrcList);
+ sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
if( pMatch && !pMatch->pSelect ){
pExpr->pTab = pMatch->pTab;
}
@@ -1455,16 +1572,15 @@ int sqlite3ExprResolveNames(
Expr *pExpr /* The expression to be analyzed. */
){
int savedHasAgg;
+
if( pExpr==0 ) return 0;
#if SQLITE_MAX_EXPR_DEPTH>0
- if( (pExpr->nHeight+pNC->pParse->nHeight)>SQLITE_MAX_EXPR_DEPTH ){
- sqlite3ErrorMsg(pNC->pParse,
- "Expression tree is too large (maximum depth %d)",
- SQLITE_MAX_EXPR_DEPTH
- );
- return 1;
+ {
+ if( checkExprHeight(pNC->pParse, pExpr->nHeight + pNC->pParse->nHeight) ){
+ return 1;
+ }
+ pNC->pParse->nHeight += pExpr->nHeight;
}
- pNC->pParse->nHeight += pExpr->nHeight;
#endif
savedHasAgg = pNC->hasAgg;
pNC->hasAgg = 0;
@@ -1493,6 +1609,207 @@ struct QueryCoder {
NameContext *pNC; /* Namespace of first enclosing query */
};
+#ifdef SQLITE_TEST
+ int sqlite3_enable_in_opt = 1;
+#else
+ #define sqlite3_enable_in_opt 1
+#endif
+
+/*
+** Return true if the IN operator optimization is enabled and
+** the SELECT statement p exists and is of the
+** simple form:
+**
+** SELECT <column> FROM <table>
+**
+** If this is the case, it may be possible to use an existing table
+** or index instead of generating an epheremal table.
+*/
+#ifndef SQLITE_OMIT_SUBQUERY
+static int isCandidateForInOpt(Select *p){
+ SrcList *pSrc;
+ ExprList *pEList;
+ Table *pTab;
+ if( !sqlite3_enable_in_opt ) return 0; /* IN optimization must be enabled */
+ if( p==0 ) return 0; /* right-hand side of IN is SELECT */
+ if( p->pPrior ) return 0; /* Not a compound SELECT */
+ if( p->isDistinct ) return 0; /* No DISTINCT keyword */
+ if( p->isAgg ) return 0; /* Contains no aggregate functions */
+ if( p->pGroupBy ) return 0; /* Has no GROUP BY clause */
+ if( p->pLimit ) return 0; /* Has no LIMIT clause */
+ if( p->pOffset ) return 0;
+ if( p->pWhere ) return 0; /* Has no WHERE clause */
+ pSrc = p->pSrc;
+ if( pSrc==0 ) return 0; /* A single table in the FROM clause */
+ if( pSrc->nSrc!=1 ) return 0;
+ if( pSrc->a[0].pSelect ) return 0; /* FROM clause is not a subquery */
+ pTab = pSrc->a[0].pTab;
+ if( pTab==0 ) return 0;
+ if( pTab->pSelect ) return 0; /* FROM clause is not a view */
+ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
+ pEList = p->pEList;
+ if( pEList->nExpr!=1 ) return 0; /* One column in the result set */
+ if( pEList->a[0].pExpr->op!=TK_COLUMN ) return 0; /* Result is a column */
+ return 1;
+}
+#endif /* SQLITE_OMIT_SUBQUERY */
+
+/*
+** This function is used by the implementation of the IN (...) operator.
+** It's job is to find or create a b-tree structure that may be used
+** either to test for membership of the (...) set or to iterate through
+** its members, skipping duplicates.
+**
+** The cursor opened on the structure (database table, database index
+** or ephermal table) is stored in pX->iTable before this function returns.
+** The returned value indicates the structure type, as follows:
+**
+** IN_INDEX_ROWID - The cursor was opened on a database table.
+** IN_INDEX_INDEX - The cursor was opened on a database index.
+** IN_INDEX_EPH - The cursor was opened on a specially created and
+** populated epheremal table.
+**
+** An existing structure may only be used if the SELECT is of the simple
+** form:
+**
+** SELECT <column> FROM <table>
+**
+** If prNotFound parameter is 0, then the structure will be used to iterate
+** through the set members, skipping any duplicates. In this case an
+** epheremal table must be used unless the selected <column> is guaranteed
+** to be unique - either because it is an INTEGER PRIMARY KEY or it
+** is unique by virtue of a constraint or implicit index.
+**
+** If the prNotFound parameter is not 0, then the structure will be used
+** for fast set membership tests. In this case an epheremal table must
+** be used unless <column> is an INTEGER PRIMARY KEY or an index can
+** be found with <column> as its left-most column.
+**
+** When the structure is being used for set membership tests, the user
+** needs to know whether or not the structure contains an SQL NULL
+** value in order to correctly evaluate expressions like "X IN (Y, Z)".
+** If there is a chance that the structure may contain a NULL value at
+** runtime, then a register is allocated and the register number written
+** to *prNotFound. If there is no chance that the structure contains a
+** NULL value, then *prNotFound is left unchanged.
+**
+** If a register is allocated and its location stored in *prNotFound, then
+** its initial value is NULL. If the structure does not remain constant
+** for the duration of the query (i.e. the set is a correlated sub-select),
+** the value of the allocated register is reset to NULL each time the
+** structure is repopulated. This allows the caller to use vdbe code
+** equivalent to the following:
+**
+** if( register==NULL ){
+** has_null = <test if data structure contains null>
+** register = 1
+** }
+**
+** in order to avoid running the <test if data structure contains null>
+** test more often than is necessary.
+*/
+#ifndef SQLITE_OMIT_SUBQUERY
+int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
+ Select *p;
+ int eType = 0;
+ int iTab = pParse->nTab++;
+ int mustBeUnique = !prNotFound;
+
+ /* The follwing if(...) expression is true if the SELECT is of the
+ ** simple form:
+ **
+ ** SELECT <column> FROM <table>
+ **
+ ** If this is the case, it may be possible to use an existing table
+ ** or index instead of generating an epheremal table.
+ */
+ p = pX->pSelect;
+ if( isCandidateForInOpt(p) ){
+ sqlite3 *db = pParse->db;
+ Index *pIdx;
+ Expr *pExpr = p->pEList->a[0].pExpr;
+ int iCol = pExpr->iColumn;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+
+ /* This function is only called from two places. In both cases the vdbe
+ ** has already been allocated. So assume sqlite3GetVdbe() is always
+ ** successful here.
+ */
+ assert(v);
+ if( iCol<0 ){
+ int iMem = ++pParse->nMem;
+ int iAddr;
+ Table *pTab = p->pSrc->a[0].pTab;
+ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ sqlite3VdbeUsesBtree(v, iDb);
+
+ iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
+
+ sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
+ eType = IN_INDEX_ROWID;
+
+ sqlite3VdbeJumpHere(v, iAddr);
+ }else{
+ /* The collation sequence used by the comparison. If an index is to
+ ** be used in place of a temp-table, it must be ordered according
+ ** to this collation sequence.
+ */
+ CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr);
+
+ /* Check that the affinity that will be used to perform the
+ ** comparison is the same as the affinity of the column. If
+ ** it is not, it is not possible to use any index.
+ */
+ Table *pTab = p->pSrc->a[0].pTab;
+ char aff = comparisonAffinity(pX);
+ int affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE);
+
+ for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
+ if( (pIdx->aiColumn[0]==iCol)
+ && (pReq==sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], -1, 0))
+ && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
+ ){
+ int iDb;
+ int iMem = ++pParse->nMem;
+ int iAddr;
+ char *pKey;
+
+ pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
+ iDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
+ sqlite3VdbeUsesBtree(v, iDb);
+
+ iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
+
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn);
+ sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
+ pKey,P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pIdx->zName));
+ eType = IN_INDEX_INDEX;
+
+ sqlite3VdbeJumpHere(v, iAddr);
+ if( prNotFound && !pTab->aCol[iCol].notNull ){
+ *prNotFound = ++pParse->nMem;
+ }
+ }
+ }
+ }
+ }
+
+ if( eType==0 ){
+ int rMayHaveNull = 0;
+ if( prNotFound ){
+ *prNotFound = rMayHaveNull = ++pParse->nMem;
+ }
+ sqlite3CodeSubselect(pParse, pX, rMayHaveNull);
+ eType = IN_INDEX_EPH;
+ }else{
+ pX->iTable = iTab;
+ }
+ return eType;
+}
+#endif
/*
** Generate code for scalar subqueries used as an expression
@@ -1507,7 +1824,7 @@ struct QueryCoder {
** operator or subquery.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
+void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr, int rMayHaveNull){
int testAddr = 0; /* One-time test address */
Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
@@ -1524,11 +1841,10 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** save the results, and reuse the same result on subsequent invocations.
*/
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){
- int mem = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemLoad, mem, 0);
- testAddr = sqlite3VdbeAddOp(v, OP_If, 0, 0);
- assert( testAddr>0 || sqlite3MallocFailed() );
- sqlite3VdbeAddOp(v, OP_MemInt, 1, mem);
+ int mem = ++pParse->nMem;
+ sqlite3VdbeAddOp1(v, OP_If, mem);
+ testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
+ assert( testAddr>0 || pParse->db->mallocFailed );
}
switch( pExpr->op ){
@@ -1537,6 +1853,10 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
KeyInfo keyInfo;
int addr; /* Address of OP_OpenEphemeral instruction */
+ if( rMayHaveNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
+ }
+
affinity = sqlite3ExprAffinity(pExpr->pLeft);
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
@@ -1553,10 +1873,9 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** is used.
*/
pExpr->iTable = pParse->nTab++;
- addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, pExpr->iTable, 0);
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, 1);
memset(&keyInfo, 0, sizeof(keyInfo));
keyInfo.nField = 1;
- sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1);
if( pExpr->pSelect ){
/* Case 1: expr IN (SELECT ...)
@@ -1564,10 +1883,13 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** Generate code to write the results of the select into the temporary
** table allocated and opened above.
*/
- int iParm = pExpr->iTable + (((int)affinity)<<16);
+ SelectDest dest;
ExprList *pEList;
+
+ sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
+ dest.affinity = (int)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
- if( sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0) ){
+ if( sqlite3Select(pParse, pExpr->pSelect, &dest, 0, 0, 0) ){
return;
}
pEList = pExpr->pSelect->pEList;
@@ -1586,6 +1908,7 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
int i;
ExprList *pList = pExpr->pList;
struct ExprList_item *pItem;
+ int r1, r2, r3;
if( !affinity ){
affinity = SQLITE_AFF_NONE;
@@ -1593,6 +1916,8 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
keyInfo.aColl[0] = pExpr->pLeft->pColl;
/* Loop through each expression in <exprlist>. */
+ r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3GetTempReg(pParse);
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
Expr *pE2 = pItem->pExpr;
@@ -1601,18 +1926,24 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( testAddr>0 && !sqlite3ExprIsConstant(pE2) ){
- sqlite3VdbeChangeToNoop(v, testAddr-1, 3);
+ if( testAddr && !sqlite3ExprIsConstant(pE2) ){
+ sqlite3VdbeChangeToNoop(v, testAddr-1, 2);
testAddr = 0;
}
/* Evaluate the expression and insert it into the temp table */
- sqlite3ExprCode(pParse, pE2);
- sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1);
- sqlite3VdbeAddOp(v, OP_IdxInsert, pExpr->iTable, 0);
+ pParse->disableColCache++;
+ r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
+ sqlite3ExprCacheAffinityChange(pParse, r3, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
}
+ sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempReg(pParse, r2);
}
- sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO);
+ sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO);
break;
}
@@ -1624,31 +1955,31 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
*/
static const Token one = { (u8*)"1", 0, 1 };
Select *pSel;
- int iMem;
- int sop;
+ SelectDest dest;
- pExpr->iColumn = iMem = pParse->nMem++;
pSel = pExpr->pSelect;
+ sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
if( pExpr->op==TK_SELECT ){
- sop = SRT_Mem;
- sqlite3VdbeAddOp(v, OP_MemNull, iMem, 0);
- VdbeComment((v, "# Init subquery result"));
+ dest.eDest = SRT_Mem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iParm);
+ VdbeComment((v, "Init subquery result"));
}else{
- sop = SRT_Exists;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, iMem);
- VdbeComment((v, "# Init EXISTS result"));
+ dest.eDest = SRT_Exists;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm);
+ VdbeComment((v, "Init EXISTS result"));
}
- sqlite3ExprDelete(pSel->pLimit);
- pSel->pLimit = sqlite3Expr(TK_INTEGER, 0, 0, &one);
- if( sqlite3Select(pParse, pSel, sop, iMem, 0, 0, 0, 0) ){
+ sqlite3ExprDelete(pParse->db, pSel->pLimit);
+ pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &one);
+ if( sqlite3Select(pParse, pSel, &dest, 0, 0, 0) ){
return;
}
+ pExpr->iColumn = dest.iParm;
break;
}
}
if( testAddr ){
- sqlite3VdbeJumpHere(v, testAddr);
+ sqlite3VdbeJumpHere(v, testAddr-1);
}
return;
@@ -1656,19 +1987,72 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
#endif /* SQLITE_OMIT_SUBQUERY */
/*
-** Generate an instruction that will put the integer describe by
-** text z[0..n-1] on the stack.
+** Duplicate an 8-byte value
+*/
+static char *dup8bytes(Vdbe *v, const char *in){
+ char *out = sqlite3DbMallocRaw(sqlite3VdbeDb(v), 8);
+ if( out ){
+ memcpy(out, in, 8);
+ }
+ return out;
+}
+
+/*
+** Generate an instruction that will put the floating point
+** value described by z[0..n-1] into register iMem.
+**
+** The z[] string will probably not be zero-terminated. But the
+** z[n] character is guaranteed to be something that does not look
+** like the continuation of the number.
*/
-static void codeInteger(Vdbe *v, const char *z, int n){
- assert( z || sqlite3MallocFailed() );
+static void codeReal(Vdbe *v, const char *z, int n, int negateFlag, int iMem){
+ assert( z || v==0 || sqlite3VdbeDb(v)->mallocFailed );
if( z ){
+ double value;
+ char *zV;
+ assert( !isdigit(z[n]) );
+ sqlite3AtoF(z, &value);
+ if( sqlite3IsNaN(value) ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iMem);
+ }else{
+ if( negateFlag ) value = -value;
+ zV = dup8bytes(v, (char*)&value);
+ sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL);
+ }
+ }
+}
+
+
+/*
+** Generate an instruction that will put the integer describe by
+** text z[0..n-1] into register iMem.
+**
+** The z[] string will probably not be zero-terminated. But the
+** z[n] character is guaranteed to be something that does not look
+** like the continuation of the number.
+*/
+static void codeInteger(Vdbe *v, Expr *pExpr, int negFlag, int iMem){
+ const char *z;
+ if( pExpr->flags & EP_IntValue ){
+ int i = pExpr->iTable;
+ if( negFlag ) i = -i;
+ sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
+ }else if( (z = (char*)pExpr->token.z)!=0 ){
int i;
+ int n = pExpr->token.n;
+ assert( !isdigit(z[n]) );
if( sqlite3GetInt32(z, &i) ){
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- }else if( sqlite3FitsIn64Bits(z) ){
- sqlite3VdbeOp3(v, OP_Int64, 0, 0, z, n);
+ if( negFlag ) i = -i;
+ sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
+ }else if( sqlite3FitsIn64Bits(z, negFlag) ){
+ i64 value;
+ char *zV;
+ sqlite3Atoi64(z, &value);
+ if( negFlag ) value = -value;
+ zV = dup8bytes(v, (char*)&value);
+ sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
}else{
- sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n);
+ codeReal(v, z, n, negFlag, iMem);
}
}
}
@@ -1676,59 +2060,236 @@ static void codeInteger(Vdbe *v, const char *z, int n){
/*
** Generate code that will extract the iColumn-th column from
-** table pTab and push that column value on the stack. There
-** is an open cursor to pTab in iTable. If iColumn<0 then
-** code is generated that extracts the rowid.
+** table pTab and store the column value in a register. An effort
+** is made to store the column value in register iReg, but this is
+** not guaranteed. The location of the column value is returned.
+**
+** There must be an open cursor to pTab in iTable when this routine
+** is called. If iColumn<0 then code is generated that extracts the rowid.
+**
+** This routine might attempt to reuse the value of the column that
+** has already been loaded into a register. The value will always
+** be used if it has not undergone any affinity changes. But if
+** an affinity change has occurred, then the cached value will only be
+** used if allowAffChng is true.
*/
-void sqlite3ExprCodeGetColumn(Vdbe *v, Table *pTab, int iColumn, int iTable){
+int sqlite3ExprCodeGetColumn(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* Description of the table we are reading from */
+ int iColumn, /* Index of the table column */
+ int iTable, /* The cursor pointing to the table */
+ int iReg, /* Store results here */
+ int allowAffChng /* True if prior affinity changes are OK */
+){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ struct yColCache *p;
+
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+ if( p->iTable==iTable && p->iColumn==iColumn
+ && (!p->affChange || allowAffChng) ){
+#if 0
+ sqlite3VdbeAddOp0(v, OP_Noop);
+ VdbeComment((v, "OPT: tab%d.col%d -> r%d", iTable, iColumn, p->iReg));
+#endif
+ return p->iReg;
+ }
+ }
+ assert( v!=0 );
if( iColumn<0 ){
int op = (pTab && IsVirtual(pTab)) ? OP_VRowid : OP_Rowid;
- sqlite3VdbeAddOp(v, op, iTable, 0);
+ sqlite3VdbeAddOp2(v, op, iTable, iReg);
}else if( pTab==0 ){
- sqlite3VdbeAddOp(v, OP_Column, iTable, iColumn);
+ sqlite3VdbeAddOp3(v, OP_Column, iTable, iColumn, iReg);
}else{
int op = IsVirtual(pTab) ? OP_VColumn : OP_Column;
- sqlite3VdbeAddOp(v, op, iTable, iColumn);
+ sqlite3VdbeAddOp3(v, op, iTable, iColumn, iReg);
sqlite3ColumnDefault(v, pTab, iColumn);
#ifndef SQLITE_OMIT_FLOATING_POINT
if( pTab->aCol[iColumn].affinity==SQLITE_AFF_REAL ){
- sqlite3VdbeAddOp(v, OP_RealAffinity, 0, 0);
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
}
#endif
}
+ if( pParse->disableColCache==0 ){
+ i = pParse->iColCache;
+ p = &pParse->aColCache[i];
+ p->iTable = iTable;
+ p->iColumn = iColumn;
+ p->iReg = iReg;
+ p->affChange = 0;
+ i++;
+ if( i>=ArraySize(pParse->aColCache) ) i = 0;
+ if( i>pParse->nColCache ) pParse->nColCache = i;
+ pParse->iColCache = i;
+ }
+ return iReg;
+}
+
+/*
+** Clear all column cache entries associated with the vdbe
+** cursor with cursor number iTable.
+*/
+void sqlite3ExprClearColumnCache(Parse *pParse, int iTable){
+ if( iTable<0 ){
+ pParse->nColCache = 0;
+ pParse->iColCache = 0;
+ }else{
+ int i;
+ for(i=0; i<pParse->nColCache; i++){
+ if( pParse->aColCache[i].iTable==iTable ){
+ testcase( i==pParse->nColCache-1 );
+ pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
+ pParse->iColCache = pParse->nColCache;
+ }
+ }
+ }
+}
+
+/*
+** Record the fact that an affinity change has occurred on iCount
+** registers starting with iStart.
+*/
+void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){
+ int iEnd = iStart + iCount - 1;
+ int i;
+ for(i=0; i<pParse->nColCache; i++){
+ int r = pParse->aColCache[i].iReg;
+ if( r>=iStart && r<=iEnd ){
+ pParse->aColCache[i].affChange = 1;
+ }
+ }
+}
+
+/*
+** Generate code to move content from registers iFrom...iFrom+nReg-1
+** over to iTo..iTo+nReg-1. Keep the column cache up-to-date.
+*/
+void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
+ int i;
+ if( iFrom==iTo ) return;
+ sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
+ for(i=0; i<pParse->nColCache; i++){
+ int x = pParse->aColCache[i].iReg;
+ if( x>=iFrom && x<iFrom+nReg ){
+ pParse->aColCache[i].iReg += iTo-iFrom;
+ }
+ }
+}
+
+/*
+** Generate code to copy content from registers iFrom...iFrom+nReg-1
+** over to iTo..iTo+nReg-1.
+*/
+void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int nReg){
+ int i;
+ if( iFrom==iTo ) return;
+ for(i=0; i<nReg; i++){
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, iFrom+i, iTo+i);
+ }
+}
+
+/*
+** Return true if any register in the range iFrom..iTo (inclusive)
+** is used as part of the column cache.
+*/
+static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
+ int i;
+ for(i=0; i<pParse->nColCache; i++){
+ int r = pParse->aColCache[i].iReg;
+ if( r>=iFrom && r<=iTo ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Theres is a value in register iCurrent. We ultimately want
+** the value to be in register iTarget. It might be that
+** iCurrent and iTarget are the same register.
+**
+** We are going to modify the value, so we need to make sure it
+** is not a cached register. If iCurrent is a cached register,
+** then try to move the value over to iTarget. If iTarget is a
+** cached register, then clear the corresponding cache line.
+**
+** Return the register that the value ends up in.
+*/
+int sqlite3ExprWritableRegister(Parse *pParse, int iCurrent, int iTarget){
+ int i;
+ assert( pParse->pVdbe!=0 );
+ if( !usedAsColumnCache(pParse, iCurrent, iCurrent) ){
+ return iCurrent;
+ }
+ if( iCurrent!=iTarget ){
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, iCurrent, iTarget);
+ }
+ for(i=0; i<pParse->nColCache; i++){
+ if( pParse->aColCache[i].iReg==iTarget ){
+ pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
+ pParse->iColCache = pParse->nColCache;
+ }
+ }
+ return iTarget;
+}
+
+/*
+** If the last instruction coded is an ephemeral copy of any of
+** the registers in the nReg registers beginning with iReg, then
+** convert the last instruction from OP_SCopy to OP_Copy.
+*/
+void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){
+ int addr;
+ VdbeOp *pOp;
+ Vdbe *v;
+
+ v = pParse->pVdbe;
+ addr = sqlite3VdbeCurrentAddr(v);
+ pOp = sqlite3VdbeGetOp(v, addr-1);
+ assert( pOp || pParse->db->mallocFailed );
+ if( pOp && pOp->opcode==OP_SCopy && pOp->p1>=iReg && pOp->p1<iReg+nReg ){
+ pOp->opcode = OP_Copy;
+ }
}
/*
** Generate code into the current Vdbe to evaluate the given
-** expression and leave the result on the top of stack.
+** expression. Attempt to store the results in register "target".
+** Return the register where results are stored.
**
-** This code depends on the fact that certain token values (ex: TK_EQ)
-** are the same as opcode values (ex: OP_Eq) that implement the corresponding
-** operation. Special comments in vdbe.c and the mkopcodeh.awk script in
-** the make process cause these values to align. Assert()s in the code
-** below verify that the numbers are aligned correctly.
+** With this routine, there is no guaranteed that results will
+** be stored in target. The result might be stored in some other
+** register if it is convenient to do so. The calling function
+** must check the return code and move the results to the desired
+** register.
*/
-void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
- Vdbe *v = pParse->pVdbe;
- int op;
- int stackChng = 1; /* Amount of change to stack depth */
+int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
+ Vdbe *v = pParse->pVdbe; /* The VM under construction */
+ int op; /* The opcode being coded */
+ int inReg = target; /* Results stored in register inReg */
+ int regFree1 = 0; /* If non-zero free this temporary register */
+ int regFree2 = 0; /* If non-zero free this temporary register */
+ int r1, r2, r3, r4; /* Various register numbers */
+
+ assert( v!=0 || pParse->db->mallocFailed );
+ assert( target>0 && target<=pParse->nMem );
+ if( v==0 ) return 0;
- if( v==0 ) return;
if( pExpr==0 ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
- return;
+ op = TK_NULL;
+ }else{
+ op = pExpr->op;
}
- op = pExpr->op;
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
- sqlite3VdbeAddOp(v, OP_MemLoad, pCol->iMem, 0);
+ assert( pCol->iMem>0 );
+ inReg = pCol->iMem;
break;
}else if( pAggInfo->useSortingIdx ){
- sqlite3VdbeAddOp(v, OP_Column, pAggInfo->sortingIdx,
- pCol->iSorterColumn);
+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdx,
+ pCol->iSorterColumn, target);
break;
}
/* Otherwise, fall thru into the TK_COLUMN case */
@@ -1736,60 +2297,66 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
case TK_COLUMN: {
if( pExpr->iTable<0 ){
/* This only happens when coding check constraints */
- assert( pParse->ckOffset>0 );
- sqlite3VdbeAddOp(v, OP_Dup, pParse->ckOffset-pExpr->iColumn-1, 1);
+ assert( pParse->ckBase>0 );
+ inReg = pExpr->iColumn + pParse->ckBase;
}else{
- sqlite3ExprCodeGetColumn(v, pExpr->pTab, pExpr->iColumn, pExpr->iTable);
+ testcase( (pExpr->flags & EP_AnyAff)!=0 );
+ inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ pExpr->iColumn, pExpr->iTable, target,
+ pExpr->flags & EP_AnyAff);
}
break;
}
case TK_INTEGER: {
- codeInteger(v, (char*)pExpr->token.z, pExpr->token.n);
+ codeInteger(v, pExpr, 0, target);
+ break;
+ }
+ case TK_FLOAT: {
+ codeReal(v, (char*)pExpr->token.z, pExpr->token.n, 0, target);
break;
}
- case TK_FLOAT:
case TK_STRING: {
- assert( TK_FLOAT==OP_Real );
- assert( TK_STRING==OP_String8 );
- sqlite3DequoteExpr(pExpr);
- sqlite3VdbeOp3(v, op, 0, 0, (char*)pExpr->token.z, pExpr->token.n);
+ sqlite3DequoteExpr(pParse->db, pExpr);
+ sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
+ (char*)pExpr->token.z, pExpr->token.n);
break;
}
case TK_NULL: {
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
break;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case TK_BLOB: {
int n;
const char *z;
- assert( TK_BLOB==OP_HexBlob );
+ char *zBlob;
+ assert( pExpr->token.n>=3 );
+ assert( pExpr->token.z[0]=='x' || pExpr->token.z[0]=='X' );
+ assert( pExpr->token.z[1]=='\'' );
+ assert( pExpr->token.z[pExpr->token.n-1]=='\'' );
n = pExpr->token.n - 3;
z = (char*)pExpr->token.z + 2;
- assert( n>=0 );
- if( n==0 ){
- z = "";
- }
- sqlite3VdbeOp3(v, op, 0, 0, z, n);
+ zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n);
+ sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC);
break;
}
#endif
case TK_VARIABLE: {
- sqlite3VdbeAddOp(v, OP_Variable, pExpr->iTable, 0);
+ sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iTable, target);
if( pExpr->token.n>1 ){
- sqlite3VdbeChangeP3(v, -1, (char*)pExpr->token.z, pExpr->token.n);
+ sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n);
}
break;
}
case TK_REGISTER: {
- sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iTable, 0);
+ inReg = pExpr->iTable;
break;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
int aff, to_op;
- sqlite3ExprCode(pParse, pExpr->pLeft);
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
aff = sqlite3AffinityType(&pExpr->token);
to_op = aff - SQLITE_AFF_TEXT + OP_ToText;
assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT );
@@ -1797,8 +2364,14 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
assert( to_op==OP_ToNumeric || aff!=SQLITE_AFF_NUMERIC );
assert( to_op==OP_ToInt || aff!=SQLITE_AFF_INTEGER );
assert( to_op==OP_ToReal || aff!=SQLITE_AFF_REAL );
- sqlite3VdbeAddOp(v, to_op, 0, 0);
- stackChng = 0;
+ testcase( to_op==OP_ToText );
+ testcase( to_op==OP_ToBlob );
+ testcase( to_op==OP_ToNumeric );
+ testcase( to_op==OP_ToInt );
+ testcase( to_op==OP_ToReal );
+ sqlite3VdbeAddOp1(v, to_op, inReg);
+ testcase( usedAsColumnCache(pParse, inReg, inReg) );
+ sqlite3ExprCacheAffinityChange(pParse, inReg, 1);
break;
}
#endif /* SQLITE_OMIT_CAST */
@@ -1814,10 +2387,18 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
assert( TK_GE==OP_Ge );
assert( TK_EQ==OP_Eq );
assert( TK_NE==OP_Ne );
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3ExprCode(pParse, pExpr->pRight);
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0);
- stackChng = -1;
+ testcase( op==TK_LT );
+ testcase( op==TK_LE );
+ testcase( op==TK_GT );
+ testcase( op==TK_GE );
+ testcase( op==TK_EQ );
+ testcase( op==TK_NE );
+ codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+ pExpr->pRight, &r2, &regFree2);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
+ r1, r2, inReg, SQLITE_STOREP2);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
break;
}
case TK_AND:
@@ -1843,48 +2424,69 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
assert( TK_LSHIFT==OP_ShiftLeft );
assert( TK_RSHIFT==OP_ShiftRight );
assert( TK_CONCAT==OP_Concat );
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3ExprCode(pParse, pExpr->pRight);
- sqlite3VdbeAddOp(v, op, 0, 0);
- stackChng = -1;
+ testcase( op==TK_AND );
+ testcase( op==TK_OR );
+ testcase( op==TK_PLUS );
+ testcase( op==TK_MINUS );
+ testcase( op==TK_REM );
+ testcase( op==TK_BITAND );
+ testcase( op==TK_BITOR );
+ testcase( op==TK_SLASH );
+ testcase( op==TK_LSHIFT );
+ testcase( op==TK_RSHIFT );
+ testcase( op==TK_CONCAT );
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+ sqlite3VdbeAddOp3(v, op, r2, r1, target);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
break;
}
case TK_UMINUS: {
Expr *pLeft = pExpr->pLeft;
assert( pLeft );
if( pLeft->op==TK_FLOAT || pLeft->op==TK_INTEGER ){
- Token *p = &pLeft->token;
- char *z = sqlite3MPrintf("-%.*s", p->n, p->z);
if( pLeft->op==TK_FLOAT ){
- sqlite3VdbeOp3(v, OP_Real, 0, 0, z, p->n+1);
+ codeReal(v, (char*)pLeft->token.z, pLeft->token.n, 1, target);
}else{
- codeInteger(v, z, p->n+1);
+ codeInteger(v, pLeft, 1, target);
}
- sqliteFree(z);
- break;
+ }else{
+ regFree1 = r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r1);
+ r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree2);
+ sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
+ testcase( regFree2==0 );
}
- /* Fall through into TK_NOT */
+ inReg = target;
+ break;
}
case TK_BITNOT:
case TK_NOT: {
assert( TK_BITNOT==OP_BitNot );
assert( TK_NOT==OP_Not );
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3VdbeAddOp(v, op, 0, 0);
- stackChng = 0;
+ testcase( op==TK_BITNOT );
+ testcase( op==TK_NOT );
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ testcase( inReg==target );
+ testcase( usedAsColumnCache(pParse, inReg, inReg) );
+ inReg = sqlite3ExprWritableRegister(pParse, inReg, target);
+ sqlite3VdbeAddOp1(v, op, inReg);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- int dest;
+ int addr;
assert( TK_ISNULL==OP_IsNull );
assert( TK_NOTNULL==OP_NotNull );
- sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
- sqlite3ExprCode(pParse, pExpr->pLeft);
- dest = sqlite3VdbeCurrentAddr(v) + 2;
- sqlite3VdbeAddOp(v, op, 1, dest);
- sqlite3VdbeAddOp(v, OP_AddImm, -1, 0);
- stackChng = 0;
+ testcase( op==TK_ISNULL );
+ testcase( op==TK_NOTNULL );
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, target);
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ testcase( regFree1==0 );
+ addr = sqlite3VdbeAddOp1(v, op, r1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, target, -1);
+ sqlite3VdbeJumpHere(v, addr);
break;
}
case TK_AGG_FUNCTION: {
@@ -1893,7 +2495,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
sqlite3ErrorMsg(pParse, "misuse of aggregate: %T",
&pExpr->span);
}else{
- sqlite3VdbeAddOp(v, OP_MemLoad, pInfo->aFunc[pExpr->iAgg].iMem, 0);
+ inReg = pInfo->aFunc[pExpr->iAgg].iMem;
}
break;
}
@@ -1906,13 +2508,23 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
const char *zId;
int constMask = 0;
int i;
- u8 enc = ENC(pParse->db);
+ sqlite3 *db = pParse->db;
+ u8 enc = ENC(db);
CollSeq *pColl = 0;
+
+ testcase( op==TK_CONST_FUNC );
+ testcase( op==TK_FUNCTION );
zId = (char*)pExpr->token.z;
nId = pExpr->token.n;
pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0);
assert( pDef!=0 );
- nExpr = sqlite3ExprCodeExprList(pParse, pList);
+ if( pList ){
+ nExpr = pList->nExpr;
+ r1 = sqlite3GetTempRange(pParse, nExpr);
+ sqlite3ExprCodeExprList(pParse, pList, r1, 1);
+ }else{
+ nExpr = r1 = 0;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Possibly overload the function if the first argument is
** a virtual table column.
@@ -1927,9 +2539,9 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
** for function overloading. But we use the B term in "glob(B,A)".
*/
if( nExpr>=2 && (pExpr->flags & EP_InfixFunc) ){
- pDef = sqlite3VtabOverloadFunction(pDef, nExpr, pList->a[1].pExpr);
+ pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[1].pExpr);
}else if( nExpr>0 ){
- pDef = sqlite3VtabOverloadFunction(pDef, nExpr, pList->a[0].pExpr);
+ pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[0].pExpr);
}
#endif
for(i=0; i<nExpr && i<32; i++){
@@ -1942,81 +2554,190 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
}
if( pDef->needCollSeq ){
if( !pColl ) pColl = pParse->db->pDfltColl;
- sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
+ }
+ sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
+ (char*)pDef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, nExpr);
+ if( nExpr ){
+ sqlite3ReleaseTempRange(pParse, r1, nExpr);
}
- sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF);
- stackChng = 1-nExpr;
+ sqlite3ExprCacheAffinityChange(pParse, r1, nExpr);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS:
case TK_SELECT: {
+ testcase( op==TK_EXISTS );
+ testcase( op==TK_SELECT );
if( pExpr->iColumn==0 ){
- sqlite3CodeSubselect(pParse, pExpr);
+ sqlite3CodeSubselect(pParse, pExpr, 0);
}
- sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0);
- VdbeComment((v, "# load subquery result"));
+ inReg = pExpr->iColumn;
break;
}
case TK_IN: {
- int addr;
+ int rNotFound = 0;
+ int rMayHaveNull = 0;
+ int j2, j3, j4, j5;
char affinity;
- int ckOffset = pParse->ckOffset;
- sqlite3CodeSubselect(pParse, pExpr);
+ int eType;
+
+ VdbeNoopComment((v, "begin IN expr r%d", target));
+ eType = sqlite3FindInIndex(pParse, pExpr, &rMayHaveNull);
+ if( rMayHaveNull ){
+ rNotFound = ++pParse->nMem;
+ }
/* Figure out the affinity to use to create a key from the results
** of the expression. affinityStr stores a static string suitable for
- ** P3 of OP_MakeRecord.
+ ** P4 of OP_MakeRecord.
*/
affinity = comparisonAffinity(pExpr);
- sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
- pParse->ckOffset = (ckOffset ? (ckOffset+1) : 0);
/* Code the <expr> from "<expr> IN (...)". The temporary table
** pExpr->iTable contains the values that make up the (...) set.
*/
- sqlite3ExprCode(pParse, pExpr->pLeft);
- addr = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+4); /* addr + 0 */
- sqlite3VdbeAddOp(v, OP_Pop, 2, 0);
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, addr+7);
- sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); /* addr + 4 */
- sqlite3VdbeAddOp(v, OP_Found, pExpr->iTable, addr+7);
- sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); /* addr + 6 */
+ pParse->disableColCache++;
+ sqlite3ExprCode(pParse, pExpr->pLeft, target);
+ pParse->disableColCache--;
+ j2 = sqlite3VdbeAddOp1(v, OP_IsNull, target);
+ if( eType==IN_INDEX_ROWID ){
+ j3 = sqlite3VdbeAddOp1(v, OP_MustBeInt, target);
+ j4 = sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, 0, target);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, target);
+ j5 = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, j3);
+ sqlite3VdbeJumpHere(v, j4);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
+ }else{
+ r2 = regFree2 = sqlite3GetTempReg(pParse);
+ /* Create a record and test for set membership. If the set contains
+ ** the value, then jump to the end of the test code. The target
+ ** register still contains the true (1) value written to it earlier.
+ */
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, target, 1, r2, &affinity, 1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, target);
+ j5 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
+
+ /* If the set membership test fails, then the result of the
+ ** "x IN (...)" expression must be either 0 or NULL. If the set
+ ** contains no NULL values, then the result is 0. If the set
+ ** contains one or more NULL values, then the result of the
+ ** expression is also NULL.
+ */
+ if( rNotFound==0 ){
+ /* This branch runs if it is known at compile time (now) that
+ ** the set contains no NULL values. This happens as the result
+ ** of a "NOT NULL" constraint in the database schema. No need
+ ** to test the data structure at runtime in this case.
+ */
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
+ }else{
+ /* This block populates the rNotFound register with either NULL
+ ** or 0 (an integer value). If the data structure contains one
+ ** or more NULLs, then set rNotFound to NULL. Otherwise, set it
+ ** to 0. If register rMayHaveNull is already set to some value
+ ** other than NULL, then the test has already been run and
+ ** rNotFound is already populated.
+ */
+ static const char nullRecord[] = { 0x02, 0x00 };
+ j3 = sqlite3VdbeAddOp1(v, OP_NotNull, rMayHaveNull);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, rNotFound);
+ sqlite3VdbeAddOp4(v, OP_Blob, 2, rMayHaveNull, 0,
+ nullRecord, P4_STATIC);
+ j4 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, rMayHaveNull);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, rNotFound);
+ sqlite3VdbeJumpHere(v, j4);
+ sqlite3VdbeJumpHere(v, j3);
+
+ /* Copy the value of register rNotFound (which is either NULL or 0)
+ ** into the target register. This will be the result of the
+ ** expression.
+ */
+ sqlite3VdbeAddOp2(v, OP_Copy, rNotFound, target);
+ }
+ }
+ sqlite3VdbeJumpHere(v, j2);
+ sqlite3VdbeJumpHere(v, j5);
+ VdbeComment((v, "end IN expr r%d", target));
break;
}
#endif
+ /*
+ ** x BETWEEN y AND z
+ **
+ ** This is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** X is stored in pExpr->pLeft.
+ ** Y is stored in pExpr->pList->a[0].pExpr.
+ ** Z is stored in pExpr->pList->a[1].pExpr.
+ */
case TK_BETWEEN: {
Expr *pLeft = pExpr->pLeft;
struct ExprList_item *pLItem = pExpr->pList->a;
Expr *pRight = pLItem->pExpr;
- sqlite3ExprCode(pParse, pLeft);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3ExprCode(pParse, pRight);
- codeCompare(pParse, pLeft, pRight, OP_Ge, 0, 0);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+
+ codeCompareOperands(pParse, pLeft, &r1, &regFree1,
+ pRight, &r2, &regFree2);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
+ r3 = sqlite3GetTempReg(pParse);
+ r4 = sqlite3GetTempReg(pParse);
+ codeCompare(pParse, pLeft, pRight, OP_Ge,
+ r1, r2, r3, SQLITE_STOREP2);
pLItem++;
pRight = pLItem->pExpr;
- sqlite3ExprCode(pParse, pRight);
- codeCompare(pParse, pLeft, pRight, OP_Le, 0, 0);
- sqlite3VdbeAddOp(v, OP_And, 0, 0);
+ sqlite3ReleaseTempReg(pParse, regFree2);
+ r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
+ testcase( regFree2==0 );
+ codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2);
+ sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
+ sqlite3ReleaseTempReg(pParse, r3);
+ sqlite3ReleaseTempReg(pParse, r4);
break;
}
case TK_UPLUS: {
- sqlite3ExprCode(pParse, pExpr->pLeft);
- stackChng = 0;
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
break;
}
+
+ /*
+ ** Form A:
+ ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
+ **
+ ** Form B:
+ ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
+ **
+ ** Form A is can be transformed into the equivalent form B as follows:
+ ** CASE WHEN x=e1 THEN r1 WHEN x=e2 THEN r2 ...
+ ** WHEN x=eN THEN rN ELSE y END
+ **
+ ** X (if it exists) is in pExpr->pLeft.
+ ** Y is in pExpr->pRight. The Y is also optional. If there is no
+ ** ELSE clause and no other term matches, then the result of the
+ ** exprssion is NULL.
+ ** Ei is in pExpr->pList->a[i*2] and Ri is pExpr->pList->a[i*2+1].
+ **
+ ** The result of the expression is the Ri for the first matching Ei,
+ ** or if there is no matching Ei, the ELSE term Y, or if there is
+ ** no ELSE term, NULL.
+ */
case TK_CASE: {
- int expr_end_label;
- int jumpInst;
- int nExpr;
- int i;
- ExprList *pEList;
- struct ExprList_item *aListelem;
+ int endLabel; /* GOTO label for end of CASE stmt */
+ int nextCase; /* GOTO label for next WHEN clause */
+ int nExpr; /* 2x number of WHEN terms */
+ int i; /* Loop counter */
+ ExprList *pEList; /* List of WHEN terms */
+ struct ExprList_item *aListelem; /* Array of WHEN terms */
+ Expr opCompare; /* The X==Ei expression */
+ Expr cacheX; /* Cached expression X */
+ Expr *pX; /* The X expression */
+ Expr *pTest; /* X==Ei (form A) or just Ei (form B) */
assert(pExpr->pList);
assert((pExpr->pList->nExpr % 2) == 0);
@@ -2024,33 +2745,42 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
pEList = pExpr->pList;
aListelem = pEList->a;
nExpr = pEList->nExpr;
- expr_end_label = sqlite3VdbeMakeLabel(v);
- if( pExpr->pLeft ){
- sqlite3ExprCode(pParse, pExpr->pLeft);
+ endLabel = sqlite3VdbeMakeLabel(v);
+ if( (pX = pExpr->pLeft)!=0 ){
+ cacheX = *pX;
+ testcase( pX->op==TK_COLUMN || pX->op==TK_REGISTER );
+ cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, &regFree1);
+ testcase( regFree1==0 );
+ cacheX.op = TK_REGISTER;
+ cacheX.iColumn = 0;
+ opCompare.op = TK_EQ;
+ opCompare.pLeft = &cacheX;
+ pTest = &opCompare;
}
+ pParse->disableColCache++;
for(i=0; i<nExpr; i=i+2){
- sqlite3ExprCode(pParse, aListelem[i].pExpr);
- if( pExpr->pLeft ){
- sqlite3VdbeAddOp(v, OP_Dup, 1, 1);
- jumpInst = codeCompare(pParse, pExpr->pLeft, aListelem[i].pExpr,
- OP_Ne, 0, 1);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ if( pX ){
+ opCompare.pRight = aListelem[i].pExpr;
}else{
- jumpInst = sqlite3VdbeAddOp(v, OP_IfNot, 1, 0);
+ pTest = aListelem[i].pExpr;
}
- sqlite3ExprCode(pParse, aListelem[i+1].pExpr);
- sqlite3VdbeAddOp(v, OP_Goto, 0, expr_end_label);
- sqlite3VdbeJumpHere(v, jumpInst);
- }
- if( pExpr->pLeft ){
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ nextCase = sqlite3VdbeMakeLabel(v);
+ testcase( pTest->op==TK_COLUMN || pTest->op==TK_REGISTER );
+ sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL);
+ testcase( aListelem[i+1].pExpr->op==TK_COLUMN );
+ testcase( aListelem[i+1].pExpr->op==TK_REGISTER );
+ sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel);
+ sqlite3VdbeResolveLabel(v, nextCase);
}
if( pExpr->pRight ){
- sqlite3ExprCode(pParse, pExpr->pRight);
+ sqlite3ExprCode(pParse, pExpr->pRight, target);
}else{
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
- sqlite3VdbeResolveLabel(v, expr_end_label);
+ sqlite3VdbeResolveLabel(v, endLabel);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
break;
}
#ifndef SQLITE_OMIT_TRIGGER
@@ -2058,76 +2788,231 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
if( !pParse->trigStack ){
sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program");
- return;
+ return 0;
}
if( pExpr->iColumn!=OE_Ignore ){
assert( pExpr->iColumn==OE_Rollback ||
pExpr->iColumn == OE_Abort ||
pExpr->iColumn == OE_Fail );
- sqlite3DequoteExpr(pExpr);
- sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn,
+ sqlite3DequoteExpr(pParse->db, pExpr);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0,
(char*)pExpr->token.z, pExpr->token.n);
} else {
assert( pExpr->iColumn == OE_Ignore );
- sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
- VdbeComment((v, "# raise(IGNORE)"));
+ sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
+ VdbeComment((v, "raise(IGNORE)"));
}
- stackChng = 0;
break;
}
#endif
}
+ sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ReleaseTempReg(pParse, regFree2);
+ return inReg;
+}
+
+/*
+** Generate code to evaluate an expression and store the results
+** into a register. Return the register number where the results
+** are stored.
+**
+** If the register is a temporary register that can be deallocated,
+** then write its number into *pReg. If the result register is not
+** a temporary, then set *pReg to zero.
+*/
+int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
+ int r1 = sqlite3GetTempReg(pParse);
+ int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
+ if( r2==r1 ){
+ *pReg = r1;
+ }else{
+ sqlite3ReleaseTempReg(pParse, r1);
+ *pReg = 0;
+ }
+ return r2;
+}
- if( pParse->ckOffset ){
- pParse->ckOffset += stackChng;
- assert( pParse->ckOffset );
+/*
+** Generate code that will evaluate expression pExpr and store the
+** results in register target. The results are guaranteed to appear
+** in register target.
+*/
+int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
+ int inReg;
+
+ assert( target>0 && target<=pParse->nMem );
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
+ assert( pParse->pVdbe || pParse->db->mallocFailed );
+ if( inReg!=target && pParse->pVdbe ){
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
}
+ return target;
}
-#ifndef SQLITE_OMIT_TRIGGER
/*
-** Generate code that evalutes the given expression and leaves the result
-** on the stack. See also sqlite3ExprCode().
+** Generate code that evalutes the given expression and puts the result
+** in register target.
**
-** This routine might also cache the result and modify the pExpr tree
-** so that it will make use of the cached result on subsequent evaluations
-** rather than evaluate the whole expression again. Trivial expressions are
-** not cached. If the expression is cached, its result is stored in a
-** memory location.
+** Also make a copy of the expression results into another "cache" register
+** and modify the expression so that the next time it is evaluated,
+** the result is a copy of the cache register.
+**
+** This routine is used for expressions that are used multiple
+** times. They are evaluated once and the results of the expression
+** are reused.
*/
-void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr){
+int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
Vdbe *v = pParse->pVdbe;
- int iMem;
- int addr1, addr2;
- if( v==0 ) return;
- addr1 = sqlite3VdbeCurrentAddr(v);
- sqlite3ExprCode(pParse, pExpr);
- addr2 = sqlite3VdbeCurrentAddr(v);
- if( addr2>addr1+1 || sqlite3VdbeGetOp(v, addr1)->opcode==OP_Function ){
- iMem = pExpr->iTable = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0);
+ int inReg;
+ inReg = sqlite3ExprCode(pParse, pExpr, target);
+ assert( target>0 );
+ if( pExpr->op!=TK_REGISTER ){
+ int iMem;
+ iMem = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
+ pExpr->iTable = iMem;
+ pExpr->iColumn = pExpr->op;
pExpr->op = TK_REGISTER;
}
+ return inReg;
}
+
+/*
+** Return TRUE if pExpr is an constant expression that is appropriate
+** for factoring out of a loop. Appropriate expressions are:
+**
+** * Any expression that evaluates to two or more opcodes.
+**
+** * Any OP_Integer, OP_Real, OP_String, OP_Blob, OP_Null,
+** or OP_Variable that does not need to be placed in a
+** specific register.
+**
+** There is no point in factoring out single-instruction constant
+** expressions that need to be placed in a particular register.
+** We could factor them out, but then we would end up adding an
+** OP_SCopy instruction to move the value into the correct register
+** later. We might as well just use the original instruction and
+** avoid the OP_SCopy.
+*/
+static int isAppropriateForFactoring(Expr *p){
+ if( !sqlite3ExprIsConstantNotJoin(p) ){
+ return 0; /* Only constant expressions are appropriate for factoring */
+ }
+ if( (p->flags & EP_FixedDest)==0 ){
+ return 1; /* Any constant without a fixed destination is appropriate */
+ }
+ while( p->op==TK_UPLUS ) p = p->pLeft;
+ switch( p->op ){
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB:
#endif
+ case TK_VARIABLE:
+ case TK_INTEGER:
+ case TK_FLOAT:
+ case TK_NULL:
+ case TK_STRING: {
+ testcase( p->op==TK_BLOB );
+ testcase( p->op==TK_VARIABLE );
+ testcase( p->op==TK_INTEGER );
+ testcase( p->op==TK_FLOAT );
+ testcase( p->op==TK_NULL );
+ testcase( p->op==TK_STRING );
+ /* Single-instruction constants with a fixed destination are
+ ** better done in-line. If we factor them, they will just end
+ ** up generating an OP_SCopy to move the value to the destination
+ ** register. */
+ return 0;
+ }
+ case TK_UMINUS: {
+ if( p->pLeft->op==TK_FLOAT || p->pLeft->op==TK_INTEGER ){
+ return 0;
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ return 1;
+}
+
+/*
+** If pExpr is a constant expression that is appropriate for
+** factoring out of a loop, then evaluate the expression
+** into a register and convert the expression into a TK_REGISTER
+** expression.
+*/
+static int evalConstExpr(void *pArg, Expr *pExpr){
+ Parse *pParse = (Parse*)pArg;
+ switch( pExpr->op ){
+ case TK_REGISTER: {
+ return 1;
+ }
+ case TK_FUNCTION:
+ case TK_AGG_FUNCTION:
+ case TK_CONST_FUNC: {
+ /* The arguments to a function have a fixed destination.
+ ** Mark them this way to avoid generated unneeded OP_SCopy
+ ** instructions.
+ */
+ ExprList *pList = pExpr->pList;
+ if( pList ){
+ int i = pList->nExpr;
+ struct ExprList_item *pItem = pList->a;
+ for(; i>0; i--, pItem++){
+ if( pItem->pExpr ) pItem->pExpr->flags |= EP_FixedDest;
+ }
+ }
+ break;
+ }
+ }
+ if( isAppropriateForFactoring(pExpr) ){
+ int r1 = ++pParse->nMem;
+ int r2;
+ r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
+ if( r1!=r2 ) sqlite3ReleaseTempReg(pParse, r1);
+ pExpr->iColumn = pExpr->op;
+ pExpr->op = TK_REGISTER;
+ pExpr->iTable = r2;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Preevaluate constant subexpressions within pExpr and store the
+** results in registers. Modify pExpr so that the constant subexpresions
+** are TK_REGISTER opcodes that refer to the precomputed values.
+*/
+void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
+ walkExprTree(pExpr, evalConstExpr, pParse);
+}
+
/*
** Generate code that pushes the value of every element of the given
-** expression list onto the stack.
+** expression list into a sequence of registers beginning at target.
**
-** Return the number of elements pushed onto the stack.
+** Return the number of elements evaluated.
*/
int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */
- ExprList *pList /* The expression list to be coded */
+ ExprList *pList, /* The expression list to be coded */
+ int target, /* Where to write results */
+ int doHardCopy /* Call sqlite3ExprHardCopy on each element if true */
){
struct ExprList_item *pItem;
int i, n;
- if( pList==0 ) return 0;
+ assert( pList!=0 || pParse->db->mallocFailed );
+ if( pList==0 ){
+ return 0;
+ }
+ assert( target>0 );
n = pList->nExpr;
- for(pItem=pList->a, i=n; i>0; i--, pItem++){
- sqlite3ExprCode(pParse, pItem->pExpr);
+ for(pItem=pList->a, i=0; i<n; i++, pItem++){
+ sqlite3ExprCode(pParse, pItem->pExpr, target+i);
+ if( doHardCopy ) sqlite3ExprHardCopy(pParse, target, n);
}
return n;
}
@@ -2138,7 +3023,7 @@ int sqlite3ExprCodeExprList(
** continues straight thru if the expression is false.
**
** If the expression evaluates to NULL (neither true nor false), then
-** take the jump if the jumpIfNull flag is true.
+** take the jump if the jumpIfNull flag is SQLITE_JUMPIFNULL.
**
** This code depends on the fact that certain token values (ex: TK_EQ)
** are the same as opcode values (ex: OP_Eq) that implement the corresponding
@@ -2149,23 +3034,38 @@ int sqlite3ExprCodeExprList(
void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Vdbe *v = pParse->pVdbe;
int op = 0;
- int ckOffset = pParse->ckOffset;
+ int regFree1 = 0;
+ int regFree2 = 0;
+ int r1, r2;
+
+ assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
if( v==0 || pExpr==0 ) return;
op = pExpr->op;
switch( op ){
case TK_AND: {
int d2 = sqlite3VdbeMakeLabel(v);
- sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull);
+ testcase( jumpIfNull==0 );
+ testcase( pParse->disableColCache==0 );
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
+ pParse->disableColCache++;
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
sqlite3VdbeResolveLabel(v, d2);
break;
}
case TK_OR: {
+ testcase( jumpIfNull==0 );
+ testcase( pParse->disableColCache==0 );
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ pParse->disableColCache++;
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
break;
}
case TK_NOT: {
+ testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
break;
}
@@ -2181,50 +3081,74 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
assert( TK_GE==OP_Ge );
assert( TK_EQ==OP_Eq );
assert( TK_NE==OP_Ne );
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3ExprCode(pParse, pExpr->pRight);
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull);
+ testcase( op==TK_LT );
+ testcase( op==TK_LE );
+ testcase( op==TK_GT );
+ testcase( op==TK_GE );
+ testcase( op==TK_EQ );
+ testcase( op==TK_NE );
+ testcase( jumpIfNull==0 );
+ codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+ pExpr->pRight, &r2, &regFree2);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
+ r1, r2, dest, jumpIfNull);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
assert( TK_ISNULL==OP_IsNull );
assert( TK_NOTNULL==OP_NotNull );
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3VdbeAddOp(v, op, 1, dest);
+ testcase( op==TK_ISNULL );
+ testcase( op==TK_NOTNULL );
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeAddOp2(v, op, r1, dest);
+ testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
- /* The expression "x BETWEEN y AND z" is implemented as:
+ /* x BETWEEN y AND z
**
- ** 1 IF (x < y) GOTO 3
- ** 2 IF (x <= z) GOTO <dest>
- ** 3 ...
+ ** Is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** Code it as such, taking care to do the common subexpression
+ ** elementation of x.
*/
- int addr;
- Expr *pLeft = pExpr->pLeft;
- Expr *pRight = pExpr->pList->a[0].pExpr;
- sqlite3ExprCode(pParse, pLeft);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3ExprCode(pParse, pRight);
- addr = codeCompare(pParse, pLeft, pRight, OP_Lt, 0, !jumpIfNull);
-
- pRight = pExpr->pList->a[1].pExpr;
- sqlite3ExprCode(pParse, pRight);
- codeCompare(pParse, pLeft, pRight, OP_Le, dest, jumpIfNull);
-
- sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
- sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ Expr exprAnd;
+ Expr compLeft;
+ Expr compRight;
+ Expr exprX;
+
+ exprX = *pExpr->pLeft;
+ exprAnd.op = TK_AND;
+ exprAnd.pLeft = &compLeft;
+ exprAnd.pRight = &compRight;
+ compLeft.op = TK_GE;
+ compLeft.pLeft = &exprX;
+ compLeft.pRight = pExpr->pList->a[0].pExpr;
+ compRight.op = TK_LE;
+ compRight.pLeft = &exprX;
+ compRight.pRight = pExpr->pList->a[1].pExpr;
+ exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
+ testcase( regFree1==0 );
+ exprX.op = TK_REGISTER;
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
break;
}
default: {
- sqlite3ExprCode(pParse, pExpr);
- sqlite3VdbeAddOp(v, OP_If, jumpIfNull, dest);
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
break;
}
}
- pParse->ckOffset = ckOffset;
+ sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ReleaseTempReg(pParse, regFree2);
}
/*
@@ -2233,12 +3157,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
** continues straight thru if the expression is true.
**
** If the expression evaluates to NULL (neither true nor false) then
-** jump if jumpIfNull is true or fall through if jumpIfNull is false.
+** jump if jumpIfNull is SQLITE_JUMPIFNULL or fall through if jumpIfNull
+** is 0.
*/
void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Vdbe *v = pParse->pVdbe;
int op = 0;
- int ckOffset = pParse->ckOffset;
+ int regFree1 = 0;
+ int regFree2 = 0;
+ int r1, r2;
+
+ assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
if( v==0 || pExpr==0 ) return;
/* The value of pExpr->op and op are related as follows:
@@ -2274,14 +3203,24 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
switch( pExpr->op ){
case TK_AND: {
+ testcase( jumpIfNull==0 );
+ testcase( pParse->disableColCache==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ pParse->disableColCache++;
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
break;
}
case TK_OR: {
int d2 = sqlite3VdbeMakeLabel(v);
- sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull);
+ testcase( jumpIfNull==0 );
+ testcase( pParse->disableColCache==0 );
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
+ pParse->disableColCache++;
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ assert( pParse->disableColCache>0 );
+ pParse->disableColCache--;
sqlite3VdbeResolveLabel(v, d2);
break;
}
@@ -2295,47 +3234,72 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_GE:
case TK_NE:
case TK_EQ: {
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3ExprCode(pParse, pExpr->pRight);
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull);
+ testcase( op==TK_LT );
+ testcase( op==TK_LE );
+ testcase( op==TK_GT );
+ testcase( op==TK_GE );
+ testcase( op==TK_EQ );
+ testcase( op==TK_NE );
+ testcase( jumpIfNull==0 );
+ codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+ pExpr->pRight, &r2, &regFree2);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
+ r1, r2, dest, jumpIfNull);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- sqlite3ExprCode(pParse, pExpr->pLeft);
- sqlite3VdbeAddOp(v, op, 1, dest);
+ testcase( op==TK_ISNULL );
+ testcase( op==TK_NOTNULL );
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeAddOp2(v, op, r1, dest);
+ testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
- /* The expression is "x BETWEEN y AND z". It is implemented as:
+ /* x BETWEEN y AND z
+ **
+ ** Is equivalent to
+ **
+ ** x>=y AND x<=z
**
- ** 1 IF (x >= y) GOTO 3
- ** 2 GOTO <dest>
- ** 3 IF (x > z) GOTO <dest>
+ ** Code it as such, taking care to do the common subexpression
+ ** elementation of x.
*/
- int addr;
- Expr *pLeft = pExpr->pLeft;
- Expr *pRight = pExpr->pList->a[0].pExpr;
- sqlite3ExprCode(pParse, pLeft);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3ExprCode(pParse, pRight);
- addr = sqlite3VdbeCurrentAddr(v);
- codeCompare(pParse, pLeft, pRight, OP_Ge, addr+3, !jumpIfNull);
-
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, dest);
- pRight = pExpr->pList->a[1].pExpr;
- sqlite3ExprCode(pParse, pRight);
- codeCompare(pParse, pLeft, pRight, OP_Gt, dest, jumpIfNull);
+ Expr exprAnd;
+ Expr compLeft;
+ Expr compRight;
+ Expr exprX;
+
+ exprX = *pExpr->pLeft;
+ exprAnd.op = TK_AND;
+ exprAnd.pLeft = &compLeft;
+ exprAnd.pRight = &compRight;
+ compLeft.op = TK_GE;
+ compLeft.pLeft = &exprX;
+ compLeft.pRight = pExpr->pList->a[0].pExpr;
+ compRight.op = TK_LE;
+ compRight.pLeft = &exprX;
+ compRight.pRight = pExpr->pList->a[1].pExpr;
+ exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
+ testcase( regFree1==0 );
+ exprX.op = TK_REGISTER;
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull);
break;
}
default: {
- sqlite3ExprCode(pParse, pExpr);
- sqlite3VdbeAddOp(v, OP_IfNot, jumpIfNull, dest);
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
break;
}
}
- pParse->ckOffset = ckOffset;
+ sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ReleaseTempReg(pParse, regFree2);
}
/*
@@ -2389,9 +3353,10 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Add a new element to the pAggInfo->aCol[] array. Return the index of
** the new element. Return a negative number if malloc fails.
*/
-static int addAggInfoColumn(AggInfo *pInfo){
+static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
int i;
pInfo->aCol = sqlite3ArrayAllocate(
+ db,
pInfo->aCol,
sizeof(pInfo->aCol[0]),
3,
@@ -2406,9 +3371,10 @@ static int addAggInfoColumn(AggInfo *pInfo){
** Add a new element to the pAggInfo->aFunc[] array. Return the index of
** the new element. Return a negative number if malloc fails.
*/
-static int addAggInfoFunc(AggInfo *pInfo){
+static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
int i;
pInfo->aFunc = sqlite3ArrayAllocate(
+ db,
pInfo->aFunc,
sizeof(pInfo->aFunc[0]),
3,
@@ -2432,7 +3398,6 @@ static int analyzeAggregate(void *pArg, Expr *pExpr){
Parse *pParse = pNC->pParse;
SrcList *pSrcList = pNC->pSrcList;
AggInfo *pAggInfo = pNC->pAggInfo;
-
switch( pExpr->op ){
case TK_AGG_COLUMN:
@@ -2458,12 +3423,14 @@ static int analyzeAggregate(void *pArg, Expr *pExpr){
break;
}
}
- if( k>=pAggInfo->nColumn && (k = addAggInfoColumn(pAggInfo))>=0 ){
+ if( (k>=pAggInfo->nColumn)
+ && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
+ ){
pCol = &pAggInfo->aCol[k];
pCol->pTab = pExpr->pTab;
pCol->iTable = pExpr->iTable;
pCol->iColumn = pExpr->iColumn;
- pCol->iMem = pParse->nMem++;
+ pCol->iMem = ++pParse->nMem;
pCol->iSorterColumn = -1;
pCol->pExpr = pExpr;
if( pAggInfo->pGroupBy ){
@@ -2515,11 +3482,11 @@ static int analyzeAggregate(void *pArg, Expr *pExpr){
/* pExpr is original. Make a new entry in pAggInfo->aFunc[]
*/
u8 enc = ENC(pParse->db);
- i = addAggInfoFunc(pAggInfo);
+ i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){
pItem = &pAggInfo->aFunc[i];
pItem->pExpr = pExpr;
- pItem->iMem = pParse->nMem++;
+ pItem->iMem = ++pParse->nMem;
pItem->pFunc = sqlite3FindFunction(pParse->db,
(char*)pExpr->token.z, pExpr->token.n,
pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
@@ -2558,14 +3525,9 @@ static int analyzeAggregate(void *pArg, Expr *pExpr){
**
** This routine should only be called after the expression has been
** analyzed by sqlite3ExprResolveNames().
-**
-** If errors are seen, leave an error message in zErrMsg and return
-** the number of errors.
*/
-int sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
- int nErr = pNC->pParse->nErr;
+void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
walkExprTree(pExpr, analyzeAggregate, pNC);
- return pNC->pParse->nErr - nErr;
}
/*
@@ -2574,14 +3536,51 @@ int sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
**
** If an error is found, the analysis is cut short.
*/
-int sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){
+void sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){
struct ExprList_item *pItem;
int i;
- int nErr = 0;
if( pList ){
- for(pItem=pList->a, i=0; nErr==0 && i<pList->nExpr; i++, pItem++){
- nErr += sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr);
+ for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
+ sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr);
}
}
- return nErr;
+}
+
+/*
+** Allocate or deallocate temporary use registers during code generation.
+*/
+int sqlite3GetTempReg(Parse *pParse){
+ if( pParse->nTempReg==0 ){
+ return ++pParse->nMem;
+ }
+ return pParse->aTempReg[--pParse->nTempReg];
+}
+void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
+ if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
+ sqlite3ExprWritableRegister(pParse, iReg, iReg);
+ pParse->aTempReg[pParse->nTempReg++] = iReg;
+ }
+}
+
+/*
+** Allocate or deallocate a block of nReg consecutive registers
+*/
+int sqlite3GetTempRange(Parse *pParse, int nReg){
+ int i, n;
+ i = pParse->iRangeReg;
+ n = pParse->nRangeReg;
+ if( nReg<=n && !usedAsColumnCache(pParse, i, i+n-1) ){
+ pParse->iRangeReg += nReg;
+ pParse->nRangeReg -= nReg;
+ }else{
+ i = pParse->nMem+1;
+ pParse->nMem += nReg;
+ }
+ return i;
+}
+void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
+ if( nReg>pParse->nRangeReg ){
+ pParse->nRangeReg = nReg;
+ pParse->iRangeReg = iReg;
+ }
}
diff --git a/third_party/sqlite/src/fault.c b/third_party/sqlite/src/fault.c
new file mode 100755
index 0000000..5ede8b9
--- /dev/null
+++ b/third_party/sqlite/src/fault.c
@@ -0,0 +1,71 @@
+/*
+** 2008 Jan 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.
+**
+*************************************************************************
+**
+** $Id: fault.c,v 1.10 2008/06/22 12:37:58 drh Exp $
+*/
+
+/*
+** This file contains code to support the concept of "benign"
+** malloc failures (when the xMalloc() or xRealloc() method of the
+** sqlite3_mem_methods structure fails to allocate a block of memory
+** and returns 0).
+**
+** Most malloc failures are non-benign. After they occur, SQLite
+** abandons the current operation and returns an error code (usually
+** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily
+** fatal. For example, if a malloc fails while resizing a hash table, this
+** is completely recoverable simply by not carrying out the resize. The
+** hash table will continue to function normally. So a malloc failure
+** during a hash table resize is a benign fault.
+*/
+
+#include "sqliteInt.h"
+
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+
+/*
+** Global variables.
+*/
+static struct BenignMallocHooks {
+ void (*xBenignBegin)(void);
+ void (*xBenignEnd)(void);
+} hooks;
+
+/*
+** Register hooks to call when sqlite3BeginBenignMalloc() and
+** sqlite3EndBenignMalloc() are called, respectively.
+*/
+void sqlite3BenignMallocHooks(
+ void (*xBenignBegin)(void),
+ void (*xBenignEnd)(void)
+){
+ hooks.xBenignBegin = xBenignBegin;
+ hooks.xBenignEnd = xBenignEnd;
+}
+
+/*
+** This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that
+** subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc()
+** indicates that subsequent malloc failures are non-benign.
+*/
+void sqlite3BeginBenignMalloc(void){
+ if( hooks.xBenignBegin ){
+ hooks.xBenignBegin();
+ }
+}
+void sqlite3EndBenignMalloc(void){
+ if( hooks.xBenignEnd ){
+ hooks.xBenignEnd();
+ }
+}
+
+#endif /* #ifndef SQLITE_OMIT_BUILTIN_TEST */
diff --git a/third_party/sqlite/func.c b/third_party/sqlite/src/func.c
index 2a7019d..7316c74 100644..100755
--- a/third_party/sqlite/func.c
+++ b/third_party/sqlite/src/func.c
@@ -16,15 +16,13 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: func.c,v 1.163 2007/07/26 06:50:06 danielk1977 Exp $
+** $Id: func.c,v 1.196 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
-/* #include <math.h> */
#include <stdlib.h>
#include <assert.h>
#include "vdbeInt.h"
-#include "os.h"
/*
@@ -171,7 +169,7 @@ static void substrFunc(
int p0type;
i64 p1, p2;
- assert( argc==3 );
+ assert( argc==3 || argc==2 );
p0type = sqlite3_value_type(argv[0]);
if( p0type==SQLITE_BLOB ){
len = sqlite3_value_bytes(argv[0]);
@@ -187,7 +185,11 @@ static void substrFunc(
}
}
p1 = sqlite3_value_int(argv[1]);
- p2 = sqlite3_value_int(argv[2]);
+ if( argc==3 ){
+ p2 = sqlite3_value_int(argv[2]);
+ }else{
+ p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH];
+ }
if( p1<0 ){
p1 += len;
if( p1<0 ){
@@ -237,6 +239,25 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
/*
+** Allocate nByte bytes of space using sqlite3_malloc(). If the
+** allocation fails, call sqlite3_result_error_nomem() to notify
+** the database handle that malloc() has failed.
+*/
+static void *contextMalloc(sqlite3_context *context, i64 nByte){
+ char *z;
+ if( nByte>sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH] ){
+ sqlite3_result_error_toobig(context);
+ z = 0;
+ }else{
+ z = sqlite3Malloc(nByte);
+ if( !z && nByte>0 ){
+ sqlite3_result_error_nomem(context);
+ }
+ }
+ return z;
+}
+
+/*
** Implementation of the upper() and lower() SQL functions.
*/
static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
@@ -249,7 +270,7 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
/* Verify that the call to _bytes() does not invalidate the _text() pointer */
assert( z2==(char*)sqlite3_value_text(argv[0]) );
if( z2 ){
- z1 = sqlite3_malloc(n+1);
+ z1 = contextMalloc(context, ((i64)n)+1);
if( z1 ){
memcpy(z1, z2, n+1);
for(i=0; z1[i]; i++){
@@ -269,7 +290,7 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
/* Verify that the call to _bytes() does not invalidate the _text() pointer */
assert( z2==(char*)sqlite3_value_text(argv[0]) );
if( z2 ){
- z1 = sqlite3_malloc(n+1);
+ z1 = contextMalloc(context, ((i64)n)+1);
if( z1 ){
memcpy(z1, z2, n+1);
for(i=0; z1[i]; i++){
@@ -308,7 +329,7 @@ static void randomFunc(
sqlite3_value **argv
){
sqlite_int64 r;
- sqlite3Randomness(sizeof(r), &r);
+ sqlite3_randomness(sizeof(r), &r);
if( (r<<1)==0 ) r = 0; /* Prevent 0x8000.... as the result so that we */
/* can always do abs() of the result */
sqlite3_result_int64(context, r);
@@ -330,14 +351,10 @@ static void randomBlob(
if( n<1 ){
n = 1;
}
- if( n>SQLITE_MAX_LENGTH ){
- sqlite3_result_error_toobig(context);
- return;
- }
- p = sqliteMalloc(n);
+ p = contextMalloc(context, n);
if( p ){
- sqlite3Randomness(n, p);
- sqlite3_result_blob(context, (char*)p, n, sqlite3FreeX);
+ sqlite3_randomness(n, p);
+ sqlite3_result_blob(context, (char*)p, n, sqlite3_free);
}
}
@@ -350,7 +367,7 @@ static void last_insert_rowid(
int arg,
sqlite3_value **argv
){
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_result_int64(context, sqlite3_last_insert_rowid(db));
}
@@ -363,7 +380,7 @@ static void changes(
int arg,
sqlite3_value **argv
){
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_result_int(context, sqlite3_changes(db));
}
@@ -376,7 +393,7 @@ static void total_changes(
int arg,
sqlite3_value **argv
){
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_result_int(context, sqlite3_total_changes(db));
}
@@ -390,6 +407,19 @@ struct compareInfo {
u8 noCase;
};
+/*
+** For LIKE and GLOB matching on EBCDIC machines, assume that every
+** character is exactly one byte in size. Also, all characters are
+** able to participate in upper-case-to-lower-case mappings in EBCDIC
+** whereas only characters less than 0x80 do in ASCII.
+*/
+#if defined(SQLITE_EBCDIC)
+# define sqlite3Utf8Read(A,B,C) (*(A++))
+# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
+#else
+# define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
+#endif
+
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
/* The correct SQL-92 behavior is for the LIKE operator to ignore
** case. Thus 'a' LIKE 'A' would be true. */
@@ -466,11 +496,11 @@ static int patternCompare(
}
while( (c2 = sqlite3Utf8Read(zString,0,&zString))!=0 ){
if( noCase ){
- c2 = c2<0x80 ? sqlite3UpperToLower[c2] : c2;
- c = c<0x80 ? sqlite3UpperToLower[c] : c;
+ GlogUpperToLower(c2);
+ GlogUpperToLower(c);
while( c2 != 0 && c2 != c ){
c2 = sqlite3Utf8Read(zString, 0, &zString);
- if( c2<0x80 ) c2 = sqlite3UpperToLower[c2];
+ GlogUpperToLower(c2);
}
}else{
while( c2 != 0 && c2 != c ){
@@ -522,8 +552,8 @@ static int patternCompare(
}else{
c2 = sqlite3Utf8Read(zString, 0, &zString);
if( noCase ){
- c = c<0x80 ? sqlite3UpperToLower[c] : c;
- c2 = c2<0x80 ? sqlite3UpperToLower[c2] : c2;
+ GlogUpperToLower(c);
+ GlogUpperToLower(c2);
}
if( c!=c2 ){
return 0;
@@ -563,6 +593,7 @@ static void likeFunc(
){
const unsigned char *zA, *zB;
int escape = 0;
+ sqlite3 *db = sqlite3_context_db_handle(context);
zB = sqlite3_value_text(argv[0]);
zA = sqlite3_value_text(argv[1]);
@@ -570,7 +601,8 @@ static void likeFunc(
/* Limit the length of the LIKE or GLOB pattern to avoid problems
** of deep recursion and N*N behavior in patternCompare().
*/
- if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
+ if( sqlite3_value_bytes(argv[0]) >
+ db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
return;
}
@@ -662,15 +694,8 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
char const *zBlob = sqlite3_value_blob(argv[0]);
int nBlob = sqlite3_value_bytes(argv[0]);
assert( zBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */
-
- if( 2*nBlob+4>SQLITE_MAX_LENGTH ){
- sqlite3_result_error_toobig(context);
- return;
- }
- zText = (char *)sqliteMalloc((2*nBlob)+4);
- if( !zText ){
- sqlite3_result_error(context, "out of memory", -1);
- }else{
+ zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4);
+ if( zText ){
int i;
for(i=0; i<nBlob; i++){
zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F];
@@ -681,7 +706,7 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
zText[0] = 'X';
zText[1] = '\'';
sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
- sqliteFree(zText);
+ sqlite3_free(zText);
}
break;
}
@@ -693,23 +718,19 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
if( zArg==0 ) return;
for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; }
- if( i+n+3>SQLITE_MAX_LENGTH ){
- sqlite3_result_error_toobig(context);
- return;
- }
- z = sqliteMalloc( i+n+3 );
- if( z==0 ) return;
- z[0] = '\'';
- for(i=0, j=1; zArg[i]; i++){
- z[j++] = zArg[i];
- if( zArg[i]=='\'' ){
- z[j++] = '\'';
+ z = contextMalloc(context, ((i64)i)+((i64)n)+3);
+ if( z ){
+ z[0] = '\'';
+ for(i=0, j=1; zArg[i]; i++){
+ z[j++] = zArg[i];
+ if( zArg[i]=='\'' ){
+ z[j++] = '\'';
+ }
}
+ z[j++] = '\'';
+ z[j] = 0;
+ sqlite3_result_text(context, z, j, sqlite3_free);
}
- z[j++] = '\'';
- z[j] = 0;
- sqlite3_result_text(context, z, j, SQLITE_TRANSIENT);
- sqliteFree(z);
}
}
}
@@ -729,20 +750,17 @@ static void hexFunc(
assert( argc==1 );
pBlob = sqlite3_value_blob(argv[0]);
n = sqlite3_value_bytes(argv[0]);
- if( n*2+1>SQLITE_MAX_LENGTH ){
- sqlite3_result_error_toobig(context);
- return;
- }
assert( pBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */
- z = zHex = sqlite3_malloc(n*2 + 1);
- if( zHex==0 ) return;
- for(i=0; i<n; i++, pBlob++){
- unsigned char c = *pBlob;
- *(z++) = hexdigits[(c>>4)&0xf];
- *(z++) = hexdigits[c&0xf];
+ z = zHex = contextMalloc(context, ((i64)n)*2 + 1);
+ if( zHex ){
+ for(i=0; i<n; i++, pBlob++){
+ unsigned char c = *pBlob;
+ *(z++) = hexdigits[(c>>4)&0xf];
+ *(z++) = hexdigits[c&0xf];
+ }
+ *z = 0;
+ sqlite3_result_text(context, zHex, n*2, sqlite3_free);
}
- *z = 0;
- sqlite3_result_text(context, zHex, n*2, sqlite3_free);
}
/*
@@ -800,7 +818,7 @@ static void replaceFunc(
assert( zRep==sqlite3_value_text(argv[2]) );
nOut = nStr + 1;
assert( nOut<SQLITE_MAX_LENGTH );
- zOut = sqlite3_malloc((int)nOut);
+ zOut = contextMalloc(context, (i64)nOut);
if( zOut==0 ){
return;
}
@@ -809,14 +827,19 @@ static void replaceFunc(
if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){
zOut[j++] = zStr[i];
}else{
+ u8 *zOld;
+ sqlite3 *db = sqlite3_context_db_handle(context);
nOut += nRep - nPattern;
- if( nOut>=SQLITE_MAX_LENGTH ){
+ if( nOut>=db->aLimit[SQLITE_LIMIT_LENGTH] ){
sqlite3_result_error_toobig(context);
- sqlite3_free(zOut);
+ sqlite3DbFree(db, zOut);
return;
}
+ zOld = zOut;
zOut = sqlite3_realloc(zOut, (int)nOut);
if( zOut==0 ){
+ sqlite3_result_error_nomem(context);
+ sqlite3DbFree(db, zOld);
return;
}
memcpy(&zOut[j], zRep, nRep);
@@ -847,7 +870,7 @@ static void trimFunc(
int flags; /* 1: trimleft 2: trimright 3: trim */
int i; /* Loop counter */
unsigned char *aLen; /* Length of each character in zCharSet */
- const unsigned char **azChar; /* Individual characters in zCharSet */
+ unsigned char **azChar; /* Individual characters in zCharSet */
int nChar; /* Number of characters in zCharSet */
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
@@ -862,7 +885,7 @@ static void trimFunc(
static const unsigned char *azOne[] = { (u8*)" " };
nChar = 1;
aLen = (u8*)lenOne;
- azChar = azOne;
+ azChar = (unsigned char **)azOne;
zCharSet = 0;
}else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){
return;
@@ -872,20 +895,20 @@ static void trimFunc(
SQLITE_SKIP_UTF8(z);
}
if( nChar>0 ){
- azChar = sqlite3_malloc( nChar*(sizeof(char*)+1) );
+ azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1));
if( azChar==0 ){
return;
}
aLen = (unsigned char*)&azChar[nChar];
for(z=zCharSet, nChar=0; *z; nChar++){
- azChar[nChar] = z;
+ azChar[nChar] = (unsigned char *)z;
SQLITE_SKIP_UTF8(z);
aLen[nChar] = z - azChar[nChar];
}
}
}
if( nChar>0 ){
- flags = (int)sqlite3_user_data(context);
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context));
if( flags & 1 ){
while( nIn>0 ){
int len;
@@ -910,7 +933,7 @@ static void trimFunc(
}
}
if( zCharSet ){
- sqlite3_free(azChar);
+ sqlite3_free((void*)azChar);
}
}
sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
@@ -974,7 +997,7 @@ static void soundexFunc(
static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
const char *zFile = (const char *)sqlite3_value_text(argv[0]);
const char *zProc;
- sqlite3 *db = sqlite3_user_data(context);
+ sqlite3 *db = sqlite3_context_db_handle(context);
char *zErrMsg = 0;
if( argc==2 ){
@@ -989,164 +1012,6 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
}
#endif
-#ifdef SQLITE_TEST
-/*
-** This function generates a string of random characters. Used for
-** generating test data.
-*/
-static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){
- static const unsigned char zSrc[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- ".-!,:*^+=_|?/<> ";
- int iMin, iMax, n, r, i;
- unsigned char zBuf[1000];
- if( argc>=1 ){
- iMin = sqlite3_value_int(argv[0]);
- if( iMin<0 ) iMin = 0;
- if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1;
- }else{
- iMin = 1;
- }
- if( argc>=2 ){
- iMax = sqlite3_value_int(argv[1]);
- if( iMax<iMin ) iMax = iMin;
- if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf)-1;
- }else{
- iMax = 50;
- }
- n = iMin;
- if( iMax>iMin ){
- sqlite3Randomness(sizeof(r), &r);
- r &= 0x7fffffff;
- n += r%(iMax + 1 - iMin);
- }
- assert( n<sizeof(zBuf) );
- sqlite3Randomness(n, zBuf);
- for(i=0; i<n; i++){
- zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)];
- }
- zBuf[n] = 0;
- sqlite3_result_text(context, (char*)zBuf, n, SQLITE_TRANSIENT);
-}
-#endif /* SQLITE_TEST */
-
-#ifdef SQLITE_TEST
-/*
-** The following two SQL functions are used to test returning a text
-** result with a destructor. Function 'test_destructor' takes one argument
-** and returns the same argument interpreted as TEXT. A destructor is
-** passed with the sqlite3_result_text() call.
-**
-** SQL function 'test_destructor_count' returns the number of outstanding
-** allocations made by 'test_destructor';
-**
-** WARNING: Not threadsafe.
-*/
-static int test_destructor_count_var = 0;
-static void destructor(void *p){
- char *zVal = (char *)p;
- assert(zVal);
- zVal--;
- sqliteFree(zVal);
- test_destructor_count_var--;
-}
-static void test_destructor(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **argv
-){
- char *zVal;
- int len;
- sqlite3 *db = sqlite3_user_data(pCtx);
-
- test_destructor_count_var++;
- assert( nArg==1 );
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- len = sqlite3ValueBytes(argv[0], ENC(db));
- zVal = sqliteMalloc(len+3);
- zVal[len] = 0;
- zVal[len-1] = 0;
- assert( zVal );
- zVal++;
- memcpy(zVal, sqlite3ValueText(argv[0], ENC(db)), len);
- if( ENC(db)==SQLITE_UTF8 ){
- sqlite3_result_text(pCtx, zVal, -1, destructor);
-#ifndef SQLITE_OMIT_UTF16
- }else if( ENC(db)==SQLITE_UTF16LE ){
- sqlite3_result_text16le(pCtx, zVal, -1, destructor);
- }else{
- sqlite3_result_text16be(pCtx, zVal, -1, destructor);
-#endif /* SQLITE_OMIT_UTF16 */
- }
-}
-static void test_destructor_count(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **argv
-){
- sqlite3_result_int(pCtx, test_destructor_count_var);
-}
-#endif /* SQLITE_TEST */
-
-#ifdef SQLITE_TEST
-/*
-** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata()
-** interface.
-**
-** The test_auxdata() SQL function attempts to register each of its arguments
-** as auxiliary data. If there are no prior registrations of aux data for
-** that argument (meaning the argument is not a constant or this is its first
-** call) then the result for that argument is 0. If there is a prior
-** registration, the result for that argument is 1. The overall result
-** is the individual argument results separated by spaces.
-*/
-static void free_test_auxdata(void *p) {sqliteFree(p);}
-static void test_auxdata(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **argv
-){
- int i;
- char *zRet = sqliteMalloc(nArg*2);
- if( !zRet ) return;
- for(i=0; i<nArg; i++){
- char const *z = (char*)sqlite3_value_text(argv[i]);
- if( z ){
- char *zAux = sqlite3_get_auxdata(pCtx, i);
- if( zAux ){
- zRet[i*2] = '1';
- if( strcmp(zAux, z) ){
- free_test_auxdata((void *)zRet);
- sqlite3_result_error(pCtx, "Auxilary data corruption", -1);
- return;
- }
- }else{
- zRet[i*2] = '0';
- zAux = sqliteStrDup(z);
- sqlite3_set_auxdata(pCtx, i, zAux, free_test_auxdata);
- }
- zRet[i*2+1] = ' ';
- }
- }
- sqlite3_result_text(pCtx, zRet, 2*nArg-1, free_test_auxdata);
-}
-#endif /* SQLITE_TEST */
-
-#ifdef SQLITE_TEST
-/*
-** A function to test error reporting from user functions. This function
-** returns a copy of it's first argument as an error.
-*/
-static void test_error(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **argv
-){
- sqlite3_result_error(pCtx, (char*)sqlite3_value_text(argv[0]), 0);
-}
-#endif /* SQLITE_TEST */
/*
** An instance of the following structure holds the context of a
@@ -1290,6 +1155,58 @@ static void minMaxFinalize(sqlite3_context *context){
}
}
+/*
+** group_concat(EXPR, ?SEPARATOR?)
+*/
+static void groupConcatStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *zVal;
+ StrAccum *pAccum;
+ const char *zSep;
+ int nVal, nSep, i;
+ if( argc==0 || sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
+
+ if( pAccum ){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ pAccum->useMalloc = 1;
+ pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
+ if( pAccum->nChar ){
+ if( argc>1 ){
+ zSep = (char*)sqlite3_value_text(argv[argc-1]);
+ nSep = sqlite3_value_bytes(argv[argc-1]);
+ }else{
+ zSep = ",";
+ nSep = 1;
+ }
+ sqlite3StrAccumAppend(pAccum, zSep, nSep);
+ }
+ i = 0;
+ do{
+ zVal = (char*)sqlite3_value_text(argv[i]);
+ nVal = sqlite3_value_bytes(argv[i]);
+ sqlite3StrAccumAppend(pAccum, zVal, nVal);
+ i++;
+ }while( i<argc-1 );
+ }
+}
+static void groupConcatFinalize(sqlite3_context *context){
+ StrAccum *pAccum;
+ pAccum = sqlite3_aggregate_context(context, 0);
+ if( pAccum ){
+ if( pAccum->tooBig ){
+ sqlite3_result_error_toobig(context);
+ }else if( pAccum->mallocFailed ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
+ sqlite3_free);
+ }
+ }
+}
/*
** This function registered all of the above C functions as SQL
@@ -1300,7 +1217,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
static const struct {
char *zName;
signed char nArg;
- u8 argType; /* ff: db 1: 0, 2: 1, 3: 2,... N: N-1. */
+ u8 argType; /* 1: 0, 2: 1, 3: 2,... N: N-1. */
u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */
u8 needCollSeq;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
@@ -1311,6 +1228,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "max", 0, 1, SQLITE_UTF8, 1, 0 },
{ "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc },
{ "length", 1, 0, SQLITE_UTF8, 0, lengthFunc },
+ { "substr", 2, 0, SQLITE_UTF8, 0, substrFunc },
{ "substr", 3, 0, SQLITE_UTF8, 0, substrFunc },
{ "abs", 1, 0, SQLITE_UTF8, 0, absFunc },
{ "round", 1, 0, SQLITE_UTF8, 0, roundFunc },
@@ -1327,9 +1245,9 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
{ "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
{ "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
- { "last_insert_rowid", 0, 0xff, SQLITE_UTF8, 0, last_insert_rowid },
- { "changes", 0, 0xff, SQLITE_UTF8, 0, changes },
- { "total_changes", 0, 0xff, SQLITE_UTF8, 0, total_changes },
+ { "last_insert_rowid", 0, 0, SQLITE_UTF8, 0, last_insert_rowid },
+ { "changes", 0, 0, SQLITE_UTF8, 0, changes },
+ { "total_changes", 0, 0, SQLITE_UTF8, 0, total_changes },
{ "replace", 3, 0, SQLITE_UTF8, 0, replaceFunc },
{ "ltrim", 1, 1, SQLITE_UTF8, 0, trimFunc },
{ "ltrim", 2, 1, SQLITE_UTF8, 0, trimFunc },
@@ -1342,15 +1260,8 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc},
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
- { "load_extension", 1, 0xff, SQLITE_UTF8, 0, loadExt },
- { "load_extension", 2, 0xff, SQLITE_UTF8, 0, loadExt },
-#endif
-#ifdef SQLITE_TEST
- { "randstr", 2, 0, SQLITE_UTF8, 0, randStr },
- { "test_destructor", 1, 0xff, SQLITE_UTF8, 0, test_destructor},
- { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count},
- { "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata},
- { "test_error", 1, 0, SQLITE_UTF8, 0, test_error},
+ { "load_extension", 1, 0, SQLITE_UTF8, 0, loadExt },
+ { "load_extension", 2, 0, SQLITE_UTF8, 0, loadExt },
#endif
};
static const struct {
@@ -1368,17 +1279,14 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "avg", 1, 0, 0, sumStep, avgFinalize },
{ "count", 0, 0, 0, countStep, countFinalize },
{ "count", 1, 0, 0, countStep, countFinalize },
+ { "group_concat", -1, 0, 0, groupConcatStep, groupConcatFinalize },
};
int i;
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
void *pArg;
u8 argType = aFuncs[i].argType;
- if( argType==0xff ){
- pArg = db;
- }else{
- pArg = (void*)(int)argType;
- }
+ pArg = SQLITE_INT_TO_PTR(argType);
sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
if( aFuncs[i].needCollSeq ){
@@ -1396,7 +1304,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
sqlite3AttachFunctions(db);
#endif
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
- void *pArg = (void*)(int)aAggs[i].argType;
+ void *pArg = SQLITE_INT_TO_PTR(aAggs[i].argType);
sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8,
pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
if( aAggs[i].needCollSeq ){
@@ -1408,11 +1316,11 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
}
}
sqlite3RegisterDateTimeFunctions(db);
- if( !sqlite3MallocFailed() ){
+ if( !db->mallocFailed ){
int rc = sqlite3_overload_function(db, "MATCH", 2);
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
if( rc==SQLITE_NOMEM ){
- sqlite3FailedMalloc();
+ db->mallocFailed = 1;
}
}
#ifdef SQLITE_SSE
diff --git a/third_party/sqlite/src/global.c b/third_party/sqlite/src/global.c
new file mode 100755
index 0000000..fa59d4a
--- /dev/null
+++ b/third_party/sqlite/src/global.c
@@ -0,0 +1,77 @@
+/*
+** 2008 June 13
+**
+** 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 contains definitions of global variables and contants.
+**
+** $Id: global.c,v 1.4 2008/07/28 19:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+
+/* An array to map all upper-case characters into their corresponding
+** lower-case character.
+**
+** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
+** handle case conversions for the UTF character set since the tables
+** involved are nearly as big or bigger than SQLite itself.
+*/
+const unsigned char sqlite3UpperToLower[] = {
+#ifdef SQLITE_ASCII
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
+ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
+ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
+ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
+ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
+ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
+ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
+ 252,253,254,255
+#endif
+#ifdef SQLITE_EBCDIC
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */
+ 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */
+ 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */
+ 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */
+ 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */
+ 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */
+ 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */
+ 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */
+#endif
+};
+
+/*
+** The following singleton contains the global configuration for
+** the SQLite library.
+*/
+struct Sqlite3Config sqlite3Config = {
+ 1, /* bMemstat */
+ 1, /* bCoreMutex */
+ 1, /* bFullMutex */
+ 0x7ffffffe, /* mxStrlen */
+ 100, /* szLookaside */
+ 500, /* nLookaside */
+ /* Other fields all default to zero */
+};
diff --git a/third_party/sqlite/hash.c b/third_party/sqlite/src/hash.c
index 86d9995..df73698 100644..100755
--- a/third_party/sqlite/hash.c
+++ b/third_party/sqlite/src/hash.c
@@ -12,7 +12,7 @@
** This is the implementation of generic hash-tables
** used in SQLite.
**
-** $Id: hash.c,v 1.19 2007/03/31 03:59:24 drh Exp $
+** $Id: hash.c,v 1.30 2008/06/20 14:59:51 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <assert.h>
@@ -41,8 +41,6 @@ void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){
pNew->count = 0;
pNew->htsize = 0;
pNew->ht = 0;
- pNew->xMalloc = sqlite3MallocX;
- pNew->xFree = sqlite3FreeX;
}
/* Remove all entries from a hash table. Reclaim all memory.
@@ -55,15 +53,15 @@ void sqlite3HashClear(Hash *pH){
assert( pH!=0 );
elem = pH->first;
pH->first = 0;
- if( pH->ht ) pH->xFree(pH->ht);
+ sqlite3_free(pH->ht);
pH->ht = 0;
pH->htsize = 0;
while( elem ){
HashElem *next_elem = elem->next;
if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
+ sqlite3_free(elem->pKey);
}
- pH->xFree(elem);
+ sqlite3_free(elem);
elem = next_elem;
}
pH->count = 0;
@@ -216,17 +214,31 @@ static void insertElement(
/* Resize the hash table so that it cantains "new_size" buckets.
** "new_size" must be a power of 2. The hash table might fail
-** to resize if sqliteMalloc() fails.
+** to resize if sqlite3_malloc() fails.
*/
static void rehash(Hash *pH, int new_size){
struct _ht *new_ht; /* The new hash table */
HashElem *elem, *next_elem; /* For looping over existing elements */
int (*xHash)(const void*,int); /* The hash function */
- assert( (new_size & (new_size-1))==0 );
- new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
+#ifdef SQLITE_MALLOC_SOFT_LIMIT
+ if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
+ new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht);
+ }
+ if( new_size==pH->htsize ) return;
+#endif
+
+ /* There is a call to sqlite3_malloc() inside rehash(). If there is
+ ** already an allocation at pH->ht, then if this malloc() fails it
+ ** is benign (since failing to resize a hash table is a performance
+ ** hit only, not a fatal error).
+ */
+ if( pH->htsize>0 ) sqlite3BeginBenignMalloc();
+ new_ht = (struct _ht *)sqlite3MallocZero( new_size*sizeof(struct _ht) );
+ if( pH->htsize>0 ) sqlite3EndBenignMalloc();
+
if( new_ht==0 ) return;
- if( pH->ht ) pH->xFree(pH->ht);
+ sqlite3_free(pH->ht);
pH->ht = new_ht;
pH->htsize = new_size;
xHash = hashFunction(pH->keyClass);
@@ -292,9 +304,9 @@ static void removeElementGivenHash(
pEntry->chain = 0;
}
if( pH->copyKey ){
- pH->xFree(elem->pKey);
+ sqlite3_free(elem->pKey);
}
- pH->xFree( elem );
+ sqlite3_free( elem );
pH->count--;
if( pH->count<=0 ){
assert( pH->first==0 );
@@ -304,10 +316,11 @@ static void removeElementGivenHash(
}
/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
-** found, or NULL if there is no match.
+** that matches pKey,nKey. Return a pointer to the corresponding
+** HashElem structure for this element if it is found, or NULL
+** otherwise.
*/
-void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
+HashElem *sqlite3HashFindElem(const Hash *pH, const void *pKey, int nKey){
int h; /* A hash on key */
HashElem *elem; /* The element that matches key */
int (*xHash)(const void*,int); /* The hash function */
@@ -316,8 +329,17 @@ void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
xHash = hashFunction(pH->keyClass);
assert( xHash!=0 );
h = (*xHash)(pKey,nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
+ elem = findElementGivenHash(pH,pKey,nKey, h % pH->htsize);
+ return elem;
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey. Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
+ HashElem *elem; /* The element that matches key */
+ elem = sqlite3HashFindElem(pH, pKey, nKey);
return elem ? elem->data : 0;
}
@@ -347,25 +369,30 @@ void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
xHash = hashFunction(pH->keyClass);
assert( xHash!=0 );
hraw = (*xHash)(pKey, nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- elem = findElementGivenHash(pH,pKey,nKey,h);
- if( elem ){
- void *old_data = elem->data;
- if( data==0 ){
- removeElementGivenHash(pH,elem,h);
- }else{
- elem->data = data;
+ if( pH->htsize ){
+ h = hraw % pH->htsize;
+ elem = findElementGivenHash(pH,pKey,nKey,h);
+ if( elem ){
+ void *old_data = elem->data;
+ if( data==0 ){
+ removeElementGivenHash(pH,elem,h);
+ }else{
+ elem->data = data;
+ if( !pH->copyKey ){
+ elem->pKey = (void *)pKey;
+ }
+ assert(nKey==elem->nKey);
+ }
+ return old_data;
}
- return old_data;
}
if( data==0 ) return 0;
- new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
+ new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
if( new_elem==0 ) return data;
if( pH->copyKey && pKey!=0 ){
- new_elem->pKey = pH->xMalloc( nKey );
+ new_elem->pKey = sqlite3Malloc( nKey );
if( new_elem->pKey==0 ){
- pH->xFree(new_elem);
+ sqlite3_free(new_elem);
return data;
}
memcpy((void*)new_elem->pKey, pKey, nKey);
@@ -375,13 +402,13 @@ void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
new_elem->nKey = nKey;
pH->count++;
if( pH->htsize==0 ){
- rehash(pH,8);
+ rehash(pH, 128/sizeof(pH->ht[0]));
if( pH->htsize==0 ){
pH->count = 0;
if( pH->copyKey ){
- pH->xFree(new_elem->pKey);
+ sqlite3_free(new_elem->pKey);
}
- pH->xFree(new_elem);
+ sqlite3_free(new_elem);
return data;
}
}
@@ -389,8 +416,7 @@ void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
rehash(pH,pH->htsize*2);
}
assert( pH->htsize>0 );
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
+ h = hraw % pH->htsize;
insertElement(pH, &pH->ht[h], new_elem);
new_elem->data = data;
return 0;
diff --git a/third_party/sqlite/hash.h b/third_party/sqlite/src/hash.h
index 78f60b4..e3274e9 100644..100755
--- a/third_party/sqlite/hash.h
+++ b/third_party/sqlite/src/hash.h
@@ -12,7 +12,7 @@
** This is the header file for the generic hash-table implemenation
** used in SQLite.
**
-** $Id: hash.h,v 1.9 2006/02/14 10:48:39 danielk1977 Exp $
+** $Id: hash.h,v 1.11 2007/09/04 14:31:47 danielk1977 Exp $
*/
#ifndef _SQLITE_HASH_H_
#define _SQLITE_HASH_H_
@@ -33,10 +33,8 @@ struct Hash {
char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */
char copyKey; /* True if copy of key made on insert */
int count; /* Number of entries in this table */
- HashElem *first; /* The first element of the array */
- void *(*xMalloc)(int); /* malloc() function to use */
- void (*xFree)(void *); /* free() function to use */
int htsize; /* Number of buckets in the hash table */
+ HashElem *first; /* The first element of the array */
struct _ht { /* the hash table */
int count; /* Number of entries with this hash */
HashElem *chain; /* Pointer to first entry with this hash */
@@ -83,6 +81,7 @@ struct HashElem {
void sqlite3HashInit(Hash*, int keytype, int copyKey);
void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
+HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey);
void sqlite3HashClear(Hash*);
/*
diff --git a/third_party/sqlite/src/hwtime.h b/third_party/sqlite/src/hwtime.h
new file mode 100755
index 0000000..896041e
--- /dev/null
+++ b/third_party/sqlite/src/hwtime.h
@@ -0,0 +1,87 @@
+/*
+** 2008 May 27
+**
+** 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 contains inline asm code for retrieving "high-performance"
+** counters for x86 class CPUs.
+**
+** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $
+*/
+#ifndef _HWTIME_H_
+#define _HWTIME_H_
+
+/*
+** The following routine only works on pentium-class (or newer) processors.
+** It uses the RDTSC opcode to read the cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \
+ (defined(i386) || defined(__i386__) || defined(_M_IX86))
+
+ #if defined(__GNUC__)
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+ #elif defined(_MSC_VER)
+
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
+ __asm {
+ rdtsc
+ ret ; return value at EDX:EAX
+ }
+ }
+
+ #endif
+
+#elif (defined(__GNUC__) && defined(__x86_64__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long val;
+ __asm__ __volatile__ ("rdtsc" : "=A" (val));
+ return val;
+ }
+
+#elif (defined(__GNUC__) && defined(__ppc__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long long retval;
+ unsigned long junk;
+ __asm__ __volatile__ ("\n\
+ 1: mftbu %1\n\
+ mftb %L0\n\
+ mftbu %0\n\
+ cmpw %0,%1\n\
+ bne 1b"
+ : "=r" (retval), "=r" (junk));
+ return retval;
+ }
+
+#else
+
+ #error Need implementation of sqlite3Hwtime() for your platform.
+
+ /*
+ ** To compile without implementing sqlite3Hwtime() for your platform,
+ ** you can remove the above #error and use the following
+ ** stub function. You will lose timing support for many
+ ** of the debugging and testing utilities, but it should at
+ ** least compile and run.
+ */
+ sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
+
+#endif
+
+#endif /* !defined(_HWTIME_H_) */
diff --git a/third_party/sqlite/insert.c b/third_party/sqlite/src/insert.c
index ba9426d..9a3d46f 100644..100755
--- a/third_party/sqlite/insert.c
+++ b/third_party/sqlite/src/insert.c
@@ -12,12 +12,12 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
-** $Id: insert.c,v 1.188 2007/07/23 19:39:47 drh Exp $
+** $Id: insert.c,v 1.248 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
/*
-** Set P3 of the most recently inserted opcode to a column affinity
+** Set P4 of the most recently inserted opcode to a column affinity
** string for index pIdx. A column affinity string has one character
** for each column in the table, according to the affinity of the column:
**
@@ -28,6 +28,9 @@
** 'c' NUMERIC
** 'd' INTEGER
** 'e' REAL
+**
+** An extra 'b' is appended to the end of the string to cover the
+** rowid that appears as the last column in every index.
*/
void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
if( !pIdx->zColAff ){
@@ -41,21 +44,24 @@ void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
*/
int n;
Table *pTab = pIdx->pTable;
- pIdx->zColAff = (char *)sqliteMalloc(pIdx->nColumn+1);
+ sqlite3 *db = sqlite3VdbeDb(v);
+ pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2);
if( !pIdx->zColAff ){
+ db->mallocFailed = 1;
return;
}
for(n=0; n<pIdx->nColumn; n++){
pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
}
- pIdx->zColAff[pIdx->nColumn] = '\0';
+ pIdx->zColAff[n++] = SQLITE_AFF_NONE;
+ pIdx->zColAff[n] = 0;
}
- sqlite3VdbeChangeP3(v, -1, pIdx->zColAff, 0);
+ sqlite3VdbeChangeP4(v, -1, pIdx->zColAff, 0);
}
/*
-** Set P3 of the most recently inserted opcode to a column affinity
+** Set P4 of the most recently inserted opcode to a column affinity
** string for table pTab. A column affinity string has one character
** for each column indexed by the index, according to the affinity of the
** column:
@@ -79,9 +85,11 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
if( !pTab->zColAff ){
char *zColAff;
int i;
+ sqlite3 *db = sqlite3VdbeDb(v);
- zColAff = (char *)sqliteMalloc(pTab->nCol+1);
+ zColAff = (char *)sqlite3Malloc(pTab->nCol+1);
if( !zColAff ){
+ db->mallocFailed = 1;
return;
}
@@ -93,27 +101,41 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
pTab->zColAff = zColAff;
}
- sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0);
+ sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0);
}
/*
-** Return non-zero if SELECT statement p opens the table with rootpage
-** iTab in database iDb. This is used to see if a statement of the form
-** "INSERT INTO <iDb, iTab> SELECT ..." can run without using temporary
-** table for the results of the SELECT.
-**
-** No checking is done for sub-selects that are part of expressions.
+** Return non-zero if the table pTab in database iDb or any of its indices
+** have been opened at any point in the VDBE program beginning at location
+** iStartAddr throught the end of the program. This is used to see if
+** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can
+** run without using temporary table for the results of the SELECT.
*/
-static int selectReadsTable(Select *p, Schema *pSchema, int iTab){
+static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){
int i;
- struct SrcList_item *pItem;
- if( p->pSrc==0 ) return 0;
- for(i=0, pItem=p->pSrc->a; i<p->pSrc->nSrc; i++, pItem++){
- if( pItem->pSelect ){
- if( selectReadsTable(pItem->pSelect, pSchema, iTab) ) return 1;
- }else{
- if( pItem->pTab->pSchema==pSchema && pItem->pTab->tnum==iTab ) return 1;
+ int iEnd = sqlite3VdbeCurrentAddr(v);
+ for(i=iStartAddr; i<iEnd; i++){
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, i);
+ assert( pOp!=0 );
+ if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
+ Index *pIndex;
+ int tnum = pOp->p2;
+ if( tnum==pTab->tnum ){
+ return 1;
+ }
+ for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
+ if( tnum==pIndex->tnum ){
+ return 1;
+ }
+ }
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pTab->pVtab ){
+ assert( pOp->p4.pVtab!=0 );
+ assert( pOp->p4type==P4_VTAB );
+ return 1;
+ }
+#endif
}
return 0;
}
@@ -122,45 +144,49 @@ static int selectReadsTable(Select *p, Schema *pSchema, int iTab){
/*
** Write out code to initialize the autoincrement logic. This code
** looks up the current autoincrement value in the sqlite_sequence
-** table and stores that value in a memory cell. Code generated by
-** autoIncStep() will keep that memory cell holding the largest
+** table and stores that value in a register. Code generated by
+** autoIncStep() will keep that register holding the largest
** rowid value. Code generated by autoIncEnd() will write the new
** largest value of the counter back into the sqlite_sequence table.
**
** This routine returns the index of the mem[] cell that contains
** the maximum rowid counter.
**
-** Two memory cells are allocated. The next memory cell after the
-** one returned holds the rowid in sqlite_sequence where we will
-** write back the revised maximum rowid.
+** Three consecutive registers are allocated by this routine. The
+** first two hold the name of the target table and the maximum rowid
+** inserted into the target table, respectively.
+** The third holds the rowid in sqlite_sequence where we will
+** write back the revised maximum rowid. This routine returns the
+** index of the second of these three registers.
*/
static int autoIncBegin(
Parse *pParse, /* Parsing context */
int iDb, /* Index of the database holding pTab */
Table *pTab /* The table we are writing to */
){
- int memId = 0;
+ int memId = 0; /* Register holding maximum rowid */
if( pTab->autoInc ){
Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb];
int iCur = pParse->nTab;
- int addr;
+ int addr; /* Address of the top of the loop */
assert( v );
- addr = sqlite3VdbeCurrentAddr(v);
- memId = pParse->nMem+1;
- pParse->nMem += 2;
+ pParse->nMem++; /* Holds name of table */
+ memId = ++pParse->nMem;
+ pParse->nMem++;
sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
- sqlite3VdbeAddOp(v, OP_Rewind, iCur, addr+13);
- sqlite3VdbeAddOp(v, OP_Column, iCur, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0);
- sqlite3VdbeAddOp(v, OP_Ne, 0x100, addr+12);
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, memId-1, 1);
- sqlite3VdbeAddOp(v, OP_Column, iCur, 1);
- sqlite3VdbeAddOp(v, OP_MemStore, memId, 1);
- sqlite3VdbeAddOp(v, OP_Goto, 0, addr+13);
- sqlite3VdbeAddOp(v, OP_Next, iCur, addr+4);
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addr+9);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, memId);
+ sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId);
+ sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, memId+1);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, 1, memId);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9);
+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+2);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, memId);
+ sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
}
return memId;
}
@@ -173,15 +199,15 @@ static int autoIncBegin(
** larger than the maximum rowid in the memId memory cell, then the
** memory cell is updated. The stack is unchanged.
*/
-static void autoIncStep(Parse *pParse, int memId){
+static void autoIncStep(Parse *pParse, int memId, int regRowid){
if( memId>0 ){
- sqlite3VdbeAddOp(pParse->pVdbe, OP_MemMax, memId, 0);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MemMax, memId, regRowid);
}
}
/*
** After doing one or more inserts, the maximum rowid is stored
-** in mem[memId]. Generate code to write this value back into the
+** in reg[memId]. Generate code to write this value back into the
** the sqlite_sequence table.
*/
static void autoIncEnd(
@@ -194,19 +220,18 @@ static void autoIncEnd(
int iCur = pParse->nTab;
Vdbe *v = pParse->pVdbe;
Db *pDb = &pParse->db->aDb[iDb];
- int addr;
+ int j1;
+ int iRec = ++pParse->nMem; /* Memory cell used for record */
+
assert( v );
- addr = sqlite3VdbeCurrentAddr(v);
sqlite3OpenTable(pParse, iCur, iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
- sqlite3VdbeAddOp(v, OP_MemLoad, memId-1, 0);
- sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+7);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_NewRowid, iCur, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0);
- sqlite3VdbeAddOp(v, OP_MemLoad, memId, 0);
- sqlite3VdbeAddOp(v, OP_MakeRecord, 2, 0);
- sqlite3VdbeAddOp(v, OP_Insert, iCur, OPFLAG_APPEND);
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iCur, memId+1);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec);
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, iRec, memId+1);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+ sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
}
#else
@@ -215,7 +240,7 @@ static void autoIncEnd(
** above are all no-ops
*/
# define autoIncBegin(A,B,C) (0)
-# define autoIncStep(A,B)
+# define autoIncStep(A,B,C)
# define autoIncEnd(A,B,C,D)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
@@ -246,7 +271,8 @@ static int xferOptimization(
**
** The code generated follows one of four templates. For a simple
** select with data coming from a VALUES clause, the code executes
-** once straight down through. The template looks like this:
+** once straight down through. Pseudo-code follows (we call this
+** the "1st template"):
**
** open write cursor to <table> and its indices
** puts VALUES clause expressions onto the stack
@@ -264,7 +290,7 @@ static int xferOptimization(
** schemas, including all the same indices, then a special optimization
** is invoked that copies raw records from <table2> over to <table1>.
** See the xferOptimization() function for the implementation of this
-** template. This is the second template.
+** template. This is the 2nd template.
**
** open a write cursor to <table>
** open read cursor on <table2>
@@ -277,45 +303,58 @@ static int xferOptimization(
** close cursors
** end foreach
**
-** The third template is for when the second template does not apply
+** The 3rd template is for when the second template does not apply
** and the SELECT clause does not read from <table> at any time.
** The generated code follows this template:
**
+** EOF <- 0
+** X <- A
** goto B
** A: setup for the SELECT
** loop over the rows in the SELECT
-** gosub C
+** load values into registers R..R+n
+** yield X
** end loop
** cleanup after the SELECT
-** goto D
-** B: open write cursor to <table> and its indices
+** EOF <- 1
+** yield X
** goto A
-** C: insert the select result into <table>
-** return
+** B: open write cursor to <table> and its indices
+** C: yield X
+** if EOF goto D
+** insert the select result into <table> from R..R+n
+** goto C
** D: cleanup
**
-** The fourth template is used if the insert statement takes its
+** The 4th template is used if the insert statement takes its
** values from a SELECT but the data is being inserted into a table
** that is also read as part of the SELECT. In the third form,
** we have to use a intermediate table to store the results of
** the select. The template is like this:
**
+** EOF <- 0
+** X <- A
** goto B
** A: setup for the SELECT
** loop over the tables in the SELECT
-** gosub C
+** load value into register R..R+n
+** yield X
** end loop
** cleanup after the SELECT
-** goto D
-** C: insert the select result into the intermediate table
-** return
-** B: open a cursor to an intermediate table
-** goto A
-** D: open write cursor to <table> and its indices
-** loop over the intermediate table
+** EOF <- 1
+** yield X
+** halt-error
+** B: open temp table
+** L: yield X
+** if EOF goto M
+** insert row from R..R+n into temp table
+** goto L
+** M: open write cursor to <table> and its indices
+** rewind temp table
+** C: loop over rows of intermediate table
** transfer values form intermediate table into <table>
-** end the loop
-** cleanup
+** end loop
+** D: cleanup
*/
void sqlite3Insert(
Parse *pParse, /* Parser context */
@@ -325,41 +364,50 @@ void sqlite3Insert(
IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */
){
- Table *pTab; /* The table to insert into */
+ sqlite3 *db; /* The main database structure */
+ Table *pTab; /* The table to insert into. aka TABLE */
char *zTab; /* Name of the table into which we are inserting */
const char *zDb; /* Name of the database holding this table */
int i, j, idx; /* Loop counters */
Vdbe *v; /* Generate code into this virtual machine */
Index *pIdx; /* For looping over indices of the table */
int nColumn; /* Number of columns in the data */
- int base = 0; /* VDBE Cursor number for pTab */
- int iCont=0,iBreak=0; /* Beginning and end of the loop over srcTab */
- sqlite3 *db; /* The main database structure */
+ int nHidden = 0; /* Number of hidden columns if TABLE is virtual */
+ int baseCur = 0; /* VDBE Cursor number for pTab */
int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
int endOfLoop; /* Label for the end of the insertion loop */
int useTempTable = 0; /* Store SELECT results in intermediate table */
int srcTab = 0; /* Data comes from this temporary cursor if >=0 */
- int iSelectLoop = 0; /* Address of code that implements the SELECT */
- int iCleanup = 0; /* Address of the cleanup code */
- int iInsertBlock = 0; /* Address of the subroutine used to insert data */
- int iCntMem = 0; /* Memory cell used for the row counter */
- int newIdx = -1; /* Cursor for the NEW table */
+ int addrInsTop = 0; /* Jump to label "D" */
+ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
+ int addrSelect = 0; /* Address of coroutine that implements the SELECT */
+ SelectDest dest; /* Destination for SELECT on rhs of INSERT */
+ int newIdx = -1; /* Cursor for the NEW pseudo-table */
+ int iDb; /* Index of database holding TABLE */
Db *pDb; /* The database containing table being inserted into */
- int counterMem = 0; /* Memory cell holding AUTOINCREMENT counter */
int appendFlag = 0; /* True if the insert is likely to be an append */
- int iDb;
- int nHidden = 0;
+ /* Register allocations */
+ int regFromSelect; /* Base register for data coming from SELECT */
+ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */
+ int regRowCount = 0; /* Memory cell used for the row counter */
+ int regIns; /* Block of regs holding rowid+data being inserted */
+ int regRowid; /* registers holding insert rowid */
+ int regData; /* register holding first column to insert */
+ int regRecord; /* Holds the assemblied row record */
+ int regEof; /* Register recording end of SELECT data */
+ int *aRegIdx = 0; /* One register allocated to each index */
+
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */
int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
#endif
- if( pParse->nErr || sqlite3MallocFailed() ){
+ db = pParse->db;
+ if( pParse->nErr || db->mallocFailed ){
goto insert_cleanup;
}
- db = pParse->db;
/* Locate the table into which we will be inserting new information.
*/
@@ -429,6 +477,8 @@ void sqlite3Insert(
**
** Then special optimizations can be applied that make the transfer
** very fast and which reduce fragmentation of indices.
+ **
+ ** This is the 2nd template.
*/
if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){
assert( !triggers_exist );
@@ -438,74 +488,108 @@ void sqlite3Insert(
#endif /* SQLITE_OMIT_XFER_OPT */
/* If this is an AUTOINCREMENT table, look up the sequence number in the
- ** sqlite_sequence table and store it in memory cell counterMem. Also
- ** remember the rowid of the sqlite_sequence table entry in memory cell
- ** counterRowid.
+ ** sqlite_sequence table and store it in memory cell regAutoinc.
*/
- counterMem = autoIncBegin(pParse, iDb, pTab);
+ regAutoinc = autoIncBegin(pParse, iDb, pTab);
/* Figure out how many columns of data are supplied. If the data
- ** is coming from a SELECT statement, then this step also generates
- ** all the code to implement the SELECT statement and invoke a subroutine
- ** to process each row of the result. (Template 2.) If the SELECT
- ** statement uses the the table that is being inserted into, then the
- ** subroutine is also coded here. That subroutine stores the SELECT
- ** results in a temporary table. (Template 3.)
+ ** is coming from a SELECT statement, then generate a co-routine that
+ ** produces a single row of the SELECT on each invocation. The
+ ** co-routine is the common header to the 3rd and 4th templates.
*/
if( pSelect ){
/* Data is coming from a SELECT. Generate code to implement that SELECT
+ ** as a co-routine. The code is common to both the 3rd and 4th
+ ** templates:
+ **
+ ** EOF <- 0
+ ** X <- A
+ ** goto B
+ ** A: setup for the SELECT
+ ** loop over the tables in the SELECT
+ ** load value into register R..R+n
+ ** yield X
+ ** end loop
+ ** cleanup after the SELECT
+ ** EOF <- 1
+ ** yield X
+ ** halt-error
+ **
+ ** On each invocation of the co-routine, it puts a single row of the
+ ** SELECT result into registers dest.iMem...dest.iMem+dest.nMem-1.
+ ** (These output registers are allocated by sqlite3Select().) When
+ ** the SELECT completes, it sets the EOF flag stored in regEof.
*/
- int rc, iInitCode;
- iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
- iSelectLoop = sqlite3VdbeCurrentAddr(v);
- iInsertBlock = sqlite3VdbeMakeLabel(v);
+ int rc, j1;
+
+ regEof = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */
+ VdbeComment((v, "SELECT eof flag"));
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, ++pParse->nMem);
+ addrSelect = sqlite3VdbeCurrentAddr(v)+2;
+ sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.iParm);
+ j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ VdbeComment((v, "Jump over SELECT coroutine"));
/* Resolve the expressions in the SELECT statement and execute it. */
- rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock,0,0,0,0);
- if( rc || pParse->nErr || sqlite3MallocFailed() ){
+ rc = sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
+ if( rc || pParse->nErr || db->mallocFailed ){
goto insert_cleanup;
}
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
+ sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); /* yield X */
+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort);
+ VdbeComment((v, "End of SELECT coroutine"));
+ sqlite3VdbeJumpHere(v, j1); /* label B: */
- iCleanup = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup);
+ regFromSelect = dest.iMem;
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
+ assert( dest.nMem==nColumn );
/* Set useTempTable to TRUE if the result of the SELECT statement
- ** should be written into a temporary table. Set to FALSE if each
- ** row of the SELECT can be written directly into the result table.
+ ** should be written into a temporary table (template 4). Set to
+ ** FALSE if each* row of the SELECT can be written directly into
+ ** the destination table (template 3).
**
** A temp table must be used if the table being updated is also one
** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers.
*/
- if( triggers_exist || selectReadsTable(pSelect,pTab->pSchema,pTab->tnum) ){
+ if( triggers_exist || readsTable(v, addrSelect, iDb, pTab) ){
useTempTable = 1;
}
if( useTempTable ){
- /* Generate the subroutine that SELECT calls to process each row of
- ** the result. Store the result in a temporary table
+ /* Invoke the coroutine to extract information from the SELECT
+ ** and add it to a transient table srcTab. The code generated
+ ** here is from the 4th template:
+ **
+ ** B: open temp table
+ ** L: yield X
+ ** if EOF goto M
+ ** insert row from R..R+n into temp table
+ ** goto L
+ ** M: ...
*/
+ int regRec; /* Register to hold packed record */
+ int regRowid; /* Register to hold temp table ROWID */
+ int addrTop; /* Label "L" */
+ int addrIf; /* Address of jump to M */
+
srcTab = pParse->nTab++;
- sqlite3VdbeResolveLabel(v, iInsertBlock);
- sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
- sqlite3VdbeAddOp(v, OP_NewRowid, srcTab, 0);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
- sqlite3VdbeAddOp(v, OP_Insert, srcTab, OPFLAG_APPEND);
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
-
- /* The following code runs first because the GOTO at the very top
- ** of the program jumps to it. Create the temporary table, then jump
- ** back up and execute the SELECT code above.
- */
- sqlite3VdbeJumpHere(v, iInitCode);
- sqlite3VdbeAddOp(v, OP_OpenEphemeral, srcTab, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn);
- sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
- sqlite3VdbeResolveLabel(v, iCleanup);
- }else{
- sqlite3VdbeJumpHere(v, iInitCode);
+ regRec = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
+ addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
+ sqlite3VdbeJumpHere(v, addrIf);
+ sqlite3ReleaseTempReg(pParse, regRec);
+ sqlite3ReleaseTempReg(pParse, regRowid);
}
}else{
/* This is the case if the data for the INSERT is coming from a VALUES
@@ -515,7 +599,7 @@ void sqlite3Insert(
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
srcTab = -1;
- useTempTable = 0;
+ assert( useTempTable==0 );
nColumn = pList ? pList->nExpr : 0;
for(i=0; i<nColumn; i++){
if( sqlite3ExprResolveNames(&sNC, pList->a[i].pExpr) ){
@@ -592,41 +676,79 @@ void sqlite3Insert(
/* Open the temp table for FOR EACH ROW triggers
*/
if( triggers_exist ){
- sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
}
/* Initialize the count of rows to be inserted
*/
if( db->flags & SQLITE_CountRows ){
- iCntMem = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, iCntMem);
+ regRowCount = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
- /* Open tables and indices if there are no row triggers */
- if( !triggers_exist ){
- base = pParse->nTab;
- sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
+ /* If this is not a view, open the table and and all indices */
+ if( !isView ){
+ int nIdx;
+ int i;
+
+ baseCur = pParse->nTab;
+ nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite);
+ aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
+ if( aRegIdx==0 ){
+ goto insert_cleanup;
+ }
+ for(i=0; i<nIdx; i++){
+ aRegIdx[i] = ++pParse->nMem;
+ }
}
- /* If the data source is a temporary table, then we have to create
- ** a loop because there might be multiple rows of data. If the data
- ** source is a subroutine call from the SELECT statement, then we need
- ** to launch the SELECT statement processing.
- */
+ /* This is the top of the main insertion loop */
if( useTempTable ){
- iBreak = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp(v, OP_Rewind, srcTab, iBreak);
- iCont = sqlite3VdbeCurrentAddr(v);
+ /* This block codes the top of loop only. The complete loop is the
+ ** following pseudocode (template 4):
+ **
+ ** rewind temp table
+ ** C: loop over rows of intermediate table
+ ** transfer values form intermediate table into <table>
+ ** end loop
+ ** D: ...
+ */
+ addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
+ addrCont = sqlite3VdbeCurrentAddr(v);
}else if( pSelect ){
- sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
- sqlite3VdbeResolveLabel(v, iInsertBlock);
+ /* This block codes the top of loop only. The complete loop is the
+ ** following pseudocode (template 3):
+ **
+ ** C: yield X
+ ** if EOF goto D
+ ** insert the select result into <table> from R..R+n
+ ** goto C
+ ** D: ...
+ */
+ addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
+ addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof);
}
+ /* Allocate registers for holding the rowid of the new row,
+ ** the content of the new row, and the assemblied row record.
+ */
+ regRecord = ++pParse->nMem;
+ regRowid = regIns = pParse->nMem+1;
+ pParse->nMem += pTab->nCol + 1;
+ if( IsVirtual(pTab) ){
+ regRowid++;
+ pParse->nMem++;
+ }
+ regData = regRowid+1;
+
/* Run the BEFORE and INSTEAD OF triggers, if there are any
*/
endOfLoop = sqlite3VdbeMakeLabel(v);
if( triggers_exist & TRIGGER_BEFORE ){
+ int regRowid;
+ int regCols;
+ int regRec;
/* build the NEW.* reference row. Note that if there is an INTEGER
** PRIMARY KEY into which a NULL is being inserted, that NULL will be
@@ -634,17 +756,19 @@ void sqlite3Insert(
** we do not know what the unique ID will be (because the insert has
** not happened yet) so we substitute a rowid of -1
*/
+ regRowid = sqlite3GetTempReg(pParse);
if( keyColumn<0 ){
- sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid);
}else if( useTempTable ){
- sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid);
}else{
+ int j1;
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
- sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
- sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
}
/* Cannot have triggers on a virtual table. If it were possible,
@@ -654,6 +778,7 @@ void sqlite3Insert(
/* Create the new column data
*/
+ regCols = sqlite3GetTempRange(pParse, pTab->nCol);
for(i=0; i<pTab->nCol; i++){
if( pColumn==0 ){
j = i;
@@ -663,15 +788,16 @@ void sqlite3Insert(
}
}
if( pColumn && j>=pColumn->nId ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i);
}else if( useTempTable ){
- sqlite3VdbeAddOp(v, OP_Column, srcTab, j);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i);
}else{
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr);
+ sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i);
}
}
- sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ regRec = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRec);
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
** do not attempt any conversions before assembling the record.
@@ -681,23 +807,18 @@ void sqlite3Insert(
if( !isView ){
sqlite3TableAffinityStr(v, pTab);
}
- sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0);
+ sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
+ sqlite3ReleaseTempReg(pParse, regRec);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
/* Fire BEFORE or INSTEAD OF triggers */
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab,
- newIdx, -1, onError, endOfLoop) ){
+ newIdx, -1, onError, endOfLoop, 0, 0) ){
goto insert_cleanup;
}
}
- /* If any triggers exists, the opening of tables and indices is deferred
- ** until now.
- */
- if( triggers_exist && !isView ){
- base = pParse->nTab;
- sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
- }
-
/* Push the record number for the new entry onto the stack. The
** record number is a randomly generate integer created by NewRowid
** except when the table has an INTEGER PRIMARY KEY column, in which
@@ -705,53 +826,61 @@ void sqlite3Insert(
*/
if( !isView ){
if( IsVirtual(pTab) ){
- /* The row that the VUpdate opcode will delete: none */
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ /* The row that the VUpdate opcode will delete: none */
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regIns);
}
if( keyColumn>=0 ){
if( useTempTable ){
- sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid);
}else if( pSelect ){
- sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid);
}else{
VdbeOp *pOp;
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
+ sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
pOp = sqlite3VdbeGetOp(v, sqlite3VdbeCurrentAddr(v) - 1);
- if( pOp && pOp->opcode==OP_Null ){
+ if( pOp && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
appendFlag = 1;
pOp->opcode = OP_NewRowid;
- pOp->p1 = base;
- pOp->p2 = counterMem;
+ pOp->p1 = baseCur;
+ pOp->p2 = regRowid;
+ pOp->p3 = regAutoinc;
}
}
/* If the PRIMARY KEY expression is NULL, then use OP_NewRowid
** to generate a unique primary key value.
*/
if( !appendFlag ){
- sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_NewRowid, base, counterMem);
- sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ int j1;
+ if( !IsVirtual(pTab) ){
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
+ sqlite3VdbeJumpHere(v, j1);
+ }else{
+ j1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2);
+ }
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
}
}else if( IsVirtual(pTab) ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid);
}else{
- sqlite3VdbeAddOp(v, OP_NewRowid, base, counterMem);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
appendFlag = 1;
}
- autoIncStep(pParse, counterMem);
+ autoIncStep(pParse, regAutoinc, regRowid);
/* Push onto the stack, data for all columns of the new entry, beginning
** with the first column.
*/
nHidden = 0;
for(i=0; i<pTab->nCol; i++){
+ int iRegStore = regRowid+1+i;
if( i==pTab->iPKey ){
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
** Whenever this column is read, the record number will be substituted
** in its place. So will fill this column with a NULL to avoid
** taking up data space with information that will never be used. */
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore);
continue;
}
if( pColumn==0 ){
@@ -768,13 +897,13 @@ void sqlite3Insert(
}
}
if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore);
}else if( useTempTable ){
- sqlite3VdbeAddOp(v, OP_Column, srcTab, j);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
}else if( pSelect ){
- sqlite3VdbeAddOp(v, OP_Dup, i+nColumn-j+IsVirtual(pTab), 1);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
}else{
- sqlite3ExprCode(pParse, pList->a[j].pExpr);
+ sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
}
}
@@ -783,68 +912,77 @@ void sqlite3Insert(
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
- pParse->pVirtualLock = pTab;
- sqlite3VdbeOp3(v, OP_VUpdate, 1, pTab->nCol+2,
- (const char*)pTab->pVtab, P3_VTAB);
+ sqlite3VtabMakeWritable(pParse, pTab);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns,
+ (const char*)pTab->pVtab, P4_VTAB);
}else
#endif
{
- sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
- 0, onError, endOfLoop);
- sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0,
- (triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
- appendFlag);
+ sqlite3GenerateConstraintChecks(
+ pParse,
+ pTab,
+ baseCur,
+ regIns,
+ aRegIdx,
+ keyColumn>=0,
+ 0,
+ onError,
+ endOfLoop
+ );
+ sqlite3CompleteInsertion(
+ pParse,
+ pTab,
+ baseCur,
+ regIns,
+ aRegIdx,
+ 0,
+ 0,
+ (triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
+ appendFlag
+ );
}
}
/* Update the count of rows that are inserted
*/
if( (db->flags & SQLITE_CountRows)!=0 ){
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, iCntMem);
+ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
if( triggers_exist ){
- /* Close all tables opened */
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Close, base, 0);
- for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
- }
- }
-
/* Code AFTER triggers */
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab,
- newIdx, -1, onError, endOfLoop) ){
+ newIdx, -1, onError, endOfLoop, 0, 0) ){
goto insert_cleanup;
}
}
- /* The bottom of the loop, if the data source is a SELECT statement
+ /* The bottom of the main insertion loop, if the data source
+ ** is a SELECT statement.
*/
sqlite3VdbeResolveLabel(v, endOfLoop);
if( useTempTable ){
- sqlite3VdbeAddOp(v, OP_Next, srcTab, iCont);
- sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp(v, OP_Close, srcTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont);
+ sqlite3VdbeJumpHere(v, addrInsTop);
+ sqlite3VdbeAddOp1(v, OP_Close, srcTab);
}else if( pSelect ){
- sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
- sqlite3VdbeResolveLabel(v, iCleanup);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrCont);
+ sqlite3VdbeJumpHere(v, addrInsTop);
}
- if( !triggers_exist && !IsVirtual(pTab) ){
+ if( !IsVirtual(pTab) && !isView ){
/* Close all tables opened */
- sqlite3VdbeAddOp(v, OP_Close, base, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, baseCur);
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur);
}
}
/* Update the sqlite_sequence table by storing the content of the
- ** counter value in memory counterMem back into the sqlite_sequence
+ ** counter value in memory regAutoinc back into the sqlite_sequence
** table.
*/
- autoIncEnd(pParse, iDb, pTab, counterMem);
+ autoIncEnd(pParse, iDb, pTab, regAutoinc);
/*
** Return the number of rows inserted. If this routine is
@@ -852,28 +990,27 @@ void sqlite3Insert(
** invoke the callback function.
*/
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
- sqlite3VdbeAddOp(v, OP_MemLoad, iCntMem, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", P4_STATIC);
}
insert_cleanup:
- sqlite3SrcListDelete(pTabList);
- sqlite3ExprListDelete(pList);
- sqlite3SelectDelete(pSelect);
- sqlite3IdListDelete(pColumn);
+ sqlite3SrcListDelete(db, pTabList);
+ sqlite3ExprListDelete(db, pList);
+ sqlite3SelectDelete(db, pSelect);
+ sqlite3IdListDelete(db, pColumn);
+ sqlite3DbFree(db, aRegIdx);
}
/*
-** Generate code to do a constraint check prior to an INSERT or an UPDATE.
+** Generate code to do constraint checks prior to an INSERT or an UPDATE.
**
-** When this routine is called, the stack contains (from bottom to top)
-** the following values:
+** The input is a range of consecutive registers as follows:
**
** 1. The rowid of the row to be updated before the update. This
** value is omitted unless we are doing an UPDATE that involves a
-** change to the record number.
+** change to the record number or writing to a virtual table.
**
** 2. The rowid of the row after the update.
**
@@ -883,15 +1020,20 @@ insert_cleanup:
**
** N. The data in the last column of the entry after the update.
**
+** The regRowid parameter is the index of the register containing (2).
+**
** The old rowid shown as entry (1) above is omitted unless both isUpdate
** and rowidChng are 1. isUpdate is true for UPDATEs and false for
-** INSERTs and rowidChng is true if the record number is being changed.
+** INSERTs. RowidChng means that the new rowid is explicitly specified by
+** the update or insert statement. If rowidChng is false, it means that
+** the rowid is computed automatically in an insert or that the rowid value
+** is not modified by the update.
**
-** The code generated by this routine pushes additional entries onto
-** the stack which are the keys for new index entries for the new record.
-** The order of index keys is the same as the order of the indices on
-** the pTable->pIndex list. A key is only created for index i if
-** aIdxUsed!=0 and aIdxUsed[i]!=0.
+** The code generated by this routine store new index entries into
+** registers identified by aRegIdx[]. No index entry is created for
+** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is
+** the same as the order of indices on the linked list of indices
+** attached to the table.
**
** This routine also generates code to check constraints. NOT NULL,
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
@@ -933,23 +1075,18 @@ insert_cleanup:
** for the constraint is used.
**
** The calling routine must open a read/write cursor for pTab with
-** cursor number "base". All indices of pTab must also have open
-** read/write cursors with cursor number base+i for the i-th cursor.
+** cursor number "baseCur". All indices of pTab must also have open
+** read/write cursors with cursor number baseCur+i for the i-th cursor.
** Except, if there is no possibility of a REPLACE action then
-** cursors do not need to be open for indices where aIdxUsed[i]==0.
-**
-** If the isUpdate flag is true, it means that the "base" cursor is
-** initially pointing to an entry that is being updated. The isUpdate
-** flag causes extra code to be generated so that the "base" cursor
-** is still pointing at the same entry after the routine returns.
-** Without the isUpdate flag, the "base" cursor might be moved.
+** cursors do not need to be open for indices where aRegIdx[i]==0.
*/
void sqlite3GenerateConstraintChecks(
Parse *pParse, /* The parser context */
Table *pTab, /* the table into which we are inserting */
- int base, /* Index of a read/write cursor pointing at pTab */
- char *aIdxUsed, /* Which indices are used. NULL means all are used */
- int rowidChng, /* True if the record number will change */
+ int baseCur, /* Index of a read/write cursor pointing at pTab */
+ int regRowid, /* Index of the range of input registers */
+ int *aRegIdx, /* Register used by each index. 0 for unused indices */
+ int rowidChng, /* True if the rowid might collide with existing entry */
int isUpdate, /* True for UPDATE, False for INSERT */
int overrideError, /* Override onError to this if not OE_Default */
int ignoreDest /* Jump to this label on an OE_Ignore resolution */
@@ -958,18 +1095,19 @@ void sqlite3GenerateConstraintChecks(
Vdbe *v;
int nCol;
int onError;
- int addr;
- int extra;
+ int j1, j2, j3; /* Addresses of jump instructions */
+ int regData; /* Register containing first data column */
int iCur;
Index *pIdx;
int seenReplace = 0;
- int jumpInst1=0, jumpInst2;
int hasTwoRowids = (isUpdate && rowidChng);
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
+ regData = regRowid + 1;
+
/* Test all NOT NULL constraints.
*/
@@ -987,33 +1125,30 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
onError = OE_Abort;
}
- sqlite3VdbeAddOp(v, OP_Dup, nCol-1-i, 1);
- addr = sqlite3VdbeAddOp(v, OP_NotNull, 1, 0);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|| onError==OE_Ignore || onError==OE_Replace );
switch( onError ){
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- char *zMsg = 0;
- sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
- sqlite3SetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName,
- " may not be NULL", (char*)0);
- sqlite3VdbeChangeP3(v, -1, zMsg, P3_DYNAMIC);
+ char *zMsg;
+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError);
+ zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",
+ pTab->zName, pTab->aCol[i].zName);
+ sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
break;
}
case OE_Ignore: {
- sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRowids, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
break;
}
case OE_Replace: {
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt);
- sqlite3VdbeAddOp(v, OP_Push, nCol-i, 0);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
break;
}
}
- sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeJumpHere(v, j1);
}
/* Test all CHECK constraints
@@ -1021,17 +1156,13 @@ void sqlite3GenerateConstraintChecks(
#ifndef SQLITE_OMIT_CHECK
if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){
int allOk = sqlite3VdbeMakeLabel(v);
- assert( pParse->ckOffset==0 );
- pParse->ckOffset = nCol;
- sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, 1);
- assert( pParse->ckOffset==nCol );
- pParse->ckOffset = 0;
+ pParse->ckBase = regData;
+ sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL);
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
if( onError==OE_Ignore ){
- sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRowids, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
- sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError);
}
sqlite3VdbeResolveLabel(v, allOk);
}
@@ -1049,70 +1180,66 @@ void sqlite3GenerateConstraintChecks(
onError = OE_Abort;
}
- if( isUpdate ){
- sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
- sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
- jumpInst1 = sqlite3VdbeAddOp(v, OP_Eq, 0, 0);
- }
- sqlite3VdbeAddOp(v, OP_Dup, nCol, 1);
- jumpInst2 = sqlite3VdbeAddOp(v, OP_NotExists, base, 0);
- switch( onError ){
- default: {
- onError = OE_Abort;
- /* Fall thru into the next case */
- }
- case OE_Rollback:
- case OE_Abort:
- case OE_Fail: {
- sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError,
- "PRIMARY KEY must be unique", P3_STATIC);
- break;
+ if( onError!=OE_Replace || pTab->pIndex ){
+ if( isUpdate ){
+ j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, regRowid-1);
}
- case OE_Replace: {
- sqlite3GenerateRowIndexDelete(v, pTab, base, 0);
- if( isUpdate ){
- sqlite3VdbeAddOp(v, OP_Dup, nCol+hasTwoRowids, 1);
- sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
+ j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
+ switch( onError ){
+ default: {
+ onError = OE_Abort;
+ /* Fall thru into the next case */
+ }
+ case OE_Rollback:
+ case OE_Abort:
+ case OE_Fail: {
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0,
+ "PRIMARY KEY must be unique", P4_STATIC);
+ break;
+ }
+ case OE_Replace: {
+ sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0);
+ seenReplace = 1;
+ break;
+ }
+ case OE_Ignore: {
+ assert( seenReplace==0 );
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ break;
}
- seenReplace = 1;
- break;
}
- case OE_Ignore: {
- assert( seenReplace==0 );
- sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRowids, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
- break;
+ sqlite3VdbeJumpHere(v, j3);
+ if( isUpdate ){
+ sqlite3VdbeJumpHere(v, j2);
}
}
- sqlite3VdbeJumpHere(v, jumpInst2);
- if( isUpdate ){
- sqlite3VdbeJumpHere(v, jumpInst1);
- sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
- sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
- }
}
/* Test all UNIQUE constraints by creating entries for each UNIQUE
** index and making sure that duplicate entries do not already exist.
** Add the new records to the indices as we go.
*/
- extra = -1;
for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
- if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */
- extra++;
+ int regIdx;
+ int regR;
+
+ if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */
/* Create a key for accessing the index entry */
- sqlite3VdbeAddOp(v, OP_Dup, nCol+extra, 1);
+ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1);
for(i=0; i<pIdx->nColumn; i++){
int idx = pIdx->aiColumn[i];
if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
}else{
- sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i);
}
}
- jumpInst1 = sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
sqlite3IndexAffinityStr(v, pIdx);
+ sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
/* Find out what action to take in case there is an indexing conflict */
onError = pIdx->onError;
@@ -1129,8 +1256,12 @@ void sqlite3GenerateConstraintChecks(
/* Check to see if the new index entry will be unique */
- sqlite3VdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRowids, 1);
- jumpInst2 = sqlite3VdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
+ j2 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdx, 0, pIdx->nColumn);
+ regR = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR);
+ j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
+ regR, SQLITE_INT_TO_PTR(aRegIdx[iCur]),
+ P4_INT32);
/* Generate code that executes if the new index entry is not unique */
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
@@ -1162,38 +1293,31 @@ void sqlite3GenerateConstraintChecks(
}
sqlite3_snprintf(sizeof(zErrMsg)-n1, &zErrMsg[n1],
pIdx->nColumn>1 ? " are not unique" : " is not unique");
- sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErrMsg,0);
break;
}
case OE_Ignore: {
assert( seenReplace==0 );
- sqlite3VdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRowids, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
break;
}
case OE_Replace: {
- sqlite3GenerateRowDelete(pParse->db, v, pTab, base, 0);
- if( isUpdate ){
- sqlite3VdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRowids, 1);
- sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
- }
+ sqlite3GenerateRowDelete(pParse, pTab, baseCur, regR, 0);
seenReplace = 1;
break;
}
}
-#if NULL_DISTINCT_FOR_UNIQUE
- sqlite3VdbeJumpHere(v, jumpInst1);
-#endif
- sqlite3VdbeJumpHere(v, jumpInst2);
+ sqlite3VdbeJumpHere(v, j2);
+ sqlite3VdbeJumpHere(v, j3);
+ sqlite3ReleaseTempReg(pParse, regR);
}
}
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
-** The stack must contain keys for all active indices followed by data
-** and the rowid for the new entry. This routine creates the new
-** entries in all indices and in the main table.
+** A consecutive range of registers starting at regRowid contains the
+** rowid and the content to be inserted.
**
** The arguments to this routine should be the same as the first six
** arguments to sqlite3GenerateConstraintChecks.
@@ -1201,8 +1325,9 @@ void sqlite3GenerateConstraintChecks(
void sqlite3CompleteInsertion(
Parse *pParse, /* The parser context */
Table *pTab, /* the table into which we are inserting */
- int base, /* Index of a read/write cursor pointing at pTab */
- char *aIdxUsed, /* Which indices are used. NULL means all are used */
+ int baseCur, /* Index of a read/write cursor pointing at pTab */
+ int regRowid, /* Range of content */
+ int *aRegIdx, /* Register used by each index. 0 for unused indices */
int rowidChng, /* True if the record number will change */
int isUpdate, /* True for UPDATE, False for INSERT */
int newIdx, /* Index of NEW table for triggers. -1 if none */
@@ -1213,22 +1338,25 @@ void sqlite3CompleteInsertion(
int nIdx;
Index *pIdx;
int pik_flags;
+ int regData;
+ int regRec;
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
for(i=nIdx-1; i>=0; i--){
- if( aIdxUsed && aIdxUsed[i]==0 ) continue;
- sqlite3VdbeAddOp(v, OP_IdxInsert, base+i+1, 0);
+ if( aRegIdx[i]==0 ) continue;
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
}
- sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ regData = regRowid + 1;
+ regRec = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
sqlite3TableAffinityStr(v, pTab);
+ sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
#ifndef SQLITE_OMIT_TRIGGER
if( newIdx>=0 ){
- sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
- sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
- sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0);
+ sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
}
#endif
if( pParse->nested ){
@@ -1240,25 +1368,24 @@ void sqlite3CompleteInsertion(
if( appendBias ){
pik_flags |= OPFLAG_APPEND;
}
- sqlite3VdbeAddOp(v, OP_Insert, base, pik_flags);
+ sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
if( !pParse->nested ){
- sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
- }
-
- if( isUpdate && rowidChng ){
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
}
+ sqlite3VdbeChangeP5(v, pik_flags);
}
/*
** Generate code that will open cursors for a table and for all
-** indices of that table. The "base" parameter is the cursor number used
+** indices of that table. The "baseCur" parameter is the cursor number used
** for the table. Indices are opened on subsequent cursors.
+**
+** Return the number of indices on the table.
*/
-void sqlite3OpenTableAndIndices(
+int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
- int base, /* Cursor number assigned to the table */
+ int baseCur, /* Cursor number assigned to the table */
int op /* OP_OpenRead or OP_OpenWrite */
){
int i;
@@ -1266,21 +1393,22 @@ void sqlite3OpenTableAndIndices(
Index *pIdx;
Vdbe *v;
- if( IsVirtual(pTab) ) return;
+ if( IsVirtual(pTab) ) return 0;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
- sqlite3OpenTable(pParse, base, iDb, pTab, op);
+ sqlite3OpenTable(pParse, baseCur, iDb, pTab, op);
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- VdbeComment((v, "# %s", pIdx->zName));
- sqlite3VdbeOp3(v, op, i+base, pIdx->tnum, (char*)pKey, P3_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb,
+ (char*)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pIdx->zName));
}
- if( pParse->nTab<=base+i ){
- pParse->nTab = base+i;
+ if( pParse->nTab<=baseCur+i ){
+ pParse->nTab = baseCur+i;
}
+ return i-1;
}
@@ -1338,7 +1466,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
return 0; /* Different sort orders */
}
if( pSrc->azColl[i]!=pDest->azColl[i] ){
- return 0; /* Different sort orders */
+ return 0; /* Different collating sequences */
}
}
@@ -1396,8 +1524,9 @@ static int xferOptimization(
int emptySrcTest; /* Address of test for empty pSrc */
Vdbe *v; /* The VDBE we are building */
KeyInfo *pKey; /* Key information for an index */
- int counterMem; /* Memory register used by AUTOINC */
+ int regAutoinc; /* Memory register used by AUTOINC */
int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */
+ int regData, regRowid; /* Registers holding data and rowid */
if( pSelect==0 ){
return 0; /* Must be of the form INSERT INTO ... SELECT ... */
@@ -1416,9 +1545,7 @@ static int xferOptimization(
if( onError!=OE_Abort && onError!=OE_Rollback ){
return 0; /* Cannot do OR REPLACE or OR IGNORE or OR FAIL */
}
- if( pSelect->pSrc==0 ){
- return 0; /* SELECT must have a FROM clause */
- }
+ assert(pSelect->pSrc); /* allocated even if there is no FROM clause */
if( pSelect->pSrc->nSrc!=1 ){
return 0; /* FROM clause must have exactly one term */
}
@@ -1461,7 +1588,7 @@ static int xferOptimization(
** we have to check the semantics.
*/
pItem = pSelect->pSrc->a;
- pSrc = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase);
+ pSrc = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
if( pSrc==0 ){
return 0; /* FROM clause does not contain a real table */
}
@@ -1523,9 +1650,10 @@ static int xferOptimization(
#endif
iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema);
v = sqlite3GetVdbe(pParse);
+ sqlite3CodeVerifySchema(pParse, iDbSrc);
iSrc = pParse->nTab++;
iDest = pParse->nTab++;
- counterMem = autoIncBegin(pParse, iDbDest, pDest);
+ regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
if( (pDest->iPKey<0 && pDest->pIndex!=0) || destHasUniqueIdx ){
/* If tables do not have an INTEGER PRIMARY KEY and there
@@ -1538,67 +1666,73 @@ static int xferOptimization(
** insure that all entries in the union of DEST and SRC will be
** unique.
*/
- addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iDest, 0);
- emptyDestTest = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);
+ emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
sqlite3VdbeJumpHere(v, addr1);
}else{
emptyDestTest = 0;
}
sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
- emptySrcTest = sqlite3VdbeAddOp(v, OP_Rewind, iSrc, 0);
+ emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
+ regData = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
if( pDest->iPKey>=0 ){
- addr1 = sqlite3VdbeAddOp(v, OP_Rowid, iSrc, 0);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- addr2 = sqlite3VdbeAddOp(v, OP_NotExists, iDest, 0);
- sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError,
- "PRIMARY KEY must be unique", P3_STATIC);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0,
+ "PRIMARY KEY must be unique", P4_STATIC);
sqlite3VdbeJumpHere(v, addr2);
- autoIncStep(pParse, counterMem);
+ autoIncStep(pParse, regAutoinc, regRowid);
}else if( pDest->pIndex==0 ){
- addr1 = sqlite3VdbeAddOp(v, OP_NewRowid, iDest, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
}else{
- addr1 = sqlite3VdbeAddOp(v, OP_Rowid, iSrc, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
assert( pDest->autoInc==0 );
}
- sqlite3VdbeAddOp(v, OP_RowData, iSrc, 0);
- sqlite3VdbeOp3(v, OP_Insert, iDest,
- OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND,
- pDest->zName, 0);
- sqlite3VdbeAddOp(v, OP_Next, iSrc, addr1);
- autoIncEnd(pParse, iDbDest, pDest, counterMem);
+ sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
+ sqlite3VdbeChangeP4(v, -1, pDest->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1);
+ autoIncEnd(pParse, iDbDest, pDest, regAutoinc);
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
for(pSrcIdx=pSrc->pIndex; pSrcIdx; pSrcIdx=pSrcIdx->pNext){
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
}
assert( pSrcIdx );
- sqlite3VdbeAddOp(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp(v, OP_Close, iDest, 0);
- sqlite3VdbeAddOp(v, OP_Integer, iDbSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx);
- VdbeComment((v, "# %s", pSrcIdx->zName));
- sqlite3VdbeOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum,
- (char*)pKey, P3_KEYINFO_HANDOFF);
- sqlite3VdbeAddOp(v, OP_Integer, iDbDest, 0);
+ sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc,
+ (char*)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pSrcIdx->zName));
pKey = sqlite3IndexKeyinfo(pParse, pDestIdx);
- VdbeComment((v, "# %s", pDestIdx->zName));
- sqlite3VdbeOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum,
- (char*)pKey, P3_KEYINFO_HANDOFF);
- addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iSrc, 0);
- sqlite3VdbeAddOp(v, OP_RowKey, iSrc, 0);
- sqlite3VdbeAddOp(v, OP_IdxInsert, iDest, 1);
- sqlite3VdbeAddOp(v, OP_Next, iSrc, addr1+1);
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest,
+ (char*)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pDestIdx->zName));
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
+ sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1);
sqlite3VdbeJumpHere(v, addr1);
}
sqlite3VdbeJumpHere(v, emptySrcTest);
- sqlite3VdbeAddOp(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp(v, OP_Close, iDest, 0);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ sqlite3ReleaseTempReg(pParse, regData);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
if( emptyDestTest ){
- sqlite3VdbeAddOp(v, OP_Halt, SQLITE_OK, 0);
+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
sqlite3VdbeJumpHere(v, emptyDestTest);
- sqlite3VdbeAddOp(v, OP_Close, iDest, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
return 0;
}else{
return 1;
}
}
#endif /* SQLITE_OMIT_XFER_OPT */
+
+/* Make sure "isView" gets undefined in case this file becomes part of
+** the amalgamation - so that subsequent files do not see isView as a
+** macro. */
+#undef isView
diff --git a/third_party/sqlite/src/journal.c b/third_party/sqlite/src/journal.c
new file mode 100755
index 0000000..bdc36bc
--- /dev/null
+++ b/third_party/sqlite/src/journal.c
@@ -0,0 +1,239 @@
+/*
+** 2007 August 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.
+**
+*************************************************************************
+**
+** @(#) $Id: journal.c,v 1.8 2008/05/01 18:01:47 drh Exp $
+*/
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+
+/*
+** This file implements a special kind of sqlite3_file object used
+** by SQLite to create journal files if the atomic-write optimization
+** is enabled.
+**
+** The distinctive characteristic of this sqlite3_file is that the
+** actual on disk file is created lazily. When the file is created,
+** the caller specifies a buffer size for an in-memory buffer to
+** be used to service read() and write() requests. The actual file
+** on disk is not created or populated until either:
+**
+** 1) The in-memory representation grows too large for the allocated
+** buffer, or
+** 2) The xSync() method is called.
+*/
+
+#include "sqliteInt.h"
+
+
+/*
+** A JournalFile object is a subclass of sqlite3_file used by
+** as an open file handle for journal files.
+*/
+struct JournalFile {
+ sqlite3_io_methods *pMethod; /* I/O methods on journal files */
+ int nBuf; /* Size of zBuf[] in bytes */
+ char *zBuf; /* Space to buffer journal writes */
+ int iSize; /* Amount of zBuf[] currently used */
+ int flags; /* xOpen flags */
+ sqlite3_vfs *pVfs; /* The "real" underlying VFS */
+ sqlite3_file *pReal; /* The "real" underlying file descriptor */
+ const char *zJournal; /* Name of the journal file */
+};
+typedef struct JournalFile JournalFile;
+
+/*
+** If it does not already exists, create and populate the on-disk file
+** for JournalFile p.
+*/
+static int createFile(JournalFile *p){
+ int rc = SQLITE_OK;
+ if( !p->pReal ){
+ sqlite3_file *pReal = (sqlite3_file *)&p[1];
+ rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
+ if( rc==SQLITE_OK ){
+ p->pReal = pReal;
+ if( p->iSize>0 ){
+ assert(p->iSize<=p->nBuf);
+ rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Close the file.
+*/
+static int jrnlClose(sqlite3_file *pJfd){
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ sqlite3OsClose(p->pReal);
+ }
+ sqlite3_free(p->zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from the file.
+*/
+static int jrnlRead(
+ sqlite3_file *pJfd, /* The journal file from which to read */
+ void *zBuf, /* Put the results here */
+ int iAmt, /* Number of bytes to read */
+ sqlite_int64 iOfst /* Begin reading at this offset */
+){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+ }else{
+ assert( iAmt+iOfst<=p->iSize );
+ memcpy(zBuf, &p->zBuf[iOfst], iAmt);
+ }
+ return rc;
+}
+
+/*
+** Write data to the file.
+*/
+static int jrnlWrite(
+ sqlite3_file *pJfd, /* The journal file into which to write */
+ const void *zBuf, /* Take data to be written from here */
+ int iAmt, /* Number of bytes to write */
+ sqlite_int64 iOfst /* Begin writing at this offset into the file */
+){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
+ rc = createFile(p);
+ }
+ if( rc==SQLITE_OK ){
+ if( p->pReal ){
+ rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+ }else{
+ memcpy(&p->zBuf[iOfst], zBuf, iAmt);
+ if( p->iSize<(iOfst+iAmt) ){
+ p->iSize = (iOfst+iAmt);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Truncate the file.
+*/
+static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsTruncate(p->pReal, size);
+ }else if( size<p->iSize ){
+ p->iSize = size;
+ }
+ return rc;
+}
+
+/*
+** Sync the file.
+*/
+static int jrnlSync(sqlite3_file *pJfd, int flags){
+ int rc;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsSync(p->pReal, flags);
+ }else{
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Query the size of the file in bytes.
+*/
+static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
+ int rc = SQLITE_OK;
+ JournalFile *p = (JournalFile *)pJfd;
+ if( p->pReal ){
+ rc = sqlite3OsFileSize(p->pReal, pSize);
+ }else{
+ *pSize = (sqlite_int64) p->iSize;
+ }
+ return rc;
+}
+
+/*
+** Table of methods for JournalFile sqlite3_file object.
+*/
+static struct sqlite3_io_methods JournalFileMethods = {
+ 1, /* iVersion */
+ jrnlClose, /* xClose */
+ jrnlRead, /* xRead */
+ jrnlWrite, /* xWrite */
+ jrnlTruncate, /* xTruncate */
+ jrnlSync, /* xSync */
+ jrnlFileSize, /* xFileSize */
+ 0, /* xLock */
+ 0, /* xUnlock */
+ 0, /* xCheckReservedLock */
+ 0, /* xFileControl */
+ 0, /* xSectorSize */
+ 0 /* xDeviceCharacteristics */
+};
+
+/*
+** Open a journal file.
+*/
+int sqlite3JournalOpen(
+ sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
+ const char *zName, /* Name of the journal file */
+ sqlite3_file *pJfd, /* Preallocated, blank file handle */
+ int flags, /* Opening flags */
+ int nBuf /* Bytes buffered before opening the file */
+){
+ JournalFile *p = (JournalFile *)pJfd;
+ memset(p, 0, sqlite3JournalSize(pVfs));
+ if( nBuf>0 ){
+ p->zBuf = sqlite3MallocZero(nBuf);
+ if( !p->zBuf ){
+ return SQLITE_NOMEM;
+ }
+ }else{
+ return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ }
+ p->pMethod = &JournalFileMethods;
+ p->nBuf = nBuf;
+ p->flags = flags;
+ p->zJournal = zName;
+ p->pVfs = pVfs;
+ return SQLITE_OK;
+}
+
+/*
+** If the argument p points to a JournalFile structure, and the underlying
+** file has not yet been created, create it now.
+*/
+int sqlite3JournalCreate(sqlite3_file *p){
+ if( p->pMethods!=&JournalFileMethods ){
+ return SQLITE_OK;
+ }
+ return createFile((JournalFile *)p);
+}
+
+/*
+** Return the number of bytes required to store a JournalFile that uses vfs
+** pVfs to create the underlying on-disk files.
+*/
+int sqlite3JournalSize(sqlite3_vfs *pVfs){
+ return (pVfs->szOsFile+sizeof(JournalFile));
+}
+#endif
diff --git a/third_party/sqlite/legacy.c b/third_party/sqlite/src/legacy.c
index 8d96b1e..e6b75c0 100644..100755
--- a/third_party/sqlite/legacy.c
+++ b/third_party/sqlite/src/legacy.c
@@ -14,11 +14,10 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: legacy.c,v 1.18 2007/05/04 13:15:56 drh Exp $
+** $Id: legacy.c,v 1.29 2008/08/02 03:50:39 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
/*
@@ -46,7 +45,10 @@ int sqlite3_exec(
int nRetry = 0;
int nCallback;
- if( zSql==0 ) return SQLITE_OK;
+ if( zSql==0 ) zSql = "";
+
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3Error(db, SQLITE_OK, 0);
while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
int nCol;
char **azVals = 0;
@@ -64,12 +66,7 @@ int sqlite3_exec(
}
nCallback = 0;
-
nCol = sqlite3_column_count(pStmt);
- azCols = sqliteMalloc(2*nCol*sizeof(const char *) + 1);
- if( azCols==0 ){
- goto exec_out;
- }
while( 1 ){
int i;
@@ -79,8 +76,17 @@ int sqlite3_exec(
if( xCallback && (SQLITE_ROW==rc ||
(SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){
if( 0==nCallback ){
+ if( azCols==0 ){
+ azCols = sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1);
+ if( azCols==0 ){
+ goto exec_out;
+ }
+ }
for(i=0; i<nCol; i++){
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
+ /* sqlite3VdbeSetColName() installs column names as UTF8
+ ** strings so there is no way for sqlite3_column_name() to fail. */
+ assert( azCols[i]!=0 );
}
nCallback++;
}
@@ -88,10 +94,17 @@ int sqlite3_exec(
azVals = &azCols[nCol];
for(i=0; i<nCol; i++){
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
+ if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
+ db->mallocFailed = 1;
+ goto exec_out;
+ }
}
}
if( xCallback(pArg, nCol, azVals, azCols) ){
rc = SQLITE_ABORT;
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ sqlite3Error(db, SQLITE_ABORT, 0);
goto exec_out;
}
}
@@ -108,18 +121,18 @@ int sqlite3_exec(
}
}
- sqliteFree(azCols);
+ sqlite3DbFree(db, azCols);
azCols = 0;
}
exec_out:
if( pStmt ) sqlite3_finalize(pStmt);
- if( azCols ) sqliteFree(azCols);
+ sqlite3DbFree(db, azCols);
- rc = sqlite3ApiExit(0, rc);
+ rc = sqlite3ApiExit(db, rc);
if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
int nErrMsg = 1 + strlen(sqlite3_errmsg(db));
- *pzErrMsg = sqlite3_malloc(nErrMsg);
+ *pzErrMsg = sqlite3Malloc(nErrMsg);
if( *pzErrMsg ){
memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);
}
@@ -128,5 +141,6 @@ exec_out:
}
assert( (rc&db->errMask)==rc );
+ sqlite3_mutex_leave(db->mutex);
return rc;
}
diff --git a/third_party/sqlite/loadext.c b/third_party/sqlite/src/loadext.c
index b2f4726..2f7ca52 100644..100755
--- a/third_party/sqlite/loadext.c
+++ b/third_party/sqlite/src/loadext.c
@@ -11,16 +11,20 @@
*************************************************************************
** This file contains code used to dynamically load extensions into
** the SQLite library.
+**
+** $Id: loadext.c,v 1.53 2008/08/02 03:50:39 drh Exp $
*/
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-#define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
+#ifndef SQLITE_CORE
+ #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
+#endif
#include "sqlite3ext.h"
#include "sqliteInt.h"
-#include "os.h"
#include <string.h>
#include <ctype.h>
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+
/*
** Some API routines are omitted when various features are
** excluded from a build of SQLite. Substitute a NULL pointer
@@ -94,6 +98,15 @@
# define sqlite3_get_table 0
#endif
+#ifdef SQLITE_OMIT_INCRBLOB
+#define sqlite3_bind_zeroblob 0
+#define sqlite3_blob_bytes 0
+#define sqlite3_blob_close 0
+#define sqlite3_blob_open 0
+#define sqlite3_blob_read 0
+#define sqlite3_blob_write 0
+#endif
+
/*
** The following structure contains pointers to all SQLite API routines.
** A pointer to this structure is passed into extensions when they are
@@ -109,7 +122,7 @@
** also check to make sure that the pointer to the function is
** not NULL before calling it.
*/
-const sqlite3_api_routines sqlite3_apis = {
+static const sqlite3_api_routines sqlite3Apis = {
sqlite3_aggregate_context,
sqlite3_aggregate_count,
sqlite3_bind_blob,
@@ -247,6 +260,60 @@ const sqlite3_api_routines sqlite3_apis = {
*/
sqlite3_create_module_v2,
+ /*
+ ** Added for 3.5.0
+ */
+ sqlite3_bind_zeroblob,
+ sqlite3_blob_bytes,
+ sqlite3_blob_close,
+ sqlite3_blob_open,
+ sqlite3_blob_read,
+ sqlite3_blob_write,
+ sqlite3_create_collation_v2,
+ sqlite3_file_control,
+ sqlite3_memory_highwater,
+ sqlite3_memory_used,
+#ifdef SQLITE_MUTEX_NOOP
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+#else
+ sqlite3_mutex_alloc,
+ sqlite3_mutex_enter,
+ sqlite3_mutex_free,
+ sqlite3_mutex_leave,
+ sqlite3_mutex_try,
+#endif
+ sqlite3_open_v2,
+ sqlite3_release_memory,
+ sqlite3_result_error_nomem,
+ sqlite3_result_error_toobig,
+ sqlite3_sleep,
+ sqlite3_soft_heap_limit,
+ sqlite3_vfs_find,
+ sqlite3_vfs_register,
+ sqlite3_vfs_unregister,
+
+ /*
+ ** Added for 3.5.8
+ */
+ sqlite3_threadsafe,
+ sqlite3_result_zeroblob,
+ sqlite3_result_error_code,
+ sqlite3_test_control,
+ sqlite3_randomness,
+ sqlite3_context_db_handle,
+
+ /*
+ ** Added for 3.6.0
+ */
+ sqlite3_extended_result_codes,
+ sqlite3_limit,
+ sqlite3_next_stmt,
+ sqlite3_sql,
+ sqlite3_status,
};
/*
@@ -259,14 +326,15 @@ const sqlite3_api_routines sqlite3_apis = {
**
** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with
** error message text. The calling function should free this memory
-** by calling sqlite3_free().
+** by calling sqlite3DbFree(db, ).
*/
-int sqlite3_load_extension(
+static int sqlite3LoadExtension(
sqlite3 *db, /* Load the extension into this database connection */
const char *zFile, /* Name of the shared library containing extension */
const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
char **pzErrMsg /* Put error message here if not 0 */
){
+ sqlite3_vfs *pVfs = db->pVfs;
void *handle;
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
char *zErrmsg = 0;
@@ -289,46 +357,66 @@ int sqlite3_load_extension(
zProc = "sqlite3_extension_init";
}
- handle = sqlite3OsDlopen(zFile);
+ handle = sqlite3OsDlOpen(pVfs, zFile);
if( handle==0 ){
if( pzErrMsg ){
- *pzErrMsg = sqlite3_mprintf("unable to open shared library [%s]", zFile);
+ char zErr[256];
+ zErr[sizeof(zErr)-1] = '\0';
+ sqlite3_snprintf(sizeof(zErr)-1, zErr,
+ "unable to open shared library [%s]", zFile);
+ sqlite3OsDlError(pVfs, sizeof(zErr)-1, zErr);
+ *pzErrMsg = sqlite3DbStrDup(0, zErr);
}
return SQLITE_ERROR;
}
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
- sqlite3OsDlsym(handle, zProc);
+ sqlite3OsDlSym(pVfs, handle, zProc);
if( xInit==0 ){
if( pzErrMsg ){
- *pzErrMsg = sqlite3_mprintf("no entry point [%s] in shared library [%s]",
- zProc, zFile);
+ char zErr[256];
+ zErr[sizeof(zErr)-1] = '\0';
+ sqlite3_snprintf(sizeof(zErr)-1, zErr,
+ "no entry point [%s] in shared library [%s]", zProc,zFile);
+ sqlite3OsDlError(pVfs, sizeof(zErr)-1, zErr);
+ *pzErrMsg = sqlite3DbStrDup(0, zErr);
+ sqlite3OsDlClose(pVfs, handle);
}
- sqlite3OsDlclose(handle);
return SQLITE_ERROR;
- }else if( xInit(db, &zErrmsg, &sqlite3_apis) ){
+ }else if( xInit(db, &zErrmsg, &sqlite3Apis) ){
if( pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
}
sqlite3_free(zErrmsg);
- sqlite3OsDlclose(handle);
+ sqlite3OsDlClose(pVfs, handle);
return SQLITE_ERROR;
}
/* Append the new shared library handle to the db->aExtension array. */
- db->nExtension++;
- aHandle = sqliteMalloc(sizeof(handle)*db->nExtension);
+ aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1));
if( aHandle==0 ){
return SQLITE_NOMEM;
}
if( db->nExtension>0 ){
- memcpy(aHandle, db->aExtension, sizeof(handle)*(db->nExtension-1));
+ memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension);
}
- sqliteFree(db->aExtension);
+ sqlite3DbFree(db, db->aExtension);
db->aExtension = aHandle;
- db->aExtension[db->nExtension-1] = handle;
+ db->aExtension[db->nExtension++] = handle;
return SQLITE_OK;
}
+int sqlite3_load_extension(
+ sqlite3 *db, /* Load the extension into this database connection */
+ const char *zFile, /* Name of the shared library containing extension */
+ const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
+ char **pzErrMsg /* Put error message here if not 0 */
+){
+ int rc;
+ sqlite3_mutex_enter(db->mutex);
+ rc = sqlite3LoadExtension(db, zFile, zProc, pzErrMsg);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
/*
** Call this routine when the database connection is closing in order
@@ -336,10 +424,11 @@ int sqlite3_load_extension(
*/
void sqlite3CloseExtensions(sqlite3 *db){
int i;
+ assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nExtension; i++){
- sqlite3OsDlclose(db->aExtension[i]);
+ sqlite3OsDlClose(db->pVfs, db->aExtension[i]);
}
- sqliteFree(db->aExtension);
+ sqlite3DbFree(db, db->aExtension);
}
/*
@@ -347,22 +436,40 @@ void sqlite3CloseExtensions(sqlite3 *db){
** default so as not to open security holes in older applications.
*/
int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
+ sqlite3_mutex_enter(db->mutex);
if( onoff ){
db->flags |= SQLITE_LoadExtension;
}else{
db->flags &= ~SQLITE_LoadExtension;
}
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
/*
-** A list of automatically loaded extensions.
+** The auto-extension code added regardless of whether or not extension
+** loading is supported. We need a dummy sqlite3Apis pointer for that
+** code if regular extension loading is not available. This is that
+** dummy pointer.
+*/
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+static const sqlite3_api_routines sqlite3Apis = { 0 };
+#endif
+
+
+/*
+** The following object holds the list of automatically loaded
+** extensions.
**
-** This list is shared across threads, so be sure to hold the
-** mutex while accessing or changing it.
+** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER
+** mutex must be held while accessing this list.
*/
-static int nAutoExtension = 0;
-static void **aAutoExtension = 0;
+static struct {
+ int nExt; /* Number of entries in aExt[] */
+ void **aExt; /* Pointers to the extension init functions */
+} autoext = { 0, 0 };
/*
@@ -370,37 +477,57 @@ static void **aAutoExtension = 0;
** loaded by every new database connection.
*/
int sqlite3_auto_extension(void *xInit){
- int i;
int rc = SQLITE_OK;
- sqlite3OsEnterMutex();
- for(i=0; i<nAutoExtension; i++){
- if( aAutoExtension[i]==xInit ) break;
- }
- if( i==nAutoExtension ){
- nAutoExtension++;
- aAutoExtension = sqlite3Realloc( aAutoExtension,
- nAutoExtension*sizeof(aAutoExtension[0]) );
- if( aAutoExtension==0 ){
- nAutoExtension = 0;
- rc = SQLITE_NOMEM;
- }else{
- aAutoExtension[nAutoExtension-1] = xInit;
+#ifndef SQLITE_OMIT_AUTOINIT
+ rc = sqlite3_initialize();
+ if( rc ){
+ return rc;
+ }else
+#endif
+ {
+ int i;
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ for(i=0; i<autoext.nExt; i++){
+ if( autoext.aExt[i]==xInit ) break;
}
+ if( i==autoext.nExt ){
+ int nByte = (autoext.nExt+1)*sizeof(autoext.aExt[0]);
+ void **aNew;
+ aNew = sqlite3_realloc(autoext.aExt, nByte);
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ autoext.aExt = aNew;
+ autoext.aExt[autoext.nExt] = xInit;
+ autoext.nExt++;
+ }
+ }
+ sqlite3_mutex_leave(mutex);
+ assert( (rc&0xff)==rc );
+ return rc;
}
- sqlite3OsLeaveMutex();
- assert( (rc&0xff)==rc );
- return rc;
}
/*
** Reset the automatic extension loading mechanism.
*/
void sqlite3_reset_auto_extension(void){
- sqlite3OsEnterMutex();
- sqliteFree(aAutoExtension);
- aAutoExtension = 0;
- nAutoExtension = 0;
- sqlite3OsLeaveMutex();
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize()==SQLITE_OK )
+#endif
+ {
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ sqlite3_free(autoext.aExt);
+ autoext.aExt = 0;
+ autoext.nExt = 0;
+ sqlite3_mutex_leave(mutex);
+ }
}
/*
@@ -412,29 +539,31 @@ int sqlite3AutoLoadExtensions(sqlite3 *db){
int rc = SQLITE_OK;
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
- if( nAutoExtension==0 ){
+ if( autoext.nExt==0 ){
/* Common case: early out without every having to acquire a mutex */
return SQLITE_OK;
}
for(i=0; go; i++){
char *zErrmsg = 0;
- sqlite3OsEnterMutex();
- if( i>=nAutoExtension ){
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ if( i>=autoext.nExt ){
xInit = 0;
go = 0;
}else{
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
- aAutoExtension[i];
+ autoext.aExt[i];
}
- sqlite3OsLeaveMutex();
- if( xInit && xInit(db, &zErrmsg, &sqlite3_apis) ){
+ sqlite3_mutex_leave(mutex);
+ if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
sqlite3Error(db, SQLITE_ERROR,
"automatic extension loading failed: %s", zErrmsg);
go = 0;
rc = SQLITE_ERROR;
+ sqlite3_free(zErrmsg);
}
}
return rc;
}
-
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
diff --git a/third_party/sqlite/main.c b/third_party/sqlite/src/main.c
index a8c69cb..68500eb 100644..100755
--- a/third_party/sqlite/main.c
+++ b/third_party/sqlite/src/main.c
@@ -14,26 +14,35 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.378 2007/08/13 15:28:34 danielk1977 Exp $
+** $Id: main.c,v 1.486 2008/08/04 20:13:27 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
+#ifdef SQLITE_ENABLE_FTS3
+# include "fts3.h"
+#endif
+#ifdef SQLITE_ENABLE_RTREE
+# include "rtree.h"
+#endif
+
/*
** The version of the library
*/
const char sqlite3_version[] = SQLITE_VERSION;
const char *sqlite3_libversion(void){ return sqlite3_version; }
int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
+int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
+#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
/*
** If the following function pointer is not NULL and if
** SQLITE_ENABLE_IOTRACE is enabled, then messages describing
** I/O active are written using this function. These messages
** are intended for debugging activity only.
*/
-void (*sqlite3_io_trace)(const char*, ...) = 0;
+void (*sqlite3IoTrace)(const char*, ...) = 0;
+#endif
/*
** If the following global variable points to a string which is the
@@ -44,13 +53,346 @@ void (*sqlite3_io_trace)(const char*, ...) = 0;
*/
char *sqlite3_temp_directory = 0;
+/*
+** Initialize SQLite.
+**
+** This routine must be called to initialize the memory allocation,
+** VFS, and mutex subsystesms prior to doing any serious work with
+** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT
+** this routine will be called automatically by key routines such as
+** sqlite3_open().
+**
+** This routine is a no-op except on its very first call for the process,
+** or for the first call after a call to sqlite3_shutdown.
+*/
+int sqlite3_initialize(void){
+ static int inProgress = 0;
+ int rc;
+
+ /* If SQLite is already initialized, this call is a no-op. */
+ if( sqlite3Config.isInit ) return SQLITE_OK;
+
+ /* Make sure the mutex system is initialized. */
+ rc = sqlite3MutexInit();
+
+ if( rc==SQLITE_OK ){
+
+ /* Initialize the malloc() system and the recursive pInitMutex mutex.
+ ** This operation is protected by the STATIC_MASTER mutex.
+ */
+ sqlite3_mutex *pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMaster);
+ if( !sqlite3Config.isMallocInit ){
+ rc = sqlite3MallocInit();
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3Config.isMallocInit = 1;
+ if( !sqlite3Config.pInitMutex ){
+ sqlite3Config.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+ if( sqlite3Config.bCoreMutex && !sqlite3Config.pInitMutex ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+ }
+ sqlite3_mutex_leave(pMaster);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ /* Enter the recursive pInitMutex mutex. After doing so, if the
+ ** sqlite3Config.isInit flag is true, then some other thread has
+ ** finished doing the initialization. If the inProgress flag is
+ ** true, then this function is being called recursively from within
+ ** the sqlite3_os_init() call below. In either case, exit early.
+ */
+ sqlite3_mutex_enter(sqlite3Config.pInitMutex);
+ if( sqlite3Config.isInit || inProgress ){
+ sqlite3_mutex_leave(sqlite3Config.pInitMutex);
+ return SQLITE_OK;
+ }
+ sqlite3StatusReset();
+ inProgress = 1;
+ rc = sqlite3_os_init();
+ inProgress = 0;
+ sqlite3Config.isInit = (rc==SQLITE_OK ? 1 : 0);
+ sqlite3_mutex_leave(sqlite3Config.pInitMutex);
+ }
+
+ /* Check NaN support. */
+#ifndef NDEBUG
+ /* This section of code's only "output" is via assert() statements. */
+ if ( rc==SQLITE_OK ){
+ u64 x = (((u64)1)<<63)-1;
+ double y;
+ assert(sizeof(x)==8);
+ assert(sizeof(x)==sizeof(y));
+ memcpy(&y, &x, 8);
+ assert( sqlite3IsNaN(y) );
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** Undo the effects of sqlite3_initialize(). Must not be called while
+** there are outstanding database connections or memory allocations or
+** while any part of SQLite is otherwise in use in any thread. This
+** routine is not threadsafe. Not by a long shot.
+*/
+int sqlite3_shutdown(void){
+ sqlite3_mutex_free(sqlite3Config.pInitMutex);
+ sqlite3Config.pInitMutex = 0;
+ sqlite3Config.isMallocInit = 0;
+ if( sqlite3Config.isInit ){
+ sqlite3_os_end();
+ }
+ if( sqlite3Config.m.xShutdown ){
+ sqlite3MallocEnd();
+ }
+ if( sqlite3Config.mutex.xMutexEnd ){
+ sqlite3MutexEnd();
+ }
+ sqlite3Config.isInit = 0;
+ return SQLITE_OK;
+}
+
+/*
+** This API allows applications to modify the global configuration of
+** the SQLite library at run-time.
+**
+** This routine should only be called when there are no outstanding
+** database connections or memory allocations. This routine is not
+** threadsafe. Failure to heed these warnings can lead to unpredictable
+** behavior.
+*/
+int sqlite3_config(int op, ...){
+ va_list ap;
+ int rc = SQLITE_OK;
+
+ /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while
+ ** the SQLite library is in use. */
+ if( sqlite3Config.isInit ) return SQLITE_MISUSE;
+
+ va_start(ap, op);
+ switch( op ){
+ case SQLITE_CONFIG_SINGLETHREAD: {
+ /* Disable all mutexing */
+ sqlite3Config.bCoreMutex = 0;
+ sqlite3Config.bFullMutex = 0;
+ break;
+ }
+ case SQLITE_CONFIG_MULTITHREAD: {
+ /* Disable mutexing of database connections */
+ /* Enable mutexing of core data structures */
+ sqlite3Config.bCoreMutex = 1;
+ sqlite3Config.bFullMutex = 0;
+ break;
+ }
+ case SQLITE_CONFIG_SERIALIZED: {
+ /* Enable all mutexing */
+ sqlite3Config.bCoreMutex = 1;
+ sqlite3Config.bFullMutex = 1;
+ break;
+ }
+ case SQLITE_CONFIG_MALLOC: {
+ /* Specify an alternative malloc implementation */
+ sqlite3Config.m = *va_arg(ap, sqlite3_mem_methods*);
+ break;
+ }
+ case SQLITE_CONFIG_GETMALLOC: {
+ /* Retrieve the current malloc() implementation */
+ if( sqlite3Config.m.xMalloc==0 ) sqlite3MemSetDefault();
+ *va_arg(ap, sqlite3_mem_methods*) = sqlite3Config.m;
+ break;
+ }
+ case SQLITE_CONFIG_MUTEX: {
+ /* Specify an alternative mutex implementation */
+ sqlite3Config.mutex = *va_arg(ap, sqlite3_mutex_methods*);
+ break;
+ }
+ case SQLITE_CONFIG_GETMUTEX: {
+ /* Retrieve the current mutex implementation */
+ *va_arg(ap, sqlite3_mutex_methods*) = sqlite3Config.mutex;
+ break;
+ }
+ case SQLITE_CONFIG_MEMSTATUS: {
+ /* Enable or disable the malloc status collection */
+ sqlite3Config.bMemstat = va_arg(ap, int);
+ break;
+ }
+ case SQLITE_CONFIG_SCRATCH: {
+ /* Designate a buffer for scratch memory space */
+ sqlite3Config.pScratch = va_arg(ap, void*);
+ sqlite3Config.szScratch = va_arg(ap, int);
+ sqlite3Config.nScratch = va_arg(ap, int);
+ break;
+ }
+ case SQLITE_CONFIG_PAGECACHE: {
+ /* Designate a buffer for scratch memory space */
+ sqlite3Config.pPage = va_arg(ap, void*);
+ sqlite3Config.szPage = va_arg(ap, int);
+ sqlite3Config.nPage = va_arg(ap, int);
+ break;
+ }
+
+#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
+ case SQLITE_CONFIG_HEAP: {
+ /* Designate a buffer for heap memory space */
+ sqlite3Config.pHeap = va_arg(ap, void*);
+ sqlite3Config.nHeap = va_arg(ap, int);
+ sqlite3Config.mnReq = va_arg(ap, int);
+
+ if( sqlite3Config.pHeap==0 ){
+ /* If the heap pointer is NULL, then restore the malloc implementation
+ ** back to NULL pointers too. This will cause the malloc to go
+ ** back to its default implementation when sqlite3_initialize() is
+ ** run.
+ */
+ memset(&sqlite3Config.m, 0, sizeof(sqlite3Config.m));
+ }else{
+ /* The heap pointer is not NULL, then install one of the
+ ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor
+ ** ENABLE_MEMSYS5 is defined, return an error.
+ ** the default case and return an error.
+ */
+#ifdef SQLITE_ENABLE_MEMSYS3
+ sqlite3Config.m = *sqlite3MemGetMemsys3();
+#endif
+#ifdef SQLITE_ENABLE_MEMSYS5
+ sqlite3Config.m = *sqlite3MemGetMemsys5();
+#endif
+ }
+ break;
+ }
+#endif
+
+#if defined(SQLITE_ENABLE_MEMSYS6)
+ case SQLITE_CONFIG_CHUNKALLOC: {
+ sqlite3Config.nSmall = va_arg(ap, int);
+ sqlite3Config.m = *sqlite3MemGetMemsys6();
+ break;
+ }
+#endif
+
+ case SQLITE_CONFIG_LOOKASIDE: {
+ sqlite3Config.szLookaside = va_arg(ap, int);
+ sqlite3Config.nLookaside = va_arg(ap, int);
+ break;
+ }
+
+ default: {
+ rc = SQLITE_ERROR;
+ break;
+ }
+ }
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Set up the lookaside buffers for a database connection.
+** Return SQLITE_OK on success.
+** If lookaside is already active, return SQLITE_BUSY.
+**
+** The sz parameter is the number of bytes in each lookaside slot.
+** The cnt parameter is the number of slots. If pStart is NULL the
+** space for the lookaside memory is obtained from sqlite3_malloc().
+** If pStart is not NULL then it is sz*cnt bytes of memory to use for
+** the lookaside memory.
+*/
+static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
+ void *pStart;
+ if( db->lookaside.nOut ){
+ return SQLITE_BUSY;
+ }
+ if( sz<0 ) sz = 0;
+ if( cnt<0 ) cnt = 0;
+ sz = (sz+7)&~7;
+ if( pBuf==0 ){
+ sqlite3BeginBenignMalloc();
+ pStart = sqlite3Malloc( sz*cnt );
+ sqlite3EndBenignMalloc();
+ }else{
+ pStart = pBuf;
+ }
+ if( db->lookaside.bMalloced ){
+ sqlite3_free(db->lookaside.pStart);
+ }
+ db->lookaside.pStart = pStart;
+ db->lookaside.pFree = 0;
+ db->lookaside.sz = sz;
+ db->lookaside.bMalloced = pBuf==0;
+ if( pStart ){
+ int i;
+ LookasideSlot *p;
+ p = (LookasideSlot*)pStart;
+ for(i=cnt-1; i>=0; i--){
+ p->pNext = db->lookaside.pFree;
+ db->lookaside.pFree = p;
+ p = (LookasideSlot*)&((u8*)p)[sz];
+ }
+ db->lookaside.pEnd = p;
+ db->lookaside.bEnabled = 1;
+ }else{
+ db->lookaside.pEnd = 0;
+ db->lookaside.bEnabled = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Configuration settings for an individual database connection
+*/
+int sqlite3_db_config(sqlite3 *db, int op, ...){
+ va_list ap;
+ int rc;
+ va_start(ap, op);
+ switch( op ){
+ case SQLITE_DBCONFIG_LOOKASIDE: {
+ void *pBuf = va_arg(ap, void*);
+ int sz = va_arg(ap, int);
+ int cnt = va_arg(ap, int);
+ rc = setupLookaside(db, pBuf, sz, cnt);
+ break;
+ }
+ default: {
+ rc = SQLITE_ERROR;
+ break;
+ }
+ }
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Routine needed to support the testcase() macro.
+*/
+#ifdef SQLITE_COVERAGE_TEST
+void sqlite3Coverage(int x){
+ static int dummy = 0;
+ dummy += x;
+}
+#endif
+
+
+/*
+** Return true if the buffer z[0..n-1] contains all spaces.
+*/
+static int allSpaces(const char *z, int n){
+ while( n>0 && z[n-1]==' ' ){ n--; }
+ return n==0;
+}
/*
** This is the default collating function named "BINARY" which is always
** available.
+**
+** If the padFlag argument is not NULL then space padding at the end
+** of strings is ignored. This implements the RTRIM collation.
*/
static int binCollFunc(
- void *NotUsed,
+ void *padFlag,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
@@ -58,7 +400,14 @@ static int binCollFunc(
n = nKey1<nKey2 ? nKey1 : nKey2;
rc = memcmp(pKey1, pKey2, n);
if( rc==0 ){
- rc = nKey1 - nKey2;
+ if( padFlag
+ && allSpaces(((char*)pKey1)+n, nKey1-n)
+ && allSpaces(((char*)pKey2)+n, nKey2-n)
+ ){
+ /* Leave rc unchanged at 0 */
+ }else{
+ rc = nKey1 - nKey2;
+ }
}
return rc;
}
@@ -116,9 +465,10 @@ int sqlite3_close(sqlite3 *db){
if( !db ){
return SQLITE_OK;
}
- if( sqlite3SafetyCheck(db) ){
+ if( !sqlite3SafetyCheckSickOrOk(db) ){
return SQLITE_MISUSE;
}
+ sqlite3_mutex_enter(db->mutex);
#ifdef SQLITE_SSE
{
@@ -142,22 +492,10 @@ int sqlite3_close(sqlite3 *db){
if( db->pVdbe ){
sqlite3Error(db, SQLITE_BUSY,
"Unable to close due to unfinalised statements");
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_BUSY;
}
- assert( !sqlite3SafetyCheck(db) );
-
- /* FIX ME: db->magic may be set to SQLITE_MAGIC_CLOSED if the database
- ** cannot be opened for some reason. So this routine needs to run in
- ** that case. But maybe there should be an extra magic value for the
- ** "failed to open" state.
- **
- ** TODO: Coverage tests do not test the case where this condition is
- ** true. It's hard to see how to cause it without messing with threads.
- */
- if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){
- /* printf("DID NOT CLOSE\n"); fflush(stdout); */
- return SQLITE_ERROR;
- }
+ assert( sqlite3SafetyCheckSickOrOk(db) );
for(j=0; j<db->nDb; j++){
struct Db *pDb = &db->aDb[j];
@@ -176,7 +514,7 @@ int sqlite3_close(sqlite3 *db){
FuncDef *pFunc, *pNext;
for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){
pNext = pFunc->pNext;
- sqliteFree(pFunc);
+ sqlite3DbFree(db, pFunc);
}
}
@@ -188,7 +526,7 @@ int sqlite3_close(sqlite3 *db){
pColl[j].xDel(pColl[j].pUser);
}
}
- sqliteFree(pColl);
+ sqlite3DbFree(db, pColl);
}
sqlite3HashClear(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -197,7 +535,7 @@ int sqlite3_close(sqlite3 *db){
if( pMod->xDestroy ){
pMod->xDestroy(pMod->pAux);
}
- sqliteFree(pMod);
+ sqlite3DbFree(db, pMod);
}
sqlite3HashClear(&db->aModule);
#endif
@@ -217,9 +555,14 @@ int sqlite3_close(sqlite3 *db){
** the same sqliteMalloc() as the one that allocates the database
** structure?
*/
- sqliteFree(db->aDb[1].pSchema);
- sqliteFree(db);
- sqlite3ReleaseThreadData();
+ sqlite3DbFree(db, db->aDb[1].pSchema);
+ sqlite3_mutex_leave(db->mutex);
+ db->magic = SQLITE_MAGIC_CLOSED;
+ sqlite3_mutex_free(db->mutex);
+ if( db->lookaside.bMalloced ){
+ sqlite3_free(db->lookaside.pStart);
+ }
+ sqlite3_free(db);
return SQLITE_OK;
}
@@ -229,6 +572,8 @@ int sqlite3_close(sqlite3 *db){
void sqlite3RollbackAll(sqlite3 *db){
int i;
int inTrans = 0;
+ assert( sqlite3_mutex_held(db->mutex) );
+ sqlite3BeginBenignMalloc();
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt ){
if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){
@@ -239,6 +584,8 @@ void sqlite3RollbackAll(sqlite3 *db){
}
}
sqlite3VtabRollback(db);
+ sqlite3EndBenignMalloc();
+
if( db->flags&SQLITE_InternChanges ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetInternalSchema(db, 0);
@@ -278,7 +625,7 @@ const char *sqlite3ErrStr(int rc){
case SQLITE_CONSTRAINT: z = "constraint failed"; break;
case SQLITE_MISMATCH: z = "datatype mismatch"; break;
case SQLITE_MISUSE: z = "library routine called out of sequence";break;
- case SQLITE_NOLFS: z = "kernel lacks large file support"; break;
+ case SQLITE_NOLFS: z = "large file support is disabled"; break;
case SQLITE_AUTH: z = "authorization denied"; break;
case SQLITE_FORMAT: z = "auxiliary database format error"; break;
case SQLITE_RANGE: z = "bind or column index out of range"; break;
@@ -298,13 +645,14 @@ static int sqliteDefaultBusyCallback(
void *ptr, /* Database connection */
int count /* Number of times table has been busy */
){
-#if OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP)
+#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP)
static const u8 delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
static const u8 totals[] =
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY (sizeof(delays)/sizeof(delays[0]))
- int timeout = ((sqlite3 *)ptr)->busyTimeout;
+ sqlite3 *db = (sqlite3 *)ptr;
+ int timeout = db->busyTimeout;
int delay, prior;
assert( count>=0 );
@@ -319,14 +667,15 @@ static int sqliteDefaultBusyCallback(
delay = timeout - prior;
if( delay<=0 ) return 0;
}
- sqlite3OsSleep(delay);
+ sqlite3OsSleep(db->pVfs, delay*1000);
return 1;
#else
+ sqlite3 *db = (sqlite3 *)ptr;
int timeout = ((sqlite3 *)ptr)->busyTimeout;
if( (count+1)*1000 > timeout ){
return 0;
}
- sqlite3OsSleep(1000);
+ sqlite3OsSleep(db->pVfs, 1000000);
return 1;
#endif
}
@@ -340,7 +689,7 @@ static int sqliteDefaultBusyCallback(
*/
int sqlite3InvokeBusyHandler(BusyHandler *p){
int rc;
- if( p==0 || p->xFunc==0 || p->nBusy<0 ) return 0;
+ if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0;
rc = p->xFunc(p->pArg, p->nBusy);
if( rc==0 ){
p->nBusy = -1;
@@ -359,12 +708,11 @@ int sqlite3_busy_handler(
int (*xBusy)(void*,int),
void *pArg
){
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
+ sqlite3_mutex_enter(db->mutex);
db->busyHandler.xFunc = xBusy;
db->busyHandler.pArg = pArg;
db->busyHandler.nBusy = 0;
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -380,17 +728,17 @@ void sqlite3_progress_handler(
int (*xProgress)(void*),
void *pArg
){
- if( !sqlite3SafetyCheck(db) ){
- if( nOps>0 ){
- db->xProgress = xProgress;
- db->nProgressOps = nOps;
- db->pProgressArg = pArg;
- }else{
- db->xProgress = 0;
- db->nProgressOps = 0;
- db->pProgressArg = 0;
- }
+ sqlite3_mutex_enter(db->mutex);
+ if( nOps>0 ){
+ db->xProgress = xProgress;
+ db->nProgressOps = nOps;
+ db->pProgressArg = pArg;
+ }else{
+ db->xProgress = 0;
+ db->nProgressOps = 0;
+ db->pProgressArg = 0;
}
+ sqlite3_mutex_leave(db->mutex);
}
#endif
@@ -400,9 +748,6 @@ void sqlite3_progress_handler(
** specified number of milliseconds before returning 0.
*/
int sqlite3_busy_timeout(sqlite3 *db, int ms){
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
if( ms>0 ){
db->busyTimeout = ms;
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
@@ -416,35 +761,9 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){
** Cause any pending operation to stop at its earliest opportunity.
*/
void sqlite3_interrupt(sqlite3 *db){
- if( db && (db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_BUSY) ){
- db->u1.isInterrupted = 1;
- }
+ db->u1.isInterrupted = 1;
}
-/*
-** Memory allocation routines that use SQLites internal memory
-** memory allocator. Depending on how SQLite is compiled, the
-** internal memory allocator might be just an alias for the
-** system default malloc/realloc/free. Or the built-in allocator
-** might do extra stuff like put sentinals around buffers to
-** check for overruns or look for memory leaks.
-**
-** Use sqlite3_free() to free memory returned by sqlite3_mprintf().
-*/
-void sqlite3_free(void *p){ if( p ) sqlite3OsFree(p); }
-void *sqlite3_malloc(int nByte){ return nByte>0 ? sqlite3OsMalloc(nByte) : 0; }
-void *sqlite3_realloc(void *pOld, int nByte){
- if( pOld ){
- if( nByte>0 ){
- return sqlite3OsRealloc(pOld, nByte);
- }else{
- sqlite3OsFree(pOld);
- return 0;
- }
- }else{
- return sqlite3_malloc(nByte);
- }
-}
/*
** This function is exactly the same as sqlite3_create_function(), except
@@ -465,15 +784,13 @@ int sqlite3CreateFunc(
FuncDef *p;
int nName;
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
+ assert( sqlite3_mutex_held(db->mutex) );
if( zFunctionName==0 ||
(xFunc && (xFinal || xStep)) ||
(!xFunc && (xFinal && !xStep)) ||
(!xFunc && (!xFinal && xStep)) ||
- (nArg<-1 || nArg>127) ||
- (255<(nName = strlen(zFunctionName))) ){
+ (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
+ (255<(nName = sqlite3Strlen(db, zFunctionName))) ){
sqlite3Error(db, SQLITE_ERROR, "bad parameters");
return SQLITE_ERROR;
}
@@ -492,10 +809,13 @@ int sqlite3CreateFunc(
int rc;
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
pUserData, xFunc, xStep, xFinal);
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
- pUserData, xFunc, xStep, xFinal);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
+ pUserData, xFunc, xStep, xFinal);
+ }
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
enc = SQLITE_UTF16BE;
}
#else
@@ -512,7 +832,7 @@ int sqlite3CreateFunc(
if( db->activeVdbeCnt ){
sqlite3Error(db, SQLITE_BUSY,
"Unable to delete/modify user-function due to active statements");
- assert( !sqlite3MallocFailed() );
+ assert( !db->mallocFailed );
return SQLITE_BUSY;
}else{
sqlite3ExpirePreparedStatements(db);
@@ -520,14 +840,16 @@ int sqlite3CreateFunc(
}
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 1);
- if( p ){
- p->flags = 0;
- p->xFunc = xFunc;
- p->xStep = xStep;
- p->xFinalize = xFinal;
- p->pUserData = pUserData;
- p->nArg = nArg;
+ assert(p || db->mallocFailed);
+ if( !p ){
+ return SQLITE_NOMEM;
}
+ p->flags = 0;
+ p->xFunc = xFunc;
+ p->xStep = xStep;
+ p->xFinalize = xFinal;
+ p->pUserData = pUserData;
+ p->nArg = nArg;
return SQLITE_OK;
}
@@ -545,10 +867,11 @@ int sqlite3_create_function(
void (*xFinal)(sqlite3_context*)
){
int rc;
- assert( !sqlite3MallocFailed() );
+ sqlite3_mutex_enter(db->mutex);
rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal);
-
- return sqlite3ApiExit(db, rc);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
#ifndef SQLITE_OMIT_UTF16
@@ -564,13 +887,14 @@ int sqlite3_create_function16(
){
int rc;
char *zFunc8;
- assert( !sqlite3MallocFailed() );
-
- zFunc8 = sqlite3Utf16to8(zFunctionName, -1);
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
+ zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1);
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal);
- sqliteFree(zFunc8);
-
- return sqlite3ApiExit(db, rc);
+ sqlite3DbFree(db, zFunc8);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
#endif
@@ -592,12 +916,16 @@ int sqlite3_overload_function(
const char *zName,
int nArg
){
- int nName = strlen(zName);
+ int nName = sqlite3Strlen(db, zName);
+ int rc;
+ sqlite3_mutex_enter(db->mutex);
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
0, sqlite3InvalidFunction, 0, 0);
}
- return sqlite3ApiExit(db, SQLITE_OK);
+ rc = sqlite3ApiExit(db, SQLITE_OK);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
#ifndef SQLITE_OMIT_TRACE
@@ -610,9 +938,12 @@ int sqlite3_overload_function(
** SQL statement.
*/
void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
- void *pOld = db->pTraceArg;
+ void *pOld;
+ sqlite3_mutex_enter(db->mutex);
+ pOld = db->pTraceArg;
db->xTrace = xTrace;
db->pTraceArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
return pOld;
}
/*
@@ -628,9 +959,12 @@ void *sqlite3_profile(
void (*xProfile)(void*,const char*,sqlite_uint64),
void *pArg
){
- void *pOld = db->pProfileArg;
+ void *pOld;
+ sqlite3_mutex_enter(db->mutex);
+ pOld = db->pProfileArg;
db->xProfile = xProfile;
db->pProfileArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
return pOld;
}
#endif /* SQLITE_OMIT_TRACE */
@@ -646,9 +980,12 @@ void *sqlite3_commit_hook(
int (*xCallback)(void*), /* Function to invoke on each commit */
void *pArg /* Argument to the function */
){
- void *pOld = db->pCommitArg;
+ void *pOld;
+ sqlite3_mutex_enter(db->mutex);
+ pOld = db->pCommitArg;
db->xCommitCallback = xCallback;
db->pCommitArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
return pOld;
}
@@ -661,9 +998,12 @@ void *sqlite3_update_hook(
void (*xCallback)(void*,int,char const *,char const *,sqlite_int64),
void *pArg /* Argument to the function */
){
- void *pRet = db->pUpdateArg;
+ void *pRet;
+ sqlite3_mutex_enter(db->mutex);
+ pRet = db->pUpdateArg;
db->xUpdateCallback = xCallback;
db->pUpdateArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
return pRet;
}
@@ -676,9 +1016,12 @@ void *sqlite3_rollback_hook(
void (*xCallback)(void*), /* Callback function */
void *pArg /* Argument to the function */
){
- void *pRet = db->pRollbackArg;
+ void *pRet;
+ sqlite3_mutex_enter(db->mutex);
+ pRet = db->pRollbackArg;
db->xRollbackCallback = xCallback;
db->pRollbackArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
return pRet;
}
@@ -693,57 +1036,67 @@ void *sqlite3_rollback_hook(
**
** A virtual database can be either a disk file (that is automatically
** deleted when the file is closed) or it an be held entirely in memory,
-** depending on the values of the TEMP_STORE compile-time macro and the
+** depending on the values of the SQLITE_TEMP_STORE compile-time macro and the
** db->temp_store variable, according to the following chart:
**
-** TEMP_STORE db->temp_store Location of temporary database
-** ---------- -------------- ------------------------------
-** 0 any file
-** 1 1 file
-** 1 2 memory
-** 1 0 file
-** 2 1 file
-** 2 2 memory
-** 2 0 memory
-** 3 any memory
+** SQLITE_TEMP_STORE db->temp_store Location of temporary database
+** ----------------- -------------- ------------------------------
+** 0 any file
+** 1 1 file
+** 1 2 memory
+** 1 0 file
+** 2 1 file
+** 2 2 memory
+** 2 0 memory
+** 3 any memory
*/
int sqlite3BtreeFactory(
const sqlite3 *db, /* Main database when opening aux otherwise 0 */
const char *zFilename, /* Name of the file containing the BTree database */
int omitJournal, /* if TRUE then do not journal this file */
int nCache, /* How many pages in the page cache */
+ int vfsFlags, /* Flags passed through to vfsOpen */
Btree **ppBtree /* Pointer to new Btree object written here */
){
- int btree_flags = 0;
+ int btFlags = 0;
int rc;
+ assert( sqlite3_mutex_held(db->mutex) );
assert( ppBtree != 0);
if( omitJournal ){
- btree_flags |= BTREE_OMIT_JOURNAL;
+ btFlags |= BTREE_OMIT_JOURNAL;
}
if( db->flags & SQLITE_NoReadlock ){
- btree_flags |= BTREE_NO_READLOCK;
+ btFlags |= BTREE_NO_READLOCK;
}
if( zFilename==0 ){
-#if TEMP_STORE==0
+#if SQLITE_TEMP_STORE==0
/* Do nothing */
#endif
#ifndef SQLITE_OMIT_MEMORYDB
-#if TEMP_STORE==1
+#if SQLITE_TEMP_STORE==1
if( db->temp_store==2 ) zFilename = ":memory:";
#endif
-#if TEMP_STORE==2
+#if SQLITE_TEMP_STORE==2
if( db->temp_store!=1 ) zFilename = ":memory:";
#endif
-#if TEMP_STORE==3
+#if SQLITE_TEMP_STORE==3
zFilename = ":memory:";
#endif
#endif /* SQLITE_OMIT_MEMORYDB */
}
- rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btree_flags);
- if( rc==SQLITE_OK ){
- sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler);
+ if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (zFilename==0 || *zFilename==0) ){
+ vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
+ }
+ rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btFlags, vfsFlags);
+
+ /* If the B-Tree was successfully opened, set the pager-cache size to the
+ ** default value. Except, if the call to BtreeOpen() returned a handle
+ ** open on an existing shared pager-cache, do not change the pager-cache
+ ** size.
+ */
+ if( rc==SQLITE_OK && 0==sqlite3BtreeSchema(*ppBtree, 0, 0) ){
sqlite3BtreeSetCacheSize(*ppBtree, nCache);
}
return rc;
@@ -755,17 +1108,20 @@ int sqlite3BtreeFactory(
*/
const char *sqlite3_errmsg(sqlite3 *db){
const char *z;
- assert( !sqlite3MallocFailed() );
if( !db ){
return sqlite3ErrStr(SQLITE_NOMEM);
}
- if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){
+ if( !sqlite3SafetyCheckSickOrOk(db) ){
return sqlite3ErrStr(SQLITE_MISUSE);
}
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
z = (char*)sqlite3_value_text(db->pErr);
+ assert( !db->mallocFailed );
if( z==0 ){
z = sqlite3ErrStr(db->errCode);
}
+ sqlite3_mutex_leave(db->mutex);
return z;
}
@@ -795,20 +1151,27 @@ const void *sqlite3_errmsg16(sqlite3 *db){
};
const void *z;
- assert( !sqlite3MallocFailed() );
if( !db ){
return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
}
- if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){
+ if( !sqlite3SafetyCheckSickOrOk(db) ){
return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
}
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
z = sqlite3_value_text16(db->pErr);
if( z==0 ){
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
SQLITE_UTF8, SQLITE_STATIC);
z = sqlite3_value_text16(db->pErr);
}
- sqlite3ApiExit(0, 0);
+ /* A malloc() may have failed within the call to sqlite3_value_text16()
+ ** above. If this is the case, then the db->mallocFailed flag needs to
+ ** be cleared before returning. Do this directly, instead of via
+ ** sqlite3ApiExit(), to avoid setting the database handle error message.
+ */
+ db->mallocFailed = 0;
+ sqlite3_mutex_leave(db->mutex);
return z;
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -818,12 +1181,12 @@ const void *sqlite3_errmsg16(sqlite3 *db){
** passed to this function, we assume a malloc() failed during sqlite3_open().
*/
int sqlite3_errcode(sqlite3 *db){
- if( !db || sqlite3MallocFailed() ){
- return SQLITE_NOMEM;
- }
- if( sqlite3SafetyCheck(db) ){
+ if( db && !sqlite3SafetyCheckSickOrOk(db) ){
return SQLITE_MISUSE;
}
+ if( !db || db->mallocFailed ){
+ return SQLITE_NOMEM;
+ }
return db->errCode & db->errMask;
}
@@ -841,10 +1204,9 @@ static int createCollation(
){
CollSeq *pColl;
int enc2;
+ int nName;
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
+ assert( sqlite3_mutex_held(db->mutex) );
/* If SQLITE_UTF16 is specified as the encoding type, transform this
** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the
@@ -854,17 +1216,16 @@ static int createCollation(
if( enc2==SQLITE_UTF16 ){
enc2 = SQLITE_UTF16NATIVE;
}
-
if( (enc2&~3)!=0 ){
- sqlite3Error(db, SQLITE_ERROR, "unknown encoding");
- return SQLITE_ERROR;
+ return SQLITE_MISUSE;
}
/* Check if this call is removing or replacing an existing collation
** sequence. If so, and there are active VMs, return busy. If there
** are no active VMs, invalidate any pre-compiled statements.
*/
- pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, strlen(zName), 0);
+ nName = sqlite3Strlen(db, zName);
+ pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, nName, 0);
if( pColl && pColl->xCmp ){
if( db->activeVdbeCnt ){
sqlite3Error(db, SQLITE_BUSY,
@@ -880,7 +1241,7 @@ static int createCollation(
** to be called.
*/
if( (pColl->enc & ~SQLITE_UTF16_ALIGNED)==enc2 ){
- CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName, strlen(zName));
+ CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
int j;
for(j=0; j<3; j++){
CollSeq *p = &aColl[j];
@@ -894,7 +1255,7 @@ static int createCollation(
}
}
- pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, strlen(zName), 1);
+ pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, nName, 1);
if( pColl ){
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
@@ -907,29 +1268,140 @@ static int createCollation(
/*
+** This array defines hard upper bounds on limit values. The
+** initializer must be kept in sync with the SQLITE_LIMIT_*
+** #defines in sqlite3.h.
+*/
+static const int aHardLimit[] = {
+ SQLITE_MAX_LENGTH,
+ SQLITE_MAX_SQL_LENGTH,
+ SQLITE_MAX_COLUMN,
+ SQLITE_MAX_EXPR_DEPTH,
+ SQLITE_MAX_COMPOUND_SELECT,
+ SQLITE_MAX_VDBE_OP,
+ SQLITE_MAX_FUNCTION_ARG,
+ SQLITE_MAX_ATTACHED,
+ SQLITE_MAX_LIKE_PATTERN_LENGTH,
+ SQLITE_MAX_VARIABLE_NUMBER,
+};
+
+/*
+** Make sure the hard limits are set to reasonable values
+*/
+#if SQLITE_MAX_LENGTH<100
+# error SQLITE_MAX_LENGTH must be at least 100
+#endif
+#if SQLITE_MAX_SQL_LENGTH<100
+# error SQLITE_MAX_SQL_LENGTH must be at least 100
+#endif
+#if SQLITE_MAX_SQL_LENGTH>SQLITE_MAX_LENGTH
+# error SQLITE_MAX_SQL_LENGTH must not be greater than SQLITE_MAX_LENGTH
+#endif
+#if SQLITE_MAX_COMPOUND_SELECT<2
+# error SQLITE_MAX_COMPOUND_SELECT must be at least 2
+#endif
+#if SQLITE_MAX_VDBE_OP<40
+# error SQLITE_MAX_VDBE_OP must be at least 40
+#endif
+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
+#endif
+#if SQLITE_MAX_ATTACH<0 || SQLITE_MAX_ATTACH>30
+# error SQLITE_MAX_ATTACH must be between 0 and 30
+#endif
+#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
+# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
+#endif
+#if SQLITE_MAX_VARIABLE_NUMBER<1
+# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1
+#endif
+
+
+/*
+** Change the value of a limit. Report the old value.
+** If an invalid limit index is supplied, report -1.
+** Make no changes but still report the old value if the
+** new limit is negative.
+**
+** A new lower limit does not shrink existing constructs.
+** It merely prevents new constructs that exceed the limit
+** from forming.
+*/
+int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
+ int oldLimit;
+ if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
+ return -1;
+ }
+ oldLimit = db->aLimit[limitId];
+ if( newLimit>=0 ){
+ if( newLimit>aHardLimit[limitId] ){
+ newLimit = aHardLimit[limitId];
+ }
+ db->aLimit[limitId] = newLimit;
+ }
+ return oldLimit;
+}
+
+/*
** This routine does the work of opening a database on behalf of
** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
** is UTF-8 encoded.
*/
static int openDatabase(
const char *zFilename, /* Database filename UTF-8 encoded */
- sqlite3 **ppDb /* OUT: Returned database handle */
+ sqlite3 **ppDb, /* OUT: Returned database handle */
+ unsigned flags, /* Operational flags */
+ const char *zVfs /* Name of the VFS to use */
){
sqlite3 *db;
int rc;
CollSeq *pColl;
+ int isThreadsafe = 1;
- assert( !sqlite3MallocFailed() );
+#ifndef SQLITE_OMIT_AUTOINIT
+ rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+
+ if( flags&SQLITE_OPEN_NOMUTEX ){
+ isThreadsafe = 0;
+ }
+
+ /* Remove harmful bits from the flags parameter */
+ flags &= ~( SQLITE_OPEN_DELETEONCLOSE |
+ SQLITE_OPEN_MAIN_DB |
+ SQLITE_OPEN_TEMP_DB |
+ SQLITE_OPEN_TRANSIENT_DB |
+ SQLITE_OPEN_MAIN_JOURNAL |
+ SQLITE_OPEN_TEMP_JOURNAL |
+ SQLITE_OPEN_SUBJOURNAL |
+ SQLITE_OPEN_MASTER_JOURNAL |
+ SQLITE_OPEN_NOMUTEX
+ );
/* Allocate the sqlite data structure */
- db = sqliteMalloc( sizeof(sqlite3) );
+ db = sqlite3MallocZero( sizeof(sqlite3) );
if( db==0 ) goto opendb_out;
+ if( sqlite3Config.bFullMutex && isThreadsafe ){
+ db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+ if( db->mutex==0 ){
+ sqlite3_free(db);
+ db = 0;
+ goto opendb_out;
+ }
+ }
+ sqlite3_mutex_enter(db->mutex);
db->errMask = 0xff;
db->priorNewRowid = 0;
- db->magic = SQLITE_MAGIC_BUSY;
db->nDb = 2;
+ db->magic = SQLITE_MAGIC_BUSY;
db->aDb = db->aDbStatic;
+
+ assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
+ memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
db->autoCommit = 1;
+ db->nextAutovac = -1;
+ db->nextPagesize = 0;
db->flags |= SQLITE_ShortColNames
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
@@ -944,19 +1416,28 @@ static int openDatabase(
sqlite3HashInit(&db->aModule, SQLITE_HASH_STRING, 0);
#endif
+ db->pVfs = sqlite3_vfs_find(zVfs);
+ if( !db->pVfs ){
+ rc = SQLITE_ERROR;
+ db->magic = SQLITE_MAGIC_SICK;
+ sqlite3Error(db, rc, "no such vfs: %s", zVfs);
+ goto opendb_out;
+ }
+
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
** and UTF-16, so add a version for each to avoid any unnecessary
** conversions. The only error that can occur here is a malloc() failure.
*/
- if( createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0) ||
- createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0) ||
- createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0) ||
- (db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0))==0
- ){
- assert( sqlite3MallocFailed() );
- db->magic = SQLITE_MAGIC_CLOSED;
+ createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0);
+ createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0);
+ createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
+ createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
+ if( db->mallocFailed ){
+ db->magic = SQLITE_MAGIC_SICK;
goto opendb_out;
}
+ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0);
+ assert( db->pDfltColl!=0 );
/* Also add a UTF-8 case-insensitive collation sequence. */
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
@@ -969,15 +1450,17 @@ static int openDatabase(
}
/* Open the backend database driver */
- rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE,
+ db->openFlags = flags;
+ rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE,
+ flags | SQLITE_OPEN_MAIN_DB,
&db->aDb[0].pBt);
if( rc!=SQLITE_OK ){
sqlite3Error(db, rc, 0);
- db->magic = SQLITE_MAGIC_CLOSED;
+ db->magic = SQLITE_MAGIC_SICK;
goto opendb_out;
}
- db->aDb[0].pSchema = sqlite3SchemaGet(db->aDb[0].pBt);
- db->aDb[1].pSchema = sqlite3SchemaGet(0);
+ db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
+ db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
/* The default safety_level for the main database is 'full'; for the temp
@@ -991,7 +1474,7 @@ static int openDatabase(
#endif
db->magic = SQLITE_MAGIC_OPEN;
- if( sqlite3MallocFailed() ){
+ if( db->mallocFailed ){
goto opendb_out;
}
@@ -1011,25 +1494,38 @@ static int openDatabase(
}
#ifdef SQLITE_ENABLE_FTS1
- if( !sqlite3MallocFailed() ){
+ if( !db->mallocFailed ){
extern int sqlite3Fts1Init(sqlite3*);
rc = sqlite3Fts1Init(db);
}
#endif
#ifdef SQLITE_ENABLE_FTS2
- if( !sqlite3MallocFailed() && rc==SQLITE_OK ){
+ if( !db->mallocFailed && rc==SQLITE_OK ){
extern int sqlite3Fts2Init(sqlite3*);
rc = sqlite3Fts2Init(db);
}
#endif
+#ifdef SQLITE_ENABLE_FTS3
+ if( !db->mallocFailed && rc==SQLITE_OK ){
+ rc = sqlite3Fts3Init(db);
+ }
+#endif
+
#ifdef SQLITE_ENABLE_ICU
- if( !sqlite3MallocFailed() && rc==SQLITE_OK ){
+ if( !db->mallocFailed && rc==SQLITE_OK ){
extern int sqlite3IcuInit(sqlite3*);
rc = sqlite3IcuInit(db);
}
#endif
+
+#ifdef SQLITE_ENABLE_RTREE
+ if( !db->mallocFailed && rc==SQLITE_OK){
+ rc = sqlite3RtreeInit(db);
+ }
+#endif
+
sqlite3Error(db, rc, 0);
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
@@ -1042,7 +1538,14 @@ static int openDatabase(
SQLITE_DEFAULT_LOCKING_MODE);
#endif
+ /* Enable the lookaside-malloc subsystem */
+ setupLookaside(db, 0, sqlite3Config.szLookaside, sqlite3Config.nLookaside);
+
opendb_out:
+ if( db ){
+ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3Config.bFullMutex==0 );
+ sqlite3_mutex_leave(db->mutex);
+ }
if( SQLITE_NOMEM==(rc = sqlite3_errcode(db)) ){
sqlite3_close(db);
db = 0;
@@ -1058,7 +1561,16 @@ int sqlite3_open(
const char *zFilename,
sqlite3 **ppDb
){
- return openDatabase(zFilename, ppDb);
+ return openDatabase(zFilename, ppDb,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
+}
+int sqlite3_open_v2(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb, /* OUT: SQLite db handle */
+ int flags, /* Flags */
+ const char *zVfs /* Name of VFS module to use */
+){
+ return openDatabase(filename, ppDb, flags, zVfs);
}
#ifndef SQLITE_OMIT_UTF16
@@ -1070,24 +1582,28 @@ int sqlite3_open16(
sqlite3 **ppDb
){
char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */
- int rc = SQLITE_OK;
sqlite3_value *pVal;
+ int rc;
assert( zFilename );
assert( ppDb );
*ppDb = 0;
- pVal = sqlite3ValueNew();
+#ifndef SQLITE_OMIT_AUTOINIT
+ rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ pVal = sqlite3ValueNew(0);
sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC);
zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8);
if( zFilename8 ){
- rc = openDatabase(zFilename8, ppDb);
- if( rc==SQLITE_OK && *ppDb ){
- rc = sqlite3_exec(*ppDb, "PRAGMA encoding = 'UTF-16'", 0, 0, 0);
- if( rc!=SQLITE_OK ){
- sqlite3_close(*ppDb);
- *ppDb = 0;
- }
+ rc = openDatabase(zFilename8, ppDb,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
+ assert( *ppDb || rc==SQLITE_NOMEM );
+ if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){
+ ENC(*ppDb) = SQLITE_UTF16NATIVE;
}
+ }else{
+ rc = SQLITE_NOMEM;
}
sqlite3ValueFree(pVal);
@@ -1096,45 +1612,6 @@ int sqlite3_open16(
#endif /* SQLITE_OMIT_UTF16 */
/*
-** The following routine destroys a virtual machine that is created by
-** the sqlite3_compile() routine. The integer returned is an SQLITE_
-** success/failure code that describes the result of executing the virtual
-** machine.
-**
-** This routine sets the error code and string returned by
-** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
-*/
-int sqlite3_finalize(sqlite3_stmt *pStmt){
- int rc;
- if( pStmt==0 ){
- rc = SQLITE_OK;
- }else{
- rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
- }
- return rc;
-}
-
-/*
-** Terminate the current execution of an SQL statement and reset it
-** back to its starting state so that it can be reused. A success code from
-** the prior execution is returned.
-**
-** This routine sets the error code and string returned by
-** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
-*/
-int sqlite3_reset(sqlite3_stmt *pStmt){
- int rc;
- if( pStmt==0 ){
- rc = SQLITE_OK;
- }else{
- rc = sqlite3VdbeReset((Vdbe*)pStmt);
- sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0);
- assert( (rc & (sqlite3_db_handle(pStmt)->errMask))==rc );
- }
- return rc;
-}
-
-/*
** Register a new collation sequence with the database handle db.
*/
int sqlite3_create_collation(
@@ -1145,9 +1622,12 @@ int sqlite3_create_collation(
int(*xCompare)(void*,int,const void*,int,const void*)
){
int rc;
- assert( !sqlite3MallocFailed() );
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
rc = createCollation(db, zName, enc, pCtx, xCompare, 0);
- return sqlite3ApiExit(db, rc);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
/*
@@ -1162,9 +1642,12 @@ int sqlite3_create_collation_v2(
void(*xDel)(void*)
){
int rc;
- assert( !sqlite3MallocFailed() );
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
rc = createCollation(db, zName, enc, pCtx, xCompare, xDel);
- return sqlite3ApiExit(db, rc);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
#ifndef SQLITE_OMIT_UTF16
@@ -1173,20 +1656,23 @@ int sqlite3_create_collation_v2(
*/
int sqlite3_create_collation16(
sqlite3* db,
- const char *zName,
+ const void *zName,
int enc,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
int rc = SQLITE_OK;
- char *zName8;
- assert( !sqlite3MallocFailed() );
- zName8 = sqlite3Utf16to8(zName, -1);
+ char *zName8;
+ sqlite3_mutex_enter(db->mutex);
+ assert( !db->mallocFailed );
+ zName8 = sqlite3Utf16to8(db, zName, -1);
if( zName8 ){
rc = createCollation(db, zName8, enc, pCtx, xCompare, 0);
- sqliteFree(zName8);
+ sqlite3DbFree(db, zName8);
}
- return sqlite3ApiExit(db, rc);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -1199,12 +1685,11 @@ int sqlite3_collation_needed(
void *pCollNeededArg,
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)
){
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
+ sqlite3_mutex_enter(db->mutex);
db->xCollNeeded = xCollNeeded;
db->xCollNeeded16 = 0;
db->pCollNeededArg = pCollNeededArg;
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -1218,12 +1703,11 @@ int sqlite3_collation_needed16(
void *pCollNeededArg,
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
){
- if( sqlite3SafetyCheck(db) ){
- return SQLITE_MISUSE;
- }
+ sqlite3_mutex_enter(db->mutex);
db->xCollNeeded = 0;
db->xCollNeeded16 = xCollNeeded16;
db->pCollNeededArg = pCollNeededArg;
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -1233,7 +1717,7 @@ int sqlite3_collation_needed16(
** This function is now an anachronism. It used to be used to recover from a
** malloc() failure, but SQLite now does this automatically.
*/
-int sqlite3_global_recover(){
+int sqlite3_global_recover(void){
return SQLITE_OK;
}
#endif
@@ -1261,45 +1745,14 @@ int sqlite3Corrupt(void){
}
#endif
-
-#ifndef SQLITE_OMIT_SHARED_CACHE
-/*
-** Enable or disable the shared pager and schema features for the
-** current thread.
-**
-** This routine should only be called when there are no open
-** database connections.
-*/
-int sqlite3_enable_shared_cache(int enable){
- ThreadData *pTd = sqlite3ThreadData();
- if( pTd ){
- /* It is only legal to call sqlite3_enable_shared_cache() when there
- ** are no currently open b-trees that were opened by the calling thread.
- ** This condition is only easy to detect if the shared-cache were
- ** previously enabled (and is being disabled).
- */
- if( pTd->pBtree && !enable ){
- assert( pTd->useSharedData );
- return SQLITE_MISUSE;
- }
-
- pTd->useSharedData = enable;
- sqlite3ReleaseThreadData();
- }
- return sqlite3ApiExit(0, SQLITE_OK);
-}
-#endif
-
/*
** This is a convenience routine that makes sure that all thread-specific
** data for this thread has been deallocated.
+**
+** SQLite no longer uses thread-specific data so this routine is now a
+** no-op. It is retained for historical compatibility.
*/
void sqlite3_thread_cleanup(void){
- ThreadData *pTd = sqlite3OsThreadSpecificData(0);
- if( pTd ){
- memset(pTd, 0, sizeof(*pTd));
- sqlite3OsThreadSpecificData(-1);
- }
}
/*
@@ -1316,7 +1769,7 @@ int sqlite3_table_column_metadata(
char const **pzCollSeq, /* OUTPUT: Collation sequence name */
int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
int *pPrimaryKey, /* OUTPUT: True if column part of PK */
- int *pAutoinc /* OUTPUT: True if colums is auto-increment */
+ int *pAutoinc /* OUTPUT: True if column is auto-increment */
){
int rc;
char *zErrMsg = 0;
@@ -1331,10 +1784,11 @@ int sqlite3_table_column_metadata(
int autoinc = 0;
/* Ensure the database schema has been loaded */
- if( sqlite3SafetyOn(db) ){
- return SQLITE_MISUSE;
- }
+ sqlite3_mutex_enter(db->mutex);
+ (void)sqlite3SafetyOn(db);
+ sqlite3BtreeEnterAll(db);
rc = sqlite3Init(db, &zErrMsg);
+ sqlite3BtreeLeaveAll(db);
if( SQLITE_OK!=rc ){
goto error_out;
}
@@ -1378,9 +1832,9 @@ int sqlite3_table_column_metadata(
if( pCol ){
zDataType = pCol->zType;
zCollSeq = pCol->zColl;
- notnull = (pCol->notNull?1:0);
- primarykey = (pCol->isPrimKey?1:0);
- autoinc = ((pTab->iPKey==iCol && pTab->autoInc)?1:0);
+ notnull = pCol->notNull!=0;
+ primarykey = pCol->isPrimKey!=0;
+ autoinc = pTab->iPKey==iCol && pTab->autoInc;
}else{
zDataType = "INTEGER";
primarykey = 1;
@@ -1390,9 +1844,7 @@ int sqlite3_table_column_metadata(
}
error_out:
- if( sqlite3SafetyOff(db) ){
- rc = SQLITE_MISUSE;
- }
+ (void)sqlite3SafetyOff(db);
/* Whether the function call succeeded or failed, set the output parameters
** to whatever their local counterparts contain. If an error did occur,
@@ -1405,39 +1857,149 @@ error_out:
if( pAutoinc ) *pAutoinc = autoinc;
if( SQLITE_OK==rc && !pTab ){
- sqlite3SetString(&zErrMsg, "no such table column: ", zTableName, ".",
- zColumnName, 0);
+ sqlite3DbFree(db, zErrMsg);
+ zErrMsg = sqlite3MPrintf(db, "no such table column: %s.%s", zTableName,
+ zColumnName);
rc = SQLITE_ERROR;
}
sqlite3Error(db, rc, (zErrMsg?"%s":0), zErrMsg);
- sqliteFree(zErrMsg);
- return sqlite3ApiExit(db, rc);
-}
-#endif
-
-/*
-** Set all the parameters in the compiled SQL statement to NULL.
-*/
-int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
- int i;
- int rc = SQLITE_OK;
- for(i=1; rc==SQLITE_OK && i<=sqlite3_bind_parameter_count(pStmt); i++){
- rc = sqlite3_bind_null(pStmt, i);
- }
+ sqlite3DbFree(db, zErrMsg);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
return rc;
}
+#endif
/*
** Sleep for a little while. Return the amount of time slept.
*/
int sqlite3_sleep(int ms){
- return sqlite3OsSleep(ms);
+ sqlite3_vfs *pVfs;
+ int rc;
+ pVfs = sqlite3_vfs_find(0);
+ if( pVfs==0 ) return 0;
+
+ /* This function works in milliseconds, but the underlying OsSleep()
+ ** API uses microseconds. Hence the 1000's.
+ */
+ rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000);
+ return rc;
}
/*
** Enable or disable the extended result codes.
*/
int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
+ sqlite3_mutex_enter(db->mutex);
db->errMask = onoff ? 0xffffffff : 0xff;
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
+
+/*
+** Invoke the xFileControl method on a particular database.
+*/
+int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
+ int rc = SQLITE_ERROR;
+ int iDb;
+ sqlite3_mutex_enter(db->mutex);
+ if( zDbName==0 ){
+ iDb = 0;
+ }else{
+ for(iDb=0; iDb<db->nDb; iDb++){
+ if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break;
+ }
+ }
+ if( iDb<db->nDb ){
+ Btree *pBtree = db->aDb[iDb].pBt;
+ if( pBtree ){
+ Pager *pPager;
+ sqlite3_file *fd;
+ sqlite3BtreeEnter(pBtree);
+ pPager = sqlite3BtreePager(pBtree);
+ assert( pPager!=0 );
+ fd = sqlite3PagerFile(pPager);
+ assert( fd!=0 );
+ if( fd->pMethods ){
+ rc = sqlite3OsFileControl(fd, op, pArg);
+ }
+ sqlite3BtreeLeave(pBtree);
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
+/*
+** Interface to the testing logic.
+*/
+int sqlite3_test_control(int op, ...){
+ int rc = 0;
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ va_list ap;
+ va_start(ap, op);
+ switch( op ){
+
+ /*
+ ** Save the current state of the PRNG.
+ */
+ case SQLITE_TESTCTRL_PRNG_SAVE: {
+ sqlite3PrngSaveState();
+ break;
+ }
+
+ /*
+ ** Restore the state of the PRNG to the last state saved using
+ ** PRNG_SAVE. If PRNG_SAVE has never before been called, then
+ ** this verb acts like PRNG_RESET.
+ */
+ case SQLITE_TESTCTRL_PRNG_RESTORE: {
+ sqlite3PrngRestoreState();
+ break;
+ }
+
+ /*
+ ** Reset the PRNG back to its uninitialized state. The next call
+ ** to sqlite3_randomness() will reseed the PRNG using a single call
+ ** to the xRandomness method of the default VFS.
+ */
+ case SQLITE_TESTCTRL_PRNG_RESET: {
+ sqlite3PrngResetState();
+ break;
+ }
+
+ /*
+ ** sqlite3_test_control(BITVEC_TEST, size, program)
+ **
+ ** Run a test against a Bitvec object of size. The program argument
+ ** is an array of integers that defines the test. Return -1 on a
+ ** memory allocation error, 0 on success, or non-zero for an error.
+ ** See the sqlite3BitvecBuiltinTest() for additional information.
+ */
+ case SQLITE_TESTCTRL_BITVEC_TEST: {
+ int sz = va_arg(ap, int);
+ int *aProg = va_arg(ap, int*);
+ rc = sqlite3BitvecBuiltinTest(sz, aProg);
+ break;
+ }
+
+ /*
+ ** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
+ **
+ ** Register hooks to call to indicate which malloc() failures
+ ** are benign.
+ */
+ case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: {
+ typedef void (*void_function)(void);
+ void_function xBenignBegin;
+ void_function xBenignEnd;
+ xBenignBegin = va_arg(ap, void_function);
+ xBenignEnd = va_arg(ap, void_function);
+ sqlite3BenignMallocHooks(xBenignBegin, xBenignEnd);
+ break;
+ }
+ }
+ va_end(ap);
+#endif /* SQLITE_OMIT_BUILTIN_TEST */
+ return rc;
+}
diff --git a/third_party/sqlite/src/malloc.c b/third_party/sqlite/src/malloc.c
new file mode 100755
index 0000000..fa9c88f
--- /dev/null
+++ b/third_party/sqlite/src/malloc.c
@@ -0,0 +1,742 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+**
+** Memory allocation functions used throughout sqlite.
+**
+** $Id: malloc.c,v 1.34 2008/08/05 17:53:23 drh Exp $
+*/
+#include "sqliteInt.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+/*
+** This routine runs when the memory allocator sees that the
+** total memory allocation is about to exceed the soft heap
+** limit.
+*/
+static void softHeapLimitEnforcer(
+ void *NotUsed,
+ sqlite3_int64 inUse,
+ int allocSize
+){
+ sqlite3_release_memory(allocSize);
+}
+
+/*
+** Set the soft heap-size limit for the library. Passing a zero or
+** negative value indicates no limit.
+*/
+void sqlite3_soft_heap_limit(int n){
+ sqlite3_uint64 iLimit;
+ int overage;
+ if( n<0 ){
+ iLimit = 0;
+ }else{
+ iLimit = n;
+ }
+ sqlite3_initialize();
+ if( iLimit>0 ){
+ sqlite3_memory_alarm(softHeapLimitEnforcer, 0, iLimit);
+ }else{
+ sqlite3_memory_alarm(0, 0, 0);
+ }
+ overage = sqlite3_memory_used() - n;
+ if( overage>0 ){
+ sqlite3_release_memory(overage);
+ }
+}
+
+/*
+** Attempt to release up to n bytes of non-essential memory currently
+** held by SQLite. An example of non-essential memory is memory used to
+** cache database pages that are not currently in use.
+*/
+int sqlite3_release_memory(int n){
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ int nRet = sqlite3VdbeReleaseMemory(n);
+ nRet += sqlite3PagerReleaseMemory(n-nRet);
+ return nRet;
+#else
+ return SQLITE_OK;
+#endif
+}
+
+/*
+** State information local to the memory allocation subsystem.
+*/
+static struct {
+ sqlite3_mutex *mutex; /* Mutex to serialize access */
+
+ /*
+ ** The alarm callback and its arguments. The mem0.mutex lock will
+ ** be held while the callback is running. Recursive calls into
+ ** the memory subsystem are allowed, but no new callbacks will be
+ ** issued. The alarmBusy variable is set to prevent recursive
+ ** callbacks.
+ */
+ sqlite3_int64 alarmThreshold;
+ void (*alarmCallback)(void*, sqlite3_int64,int);
+ void *alarmArg;
+ int alarmBusy;
+
+ /*
+ ** Pointers to the end of sqlite3Config.pScratch and
+ ** sqlite3Config.pPage to a block of memory that records
+ ** which pages are available.
+ */
+ u32 *aScratchFree;
+ u32 *aPageFree;
+
+ /* Number of free pages for scratch and page-cache memory */
+ u32 nScratchFree;
+ u32 nPageFree;
+} mem0;
+
+/*
+** Initialize the memory allocation subsystem.
+*/
+int sqlite3MallocInit(void){
+ if( sqlite3Config.m.xMalloc==0 ){
+ sqlite3MemSetDefault();
+ }
+ memset(&mem0, 0, sizeof(mem0));
+ if( sqlite3Config.bCoreMutex ){
+ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ if( sqlite3Config.pScratch && sqlite3Config.szScratch>=100
+ && sqlite3Config.nScratch>=0 ){
+ int i;
+ sqlite3Config.szScratch -= 4;
+ mem0.aScratchFree = (u32*)&((char*)sqlite3Config.pScratch)
+ [sqlite3Config.szScratch*sqlite3Config.nScratch];
+ for(i=0; i<sqlite3Config.nScratch; i++){ mem0.aScratchFree[i] = i; }
+ mem0.nScratchFree = sqlite3Config.nScratch;
+ }else{
+ sqlite3Config.pScratch = 0;
+ sqlite3Config.szScratch = 0;
+ }
+ if( sqlite3Config.pPage && sqlite3Config.szPage>=512
+ && sqlite3Config.nPage>=1 ){
+ int i;
+ int overhead;
+ int sz = sqlite3Config.szPage;
+ int n = sqlite3Config.nPage;
+ overhead = (4*n + sz - 1)/sz;
+ sqlite3Config.nPage -= overhead;
+ mem0.aPageFree = (u32*)&((char*)sqlite3Config.pPage)
+ [sqlite3Config.szPage*sqlite3Config.nPage];
+ for(i=0; i<sqlite3Config.nPage; i++){ mem0.aPageFree[i] = i; }
+ mem0.nPageFree = sqlite3Config.nPage;
+ }else{
+ sqlite3Config.pPage = 0;
+ sqlite3Config.szPage = 0;
+ }
+ return sqlite3Config.m.xInit(sqlite3Config.m.pAppData);
+}
+
+/*
+** Deinitialize the memory allocation subsystem.
+*/
+void sqlite3MallocEnd(void){
+ sqlite3Config.m.xShutdown(sqlite3Config.m.pAppData);
+ memset(&mem0, 0, sizeof(mem0));
+}
+
+/*
+** Return the amount of memory currently checked out.
+*/
+sqlite3_int64 sqlite3_memory_used(void){
+ int n, mx;
+ sqlite3_int64 res;
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
+ res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */
+ return res;
+}
+
+/*
+** Return the maximum amount of memory that has ever been
+** checked out since either the beginning of this process
+** or since the most recent reset.
+*/
+sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
+ int n, mx;
+ sqlite3_int64 res;
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag);
+ res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */
+ return res;
+}
+
+/*
+** Change the alarm callback
+*/
+int sqlite3_memory_alarm(
+ void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
+ void *pArg,
+ sqlite3_int64 iThreshold
+){
+ sqlite3_mutex_enter(mem0.mutex);
+ mem0.alarmCallback = xCallback;
+ mem0.alarmArg = pArg;
+ mem0.alarmThreshold = iThreshold;
+ sqlite3_mutex_leave(mem0.mutex);
+ return SQLITE_OK;
+}
+
+/*
+** Trigger the alarm
+*/
+static void sqlite3MallocAlarm(int nByte){
+ void (*xCallback)(void*,sqlite3_int64,int);
+ sqlite3_int64 nowUsed;
+ void *pArg;
+ if( mem0.alarmCallback==0 || mem0.alarmBusy ) return;
+ mem0.alarmBusy = 1;
+ xCallback = mem0.alarmCallback;
+ nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ pArg = mem0.alarmArg;
+ sqlite3_mutex_leave(mem0.mutex);
+ xCallback(pArg, nowUsed, nByte);
+ sqlite3_mutex_enter(mem0.mutex);
+ mem0.alarmBusy = 0;
+}
+
+/*
+** Do a memory allocation with statistics and alarms. Assume the
+** lock is already held.
+*/
+static int mallocWithAlarm(int n, void **pp){
+ int nFull;
+ void *p;
+ assert( sqlite3_mutex_held(mem0.mutex) );
+ nFull = sqlite3Config.m.xRoundup(n);
+ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
+ if( mem0.alarmCallback!=0 ){
+ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ if( nUsed+nFull >= mem0.alarmThreshold ){
+ sqlite3MallocAlarm(nFull);
+ }
+ }
+ p = sqlite3Config.m.xMalloc(nFull);
+ if( p==0 && mem0.alarmCallback ){
+ sqlite3MallocAlarm(nFull);
+ p = sqlite3Config.m.xMalloc(nFull);
+ }
+ if( p ){
+ nFull = sqlite3MallocSize(p);
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
+ }
+ *pp = p;
+ return nFull;
+}
+
+/*
+** Allocate memory. This routine is like sqlite3_malloc() except that it
+** assumes the memory subsystem has already been initialized.
+*/
+void *sqlite3Malloc(int n){
+ void *p;
+ if( n<=0 ){
+ p = 0;
+ }else if( sqlite3Config.bMemstat ){
+ sqlite3_mutex_enter(mem0.mutex);
+ mallocWithAlarm(n, &p);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ p = sqlite3Config.m.xMalloc(n);
+ }
+ return p;
+}
+
+/*
+** This version of the memory allocation is for use by the application.
+** First make sure the memory subsystem is initialized, then do the
+** allocation.
+*/
+void *sqlite3_malloc(int n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return sqlite3Malloc(n);
+}
+
+/*
+** Each thread may only have a single outstanding allocation from
+** xScratchMalloc(). We verify this constraint in the single-threaded
+** case by setting scratchAllocOut to 1 when an allocation
+** is outstanding clearing it when the allocation is freed.
+*/
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+static int scratchAllocOut = 0;
+#endif
+
+
+/*
+** Allocate memory that is to be used and released right away.
+** This routine is similar to alloca() in that it is not intended
+** for situations where the memory might be held long-term. This
+** routine is intended to get memory to old large transient data
+** structures that would not normally fit on the stack of an
+** embedded processor.
+*/
+void *sqlite3ScratchMalloc(int n){
+ void *p;
+ assert( n>0 );
+
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ /* Verify that no more than one scratch allocation per thread
+ ** is outstanding at one time. (This is only checked in the
+ ** single-threaded case since checking in the multi-threaded case
+ ** would be much more complicated.) */
+ assert( scratchAllocOut==0 );
+#endif
+
+ if( sqlite3Config.szScratch<n ){
+ goto scratch_overflow;
+ }else{
+ sqlite3_mutex_enter(mem0.mutex);
+ if( mem0.nScratchFree==0 ){
+ sqlite3_mutex_leave(mem0.mutex);
+ goto scratch_overflow;
+ }else{
+ int i;
+ i = mem0.aScratchFree[--mem0.nScratchFree];
+ sqlite3_mutex_leave(mem0.mutex);
+ i *= sqlite3Config.szScratch;
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
+ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ p = (void*)&((char*)sqlite3Config.pScratch)[i];
+ }
+ }
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ scratchAllocOut = p!=0;
+#endif
+
+ return p;
+
+scratch_overflow:
+ if( sqlite3Config.bMemstat ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ n = mallocWithAlarm(n, &p);
+ if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ p = sqlite3Config.m.xMalloc(n);
+ }
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ scratchAllocOut = p!=0;
+#endif
+ return p;
+}
+void sqlite3ScratchFree(void *p){
+ if( p ){
+
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ /* Verify that no more than one scratch allocation per thread
+ ** is outstanding at one time. (This is only checked in the
+ ** single-threaded case since checking in the multi-threaded case
+ ** would be much more complicated.) */
+ assert( scratchAllocOut==1 );
+ scratchAllocOut = 0;
+#endif
+
+ if( sqlite3Config.pScratch==0
+ || p<sqlite3Config.pScratch
+ || p>=(void*)mem0.aScratchFree ){
+ if( sqlite3Config.bMemstat ){
+ int iSize = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
+ sqlite3Config.m.xFree(p);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ sqlite3Config.m.xFree(p);
+ }
+ }else{
+ int i;
+ i = (u8 *)p - (u8 *)sqlite3Config.pScratch;
+ i /= sqlite3Config.szScratch;
+ assert( i>=0 && i<sqlite3Config.nScratch );
+ sqlite3_mutex_enter(mem0.mutex);
+ assert( mem0.nScratchFree<sqlite3Config.nScratch );
+ mem0.aScratchFree[mem0.nScratchFree++] = i;
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
+ sqlite3_mutex_leave(mem0.mutex);
+ }
+ }
+}
+
+/*
+** Allocate memory to be used by the page cache. Make use of the
+** memory buffer provided by SQLITE_CONFIG_PAGECACHE if there is one
+** and that memory is of the right size and is not completely
+** consumed. Otherwise, failover to sqlite3Malloc().
+*/
+void *sqlite3PageMalloc(int n){
+ void *p;
+ assert( n>0 );
+ assert( (n & (n-1))==0 );
+ assert( n>=512 && n<=32768 );
+
+ if( sqlite3Config.szPage<n ){
+ goto page_overflow;
+ }else{
+ sqlite3_mutex_enter(mem0.mutex);
+ if( mem0.nPageFree==0 ){
+ sqlite3_mutex_leave(mem0.mutex);
+ goto page_overflow;
+ }else{
+ int i;
+ i = mem0.aPageFree[--mem0.nPageFree];
+ sqlite3_mutex_leave(mem0.mutex);
+ i *= sqlite3Config.szPage;
+ sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
+ p = (void*)&((char*)sqlite3Config.pPage)[i];
+ }
+ }
+ return p;
+
+page_overflow:
+ if( sqlite3Config.bMemstat ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
+ n = mallocWithAlarm(n, &p);
+ if( p ) sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, n);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ p = sqlite3Config.m.xMalloc(n);
+ }
+ return p;
+}
+void sqlite3PageFree(void *p){
+ if( p ){
+ if( sqlite3Config.pPage==0
+ || p<sqlite3Config.pPage
+ || p>=(void*)mem0.aPageFree ){
+ /* In this case, the page allocation was obtained from a regular
+ ** call to sqlite3_mem_methods.xMalloc() (a page-cache-memory
+ ** "overflow"). Free the block with sqlite3_mem_methods.xFree().
+ */
+ if( sqlite3Config.bMemstat ){
+ int iSize = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
+ sqlite3Config.m.xFree(p);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ sqlite3Config.m.xFree(p);
+ }
+ }else{
+ /* The page allocation was allocated from the sqlite3Config.pPage
+ ** buffer. In this case all that is add the index of the page in
+ ** the sqlite3Config.pPage array to the set of free indexes stored
+ ** in the mem0.aPageFree[] array.
+ */
+ int i;
+ i = (u8 *)p - (u8 *)sqlite3Config.pPage;
+ i /= sqlite3Config.szPage;
+ assert( i>=0 && i<sqlite3Config.nPage );
+ sqlite3_mutex_enter(mem0.mutex);
+ assert( mem0.nPageFree<sqlite3Config.nPage );
+ mem0.aPageFree[mem0.nPageFree++] = i;
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
+ sqlite3_mutex_leave(mem0.mutex);
+#if !defined(NDEBUG) && 0
+ /* Assert that a duplicate was not just inserted into aPageFree[]. */
+ for(i=0; i<mem0.nPageFree-1; i++){
+ assert( mem0.aPageFree[i]!=mem0.aPageFree[mem0.nPageFree-1] );
+ }
+#endif
+ }
+ }
+}
+
+/*
+** TRUE if p is a lookaside memory allocation from db
+*/
+static int isLookaside(sqlite3 *db, void *p){
+ return db && p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+}
+
+/*
+** Return the size of a memory allocation previously obtained from
+** sqlite3Malloc() or sqlite3_malloc().
+*/
+int sqlite3MallocSize(void *p){
+ return sqlite3Config.m.xSize(p);
+}
+int sqlite3DbMallocSize(sqlite3 *db, void *p){
+ if( isLookaside(db, p) ){
+ return db->lookaside.sz;
+ }else{
+ return sqlite3Config.m.xSize(p);
+ }
+}
+
+/*
+** Free memory previously obtained from sqlite3Malloc().
+*/
+void sqlite3_free(void *p){
+ if( p==0 ) return;
+ if( sqlite3Config.bMemstat ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
+ sqlite3Config.m.xFree(p);
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ sqlite3Config.m.xFree(p);
+ }
+}
+
+/*
+** Free memory that might be associated with a particular database
+** connection.
+*/
+void sqlite3DbFree(sqlite3 *db, void *p){
+ if( isLookaside(db, p) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ pBuf->pNext = db->lookaside.pFree;
+ db->lookaside.pFree = pBuf;
+ db->lookaside.nOut--;
+ }else{
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+void *sqlite3Realloc(void *pOld, int nBytes){
+ int nOld, nNew;
+ void *pNew;
+ if( pOld==0 ){
+ return sqlite3Malloc(nBytes);
+ }
+ if( nBytes<=0 ){
+ sqlite3_free(pOld);
+ return 0;
+ }
+ nOld = sqlite3MallocSize(pOld);
+ if( sqlite3Config.bMemstat ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
+ nNew = sqlite3Config.m.xRoundup(nBytes);
+ if( nOld==nNew ){
+ pNew = pOld;
+ }else{
+ if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >=
+ mem0.alarmThreshold ){
+ sqlite3MallocAlarm(nNew-nOld);
+ }
+ pNew = sqlite3Config.m.xRealloc(pOld, nNew);
+ if( pNew==0 && mem0.alarmCallback ){
+ sqlite3MallocAlarm(nBytes);
+ pNew = sqlite3Config.m.xRealloc(pOld, nNew);
+ }
+ if( pNew ){
+ nNew = sqlite3MallocSize(pNew);
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
+ }
+ }
+ sqlite3_mutex_leave(mem0.mutex);
+ }else{
+ pNew = sqlite3Config.m.xRealloc(pOld, nBytes);
+ }
+ return pNew;
+}
+
+/*
+** The public interface to sqlite3Realloc. Make sure that the memory
+** subsystem is initialized prior to invoking sqliteRealloc.
+*/
+void *sqlite3_realloc(void *pOld, int n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return sqlite3Realloc(pOld, n);
+}
+
+
+/*
+** Allocate and zero memory.
+*/
+void *sqlite3MallocZero(int n){
+ void *p = sqlite3Malloc(n);
+ if( p ){
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/*
+** Allocate and zero memory. If the allocation fails, make
+** the mallocFailed flag in the connection pointer.
+*/
+void *sqlite3DbMallocZero(sqlite3 *db, int n){
+ void *p = sqlite3DbMallocRaw(db, n);
+ if( p ){
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/*
+** Allocate and zero memory. If the allocation fails, make
+** the mallocFailed flag in the connection pointer.
+*/
+void *sqlite3DbMallocRaw(sqlite3 *db, int n){
+ void *p;
+ if( db ){
+ LookasideSlot *pBuf;
+ if( db->mallocFailed ){
+ return 0;
+ }
+ if( db->lookaside.bEnabled && n<=db->lookaside.sz
+ && (pBuf = db->lookaside.pFree)!=0 ){
+ db->lookaside.pFree = pBuf->pNext;
+ db->lookaside.nOut++;
+ if( db->lookaside.nOut>db->lookaside.mxOut ){
+ db->lookaside.mxOut = db->lookaside.nOut;
+ }
+ return (void*)pBuf;
+ }
+ }
+ p = sqlite3Malloc(n);
+ if( !p && db ){
+ db->mallocFailed = 1;
+ }
+ return p;
+}
+
+/*
+** Resize the block of memory pointed to by p to n bytes. If the
+** resize fails, set the mallocFailed flag in the connection object.
+*/
+void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
+ void *pNew = 0;
+ if( db->mallocFailed==0 ){
+ if( p==0 ){
+ return sqlite3DbMallocRaw(db, n);
+ }
+ if( isLookaside(db, p) ){
+ if( n<=db->lookaside.sz ){
+ return p;
+ }
+ pNew = sqlite3DbMallocRaw(db, n);
+ if( pNew ){
+ memcpy(pNew, p, db->lookaside.sz);
+ sqlite3DbFree(db, p);
+ }
+ }else{
+ pNew = sqlite3_realloc(p, n);
+ if( !pNew ){
+ db->mallocFailed = 1;
+ }
+ }
+ }
+ return pNew;
+}
+
+/*
+** Attempt to reallocate p. If the reallocation fails, then free p
+** and set the mallocFailed flag in the database connection.
+*/
+void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, int n){
+ void *pNew;
+ pNew = sqlite3DbRealloc(db, p, n);
+ if( !pNew ){
+ sqlite3DbFree(db, p);
+ }
+ return pNew;
+}
+
+/*
+** Make a copy of a string in memory obtained from sqliteMalloc(). These
+** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
+** is because when memory debugging is turned on, these two functions are
+** called via macros that record the current file and line number in the
+** ThreadData structure.
+*/
+char *sqlite3DbStrDup(sqlite3 *db, const char *z){
+ char *zNew;
+ size_t n;
+ if( z==0 ){
+ return 0;
+ }
+ n = strlen(z)+1;
+ assert( (n&0x7fffffff)==n );
+ zNew = sqlite3DbMallocRaw(db, (int)n);
+ if( zNew ){
+ memcpy(zNew, z, n);
+ }
+ return zNew;
+}
+char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){
+ char *zNew;
+ if( z==0 ){
+ return 0;
+ }
+ assert( (n&0x7fffffff)==n );
+ zNew = sqlite3DbMallocRaw(db, n+1);
+ if( zNew ){
+ memcpy(zNew, z, n);
+ zNew[n] = 0;
+ }
+ return zNew;
+}
+
+/*
+** Create a string from the zFromat argument and the va_list that follows.
+** Store the string in memory obtained from sqliteMalloc() and make *pz
+** point to that string.
+*/
+void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
+ va_list ap;
+ char *z;
+
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(db, zFormat, ap);
+ va_end(ap);
+ sqlite3DbFree(db, *pz);
+ *pz = z;
+}
+
+
+/*
+** This function must be called before exiting any API function (i.e.
+** returning control to the user) that has called sqlite3_malloc or
+** sqlite3_realloc.
+**
+** The returned value is normally a copy of the second argument to this
+** function. However, if a malloc() failure has occured since the previous
+** invocation SQLITE_NOMEM is returned instead.
+**
+** If the first argument, db, is not NULL and a malloc() error has occured,
+** then the connection error-code (the value returned by sqlite3_errcode())
+** is set to SQLITE_NOMEM.
+*/
+int sqlite3ApiExit(sqlite3* db, int rc){
+ /* If the db handle is not NULL, then we must hold the connection handle
+ ** mutex here. Otherwise the read (and possible write) of db->mallocFailed
+ ** is unsafe, as is the call to sqlite3Error().
+ */
+ assert( !db || sqlite3_mutex_held(db->mutex) );
+ if( db && db->mallocFailed ){
+ sqlite3Error(db, SQLITE_NOMEM, 0);
+ db->mallocFailed = 0;
+ rc = SQLITE_NOMEM;
+ }
+ return rc & (db ? db->errMask : 0xff);
+}
diff --git a/third_party/sqlite/src/md5.c b/third_party/sqlite/src/md5.c
new file mode 100755
index 0000000..a6b9abb
--- /dev/null
+++ b/third_party/sqlite/src/md5.c
@@ -0,0 +1,389 @@
+/*
+** SQLite uses this code for testing only. It is not a part of
+** the SQLite library. This file implements two new TCL commands
+** "md5" and "md5file" that compute md5 checksums on arbitrary text
+** and on complete files. These commands are used by the "testfixture"
+** program to help verify the correct operation of the SQLite library.
+**
+** The original use of these TCL commands was to test the ROLLBACK
+** feature of SQLite. First compute the MD5-checksum of the database.
+** Then make some changes but rollback the changes rather than commit
+** them. Compute a second MD5-checksum of the file and verify that the
+** two checksums are the same. Such is the original use of this code.
+** New uses may have been added since this comment was written.
+**
+** $Id: md5.c,v 1.17 2008/05/16 04:51:55 danielk1977 Exp $
+*/
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <tcl.h>
+#include <string.h>
+#include "sqlite3.h"
+
+/*
+ * If compiled on a machine that doesn't have a 32-bit integer,
+ * you just set "uint32" to the appropriate datatype for an
+ * unsigned 32-bit integer. For example:
+ *
+ * cc -Duint32='unsigned long' md5.c
+ *
+ */
+#ifndef uint32
+# define uint32 unsigned int
+#endif
+
+struct Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+typedef char MD5Context[88];
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse (unsigned char *buf, unsigned longs){
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], const uint32 in[16]){
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void MD5Init(MD5Context *pCtx){
+ struct Context *ctx = (struct Context *)pCtx;
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static
+void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
+ struct Context *ctx = (struct Context *)pCtx;
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
+ struct Context *ctx = (struct Context *)pCtx;
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/*
+** Convert a digest into base-16. digest should be declared as
+** "unsigned char digest[16]" in the calling function. The MD5
+** digest is stored in the first 16 bytes. zBuf should
+** be "char zBuf[33]".
+*/
+static void DigestToBase16(unsigned char *digest, char *zBuf){
+ static char const zEncode[] = "0123456789abcdef";
+ int i, j;
+
+ for(j=i=0; i<16; i++){
+ int a = digest[i];
+ zBuf[j++] = zEncode[(a>>4)&0xf];
+ zBuf[j++] = zEncode[a & 0xf];
+ }
+ zBuf[j] = 0;
+}
+
+/*
+** A TCL command for md5. The argument is the text to be hashed. The
+** Result is the hash in base64.
+*/
+static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
+ MD5Context ctx;
+ unsigned char digest[16];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " TEXT\"", 0);
+ return TCL_ERROR;
+ }
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
+ MD5Final(digest, &ctx);
+ DigestToBase16(digest, interp->result);
+ return TCL_OK;
+}
+
+/*
+** A TCL command to take the md5 hash of a file. The argument is the
+** name of the file.
+*/
+static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
+ FILE *in;
+ MD5Context ctx;
+ unsigned char digest[16];
+ char zBuf[10240];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ in = fopen(argv[1],"rb");
+ if( in==0 ){
+ Tcl_AppendResult(interp,"unable to open file \"", argv[1],
+ "\" for reading", 0);
+ return TCL_ERROR;
+ }
+ MD5Init(&ctx);
+ for(;;){
+ int n;
+ n = fread(zBuf, 1, sizeof(zBuf), in);
+ if( n<=0 ) break;
+ MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
+ }
+ fclose(in);
+ MD5Final(digest, &ctx);
+ DigestToBase16(digest, interp->result);
+ return TCL_OK;
+}
+
+/*
+** Register the two TCL commands above with the TCL interpreter.
+*/
+int Md5_Init(Tcl_Interp *interp){
+ Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
+ Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
+ return TCL_OK;
+}
+
+/*
+** During testing, the special md5sum() aggregate function is available.
+** inside SQLite. The following routines implement that function.
+*/
+static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
+ MD5Context *p;
+ int i;
+ if( argc<1 ) return;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p==0 ) return;
+ if( sqlite3_aggregate_count(context)==1 ){
+ MD5Init(p);
+ }
+ for(i=0; i<argc; i++){
+ const char *zData = (char*)sqlite3_value_text(argv[i]);
+ if( zData ){
+ MD5Update(p, (unsigned char*)zData, strlen(zData));
+ }
+ }
+}
+static void md5finalize(sqlite3_context *context){
+ MD5Context *p;
+ unsigned char digest[16];
+ char zBuf[33];
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ MD5Final(digest,p);
+ DigestToBase16(digest, zBuf);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+}
+void Md5_Register(sqlite3 *db){
+ sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
+ md5step, md5finalize);
+}
diff --git a/third_party/sqlite/src/mem1.c b/third_party/sqlite/src/mem1.c
new file mode 100755
index 0000000..1a3a68e
--- /dev/null
+++ b/third_party/sqlite/src/mem1.c
@@ -0,0 +1,147 @@
+/*
+** 2007 August 14
+**
+** 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 contains low-level memory allocation drivers for when
+** SQLite will use the standard C-library malloc/realloc/free interface
+** to obtain the memory it needs.
+**
+** This file contains implementations of the low-level memory allocation
+** routines specified in the sqlite3_mem_methods object.
+**
+** $Id: mem1.c,v 1.25 2008/07/25 08:49:00 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This version of the memory allocator is the default. It is
+** used when no other memory allocator is specified using compile-time
+** macros.
+*/
+#ifdef SQLITE_SYSTEM_MALLOC
+
+/*
+** Like malloc(), but remember the size of the allocation
+** so that we can find it later using sqlite3MemSize().
+**
+** For this low-level routine, we are guaranteed that nByte>0 because
+** cases of nByte<=0 will be intercepted and dealt with by higher level
+** routines.
+*/
+static void *sqlite3MemMalloc(int nByte){
+ sqlite3_int64 *p;
+ assert( nByte>0 );
+ nByte = (nByte+7)&~7;
+ p = malloc( nByte+8 );
+ if( p ){
+ p[0] = nByte;
+ p++;
+ }
+ return (void *)p;
+}
+
+/*
+** Like free() but works for allocations obtained from sqlite3MemMalloc()
+** or sqlite3MemRealloc().
+**
+** For this low-level routine, we already know that pPrior!=0 since
+** cases where pPrior==0 will have been intecepted and dealt with
+** by higher-level routines.
+*/
+static void sqlite3MemFree(void *pPrior){
+ sqlite3_int64 *p = (sqlite3_int64*)pPrior;
+ assert( pPrior!=0 );
+ p--;
+ free(p);
+}
+
+/*
+** Like realloc(). Resize an allocation previously obtained from
+** sqlite3MemMalloc().
+**
+** For this low-level interface, we know that pPrior!=0. Cases where
+** pPrior==0 while have been intercepted by higher-level routine and
+** redirected to xMalloc. Similarly, we know that nByte>0 becauses
+** cases where nByte<=0 will have been intercepted by higher-level
+** routines and redirected to xFree.
+*/
+static void *sqlite3MemRealloc(void *pPrior, int nByte){
+ sqlite3_int64 *p = (sqlite3_int64*)pPrior;
+ assert( pPrior!=0 && nByte>0 );
+ nByte = (nByte+7)&~7;
+ p = (sqlite3_int64*)pPrior;
+ p--;
+ p = realloc(p, nByte+8 );
+ if( p ){
+ p[0] = nByte;
+ p++;
+ }
+ return (void*)p;
+}
+
+/*
+** Report the allocated size of a prior return from xMalloc()
+** or xRealloc().
+*/
+static int sqlite3MemSize(void *pPrior){
+ sqlite3_int64 *p;
+ if( pPrior==0 ) return 0;
+ p = (sqlite3_int64*)pPrior;
+ p--;
+ return p[0];
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int sqlite3MemRoundup(int n){
+ return (n+7) & ~7;
+}
+
+/*
+** Initialize this module.
+*/
+static int sqlite3MemInit(void *NotUsed){
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize this module.
+*/
+static void sqlite3MemShutdown(void *NotUsed){
+ return;
+}
+
+const sqlite3_mem_methods *sqlite3MemGetDefault(void){
+ static const sqlite3_mem_methods defaultMethods = {
+ sqlite3MemMalloc,
+ sqlite3MemFree,
+ sqlite3MemRealloc,
+ sqlite3MemSize,
+ sqlite3MemRoundup,
+ sqlite3MemInit,
+ sqlite3MemShutdown,
+ 0
+ };
+ return &defaultMethods;
+}
+
+/*
+** This routine is the only routine in this file with external linkage.
+**
+** Populate the low-level memory allocation function pointers in
+** sqlite3Config.m with pointers to the routines in this file.
+*/
+void sqlite3MemSetDefault(void){
+ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetDefault());
+}
+
+#endif /* SQLITE_SYSTEM_MALLOC */
diff --git a/third_party/sqlite/src/mem2.c b/third_party/sqlite/src/mem2.c
new file mode 100755
index 0000000..f1425e8
--- /dev/null
+++ b/third_party/sqlite/src/mem2.c
@@ -0,0 +1,443 @@
+/*
+** 2007 August 15
+**
+** 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 contains low-level memory allocation drivers for when
+** SQLite will use the standard C-library malloc/realloc/free interface
+** to obtain the memory it needs while adding lots of additional debugging
+** information to each allocation in order to help detect and fix memory
+** leaks and memory usage errors.
+**
+** This file contains implementations of the low-level memory allocation
+** routines specified in the sqlite3_mem_methods object.
+**
+** $Id: mem2.c,v 1.37 2008/07/25 08:49:00 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This version of the memory allocator is used only if the
+** SQLITE_MEMDEBUG macro is defined
+*/
+#ifdef SQLITE_MEMDEBUG
+
+/*
+** The backtrace functionality is only available with GLIBC
+*/
+#ifdef __GLIBC__
+ extern int backtrace(void**,int);
+ extern void backtrace_symbols_fd(void*const*,int,int);
+#else
+# define backtrace(A,B) 0
+# define backtrace_symbols_fd(A,B,C)
+#endif
+#include <stdio.h>
+
+/*
+** Each memory allocation looks like this:
+**
+** ------------------------------------------------------------------------
+** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard |
+** ------------------------------------------------------------------------
+**
+** The application code sees only a pointer to the allocation. We have
+** to back up from the allocation pointer to find the MemBlockHdr. The
+** MemBlockHdr tells us the size of the allocation and the number of
+** backtrace pointers. There is also a guard word at the end of the
+** MemBlockHdr.
+*/
+struct MemBlockHdr {
+ i64 iSize; /* Size of this allocation */
+ struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */
+ char nBacktrace; /* Number of backtraces on this alloc */
+ char nBacktraceSlots; /* Available backtrace slots */
+ short nTitle; /* Bytes of title; includes '\0' */
+ int iForeGuard; /* Guard word for sanity */
+};
+
+/*
+** Guard words
+*/
+#define FOREGUARD 0x80F5E153
+#define REARGUARD 0xE4676B53
+
+/*
+** Number of malloc size increments to track.
+*/
+#define NCSIZE 1000
+
+/*
+** All of the static variables used by this module are collected
+** into a single structure named "mem". This is to keep the
+** static variables organized and to reduce namespace pollution
+** when this module is combined with other in the amalgamation.
+*/
+static struct {
+
+ /*
+ ** Mutex to control access to the memory allocation subsystem.
+ */
+ sqlite3_mutex *mutex;
+
+ /*
+ ** Head and tail of a linked list of all outstanding allocations
+ */
+ struct MemBlockHdr *pFirst;
+ struct MemBlockHdr *pLast;
+
+ /*
+ ** The number of levels of backtrace to save in new allocations.
+ */
+ int nBacktrace;
+ void (*xBacktrace)(int, int, void **);
+
+ /*
+ ** Title text to insert in front of each block
+ */
+ int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */
+ char zTitle[100]; /* The title text */
+
+ /*
+ ** sqlite3MallocDisallow() increments the following counter.
+ ** sqlite3MallocAllow() decrements it.
+ */
+ int disallow; /* Do not allow memory allocation */
+
+ /*
+ ** Gather statistics on the sizes of memory allocations.
+ ** nAlloc[i] is the number of allocation attempts of i*8
+ ** bytes. i==NCSIZE is the number of allocation attempts for
+ ** sizes more than NCSIZE*8 bytes.
+ */
+ int nAlloc[NCSIZE]; /* Total number of allocations */
+ int nCurrent[NCSIZE]; /* Current number of allocations */
+ int mxCurrent[NCSIZE]; /* Highwater mark for nCurrent */
+
+} mem;
+
+
+/*
+** Adjust memory usage statistics
+*/
+static void adjustStats(int iSize, int increment){
+ int i = ((iSize+7)&~7)/8;
+ if( i>NCSIZE-1 ){
+ i = NCSIZE - 1;
+ }
+ if( increment>0 ){
+ mem.nAlloc[i]++;
+ mem.nCurrent[i]++;
+ if( mem.nCurrent[i]>mem.mxCurrent[i] ){
+ mem.mxCurrent[i] = mem.nCurrent[i];
+ }
+ }else{
+ mem.nCurrent[i]--;
+ assert( mem.nCurrent[i]>=0 );
+ }
+}
+
+/*
+** Given an allocation, find the MemBlockHdr for that allocation.
+**
+** This routine checks the guards at either end of the allocation and
+** if they are incorrect it asserts.
+*/
+static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
+ struct MemBlockHdr *p;
+ int *pInt;
+ u8 *pU8;
+ int nReserve;
+
+ p = (struct MemBlockHdr*)pAllocation;
+ p--;
+ assert( p->iForeGuard==FOREGUARD );
+ nReserve = (p->iSize+7)&~7;
+ pInt = (int*)pAllocation;
+ pU8 = (u8*)pAllocation;
+ assert( pInt[nReserve/sizeof(int)]==REARGUARD );
+ assert( (nReserve-0)<=p->iSize || pU8[nReserve-1]==0x65 );
+ assert( (nReserve-1)<=p->iSize || pU8[nReserve-2]==0x65 );
+ assert( (nReserve-2)<=p->iSize || pU8[nReserve-3]==0x65 );
+ return p;
+}
+
+/*
+** Return the number of bytes currently allocated at address p.
+*/
+static int sqlite3MemSize(void *p){
+ struct MemBlockHdr *pHdr;
+ if( !p ){
+ return 0;
+ }
+ pHdr = sqlite3MemsysGetHeader(p);
+ return pHdr->iSize;
+}
+
+/*
+** Initialize the memory allocation subsystem.
+*/
+static int sqlite3MemInit(void *NotUsed){
+ if( !sqlite3Config.bMemstat ){
+ /* If memory status is enabled, then the malloc.c wrapper will already
+ ** hold the STATIC_MEM mutex when the routines here are invoked. */
+ mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize the memory allocation subsystem.
+*/
+static void sqlite3MemShutdown(void *NotUsed){
+ mem.mutex = 0;
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int sqlite3MemRoundup(int n){
+ return (n+7) & ~7;
+}
+
+/*
+** Allocate nByte bytes of memory.
+*/
+static void *sqlite3MemMalloc(int nByte){
+ struct MemBlockHdr *pHdr;
+ void **pBt;
+ char *z;
+ int *pInt;
+ void *p = 0;
+ int totalSize;
+ int nReserve;
+ sqlite3_mutex_enter(mem.mutex);
+ assert( mem.disallow==0 );
+ nReserve = (nByte+7)&~7;
+ totalSize = nReserve + sizeof(*pHdr) + sizeof(int) +
+ mem.nBacktrace*sizeof(void*) + mem.nTitle;
+ p = malloc(totalSize);
+ if( p ){
+ z = p;
+ pBt = (void**)&z[mem.nTitle];
+ pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace];
+ pHdr->pNext = 0;
+ pHdr->pPrev = mem.pLast;
+ if( mem.pLast ){
+ mem.pLast->pNext = pHdr;
+ }else{
+ mem.pFirst = pHdr;
+ }
+ mem.pLast = pHdr;
+ pHdr->iForeGuard = FOREGUARD;
+ pHdr->nBacktraceSlots = mem.nBacktrace;
+ pHdr->nTitle = mem.nTitle;
+ if( mem.nBacktrace ){
+ void *aAddr[40];
+ pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1;
+ memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*));
+ if( mem.xBacktrace ){
+ mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]);
+ }
+ }else{
+ pHdr->nBacktrace = 0;
+ }
+ if( mem.nTitle ){
+ memcpy(z, mem.zTitle, mem.nTitle);
+ }
+ pHdr->iSize = nByte;
+ adjustStats(nByte, +1);
+ pInt = (int*)&pHdr[1];
+ pInt[nReserve/sizeof(int)] = REARGUARD;
+ memset(pInt, 0x65, nReserve);
+ p = (void*)pInt;
+ }
+ sqlite3_mutex_leave(mem.mutex);
+ return p;
+}
+
+/*
+** Free memory.
+*/
+static void sqlite3MemFree(void *pPrior){
+ struct MemBlockHdr *pHdr;
+ void **pBt;
+ char *z;
+ assert( sqlite3Config.bMemstat || mem.mutex!=0 );
+ pHdr = sqlite3MemsysGetHeader(pPrior);
+ pBt = (void**)pHdr;
+ pBt -= pHdr->nBacktraceSlots;
+ sqlite3_mutex_enter(mem.mutex);
+ if( pHdr->pPrev ){
+ assert( pHdr->pPrev->pNext==pHdr );
+ pHdr->pPrev->pNext = pHdr->pNext;
+ }else{
+ assert( mem.pFirst==pHdr );
+ mem.pFirst = pHdr->pNext;
+ }
+ if( pHdr->pNext ){
+ assert( pHdr->pNext->pPrev==pHdr );
+ pHdr->pNext->pPrev = pHdr->pPrev;
+ }else{
+ assert( mem.pLast==pHdr );
+ mem.pLast = pHdr->pPrev;
+ }
+ z = (char*)pBt;
+ z -= pHdr->nTitle;
+ adjustStats(pHdr->iSize, -1);
+ memset(z, 0x2b, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
+ pHdr->iSize + sizeof(int) + pHdr->nTitle);
+ free(z);
+ sqlite3_mutex_leave(mem.mutex);
+}
+
+/*
+** Change the size of an existing memory allocation.
+**
+** For this debugging implementation, we *always* make a copy of the
+** allocation into a new place in memory. In this way, if the
+** higher level code is using pointer to the old allocation, it is
+** much more likely to break and we are much more liking to find
+** the error.
+*/
+static void *sqlite3MemRealloc(void *pPrior, int nByte){
+ struct MemBlockHdr *pOldHdr;
+ void *pNew;
+ assert( mem.disallow==0 );
+ pOldHdr = sqlite3MemsysGetHeader(pPrior);
+ pNew = sqlite3MemMalloc(nByte);
+ if( pNew ){
+ memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
+ if( nByte>pOldHdr->iSize ){
+ memset(&((char*)pNew)[pOldHdr->iSize], 0x2b, nByte - pOldHdr->iSize);
+ }
+ sqlite3MemFree(pPrior);
+ }
+ return pNew;
+}
+
+
+const sqlite3_mem_methods *sqlite3MemGetDefault(void){
+ static const sqlite3_mem_methods defaultMethods = {
+ sqlite3MemMalloc,
+ sqlite3MemFree,
+ sqlite3MemRealloc,
+ sqlite3MemSize,
+ sqlite3MemRoundup,
+ sqlite3MemInit,
+ sqlite3MemShutdown,
+ 0
+ };
+ return &defaultMethods;
+}
+
+/*
+** Populate the low-level memory allocation function pointers in
+** sqlite3Config.m with pointers to the routines in this file.
+*/
+void sqlite3MemSetDefault(void){
+ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetDefault());
+}
+
+/*
+** Set the number of backtrace levels kept for each allocation.
+** A value of zero turns off backtracing. The number is always rounded
+** up to a multiple of 2.
+*/
+void sqlite3MemdebugBacktrace(int depth){
+ if( depth<0 ){ depth = 0; }
+ if( depth>20 ){ depth = 20; }
+ depth = (depth+1)&0xfe;
+ mem.nBacktrace = depth;
+}
+
+void sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){
+ mem.xBacktrace = xBacktrace;
+}
+
+/*
+** Set the title string for subsequent allocations.
+*/
+void sqlite3MemdebugSettitle(const char *zTitle){
+ int n = strlen(zTitle) + 1;
+ sqlite3_mutex_enter(mem.mutex);
+ if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1;
+ memcpy(mem.zTitle, zTitle, n);
+ mem.zTitle[n] = 0;
+ mem.nTitle = (n+7)&~7;
+ sqlite3_mutex_leave(mem.mutex);
+}
+
+void sqlite3MemdebugSync(){
+ struct MemBlockHdr *pHdr;
+ for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
+ void **pBt = (void**)pHdr;
+ pBt -= pHdr->nBacktraceSlots;
+ mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
+ }
+}
+
+/*
+** Open the file indicated and write a log of all unfreed memory
+** allocations into that log.
+*/
+void sqlite3MemdebugDump(const char *zFilename){
+ FILE *out;
+ struct MemBlockHdr *pHdr;
+ void **pBt;
+ int i;
+ out = fopen(zFilename, "w");
+ if( out==0 ){
+ fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
+ zFilename);
+ return;
+ }
+ for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
+ char *z = (char*)pHdr;
+ z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle;
+ fprintf(out, "**** %lld bytes at %p from %s ****\n",
+ pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???");
+ if( pHdr->nBacktrace ){
+ fflush(out);
+ pBt = (void**)pHdr;
+ pBt -= pHdr->nBacktraceSlots;
+ backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out));
+ fprintf(out, "\n");
+ }
+ }
+ fprintf(out, "COUNTS:\n");
+ for(i=0; i<NCSIZE-1; i++){
+ if( mem.nAlloc[i] ){
+ fprintf(out, " %5d: %10d %10d %10d\n",
+ i*8, mem.nAlloc[i], mem.nCurrent[i], mem.mxCurrent[i]);
+ }
+ }
+ if( mem.nAlloc[NCSIZE-1] ){
+ fprintf(out, " %5d: %10d %10d %10d\n",
+ NCSIZE*8-8, mem.nAlloc[NCSIZE-1],
+ mem.nCurrent[NCSIZE-1], mem.mxCurrent[NCSIZE-1]);
+ }
+ fclose(out);
+}
+
+/*
+** Return the number of times sqlite3MemMalloc() has been called.
+*/
+int sqlite3MemdebugMallocCount(){
+ int i;
+ int nTotal = 0;
+ for(i=0; i<NCSIZE; i++){
+ nTotal += mem.nAlloc[i];
+ }
+ return nTotal;
+}
+
+
+#endif /* SQLITE_MEMDEBUG */
diff --git a/third_party/sqlite/src/mem3.c b/third_party/sqlite/src/mem3.c
new file mode 100755
index 0000000..46a5898
--- /dev/null
+++ b/third_party/sqlite/src/mem3.c
@@ -0,0 +1,682 @@
+/*
+** 2007 October 14
+**
+** 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 contains the C functions that implement a memory
+** allocation subsystem for use by SQLite.
+**
+** This version of the memory allocation subsystem omits all
+** use of malloc(). The SQLite user supplies a block of memory
+** before calling sqlite3_initialize() from which allocations
+** are made and returned by the xMalloc() and xRealloc()
+** implementations. Once sqlite3_initialize() has been called,
+** the amount of memory available to SQLite is fixed and cannot
+** be changed.
+**
+** This version of the memory allocation subsystem is included
+** in the build only if SQLITE_ENABLE_MEMSYS3 is defined.
+**
+** $Id: mem3.c,v 1.20 2008/07/18 18:56:17 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This version of the memory allocator is only built into the library
+** SQLITE_ENABLE_MEMSYS3 is defined. Defining this symbol does not
+** mean that the library will use a memory-pool by default, just that
+** it is available. The mempool allocator is activated by calling
+** sqlite3_config().
+*/
+#ifdef SQLITE_ENABLE_MEMSYS3
+
+/*
+** Maximum size (in Mem3Blocks) of a "small" chunk.
+*/
+#define MX_SMALL 10
+
+
+/*
+** Number of freelist hash slots
+*/
+#define N_HASH 61
+
+/*
+** A memory allocation (also called a "chunk") consists of two or
+** more blocks where each block is 8 bytes. The first 8 bytes are
+** a header that is not returned to the user.
+**
+** A chunk is two or more blocks that is either checked out or
+** free. The first block has format u.hdr. u.hdr.size4x is 4 times the
+** size of the allocation in blocks if the allocation is free.
+** The u.hdr.size4x&1 bit is true if the chunk is checked out and
+** false if the chunk is on the freelist. The u.hdr.size4x&2 bit
+** is true if the previous chunk is checked out and false if the
+** previous chunk is free. The u.hdr.prevSize field is the size of
+** the previous chunk in blocks if the previous chunk is on the
+** freelist. If the previous chunk is checked out, then
+** u.hdr.prevSize can be part of the data for that chunk and should
+** not be read or written.
+**
+** We often identify a chunk by its index in mem3.aPool[]. When
+** this is done, the chunk index refers to the second block of
+** the chunk. In this way, the first chunk has an index of 1.
+** A chunk index of 0 means "no such chunk" and is the equivalent
+** of a NULL pointer.
+**
+** The second block of free chunks is of the form u.list. The
+** two fields form a double-linked list of chunks of related sizes.
+** Pointers to the head of the list are stored in mem3.aiSmall[]
+** for smaller chunks and mem3.aiHash[] for larger chunks.
+**
+** The second block of a chunk is user data if the chunk is checked
+** out. If a chunk is checked out, the user data may extend into
+** the u.hdr.prevSize value of the following chunk.
+*/
+typedef struct Mem3Block Mem3Block;
+struct Mem3Block {
+ union {
+ struct {
+ u32 prevSize; /* Size of previous chunk in Mem3Block elements */
+ u32 size4x; /* 4x the size of current chunk in Mem3Block elements */
+ } hdr;
+ struct {
+ u32 next; /* Index in mem3.aPool[] of next free chunk */
+ u32 prev; /* Index in mem3.aPool[] of previous free chunk */
+ } list;
+ } u;
+};
+
+/*
+** All of the static variables used by this module are collected
+** into a single structure named "mem3". This is to keep the
+** static variables organized and to reduce namespace pollution
+** when this module is combined with other in the amalgamation.
+*/
+static struct {
+ /*
+ ** True if we are evaluating an out-of-memory callback.
+ */
+ int alarmBusy;
+
+ /*
+ ** Mutex to control access to the memory allocation subsystem.
+ */
+ sqlite3_mutex *mutex;
+
+ /*
+ ** The minimum amount of free space that we have seen.
+ */
+ u32 mnMaster;
+
+ /*
+ ** iMaster is the index of the master chunk. Most new allocations
+ ** occur off of this chunk. szMaster is the size (in Mem3Blocks)
+ ** of the current master. iMaster is 0 if there is not master chunk.
+ ** The master chunk is not in either the aiHash[] or aiSmall[].
+ */
+ u32 iMaster;
+ u32 szMaster;
+
+ /*
+ ** Array of lists of free blocks according to the block size
+ ** for smaller chunks, or a hash on the block size for larger
+ ** chunks.
+ */
+ u32 aiSmall[MX_SMALL-1]; /* For sizes 2 through MX_SMALL, inclusive */
+ u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */
+
+ /*
+ ** Memory available for allocation. nPool is the size of the array
+ ** (in Mem3Blocks) pointed to by aPool less 2.
+ */
+ u32 nPool;
+ Mem3Block *aPool;
+} mem3;
+
+/*
+** Unlink the chunk at mem3.aPool[i] from list it is currently
+** on. *pRoot is the list that i is a member of.
+*/
+static void memsys3UnlinkFromList(u32 i, u32 *pRoot){
+ u32 next = mem3.aPool[i].u.list.next;
+ u32 prev = mem3.aPool[i].u.list.prev;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ if( prev==0 ){
+ *pRoot = next;
+ }else{
+ mem3.aPool[prev].u.list.next = next;
+ }
+ if( next ){
+ mem3.aPool[next].u.list.prev = prev;
+ }
+ mem3.aPool[i].u.list.next = 0;
+ mem3.aPool[i].u.list.prev = 0;
+}
+
+/*
+** Unlink the chunk at index i from
+** whatever list is currently a member of.
+*/
+static void memsys3Unlink(u32 i){
+ u32 size, hash;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 );
+ assert( i>=1 );
+ size = mem3.aPool[i-1].u.hdr.size4x/4;
+ assert( size==mem3.aPool[i+size-1].u.hdr.prevSize );
+ assert( size>=2 );
+ if( size <= MX_SMALL ){
+ memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]);
+ }else{
+ hash = size % N_HASH;
+ memsys3UnlinkFromList(i, &mem3.aiHash[hash]);
+ }
+}
+
+/*
+** Link the chunk at mem3.aPool[i] so that is on the list rooted
+** at *pRoot.
+*/
+static void memsys3LinkIntoList(u32 i, u32 *pRoot){
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ mem3.aPool[i].u.list.next = *pRoot;
+ mem3.aPool[i].u.list.prev = 0;
+ if( *pRoot ){
+ mem3.aPool[*pRoot].u.list.prev = i;
+ }
+ *pRoot = i;
+}
+
+/*
+** Link the chunk at index i into either the appropriate
+** small chunk list, or into the large chunk hash table.
+*/
+static void memsys3Link(u32 i){
+ u32 size, hash;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( i>=1 );
+ assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 );
+ size = mem3.aPool[i-1].u.hdr.size4x/4;
+ assert( size==mem3.aPool[i+size-1].u.hdr.prevSize );
+ assert( size>=2 );
+ if( size <= MX_SMALL ){
+ memsys3LinkIntoList(i, &mem3.aiSmall[size-2]);
+ }else{
+ hash = size % N_HASH;
+ memsys3LinkIntoList(i, &mem3.aiHash[hash]);
+ }
+}
+
+/*
+** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
+** will already be held (obtained by code in malloc.c) if
+** sqlite3Config.bMemStat is true.
+*/
+static void memsys3Enter(void){
+ if( sqlite3Config.bMemstat==0 && mem3.mutex==0 ){
+ mem3.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ sqlite3_mutex_enter(mem3.mutex);
+}
+static void memsys3Leave(void){
+ sqlite3_mutex_leave(mem3.mutex);
+}
+
+/*
+** Called when we are unable to satisfy an allocation of nBytes.
+*/
+static void memsys3OutOfMemory(int nByte){
+ if( !mem3.alarmBusy ){
+ mem3.alarmBusy = 1;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ sqlite3_mutex_leave(mem3.mutex);
+ sqlite3_release_memory(nByte);
+ sqlite3_mutex_enter(mem3.mutex);
+ mem3.alarmBusy = 0;
+ }
+}
+
+
+/*
+** Chunk i is a free chunk that has been unlinked. Adjust its
+** size parameters for check-out and return a pointer to the
+** user portion of the chunk.
+*/
+static void *memsys3Checkout(u32 i, int nBlock){
+ u32 x;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( i>=1 );
+ assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock );
+ assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock );
+ x = mem3.aPool[i-1].u.hdr.size4x;
+ mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2);
+ mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock;
+ mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2;
+ return &mem3.aPool[i];
+}
+
+/*
+** Carve a piece off of the end of the mem3.iMaster free chunk.
+** Return a pointer to the new allocation. Or, if the master chunk
+** is not large enough, return 0.
+*/
+static void *memsys3FromMaster(int nBlock){
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( mem3.szMaster>=nBlock );
+ if( nBlock>=mem3.szMaster-1 ){
+ /* Use the entire master */
+ void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
+ mem3.iMaster = 0;
+ mem3.szMaster = 0;
+ mem3.mnMaster = 0;
+ return p;
+ }else{
+ /* Split the master block. Return the tail. */
+ u32 newi, x;
+ newi = mem3.iMaster + mem3.szMaster - nBlock;
+ assert( newi > mem3.iMaster+1 );
+ mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
+ mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
+ mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
+ mem3.szMaster -= nBlock;
+ mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
+ x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
+ mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
+ if( mem3.szMaster < mem3.mnMaster ){
+ mem3.mnMaster = mem3.szMaster;
+ }
+ return (void*)&mem3.aPool[newi];
+ }
+}
+
+/*
+** *pRoot is the head of a list of free chunks of the same size
+** or same size hash. In other words, *pRoot is an entry in either
+** mem3.aiSmall[] or mem3.aiHash[].
+**
+** This routine examines all entries on the given list and tries
+** to coalesce each entries with adjacent free chunks.
+**
+** If it sees a chunk that is larger than mem3.iMaster, it replaces
+** the current mem3.iMaster with the new larger chunk. In order for
+** this mem3.iMaster replacement to work, the master chunk must be
+** linked into the hash tables. That is not the normal state of
+** affairs, of course. The calling routine must link the master
+** chunk before invoking this routine, then must unlink the (possibly
+** changed) master chunk once this routine has finished.
+*/
+static void memsys3Merge(u32 *pRoot){
+ u32 iNext, prev, size, i, x;
+
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ for(i=*pRoot; i>0; i=iNext){
+ iNext = mem3.aPool[i].u.list.next;
+ size = mem3.aPool[i-1].u.hdr.size4x;
+ assert( (size&1)==0 );
+ if( (size&2)==0 ){
+ memsys3UnlinkFromList(i, pRoot);
+ assert( i > mem3.aPool[i-1].u.hdr.prevSize );
+ prev = i - mem3.aPool[i-1].u.hdr.prevSize;
+ if( prev==iNext ){
+ iNext = mem3.aPool[prev].u.list.next;
+ }
+ memsys3Unlink(prev);
+ size = i + size/4 - prev;
+ x = mem3.aPool[prev-1].u.hdr.size4x & 2;
+ mem3.aPool[prev-1].u.hdr.size4x = size*4 | x;
+ mem3.aPool[prev+size-1].u.hdr.prevSize = size;
+ memsys3Link(prev);
+ i = prev;
+ }else{
+ size /= 4;
+ }
+ if( size>mem3.szMaster ){
+ mem3.iMaster = i;
+ mem3.szMaster = size;
+ }
+ }
+}
+
+/*
+** Return a block of memory of at least nBytes in size.
+** Return NULL if unable.
+**
+** This function assumes that the necessary mutexes, if any, are
+** already held by the caller. Hence "Unsafe".
+*/
+static void *memsys3MallocUnsafe(int nByte){
+ u32 i;
+ int nBlock;
+ int toFree;
+
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( sizeof(Mem3Block)==8 );
+ if( nByte<=12 ){
+ nBlock = 2;
+ }else{
+ nBlock = (nByte + 11)/8;
+ }
+ assert( nBlock>=2 );
+
+ /* STEP 1:
+ ** Look for an entry of the correct size in either the small
+ ** chunk table or in the large chunk hash table. This is
+ ** successful most of the time (about 9 times out of 10).
+ */
+ if( nBlock <= MX_SMALL ){
+ i = mem3.aiSmall[nBlock-2];
+ if( i>0 ){
+ memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]);
+ return memsys3Checkout(i, nBlock);
+ }
+ }else{
+ int hash = nBlock % N_HASH;
+ for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){
+ if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){
+ memsys3UnlinkFromList(i, &mem3.aiHash[hash]);
+ return memsys3Checkout(i, nBlock);
+ }
+ }
+ }
+
+ /* STEP 2:
+ ** Try to satisfy the allocation by carving a piece off of the end
+ ** of the master chunk. This step usually works if step 1 fails.
+ */
+ if( mem3.szMaster>=nBlock ){
+ return memsys3FromMaster(nBlock);
+ }
+
+
+ /* STEP 3:
+ ** Loop through the entire memory pool. Coalesce adjacent free
+ ** chunks. Recompute the master chunk as the largest free chunk.
+ ** Then try again to satisfy the allocation by carving a piece off
+ ** of the end of the master chunk. This step happens very
+ ** rarely (we hope!)
+ */
+ for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){
+ memsys3OutOfMemory(toFree);
+ if( mem3.iMaster ){
+ memsys3Link(mem3.iMaster);
+ mem3.iMaster = 0;
+ mem3.szMaster = 0;
+ }
+ for(i=0; i<N_HASH; i++){
+ memsys3Merge(&mem3.aiHash[i]);
+ }
+ for(i=0; i<MX_SMALL-1; i++){
+ memsys3Merge(&mem3.aiSmall[i]);
+ }
+ if( mem3.szMaster ){
+ memsys3Unlink(mem3.iMaster);
+ if( mem3.szMaster>=nBlock ){
+ return memsys3FromMaster(nBlock);
+ }
+ }
+ }
+
+ /* If none of the above worked, then we fail. */
+ return 0;
+}
+
+/*
+** Free an outstanding memory allocation.
+**
+** This function assumes that the necessary mutexes, if any, are
+** already held by the caller. Hence "Unsafe".
+*/
+void memsys3FreeUnsafe(void *pOld){
+ Mem3Block *p = (Mem3Block*)pOld;
+ int i;
+ u32 size, x;
+ assert( sqlite3_mutex_held(mem3.mutex) );
+ assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] );
+ i = p - mem3.aPool;
+ assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 );
+ size = mem3.aPool[i-1].u.hdr.size4x/4;
+ assert( i+size<=mem3.nPool+1 );
+ mem3.aPool[i-1].u.hdr.size4x &= ~1;
+ mem3.aPool[i+size-1].u.hdr.prevSize = size;
+ mem3.aPool[i+size-1].u.hdr.size4x &= ~2;
+ memsys3Link(i);
+
+ /* Try to expand the master using the newly freed chunk */
+ if( mem3.iMaster ){
+ while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){
+ size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize;
+ mem3.iMaster -= size;
+ mem3.szMaster += size;
+ memsys3Unlink(mem3.iMaster);
+ x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
+ mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
+ mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
+ }
+ x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
+ while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){
+ memsys3Unlink(mem3.iMaster+mem3.szMaster);
+ mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4;
+ mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
+ mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
+ }
+ }
+}
+
+/*
+** Return the size of an outstanding allocation, in bytes. The
+** size returned omits the 8-byte header overhead. This only
+** works for chunks that are currently checked out.
+*/
+static int memsys3Size(void *p){
+ Mem3Block *pBlock;
+ if( p==0 ) return 0;
+ pBlock = (Mem3Block*)p;
+ assert( (pBlock[-1].u.hdr.size4x&1)!=0 );
+ return (pBlock[-1].u.hdr.size4x&~3)*2 - 4;
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int memsys3Roundup(int n){
+ if( n<=12 ){
+ return 12;
+ }else{
+ return ((n+11)&~7) - 4;
+ }
+}
+
+/*
+** Allocate nBytes of memory.
+*/
+static void *memsys3Malloc(int nBytes){
+ sqlite3_int64 *p;
+ assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */
+ memsys3Enter();
+ p = memsys3MallocUnsafe(nBytes);
+ memsys3Leave();
+ return (void*)p;
+}
+
+/*
+** Free memory.
+*/
+void memsys3Free(void *pPrior){
+ assert( pPrior );
+ memsys3Enter();
+ memsys3FreeUnsafe(pPrior);
+ memsys3Leave();
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+void *memsys3Realloc(void *pPrior, int nBytes){
+ int nOld;
+ void *p;
+ if( pPrior==0 ){
+ return sqlite3_malloc(nBytes);
+ }
+ if( nBytes<=0 ){
+ sqlite3_free(pPrior);
+ return 0;
+ }
+ nOld = memsys3Size(pPrior);
+ if( nBytes<=nOld && nBytes>=nOld-128 ){
+ return pPrior;
+ }
+ memsys3Enter();
+ p = memsys3MallocUnsafe(nBytes);
+ if( p ){
+ if( nOld<nBytes ){
+ memcpy(p, pPrior, nOld);
+ }else{
+ memcpy(p, pPrior, nBytes);
+ }
+ memsys3FreeUnsafe(pPrior);
+ }
+ memsys3Leave();
+ return p;
+}
+
+/*
+** Initialize this module.
+*/
+static int memsys3Init(void *NotUsed){
+ if( !sqlite3Config.pHeap ){
+ return SQLITE_ERROR;
+ }
+
+ /* Store a pointer to the memory block in global structure mem3. */
+ assert( sizeof(Mem3Block)==8 );
+ mem3.aPool = (Mem3Block *)sqlite3Config.pHeap;
+ mem3.nPool = (sqlite3Config.nHeap / sizeof(Mem3Block)) - 2;
+
+ /* Initialize the master block. */
+ mem3.szMaster = mem3.nPool;
+ mem3.mnMaster = mem3.szMaster;
+ mem3.iMaster = 1;
+ mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2;
+ mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool;
+ mem3.aPool[mem3.nPool].u.hdr.size4x = 1;
+
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize this module.
+*/
+static void memsys3Shutdown(void *NotUsed){
+ return;
+}
+
+
+
+/*
+** Open the file indicated and write a log of all unfreed memory
+** allocations into that log.
+*/
+#ifdef SQLITE_DEBUG
+void sqlite3Memsys3Dump(const char *zFilename){
+ FILE *out;
+ int i, j;
+ u32 size;
+ if( zFilename==0 || zFilename[0]==0 ){
+ out = stdout;
+ }else{
+ out = fopen(zFilename, "w");
+ if( out==0 ){
+ fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
+ zFilename);
+ return;
+ }
+ }
+ memsys3Enter();
+ fprintf(out, "CHUNKS:\n");
+ for(i=1; i<=mem3.nPool; i+=size/4){
+ size = mem3.aPool[i-1].u.hdr.size4x;
+ if( size/4<=1 ){
+ fprintf(out, "%p size error\n", &mem3.aPool[i]);
+ assert( 0 );
+ break;
+ }
+ if( (size&1)==0 && mem3.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){
+ fprintf(out, "%p tail size does not match\n", &mem3.aPool[i]);
+ assert( 0 );
+ break;
+ }
+ if( ((mem3.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){
+ fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]);
+ assert( 0 );
+ break;
+ }
+ if( size&1 ){
+ fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8);
+ }else{
+ fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8,
+ i==mem3.iMaster ? " **master**" : "");
+ }
+ }
+ for(i=0; i<MX_SMALL-1; i++){
+ if( mem3.aiSmall[i]==0 ) continue;
+ fprintf(out, "small(%2d):", i);
+ for(j = mem3.aiSmall[i]; j>0; j=mem3.aPool[j].u.list.next){
+ fprintf(out, " %p(%d)", &mem3.aPool[j],
+ (mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
+ }
+ fprintf(out, "\n");
+ }
+ for(i=0; i<N_HASH; i++){
+ if( mem3.aiHash[i]==0 ) continue;
+ fprintf(out, "hash(%2d):", i);
+ for(j = mem3.aiHash[i]; j>0; j=mem3.aPool[j].u.list.next){
+ fprintf(out, " %p(%d)", &mem3.aPool[j],
+ (mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
+ }
+ fprintf(out, "\n");
+ }
+ fprintf(out, "master=%d\n", mem3.iMaster);
+ fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szMaster*8);
+ fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnMaster*8);
+ sqlite3_mutex_leave(mem3.mutex);
+ if( out==stdout ){
+ fflush(stdout);
+ }else{
+ fclose(out);
+ }
+}
+#endif
+
+/*
+** This routine is the only routine in this file with external
+** linkage.
+**
+** Populate the low-level memory allocation function pointers in
+** sqlite3Config.m with pointers to the routines in this file. The
+** arguments specify the block of memory to manage.
+**
+** This routine is only called by sqlite3_config(), and therefore
+** is not required to be threadsafe (it is not).
+*/
+const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
+ static const sqlite3_mem_methods mempoolMethods = {
+ memsys3Malloc,
+ memsys3Free,
+ memsys3Realloc,
+ memsys3Size,
+ memsys3Roundup,
+ memsys3Init,
+ memsys3Shutdown,
+ 0
+ };
+ return &mempoolMethods;
+}
+
+#endif /* SQLITE_ENABLE_MEMSYS3 */
diff --git a/third_party/sqlite/src/mem4.c b/third_party/sqlite/src/mem4.c
new file mode 100755
index 0000000..5a78098
--- /dev/null
+++ b/third_party/sqlite/src/mem4.c
@@ -0,0 +1,393 @@
+/*
+** 2007 August 14
+**
+** 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 contains the C functions that implement a memory
+** allocation subsystem for use by SQLite.
+**
+** $Id: mem4.c,v 1.3 2008/06/18 17:09:10 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This version of the memory allocator attempts to obtain memory
+** from mmap() if the size of the allocation is close to the size
+** of a virtual memory page. If the size of the allocation is different
+** from the virtual memory page size, then ordinary malloc() is used.
+** Ordinary malloc is also used if space allocated to mmap() is
+** exhausted.
+**
+** Enable this memory allocation by compiling with -DSQLITE_MMAP_HEAP_SIZE=nnn
+** where nnn is the maximum number of bytes of mmap-ed memory you want
+** to support. This module may choose to use less memory than requested.
+**
+*/
+#ifdef SQLITE_MMAP_HEAP_SIZE
+
+/*
+** This is a test version of the memory allocator that attempts to
+** use mmap() and madvise() for allocations and frees of approximately
+** the virtual memory page size.
+*/
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+/*
+** All of the static variables used by this module are collected
+** into a single structure named "mem". This is to keep the
+** static variables organized and to reduce namespace pollution
+** when this module is combined with other in the amalgamation.
+*/
+static struct {
+ /*
+ ** The alarm callback and its arguments. The mem.mutex lock will
+ ** be held while the callback is running. Recursive calls into
+ ** the memory subsystem are allowed, but no new callbacks will be
+ ** issued. The alarmBusy variable is set to prevent recursive
+ ** callbacks.
+ */
+ sqlite3_int64 alarmThreshold;
+ void (*alarmCallback)(void*, sqlite3_int64,int);
+ void *alarmArg;
+ int alarmBusy;
+
+ /*
+ ** Mutex to control access to the memory allocation subsystem.
+ */
+ sqlite3_mutex *mutex;
+
+ /*
+ ** Current allocation and high-water mark.
+ */
+ sqlite3_int64 nowUsed;
+ sqlite3_int64 mxUsed;
+
+ /*
+ ** Current allocation and high-water marks for mmap allocated memory.
+ */
+ sqlite3_int64 nowUsedMMap;
+ sqlite3_int64 mxUsedMMap;
+
+ /*
+ ** Size of a single mmap page. Obtained from sysconf().
+ */
+ int szPage;
+ int mnPage;
+
+ /*
+ ** The number of available mmap pages.
+ */
+ int nPage;
+
+ /*
+ ** Index of the first free page. 0 means no pages have been freed.
+ */
+ int firstFree;
+
+ /* First unused page on the top of the heap.
+ */
+ int firstUnused;
+
+ /*
+ ** Bulk memory obtained from from mmap().
+ */
+ char *mmapHeap; /* first byte of the heap */
+
+} mem;
+
+
+/*
+** Enter the mutex mem.mutex. Allocate it if it is not already allocated.
+** The mmap() region is initialized the first time this routine is called.
+*/
+static void memsys4Enter(void){
+ if( mem.mutex==0 ){
+ mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ sqlite3_mutex_enter(mem.mutex);
+}
+
+/*
+** Attempt to free memory to the mmap heap. This only works if
+** the pointer p is within the range of memory addresses that
+** comprise the mmap heap. Return 1 if the memory was freed
+** successfully. Return 0 if the pointer is out of range.
+*/
+static int mmapFree(void *p){
+ char *z;
+ int idx, *a;
+ if( mem.mmapHeap==MAP_FAILED || mem.nPage==0 ){
+ return 0;
+ }
+ z = (char*)p;
+ idx = (z - mem.mmapHeap)/mem.szPage;
+ if( idx<1 || idx>=mem.nPage ){
+ return 0;
+ }
+ a = (int*)mem.mmapHeap;
+ a[idx] = a[mem.firstFree];
+ mem.firstFree = idx;
+ mem.nowUsedMMap -= mem.szPage;
+ madvise(p, mem.szPage, MADV_DONTNEED);
+ return 1;
+}
+
+/*
+** Attempt to allocate nBytes from the mmap heap. Return a pointer
+** to the allocated page. Or, return NULL if the allocation fails.
+**
+** The allocation will fail if nBytes is not the right size.
+** Or, the allocation will fail if the mmap heap has been exhausted.
+*/
+static void *mmapAlloc(int nBytes){
+ int idx = 0;
+ if( nBytes>mem.szPage || nBytes<mem.mnPage ){
+ return 0;
+ }
+ if( mem.nPage==0 ){
+ mem.szPage = sysconf(_SC_PAGE_SIZE);
+ mem.mnPage = mem.szPage - mem.szPage/10;
+ mem.nPage = SQLITE_MMAP_HEAP_SIZE/mem.szPage;
+ if( mem.nPage * sizeof(int) > mem.szPage ){
+ mem.nPage = mem.szPage/sizeof(int);
+ }
+ mem.mmapHeap = mmap(0, mem.szPage*mem.nPage, PROT_WRITE|PROT_READ,
+ MAP_ANONYMOUS|MAP_SHARED, -1, 0);
+ if( mem.mmapHeap==MAP_FAILED ){
+ mem.firstUnused = errno;
+ }else{
+ mem.firstUnused = 1;
+ mem.nowUsedMMap = mem.szPage;
+ }
+ }
+ if( mem.mmapHeap==MAP_FAILED ){
+ return 0;
+ }
+ if( mem.firstFree ){
+ int idx = mem.firstFree;
+ int *a = (int*)mem.mmapHeap;
+ mem.firstFree = a[idx];
+ }else if( mem.firstUnused<mem.nPage ){
+ idx = mem.firstUnused++;
+ }
+ if( idx ){
+ mem.nowUsedMMap += mem.szPage;
+ if( mem.nowUsedMMap>mem.mxUsedMMap ){
+ mem.mxUsedMMap = mem.nowUsedMMap;
+ }
+ return (void*)&mem.mmapHeap[idx*mem.szPage];
+ }else{
+ return 0;
+ }
+}
+
+/*
+** Release the mmap-ed memory region if it is currently allocated and
+** is not in use.
+*/
+static void mmapUnmap(void){
+ if( mem.mmapHeap==MAP_FAILED ) return;
+ if( mem.nPage==0 ) return;
+ if( mem.nowUsedMMap>mem.szPage ) return;
+ munmap(mem.mmapHeap, mem.nPage*mem.szPage);
+ mem.nowUsedMMap = 0;
+ mem.nPage = 0;
+}
+
+
+/*
+** Return the amount of memory currently checked out.
+*/
+sqlite3_int64 sqlite3_memory_used(void){
+ sqlite3_int64 n;
+ memsys4Enter();
+ n = mem.nowUsed + mem.nowUsedMMap;
+ sqlite3_mutex_leave(mem.mutex);
+ return n;
+}
+
+/*
+** Return the maximum amount of memory that has ever been
+** checked out since either the beginning of this process
+** or since the most recent reset.
+*/
+sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
+ sqlite3_int64 n;
+ memsys4Enter();
+ n = mem.mxUsed + mem.mxUsedMMap;
+ if( resetFlag ){
+ mem.mxUsed = mem.nowUsed;
+ mem.mxUsedMMap = mem.nowUsedMMap;
+ }
+ sqlite3_mutex_leave(mem.mutex);
+ return n;
+}
+
+/*
+** Change the alarm callback
+*/
+int sqlite3_memory_alarm(
+ void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
+ void *pArg,
+ sqlite3_int64 iThreshold
+){
+ memsys4Enter();
+ mem.alarmCallback = xCallback;
+ mem.alarmArg = pArg;
+ mem.alarmThreshold = iThreshold;
+ sqlite3_mutex_leave(mem.mutex);
+ return SQLITE_OK;
+}
+
+/*
+** Trigger the alarm
+*/
+static void sqlite3MemsysAlarm(int nByte){
+ void (*xCallback)(void*,sqlite3_int64,int);
+ sqlite3_int64 nowUsed;
+ void *pArg;
+ if( mem.alarmCallback==0 || mem.alarmBusy ) return;
+ mem.alarmBusy = 1;
+ xCallback = mem.alarmCallback;
+ nowUsed = mem.nowUsed;
+ pArg = mem.alarmArg;
+ sqlite3_mutex_leave(mem.mutex);
+ xCallback(pArg, nowUsed, nByte);
+ sqlite3_mutex_enter(mem.mutex);
+ mem.alarmBusy = 0;
+}
+
+/*
+** Allocate nBytes of memory
+*/
+static void *memsys4Malloc(int nBytes){
+ sqlite3_int64 *p = 0;
+ if( mem.alarmCallback!=0
+ && mem.nowUsed+mem.nowUsedMMap+nBytes>=mem.alarmThreshold ){
+ sqlite3MemsysAlarm(nBytes);
+ }
+ if( (p = mmapAlloc(nBytes))==0 ){
+ p = malloc(nBytes+8);
+ if( p==0 ){
+ sqlite3MemsysAlarm(nBytes);
+ p = malloc(nBytes+8);
+ }
+ if( p ){
+ p[0] = nBytes;
+ p++;
+ mem.nowUsed += nBytes;
+ if( mem.nowUsed>mem.mxUsed ){
+ mem.mxUsed = mem.nowUsed;
+ }
+ }
+ }
+ return (void*)p;
+}
+
+/*
+** Return the size of a memory allocation
+*/
+static int memsys4Size(void *pPrior){
+ char *z = (char*)pPrior;
+ int idx = mem.nPage ? (z - mem.mmapHeap)/mem.szPage : 0;
+ int nByte;
+ if( idx>=1 && idx<mem.nPage ){
+ nByte = mem.szPage;
+ }else{
+ sqlite3_int64 *p = pPrior;
+ p--;
+ nByte = (int)*p;
+ }
+ return nByte;
+}
+
+/*
+** Free memory.
+*/
+static void memsys4Free(void *pPrior){
+ sqlite3_int64 *p;
+ int nByte;
+ if( mmapFree(pPrior)==0 ){
+ p = pPrior;
+ p--;
+ nByte = (int)*p;
+ mem.nowUsed -= nByte;
+ free(p);
+ if( mem.nowUsed==0 ){
+ mmapUnmap();
+ }
+ }
+}
+
+/*
+** Allocate nBytes of memory
+*/
+void *sqlite3_malloc(int nBytes){
+ sqlite3_int64 *p = 0;
+ if( nBytes>0 ){
+ memsys4Enter();
+ p = memsys4Malloc(nBytes);
+ sqlite3_mutex_leave(mem.mutex);
+ }
+ return (void*)p;
+}
+
+/*
+** Free memory.
+*/
+void sqlite3_free(void *pPrior){
+ if( pPrior==0 ){
+ return;
+ }
+ assert( mem.mutex!=0 );
+ sqlite3_mutex_enter(mem.mutex);
+ memsys4Free(pPrior);
+ sqlite3_mutex_leave(mem.mutex);
+}
+
+
+
+/*
+** Change the size of an existing memory allocation
+*/
+void *sqlite3_realloc(void *pPrior, int nBytes){
+ int nOld;
+ sqlite3_int64 *p;
+ if( pPrior==0 ){
+ return sqlite3_malloc(nBytes);
+ }
+ if( nBytes<=0 ){
+ sqlite3_free(pPrior);
+ return 0;
+ }
+ nOld = memsys4Size(pPrior);
+ if( nBytes<=nOld && nBytes>=nOld-128 ){
+ return pPrior;
+ }
+ assert( mem.mutex!=0 );
+ sqlite3_mutex_enter(mem.mutex);
+ p = memsys4Malloc(nBytes);
+ if( p ){
+ if( nOld<nBytes ){
+ memcpy(p, pPrior, nOld);
+ }else{
+ memcpy(p, pPrior, nBytes);
+ }
+ memsys4Free(pPrior);
+ }
+ assert( mem.mutex!=0 );
+ sqlite3_mutex_leave(mem.mutex);
+ return (void*)p;
+}
+
+#endif /* SQLITE_MMAP_HEAP_SIZE */
diff --git a/third_party/sqlite/src/mem5.c b/third_party/sqlite/src/mem5.c
new file mode 100755
index 0000000..7ce28a3
--- /dev/null
+++ b/third_party/sqlite/src/mem5.c
@@ -0,0 +1,515 @@
+/*
+** 2007 October 14
+**
+** 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 contains the C functions that implement a memory
+** allocation subsystem for use by SQLite.
+**
+** This version of the memory allocation subsystem omits all
+** use of malloc(). The SQLite user supplies a block of memory
+** before calling sqlite3_initialize() from which allocations
+** are made and returned by the xMalloc() and xRealloc()
+** implementations. Once sqlite3_initialize() has been called,
+** the amount of memory available to SQLite is fixed and cannot
+** be changed.
+**
+** This version of the memory allocation subsystem is included
+** in the build only if SQLITE_ENABLE_MEMSYS5 is defined.
+**
+** $Id: mem5.c,v 1.11 2008/07/16 12:25:32 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This version of the memory allocator is used only when
+** SQLITE_POW2_MEMORY_SIZE is defined.
+*/
+#ifdef SQLITE_ENABLE_MEMSYS5
+
+/*
+** Log2 of the minimum size of an allocation. For example, if
+** 4 then all allocations will be rounded up to at least 16 bytes.
+** If 5 then all allocations will be rounded up to at least 32 bytes.
+*/
+#ifndef SQLITE_POW2_LOGMIN
+# define SQLITE_POW2_LOGMIN 6
+#endif
+
+/*
+** Log2 of the maximum size of an allocation.
+*/
+#ifndef SQLITE_POW2_LOGMAX
+# define SQLITE_POW2_LOGMAX 20
+#endif
+#define POW2_MAX (((unsigned int)1)<<SQLITE_POW2_LOGMAX)
+
+/*
+** Number of distinct allocation sizes.
+*/
+#define NSIZE (SQLITE_POW2_LOGMAX - SQLITE_POW2_LOGMIN + 1)
+
+/*
+** A minimum allocation is an instance of the following structure.
+** Larger allocations are an array of these structures where the
+** size of the array is a power of 2.
+*/
+typedef struct Mem5Link Mem5Link;
+struct Mem5Link {
+ int next; /* Index of next free chunk */
+ int prev; /* Index of previous free chunk */
+};
+
+/*
+** Maximum size of any allocation is ((1<<LOGMAX)*mem5.nAtom). Since
+** mem5.nAtom is always at least 8, this is not really a practical
+** limitation.
+*/
+#define LOGMAX 30
+
+/*
+** Masks used for mem5.aCtrl[] elements.
+*/
+#define CTRL_LOGSIZE 0x1f /* Log2 Size of this block relative to POW2_MIN */
+#define CTRL_FREE 0x20 /* True if not checked out */
+
+/*
+** All of the static variables used by this module are collected
+** into a single structure named "mem5". This is to keep the
+** static variables organized and to reduce namespace pollution
+** when this module is combined with other in the amalgamation.
+*/
+static struct {
+ /*
+ ** The alarm callback and its arguments. The mem5.mutex lock will
+ ** be held while the callback is running. Recursive calls into
+ ** the memory subsystem are allowed, but no new callbacks will be
+ ** issued. The alarmBusy variable is set to prevent recursive
+ ** callbacks.
+ */
+ sqlite3_int64 alarmThreshold;
+ void (*alarmCallback)(void*, sqlite3_int64,int);
+ void *alarmArg;
+ int alarmBusy;
+
+ /*
+ ** Mutex to control access to the memory allocation subsystem.
+ */
+ sqlite3_mutex *mutex;
+
+ /*
+ ** Performance statistics
+ */
+ u64 nAlloc; /* Total number of calls to malloc */
+ u64 totalAlloc; /* Total of all malloc calls - includes internal frag */
+ u64 totalExcess; /* Total internal fragmentation */
+ u32 currentOut; /* Current checkout, including internal fragmentation */
+ u32 currentCount; /* Current number of distinct checkouts */
+ u32 maxOut; /* Maximum instantaneous currentOut */
+ u32 maxCount; /* Maximum instantaneous currentCount */
+ u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
+
+ /*
+ ** Lists of free blocks of various sizes.
+ */
+ int aiFreelist[LOGMAX+1];
+
+ /*
+ ** Space for tracking which blocks are checked out and the size
+ ** of each block. One byte per block.
+ */
+ u8 *aCtrl;
+
+ /*
+ ** Memory available for allocation
+ */
+ int nAtom; /* Smallest possible allocation in bytes */
+ int nBlock; /* Number of nAtom sized blocks in zPool */
+ u8 *zPool;
+} mem5;
+
+#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.nAtom]))
+
+/*
+** Unlink the chunk at mem5.aPool[i] from list it is currently
+** on. It should be found on mem5.aiFreelist[iLogsize].
+*/
+static void memsys5Unlink(int i, int iLogsize){
+ int next, prev;
+ assert( i>=0 && i<mem5.nBlock );
+ assert( iLogsize>=0 && iLogsize<=LOGMAX );
+ assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
+
+ next = MEM5LINK(i)->next;
+ prev = MEM5LINK(i)->prev;
+ if( prev<0 ){
+ mem5.aiFreelist[iLogsize] = next;
+ }else{
+ MEM5LINK(prev)->next = next;
+ }
+ if( next>=0 ){
+ MEM5LINK(next)->prev = prev;
+ }
+}
+
+/*
+** Link the chunk at mem5.aPool[i] so that is on the iLogsize
+** free list.
+*/
+static void memsys5Link(int i, int iLogsize){
+ int x;
+ assert( sqlite3_mutex_held(mem5.mutex) );
+ assert( i>=0 && i<mem5.nBlock );
+ assert( iLogsize>=0 && iLogsize<=LOGMAX );
+ assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
+
+ x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize];
+ MEM5LINK(i)->prev = -1;
+ if( x>=0 ){
+ assert( x<mem5.nBlock );
+ MEM5LINK(x)->prev = i;
+ }
+ mem5.aiFreelist[iLogsize] = i;
+}
+
+/*
+** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
+** will already be held (obtained by code in malloc.c) if
+** sqlite3Config.bMemStat is true.
+*/
+static void memsys5Enter(void){
+ if( sqlite3Config.bMemstat==0 && mem5.mutex==0 ){
+ mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ sqlite3_mutex_enter(mem5.mutex);
+}
+static void memsys5Leave(void){
+ sqlite3_mutex_leave(mem5.mutex);
+}
+
+/*
+** Return the size of an outstanding allocation, in bytes. The
+** size returned omits the 8-byte header overhead. This only
+** works for chunks that are currently checked out.
+*/
+static int memsys5Size(void *p){
+ int iSize = 0;
+ if( p ){
+ int i = ((u8 *)p-mem5.zPool)/mem5.nAtom;
+ assert( i>=0 && i<mem5.nBlock );
+ iSize = mem5.nAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
+ }
+ return iSize;
+}
+
+/*
+** Find the first entry on the freelist iLogsize. Unlink that
+** entry and return its index.
+*/
+static int memsys5UnlinkFirst(int iLogsize){
+ int i;
+ int iFirst;
+
+ assert( iLogsize>=0 && iLogsize<=LOGMAX );
+ i = iFirst = mem5.aiFreelist[iLogsize];
+ assert( iFirst>=0 );
+ while( i>0 ){
+ if( i<iFirst ) iFirst = i;
+ i = MEM5LINK(i)->next;
+ }
+ memsys5Unlink(iFirst, iLogsize);
+ return iFirst;
+}
+
+/*
+** Return a block of memory of at least nBytes in size.
+** Return NULL if unable.
+*/
+static void *memsys5MallocUnsafe(int nByte){
+ int i; /* Index of a mem5.aPool[] slot */
+ int iBin; /* Index into mem5.aiFreelist[] */
+ int iFullSz; /* Size of allocation rounded up to power of 2 */
+ int iLogsize; /* Log2 of iFullSz/POW2_MIN */
+
+ /* Keep track of the maximum allocation request. Even unfulfilled
+ ** requests are counted */
+ if( nByte>mem5.maxRequest ){
+ mem5.maxRequest = nByte;
+ }
+
+ /* Round nByte up to the next valid power of two */
+ if( nByte>POW2_MAX ) return 0;
+ for(iFullSz=mem5.nAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){}
+
+ /* Make sure mem5.aiFreelist[iLogsize] contains at least one free
+ ** block. If not, then split a block of the next larger power of
+ ** two in order to create a new free block of size iLogsize.
+ */
+ for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){}
+ if( iBin>LOGMAX ) return 0;
+ i = memsys5UnlinkFirst(iBin);
+ while( iBin>iLogsize ){
+ int newSize;
+
+ iBin--;
+ newSize = 1 << iBin;
+ mem5.aCtrl[i+newSize] = CTRL_FREE | iBin;
+ memsys5Link(i+newSize, iBin);
+ }
+ mem5.aCtrl[i] = iLogsize;
+
+ /* Update allocator performance statistics. */
+ mem5.nAlloc++;
+ mem5.totalAlloc += iFullSz;
+ mem5.totalExcess += iFullSz - nByte;
+ mem5.currentCount++;
+ mem5.currentOut += iFullSz;
+ if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
+ if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
+
+ /* Return a pointer to the allocated memory. */
+ return (void*)&mem5.zPool[i*mem5.nAtom];
+}
+
+/*
+** Free an outstanding memory allocation.
+*/
+static void memsys5FreeUnsafe(void *pOld){
+ u32 size, iLogsize;
+ int iBlock;
+
+ /* Set iBlock to the index of the block pointed to by pOld in
+ ** the array of mem5.nAtom byte blocks pointed to by mem5.zPool.
+ */
+ iBlock = ((u8 *)pOld-mem5.zPool)/mem5.nAtom;
+
+ /* Check that the pointer pOld points to a valid, non-free block. */
+ assert( iBlock>=0 && iBlock<mem5.nBlock );
+ assert( ((u8 *)pOld-mem5.zPool)%mem5.nAtom==0 );
+ assert( (mem5.aCtrl[iBlock] & CTRL_FREE)==0 );
+
+ iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE;
+ size = 1<<iLogsize;
+ assert( iBlock+size-1<mem5.nBlock );
+
+ mem5.aCtrl[iBlock] |= CTRL_FREE;
+ mem5.aCtrl[iBlock+size-1] |= CTRL_FREE;
+ assert( mem5.currentCount>0 );
+ assert( mem5.currentOut>=0 );
+ mem5.currentCount--;
+ mem5.currentOut -= size*mem5.nAtom;
+ assert( mem5.currentOut>0 || mem5.currentCount==0 );
+ assert( mem5.currentCount>0 || mem5.currentOut==0 );
+
+ mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
+ while( iLogsize<LOGMAX ){
+ int iBuddy;
+ if( (iBlock>>iLogsize) & 1 ){
+ iBuddy = iBlock - size;
+ }else{
+ iBuddy = iBlock + size;
+ }
+ assert( iBuddy>=0 );
+ if( (iBuddy+(1<<iLogsize))>mem5.nBlock ) break;
+ if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
+ memsys5Unlink(iBuddy, iLogsize);
+ iLogsize++;
+ if( iBuddy<iBlock ){
+ mem5.aCtrl[iBuddy] = CTRL_FREE | iLogsize;
+ mem5.aCtrl[iBlock] = 0;
+ iBlock = iBuddy;
+ }else{
+ mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
+ mem5.aCtrl[iBuddy] = 0;
+ }
+ size *= 2;
+ }
+ memsys5Link(iBlock, iLogsize);
+}
+
+/*
+** Allocate nBytes of memory
+*/
+static void *memsys5Malloc(int nBytes){
+ sqlite3_int64 *p = 0;
+ if( nBytes>0 ){
+ memsys5Enter();
+ p = memsys5MallocUnsafe(nBytes);
+ memsys5Leave();
+ }
+ return (void*)p;
+}
+
+/*
+** Free memory.
+*/
+static void memsys5Free(void *pPrior){
+ if( pPrior==0 ){
+assert(0);
+ return;
+ }
+ memsys5Enter();
+ memsys5FreeUnsafe(pPrior);
+ memsys5Leave();
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+static void *memsys5Realloc(void *pPrior, int nBytes){
+ int nOld;
+ void *p;
+ if( pPrior==0 ){
+ return memsys5Malloc(nBytes);
+ }
+ if( nBytes<=0 ){
+ memsys5Free(pPrior);
+ return 0;
+ }
+ nOld = memsys5Size(pPrior);
+ if( nBytes<=nOld ){
+ return pPrior;
+ }
+ memsys5Enter();
+ p = memsys5MallocUnsafe(nBytes);
+ if( p ){
+ memcpy(p, pPrior, nOld);
+ memsys5FreeUnsafe(pPrior);
+ }
+ memsys5Leave();
+ return p;
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int memsys5Roundup(int n){
+ int iFullSz;
+ for(iFullSz=mem5.nAtom; iFullSz<n; iFullSz *= 2);
+ return iFullSz;
+}
+
+static int memsys5Log(int iValue){
+ int iLog;
+ for(iLog=0; (1<<iLog)<iValue; iLog++);
+ return iLog;
+}
+
+/*
+** Initialize this module.
+*/
+static int memsys5Init(void *NotUsed){
+ int ii;
+ int nByte = sqlite3Config.nHeap;
+ u8 *zByte = (u8 *)sqlite3Config.pHeap;
+ int nMinLog; /* Log of minimum allocation size in bytes*/
+ int iOffset;
+
+ if( !zByte ){
+ return SQLITE_ERROR;
+ }
+
+ nMinLog = memsys5Log(sqlite3Config.mnReq);
+ mem5.nAtom = (1<<nMinLog);
+ while( sizeof(Mem5Link)>mem5.nAtom ){
+ mem5.nAtom = mem5.nAtom << 1;
+ }
+
+ mem5.nBlock = (nByte / (mem5.nAtom+sizeof(u8)));
+ mem5.zPool = zByte;
+ mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.nAtom];
+
+ for(ii=0; ii<=LOGMAX; ii++){
+ mem5.aiFreelist[ii] = -1;
+ }
+
+ iOffset = 0;
+ for(ii=LOGMAX; ii>=0; ii--){
+ int nAlloc = (1<<ii);
+ if( (iOffset+nAlloc)<=mem5.nBlock ){
+ mem5.aCtrl[iOffset] = ii | CTRL_FREE;
+ memsys5Link(iOffset, ii);
+ iOffset += nAlloc;
+ }
+ assert((iOffset+nAlloc)>mem5.nBlock);
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize this module.
+*/
+static void memsys5Shutdown(void *NotUsed){
+ return;
+}
+
+/*
+** Open the file indicated and write a log of all unfreed memory
+** allocations into that log.
+*/
+void sqlite3Memsys5Dump(const char *zFilename){
+#ifdef SQLITE_DEBUG
+ FILE *out;
+ int i, j, n;
+ int nMinLog;
+
+ if( zFilename==0 || zFilename[0]==0 ){
+ out = stdout;
+ }else{
+ out = fopen(zFilename, "w");
+ if( out==0 ){
+ fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
+ zFilename);
+ return;
+ }
+ }
+ memsys5Enter();
+ nMinLog = memsys5Log(mem5.nAtom);
+ for(i=0; i<=LOGMAX && i+nMinLog<32; i++){
+ for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){}
+ fprintf(out, "freelist items of size %d: %d\n", mem5.nAtom << i, n);
+ }
+ fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc);
+ fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc);
+ fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess);
+ fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut);
+ fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount);
+ fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut);
+ fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount);
+ fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest);
+ memsys5Leave();
+ if( out==stdout ){
+ fflush(stdout);
+ }else{
+ fclose(out);
+ }
+#endif
+}
+
+/*
+** This routine is the only routine in this file with external
+** linkage. It returns a pointer to a static sqlite3_mem_methods
+** struct populated with the memsys5 methods.
+*/
+const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
+ static const sqlite3_mem_methods memsys5Methods = {
+ memsys5Malloc,
+ memsys5Free,
+ memsys5Realloc,
+ memsys5Size,
+ memsys5Roundup,
+ memsys5Init,
+ memsys5Shutdown,
+ 0
+ };
+ return &memsys5Methods;
+}
+
+#endif /* SQLITE_ENABLE_MEMSYS5 */
diff --git a/third_party/sqlite/src/mem6.c b/third_party/sqlite/src/mem6.c
new file mode 100755
index 0000000..95723ad
--- /dev/null
+++ b/third_party/sqlite/src/mem6.c
@@ -0,0 +1,496 @@
+/*
+** 2008 July 24
+**
+** 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 contains an alternative memory allocation system for SQLite.
+** This system is implemented as a wrapper around the system provided
+** by the operating system - vanilla malloc(), realloc() and free().
+**
+** This system differentiates between requests for "small" allocations
+** (by default those of 128 bytes or less) and "large" allocations (all
+** others). The 256 byte threshhold is configurable at runtime.
+**
+** All requests for large allocations are passed through to the
+** default system.
+**
+** Requests for small allocations are met by allocating space within
+** one or more larger "chunks" of memory obtained from the default
+** memory allocation system. Chunks of memory are usually 64KB or
+** larger. The algorithm used to manage space within each chunk is
+** the same as that used by mem5.c.
+**
+** This strategy is designed to prevent the default memory allocation
+** system (usually the system malloc) from suffering from heap
+** fragmentation. On some systems, heap fragmentation can cause a
+** significant real-time slowdown.
+**
+** $Id: mem6.c,v 1.7 2008/07/28 19:34:53 drh Exp $
+*/
+
+#ifdef SQLITE_ENABLE_MEMSYS6
+
+#include "sqliteInt.h"
+
+/*
+** Maximum size of any "small" allocation is ((1<<LOGMAX)*Mem6Chunk.nAtom).
+** Mem6Chunk.nAtom is always at least 8, so this is not a practical
+** limitation
+*/
+#define LOGMAX 30
+
+/*
+** Default value for the "small" allocation size threshold.
+*/
+#define SMALL_MALLOC_DEFAULT_THRESHOLD 256
+
+/*
+** Minimum size for a memory chunk.
+*/
+#define MIN_CHUNKSIZE (1<<16)
+
+#define LOG2_MINALLOC 4
+
+
+typedef struct Mem6Chunk Mem6Chunk;
+typedef struct Mem6Link Mem6Link;
+
+/*
+** A minimum allocation is an instance of the following structure.
+** Larger allocations are an array of these structures where the
+** size of the array is a power of 2.
+*/
+struct Mem6Link {
+ int next; /* Index of next free chunk */
+ int prev; /* Index of previous free chunk */
+};
+
+/*
+** Masks used for mem5.aCtrl[] elements.
+*/
+#define CTRL_LOGSIZE 0x1f /* Log2 Size of this block relative to POW2_MIN */
+#define CTRL_FREE 0x20 /* True if not checked out */
+
+struct Mem6Chunk {
+ Mem6Chunk *pNext;
+
+ /*
+ ** Lists of free blocks of various sizes.
+ */
+ int aiFreelist[LOGMAX+1];
+
+ int nCheckedOut; /* Number of currently outstanding allocations */
+
+ /*
+ ** Space for tracking which blocks are checked out and the size
+ ** of each block. One byte per block.
+ */
+ u8 *aCtrl;
+
+ /*
+ ** Memory available for allocation
+ */
+ int nAtom; /* Smallest possible allocation in bytes */
+ int nBlock; /* Number of nAtom sized blocks in zPool */
+ u8 *zPool; /* Pointer to memory chunk from which allocations are made */
+};
+
+#define MEM6LINK(idx) ((Mem6Link *)(&pChunk->zPool[(idx)*pChunk->nAtom]))
+
+struct Mem6Global {
+ int nMinAlloc; /* Minimum allowed allocation size */
+ int nThreshold; /* Allocs larger than this go to malloc() */
+ int nLogThreshold; /* log2 of (nThreshold/nMinAlloc) */
+ sqlite3_mutex *mutex;
+ Mem6Chunk *pChunk; /* Singly linked list of all memory chunks */
+} mem6;
+
+/*
+** Unlink the chunk at pChunk->aPool[i] from list it is currently
+** on. It should be found on pChunk->aiFreelist[iLogsize].
+*/
+static void memsys6Unlink(Mem6Chunk *pChunk, int i, int iLogsize){
+ int next, prev;
+ assert( i>=0 && i<pChunk->nBlock );
+ assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
+ assert( (pChunk->aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
+
+ next = MEM6LINK(i)->next;
+ prev = MEM6LINK(i)->prev;
+ if( prev<0 ){
+ pChunk->aiFreelist[iLogsize] = next;
+ }else{
+ MEM6LINK(prev)->next = next;
+ }
+ if( next>=0 ){
+ MEM6LINK(next)->prev = prev;
+ }
+}
+
+/*
+** Link the chunk at mem5.aPool[i] so that is on the iLogsize
+** free list.
+*/
+static void memsys6Link(Mem6Chunk *pChunk, int i, int iLogsize){
+ int x;
+ assert( i>=0 && i<pChunk->nBlock );
+ assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
+ assert( (pChunk->aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
+
+ x = MEM6LINK(i)->next = pChunk->aiFreelist[iLogsize];
+ MEM6LINK(i)->prev = -1;
+ if( x>=0 ){
+ assert( x<pChunk->nBlock );
+ MEM6LINK(x)->prev = i;
+ }
+ pChunk->aiFreelist[iLogsize] = i;
+}
+
+
+/*
+** Find the first entry on the freelist iLogsize. Unlink that
+** entry and return its index.
+*/
+static int memsys6UnlinkFirst(Mem6Chunk *pChunk, int iLogsize){
+ int i;
+ int iFirst;
+
+ assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
+ i = iFirst = pChunk->aiFreelist[iLogsize];
+ assert( iFirst>=0 );
+ memsys6Unlink(pChunk, iFirst, iLogsize);
+ return iFirst;
+}
+
+static int roundupLog2(int n){
+ static const char LogTable256[256] = {
+ 0, /* 1 */
+ 1, /* 2 */
+ 2, 2, /* 3..4 */
+ 3, 3, 3, 3, /* 5..8 */
+ 4, 4, 4, 4, 4, 4, 4, 4, /* 9..16 */
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 17..32 */
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 33..64 */
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 65..128 */
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 129..256 */
+ };
+
+ assert(n<=(1<<16) && n>0);
+ if( n<=256 ) return LogTable256[n-1];
+ return LogTable256[(n>>8) - ((n&0xFF)?0:1)] + 8;
+}
+
+/*
+** Allocate and return a block of (pChunk->nAtom << iLogsize) bytes from chunk
+** pChunk. If the allocation request cannot be satisfied, return 0.
+*/
+static void *chunkMalloc(Mem6Chunk *pChunk, int iLogsize){
+ int i; /* Index of a mem5.aPool[] slot */
+ int iBin; /* Index into mem5.aiFreelist[] */
+
+ /* Make sure mem5.aiFreelist[iLogsize] contains at least one free
+ ** block. If not, then split a block of the next larger power of
+ ** two in order to create a new free block of size iLogsize.
+ */
+ for(iBin=iLogsize; pChunk->aiFreelist[iBin]<0 && iBin<=mem6.nLogThreshold; iBin++){}
+ if( iBin>mem6.nLogThreshold ) return 0;
+ i = memsys6UnlinkFirst(pChunk, iBin);
+ while( iBin>iLogsize ){
+ int newSize;
+ iBin--;
+ newSize = 1 << iBin;
+ pChunk->aCtrl[i+newSize] = CTRL_FREE | iBin;
+ memsys6Link(pChunk, i+newSize, iBin);
+ }
+ pChunk->aCtrl[i] = iLogsize;
+
+ /* Return a pointer to the allocated memory. */
+ pChunk->nCheckedOut++;
+ return (void*)&pChunk->zPool[i*pChunk->nAtom];
+}
+
+/*
+** Free the allocation pointed to by p, which is guaranteed to be non-zero
+** and a part of chunk object pChunk.
+*/
+static void chunkFree(Mem6Chunk *pChunk, void *pOld){
+ u32 size, iLogsize;
+ int iBlock;
+
+ /* Set iBlock to the index of the block pointed to by pOld in
+ ** the array of pChunk->nAtom byte blocks pointed to by pChunk->zPool.
+ */
+ iBlock = ((u8 *)pOld-pChunk->zPool)/pChunk->nAtom;
+
+ /* Check that the pointer pOld points to a valid, non-free block. */
+ assert( iBlock>=0 && iBlock<pChunk->nBlock );
+ assert( ((u8 *)pOld-pChunk->zPool)%pChunk->nAtom==0 );
+ assert( (pChunk->aCtrl[iBlock] & CTRL_FREE)==0 );
+
+ iLogsize = pChunk->aCtrl[iBlock] & CTRL_LOGSIZE;
+ size = 1<<iLogsize;
+ assert( iBlock+size-1<pChunk->nBlock );
+
+ pChunk->aCtrl[iBlock] |= CTRL_FREE;
+ pChunk->aCtrl[iBlock+size-1] |= CTRL_FREE;
+
+ pChunk->aCtrl[iBlock] = CTRL_FREE | iLogsize;
+ while( iLogsize<mem6.nLogThreshold ){
+ int iBuddy;
+ if( (iBlock>>iLogsize) & 1 ){
+ iBuddy = iBlock - size;
+ }else{
+ iBuddy = iBlock + size;
+ }
+ assert( iBuddy>=0 );
+ if( (iBuddy+(1<<iLogsize))>pChunk->nBlock ) break;
+ if( pChunk->aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
+ memsys6Unlink(pChunk, iBuddy, iLogsize);
+ iLogsize++;
+ if( iBuddy<iBlock ){
+ pChunk->aCtrl[iBuddy] = CTRL_FREE | iLogsize;
+ pChunk->aCtrl[iBlock] = 0;
+ iBlock = iBuddy;
+ }else{
+ pChunk->aCtrl[iBlock] = CTRL_FREE | iLogsize;
+ pChunk->aCtrl[iBuddy] = 0;
+ }
+ size *= 2;
+ }
+ pChunk->nCheckedOut--;
+ memsys6Link(pChunk, iBlock, iLogsize);
+}
+
+/*
+** Return the actual size of the block pointed to by p, which is guaranteed
+** to have been allocated from chunk pChunk.
+*/
+static int chunkSize(Mem6Chunk *pChunk, void *p){
+ int iSize = 0;
+ if( p ){
+ int i = ((u8 *)p-pChunk->zPool)/pChunk->nAtom;
+ assert( i>=0 && i<pChunk->nBlock );
+ iSize = pChunk->nAtom * (1 << (pChunk->aCtrl[i]&CTRL_LOGSIZE));
+ }
+ return iSize;
+}
+
+/*
+** Return true if there are currently no outstanding allocations.
+*/
+static int chunkIsEmpty(Mem6Chunk *pChunk){
+ return (pChunk->nCheckedOut==0);
+}
+
+/*
+** Initialize the buffer zChunk, which is nChunk bytes in size, as
+** an Mem6Chunk object. Return a copy of the zChunk pointer.
+*/
+static Mem6Chunk *chunkInit(u8 *zChunk, int nChunk, int nMinAlloc){
+ int ii;
+ int iOffset;
+ Mem6Chunk *pChunk = (Mem6Chunk *)zChunk;
+
+ assert( nChunk>sizeof(Mem6Chunk) );
+ assert( nMinAlloc>sizeof(Mem6Link) );
+
+ memset(pChunk, 0, sizeof(Mem6Chunk));
+ pChunk->nAtom = nMinAlloc;
+ pChunk->nBlock = ((nChunk-sizeof(Mem6Chunk)) / (pChunk->nAtom+sizeof(u8)));
+
+ pChunk->zPool = (u8 *)&pChunk[1];
+ pChunk->aCtrl = &pChunk->zPool[pChunk->nBlock*pChunk->nAtom];
+
+ for(ii=0; ii<=mem6.nLogThreshold; ii++){
+ pChunk->aiFreelist[ii] = -1;
+ }
+
+ iOffset = 0;
+ for(ii=mem6.nLogThreshold; ii>=0; ii--){
+ int nAlloc = (1<<ii);
+ while( (iOffset+nAlloc)<=pChunk->nBlock ){
+ pChunk->aCtrl[iOffset] = ii | CTRL_FREE;
+ memsys6Link(pChunk, iOffset, ii);
+ iOffset += nAlloc;
+ }
+ }
+
+ return pChunk;
+}
+
+
+static void mem6Enter(void){
+ sqlite3_mutex_enter(mem6.mutex);
+}
+
+static void mem6Leave(void){
+ sqlite3_mutex_leave(mem6.mutex);
+}
+
+/*
+** Based on the number and size of the currently allocated chunks, return
+** the size of the next chunk to allocate, in bytes.
+*/
+static int nextChunkSize(void){
+ int iTotal = MIN_CHUNKSIZE;
+ Mem6Chunk *p;
+ for(p=mem6.pChunk; p; p=p->pNext){
+ iTotal = iTotal*2;
+ }
+ return iTotal;
+}
+
+static void freeChunk(Mem6Chunk *pChunk){
+ Mem6Chunk **pp = &mem6.pChunk;
+ for( pp=&mem6.pChunk; *pp!=pChunk; pp = &(*pp)->pNext );
+ *pp = (*pp)->pNext;
+ free(pChunk);
+}
+
+static void *memsys6Malloc(int nByte){
+ Mem6Chunk *pChunk;
+ void *p = 0;
+ int nTotal = nByte+8;
+ int iOffset = 0;
+
+ if( nTotal>mem6.nThreshold ){
+ p = malloc(nTotal);
+ }else{
+ int iLogsize = 0;
+ if( nTotal>(1<<LOG2_MINALLOC) ){
+ iLogsize = roundupLog2(nTotal) - LOG2_MINALLOC;
+ }
+ mem6Enter();
+ for(pChunk=mem6.pChunk; pChunk; pChunk=pChunk->pNext){
+ p = chunkMalloc(pChunk, iLogsize);
+ if( p ){
+ break;
+ }
+ }
+ if( !p ){
+ int iSize = nextChunkSize();
+ p = malloc(iSize);
+ if( p ){
+ pChunk = chunkInit((u8 *)p, iSize, mem6.nMinAlloc);
+ pChunk->pNext = mem6.pChunk;
+ mem6.pChunk = pChunk;
+ p = chunkMalloc(pChunk, iLogsize);
+ assert(p);
+ }
+ }
+ iOffset = ((u8*)p - (u8*)pChunk);
+ mem6Leave();
+ }
+
+ if( !p ){
+ return 0;
+ }
+ ((u32 *)p)[0] = iOffset;
+ ((u32 *)p)[1] = nByte;
+ return &((u32 *)p)[2];
+}
+
+static int memsys6Size(void *pPrior){
+ if( pPrior==0 ) return 0;
+ return ((u32*)pPrior)[-1];
+}
+
+static void memsys6Free(void *pPrior){
+ int iSlot;
+ void *p = &((u32 *)pPrior)[-2];
+ iSlot = ((u32 *)p)[0];
+ if( iSlot ){
+ Mem6Chunk *pChunk;
+ mem6Enter();
+ pChunk = (Mem6Chunk *)(&((u8 *)p)[-1 * iSlot]);
+ chunkFree(pChunk, p);
+ if( chunkIsEmpty(pChunk) ){
+ freeChunk(pChunk);
+ }
+ mem6Leave();
+ }else{
+ free(p);
+ }
+}
+
+static void *memsys6Realloc(void *p, int nByte){
+ void *p2;
+
+ if( p && nByte<=memsys6Size(p) ){
+ p2 = p;
+ }else{
+ p2 = memsys6Malloc(nByte);
+ if( p && p2 ){
+ memcpy(p2, p, memsys6Size(p));
+ memsys6Free(p);
+ }
+ }
+
+ return p2;
+}
+
+static int memsys6Roundup(int n){
+ if( n>mem6.nThreshold ){
+ return n;
+ }else{
+ return (1<<roundupLog2(n));
+ }
+}
+
+static int memsys6Init(void *pCtx){
+ u8 bMemstat = sqlite3Config.bMemstat;
+ mem6.nMinAlloc = (1 << LOG2_MINALLOC);
+ mem6.pChunk = 0;
+ mem6.nThreshold = sqlite3Config.nSmall;
+ if( mem6.nThreshold<=0 ){
+ mem6.nThreshold = SMALL_MALLOC_DEFAULT_THRESHOLD;
+ }
+ mem6.nLogThreshold = roundupLog2(mem6.nThreshold) - LOG2_MINALLOC;
+ if( !bMemstat ){
+ mem6.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
+ }
+ return SQLITE_OK;
+}
+
+static void memsys6Shutdown(void *pCtx){
+ memset(&mem6, 0, sizeof(mem6));
+}
+
+/*
+** This routine is the only routine in this file with external
+** linkage. It returns a pointer to a static sqlite3_mem_methods
+** struct populated with the memsys6 methods.
+*/
+const sqlite3_mem_methods *sqlite3MemGetMemsys6(void){
+ static const sqlite3_mem_methods memsys6Methods = {
+ memsys6Malloc,
+ memsys6Free,
+ memsys6Realloc,
+ memsys6Size,
+ memsys6Roundup,
+ memsys6Init,
+ memsys6Shutdown,
+ 0
+ };
+ return &memsys6Methods;
+}
+
+#endif
diff --git a/third_party/sqlite/src/mutex.c b/third_party/sqlite/src/mutex.c
new file mode 100755
index 0000000..43e1965eb
--- /dev/null
+++ b/third_party/sqlite/src/mutex.c
@@ -0,0 +1,273 @@
+/*
+** 2007 August 14
+**
+** 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 contains the C functions that implement mutexes.
+**
+** The implementation in this file does not provide any mutual
+** exclusion and is thus suitable for use only in applications
+** that use SQLite in a single thread. But this implementation
+** does do a lot of error checking on mutexes to make sure they
+** are called correctly and at appropriate times. Hence, this
+** implementation is suitable for testing.
+** debugging purposes
+**
+** $Id: mutex.c,v 1.27 2008/06/19 08:51:24 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+#ifndef SQLITE_MUTEX_NOOP
+/*
+** Initialize the mutex system.
+*/
+int sqlite3MutexInit(void){
+ int rc = SQLITE_OK;
+ if( sqlite3Config.bCoreMutex ){
+ if( !sqlite3Config.mutex.xMutexAlloc ){
+ /* If the xMutexAlloc method has not been set, then the user did not
+ ** install a mutex implementation via sqlite3_config() prior to
+ ** sqlite3_initialize() being called. This block copies pointers to
+ ** the default implementation into the sqlite3Config structure.
+ **
+ ** The danger is that although sqlite3_config() is not a threadsafe
+ ** API, sqlite3_initialize() is, and so multiple threads may be
+ ** attempting to run this function simultaneously. To guard write
+ ** access to the sqlite3Config structure, the 'MASTER' static mutex
+ ** is obtained before modifying it.
+ */
+ sqlite3_mutex_methods *p = sqlite3DefaultMutex();
+ sqlite3_mutex *pMaster = 0;
+
+ rc = p->xMutexInit();
+ if( rc==SQLITE_OK ){
+ pMaster = p->xMutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ assert(pMaster);
+ p->xMutexEnter(pMaster);
+ assert( sqlite3Config.mutex.xMutexAlloc==0
+ || sqlite3Config.mutex.xMutexAlloc==p->xMutexAlloc
+ );
+ if( !sqlite3Config.mutex.xMutexAlloc ){
+ sqlite3Config.mutex = *p;
+ }
+ p->xMutexLeave(pMaster);
+ }
+ }else{
+ rc = sqlite3Config.mutex.xMutexInit();
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Shutdown the mutex system. This call frees resources allocated by
+** sqlite3MutexInit().
+*/
+int sqlite3MutexEnd(void){
+ int rc = SQLITE_OK;
+ rc = sqlite3Config.mutex.xMutexEnd();
+ return rc;
+}
+
+/*
+** Retrieve a pointer to a static mutex or allocate a new dynamic one.
+*/
+sqlite3_mutex *sqlite3_mutex_alloc(int id){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return sqlite3Config.mutex.xMutexAlloc(id);
+}
+
+sqlite3_mutex *sqlite3MutexAlloc(int id){
+ if( !sqlite3Config.bCoreMutex ){
+ return 0;
+ }
+ return sqlite3Config.mutex.xMutexAlloc(id);
+}
+
+/*
+** Free a dynamic mutex.
+*/
+void sqlite3_mutex_free(sqlite3_mutex *p){
+ if( p ){
+ sqlite3Config.mutex.xMutexFree(p);
+ }
+}
+
+/*
+** Obtain the mutex p. If some other thread already has the mutex, block
+** until it can be obtained.
+*/
+void sqlite3_mutex_enter(sqlite3_mutex *p){
+ if( p ){
+ sqlite3Config.mutex.xMutexEnter(p);
+ }
+}
+
+/*
+** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another
+** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY.
+*/
+int sqlite3_mutex_try(sqlite3_mutex *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ return sqlite3Config.mutex.xMutexTry(p);
+ }
+ return rc;
+}
+
+/*
+** The sqlite3_mutex_leave() routine exits a mutex that was previously
+** entered by the same thread. The behavior is undefined if the mutex
+** is not currently entered. If a NULL pointer is passed as an argument
+** this function is a no-op.
+*/
+void sqlite3_mutex_leave(sqlite3_mutex *p){
+ if( p ){
+ sqlite3Config.mutex.xMutexLeave(p);
+ }
+}
+
+#ifndef NDEBUG
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+int sqlite3_mutex_held(sqlite3_mutex *p){
+ return p==0 || sqlite3Config.mutex.xMutexHeld(p);
+}
+int sqlite3_mutex_notheld(sqlite3_mutex *p){
+ return p==0 || sqlite3Config.mutex.xMutexNotheld(p);
+}
+#endif
+
+#endif
+
+#ifdef SQLITE_MUTEX_NOOP_DEBUG
+/*
+** In this implementation, mutexes do not provide any mutual exclusion.
+** But the error checking is provided. This implementation is useful
+** for test purposes.
+*/
+
+/*
+** The mutex object
+*/
+struct sqlite3_mutex {
+ int id; /* The mutex type */
+ int cnt; /* Number of entries without a matching leave */
+};
+
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+static int noopMutexHeld(sqlite3_mutex *p){
+ return p==0 || p->cnt>0;
+}
+static int noopMutexNotheld(sqlite3_mutex *p){
+ return p==0 || p->cnt==0;
+}
+
+/*
+** Initialize and deinitialize the mutex subsystem.
+*/
+static int noopMutexInit(void){ return SQLITE_OK; }
+static int noopMutexEnd(void){ return SQLITE_OK; }
+
+/*
+** The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. If it returns NULL
+** that means that a mutex could not be allocated.
+*/
+static sqlite3_mutex *noopMutexAlloc(int id){
+ static sqlite3_mutex aStatic[6];
+ sqlite3_mutex *pNew = 0;
+ switch( id ){
+ case SQLITE_MUTEX_FAST:
+ case SQLITE_MUTEX_RECURSIVE: {
+ pNew = sqlite3Malloc(sizeof(*pNew));
+ if( pNew ){
+ pNew->id = id;
+ pNew->cnt = 0;
+ }
+ break;
+ }
+ default: {
+ assert( id-2 >= 0 );
+ assert( id-2 < sizeof(aStatic)/sizeof(aStatic[0]) );
+ pNew = &aStatic[id-2];
+ pNew->id = id;
+ break;
+ }
+ }
+ return pNew;
+}
+
+/*
+** This routine deallocates a previously allocated mutex.
+*/
+static void noopMutexFree(sqlite3_mutex *p){
+ assert( p->cnt==0 );
+ assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+ sqlite3_free(p);
+}
+
+/*
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
+** be entered multiple times by the same thread. In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. If the same thread tries to enter any other kind of mutex
+** more than once, the behavior is undefined.
+*/
+static void noopMutexEnter(sqlite3_mutex *p){
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
+ p->cnt++;
+}
+static int noopMutexTry(sqlite3_mutex *p){
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
+ p->cnt++;
+ return SQLITE_OK;
+}
+
+/*
+** The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. The behavior
+** is undefined if the mutex is not currently entered or
+** is not currently allocated. SQLite will never do either.
+*/
+static void noopMutexLeave(sqlite3_mutex *p){
+ assert( noopMutexHeld(p) );
+ p->cnt--;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
+}
+
+sqlite3_mutex_methods *sqlite3DefaultMutex(void){
+ static sqlite3_mutex_methods sMutex = {
+ noopMutexInit,
+ noopMutexEnd,
+ noopMutexAlloc,
+ noopMutexFree,
+ noopMutexEnter,
+ noopMutexTry,
+ noopMutexLeave,
+
+ noopMutexHeld,
+ noopMutexNotheld
+ };
+
+ return &sMutex;
+}
+#endif /* SQLITE_MUTEX_NOOP_DEBUG */
diff --git a/third_party/sqlite/src/mutex.h b/third_party/sqlite/src/mutex.h
new file mode 100755
index 0000000..5f51dda
--- /dev/null
+++ b/third_party/sqlite/src/mutex.h
@@ -0,0 +1,85 @@
+/*
+** 2007 August 28
+**
+** 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 contains the common header for all mutex implementations.
+** The sqliteInt.h header #includes this file so that it is available
+** to all source files. We break it out in an effort to keep the code
+** better organized.
+**
+** NOTE: source files should *not* #include this header file directly.
+** Source files should #include the sqliteInt.h file and let that file
+** include this one indirectly.
+**
+** $Id: mutex.h,v 1.8 2008/06/26 10:41:19 danielk1977 Exp $
+*/
+
+
+#ifdef SQLITE_MUTEX_APPDEF
+/*
+** If SQLITE_MUTEX_APPDEF is defined, then this whole module is
+** omitted and equivalent functionality must be provided by the
+** application that links against the SQLite library.
+*/
+#else
+/*
+** Figure out what version of the code to use. The choices are
+**
+** SQLITE_MUTEX_NOOP For single-threaded applications that
+** do not desire error checking.
+**
+** SQLITE_MUTEX_NOOP_DEBUG For single-threaded applications with
+** error checking to help verify that mutexes
+** are being used correctly even though they
+** are not needed. Used when SQLITE_DEBUG is
+** defined on single-threaded builds.
+**
+** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
+**
+** SQLITE_MUTEX_W32 For multi-threaded applications on Win32.
+**
+** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
+*/
+#define SQLITE_MUTEX_NOOP 1 /* The default */
+#if defined(SQLITE_DEBUG) && !SQLITE_THREADSAFE
+# undef SQLITE_MUTEX_NOOP
+# define SQLITE_MUTEX_NOOP_DEBUG
+#endif
+#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_UNIX
+# undef SQLITE_MUTEX_NOOP
+# define SQLITE_MUTEX_PTHREADS
+#endif
+#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_WIN
+# undef SQLITE_MUTEX_NOOP
+# define SQLITE_MUTEX_W32
+#endif
+#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_OS2
+# undef SQLITE_MUTEX_NOOP
+# define SQLITE_MUTEX_OS2
+#endif
+
+#ifdef SQLITE_MUTEX_NOOP
+/*
+** If this is a no-op implementation, implement everything as macros.
+*/
+#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
+#define sqlite3_mutex_free(X)
+#define sqlite3_mutex_enter(X)
+#define sqlite3_mutex_try(X) SQLITE_OK
+#define sqlite3_mutex_leave(X)
+#define sqlite3_mutex_held(X) 1
+#define sqlite3_mutex_notheld(X) 1
+#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
+#define sqlite3MutexInit() SQLITE_OK
+#define sqlite3MutexEnd()
+#endif
+
+#endif /* SQLITE_MUTEX_APPDEF */
diff --git a/third_party/sqlite/src/mutex_os2.c b/third_party/sqlite/src/mutex_os2.c
new file mode 100755
index 0000000..6a44370
--- /dev/null
+++ b/third_party/sqlite/src/mutex_os2.c
@@ -0,0 +1,273 @@
+/*
+** 2007 August 28
+**
+** 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 contains the C functions that implement mutexes for OS/2
+**
+** $Id: mutex_os2.c,v 1.10 2008/06/23 22:13:28 pweilbacher Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** The code in this file is only used if SQLITE_MUTEX_OS2 is defined.
+** See the mutex.h file for details.
+*/
+#ifdef SQLITE_MUTEX_OS2
+
+/********************** OS/2 Mutex Implementation **********************
+**
+** This implementation of mutexes is built using the OS/2 API.
+*/
+
+/*
+** The mutex object
+** Each recursive mutex is an instance of the following structure.
+*/
+struct sqlite3_mutex {
+ HMTX mutex; /* Mutex controlling the lock */
+ int id; /* Mutex type */
+ int nRef; /* Number of references */
+ TID owner; /* Thread holding this mutex */
+};
+
+#define OS2_MUTEX_INITIALIZER 0,0,0,0
+
+/*
+** Initialize and deinitialize the mutex subsystem.
+*/
+static int os2MutexInit(void){ return SQLITE_OK; }
+static int os2MutexEnd(void){ return SQLITE_OK; }
+
+/*
+** The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. If it returns NULL
+** that means that a mutex could not be allocated.
+** SQLite will unwind its stack and return an error. The argument
+** to sqlite3_mutex_alloc() is one of these integer constants:
+**
+** <ul>
+** <li> SQLITE_MUTEX_FAST 0
+** <li> SQLITE_MUTEX_RECURSIVE 1
+** <li> SQLITE_MUTEX_STATIC_MASTER 2
+** <li> SQLITE_MUTEX_STATIC_MEM 3
+** <li> SQLITE_MUTEX_STATIC_PRNG 4
+** </ul>
+**
+** The first two constants cause sqlite3_mutex_alloc() to create
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
+** The mutex implementation does not need to make a distinction
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
+** not want to. But SQLite will only request a recursive mutex in
+** cases where it really needs one. If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to SQLITE_MUTEX_FAST.
+**
+** The other allowed parameters to sqlite3_mutex_alloc() each return
+** a pointer to a static preexisting mutex. Three static mutexes are
+** used by the current version of SQLite. Future versions of SQLite
+** may add additional static mutexes. Static mutexes are for internal
+** use by SQLite only. Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
+** SQLITE_MUTEX_RECURSIVE.
+**
+** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
+** returns a different mutex on every call. But for the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+*/
+static sqlite3_mutex *os2MutexAlloc(int iType){
+ sqlite3_mutex *p = NULL;
+ switch( iType ){
+ case SQLITE_MUTEX_FAST:
+ case SQLITE_MUTEX_RECURSIVE: {
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+ p->id = iType;
+ if( DosCreateMutexSem( 0, &p->mutex, 0, FALSE ) != NO_ERROR ){
+ sqlite3_free( p );
+ p = NULL;
+ }
+ }
+ break;
+ }
+ default: {
+ static volatile int isInit = 0;
+ static sqlite3_mutex staticMutexes[] = {
+ { OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
+ };
+ if ( !isInit ){
+ APIRET rc;
+ PTIB ptib;
+ PPIB ppib;
+ HMTX mutex;
+ char name[32];
+ DosGetInfoBlocks( &ptib, &ppib );
+ sqlite3_snprintf( sizeof(name), name, "\\SEM32\\SQLITE%04x",
+ ppib->pib_ulpid );
+ while( !isInit ){
+ mutex = 0;
+ rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
+ if( rc == NO_ERROR ){
+ int i;
+ if( !isInit ){
+ for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
+ DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
+ }
+ isInit = 1;
+ }
+ DosCloseMutexSem( mutex );
+ }else if( rc == ERROR_DUPLICATE_NAME ){
+ DosSleep( 1 );
+ }else{
+ return p;
+ }
+ }
+ }
+ assert( iType-2 >= 0 );
+ assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
+ p = &staticMutexes[iType-2];
+ p->id = iType;
+ break;
+ }
+ }
+ return p;
+}
+
+
+/*
+** This routine deallocates a previously allocated mutex.
+** SQLite is careful to deallocate every mutex that it allocates.
+*/
+static void os2MutexFree(sqlite3_mutex *p){
+ if( p==0 ) return;
+ assert( p->nRef==0 );
+ assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+ DosCloseMutexSem( p->mutex );
+ sqlite3_free( p );
+}
+
+/*
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
+** be entered multiple times by the same thread. In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. If the same thread tries to enter any other kind of mutex
+** more than once, the behavior is undefined.
+*/
+static void os2MutexEnter(sqlite3_mutex *p){
+ TID tid;
+ PID holder1;
+ ULONG holder2;
+ if( p==0 ) return;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
+ DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
+ DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
+ p->owner = tid;
+ p->nRef++;
+}
+static int os2MutexTry(sqlite3_mutex *p){
+ int rc;
+ TID tid;
+ PID holder1;
+ ULONG holder2;
+ if( p==0 ) return SQLITE_OK;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
+ if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR) {
+ DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
+ p->owner = tid;
+ p->nRef++;
+ rc = SQLITE_OK;
+ } else {
+ rc = SQLITE_BUSY;
+ }
+
+ return rc;
+}
+
+/*
+** The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. The behavior
+** is undefined if the mutex is not currently entered or
+** is not currently allocated. SQLite will never do either.
+*/
+static void os2MutexLeave(sqlite3_mutex *p){
+ TID tid;
+ PID holder1;
+ ULONG holder2;
+ if( p==0 ) return;
+ assert( p->nRef>0 );
+ DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
+ assert( p->owner==tid );
+ p->nRef--;
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
+ DosReleaseMutexSem(p->mutex);
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+static int os2MutexHeld(sqlite3_mutex *p){
+ TID tid;
+ PID pid;
+ ULONG ulCount;
+ PTIB ptib;
+ if( p!=0 ) {
+ DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
+ } else {
+ DosGetInfoBlocks(&ptib, NULL);
+ tid = ptib->tib_ptib2->tib2_ultid;
+ }
+ return p==0 || (p->nRef!=0 && p->owner==tid);
+}
+static int os2MutexNotheld(sqlite3_mutex *p){
+ TID tid;
+ PID pid;
+ ULONG ulCount;
+ PTIB ptib;
+ if( p!= 0 ) {
+ DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
+ } else {
+ DosGetInfoBlocks(&ptib, NULL);
+ tid = ptib->tib_ptib2->tib2_ultid;
+ }
+ return p==0 || p->nRef==0 || p->owner!=tid;
+}
+#endif
+
+sqlite3_mutex_methods *sqlite3DefaultMutex(void){
+ static sqlite3_mutex_methods sMutex = {
+ os2MutexInit,
+ os2MutexEnd,
+ os2MutexAlloc,
+ os2MutexFree,
+ os2MutexEnter,
+ os2MutexTry,
+ os2MutexLeave,
+#ifdef SQLITE_DEBUG
+ os2MutexHeld,
+ os2MutexNotheld
+#endif
+ };
+
+ return &sMutex;
+}
+#endif /* SQLITE_MUTEX_OS2 */
diff --git a/third_party/sqlite/src/mutex_unix.c b/third_party/sqlite/src/mutex_unix.c
new file mode 100755
index 0000000..31f9a97
--- /dev/null
+++ b/third_party/sqlite/src/mutex_unix.c
@@ -0,0 +1,325 @@
+/*
+** 2007 August 28
+**
+** 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 contains the C functions that implement mutexes for pthreads
+**
+** $Id: mutex_unix.c,v 1.13 2008/07/16 12:33:24 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** The code in this file is only used if we are compiling threadsafe
+** under unix with pthreads.
+**
+** Note that this implementation requires a version of pthreads that
+** supports recursive mutexes.
+*/
+#ifdef SQLITE_MUTEX_PTHREADS
+
+#include <pthread.h>
+
+
+/*
+** Each recursive mutex is an instance of the following structure.
+*/
+struct sqlite3_mutex {
+ pthread_mutex_t mutex; /* Mutex controlling the lock */
+ int id; /* Mutex type */
+ int nRef; /* Number of entrances */
+ pthread_t owner; /* Thread that is within this mutex */
+#ifdef SQLITE_DEBUG
+ int trace; /* True to trace changes */
+#endif
+};
+#ifdef SQLITE_DEBUG
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
+#else
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0 }
+#endif
+
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use only inside assert() statements. On some platforms,
+** there might be race conditions that can cause these routines to
+** deliver incorrect results. In particular, if pthread_equal() is
+** not an atomic operation, then these routines might delivery
+** incorrect results. On most platforms, pthread_equal() is a
+** comparison of two integers and is therefore atomic. But we are
+** told that HPUX is not such a platform. If so, then these routines
+** will not always work correctly on HPUX.
+**
+** On those platforms where pthread_equal() is not atomic, SQLite
+** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to
+** make sure no assert() statements are evaluated and hence these
+** routines are never called.
+*/
+#ifndef NDEBUG
+static int pthreadMutexHeld(sqlite3_mutex *p){
+ return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
+}
+static int pthreadMutexNotheld(sqlite3_mutex *p){
+ return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
+}
+#endif
+
+/*
+** Initialize and deinitialize the mutex subsystem.
+*/
+static int pthreadMutexInit(void){ return SQLITE_OK; }
+static int pthreadMutexEnd(void){ return SQLITE_OK; }
+
+/*
+** The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. If it returns NULL
+** that means that a mutex could not be allocated. SQLite
+** will unwind its stack and return an error. The argument
+** to sqlite3_mutex_alloc() is one of these integer constants:
+**
+** <ul>
+** <li> SQLITE_MUTEX_FAST
+** <li> SQLITE_MUTEX_RECURSIVE
+** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MEM
+** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_PRNG
+** <li> SQLITE_MUTEX_STATIC_LRU
+** </ul>
+**
+** The first two constants cause sqlite3_mutex_alloc() to create
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
+** The mutex implementation does not need to make a distinction
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
+** not want to. But SQLite will only request a recursive mutex in
+** cases where it really needs one. If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to SQLITE_MUTEX_FAST.
+**
+** The other allowed parameters to sqlite3_mutex_alloc() each return
+** a pointer to a static preexisting mutex. Three static mutexes are
+** used by the current version of SQLite. Future versions of SQLite
+** may add additional static mutexes. Static mutexes are for internal
+** use by SQLite only. Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
+** SQLITE_MUTEX_RECURSIVE.
+**
+** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
+** returns a different mutex on every call. But for the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+*/
+static sqlite3_mutex *pthreadMutexAlloc(int iType){
+ static sqlite3_mutex staticMutexes[] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER
+ };
+ sqlite3_mutex *p;
+ switch( iType ){
+ case SQLITE_MUTEX_RECURSIVE: {
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, we will have to
+ ** build our own. See below. */
+ pthread_mutex_init(&p->mutex, 0);
+#else
+ /* Use a recursive mutex if it is available */
+ pthread_mutexattr_t recursiveAttr;
+ pthread_mutexattr_init(&recursiveAttr);
+ pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&p->mutex, &recursiveAttr);
+ pthread_mutexattr_destroy(&recursiveAttr);
+#endif
+ p->id = iType;
+ }
+ break;
+ }
+ case SQLITE_MUTEX_FAST: {
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+ p->id = iType;
+ pthread_mutex_init(&p->mutex, 0);
+ }
+ break;
+ }
+ default: {
+ assert( iType-2 >= 0 );
+ assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
+ p = &staticMutexes[iType-2];
+ p->id = iType;
+ break;
+ }
+ }
+ return p;
+}
+
+
+/*
+** This routine deallocates a previously
+** allocated mutex. SQLite is careful to deallocate every
+** mutex that it allocates.
+*/
+static void pthreadMutexFree(sqlite3_mutex *p){
+ assert( p->nRef==0 );
+ assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+ pthread_mutex_destroy(&p->mutex);
+ sqlite3_free(p);
+}
+
+/*
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
+** be entered multiple times by the same thread. In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. If the same thread tries to enter any other kind of mutex
+** more than once, the behavior is undefined.
+*/
+static void pthreadMutexEnter(sqlite3_mutex *p){
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, then we have to grow
+ ** our own. This implementation assumes that pthread_equal()
+ ** is atomic - that it cannot be deceived into thinking self
+ ** and p->owner are equal if p->owner changes between two values
+ ** that are not equal to self while the comparison is taking place.
+ ** This implementation also assumes a coherent cache - that
+ ** separate processes cannot read different values from the same
+ ** address at the same time. If either of these two conditions
+ ** are not met, then the mutexes will fail and problems will result.
+ */
+ {
+ pthread_t self = pthread_self();
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){
+ p->nRef++;
+ }else{
+ pthread_mutex_lock(&p->mutex);
+ assert( p->nRef==0 );
+ p->owner = self;
+ p->nRef = 1;
+ }
+ }
+#else
+ /* Use the built-in recursive mutexes if they are available.
+ */
+ pthread_mutex_lock(&p->mutex);
+ p->owner = pthread_self();
+ p->nRef++;
+#endif
+
+#ifdef SQLITE_DEBUG
+ if( p->trace ){
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ }
+#endif
+}
+static int pthreadMutexTry(sqlite3_mutex *p){
+ int rc;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ /* If recursive mutexes are not available, then we have to grow
+ ** our own. This implementation assumes that pthread_equal()
+ ** is atomic - that it cannot be deceived into thinking self
+ ** and p->owner are equal if p->owner changes between two values
+ ** that are not equal to self while the comparison is taking place.
+ ** This implementation also assumes a coherent cache - that
+ ** separate processes cannot read different values from the same
+ ** address at the same time. If either of these two conditions
+ ** are not met, then the mutexes will fail and problems will result.
+ */
+ {
+ pthread_t self = pthread_self();
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){
+ p->nRef++;
+ rc = SQLITE_OK;
+ }else if( pthread_mutex_trylock(&p->mutex)==0 ){
+ assert( p->nRef==0 );
+ p->owner = self;
+ p->nRef = 1;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+ }
+#else
+ /* Use the built-in recursive mutexes if they are available.
+ */
+ if( pthread_mutex_trylock(&p->mutex)==0 ){
+ p->owner = pthread_self();
+ p->nRef++;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+#endif
+
+#ifdef SQLITE_DEBUG
+ if( rc==SQLITE_OK && p->trace ){
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ }
+#endif
+ return rc;
+}
+
+/*
+** The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. The behavior
+** is undefined if the mutex is not currently entered or
+** is not currently allocated. SQLite will never do either.
+*/
+static void pthreadMutexLeave(sqlite3_mutex *p){
+ assert( pthreadMutexHeld(p) );
+ p->nRef--;
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
+
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+ if( p->nRef==0 ){
+ pthread_mutex_unlock(&p->mutex);
+ }
+#else
+ pthread_mutex_unlock(&p->mutex);
+#endif
+
+#ifdef SQLITE_DEBUG
+ if( p->trace ){
+ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ }
+#endif
+}
+
+sqlite3_mutex_methods *sqlite3DefaultMutex(void){
+ static sqlite3_mutex_methods sMutex = {
+ pthreadMutexInit,
+ pthreadMutexEnd,
+ pthreadMutexAlloc,
+ pthreadMutexFree,
+ pthreadMutexEnter,
+ pthreadMutexTry,
+ pthreadMutexLeave,
+#ifdef SQLITE_DEBUG
+ pthreadMutexHeld,
+ pthreadMutexNotheld
+#endif
+ };
+
+ return &sMutex;
+}
+
+#endif /* SQLITE_MUTEX_PTHREAD */
diff --git a/third_party/sqlite/src/mutex_w32.c b/third_party/sqlite/src/mutex_w32.c
new file mode 100755
index 0000000..d72f0a3
--- /dev/null
+++ b/third_party/sqlite/src/mutex_w32.c
@@ -0,0 +1,244 @@
+/*
+** 2007 August 14
+**
+** 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 contains the C functions that implement mutexes for win32
+**
+** $Id: mutex_w32.c,v 1.11 2008/06/26 10:41:19 danielk1977 Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** The code in this file is only used if we are compiling multithreaded
+** on a win32 system.
+*/
+#ifdef SQLITE_MUTEX_W32
+
+/*
+** Each recursive mutex is an instance of the following structure.
+*/
+struct sqlite3_mutex {
+ CRITICAL_SECTION mutex; /* Mutex controlling the lock */
+ int id; /* Mutex type */
+ int nRef; /* Number of enterances */
+ DWORD owner; /* Thread holding this mutex */
+};
+
+/*
+** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
+** or WinCE. Return false (zero) for Win95, Win98, or WinME.
+**
+** Here is an interesting observation: Win95, Win98, and WinME lack
+** the LockFileEx() API. But we can still statically link against that
+** API as long as we don't call it win running Win95/98/ME. A call to
+** this routine is used to determine if the host is Win95/98/ME or
+** WinNT/2K/XP so that we will know whether or not we can safely call
+** the LockFileEx() API.
+*/
+#if SQLITE_OS_WINCE
+# define mutexIsNT() (1)
+#else
+ static int mutexIsNT(void){
+ static int osType = 0;
+ if( osType==0 ){
+ OSVERSIONINFO sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ GetVersionEx(&sInfo);
+ osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
+ }
+ return osType==2;
+ }
+#endif /* SQLITE_OS_WINCE */
+
+
+#ifdef SQLITE_DEBUG
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use only inside assert() statements.
+*/
+static int winMutexHeld(sqlite3_mutex *p){
+ return p->nRef!=0 && p->owner==GetCurrentThreadId();
+}
+static int winMutexNotheld(sqlite3_mutex *p){
+ return p->nRef==0 || p->owner!=GetCurrentThreadId();
+}
+#endif
+
+
+/*
+** Initialize and deinitialize the mutex subsystem.
+*/
+static int winMutexInit(void){ return SQLITE_OK; }
+static int winMutexEnd(void){ return SQLITE_OK; }
+
+/*
+** The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. If it returns NULL
+** that means that a mutex could not be allocated. SQLite
+** will unwind its stack and return an error. The argument
+** to sqlite3_mutex_alloc() is one of these integer constants:
+**
+** <ul>
+** <li> SQLITE_MUTEX_FAST 0
+** <li> SQLITE_MUTEX_RECURSIVE 1
+** <li> SQLITE_MUTEX_STATIC_MASTER 2
+** <li> SQLITE_MUTEX_STATIC_MEM 3
+** <li> SQLITE_MUTEX_STATIC_PRNG 4
+** </ul>
+**
+** The first two constants cause sqlite3_mutex_alloc() to create
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
+** The mutex implementation does not need to make a distinction
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
+** not want to. But SQLite will only request a recursive mutex in
+** cases where it really needs one. If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to SQLITE_MUTEX_FAST.
+**
+** The other allowed parameters to sqlite3_mutex_alloc() each return
+** a pointer to a static preexisting mutex. Three static mutexes are
+** used by the current version of SQLite. Future versions of SQLite
+** may add additional static mutexes. Static mutexes are for internal
+** use by SQLite only. Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
+** SQLITE_MUTEX_RECURSIVE.
+**
+** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
+** returns a different mutex on every call. But for the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+*/
+static sqlite3_mutex *winMutexAlloc(int iType){
+ sqlite3_mutex *p;
+
+ switch( iType ){
+ case SQLITE_MUTEX_FAST:
+ case SQLITE_MUTEX_RECURSIVE: {
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+ p->id = iType;
+ InitializeCriticalSection(&p->mutex);
+ }
+ break;
+ }
+ default: {
+ static sqlite3_mutex staticMutexes[6];
+ static int isInit = 0;
+ while( !isInit ){
+ static long lock = 0;
+ if( InterlockedIncrement(&lock)==1 ){
+ int i;
+ for(i=0; i<sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++){
+ InitializeCriticalSection(&staticMutexes[i].mutex);
+ }
+ isInit = 1;
+ }else{
+ Sleep(1);
+ }
+ }
+ assert( iType-2 >= 0 );
+ assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
+ p = &staticMutexes[iType-2];
+ p->id = iType;
+ break;
+ }
+ }
+ return p;
+}
+
+
+/*
+** This routine deallocates a previously
+** allocated mutex. SQLite is careful to deallocate every
+** mutex that it allocates.
+*/
+static void winMutexFree(sqlite3_mutex *p){
+ assert( p );
+ assert( p->nRef==0 );
+ assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+ DeleteCriticalSection(&p->mutex);
+ sqlite3_free(p);
+}
+
+/*
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
+** be entered multiple times by the same thread. In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. If the same thread tries to enter any other kind of mutex
+** more than once, the behavior is undefined.
+*/
+static void winMutexEnter(sqlite3_mutex *p){
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );
+ EnterCriticalSection(&p->mutex);
+ p->owner = GetCurrentThreadId();
+ p->nRef++;
+}
+static int winMutexTry(sqlite3_mutex *p){
+ int rc = SQLITE_BUSY;
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );
+ /*
+ ** The sqlite3_mutex_try() routine is very rarely used, and when it
+ ** is used it is merely an optimization. So it is OK for it to always
+ ** fail.
+ **
+ ** The TryEnterCriticalSection() interface is only available on WinNT.
+ ** And some windows compilers complain if you try to use it without
+ ** first doing some #defines that prevent SQLite from building on Win98.
+ ** For that reason, we will omit this optimization for now. See
+ ** ticket #2685.
+ */
+#if 0
+ if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
+ p->owner = GetCurrentThreadId();
+ p->nRef++;
+ rc = SQLITE_OK;
+ }
+#endif
+ return rc;
+}
+
+/*
+** The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. The behavior
+** is undefined if the mutex is not currently entered or
+** is not currently allocated. SQLite will never do either.
+*/
+static void winMutexLeave(sqlite3_mutex *p){
+ assert( p->nRef>0 );
+ assert( p->owner==GetCurrentThreadId() );
+ p->nRef--;
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
+ LeaveCriticalSection(&p->mutex);
+}
+
+sqlite3_mutex_methods *sqlite3DefaultMutex(void){
+ static sqlite3_mutex_methods sMutex = {
+ winMutexInit,
+ winMutexEnd,
+ winMutexAlloc,
+ winMutexFree,
+ winMutexEnter,
+ winMutexTry,
+ winMutexLeave,
+#ifdef SQLITE_DEBUG
+ winMutexHeld,
+ winMutexNotheld
+#endif
+ };
+
+ return &sMutex;
+}
+#endif /* SQLITE_MUTEX_W32 */
diff --git a/third_party/sqlite/src/os.c b/third_party/sqlite/src/os.c
new file mode 100755
index 0000000..0f141c37
--- /dev/null
+++ b/third_party/sqlite/src/os.c
@@ -0,0 +1,277 @@
+/*
+** 2005 November 29
+**
+** 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 contains OS interface code that is common to all
+** architectures.
+**
+** $Id: os.c,v 1.120 2008/07/28 19:34:53 drh Exp $
+*/
+#define _SQLITE_OS_C_ 1
+#include "sqliteInt.h"
+#undef _SQLITE_OS_C_
+
+/*
+** The default SQLite sqlite3_vfs implementations do not allocate
+** memory (actually, os_unix.c allocates a small amount of memory
+** from within OsOpen()), but some third-party implementations may.
+** So we test the effects of a malloc() failing and the sqlite3OsXXX()
+** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro.
+**
+** The following functions are instrumented for malloc() failure
+** testing:
+**
+** sqlite3OsOpen()
+** sqlite3OsRead()
+** sqlite3OsWrite()
+** sqlite3OsSync()
+** sqlite3OsLock()
+**
+*/
+#if defined(SQLITE_TEST) && (SQLITE_OS_WIN==0) && 0
+ #define DO_OS_MALLOC_TEST if (1) { \
+ void *pTstAlloc = sqlite3Malloc(10); \
+ if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
+ sqlite3_free(pTstAlloc); \
+ }
+#else
+ #define DO_OS_MALLOC_TEST
+#endif
+
+/*
+** The following routines are convenience wrappers around methods
+** of the sqlite3_file object. This is mostly just syntactic sugar. All
+** of this would be completely automatic if SQLite were coded using
+** C++ instead of plain old C.
+*/
+int sqlite3OsClose(sqlite3_file *pId){
+ int rc = SQLITE_OK;
+ if( pId->pMethods ){
+ rc = pId->pMethods->xClose(pId);
+ pId->pMethods = 0;
+ }
+ return rc;
+}
+int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xRead(id, pBuf, amt, offset);
+}
+int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xWrite(id, pBuf, amt, offset);
+}
+int sqlite3OsTruncate(sqlite3_file *id, i64 size){
+ return id->pMethods->xTruncate(id, size);
+}
+int sqlite3OsSync(sqlite3_file *id, int flags){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xSync(id, flags);
+}
+int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xFileSize(id, pSize);
+}
+int sqlite3OsLock(sqlite3_file *id, int lockType){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xLock(id, lockType);
+}
+int sqlite3OsUnlock(sqlite3_file *id, int lockType){
+ return id->pMethods->xUnlock(id, lockType);
+}
+int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
+ DO_OS_MALLOC_TEST;
+ return id->pMethods->xCheckReservedLock(id, pResOut);
+}
+int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
+ return id->pMethods->xFileControl(id, op, pArg);
+}
+int sqlite3OsSectorSize(sqlite3_file *id){
+ int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
+ return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
+}
+int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
+ return id->pMethods->xDeviceCharacteristics(id);
+}
+
+/*
+** The next group of routines are convenience wrappers around the
+** VFS methods.
+*/
+int sqlite3OsOpen(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ sqlite3_file *pFile,
+ int flags,
+ int *pFlagsOut
+){
+ DO_OS_MALLOC_TEST;
+ return pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut);
+}
+int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ return pVfs->xDelete(pVfs, zPath, dirSync);
+}
+int sqlite3OsAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ DO_OS_MALLOC_TEST;
+ return pVfs->xAccess(pVfs, zPath, flags, pResOut);
+}
+int sqlite3OsFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nPathOut,
+ char *zPathOut
+){
+ return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
+}
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return pVfs->xDlOpen(pVfs, zPath);
+}
+void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ pVfs->xDlError(pVfs, nByte, zBufOut);
+}
+void *sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+ return pVfs->xDlSym(pVfs, pHandle, zSymbol);
+}
+void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ pVfs->xDlClose(pVfs, pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+}
+int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
+ return pVfs->xSleep(pVfs, nMicro);
+}
+int sqlite3OsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ return pVfs->xCurrentTime(pVfs, pTimeOut);
+}
+
+int sqlite3OsOpenMalloc(
+ sqlite3_vfs *pVfs,
+ const char *zFile,
+ sqlite3_file **ppFile,
+ int flags,
+ int *pOutFlags
+){
+ int rc = SQLITE_NOMEM;
+ sqlite3_file *pFile;
+ pFile = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile);
+ if( pFile ){
+ rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pFile);
+ }else{
+ *ppFile = pFile;
+ }
+ }
+ return rc;
+}
+int sqlite3OsCloseFree(sqlite3_file *pFile){
+ int rc = SQLITE_OK;
+ assert( pFile );
+ rc = sqlite3OsClose(pFile);
+ sqlite3_free(pFile);
+ return rc;
+}
+
+/*
+** The list of all registered VFS implementations.
+*/
+static sqlite3_vfs *vfsList = 0;
+
+/*
+** Locate a VFS by name. If no name is given, simply return the
+** first VFS on the list.
+*/
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
+ sqlite3_vfs *pVfs = 0;
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex;
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return 0;
+#endif
+#ifndef SQLITE_MUTEX_NOOP
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){
+ if( zVfs==0 ) break;
+ if( strcmp(zVfs, pVfs->zName)==0 ) break;
+ }
+ sqlite3_mutex_leave(mutex);
+ return pVfs;
+}
+
+/*
+** Unlink a VFS from the linked list
+*/
+static void vfsUnlink(sqlite3_vfs *pVfs){
+ assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) );
+ if( pVfs==0 ){
+ /* No-op */
+ }else if( vfsList==pVfs ){
+ vfsList = pVfs->pNext;
+ }else if( vfsList ){
+ sqlite3_vfs *p = vfsList;
+ while( p->pNext && p->pNext!=pVfs ){
+ p = p->pNext;
+ }
+ if( p->pNext==pVfs ){
+ p->pNext = pVfs->pNext;
+ }
+ }
+}
+
+/*
+** Register a VFS with the system. It is harmless to register the same
+** VFS multiple times. The new VFS becomes the default if makeDflt is
+** true.
+*/
+int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
+ sqlite3_mutex *mutex = 0;
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(mutex);
+ vfsUnlink(pVfs);
+ if( makeDflt || vfsList==0 ){
+ pVfs->pNext = vfsList;
+ vfsList = pVfs;
+ }else{
+ pVfs->pNext = vfsList->pNext;
+ vfsList->pNext = pVfs;
+ }
+ assert(vfsList);
+ sqlite3_mutex_leave(mutex);
+ return SQLITE_OK;
+}
+
+/*
+** Unregister a VFS so that it is no longer accessible.
+*/
+int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ sqlite3_mutex_enter(mutex);
+ vfsUnlink(pVfs);
+ sqlite3_mutex_leave(mutex);
+ return SQLITE_OK;
+}
diff --git a/third_party/sqlite/src/os.h b/third_party/sqlite/src/os.h
new file mode 100755
index 0000000..04ce381
--- /dev/null
+++ b/third_party/sqlite/src/os.h
@@ -0,0 +1,279 @@
+/*
+** 2001 September 16
+**
+** 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 header file (together with is companion C source-code file
+** "os.c") attempt to abstract the underlying operating system so that
+** the SQLite library will work on both POSIX and windows systems.
+**
+** This header file is #include-ed by sqliteInt.h and thus ends up
+** being included by every source file.
+**
+** $Id: os.h,v 1.105 2008/06/26 10:41:19 danielk1977 Exp $
+*/
+#ifndef _SQLITE_OS_H_
+#define _SQLITE_OS_H_
+
+/*
+** Figure out if we are dealing with Unix, Windows, or some other
+** operating system. After the following block of preprocess macros,
+** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
+** will defined to either 1 or 0. One of the four will be 1. The other
+** three will be 0.
+*/
+#ifdef OS_SYMBIAN
+# define SQLITE_OS_SYMBIAN 1
+# define SQLITE_OS_OTHER 1
+#endif
+#if defined(SQLITE_OS_OTHER)
+# if SQLITE_OS_OTHER==1
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# undef SQLITE_OS_OS2
+# define SQLITE_OS_OS2 0
+# else
+# undef SQLITE_OS_OTHER
+# endif
+#endif
+#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
+# define SQLITE_OS_OTHER 0
+# ifndef SQLITE_OS_WIN
+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
+# define SQLITE_OS_WIN 1
+# define SQLITE_OS_UNIX 0
+# define SQLITE_OS_OS2 0
+# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 0
+# define SQLITE_OS_OS2 1
+# else
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 1
+# define SQLITE_OS_OS2 0
+# endif
+# else
+# define SQLITE_OS_UNIX 0
+# define SQLITE_OS_OS2 0
+# endif
+#else
+# ifndef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# endif
+#endif
+
+/*
+** Determine if we are dealing with WindowsCE - which has a much
+** reduced API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
+
+/*
+** Define the maximum size of a temporary filename
+*/
+#if SQLITE_OS_WIN
+# include <windows.h>
+# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
+#elif SQLITE_OS_OS2
+# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
+# include <os2safe.h> /* has to be included before os2.h for linking to work */
+# endif
+# define INCL_DOSDATETIME
+# define INCL_DOSFILEMGR
+# define INCL_DOSERRORS
+# define INCL_DOSMISC
+# define INCL_DOSPROCESS
+# define INCL_DOSMODULEMGR
+# define INCL_DOSSEMAPHORES
+# include <os2.h>
+# include <uconv.h>
+# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
+#else
+# define SQLITE_TEMPNAME_SIZE 200
+#endif
+
+/* If the SET_FULLSYNC macro is not defined above, then make it
+** a no-op
+*/
+#ifndef SET_FULLSYNC
+# define SET_FULLSYNC(x,y)
+#endif
+
+/*
+** The default size of a disk sector
+*/
+#ifndef SQLITE_DEFAULT_SECTOR_SIZE
+# define SQLITE_DEFAULT_SECTOR_SIZE 512
+#endif
+
+/*
+** Temporary files are named starting with this prefix followed by 16 random
+** alphanumeric characters, and no file extension. They are stored in the
+** OS's standard temporary file directory, and are deleted prior to exit.
+** If sqlite is being embedded in another program, you may wish to change the
+** prefix to reflect your program's name, so that if your program exits
+** prematurely, old temporary files can be easily identified. This can be done
+** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
+**
+** 2006-10-31: The default prefix used to be "sqlite_". But then
+** Mcafee started using SQLite in their anti-virus product and it
+** started putting files with the "sqlite" name in the c:/temp folder.
+** This annoyed many windows users. Those users would then do a
+** Google search for "sqlite", find the telephone numbers of the
+** developers and call to wake them up at night and complain.
+** For this reason, the default name prefix is changed to be "sqlite"
+** spelled backwards. So the temp files are still identified, but
+** anybody smart enough to figure out the code is also likely smart
+** enough to know that calling the developer will not help get rid
+** of the file.
+*/
+#ifndef SQLITE_TEMP_FILE_PREFIX
+# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
+#endif
+
+/*
+** The following values may be passed as the second argument to
+** sqlite3OsLock(). The various locks exhibit the following semantics:
+**
+** SHARED: Any number of processes may hold a SHARED lock simultaneously.
+** RESERVED: A single process may hold a RESERVED lock on a file at
+** any time. Other processes may hold and obtain new SHARED locks.
+** PENDING: A single process may hold a PENDING lock on a file at
+** any one time. Existing SHARED locks may persist, but no new
+** SHARED locks may be obtained by other processes.
+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
+**
+** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
+** process that requests an EXCLUSIVE lock may actually obtain a PENDING
+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
+** sqlite3OsLock().
+*/
+#define NO_LOCK 0
+#define SHARED_LOCK 1
+#define RESERVED_LOCK 2
+#define PENDING_LOCK 3
+#define EXCLUSIVE_LOCK 4
+
+/*
+** File Locking Notes: (Mostly about windows but also some info for Unix)
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** A SHARED_LOCK is obtained by locking a single randomly-chosen
+** byte out of a specific range of bytes. The lock byte is obtained at
+** random so two separate readers can probably access the file at the
+** same time, unless they are unlucky and choose the same lock byte.
+** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
+** There can only be one writer. A RESERVED_LOCK is obtained by locking
+** a single byte of the file that is designated as the reserved lock byte.
+** A PENDING_LOCK is obtained by locking a designated byte different from
+** the RESERVED_LOCK byte.
+**
+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
+** which means we can use reader/writer locks. When reader/writer locks
+** are used, the lock is placed on the same range of bytes that is used
+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
+** will support two or more Win95 readers or two or more WinNT readers.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** The following #defines specify the range of bytes used for locking.
+** SHARED_SIZE is the number of bytes available in the pool from which
+** a random byte is selected for a shared lock. The pool of bytes for
+** shared locks begins at SHARED_FIRST.
+**
+** These #defines are available in sqlite_aux.h so that adaptors for
+** connecting SQLite to other operating systems can use the same byte
+** ranges for locking. In particular, the same locking strategy and
+** byte ranges are used for Unix. This leaves open the possiblity of having
+** clients on win95, winNT, and unix all talking to the same shared file
+** and all locking correctly. To do so would require that samba (or whatever
+** tool is being used for file sharing) implements locks correctly between
+** windows and unix. I'm guessing that isn't likely to happen, but by
+** using the same locking range we are at least open to the possibility.
+**
+** Locking in windows is manditory. For this reason, we cannot store
+** actual data in the bytes used for locking. The pager never allocates
+** the pages involved in locking therefore. SHARED_SIZE is selected so
+** that all locks will fit on a single page even at the minimum page size.
+** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
+** is set high so that we don't have to allocate an unused page except
+** for very large databases. But one should test the page skipping logic
+** by setting PENDING_BYTE low and running the entire regression suite.
+**
+** Changing the value of PENDING_BYTE results in a subtly incompatible
+** file format. Depending on how it is changed, you might not notice
+** the incompatibility right away, even running a full regression test.
+** The default location of PENDING_BYTE is the first byte past the
+** 1GB boundary.
+**
+*/
+#ifndef SQLITE_TEST
+#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */
+#else
+extern unsigned int sqlite3_pending_byte;
+#define PENDING_BYTE sqlite3_pending_byte
+#endif
+
+#define RESERVED_BYTE (PENDING_BYTE+1)
+#define SHARED_FIRST (PENDING_BYTE+2)
+#define SHARED_SIZE 510
+
+/*
+** Functions for accessing sqlite3_file methods
+*/
+int sqlite3OsClose(sqlite3_file*);
+int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
+int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
+int sqlite3OsTruncate(sqlite3_file*, i64 size);
+int sqlite3OsSync(sqlite3_file*, int);
+int sqlite3OsFileSize(sqlite3_file*, i64 *pSize);
+int sqlite3OsLock(sqlite3_file*, int);
+int sqlite3OsUnlock(sqlite3_file*, int);
+int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
+int sqlite3OsFileControl(sqlite3_file*,int,void*);
+int sqlite3OsSectorSize(sqlite3_file *id);
+int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
+
+/*
+** Functions for accessing sqlite3_vfs methods
+*/
+int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *);
+int sqlite3OsDelete(sqlite3_vfs *, const char *, int);
+int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut);
+int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
+void sqlite3OsDlError(sqlite3_vfs *, int, char *);
+void *sqlite3OsDlSym(sqlite3_vfs *, void *, const char *);
+void sqlite3OsDlClose(sqlite3_vfs *, void *);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
+int sqlite3OsSleep(sqlite3_vfs *, int);
+int sqlite3OsCurrentTime(sqlite3_vfs *, double*);
+
+/*
+** Convenience functions for opening and closing files using
+** sqlite3_malloc() to obtain space for the file-handle structure.
+*/
+int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
+int sqlite3OsCloseFree(sqlite3_file *);
+
+#endif /* _SQLITE_OS_H_ */
diff --git a/third_party/sqlite/src/os_common.h b/third_party/sqlite/src/os_common.h
new file mode 100755
index 0000000..5fab7a4
--- /dev/null
+++ b/third_party/sqlite/src/os_common.h
@@ -0,0 +1,137 @@
+/*
+** 2004 May 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 contains macros and a little bit of code that is common to
+** all of the platform-specific files (os_*.c) and is #included into those
+** files.
+**
+** This file should be #included by the os_*.c files only. It is not a
+** general purpose header file.
+**
+** $Id: os_common.h,v 1.37 2008/05/29 20:22:37 shane Exp $
+*/
+#ifndef _OS_COMMON_H_
+#define _OS_COMMON_H_
+
+/*
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the
+** switch. The following code should catch this problem at compile-time.
+*/
+#ifdef MEMORY_DEBUG
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
+#endif
+
+
+/*
+ * When testing, this global variable stores the location of the
+ * pending-byte in the database file.
+ */
+#ifdef SQLITE_TEST
+unsigned int sqlite3_pending_byte = 0x40000000;
+#endif
+
+#ifdef SQLITE_DEBUG
+int sqlite3OSTrace = 0;
+#define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X)
+#define OSTRACE2(X,Y) if( sqlite3OSTrace ) sqlite3DebugPrintf(X,Y)
+#define OSTRACE3(X,Y,Z) if( sqlite3OSTrace ) sqlite3DebugPrintf(X,Y,Z)
+#define OSTRACE4(X,Y,Z,A) if( sqlite3OSTrace ) sqlite3DebugPrintf(X,Y,Z,A)
+#define OSTRACE5(X,Y,Z,A,B) if( sqlite3OSTrace ) sqlite3DebugPrintf(X,Y,Z,A,B)
+#define OSTRACE6(X,Y,Z,A,B,C) \
+ if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
+#define OSTRACE7(X,Y,Z,A,B,C,D) \
+ if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
+#else
+#define OSTRACE1(X)
+#define OSTRACE2(X,Y)
+#define OSTRACE3(X,Y,Z)
+#define OSTRACE4(X,Y,Z,A)
+#define OSTRACE5(X,Y,Z,A,B)
+#define OSTRACE6(X,Y,Z,A,B,C)
+#define OSTRACE7(X,Y,Z,A,B,C,D)
+#endif
+
+/*
+** Macros for performance tracing. Normally turned off. Only works
+** on i486 hardware.
+*/
+#ifdef SQLITE_PERFORMANCE_TRACE
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+#include "hwtime.h"
+
+static sqlite_uint64 g_start;
+static sqlite_uint64 g_elapsed;
+#define TIMER_START g_start=sqlite3Hwtime()
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
+#define TIMER_ELAPSED g_elapsed
+#else
+#define TIMER_START
+#define TIMER_END
+#define TIMER_ELAPSED ((sqlite_uint64)0)
+#endif
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
+int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
+int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
+int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
+int sqlite3_io_error_benign = 0; /* True if errors are benign */
+int sqlite3_diskfull_pending = 0;
+int sqlite3_diskfull = 0;
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
+#define SimulateIOError(CODE) \
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
+ || sqlite3_io_error_pending-- == 1 ) \
+ { local_ioerr(); CODE; }
+static void local_ioerr(){
+ IOTRACE(("IOERR\n"));
+ sqlite3_io_error_hit++;
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
+}
+#define SimulateDiskfullError(CODE) \
+ if( sqlite3_diskfull_pending ){ \
+ if( sqlite3_diskfull_pending == 1 ){ \
+ local_ioerr(); \
+ sqlite3_diskfull = 1; \
+ sqlite3_io_error_hit = 1; \
+ CODE; \
+ }else{ \
+ sqlite3_diskfull_pending--; \
+ } \
+ }
+#else
+#define SimulateIOErrorBenign(X)
+#define SimulateIOError(A)
+#define SimulateDiskfullError(A)
+#endif
+
+/*
+** When testing, keep a count of the number of open files.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_open_file_count = 0;
+#define OpenCounter(X) sqlite3_open_file_count+=(X)
+#else
+#define OpenCounter(X)
+#endif
+
+#endif /* !defined(_OS_COMMON_H_) */
diff --git a/third_party/sqlite/src/os_os2.c b/third_party/sqlite/src/os_os2.c
new file mode 100755
index 0000000..f1af91d
--- /dev/null
+++ b/third_party/sqlite/src/os_os2.c
@@ -0,0 +1,1119 @@
+/*
+** 2006 Feb 14
+**
+** 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 contains code that is specific to OS/2.
+**
+** $Id: os_os2.c,v 1.55 2008/07/29 18:49:29 pweilbacher Exp $
+*/
+
+#include "sqliteInt.h"
+
+#if SQLITE_OS_OS2
+
+/*
+** A Note About Memory Allocation:
+**
+** This driver uses malloc()/free() directly rather than going through
+** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
+** are designed for use on embedded systems where memory is scarce and
+** malloc failures happen frequently. OS/2 does not typically run on
+** embedded systems, and when it does the developers normally have bigger
+** problems to worry about than running out of memory. So there is not
+** a compelling need to use the wrappers.
+**
+** But there is a good reason to not use the wrappers. If we use the
+** wrappers then we will get simulated malloc() failures within this
+** driver. And that causes all kinds of problems for our tests. We
+** could enhance SQLite to deal with simulated malloc failures within
+** the OS driver, but the code to deal with those failure would not
+** be exercised on Linux (which does not need to malloc() in the driver)
+** and so we would have difficulty writing coverage tests for that
+** code. Better to leave the code out, we think.
+**
+** The point of this discussion is as follows: When creating a new
+** OS layer for an embedded system, if you use this file as an example,
+** avoid the use of malloc()/free(). Those routines work ok on OS/2
+** desktops but not so well in embedded systems.
+*/
+
+/*
+** Macros used to determine whether or not to use threads.
+*/
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
+# define SQLITE_OS2_THREADS 1
+#endif
+
+/*
+** Include code that is common to all os_*.c files
+*/
+#include "os_common.h"
+
+/*
+** The os2File structure is subclass of sqlite3_file specific for the OS/2
+** protability layer.
+*/
+typedef struct os2File os2File;
+struct os2File {
+ const sqlite3_io_methods *pMethod; /* Always the first entry */
+ HFILE h; /* Handle for accessing the file */
+ char* pathToDel; /* Name of file to delete on close, NULL if not */
+ unsigned char locktype; /* Type of lock currently held on this file */
+};
+
+#define LOCK_TIMEOUT 10L /* the default locking timeout */
+
+/*****************************************************************************
+** The next group of routines implement the I/O methods specified
+** by the sqlite3_io_methods object.
+******************************************************************************/
+
+/*
+** Close a file.
+*/
+static int os2Close( sqlite3_file *id ){
+ APIRET rc = NO_ERROR;
+ os2File *pFile;
+ if( id && (pFile = (os2File*)id) != 0 ){
+ OSTRACE2( "CLOSE %d\n", pFile->h );
+ rc = DosClose( pFile->h );
+ pFile->locktype = NO_LOCK;
+ if( pFile->pathToDel != NULL ){
+ rc = DosForceDelete( (PSZ)pFile->pathToDel );
+ free( pFile->pathToDel );
+ pFile->pathToDel = NULL;
+ }
+ id = 0;
+ OpenCounter( -1 );
+ }
+
+ return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
+}
+
+/*
+** Read data from a file into a buffer. Return SQLITE_OK if all
+** bytes were read successfully and SQLITE_IOERR if anything goes
+** wrong.
+*/
+static int os2Read(
+ sqlite3_file *id, /* File to read from */
+ void *pBuf, /* Write content into this buffer */
+ int amt, /* Number of bytes to read */
+ sqlite3_int64 offset /* Begin reading at this offset */
+){
+ ULONG fileLocation = 0L;
+ ULONG got;
+ os2File *pFile = (os2File*)id;
+ assert( id!=0 );
+ SimulateIOError( return SQLITE_IOERR_READ );
+ OSTRACE3( "READ %d lock=%d\n", pFile->h, pFile->locktype );
+ if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
+ return SQLITE_IOERR;
+ }
+ if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
+ return SQLITE_IOERR_READ;
+ }
+ if( got == (ULONG)amt )
+ return SQLITE_OK;
+ else {
+ memset(&((char*)pBuf)[got], 0, amt-got);
+ return SQLITE_IOERR_SHORT_READ;
+ }
+}
+
+/*
+** Write data from a buffer into a file. Return SQLITE_OK on success
+** or some other error code on failure.
+*/
+static int os2Write(
+ sqlite3_file *id, /* File to write into */
+ const void *pBuf, /* The bytes to be written */
+ int amt, /* Number of bytes to write */
+ sqlite3_int64 offset /* Offset into the file to begin writing at */
+){
+ ULONG fileLocation = 0L;
+ APIRET rc = NO_ERROR;
+ ULONG wrote;
+ os2File *pFile = (os2File*)id;
+ assert( id!=0 );
+ SimulateIOError( return SQLITE_IOERR_WRITE );
+ SimulateDiskfullError( return SQLITE_FULL );
+ OSTRACE3( "WRITE %d lock=%d\n", pFile->h, pFile->locktype );
+ if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
+ return SQLITE_IOERR;
+ }
+ assert( amt>0 );
+ while( amt > 0 &&
+ ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
+ wrote > 0
+ ){
+ amt -= wrote;
+ pBuf = &((char*)pBuf)[wrote];
+ }
+
+ return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
+}
+
+/*
+** Truncate an open file to a specified size
+*/
+static int os2Truncate( sqlite3_file *id, i64 nByte ){
+ APIRET rc = NO_ERROR;
+ os2File *pFile = (os2File*)id;
+ OSTRACE3( "TRUNCATE %d %lld\n", pFile->h, nByte );
+ SimulateIOError( return SQLITE_IOERR_TRUNCATE );
+ rc = DosSetFileSize( pFile->h, nByte );
+ return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
+}
+
+#ifdef SQLITE_TEST
+/*
+** Count the number of fullsyncs and normal syncs. This is used to test
+** that syncs and fullsyncs are occuring at the right times.
+*/
+int sqlite3_sync_count = 0;
+int sqlite3_fullsync_count = 0;
+#endif
+
+/*
+** Make sure all writes to a particular file are committed to disk.
+*/
+static int os2Sync( sqlite3_file *id, int flags ){
+ os2File *pFile = (os2File*)id;
+ OSTRACE3( "SYNC %d lock=%d\n", pFile->h, pFile->locktype );
+#ifdef SQLITE_TEST
+ if( flags & SQLITE_SYNC_FULL){
+ sqlite3_fullsync_count++;
+ }
+ sqlite3_sync_count++;
+#endif
+ return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
+}
+
+/*
+** Determine the current size of a file in bytes
+*/
+static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
+ APIRET rc = NO_ERROR;
+ FILESTATUS3 fsts3FileInfo;
+ memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
+ assert( id!=0 );
+ SimulateIOError( return SQLITE_IOERR );
+ rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
+ if( rc == NO_ERROR ){
+ *pSize = fsts3FileInfo.cbFile;
+ return SQLITE_OK;
+ }else{
+ return SQLITE_IOERR;
+ }
+}
+
+/*
+** Acquire a reader lock.
+*/
+static int getReadLock( os2File *pFile ){
+ FILELOCK LockArea,
+ UnlockArea;
+ APIRET res;
+ memset(&LockArea, 0, sizeof(LockArea));
+ memset(&UnlockArea, 0, sizeof(UnlockArea));
+ LockArea.lOffset = SHARED_FIRST;
+ LockArea.lRange = SHARED_SIZE;
+ UnlockArea.lOffset = 0L;
+ UnlockArea.lRange = 0L;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
+ OSTRACE3( "GETREADLOCK %d res=%d\n", pFile->h, res );
+ return res;
+}
+
+/*
+** Undo a readlock
+*/
+static int unlockReadLock( os2File *id ){
+ FILELOCK LockArea,
+ UnlockArea;
+ APIRET res;
+ memset(&LockArea, 0, sizeof(LockArea));
+ memset(&UnlockArea, 0, sizeof(UnlockArea));
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = SHARED_FIRST;
+ UnlockArea.lRange = SHARED_SIZE;
+ res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
+ OSTRACE3( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res );
+ return res;
+}
+
+/*
+** Lock the file with the lock specified by parameter locktype - one
+** of the following:
+**
+** (1) SHARED_LOCK
+** (2) RESERVED_LOCK
+** (3) PENDING_LOCK
+** (4) EXCLUSIVE_LOCK
+**
+** Sometimes when requesting one lock state, additional lock states
+** are inserted in between. The locking might fail on one of the later
+** transitions leaving the lock state different from what it started but
+** still short of its goal. The following chart shows the allowed
+** transitions and the inserted intermediate states:
+**
+** UNLOCKED -> SHARED
+** SHARED -> RESERVED
+** SHARED -> (PENDING) -> EXCLUSIVE
+** RESERVED -> (PENDING) -> EXCLUSIVE
+** PENDING -> EXCLUSIVE
+**
+** This routine will only increase a lock. The os2Unlock() routine
+** erases all locks at once and returns us immediately to locking level 0.
+** It is not possible to lower the locking level one step at a time. You
+** must go straight to locking level 0.
+*/
+static int os2Lock( sqlite3_file *id, int locktype ){
+ int rc = SQLITE_OK; /* Return code from subroutines */
+ APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
+ int newLocktype; /* Set pFile->locktype to this value before exiting */
+ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
+ FILELOCK LockArea,
+ UnlockArea;
+ os2File *pFile = (os2File*)id;
+ memset(&LockArea, 0, sizeof(LockArea));
+ memset(&UnlockArea, 0, sizeof(UnlockArea));
+ assert( pFile!=0 );
+ OSTRACE4( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype );
+
+ /* If there is already a lock of this type or more restrictive on the
+ ** os2File, do nothing. Don't use the end_lock: exit path, as
+ ** sqlite3_mutex_enter() hasn't been called yet.
+ */
+ if( pFile->locktype>=locktype ){
+ OSTRACE3( "LOCK %d %d ok (already held)\n", pFile->h, locktype );
+ return SQLITE_OK;
+ }
+
+ /* Make sure the locking sequence is correct
+ */
+ assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
+ assert( locktype!=PENDING_LOCK );
+ assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
+
+ /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
+ ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
+ ** the PENDING_LOCK byte is temporary.
+ */
+ newLocktype = pFile->locktype;
+ if( pFile->locktype==NO_LOCK
+ || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
+ ){
+ LockArea.lOffset = PENDING_BYTE;
+ LockArea.lRange = 1L;
+ UnlockArea.lOffset = 0L;
+ UnlockArea.lRange = 0L;
+
+ /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
+ if( res == NO_ERROR ){
+ gotPendingLock = 1;
+ OSTRACE3( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res );
+ }
+ }
+
+ /* Acquire a shared lock
+ */
+ if( locktype==SHARED_LOCK && res == NO_ERROR ){
+ assert( pFile->locktype==NO_LOCK );
+ res = getReadLock(pFile);
+ if( res == NO_ERROR ){
+ newLocktype = SHARED_LOCK;
+ }
+ OSTRACE3( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res );
+ }
+
+ /* Acquire a RESERVED lock
+ */
+ if( locktype==RESERVED_LOCK && res == NO_ERROR ){
+ assert( pFile->locktype==SHARED_LOCK );
+ LockArea.lOffset = RESERVED_BYTE;
+ LockArea.lRange = 1L;
+ UnlockArea.lOffset = 0L;
+ UnlockArea.lRange = 0L;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ if( res == NO_ERROR ){
+ newLocktype = RESERVED_LOCK;
+ }
+ OSTRACE3( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res );
+ }
+
+ /* Acquire a PENDING lock
+ */
+ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
+ newLocktype = PENDING_LOCK;
+ gotPendingLock = 0;
+ OSTRACE2( "LOCK %d acquire pending lock. pending lock boolean unset.\n", pFile->h );
+ }
+
+ /* Acquire an EXCLUSIVE lock
+ */
+ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
+ assert( pFile->locktype>=SHARED_LOCK );
+ res = unlockReadLock(pFile);
+ OSTRACE2( "unreadlock = %d\n", res );
+ LockArea.lOffset = SHARED_FIRST;
+ LockArea.lRange = SHARED_SIZE;
+ UnlockArea.lOffset = 0L;
+ UnlockArea.lRange = 0L;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ if( res == NO_ERROR ){
+ newLocktype = EXCLUSIVE_LOCK;
+ }else{
+ OSTRACE2( "OS/2 error-code = %d\n", res );
+ getReadLock(pFile);
+ }
+ OSTRACE3( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res );
+ }
+
+ /* If we are holding a PENDING lock that ought to be released, then
+ ** release it now.
+ */
+ if( gotPendingLock && locktype==SHARED_LOCK ){
+ int r;
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = PENDING_BYTE;
+ UnlockArea.lRange = 1L;
+ r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r );
+ }
+
+ /* Update the state of the lock has held in the file descriptor then
+ ** return the appropriate result code.
+ */
+ if( res == NO_ERROR ){
+ rc = SQLITE_OK;
+ }else{
+ OSTRACE4( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
+ locktype, newLocktype );
+ rc = SQLITE_BUSY;
+ }
+ pFile->locktype = newLocktype;
+ OSTRACE3( "LOCK %d now %d\n", pFile->h, pFile->locktype );
+ return rc;
+}
+
+/*
+** This routine checks if there is a RESERVED lock held on the specified
+** file by this or any other process. If such a lock is held, return
+** non-zero, otherwise zero.
+*/
+static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){
+ int r = 0;
+ os2File *pFile = (os2File*)id;
+ assert( pFile!=0 );
+ if( pFile->locktype>=RESERVED_LOCK ){
+ r = 1;
+ OSTRACE3( "TEST WR-LOCK %d %d (local)\n", pFile->h, r );
+ }else{
+ FILELOCK LockArea,
+ UnlockArea;
+ APIRET rc = NO_ERROR;
+ memset(&LockArea, 0, sizeof(LockArea));
+ memset(&UnlockArea, 0, sizeof(UnlockArea));
+ LockArea.lOffset = RESERVED_BYTE;
+ LockArea.lRange = 1L;
+ UnlockArea.lOffset = 0L;
+ UnlockArea.lRange = 0L;
+ rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc );
+ if( rc == NO_ERROR ){
+ APIRET rcu = NO_ERROR; /* return code for unlocking */
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = RESERVED_BYTE;
+ UnlockArea.lRange = 1L;
+ rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu );
+ }
+ r = !(rc == NO_ERROR);
+ OSTRACE3( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r );
+ }
+ *pOut = r;
+ return SQLITE_OK;
+}
+
+/*
+** Lower the locking level on file descriptor id to locktype. locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+**
+** It is not possible for this routine to fail if the second argument
+** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
+** might return SQLITE_IOERR;
+*/
+static int os2Unlock( sqlite3_file *id, int locktype ){
+ int type;
+ os2File *pFile = (os2File*)id;
+ APIRET rc = SQLITE_OK;
+ APIRET res = NO_ERROR;
+ FILELOCK LockArea,
+ UnlockArea;
+ memset(&LockArea, 0, sizeof(LockArea));
+ memset(&UnlockArea, 0, sizeof(UnlockArea));
+ assert( pFile!=0 );
+ assert( locktype<=SHARED_LOCK );
+ OSTRACE4( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype );
+ type = pFile->locktype;
+ if( type>=EXCLUSIVE_LOCK ){
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = SHARED_FIRST;
+ UnlockArea.lRange = SHARED_SIZE;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res );
+ if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
+ /* This should never happen. We should always be able to
+ ** reacquire the read lock */
+ OSTRACE3( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype );
+ rc = SQLITE_IOERR_UNLOCK;
+ }
+ }
+ if( type>=RESERVED_LOCK ){
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = RESERVED_BYTE;
+ UnlockArea.lRange = 1L;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "UNLOCK %d reserved res=%d\n", pFile->h, res );
+ }
+ if( locktype==NO_LOCK && type>=SHARED_LOCK ){
+ res = unlockReadLock(pFile);
+ OSTRACE5( "UNLOCK %d is %d want %d res=%d\n", pFile->h, type, locktype, res );
+ }
+ if( type>=PENDING_LOCK ){
+ LockArea.lOffset = 0L;
+ LockArea.lRange = 0L;
+ UnlockArea.lOffset = PENDING_BYTE;
+ UnlockArea.lRange = 1L;
+ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
+ OSTRACE3( "UNLOCK %d pending res=%d\n", pFile->h, res );
+ }
+ pFile->locktype = locktype;
+ OSTRACE3( "UNLOCK %d now %d\n", pFile->h, pFile->locktype );
+ return rc;
+}
+
+/*
+** Control and query of the open file handle.
+*/
+static int os2FileControl(sqlite3_file *id, int op, void *pArg){
+ switch( op ){
+ case SQLITE_FCNTL_LOCKSTATE: {
+ *(int*)pArg = ((os2File*)id)->locktype;
+ OSTRACE3( "FCNTL_LOCKSTATE %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_ERROR;
+}
+
+/*
+** Return the sector size in bytes of the underlying block device for
+** the specified file. This is almost always 512 bytes, but may be
+** larger for some devices.
+**
+** SQLite code assumes this function cannot fail. It also assumes that
+** if two files are created in the same file-system directory (i.e.
+** a database and its journal file) that the sector size will be the
+** same for both.
+*/
+static int os2SectorSize(sqlite3_file *id){
+ return SQLITE_DEFAULT_SECTOR_SIZE;
+}
+
+/*
+** Return a vector of device characteristics.
+*/
+static int os2DeviceCharacteristics(sqlite3_file *id){
+ return 0;
+}
+
+
+/*
+** Character set conversion objects used by conversion routines.
+*/
+static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
+static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */
+
+/*
+** Helper function to initialize the conversion objects from and to UTF-8.
+*/
+static void initUconvObjects( void ){
+ if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
+ ucUtf8 = NULL;
+ if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
+ uclCp = NULL;
+}
+
+/*
+** Helper function to free the conversion objects from and to UTF-8.
+*/
+static void freeUconvObjects( void ){
+ if ( ucUtf8 )
+ UniFreeUconvObject( ucUtf8 );
+ if ( uclCp )
+ UniFreeUconvObject( uclCp );
+ ucUtf8 = NULL;
+ uclCp = NULL;
+}
+
+/*
+** Helper function to convert UTF-8 filenames to local OS/2 codepage.
+** The two-step process: first convert the incoming UTF-8 string
+** into UCS-2 and then from UCS-2 to the current codepage.
+** The returned char pointer has to be freed.
+*/
+static char *convertUtf8PathToCp( const char *in ){
+ UniChar tempPath[CCHMAXPATH];
+ char *out = (char *)calloc( CCHMAXPATH, 1 );
+
+ if( !out )
+ return NULL;
+
+ if( !ucUtf8 || !uclCp )
+ initUconvObjects();
+
+ /* determine string for the conversion of UTF-8 which is CP1208 */
+ if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
+ return out; /* if conversion fails, return the empty string */
+
+ /* conversion for current codepage which can be used for paths */
+ UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
+
+ return out;
+}
+
+/*
+** Helper function to convert filenames from local codepage to UTF-8.
+** The two-step process: first convert the incoming codepage-specific
+** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
+** The returned char pointer has to be freed.
+**
+** This function is non-static to be able to use this in shell.c and
+** similar applications that take command line arguments.
+*/
+char *convertCpPathToUtf8( const char *in ){
+ UniChar tempPath[CCHMAXPATH];
+ char *out = (char *)calloc( CCHMAXPATH, 1 );
+
+ if( !out )
+ return NULL;
+
+ if( !ucUtf8 || !uclCp )
+ initUconvObjects();
+
+ /* conversion for current codepage which can be used for paths */
+ if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
+ return out; /* if conversion fails, return the empty string */
+
+ /* determine string for the conversion of UTF-8 which is CP1208 */
+ UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
+
+ return out;
+}
+
+/*
+** This vector defines all the methods that can operate on an
+** sqlite3_file for os2.
+*/
+static const sqlite3_io_methods os2IoMethod = {
+ 1, /* iVersion */
+ os2Close,
+ os2Read,
+ os2Write,
+ os2Truncate,
+ os2Sync,
+ os2FileSize,
+ os2Lock,
+ os2Unlock,
+ os2CheckReservedLock,
+ os2FileControl,
+ os2SectorSize,
+ os2DeviceCharacteristics
+};
+
+/***************************************************************************
+** Here ends the I/O methods that form the sqlite3_io_methods object.
+**
+** The next block of code implements the VFS methods.
+****************************************************************************/
+
+/*
+** Create a temporary file name in zBuf. zBuf must be big enough to
+** hold at pVfs->mxPathname characters.
+*/
+static int getTempname(int nBuf, char *zBuf ){
+ static const unsigned char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ int i, j;
+ char zTempPathBuf[3];
+ PSZ zTempPath = (PSZ)&zTempPathBuf;
+ if( sqlite3_temp_directory ){
+ zTempPath = sqlite3_temp_directory;
+ }else{
+ if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){
+ if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){
+ if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){
+ ULONG ulDriveNum = 0, ulDriveMap = 0;
+ DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap );
+ sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) );
+ }
+ }
+ }
+ }
+ /* Strip off a trailing slashes or backslashes, otherwise we would get *
+ * multiple (back)slashes which causes DosOpen() to fail. *
+ * Trailing spaces are not allowed, either. */
+ j = strlen(zTempPath);
+ while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/'
+ || zTempPath[j-1] == ' ' ) ){
+ j--;
+ }
+ zTempPath[j] = '\0';
+ if( !sqlite3_temp_directory ){
+ char *zTempPathUTF = convertCpPathToUtf8( zTempPath );
+ sqlite3_snprintf( nBuf-30, zBuf,
+ "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF );
+ free( zTempPathUTF );
+ }else{
+ sqlite3_snprintf( nBuf-30, zBuf,
+ "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath );
+ }
+ j = strlen( zBuf );
+ sqlite3_randomness( 20, &zBuf[j] );
+ for( i = 0; i < 20; i++, j++ ){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ OSTRACE2( "TEMP FILENAME: %s\n", zBuf );
+ return SQLITE_OK;
+}
+
+
+/*
+** Turn a relative pathname into a full pathname. Write the full
+** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname
+** bytes in size.
+*/
+static int os2FullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zRelative, /* Possibly relative input path */
+ int nFull, /* Size of output buffer in bytes */
+ char *zFull /* Output buffer */
+){
+ char *zRelativeCp = convertUtf8PathToCp( zRelative );
+ char zFullCp[CCHMAXPATH] = "\0";
+ char *zFullUTF;
+ APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp,
+ CCHMAXPATH );
+ free( zRelativeCp );
+ zFullUTF = convertCpPathToUtf8( zFullCp );
+ sqlite3_snprintf( nFull, zFull, zFullUTF );
+ free( zFullUTF );
+ return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
+}
+
+
+/*
+** Open a file.
+*/
+static int os2Open(
+ sqlite3_vfs *pVfs, /* Not used */
+ const char *zName, /* Name of the file */
+ sqlite3_file *id, /* Write the SQLite file handle here */
+ int flags, /* Open mode flags */
+ int *pOutFlags /* Status return flags */
+){
+ HFILE h;
+ ULONG ulFileAttribute = 0;
+ ULONG ulOpenFlags = 0;
+ ULONG ulOpenMode = 0;
+ os2File *pFile = (os2File*)id;
+ APIRET rc = NO_ERROR;
+ ULONG ulAction;
+ char *zNameCp;
+ char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */
+
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
+ */
+ if( !zName ){
+ int rc = getTempname(CCHMAXPATH+1, zTmpname);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ zName = zTmpname;
+ }
+
+
+ memset( pFile, 0, sizeof(*pFile) );
+
+ OSTRACE2( "OPEN want %d\n", flags );
+
+ /*ulOpenMode = flags & SQLITE_OPEN_READWRITE ? OPEN_ACCESS_READWRITE : OPEN_ACCESS_READONLY;*/
+ if( flags & SQLITE_OPEN_READWRITE ){
+ ulOpenMode |= OPEN_ACCESS_READWRITE;
+ OSTRACE1( "OPEN read/write\n" );
+ }else{
+ ulOpenMode |= OPEN_ACCESS_READONLY;
+ OSTRACE1( "OPEN read only\n" );
+ }
+
+ /*ulOpenFlags = flags & SQLITE_OPEN_CREATE ? OPEN_ACTION_CREATE_IF_NEW : OPEN_ACTION_FAIL_IF_NEW;*/
+ if( flags & SQLITE_OPEN_CREATE ){
+ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
+ OSTRACE1( "OPEN open new/create\n" );
+ }else{
+ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
+ OSTRACE1( "OPEN open existing\n" );
+ }
+
+ /*ulOpenMode |= flags & SQLITE_OPEN_MAIN_DB ? OPEN_SHARE_DENYNONE : OPEN_SHARE_DENYWRITE;*/
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ ulOpenMode |= OPEN_SHARE_DENYNONE;
+ OSTRACE1( "OPEN share read/write\n" );
+ }else{
+ ulOpenMode |= OPEN_SHARE_DENYWRITE;
+ OSTRACE1( "OPEN share read only\n" );
+ }
+
+ if( flags & (SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TEMP_JOURNAL
+ | SQLITE_OPEN_SUBJOURNAL) ){
+ char pathUtf8[CCHMAXPATH];
+#ifdef NDEBUG /* when debugging we want to make sure it is deleted */
+ ulFileAttribute = FILE_HIDDEN;
+#endif
+ ulFileAttribute = FILE_NORMAL;
+ os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 );
+ pFile->pathToDel = convertUtf8PathToCp( pathUtf8 );
+ OSTRACE1( "OPEN hidden/delete on close file attributes\n" );
+ }else{
+ ulFileAttribute = FILE_ARCHIVED | FILE_NORMAL;
+ pFile->pathToDel = NULL;
+ OSTRACE1( "OPEN normal file attribute\n" );
+ }
+
+ /* always open in random access mode for possibly better speed */
+ ulOpenMode |= OPEN_FLAGS_RANDOM;
+ ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR;
+ ulOpenMode |= OPEN_FLAGS_NOINHERIT;
+
+ zNameCp = convertUtf8PathToCp( zName );
+ rc = DosOpen( (PSZ)zNameCp,
+ &h,
+ &ulAction,
+ 0L,
+ ulFileAttribute,
+ ulOpenFlags,
+ ulOpenMode,
+ (PEAOP2)NULL );
+ free( zNameCp );
+ if( rc != NO_ERROR ){
+ OSTRACE7( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
+ rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode );
+ if( pFile->pathToDel )
+ free( pFile->pathToDel );
+ pFile->pathToDel = NULL;
+ if( flags & SQLITE_OPEN_READWRITE ){
+ OSTRACE2( "OPEN %d Invalid handle\n", ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) );
+ return os2Open( pVfs, zName, id,
+ ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE),
+ pOutFlags );
+ }else{
+ return SQLITE_CANTOPEN;
+ }
+ }
+
+ if( pOutFlags ){
+ *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
+ }
+
+ pFile->pMethod = &os2IoMethod;
+ pFile->h = h;
+ OpenCounter(+1);
+ OSTRACE3( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags );
+ return SQLITE_OK;
+}
+
+/*
+** Delete the named file.
+*/
+static int os2Delete(
+ sqlite3_vfs *pVfs, /* Not used on os2 */
+ const char *zFilename, /* Name of file to delete */
+ int syncDir /* Not used on os2 */
+){
+ APIRET rc = NO_ERROR;
+ char *zFilenameCp = convertUtf8PathToCp( zFilename );
+ SimulateIOError( return SQLITE_IOERR_DELETE );
+ rc = DosDelete( (PSZ)zFilenameCp );
+ free( zFilenameCp );
+ OSTRACE2( "DELETE \"%s\"\n", zFilename );
+ return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
+}
+
+/*
+** Check the existance and status of a file.
+*/
+static int os2Access(
+ sqlite3_vfs *pVfs, /* Not used on os2 */
+ const char *zFilename, /* Name of file to check */
+ int flags, /* Type of test to make on this file */
+ int *pOut /* Write results here */
+){
+ FILESTATUS3 fsts3ConfigInfo;
+ APIRET rc = NO_ERROR;
+ char *zFilenameCp = convertUtf8PathToCp( zFilename );
+
+ memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) );
+ rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
+ &fsts3ConfigInfo, sizeof(FILESTATUS3) );
+ free( zFilenameCp );
+ OSTRACE4( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
+ fsts3ConfigInfo.attrFile, flags, rc );
+ switch( flags ){
+ case SQLITE_ACCESS_READ:
+ case SQLITE_ACCESS_EXISTS:
+ rc = (rc == NO_ERROR);
+ OSTRACE3( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc );
+ break;
+ case SQLITE_ACCESS_READWRITE:
+ rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 );
+ OSTRACE3( "ACCESS %s access of read/write rc=%d\n", zFilename, rc );
+ break;
+ default:
+ assert( !"Invalid flags argument" );
+ }
+ *pOut = rc;
+ return SQLITE_OK;
+}
+
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Interfaces for opening a shared library, finding entry points
+** within the shared library, and closing the shared library.
+*/
+/*
+** Interfaces for opening a shared library, finding entry points
+** within the shared library, and closing the shared library.
+*/
+static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
+ UCHAR loadErr[256];
+ HMODULE hmod;
+ APIRET rc;
+ char *zFilenameCp = convertUtf8PathToCp(zFilename);
+ rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod);
+ free(zFilenameCp);
+ return rc != NO_ERROR ? 0 : (void*)hmod;
+}
+/*
+** A no-op since the error code is returned on the DosLoadModule call.
+** os2Dlopen returns zero if DosLoadModule is not successful.
+*/
+static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
+/* no-op */
+}
+static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+ PFN pfn;
+ APIRET rc;
+ rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn);
+ if( rc != NO_ERROR ){
+ /* if the symbol itself was not found, search again for the same
+ * symbol with an extra underscore, that might be needed depending
+ * on the calling convention */
+ char _zSymbol[256] = "_";
+ strncat(_zSymbol, zSymbol, 255);
+ rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn);
+ }
+ return rc != NO_ERROR ? 0 : (void*)pfn;
+}
+static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
+ DosFreeModule((HMODULE)pHandle);
+}
+#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
+ #define os2DlOpen 0
+ #define os2DlError 0
+ #define os2DlSym 0
+ #define os2DlClose 0
+#endif
+
+
+/*
+** Write up to nBuf bytes of randomness into zBuf.
+*/
+static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
+ ULONG sizeofULong = sizeof(ULONG);
+ int n = 0;
+ if( sizeof(DATETIME) <= nBuf - n ){
+ DATETIME x;
+ DosGetDateTime(&x);
+ memcpy(&zBuf[n], &x, sizeof(x));
+ n += sizeof(x);
+ }
+
+ if( sizeofULong <= nBuf - n ){
+ PPIB ppib;
+ DosGetInfoBlocks(NULL, &ppib);
+ memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong);
+ n += sizeofULong;
+ }
+
+ if( sizeofULong <= nBuf - n ){
+ PTIB ptib;
+ DosGetInfoBlocks(&ptib, NULL);
+ memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong);
+ n += sizeofULong;
+ }
+
+ /* if we still haven't filled the buffer yet the following will */
+ /* grab everything once instead of making several calls for a single item */
+ if( sizeofULong <= nBuf - n ){
+ ULONG ulSysInfo[QSV_MAX];
+ DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX);
+
+ memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong);
+ n += sizeofULong;
+
+ if( sizeofULong <= nBuf - n ){
+ memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong);
+ n += sizeofULong;
+ }
+ if( sizeofULong <= nBuf - n ){
+ memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong);
+ n += sizeofULong;
+ }
+ if( sizeofULong <= nBuf - n ){
+ memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong);
+ n += sizeofULong;
+ }
+ if( sizeofULong <= nBuf - n ){
+ memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong);
+ n += sizeofULong;
+ }
+ }
+
+ return n;
+}
+
+/*
+** Sleep for a little while. Return the amount of time slept.
+** The argument is the number of microseconds we want to sleep.
+** The return value is the number of microseconds of sleep actually
+** requested from the underlying operating system, a number which
+** might be greater than or equal to the argument, but not less
+** than the argument.
+*/
+static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){
+ DosSleep( (microsec/1000) );
+ return microsec;
+}
+
+/*
+** The following variable, if set to a non-zero value, becomes the result
+** returned from sqlite3OsCurrentTime(). This is used for testing.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_current_time = 0;
+#endif
+
+/*
+** Find the current time (in Universal Coordinated Time). Write the
+** current time and date as a Julian Day number into *prNow and
+** return 0. Return 1 if the time and date cannot be found.
+*/
+int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
+ double now;
+ SHORT minute; /* needs to be able to cope with negative timezone offset */
+ USHORT second, hour,
+ day, month, year;
+ DATETIME dt;
+ DosGetDateTime( &dt );
+ second = (USHORT)dt.seconds;
+ minute = (SHORT)dt.minutes + dt.timezone;
+ hour = (USHORT)dt.hours;
+ day = (USHORT)dt.day;
+ month = (USHORT)dt.month;
+ year = (USHORT)dt.year;
+
+ /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
+ http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */
+ /* Calculate the Julian days */
+ now = day - 32076 +
+ 1461*(year + 4800 + (month - 14)/12)/4 +
+ 367*(month - 2 - (month - 14)/12*12)/12 -
+ 3*((year + 4900 + (month - 14)/12)/100)/4;
+
+ /* Add the fractional hours, mins and seconds */
+ now += (hour + 12.0)/24.0;
+ now += minute/1440.0;
+ now += second/86400.0;
+ *prNow = now;
+#ifdef SQLITE_TEST
+ if( sqlite3_current_time ){
+ *prNow = sqlite3_current_time/86400.0 + 2440587.5;
+ }
+#endif
+ return 0;
+}
+
+static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ return 0;
+}
+
+/*
+** Initialize and deinitialize the operating system interface.
+*/
+int sqlite3_os_init(void){
+ static sqlite3_vfs os2Vfs = {
+ 1, /* iVersion */
+ sizeof(os2File), /* szOsFile */
+ CCHMAXPATH, /* mxPathname */
+ 0, /* pNext */
+ "os2", /* zName */
+ 0, /* pAppData */
+
+ os2Open, /* xOpen */
+ os2Delete, /* xDelete */
+ os2Access, /* xAccess */
+ os2FullPathname, /* xFullPathname */
+ os2DlOpen, /* xDlOpen */
+ os2DlError, /* xDlError */
+ os2DlSym, /* xDlSym */
+ os2DlClose, /* xDlClose */
+ os2Randomness, /* xRandomness */
+ os2Sleep, /* xSleep */
+ os2CurrentTime, /* xCurrentTime */
+ os2GetLastError /* xGetLastError */
+ };
+ sqlite3_vfs_register(&os2Vfs, 1);
+ initUconvObjects();
+ return SQLITE_OK;
+}
+int sqlite3_os_end(void){
+ freeUconvObjects();
+ return SQLITE_OK;
+}
+
+#endif /* SQLITE_OS_OS2 */
diff --git a/third_party/sqlite/src/os_symbian.cc b/third_party/sqlite/src/os_symbian.cc
new file mode 100755
index 0000000..5b86338
--- /dev/null
+++ b/third_party/sqlite/src/os_symbian.cc
@@ -0,0 +1,579 @@
+// Copyright 2008, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains code that is specific to Symbian.
+// Differently from the rest of SQLite, it is implemented in C++ as this is
+// the native language of the OS and all interfaces we need to use are C++.
+//
+// This file follows the Gears code style guidelines.
+
+#ifdef OS_SYMBIAN
+#include <coemain.h>
+#include <e32math.h>
+#include <f32file.h>
+#include <utf.h>
+
+extern "C" {
+#include "sqliteInt.h"
+#include "os_common.h"
+}
+
+const TInt kFileLockAttempts = 3;
+
+// The global file system session.
+RFs g_fs_session;
+
+static TInt UTF8ToUTF16(const char *in, TDes *out16) {
+ assert(in);
+ TPtrC8 in_des(reinterpret_cast<const unsigned char*>(in));
+ return CnvUtfConverter::ConvertToUnicodeFromUtf8(*out16, in_des);
+}
+
+static TInt UTF16ToUTF8(const TDesC16& in16, TDes8 *out8) {
+ return CnvUtfConverter::ConvertFromUnicodeToUtf8(*out8, in16);
+}
+
+// The SymbianFile structure is a subclass of sqlite3_file* specific to the
+// Symbian portability layer.
+struct SymbianFile {
+ const sqlite3_io_methods *methods;
+ RFile handle; // The file handle
+ TUint8 lock_type; // Type of lock currently held on this file
+ TUint16 shared_lock_byte; // Randomly chosen byte used as a shared lock
+};
+
+static SymbianFile* ConvertToSymbianFile(sqlite3_file* const id) {
+ assert(id);
+ return reinterpret_cast<SymbianFile*>(id);
+}
+
+static int SymbianClose(sqlite3_file *id) {
+ SymbianFile *file_id = ConvertToSymbianFile(id);
+ file_id->handle.Close();
+ OpenCounter(-1);
+ return SQLITE_OK;
+}
+
+static int SymbianRead(sqlite3_file *id,
+ void *buffer,
+ int amount,
+ sqlite3_int64 offset) {
+ assert(buffer);
+ assert(amount >=0);
+ assert(offset >=0);
+
+ SymbianFile* file_id = ConvertToSymbianFile(id);
+ TPtr8 dest(static_cast<unsigned char*>(buffer), amount);
+
+ if (KErrNone == file_id->handle.Read(offset, dest, amount)) {
+ if (dest.Length() == amount) {
+ return SQLITE_OK;
+ } else {
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ } else {
+ return SQLITE_IOERR;
+ }
+}
+
+static int SymbianWrite(sqlite3_file *id,
+ const void *buffer,
+ int amount,
+ sqlite3_int64 offset) {
+ assert(buffer);
+ assert(amount >=0);
+ assert(offset >=0);
+
+ SymbianFile *file_id = ConvertToSymbianFile(id);
+ TPtrC8 src(static_cast<const unsigned char*>(buffer), amount);
+ if (file_id->handle.Write(offset, src) != KErrNone) {
+ return SQLITE_IOERR_WRITE;
+ }
+
+ return SQLITE_OK;
+}
+
+static int SymbianTruncate(sqlite3_file *id, sqlite3_int64 bytes) {
+ assert(bytes >=0);
+
+ SymbianFile *file_id = ConvertToSymbianFile(id);
+ if (file_id->handle.SetSize(bytes) != KErrNone) {
+ return SQLITE_IOERR;
+ }
+ return SQLITE_OK;
+}
+
+static int SymbianSync(sqlite3_file *id, int /*flags*/) {
+ SymbianFile *file_id = ConvertToSymbianFile(id);
+ if (file_id->handle.Flush() != KErrNone) {
+ return SQLITE_IOERR;
+ } else {
+ return SQLITE_OK;
+ }
+}
+
+static int SymbianFileSize(sqlite3_file *id, sqlite3_int64 *size) {
+ assert(size);
+
+ SymbianFile *file_id = ConvertToSymbianFile(id);
+ TInt size_tmp;
+ if (file_id->handle.Size(size_tmp) != KErrNone) {
+ return SQLITE_IOERR;
+ }
+ *size = size_tmp;
+ return SQLITE_OK;
+}
+
+// File lock/unlock functions; see os_win.c for a description
+// of the algorithm used.
+static int GetReadLock(SymbianFile *file) {
+ file->shared_lock_byte = Math::Random() % (SHARED_SIZE - 1);
+ return file->handle.Lock(SHARED_FIRST + file->shared_lock_byte, 1);
+}
+
+static int UnlockReadLock(SymbianFile *file) {
+ return file->handle.UnLock(SHARED_FIRST + file->shared_lock_byte, 1);
+}
+
+static int SymbianLock(sqlite3_file *id, int lock_type) {
+ SymbianFile *file = ConvertToSymbianFile(id);
+ if (file->lock_type >= lock_type) {
+ return SQLITE_OK;
+ }
+
+ // Make sure the locking sequence is correct
+ assert(file->lock_type != NO_LOCK || lock_type == SHARED_LOCK);
+ assert(lock_type != PENDING_LOCK);
+ assert(lock_type != RESERVED_LOCK || file->lock_type == SHARED_LOCK);
+
+ // Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
+ // a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
+ // the PENDING_LOCK byte is temporary.
+ int new_lock_type = file->lock_type;
+ int got_pending_lock = 0;
+ int res = KErrNone;
+ if (file->lock_type == NO_LOCK ||
+ (lock_type == EXCLUSIVE_LOCK && file->lock_type == RESERVED_LOCK)) {
+ int count = kFileLockAttempts;
+ while (count-- > 0 &&
+ (res = file->handle.Lock(PENDING_BYTE, 1)) != KErrNone ) {
+ // Try 3 times to get the pending lock. The pending lock might be
+ // held by another reader process who will release it momentarily.
+ OSTRACE2("could not get a PENDING lock. cnt=%d\n", cnt);
+ User::After(1000);
+ }
+ got_pending_lock = (res == KErrNone? 1 : 0);
+ }
+
+ // Acquire a shared lock
+ if (lock_type == SHARED_LOCK && res == KErrNone) {
+ assert(file->lock_type == NO_LOCK);
+ res = GetReadLock(file);
+ if (res == KErrNone) {
+ new_lock_type = SHARED_LOCK;
+ }
+ }
+
+ // Acquire a RESERVED lock
+ if (lock_type == RESERVED_LOCK && res == KErrNone) {
+ assert(file->lock_type == SHARED_LOCK);
+ res = file->handle.Lock(RESERVED_BYTE, 1);
+ if (res == KErrNone) {
+ new_lock_type = RESERVED_LOCK;
+ }
+ }
+
+ // Acquire a PENDING lock
+ if (lock_type == EXCLUSIVE_LOCK && res == KErrNone) {
+ new_lock_type = PENDING_LOCK;
+ got_pending_lock = 0;
+ }
+
+ // Acquire an EXCLUSIVE lock
+ if (lock_type == EXCLUSIVE_LOCK && res == KErrNone) {
+ assert(file->lock_type >= SHARED_LOCK);
+ res = UnlockReadLock(file);
+ OSTRACE2("unreadlock = %d\n", res);
+ res = file->handle.Lock(SHARED_FIRST, SHARED_SIZE);
+ if (res == KErrNone) {
+ new_lock_type = EXCLUSIVE_LOCK;
+ } else {
+ OSTRACE2("error-code = %d\n", GetLastError());
+ GetReadLock(file);
+ }
+ }
+
+ // If we are holding a PENDING lock that ought to be released, then
+ // release it now.
+ if (got_pending_lock && lock_type == SHARED_LOCK) {
+ file->handle.UnLock(PENDING_BYTE, 1);
+ }
+
+ // Update the state of the lock held in the file descriptor, then
+ // return the appropriate result code.
+ file->lock_type = new_lock_type;
+ if (res == KErrNone) {
+ return SQLITE_OK;
+ } else {
+ OSTRACE4("LOCK FAILED %d trying for %d but got %d\n", file->handle,
+ lock_type, new_lock_type);
+ return SQLITE_BUSY;
+ }
+}
+
+static int SymbianUnlock(sqlite3_file *id, int lock_type) {
+ int type;
+ int rc = SQLITE_OK;
+ SymbianFile *file = ConvertToSymbianFile(id);
+ assert(lock_type <= SHARED_LOCK);
+ OSTRACE5("UNLOCK %d to %d was %d(%d)\n", file->handle, lock_type,
+ file->lock_type, file->shared_lock_byte);
+ type = file->lock_type;
+ if (type >= EXCLUSIVE_LOCK) {
+ file->handle.UnLock(SHARED_FIRST, SHARED_SIZE);
+ if (lock_type == SHARED_LOCK && GetReadLock(file) != KErrNone) {
+ // This should never happen. We should always be able to
+ // reacquire the read lock
+ rc = SQLITE_IOERR_UNLOCK;
+ }
+ }
+ if (type >= RESERVED_LOCK) {
+ file->handle.UnLock(RESERVED_BYTE, 1);
+ }
+ if (lock_type == NO_LOCK && type >= SHARED_LOCK) {
+ UnlockReadLock(file);
+ }
+ if (type >= PENDING_LOCK) {
+ file->handle.UnLock(PENDING_BYTE, 1);
+ }
+ file->lock_type = lock_type;
+ return rc;
+}
+
+static int SymbianCheckReservedLock(sqlite3_file *id, int *result) {
+ int rc;
+ SymbianFile *file = ConvertToSymbianFile(id);
+ if (file->lock_type >= RESERVED_LOCK) {
+ rc = 1;
+ OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc);
+ } else {
+ rc = file->handle.Lock(RESERVED_BYTE, 1);
+ if (rc == KErrNone) {
+ file->handle.UnLock(RESERVED_BYTE, 1);
+ }
+ rc = !rc;
+ OSTRACE3("TEST WR-LOCK %d %d (remote)\n", file->handle, rc);
+ }
+ *result = rc;
+ return SQLITE_OK;
+}
+
+static int SymbianFileControl(sqlite3_file */*id*/,
+ int /*op*/,
+ void */*arg*/) {
+ return SQLITE_OK;
+}
+
+static int SymbianSectorSize(sqlite3_file */*id*/) {
+ return SQLITE_DEFAULT_SECTOR_SIZE;
+}
+
+static int SymbianDeviceCharacteristics(sqlite3_file */*id*/) {
+ return 0;
+}
+
+/*
+** This vector defines all the methods that can operate on a
+** sqlite3_file for Symbian.
+*/
+static const sqlite3_io_methods SymbianIoMethod = {
+ 1, // iVersion
+ SymbianClose,
+ SymbianRead,
+ SymbianWrite,
+ SymbianTruncate,
+ SymbianSync,
+ SymbianFileSize,
+ SymbianLock,
+ SymbianUnlock,
+ SymbianCheckReservedLock,
+ SymbianFileControl,
+ SymbianSectorSize,
+ SymbianDeviceCharacteristics
+};
+
+// ============================================================================
+// vfs methods begin here
+// ============================================================================
+static int SymbianOpen(sqlite3_vfs */*vfs*/,
+ const char *name,
+ sqlite3_file *id,
+ int flags,
+ int *out_flags) {
+ TUint desired_access;
+ TUint share_mode;
+ TInt err = KErrNone;
+ TFileName name_utf16;
+ SymbianFile *file = ConvertToSymbianFile(id);
+
+ if (out_flags) {
+ *out_flags = flags;
+ }
+
+ // if the name is NULL we have to open a temporary file.
+ if (!name) {
+ TPath private_path;
+ TFileName file_name;
+ if (g_fs_session.PrivatePath(private_path) != KErrNone) {
+ return SQLITE_CANTOPEN;
+ }
+ if (file->handle.Temp(g_fs_session,
+ private_path,
+ file_name,
+ EFileWrite) !=
+ KErrNone) {
+ return SQLITE_CANTOPEN;
+ }
+ file->methods = &SymbianIoMethod;
+ file->lock_type = NO_LOCK;
+ file->shared_lock_byte = 0;
+ OpenCounter(+1);
+ return SQLITE_OK;
+ }
+
+ if (UTF8ToUTF16(name, &name_utf16) != KErrNone)
+ return SQLITE_CANTOPEN;
+
+ if (flags & SQLITE_OPEN_READWRITE) {
+ desired_access = EFileWrite;
+ } else {
+ desired_access = EFileRead;
+ }
+ if (flags & SQLITE_OPEN_MAIN_DB) {
+ share_mode = EFileShareReadersOrWriters;
+ } else {
+ share_mode = 0;
+ }
+
+ if (flags & SQLITE_OPEN_CREATE) {
+ err = file->handle.Create(g_fs_session,
+ name_utf16,
+ desired_access | share_mode);
+ if (err != KErrNone && err != KErrAlreadyExists) {
+ return SQLITE_CANTOPEN;
+ }
+ }
+
+ if (err != KErrNone) {
+ err = file->handle.Open(g_fs_session,
+ name_utf16,
+ desired_access | share_mode);
+ if (err != KErrNone && flags & SQLITE_OPEN_READWRITE) {
+ if (out_flags) {
+ *out_flags = (flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE;
+ }
+ desired_access = EFileRead;
+ err = file->handle.Open(g_fs_session,
+ name_utf16,
+ desired_access | share_mode);
+ }
+ if (err != KErrNone) {
+ return SQLITE_CANTOPEN;
+ }
+ }
+ file->methods = &SymbianIoMethod;
+ file->lock_type = NO_LOCK;
+ file->shared_lock_byte = 0;
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+static int SymbianDelete(sqlite3_vfs */*vfs*/,
+ const char *file_name,
+ int /*sync_dir*/) {
+ assert(file_name);
+ TFileName file_name_utf16;
+
+ if (UTF8ToUTF16(file_name, &file_name_utf16) != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ TInt result = g_fs_session.Delete(file_name_utf16);
+ return (result == KErrNone || result == KErrPathNotFound)?
+ SQLITE_OK : SQLITE_IOERR_DELETE;
+}
+
+static int SymbianAccess(sqlite3_vfs */*vfs*/,
+ const char *file_name,
+ int flags,
+ int *result) {
+ assert(file_name);
+ TEntry entry;
+ TFileName file_name_utf16;
+
+ if (UTF8ToUTF16(file_name, &file_name_utf16) != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ if (g_fs_session.Entry(file_name_utf16, entry) != KErrNone) {
+ *result = 0;
+ return SQLITE_OK;
+ }
+
+ switch (flags) {
+ case SQLITE_ACCESS_READ:
+ case SQLITE_ACCESS_EXISTS:
+ *result = !entry.IsDir();
+ break;
+ case SQLITE_ACCESS_READWRITE:
+ *result = !entry.IsDir() && !entry.IsReadOnly();
+ break;
+ default:
+ return SQLITE_ERROR;
+ }
+
+ return SQLITE_OK;
+}
+
+static int SymbianFullPathname(sqlite3_vfs */*vfs*/,
+ const char *relative,
+ int full_len,
+ char *full) {
+ assert(relative);
+ assert(full);
+
+ TParse parse;
+ TPath relative_utf16;
+ TPath base_path;
+ TPtr8 full_utf8(reinterpret_cast<unsigned char*>(full), full_len);
+
+ g_fs_session.PrivatePath(base_path);
+
+ if (UTF8ToUTF16(relative, &relative_utf16) != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ if (parse.Set(relative_utf16, &base_path, NULL) != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ TDesC full_utf16(parse.FullName());
+ if (UTF16ToUTF8(relative_utf16, &full_utf8) != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ full_utf8.PtrZ();
+ return SQLITE_OK;
+}
+
+static int SymbianRandomness(sqlite3_vfs */*vfs*/, int buf_len, char *buffer) {
+ assert(buffer);
+ TInt64 seed = User::TickCount();
+ for (TInt i = 0; i < buf_len; i++) {
+ buffer[i] = Math::Rand(seed) % 255;
+ }
+ return SQLITE_OK;
+}
+
+static int SymbianSleep(sqlite3_vfs */*vfs*/, int microsec) {
+ User::After(microsec);
+ return SQLITE_OK;
+}
+
+int SymbianCurrentTime(sqlite3_vfs */*vfs*/, double *now) {
+ _LIT(kEpoch, "19700101:000000.000000");
+ assert(now);
+ TTime time;
+ TTime epoch_time(kEpoch);
+ TTimeIntervalSeconds interval;
+
+ time.HomeTime();
+ // calculate seconds elapsed since 1-1-1970
+ time.SecondsFrom(epoch_time, interval);
+
+ // Julian date @ 1-1-1970 = 2440587.5
+ // seconds per day = 86400.0
+ *now = interval.Int()/86400.0 + 2440587.5;
+ return SQLITE_OK;
+}
+
+static int SymbianGetLastError(sqlite3_vfs */*vfs*/,
+ int /*buf_len*/,
+ char */*buf*/) {
+ assert(buf[0] == '\0');
+ return 0;
+}
+
+// Interfaces for opening a shared library, finding entry points
+// within the shared library, and closing the shared library.
+// TODO(marcogelmi): implement.
+#define SymbianDlOpen 0
+#define SymbianDlError 0
+#define SymbianDlSym 0
+#define SymbianDlClose 0
+
+// Initialize and deinitialize the operating system interface.
+int sqlite3_os_init(void) {
+ static sqlite3_vfs symbian_vfs = {
+ 1, // iVersion
+ sizeof(SymbianFile), // szOsFile
+ KMaxPath, // mxPathname
+ 0, // pNext
+ "symbian", // name
+ 0, // pAppData
+
+ SymbianOpen, // xOpen
+ SymbianDelete, // xDelete
+ SymbianAccess, // xAccess
+ SymbianFullPathname, // xFullPathname
+ SymbianDlOpen, // xDlOpen
+ SymbianDlError, // xDlError
+ SymbianDlSym, // xDlSym
+ SymbianDlClose, // xDlClose
+ SymbianRandomness, // xRandomness
+ SymbianSleep, // xSleep
+ SymbianCurrentTime, // xCurrentTime
+ SymbianGetLastError // xGetLastError
+ };
+
+ if (g_fs_session.Connect() != KErrNone) {
+ return SQLITE_ERROR;
+ }
+
+ if (g_fs_session.ShareAuto() != KErrNone) {
+ g_fs_session.Close();
+ return SQLITE_ERROR;
+ }
+
+ sqlite3_vfs_register(&symbian_vfs, 1);
+ return SQLITE_OK;
+}
+
+int sqlite3_os_end(void) {
+ g_fs_session.Close();
+ return SQLITE_OK;
+}
+
+#endif /* OS_SYMBIAN*/
diff --git a/third_party/sqlite/os_unix.c b/third_party/sqlite/src/os_unix.c
index 9141891..4874a64 100644..100755
--- a/third_party/sqlite/os_unix.c
+++ b/third_party/sqlite/src/os_unix.c
@@ -11,11 +11,22 @@
******************************************************************************
**
** This file contains code that is specific to Unix systems.
+**
+** $Id: os_unix.c,v 1.195 2008/07/30 17:28:04 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
-#if OS_UNIX /* This file is used on unix only */
+#if SQLITE_OS_UNIX /* This file is used on unix only */
+/*
+** If SQLITE_ENABLE_LOCKING_STYLE is defined, then several different
+** locking implementations are provided:
+**
+** * POSIX locking (the default),
+** * No locking,
+** * Dot-file locking,
+** * flock() locking,
+** * AFP locking (OSX only).
+*/
/* #define SQLITE_ENABLE_LOCKING_STYLE 0 */
/*
@@ -49,6 +60,7 @@
#include <time.h>
#include <sys/time.h>
#include <errno.h>
+
#ifdef SQLITE_ENABLE_LOCKING_STYLE
#include <sys/ioctl.h>
#include <sys/param.h>
@@ -59,10 +71,7 @@
** If we are to be thread-safe, include the pthreads header and define
** the SQLITE_UNIX_THREADS macro.
*/
-#ifndef THREADSAFE
-# define THREADSAFE 1
-#endif
-#if THREADSAFE
+#if SQLITE_THREADSAFE
# include <pthread.h>
# define SQLITE_UNIX_THREADS 1
#endif
@@ -74,62 +83,44 @@
# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644
#endif
+/*
+** Maximum supported path-length.
+*/
+#define MAX_PATHNAME 512
/*
-** The unixFile structure is subclass of OsFile specific for the unix
+** The unixFile structure is subclass of sqlite3_file specific for the unix
** protability layer.
*/
typedef struct unixFile unixFile;
struct unixFile {
- IoMethod const *pMethod; /* Always the first entry */
+ sqlite3_io_methods const *pMethod; /* Always the first entry */
+#ifdef SQLITE_TEST
+ /* In test mode, increase the size of this structure a bit so that
+ ** it is larger than the struct CrashFile defined in test6.c.
+ */
+ char aPadding[32];
+#endif
struct openCnt *pOpen; /* Info about all open fd's on this inode */
struct lockInfo *pLock; /* Info about locks on this inode */
#ifdef SQLITE_ENABLE_LOCKING_STYLE
void *lockingContext; /* Locking style specific state */
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+#endif
int h; /* The file descriptor */
unsigned char locktype; /* The type of lock held on this fd */
- unsigned char isOpen; /* True if needs to be closed */
- unsigned char fullSync; /* Use F_FULLSYNC if available */
int dirfd; /* File descriptor for the directory */
- i64 offset; /* Seek offset */
-#ifdef SQLITE_UNIX_THREADS
- pthread_t tid; /* The thread that "owns" this OsFile */
+#if SQLITE_THREADSAFE
+ pthread_t tid; /* The thread that "owns" this unixFile */
#endif
};
/*
-** Provide the ability to override some OS-layer functions during
-** testing. This is used to simulate OS crashes to verify that
-** commits are atomic even in the event of an OS crash.
-*/
-#ifdef SQLITE_CRASH_TEST
- extern int sqlite3CrashTestEnable;
- extern int sqlite3CrashOpenReadWrite(const char*, OsFile**, int*);
- extern int sqlite3CrashOpenExclusive(const char*, OsFile**, int);
- extern int sqlite3CrashOpenReadOnly(const char*, OsFile**, int);
-# define CRASH_TEST_OVERRIDE(X,A,B,C) \
- if(sqlite3CrashTestEnable){ return X(A,B,C); }
-#else
-# define CRASH_TEST_OVERRIDE(X,A,B,C) /* no-op */
-#endif
-
-
-/*
** Include code that is common to all os_*.c files
*/
#include "os_common.h"
/*
-** Do not include any of the File I/O interface procedures if the
-** SQLITE_OMIT_DISKIO macro is defined (indicating that the database
-** will be in-memory only)
-*/
-#ifndef SQLITE_OMIT_DISKIO
-
-
-/*
** Define various macros that are missing from some systems.
*/
#ifndef O_LARGEFILE
@@ -150,7 +141,7 @@ struct unixFile {
** The DJGPP compiler environment looks mostly like Unix, but it
** lacks the fcntl() system call. So redefine fcntl() to be something
** that always succeeds. This means that locking does not occur under
-** DJGPP. But it's DOS - what did you expect?
+** DJGPP. But it is DOS - what did you expect?
*/
#ifdef __DJGPP__
# define fcntl(A,B,C) 0
@@ -160,29 +151,29 @@ struct unixFile {
** The threadid macro resolves to the thread-id or to 0. Used for
** testing and debugging only.
*/
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
#define threadid pthread_self()
#else
#define threadid 0
#endif
/*
-** Set or check the OsFile.tid field. This field is set when an OsFile
-** is first opened. All subsequent uses of the OsFile verify that the
-** same thread is operating on the OsFile. Some operating systems do
+** Set or check the unixFile.tid field. This field is set when an unixFile
+** is first opened. All subsequent uses of the unixFile verify that the
+** same thread is operating on the unixFile. Some operating systems do
** not allow locks to be overridden by other threads and that restriction
** means that sqlite3* database handles cannot be moved from one thread
** to another. This logic makes sure a user does not try to do that
** by mistake.
**
-** Version 3.3.1 (2006-01-15): OsFiles can be moved from one thread to
+** Version 3.3.1 (2006-01-15): unixFile can be moved from one thread to
** another as long as we are running on a system that supports threads
** overriding each others locks (which now the most common behavior)
-** or if no locks are held. But the OsFile.pLock field needs to be
+** or if no locks are held. But the unixFile.pLock field needs to be
** recomputed because its key includes the thread-id. See the
** transferOwnership() function below for additional information
*/
-#if defined(SQLITE_UNIX_THREADS)
+#if SQLITE_THREADSAFE
# define SET_THREADID(X) (X)->tid = pthread_self()
# define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \
!pthread_equal((X)->tid, pthread_self()))
@@ -198,8 +189,6 @@ struct unixFile {
** by the same process. It does not explicitly say so, but this implies
** that it overrides locks set by the same process using a different
** file descriptor. Consider this test case:
-**
-** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
**
** Suppose ./file1 and ./file2 are really the same file (because
@@ -226,11 +215,11 @@ struct unixFile {
** locks to see if another thread has previously set a lock on that same
** inode.
**
-** The OsFile structure for POSIX is no longer just an integer file
+** The sqlite3_file structure for POSIX is no longer just an integer file
** descriptor. It is now a structure that holds the integer file
** descriptor and a pointer to a structure that describes the internal
** locks on the corresponding inode. There is one locking structure
-** per inode, so if the same inode is opened twice, both OsFile structures
+** per inode, so if the same inode is opened twice, both unixFile structures
** point to the same locking structure. The locking structure keeps
** a reference count (so we will know when to delete it) and a "cnt"
** field that tells us its internal lock status. cnt==0 means the
@@ -249,11 +238,11 @@ struct unixFile {
**
** If you close a file descriptor that points to a file that has locks,
** all locks on that file that are owned by the current process are
-** released. To work around this problem, each OsFile structure contains
+** released. To work around this problem, each unixFile structure contains
** a pointer to an openCnt structure. There is one openCnt structure
-** per open inode, which means that multiple OsFiles can point to a single
-** openCnt. When an attempt is made to close an OsFile, if there are
-** other OsFiles open on the same inode that are holding locks, the call
+** per open inode, which means that multiple unixFile can point to a single
+** openCnt. When an attempt is made to close an unixFile, if there are
+** other unixFile open on the same inode that are holding locks, the call
** to close() the file descriptor is deferred until all of the locks clear.
** The openCnt structure keeps a list of file descriptors that need to
** be closed and that list is walked (and cleared) when the last lock
@@ -303,7 +292,7 @@ struct unixFile {
struct lockKey {
dev_t dev; /* Device number */
ino_t ino; /* Inode number */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
pthread_t tid; /* Thread ID or zero if threads can override each other */
#endif
};
@@ -313,15 +302,16 @@ struct lockKey {
** inode on each thread with a different process ID. (Threads have
** different process IDs on linux, but not on most other unixes.)
**
-** A single inode can have multiple file descriptors, so each OsFile
+** A single inode can have multiple file descriptors, so each unixFile
** structure contains a pointer to an instance of this object and this
-** object keeps a count of the number of OsFiles pointing to it.
+** object keeps a count of the number of unixFile pointing to it.
*/
struct lockInfo {
struct lockKey key; /* The lookup key */
int cnt; /* Number of SHARED locks held */
int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
int nRef; /* Number of pointers to this structure */
+ struct lockInfo *pNext, *pPrev; /* List of all lockInfo objects */
};
/*
@@ -347,25 +337,24 @@ struct openCnt {
int nLock; /* Number of outstanding locks */
int nPending; /* Number of pending close() operations */
int *aPending; /* Malloced space holding fd's awaiting a close() */
+ struct openCnt *pNext, *pPrev; /* List of all openCnt objects */
};
-/*
-** These hash tables map inodes and file descriptors (really, lockKey and
-** openKey structures) into lockInfo and openCnt structures. Access to
-** these hash tables must be protected by a mutex.
+/*
+** List of all lockInfo and openCnt objects. This used to be a hash
+** table. But the number of objects is rarely more than a dozen and
+** never exceeds a few thousand. And lookup is not on a critical
+** path oo a simple linked list will suffice.
*/
-static Hash lockHash = {SQLITE_HASH_BINARY, 0, 0, 0,
- sqlite3ThreadSafeMalloc, sqlite3ThreadSafeFree, 0, 0};
-static Hash openHash = {SQLITE_HASH_BINARY, 0, 0, 0,
- sqlite3ThreadSafeMalloc, sqlite3ThreadSafeFree, 0, 0};
+static struct lockInfo *lockList = 0;
+static struct openCnt *openList = 0;
-#ifdef SQLITE_ENABLE_LOCKING_STYLE
/*
** The locking styles are associated with the different file locking
** capabilities supported by different file systems.
**
** POSIX locking style fully supports shared and exclusive byte-range locks
-** ADP locking only supports exclusive byte-range locks
+** AFP locking only supports exclusive byte-range locks
** FLOCK only supports a single file-global exclusive lock
** DOTLOCK isn't a true locking style, it refers to the use of a special
** file named the same as the database file with a '.lock' extension, this
@@ -375,17 +364,23 @@ static Hash openHash = {SQLITE_HASH_BINARY, 0, 0, 0,
** UNSUPPORTED means that no locking will be attempted, this is only used for
** file systems that are known to be unsupported
*/
-typedef enum {
- posixLockingStyle = 0, /* standard posix-advisory locks */
- afpLockingStyle, /* use afp locks */
- flockLockingStyle, /* use flock() */
- dotlockLockingStyle, /* use <file>.lock files */
- noLockingStyle, /* useful for read-only file system */
- unsupportedLockingStyle /* indicates unsupported file system */
-} sqlite3LockingStyle;
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+#define LOCKING_STYLE_POSIX 1
+#define LOCKING_STYLE_NONE 2
+#define LOCKING_STYLE_DOTFILE 3
+#define LOCKING_STYLE_FLOCK 4
+#define LOCKING_STYLE_AFP 5
-#ifdef SQLITE_UNIX_THREADS
+/*
+** Helper functions to obtain and relinquish the global mutex.
+*/
+static void enterMutex(){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+}
+static void leaveMutex(){
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+}
+
+#if SQLITE_THREADSAFE
/*
** This variable records whether or not threads can override each others
** locks.
@@ -522,19 +517,28 @@ static void testThreadLockingBehavior(int fd_orig){
close(fd);
threadsOverrideEachOthersLocks = d[0].result==0 && d[1].result==0;
}
-#endif /* SQLITE_UNIX_THREADS */
+#endif /* SQLITE_THREADSAFE */
/*
** Release a lockInfo structure previously allocated by findLockInfo().
*/
static void releaseLockInfo(struct lockInfo *pLock){
- assert( sqlite3OsInMutex(1) );
- if (pLock == NULL)
- return;
- pLock->nRef--;
- if( pLock->nRef==0 ){
- sqlite3HashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0);
- sqlite3ThreadSafeFree(pLock);
+ if( pLock ){
+ pLock->nRef--;
+ if( pLock->nRef==0 ){
+ if( pLock->pPrev ){
+ assert( pLock->pPrev->pNext==pLock );
+ pLock->pPrev->pNext = pLock->pNext;
+ }else{
+ assert( lockList==pLock );
+ lockList = pLock->pNext;
+ }
+ if( pLock->pNext ){
+ assert( pLock->pNext->pPrev==pLock );
+ pLock->pNext->pPrev = pLock->pPrev;
+ }
+ sqlite3_free(pLock);
+ }
}
}
@@ -542,14 +546,23 @@ static void releaseLockInfo(struct lockInfo *pLock){
** Release a openCnt structure previously allocated by findLockInfo().
*/
static void releaseOpenCnt(struct openCnt *pOpen){
- assert( sqlite3OsInMutex(1) );
- if (pOpen == NULL)
- return;
- pOpen->nRef--;
- if( pOpen->nRef==0 ){
- sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0);
- free(pOpen->aPending);
- sqlite3ThreadSafeFree(pOpen);
+ if( pOpen ){
+ pOpen->nRef--;
+ if( pOpen->nRef==0 ){
+ if( pOpen->pPrev ){
+ assert( pOpen->pPrev->pNext==pOpen );
+ pOpen->pPrev->pNext = pOpen->pNext;
+ }else{
+ assert( openList==pOpen );
+ openList = pOpen->pNext;
+ }
+ if( pOpen->pNext ){
+ assert( pOpen->pNext->pPrev==pOpen );
+ pOpen->pNext->pPrev = pOpen->pPrev;
+ }
+ sqlite3_free(pOpen->aPending);
+ sqlite3_free(pOpen);
+ }
}
}
@@ -558,78 +571,89 @@ static void releaseOpenCnt(struct openCnt *pOpen){
** Tests a byte-range locking query to see if byte range locks are
** supported, if not we fall back to dotlockLockingStyle.
*/
-static sqlite3LockingStyle sqlite3TestLockingStyle(const char *filePath,
- int fd) {
- /* test byte-range lock using fcntl */
+static int testLockingStyle(int fd){
struct flock lockInfo;
-
+
+ /* Test byte-range lock using fcntl(). If the call succeeds,
+ ** assume that the file-system supports POSIX style locks.
+ */
lockInfo.l_len = 1;
lockInfo.l_start = 0;
lockInfo.l_whence = SEEK_SET;
lockInfo.l_type = F_RDLCK;
+ if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) {
+ return LOCKING_STYLE_POSIX;
+ }
- if (fcntl(fd, F_GETLK, &lockInfo) != -1) {
- return posixLockingStyle;
- }
-
- /* testing for flock can give false positives. So if if the above test
- ** fails, then we fall back to using dot-lock style locking.
+ /* Testing for flock() can give false positives. So if if the above
+ ** test fails, then we fall back to using dot-file style locking.
*/
- return dotlockLockingStyle;
+ return LOCKING_STYLE_DOTFILE;
}
+#endif
/*
-** Examines the f_fstypename entry in the statfs structure as returned by
-** stat() for the file system hosting the database file, assigns the
-** appropriate locking style based on it's value. These values and
-** assignments are based on Darwin/OSX behavior and have not been tested on
+** If SQLITE_ENABLE_LOCKING_STYLE is defined, this function Examines the
+** f_fstypename entry in the statfs structure as returned by stat() for
+** the file system hosting the database file and selects the appropriate
+** locking style based on its value. These values and assignments are
+** based on Darwin/OSX behavior and have not been thoroughly tested on
** other systems.
+**
+** If SQLITE_ENABLE_LOCKING_STYLE is not defined, this function always
+** returns LOCKING_STYLE_POSIX.
*/
-static sqlite3LockingStyle sqlite3DetectLockingStyle(const char *filePath,
- int fd) {
-
-#ifdef SQLITE_FIXED_LOCKING_STYLE
- return (sqlite3LockingStyle)SQLITE_FIXED_LOCKING_STYLE;
-#else
+static int detectLockingStyle(
+ sqlite3_vfs *pVfs,
+ const char *filePath,
+ int fd
+){
+#ifdef SQLITE_ENABLE_LOCKING_STYLE
+ struct Mapping {
+ const char *zFilesystem;
+ int eLockingStyle;
+ } aMap[] = {
+ { "hfs", LOCKING_STYLE_POSIX },
+ { "ufs", LOCKING_STYLE_POSIX },
+ { "afpfs", LOCKING_STYLE_AFP },
+ { "smbfs", LOCKING_STYLE_FLOCK },
+ { "msdos", LOCKING_STYLE_DOTFILE },
+ { "webdav", LOCKING_STYLE_NONE },
+ { 0, 0 }
+ };
+ int i;
struct statfs fsInfo;
- if (statfs(filePath, &fsInfo) == -1)
- return sqlite3TestLockingStyle(filePath, fd);
-
- if (fsInfo.f_flags & MNT_RDONLY)
- return noLockingStyle;
-
- if( (!strcmp(fsInfo.f_fstypename, "hfs")) ||
- (!strcmp(fsInfo.f_fstypename, "ufs")) )
- return posixLockingStyle;
-
- if(!strcmp(fsInfo.f_fstypename, "afpfs"))
- return afpLockingStyle;
-
- if(!strcmp(fsInfo.f_fstypename, "nfs"))
- return sqlite3TestLockingStyle(filePath, fd);
-
- if(!strcmp(fsInfo.f_fstypename, "smbfs"))
- return flockLockingStyle;
-
- if(!strcmp(fsInfo.f_fstypename, "msdos"))
- return dotlockLockingStyle;
-
- if(!strcmp(fsInfo.f_fstypename, "webdav"))
- return unsupportedLockingStyle;
-
- return sqlite3TestLockingStyle(filePath, fd);
-#endif /* SQLITE_FIXED_LOCKING_STYLE */
-}
+ if( !filePath ){
+ return LOCKING_STYLE_NONE;
+ }
+ if( pVfs->pAppData ){
+ return (int)pVfs->pAppData;
+ }
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+ if( statfs(filePath, &fsInfo) != -1 ){
+ if( fsInfo.f_flags & MNT_RDONLY ){
+ return LOCKING_STYLE_NONE;
+ }
+ for(i=0; aMap[i].zFilesystem; i++){
+ if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){
+ return aMap[i].eLockingStyle;
+ }
+ }
+ }
+
+ /* Default case. Handles, amongst others, "nfs". */
+ return testLockingStyle(fd);
+#endif
+ return LOCKING_STYLE_POSIX;
+}
/*
** Given a file descriptor, locate lockInfo and openCnt structures that
** describes that file descriptor. Create new ones if necessary. The
** return values might be uninitialized if an error occurs.
**
-** Return the number of errors.
+** Return an appropriate error code.
*/
static int findLockInfo(
int fd, /* The file descriptor used in the key */
@@ -643,13 +667,35 @@ static int findLockInfo(
struct lockInfo *pLock;
struct openCnt *pOpen;
rc = fstat(fd, &statbuf);
- if( rc!=0 ) return 1;
+ if( rc!=0 ){
+#ifdef EOVERFLOW
+ if( errno==EOVERFLOW ) return SQLITE_NOLFS;
+#endif
+ return SQLITE_IOERR;
+ }
+
+ /* On OS X on an msdos filesystem, the inode number is reported
+ ** incorrectly for zero-size files. See ticket #3260. To work
+ ** around this problem (we consider it a bug in OS X, not SQLite)
+ ** we always increase the file size to 1 by writing a single byte
+ ** prior to accessing the inode number. The one byte written is
+ ** an ASCII 'S' character which also happens to be the first byte
+ ** in the header of every SQLite database. In this way, if there
+ ** is a race condition such that another thread has already populated
+ ** the first page of the database, no damage is done.
+ */
+ if( statbuf.st_size==0 ){
+ write(fd, "S", 1);
+ rc = fstat(fd, &statbuf);
+ if( rc!=0 ){
+ return SQLITE_IOERR;
+ }
+ }
- assert( sqlite3OsInMutex(1) );
memset(&key1, 0, sizeof(key1));
key1.dev = statbuf.st_dev;
key1.ino = statbuf.st_ino;
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
if( threadsOverrideEachOthersLocks<0 ){
testThreadLockingBehavior(fd);
}
@@ -658,37 +704,38 @@ static int findLockInfo(
memset(&key2, 0, sizeof(key2));
key2.dev = statbuf.st_dev;
key2.ino = statbuf.st_ino;
- pLock = (struct lockInfo*)sqlite3HashFind(&lockHash, &key1, sizeof(key1));
+ pLock = lockList;
+ while( pLock && memcmp(&key1, &pLock->key, sizeof(key1)) ){
+ pLock = pLock->pNext;
+ }
if( pLock==0 ){
- struct lockInfo *pOld;
- pLock = sqlite3ThreadSafeMalloc( sizeof(*pLock) );
+ pLock = sqlite3_malloc( sizeof(*pLock) );
if( pLock==0 ){
- rc = 1;
+ rc = SQLITE_NOMEM;
goto exit_findlockinfo;
}
pLock->key = key1;
pLock->nRef = 1;
pLock->cnt = 0;
pLock->locktype = 0;
- pOld = sqlite3HashInsert(&lockHash, &pLock->key, sizeof(key1), pLock);
- if( pOld!=0 ){
- assert( pOld==pLock );
- sqlite3ThreadSafeFree(pLock);
- rc = 1;
- goto exit_findlockinfo;
- }
+ pLock->pNext = lockList;
+ pLock->pPrev = 0;
+ if( lockList ) lockList->pPrev = pLock;
+ lockList = pLock;
}else{
pLock->nRef++;
}
*ppLock = pLock;
if( ppOpen!=0 ){
- pOpen = (struct openCnt*)sqlite3HashFind(&openHash, &key2, sizeof(key2));
+ pOpen = openList;
+ while( pOpen && memcmp(&key2, &pOpen->key, sizeof(key2)) ){
+ pOpen = pOpen->pNext;
+ }
if( pOpen==0 ){
- struct openCnt *pOld;
- pOpen = sqlite3ThreadSafeMalloc( sizeof(*pOpen) );
+ pOpen = sqlite3_malloc( sizeof(*pOpen) );
if( pOpen==0 ){
releaseLockInfo(pLock);
- rc = 1;
+ rc = SQLITE_NOMEM;
goto exit_findlockinfo;
}
pOpen->key = key2;
@@ -696,14 +743,10 @@ static int findLockInfo(
pOpen->nLock = 0;
pOpen->nPending = 0;
pOpen->aPending = 0;
- pOld = sqlite3HashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen);
- if( pOld!=0 ){
- assert( pOld==pOpen );
- sqlite3ThreadSafeFree(pOpen);
- releaseLockInfo(pLock);
- rc = 1;
- goto exit_findlockinfo;
- }
+ pOpen->pNext = openList;
+ pOpen->pPrev = 0;
+ if( openList ) openList->pPrev = pOpen;
+ openList = pOpen;
}else{
pOpen->nRef++;
}
@@ -745,7 +788,7 @@ static const char *locktypeName(int locktype){
** If the unixFile is locked and an ownership is wrong, then return
** SQLITE_MISUSE. SQLITE_OK is returned if everything works.
*/
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
static int transferOwnership(unixFile *pFile){
int rc;
pthread_t hSelf;
@@ -783,240 +826,35 @@ static int transferOwnership(unixFile *pFile){
#endif
/*
-** Delete the named file
-*/
-int sqlite3UnixDelete(const char *zFilename){
- SimulateIOError(return SQLITE_IOERR_DELETE);
- unlink(zFilename);
- return SQLITE_OK;
-}
-
-/*
-** Return TRUE if the named file exists.
-*/
-int sqlite3UnixFileExists(const char *zFilename){
- return access(zFilename, 0)==0;
-}
-
-/* Forward declaration */
-static int allocateUnixFile(
- int h, /* File descriptor of the open file */
- OsFile **pId, /* Write the real file descriptor here */
- const char *zFilename, /* Name of the file being opened */
- int delFlag /* If true, make sure the file deletes on close */
-);
-
-/*
-** Attempt to open a file for both reading and writing. If that
-** fails, try opening it read-only. If the file does not exist,
-** try to create it.
-**
-** On success, a handle for the open file is written to *id
-** and *pReadonly is set to 0 if the file was opened for reading and
-** writing or 1 if the file was opened read-only. The function returns
-** SQLITE_OK.
-**
-** On failure, the function returns SQLITE_CANTOPEN and leaves
-** *id and *pReadonly unchanged.
-*/
-int sqlite3UnixOpenReadWrite(
- const char *zFilename,
- OsFile **pId,
- int *pReadonly
-){
- int h;
-
- CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadWrite, zFilename, pId, pReadonly);
- assert( 0==*pId );
- h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY,
- SQLITE_DEFAULT_FILE_PERMISSIONS);
- if( h<0 ){
-#ifdef EISDIR
- if( errno==EISDIR ){
- return SQLITE_CANTOPEN;
- }
-#endif
- h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
- if( h<0 ){
- return SQLITE_CANTOPEN;
- }
- *pReadonly = 1;
- }else{
- *pReadonly = 0;
- }
- return allocateUnixFile(h, pId, zFilename, 0);
-}
-
-
-/*
-** Attempt to open a new file for exclusive access by this process.
-** The file will be opened for both reading and writing. To avoid
-** a potential security problem, we do not allow the file to have
-** previously existed. Nor do we allow the file to be a symbolic
-** link.
-**
-** If delFlag is true, then make arrangements to automatically delete
-** the file when it is closed.
-**
-** On success, write the file handle into *id and return SQLITE_OK.
-**
-** On failure, return SQLITE_CANTOPEN.
-*/
-int sqlite3UnixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
- int h;
-
- CRASH_TEST_OVERRIDE(sqlite3CrashOpenExclusive, zFilename, pId, delFlag);
- assert( 0==*pId );
- h = open(zFilename,
- O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY,
- delFlag ? 0600 : SQLITE_DEFAULT_FILE_PERMISSIONS);
- if( h<0 ){
- return SQLITE_CANTOPEN;
- }
- return allocateUnixFile(h, pId, zFilename, delFlag);
-}
-
-/*
-** Attempt to open a new file for read-only access.
-**
-** On success, write the file handle into *id and return SQLITE_OK.
-**
-** On failure, return SQLITE_CANTOPEN.
-*/
-int sqlite3UnixOpenReadOnly(const char *zFilename, OsFile **pId){
- int h;
-
- CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadOnly, zFilename, pId, 0);
- assert( 0==*pId );
- h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
- if( h<0 ){
- return SQLITE_CANTOPEN;
- }
- return allocateUnixFile(h, pId, zFilename, 0);
-}
-
-/*
-** Attempt to open a file descriptor for the directory that contains a
-** file. This file descriptor can be used to fsync() the directory
-** in order to make sure the creation of a new file is actually written
-** to disk.
-**
-** This routine is only meaningful for Unix. It is a no-op under
-** windows since windows does not support hard links.
-**
-** If FULL_FSYNC is enabled, this function is not longer useful,
-** a FULL_FSYNC sync applies to all pending disk operations.
-**
-** On success, a handle for a previously open file at *id is
-** updated with the new directory file descriptor and SQLITE_OK is
-** returned.
-**
-** On failure, the function returns SQLITE_CANTOPEN and leaves
-** *id unchanged.
-*/
-static int unixOpenDirectory(
- OsFile *id,
- const char *zDirname
-){
- int h;
- unixFile *pFile = (unixFile*)id;
- assert( pFile!=0 );
- SET_THREADID(pFile);
- assert( pFile->dirfd<0 );
- pFile->dirfd = h = open(zDirname, O_RDONLY|O_BINARY, 0);
- if( h<0 ){
- return SQLITE_CANTOPEN;
- }
-#ifdef FD_CLOEXEC
- fcntl(h, F_SETFD, fcntl(h, F_GETFD, 0) | FD_CLOEXEC);
-#endif
- OSTRACE3("OPENDIR %-3d %s\n", h, zDirname);
- return SQLITE_OK;
-}
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at least SQLITE_TEMPNAME_SIZE characters.
-*/
-int sqlite3UnixTempFileName(char *zBuf){
- static const char *azDirs[] = {
- 0,
- "/var/tmp",
- "/usr/tmp",
- "/tmp",
- ".",
- };
- static const unsigned char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- struct stat buf;
- const char *zDir = ".";
- azDirs[0] = sqlite3_temp_directory;
- for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
- if( azDirs[i]==0 ) continue;
- if( stat(azDirs[i], &buf) ) continue;
- if( !S_ISDIR(buf.st_mode) ) continue;
- if( access(azDirs[i], 07) ) continue;
- zDir = azDirs[i];
- break;
- }
- do{
- sqlite3_snprintf(SQLITE_TEMPNAME_SIZE, zBuf, "%s/"TEMP_FILE_PREFIX, zDir);
- j = strlen(zBuf);
- sqlite3Randomness(15, &zBuf[j]);
- for(i=0; i<15; i++, j++){
- zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
- }while( access(zBuf,0)==0 );
- return SQLITE_OK;
-}
-
-/*
-** Check that a given pathname is a directory and is writable
+** Seek to the offset passed as the second argument, then read cnt
+** bytes into pBuf. Return the number of bytes actually read.
**
+** NB: If you define USE_PREAD or USE_PREAD64, then it might also
+** be necessary to define _XOPEN_SOURCE to be 500. This varies from
+** one system to another. Since SQLite does not define USE_PREAD
+** any any form by default, we will not attempt to define _XOPEN_SOURCE.
+** See tickets #2741 and #2681.
*/
-int sqlite3UnixIsDirWritable(char *zBuf){
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- struct stat buf;
- if( zBuf==0 ) return 0;
- if( zBuf[0]==0 ) return 0;
- if( stat(zBuf, &buf) ) return 0;
- if( !S_ISDIR(buf.st_mode) ) return 0;
- if( access(zBuf, 07) ) return 0;
-#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
- return 1;
-}
-
-/*
-** Seek to the offset in id->offset then read cnt bytes into pBuf.
-** Return the number of bytes actually read. Update the offset.
-*/
-static int seekAndRead(unixFile *id, void *pBuf, int cnt){
+static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
i64 newOffset;
TIMER_START;
#if defined(USE_PREAD)
- got = pread(id->h, pBuf, cnt, id->offset);
+ got = pread(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
#elif defined(USE_PREAD64)
- got = pread64(id->h, pBuf, cnt, id->offset);
+ got = pread64(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
#else
- newOffset = lseek(id->h, id->offset, SEEK_SET);
+ newOffset = lseek(id->h, offset, SEEK_SET);
SimulateIOError( newOffset-- );
- if( newOffset!=id->offset ){
+ if( newOffset!=offset ){
return -1;
}
got = read(id->h, pBuf, cnt);
#endif
TIMER_END;
- OSTRACE5("READ %-3d %5d %7lld %d\n", id->h, got, id->offset, TIMER_ELAPSED);
- if( got>0 ){
- id->offset += got;
- }
+ OSTRACE5("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED);
return got;
}
@@ -1025,10 +863,15 @@ static int seekAndRead(unixFile *id, void *pBuf, int cnt){
** bytes were read successfully and SQLITE_IOERR if anything goes
** wrong.
*/
-static int unixRead(OsFile *id, void *pBuf, int amt){
+static int unixRead(
+ sqlite3_file *id,
+ void *pBuf,
+ int amt,
+ sqlite3_int64 offset
+){
int got;
assert( id );
- got = seekAndRead((unixFile*)id, pBuf, amt);
+ got = seekAndRead((unixFile*)id, offset, pBuf, amt);
if( got==amt ){
return SQLITE_OK;
}else if( got<0 ){
@@ -1043,26 +886,23 @@ static int unixRead(OsFile *id, void *pBuf, int amt){
** Seek to the offset in id->offset then read cnt bytes into pBuf.
** Return the number of bytes actually read. Update the offset.
*/
-static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){
+static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
int got;
i64 newOffset;
TIMER_START;
#if defined(USE_PREAD)
- got = pwrite(id->h, pBuf, cnt, id->offset);
+ got = pwrite(id->h, pBuf, cnt, offset);
#elif defined(USE_PREAD64)
- got = pwrite64(id->h, pBuf, cnt, id->offset);
+ got = pwrite64(id->h, pBuf, cnt, offset);
#else
- newOffset = lseek(id->h, id->offset, SEEK_SET);
- if( newOffset!=id->offset ){
+ newOffset = lseek(id->h, offset, SEEK_SET);
+ if( newOffset!=offset ){
return -1;
}
got = write(id->h, pBuf, cnt);
#endif
TIMER_END;
- OSTRACE5("WRITE %-3d %5d %7lld %d\n", id->h, got, id->offset, TIMER_ELAPSED);
- if( got>0 ){
- id->offset += got;
- }
+ OSTRACE5("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED);
return got;
}
@@ -1071,12 +911,18 @@ static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){
** Write data from a buffer into a file. Return SQLITE_OK on success
** or some other error code on failure.
*/
-static int unixWrite(OsFile *id, const void *pBuf, int amt){
+static int unixWrite(
+ sqlite3_file *id,
+ const void *pBuf,
+ int amt,
+ sqlite3_int64 offset
+){
int wrote = 0;
assert( id );
assert( amt>0 );
- while( amt>0 && (wrote = seekAndWrite((unixFile*)id, pBuf, amt))>0 ){
+ while( amt>0 && (wrote = seekAndWrite((unixFile*)id, offset, pBuf, amt))>0 ){
amt -= wrote;
+ offset += wrote;
pBuf = &((char*)pBuf)[wrote];
}
SimulateIOError(( wrote=(-1), amt=1 ));
@@ -1091,18 +937,6 @@ static int unixWrite(OsFile *id, const void *pBuf, int amt){
return SQLITE_OK;
}
-/*
-** Move the read/write pointer in a file.
-*/
-static int unixSeek(OsFile *id, i64 offset){
- assert( id );
-#ifdef SQLITE_TEST
- if( offset ) SimulateDiskfullError(return SQLITE_FULL);
-#endif
- ((unixFile*)id)->offset = offset;
- return SQLITE_OK;
-}
-
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
@@ -1205,25 +1039,34 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
** the directory entry for the journal was never created) and the transaction
** will not roll back - possibly leading to database corruption.
*/
-static int unixSync(OsFile *id, int dataOnly){
+static int unixSync(sqlite3_file *id, int flags){
int rc;
unixFile *pFile = (unixFile*)id;
+
+ int isDataOnly = (flags&SQLITE_SYNC_DATAONLY);
+ int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL;
+
+ /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
+ assert((flags&0x0F)==SQLITE_SYNC_NORMAL
+ || (flags&0x0F)==SQLITE_SYNC_FULL
+ );
+
assert( pFile );
OSTRACE2("SYNC %-3d\n", pFile->h);
- rc = full_fsync(pFile->h, pFile->fullSync, dataOnly);
+ rc = full_fsync(pFile->h, isFullsync, isDataOnly);
SimulateIOError( rc=1 );
if( rc ){
return SQLITE_IOERR_FSYNC;
}
if( pFile->dirfd>=0 ){
OSTRACE4("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
- HAVE_FULLFSYNC, pFile->fullSync);
+ HAVE_FULLFSYNC, isFullsync);
#ifndef SQLITE_DISABLE_DIRSYNC
/* The directory sync is only attempted if full_fsync is
** turned off or unavailable. If a full_fsync occurred above,
** then the directory sync is superfluous.
*/
- if( (!HAVE_FULLFSYNC || !pFile->fullSync) && full_fsync(pFile->dirfd,0,0) ){
+ if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
/*
** We have received multiple reports of fsync() returning
** errors when applied to directories on certain file systems.
@@ -1240,43 +1083,13 @@ static int unixSync(OsFile *id, int dataOnly){
}
/*
-** Sync the directory zDirname. This is a no-op on operating systems other
-** than UNIX.
-**
-** This is used to make sure the master journal file has truely been deleted
-** before making changes to individual journals on a multi-database commit.
-** The F_FULLFSYNC option is not needed here.
-*/
-int sqlite3UnixSyncDirectory(const char *zDirname){
-#ifdef SQLITE_DISABLE_DIRSYNC
- return SQLITE_OK;
-#else
- int fd;
- int r;
- fd = open(zDirname, O_RDONLY|O_BINARY, 0);
- OSTRACE3("DIRSYNC %-3d (%s)\n", fd, zDirname);
- if( fd<0 ){
- return SQLITE_CANTOPEN;
- }
- r = fsync(fd);
- close(fd);
- SimulateIOError( r=1 );
- if( r ){
- return SQLITE_IOERR_DIR_FSYNC;
- }else{
- return SQLITE_OK;
- }
-#endif
-}
-
-/*
** Truncate an open file to a specified size
*/
-static int unixTruncate(OsFile *id, i64 nByte){
+static int unixTruncate(sqlite3_file *id, i64 nByte){
int rc;
assert( id );
+ SimulateIOError( return SQLITE_IOERR_TRUNCATE );
rc = ftruncate(((unixFile*)id)->h, (off_t)nByte);
- SimulateIOError( rc=1 );
if( rc ){
return SQLITE_IOERR_TRUNCATE;
}else{
@@ -1287,7 +1100,7 @@ static int unixTruncate(OsFile *id, i64 nByte){
/*
** Determine the current size of a file in bytes
*/
-static int unixFileSize(OsFile *id, i64 *pSize){
+static int unixFileSize(sqlite3_file *id, i64 *pSize){
int rc;
struct stat buf;
assert( id );
@@ -1297,6 +1110,16 @@ static int unixFileSize(OsFile *id, i64 *pSize){
return SQLITE_IOERR_FSTAT;
}
*pSize = buf.st_size;
+
+ /* When opening a zero-size database, the findLockInfo() procedure
+ ** writes a single byte into that file in order to work around a bug
+ ** in the OS-X msdos filesystem. In order to avoid problems with upper
+ ** layers, we need to report this file size as zero even though it is
+ ** really 1. Ticket #3260.
+ */
+ if( *pSize==1 ) *pSize = 0;
+
+
return SQLITE_OK;
}
@@ -1306,12 +1129,14 @@ static int unixFileSize(OsFile *id, i64 *pSize){
** non-zero. If the file is unlocked or holds only SHARED locks, then
** return zero.
*/
-static int unixCheckReservedLock(OsFile *id){
+static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
int r = 0;
unixFile *pFile = (unixFile*)id;
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+
assert( pFile );
- sqlite3OsEnterMutex(); /* Because pFile->pLock is shared across threads */
+ enterMutex(); /* Because pFile->pLock is shared across threads */
/* Check if a thread in this process holds such a lock */
if( pFile->pLock->locktype>SHARED_LOCK ){
@@ -1332,10 +1157,11 @@ static int unixCheckReservedLock(OsFile *id){
}
}
- sqlite3OsLeaveMutex();
+ leaveMutex();
OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
- return r;
+ *pResOut = r;
+ return SQLITE_OK;
}
/*
@@ -1362,7 +1188,7 @@ static int unixCheckReservedLock(OsFile *id){
** This routine will only increase a lock. Use the sqlite3OsUnlock()
** routine to lower a locking level.
*/
-static int unixLock(OsFile *id, int locktype){
+static int unixLock(sqlite3_file *id, int locktype){
/* The following describes the implementation of the various locks and
** lock transitions in terms of the POSIX advisory shared and exclusive
** lock primitives (called read-locks and write-locks below, to avoid
@@ -1413,8 +1239,8 @@ static int unixLock(OsFile *id, int locktype){
locktypeName(pLock->locktype), pLock->cnt , getpid());
/* If there is already a lock of this type or more restrictive on the
- ** OsFile, do nothing. Don't use the end_lock: exit path, as
- ** sqlite3OsEnterMutex() hasn't been called yet.
+ ** unixFile, do nothing. Don't use the end_lock: exit path, as
+ ** enterMutex() hasn't been called yet.
*/
if( pFile->locktype>=locktype ){
OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h,
@@ -1430,18 +1256,18 @@ static int unixLock(OsFile *id, int locktype){
/* This mutex is needed because pFile->pLock is shared across threads
*/
- sqlite3OsEnterMutex();
+ enterMutex();
/* Make sure the current thread owns the pFile.
*/
rc = transferOwnership(pFile);
if( rc!=SQLITE_OK ){
- sqlite3OsLeaveMutex();
+ leaveMutex();
return rc;
}
pLock = pFile->pLock;
- /* If some thread using this PID has a lock via a different OsFile*
+ /* If some thread using this PID has a lock via a different unixFile*
** handle that precludes the requested lock, return BUSY.
*/
if( (pFile->locktype!=pLock->locktype &&
@@ -1551,7 +1377,7 @@ static int unixLock(OsFile *id, int locktype){
}
end_lock:
- sqlite3OsLeaveMutex();
+ leaveMutex();
OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
rc==SQLITE_OK ? "ok" : "failed");
return rc;
@@ -1564,11 +1390,12 @@ end_lock:
** If the locking level of the file descriptor is already at or below
** the requested locking level, this routine is a no-op.
*/
-static int unixUnlock(OsFile *id, int locktype){
+static int unixUnlock(sqlite3_file *id, int locktype){
struct lockInfo *pLock;
struct flock lock;
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
+ int h;
assert( pFile );
OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype,
@@ -1581,18 +1408,21 @@ static int unixUnlock(OsFile *id, int locktype){
if( CHECK_THREADID(pFile) ){
return SQLITE_MISUSE;
}
- sqlite3OsEnterMutex();
+ enterMutex();
+ h = pFile->h;
pLock = pFile->pLock;
assert( pLock->cnt!=0 );
if( pFile->locktype>SHARED_LOCK ){
assert( pLock->locktype==pFile->locktype );
+ SimulateIOErrorBenign(1);
+ SimulateIOError( h=(-1) )
+ SimulateIOErrorBenign(0);
if( locktype==SHARED_LOCK ){
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
- if( fcntl(pFile->h, F_SETLK, &lock)==(-1) ){
- /* This should never happen */
+ if( fcntl(h, F_SETLK, &lock)==(-1) ){
rc = SQLITE_IOERR_RDLOCK;
}
}
@@ -1600,10 +1430,10 @@ static int unixUnlock(OsFile *id, int locktype){
lock.l_whence = SEEK_SET;
lock.l_start = PENDING_BYTE;
lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
- if( fcntl(pFile->h, F_SETLK, &lock)!=(-1) ){
+ if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = SHARED_LOCK;
}else{
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
+ rc = SQLITE_IOERR_UNLOCK;
}
}
if( locktype==NO_LOCK ){
@@ -1618,10 +1448,14 @@ static int unixUnlock(OsFile *id, int locktype){
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = lock.l_len = 0L;
- if( fcntl(pFile->h, F_SETLK, &lock)!=(-1) ){
+ SimulateIOErrorBenign(1);
+ SimulateIOError( h=(-1) )
+ SimulateIOErrorBenign(0);
+ if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = NO_LOCK;
}else{
- rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
+ rc = SQLITE_IOERR_UNLOCK;
+ pLock->cnt = 1;
}
}
@@ -1629,65 +1463,79 @@ static int unixUnlock(OsFile *id, int locktype){
** count reaches zero, close any other file descriptors whose close
** was deferred because of outstanding locks.
*/
- pOpen = pFile->pOpen;
- pOpen->nLock--;
- assert( pOpen->nLock>=0 );
- if( pOpen->nLock==0 && pOpen->nPending>0 ){
- int i;
- for(i=0; i<pOpen->nPending; i++){
- close(pOpen->aPending[i]);
+ if( rc==SQLITE_OK ){
+ pOpen = pFile->pOpen;
+ pOpen->nLock--;
+ assert( pOpen->nLock>=0 );
+ if( pOpen->nLock==0 && pOpen->nPending>0 ){
+ int i;
+ for(i=0; i<pOpen->nPending; i++){
+ close(pOpen->aPending[i]);
+ }
+ sqlite3_free(pOpen->aPending);
+ pOpen->nPending = 0;
+ pOpen->aPending = 0;
}
- free(pOpen->aPending);
- pOpen->nPending = 0;
- pOpen->aPending = 0;
}
}
- sqlite3OsLeaveMutex();
- pFile->locktype = locktype;
+ leaveMutex();
+ if( rc==SQLITE_OK ) pFile->locktype = locktype;
return rc;
}
/*
-** Close a file.
+** This function performs the parts of the "close file" operation
+** common to all locking schemes. It closes the directory and file
+** handles, if they are valid, and sets all fields of the unixFile
+** structure to 0.
*/
-static int unixClose(OsFile **pId){
- unixFile *id = (unixFile*)*pId;
-
- if( !id ) return SQLITE_OK;
- unixUnlock(*pId, NO_LOCK);
- if( id->dirfd>=0 ) close(id->dirfd);
- id->dirfd = -1;
- sqlite3OsEnterMutex();
+static int closeUnixFile(sqlite3_file *id){
+ unixFile *pFile = (unixFile*)id;
+ if( pFile ){
+ if( pFile->dirfd>=0 ){
+ close(pFile->dirfd);
+ }
+ if( pFile->h>=0 ){
+ close(pFile->h);
+ }
+ OSTRACE2("CLOSE %-3d\n", pFile->h);
+ OpenCounter(-1);
+ memset(pFile, 0, sizeof(unixFile));
+ }
+ return SQLITE_OK;
+}
- if( id->pOpen->nLock ){
- /* If there are outstanding locks, do not actually close the file just
- ** yet because that would clear those locks. Instead, add the file
- ** descriptor to pOpen->aPending. It will be automatically closed when
- ** the last lock is cleared.
- */
- int *aNew;
- struct openCnt *pOpen = id->pOpen;
- aNew = realloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
- if( aNew==0 ){
- /* If a malloc fails, just leak the file descriptor */
- }else{
- pOpen->aPending = aNew;
- pOpen->aPending[pOpen->nPending] = id->h;
- pOpen->nPending++;
+/*
+** Close a file.
+*/
+static int unixClose(sqlite3_file *id){
+ if( id ){
+ unixFile *pFile = (unixFile *)id;
+ unixUnlock(id, NO_LOCK);
+ enterMutex();
+ if( pFile->pOpen && pFile->pOpen->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pOpen->aPending. It will be automatically closed when
+ ** the last lock is cleared.
+ */
+ int *aNew;
+ struct openCnt *pOpen = pFile->pOpen;
+ aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
+ if( aNew==0 ){
+ /* If a malloc fails, just leak the file descriptor */
+ }else{
+ pOpen->aPending = aNew;
+ pOpen->aPending[pOpen->nPending] = pFile->h;
+ pOpen->nPending++;
+ pFile->h = -1;
+ }
}
- }else{
- /* There are no outstanding locks so we can close the file immediately */
- close(id->h);
- }
- releaseLockInfo(id->pLock);
- releaseOpenCnt(id->pOpen);
-
- sqlite3OsLeaveMutex();
- id->isOpen = 0;
- OSTRACE2("CLOSE %-3d\n", id->h);
- OpenCounter(-1);
- sqlite3ThreadSafeFree(id);
- *pId = 0;
+ releaseLockInfo(pFile->pLock);
+ releaseOpenCnt(pFile->pOpen);
+ closeUnixFile(id);
+ leaveMutex();
+ }
return SQLITE_OK;
}
@@ -1701,7 +1549,7 @@ static int unixClose(OsFile **pId){
typedef struct afpLockingContext afpLockingContext;
struct afpLockingContext {
unsigned long long sharedLockByte;
- char *filePath;
+ const char *filePath;
};
struct ByteRangeLockPB2
@@ -1716,13 +1564,19 @@ struct ByteRangeLockPB2
#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2)
-/* return 0 on success, 1 on failure. To match the behavior of the
- normal posix file locking (used in unixLock for example), we should
- provide 'richer' return codes - specifically to differentiate between
- 'file busy' and 'file system error' results */
-static int _AFPFSSetLock(const char *path, int fd, unsigned long long offset,
- unsigned long long length, int setLockFlag)
-{
+/*
+** Return 0 on success, 1 on failure. To match the behavior of the
+** normal posix file locking (used in unixLock for example), we should
+** provide 'richer' return codes - specifically to differentiate between
+** 'file busy' and 'file system error' results.
+*/
+static int _AFPFSSetLock(
+ const char *path,
+ int fd,
+ unsigned long long offset,
+ unsigned long long length,
+ int setLockFlag
+){
struct ByteRangeLockPB2 pb;
int err;
@@ -1749,7 +1603,7 @@ static int _AFPFSSetLock(const char *path, int fd, unsigned long long offset,
** non-zero. If the file is unlocked or holds only SHARED locks, then
** return zero.
*/
-static int afpUnixCheckReservedLock(OsFile *id){
+static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
int r = 0;
unixFile *pFile = (unixFile*)id;
@@ -1777,25 +1631,25 @@ static int afpUnixCheckReservedLock(OsFile *id){
}
OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
- return r;
+ *pResOut = r;
+ return SQLITE_OK;
}
/* AFP-style locking following the behavior of unixLock, see the unixLock
** function comments for details of lock management. */
-static int afpUnixLock(OsFile *id, int locktype)
-{
+static int afpLock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
- int gotPendingLock = 0;
assert( pFile );
OSTRACE5("LOCK %d %s was %s pid=%d\n", pFile->h,
- locktypeName(locktype), locktypeName(pFile->locktype), getpid());
+ locktypeName(locktype), locktypeName(pFile->locktype), getpid());
+
/* If there is already a lock of this type or more restrictive on the
- ** OsFile, do nothing. Don't use the afp_end_lock: exit path, as
- ** sqlite3OsEnterMutex() hasn't been called yet.
- */
+ ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
+ ** enterMutex() hasn't been called yet.
+ */
if( pFile->locktype>=locktype ){
OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h,
locktypeName(locktype));
@@ -1803,32 +1657,32 @@ static int afpUnixLock(OsFile *id, int locktype)
}
/* Make sure the locking sequence is correct
- */
+ */
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
assert( locktype!=PENDING_LOCK );
assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
/* This mutex is needed because pFile->pLock is shared across threads
- */
- sqlite3OsEnterMutex();
+ */
+ enterMutex();
/* Make sure the current thread owns the pFile.
- */
+ */
rc = transferOwnership(pFile);
if( rc!=SQLITE_OK ){
- sqlite3OsLeaveMutex();
+ leaveMutex();
return rc;
}
/* A PENDING lock is needed before acquiring a SHARED lock and before
- ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
- ** be released.
- */
+ ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
+ ** be released.
+ */
if( locktype==SHARED_LOCK
|| (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK)
- ){
- int failed = _AFPFSSetLock(context->filePath, pFile->h,
- PENDING_BYTE, 1, 1);
+ ){
+ int failed;
+ failed = _AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 1);
if (failed) {
rc = SQLITE_BUSY;
goto afp_end_lock;
@@ -1836,11 +1690,10 @@ static int afpUnixLock(OsFile *id, int locktype)
}
/* If control gets to this point, then actually go ahead and make
- ** operating system calls for the specified lock.
- */
+ ** operating system calls for the specified lock.
+ */
if( locktype==SHARED_LOCK ){
int lk, failed;
- int tries = 0;
/* Now get the read-lock */
/* note that the quality of the randomness doesn't matter that much */
@@ -1875,7 +1728,7 @@ static int afpUnixLock(OsFile *id, int locktype)
/* Acquire an EXCLUSIVE lock */
/* Remove the shared lock before trying the range. we'll need to
- ** reestablish the shared lock if we can't get the afpUnixUnlock
+ ** reestablish the shared lock if we can't get the afpUnlock
*/
if (!_AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST +
context->sharedLockByte, 1, 0)) {
@@ -1903,21 +1756,20 @@ static int afpUnixLock(OsFile *id, int locktype)
}
afp_end_lock:
- sqlite3OsLeaveMutex();
+ leaveMutex();
OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
rc==SQLITE_OK ? "ok" : "failed");
return rc;
}
/*
- ** Lower the locking level on file descriptor pFile to locktype. locktype
- ** must be either NO_LOCK or SHARED_LOCK.
- **
- ** If the locking level of the file descriptor is already at or below
- ** the requested locking level, this routine is a no-op.
- */
-static int afpUnixUnlock(OsFile *id, int locktype) {
- struct flock lock;
+** Lower the locking level on file descriptor pFile to locktype. locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+*/
+static int afpUnlock(sqlite3_file *id, int locktype) {
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
@@ -1933,7 +1785,7 @@ static int afpUnixUnlock(OsFile *id, int locktype) {
if( CHECK_THREADID(pFile) ){
return SQLITE_MISUSE;
}
- sqlite3OsEnterMutex();
+ enterMutex();
if( pFile->locktype>SHARED_LOCK ){
if( locktype==SHARED_LOCK ){
int failed = 0;
@@ -1977,62 +1829,49 @@ static int afpUnixUnlock(OsFile *id, int locktype) {
}
if (rc == SQLITE_OK)
pFile->locktype = locktype;
- sqlite3OsLeaveMutex();
+ leaveMutex();
return rc;
}
/*
- ** Close a file & cleanup AFP specific locking context
- */
-static int afpUnixClose(OsFile **pId) {
- unixFile *id = (unixFile*)*pId;
-
- if( !id ) return SQLITE_OK;
- afpUnixUnlock(*pId, NO_LOCK);
- /* free the AFP locking structure */
- if (id->lockingContext != NULL) {
- if (((afpLockingContext *)id->lockingContext)->filePath != NULL)
- sqlite3ThreadSafeFree(((afpLockingContext*)id->lockingContext)->filePath);
- sqlite3ThreadSafeFree(id->lockingContext);
+** Close a file & cleanup AFP specific locking context
+*/
+static int afpClose(sqlite3_file *id) {
+ if( id ){
+ unixFile *pFile = (unixFile*)id;
+ afpUnlock(id, NO_LOCK);
+ sqlite3_free(pFile->lockingContext);
}
-
- if( id->dirfd>=0 ) close(id->dirfd);
- id->dirfd = -1;
- close(id->h);
- id->isOpen = 0;
- OSTRACE2("CLOSE %-3d\n", id->h);
- OpenCounter(-1);
- sqlite3ThreadSafeFree(id);
- *pId = 0;
- return SQLITE_OK;
+ return closeUnixFile(id);
}
#pragma mark flock() style locking
/*
- ** The flockLockingContext is not used
- */
+** The flockLockingContext is not used
+*/
typedef void flockLockingContext;
-static int flockUnixCheckReservedLock(OsFile *id) {
+static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
+ int r = 1;
unixFile *pFile = (unixFile*)id;
- if (pFile->locktype == RESERVED_LOCK) {
- return 1; /* already have a reserved lock */
- } else {
+ if (pFile->locktype != RESERVED_LOCK) {
/* attempt to get the lock */
int rc = flock(pFile->h, LOCK_EX | LOCK_NB);
if (!rc) {
/* got the lock, unlock it */
flock(pFile->h, LOCK_UN);
- return 0; /* no one has it reserved */
+ r = 0; /* no one has it reserved */
}
- return 1; /* someone else might have it reserved */
}
+
+ *pResOut = r;
+ return SQLITE_OK;
}
-static int flockUnixLock(OsFile *id, int locktype) {
+static int flockLock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
/* if we already have a lock, it is exclusive.
@@ -2054,7 +1893,7 @@ static int flockUnixLock(OsFile *id, int locktype) {
}
}
-static int flockUnixUnlock(OsFile *id, int locktype) {
+static int flockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
assert( locktype<=SHARED_LOCK );
@@ -2081,82 +1920,58 @@ static int flockUnixUnlock(OsFile *id, int locktype) {
}
/*
- ** Close a file.
- */
-static int flockUnixClose(OsFile **pId) {
- unixFile *id = (unixFile*)*pId;
-
- if( !id ) return SQLITE_OK;
- flockUnixUnlock(*pId, NO_LOCK);
-
- if( id->dirfd>=0 ) close(id->dirfd);
- id->dirfd = -1;
- sqlite3OsEnterMutex();
-
- close(id->h);
- sqlite3OsLeaveMutex();
- id->isOpen = 0;
- OSTRACE2("CLOSE %-3d\n", id->h);
- OpenCounter(-1);
- sqlite3ThreadSafeFree(id);
- *pId = 0;
- return SQLITE_OK;
+** Close a file.
+*/
+static int flockClose(sqlite3_file *id) {
+ if( id ){
+ flockUnlock(id, NO_LOCK);
+ }
+ return closeUnixFile(id);
}
#pragma mark Old-School .lock file based locking
-/*
- ** The dotlockLockingContext structure contains all dotlock (.lock) lock
- ** specific state
- */
-typedef struct dotlockLockingContext dotlockLockingContext;
-struct dotlockLockingContext {
- char *lockPath;
-};
-
-
-static int dotlockUnixCheckReservedLock(OsFile *id) {
+static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
+ int r = 1;
unixFile *pFile = (unixFile*)id;
- dotlockLockingContext *context =
- (dotlockLockingContext *) pFile->lockingContext;
-
- if (pFile->locktype == RESERVED_LOCK) {
- return 1; /* already have a reserved lock */
- } else {
+ char *zLockFile = (char *)pFile->lockingContext;
+
+ if (pFile->locktype != RESERVED_LOCK) {
struct stat statBuf;
- if (lstat(context->lockPath,&statBuf) == 0)
- /* file exists, someone else has the lock */
- return 1;
- else
+ if (lstat(zLockFile, &statBuf) != 0){
/* file does not exist, we could have it if we want it */
- return 0;
+ r = 0;
+ }
}
+
+ *pResOut = r;
+ return SQLITE_OK;
}
-static int dotlockUnixLock(OsFile *id, int locktype) {
+static int dotlockLock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
- dotlockLockingContext *context =
- (dotlockLockingContext *) pFile->lockingContext;
-
+ int fd;
+ char *zLockFile = (char *)pFile->lockingContext;
+
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (pFile->locktype > NO_LOCK) {
pFile->locktype = locktype;
/* Always update the timestamp on the old file */
- utimes(context->lockPath,NULL);
+ utimes(zLockFile, NULL);
return SQLITE_OK;
}
/* check to see if lock file already exists */
struct stat statBuf;
- if (lstat(context->lockPath,&statBuf) == 0){
+ if (lstat(zLockFile,&statBuf) == 0){
return SQLITE_BUSY; /* it does, busy */
}
/* grab an exclusive lock */
- int fd = open(context->lockPath,O_RDONLY|O_CREAT|O_EXCL,0600);
- if (fd < 0) {
+ fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
+ if( fd<0 ){
/* failed to open/create the file, someone else may have stolen the lock */
return SQLITE_BUSY;
}
@@ -2167,11 +1982,10 @@ static int dotlockUnixLock(OsFile *id, int locktype) {
return SQLITE_OK;
}
-static int dotlockUnixUnlock(OsFile *id, int locktype) {
+static int dotlockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
- dotlockLockingContext *context =
- (dotlockLockingContext *) pFile->lockingContext;
-
+ char *zLockFile = (char *)pFile->lockingContext;
+
assert( locktype<=SHARED_LOCK );
/* no-op if possible */
@@ -2186,7 +2000,7 @@ static int dotlockUnixUnlock(OsFile *id, int locktype) {
}
/* no, really, unlock. */
- unlink(context->lockPath);
+ unlink(zLockFile);
pFile->locktype = NO_LOCK;
return SQLITE_OK;
}
@@ -2194,148 +2008,55 @@ static int dotlockUnixUnlock(OsFile *id, int locktype) {
/*
** Close a file.
*/
-static int dotlockUnixClose(OsFile **pId) {
- unixFile *id = (unixFile*)*pId;
-
- if( !id ) return SQLITE_OK;
- dotlockUnixUnlock(*pId, NO_LOCK);
- /* free the dotlock locking structure */
- if (id->lockingContext != NULL) {
- if (((dotlockLockingContext *)id->lockingContext)->lockPath != NULL)
- sqlite3ThreadSafeFree( ( (dotlockLockingContext *)
- id->lockingContext)->lockPath);
- sqlite3ThreadSafeFree(id->lockingContext);
+static int dotlockClose(sqlite3_file *id) {
+ if( id ){
+ unixFile *pFile = (unixFile*)id;
+ dotlockUnlock(id, NO_LOCK);
+ sqlite3_free(pFile->lockingContext);
}
-
- if( id->dirfd>=0 ) close(id->dirfd);
- id->dirfd = -1;
- sqlite3OsEnterMutex();
-
- close(id->h);
-
- sqlite3OsLeaveMutex();
- id->isOpen = 0;
- OSTRACE2("CLOSE %-3d\n", id->h);
- OpenCounter(-1);
- sqlite3ThreadSafeFree(id);
- *pId = 0;
- return SQLITE_OK;
+ return closeUnixFile(id);
}
-#pragma mark No locking
+#endif /* SQLITE_ENABLE_LOCKING_STYLE */
/*
- ** The nolockLockingContext is void
- */
+** The nolockLockingContext is void
+*/
typedef void nolockLockingContext;
-static int nolockUnixCheckReservedLock(OsFile *id) {
- return 0;
-}
-
-static int nolockUnixLock(OsFile *id, int locktype) {
+static int nolockCheckReservedLock(sqlite3_file *id, int *pResOut) {
+ *pResOut = 0;
return SQLITE_OK;
}
-static int nolockUnixUnlock(OsFile *id, int locktype) {
+static int nolockLock(sqlite3_file *id, int locktype) {
return SQLITE_OK;
}
-/*
- ** Close a file.
- */
-static int nolockUnixClose(OsFile **pId) {
- unixFile *id = (unixFile*)*pId;
-
- if( !id ) return SQLITE_OK;
- if( id->dirfd>=0 ) close(id->dirfd);
- id->dirfd = -1;
- sqlite3OsEnterMutex();
-
- close(id->h);
-
- sqlite3OsLeaveMutex();
- id->isOpen = 0;
- OSTRACE2("CLOSE %-3d\n", id->h);
- OpenCounter(-1);
- sqlite3ThreadSafeFree(id);
- *pId = 0;
+static int nolockUnlock(sqlite3_file *id, int locktype) {
return SQLITE_OK;
}
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-
-/*
-** Turn a relative pathname into a full pathname. Return a pointer
-** to the full pathname stored in space obtained from sqliteMalloc().
-** The calling function is responsible for freeing this space once it
-** is no longer needed.
-*/
-char *sqlite3UnixFullPathname(const char *zRelative){
- char *zFull = 0;
- if( zRelative[0]=='/' ){
- sqlite3SetString(&zFull, zRelative, (char*)0);
- }else{
- char *zBuf = sqliteMalloc(5000);
- if( zBuf==0 ){
- return 0;
- }
- zBuf[0] = 0;
- sqlite3SetString(&zFull, getcwd(zBuf, 5000), "/", zRelative,
- (char*)0);
- sqliteFree(zBuf);
- }
-
-#if 0
- /*
- ** Remove "/./" path elements and convert "/A/./" path elements
- ** to just "/".
- */
- if( zFull ){
- int i, j;
- for(i=j=0; zFull[i]; i++){
- if( zFull[i]=='/' ){
- if( zFull[i+1]=='/' ) continue;
- if( zFull[i+1]=='.' && zFull[i+2]=='/' ){
- i += 1;
- continue;
- }
- if( zFull[i+1]=='.' && zFull[i+2]=='.' && zFull[i+3]=='/' ){
- while( j>0 && zFull[j-1]!='/' ){ j--; }
- i += 3;
- continue;
- }
- }
- zFull[j++] = zFull[i];
- }
- zFull[j] = 0;
- }
-#endif
-
- return zFull;
-}
-
/*
-** Change the value of the fullsync flag in the given file descriptor.
+** Close a file.
*/
-static void unixSetFullSync(OsFile *id, int v){
- ((unixFile*)id)->fullSync = v;
+static int nolockClose(sqlite3_file *id) {
+ return closeUnixFile(id);
}
-/*
-** Return the underlying file handle for an OsFile
-*/
-static int unixFileHandle(OsFile *id){
- return ((unixFile*)id)->h;
-}
/*
-** Return an integer that indices the type of lock currently held
-** by this handle. (Used for testing and analysis only.)
+** Information and control of an open file handle.
*/
-static int unixLockState(OsFile *id){
- return ((unixFile*)id)->locktype;
+static int unixFileControl(sqlite3_file *id, int op, void *pArg){
+ switch( op ){
+ case SQLITE_FCNTL_LOCKSTATE: {
+ *(int*)pArg = ((unixFile*)id)->locktype;
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_ERROR;
}
/*
@@ -2345,284 +2066,495 @@ static int unixLockState(OsFile *id){
**
** SQLite code assumes this function cannot fail. It also assumes that
** if two files are created in the same file-system directory (i.e.
-** a database and it's journal file) that the sector size will be the
+** a database and its journal file) that the sector size will be the
** same for both.
*/
-static int unixSectorSize(OsFile *id){
+static int unixSectorSize(sqlite3_file *id){
return SQLITE_DEFAULT_SECTOR_SIZE;
}
/*
-** This vector defines all the methods that can operate on an OsFile
-** for unix.
-*/
-static const IoMethod sqlite3UnixIoMethod = {
- unixClose,
- unixOpenDirectory,
- unixRead,
- unixWrite,
- unixSeek,
- unixTruncate,
- unixSync,
- unixSetFullSync,
- unixFileHandle,
- unixFileSize,
- unixLock,
- unixUnlock,
- unixLockState,
- unixCheckReservedLock,
- unixSectorSize,
-};
-
-#ifdef SQLITE_ENABLE_LOCKING_STYLE
-/*
- ** This vector defines all the methods that can operate on an OsFile
- ** for unix with AFP style file locking.
- */
-static const IoMethod sqlite3AFPLockingUnixIoMethod = {
- afpUnixClose,
- unixOpenDirectory,
- unixRead,
- unixWrite,
- unixSeek,
- unixTruncate,
- unixSync,
- unixSetFullSync,
- unixFileHandle,
- unixFileSize,
- afpUnixLock,
- afpUnixUnlock,
- unixLockState,
- afpUnixCheckReservedLock,
- unixSectorSize,
-};
-
-/*
- ** This vector defines all the methods that can operate on an OsFile
- ** for unix with flock() style file locking.
- */
-static const IoMethod sqlite3FlockLockingUnixIoMethod = {
- flockUnixClose,
- unixOpenDirectory,
- unixRead,
- unixWrite,
- unixSeek,
- unixTruncate,
- unixSync,
- unixSetFullSync,
- unixFileHandle,
- unixFileSize,
- flockUnixLock,
- flockUnixUnlock,
- unixLockState,
- flockUnixCheckReservedLock,
- unixSectorSize,
-};
-
-/*
- ** This vector defines all the methods that can operate on an OsFile
- ** for unix with dotlock style file locking.
- */
-static const IoMethod sqlite3DotlockLockingUnixIoMethod = {
- dotlockUnixClose,
- unixOpenDirectory,
- unixRead,
- unixWrite,
- unixSeek,
- unixTruncate,
- unixSync,
- unixSetFullSync,
- unixFileHandle,
- unixFileSize,
- dotlockUnixLock,
- dotlockUnixUnlock,
- unixLockState,
- dotlockUnixCheckReservedLock,
- unixSectorSize,
-};
-
-/*
- ** This vector defines all the methods that can operate on an OsFile
- ** for unix with dotlock style file locking.
- */
-static const IoMethod sqlite3NolockLockingUnixIoMethod = {
- nolockUnixClose,
- unixOpenDirectory,
- unixRead,
- unixWrite,
- unixSeek,
- unixTruncate,
- unixSync,
- unixSetFullSync,
- unixFileHandle,
- unixFileSize,
- nolockUnixLock,
- nolockUnixUnlock,
- unixLockState,
- nolockUnixCheckReservedLock,
- unixSectorSize,
-};
-
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+** Return the device characteristics for the file. This is always 0.
+*/
+static int unixDeviceCharacteristics(sqlite3_file *id){
+ return 0;
+}
/*
-** Allocate memory for a new unixFile and initialize that unixFile.
-** Write a pointer to the new unixFile into *pId.
-** If we run out of memory, close the file and return an error.
+** Initialize the contents of the unixFile structure pointed to by pId.
+**
+** When locking extensions are enabled, the filepath and locking style
+** are needed to determine the unixFile pMethod to use for locking operations.
+** The locking-style specific lockingContext data structure is created
+** and assigned here also.
*/
-#ifdef SQLITE_ENABLE_LOCKING_STYLE
-/*
- ** When locking extensions are enabled, the filepath and locking style
- ** are needed to determine the unixFile pMethod to use for locking operations.
- ** The locking-style specific lockingContext data structure is created
- ** and assigned here also.
- */
-static int allocateUnixFile(
+static int fillInUnixFile(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
int h, /* Open file descriptor of file being opened */
- OsFile **pId, /* Write completed initialization here */
+ int dirfd, /* Directory file descriptor */
+ sqlite3_file *pId, /* Write to the unixFile structure here */
const char *zFilename, /* Name of the file being opened */
- int delFlag /* Delete-on-or-before-close flag */
+ int noLock /* Omit locking if true */
){
- sqlite3LockingStyle lockingStyle;
- unixFile *pNew;
- unixFile f;
- int rc;
+ int eLockingStyle;
+ unixFile *pNew = (unixFile *)pId;
+ int rc = SQLITE_OK;
- memset(&f, 0, sizeof(f));
- lockingStyle = sqlite3DetectLockingStyle(zFilename, h);
- if ( lockingStyle == posixLockingStyle ) {
- sqlite3OsEnterMutex();
- rc = findLockInfo(h, &f.pLock, &f.pOpen);
- sqlite3OsLeaveMutex();
- if( rc ){
- close(h);
- unlink(zFilename);
- return SQLITE_NOMEM;
- }
- } else {
- /* pLock and pOpen are only used for posix advisory locking */
- f.pLock = NULL;
- f.pOpen = NULL;
- }
- if( delFlag ){
- unlink(zFilename);
- }
- f.dirfd = -1;
- f.h = h;
- SET_THREADID(&f);
- pNew = sqlite3ThreadSafeMalloc( sizeof(unixFile) );
- if( pNew==0 ){
- close(h);
- sqlite3OsEnterMutex();
- releaseLockInfo(f.pLock);
- releaseOpenCnt(f.pOpen);
- sqlite3OsLeaveMutex();
- *pId = 0;
- return SQLITE_NOMEM;
+ /* Macro to define the static contents of an sqlite3_io_methods
+ ** structure for a unix backend file. Different locking methods
+ ** require different functions for the xClose, xLock, xUnlock and
+ ** xCheckReservedLock methods.
+ */
+ #define IOMETHODS(xClose, xLock, xUnlock, xCheckReservedLock) { \
+ 1, /* iVersion */ \
+ xClose, /* xClose */ \
+ unixRead, /* xRead */ \
+ unixWrite, /* xWrite */ \
+ unixTruncate, /* xTruncate */ \
+ unixSync, /* xSync */ \
+ unixFileSize, /* xFileSize */ \
+ xLock, /* xLock */ \
+ xUnlock, /* xUnlock */ \
+ xCheckReservedLock, /* xCheckReservedLock */ \
+ unixFileControl, /* xFileControl */ \
+ unixSectorSize, /* xSectorSize */ \
+ unixDeviceCharacteristics /* xDeviceCapabilities */ \
+ }
+ static sqlite3_io_methods aIoMethod[] = {
+ IOMETHODS(unixClose, unixLock, unixUnlock, unixCheckReservedLock)
+ ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock)
+#ifdef SQLITE_ENABLE_LOCKING_STYLE
+ ,IOMETHODS(dotlockClose, dotlockLock, dotlockUnlock,dotlockCheckReservedLock)
+ ,IOMETHODS(flockClose, flockLock, flockUnlock, flockCheckReservedLock)
+ ,IOMETHODS(afpClose, afpLock, afpUnlock, afpCheckReservedLock)
+#endif
+ };
+ /* The order of the IOMETHODS macros above is important. It must be the
+ ** same order as the LOCKING_STYLE numbers
+ */
+ assert(LOCKING_STYLE_POSIX==1);
+ assert(LOCKING_STYLE_NONE==2);
+ assert(LOCKING_STYLE_DOTFILE==3);
+ assert(LOCKING_STYLE_FLOCK==4);
+ assert(LOCKING_STYLE_AFP==5);
+
+ assert( pNew->pLock==NULL );
+ assert( pNew->pOpen==NULL );
+
+ OSTRACE3("OPEN %-3d %s\n", h, zFilename);
+ pNew->h = h;
+ pNew->dirfd = dirfd;
+ SET_THREADID(pNew);
+
+ if( noLock ){
+ eLockingStyle = LOCKING_STYLE_NONE;
}else{
- *pNew = f;
- switch(lockingStyle) {
- case afpLockingStyle: {
- /* afp locking uses the file path so it needs to be included in
- ** the afpLockingContext */
- int nFilename;
- pNew->pMethod = &sqlite3AFPLockingUnixIoMethod;
- pNew->lockingContext =
- sqlite3ThreadSafeMalloc(sizeof(afpLockingContext));
- nFilename = strlen(zFilename)+1;
- ((afpLockingContext *)pNew->lockingContext)->filePath =
- sqlite3ThreadSafeMalloc(nFilename);
- memcpy(((afpLockingContext *)pNew->lockingContext)->filePath,
- zFilename, nFilename);
+ eLockingStyle = detectLockingStyle(pVfs, zFilename, h);
+ }
+
+ switch( eLockingStyle ){
+
+ case LOCKING_STYLE_POSIX: {
+ enterMutex();
+ rc = findLockInfo(h, &pNew->pLock, &pNew->pOpen);
+ leaveMutex();
+ break;
+ }
+
+#ifdef SQLITE_ENABLE_LOCKING_STYLE
+ case LOCKING_STYLE_AFP: {
+ /* AFP locking uses the file path so it needs to be included in
+ ** the afpLockingContext.
+ */
+ afpLockingContext *pCtx;
+ pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) );
+ if( pCtx==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ /* NB: zFilename exists and remains valid until the file is closed
+ ** according to requirement F11141. So we do not need to make a
+ ** copy of the filename. */
+ pCtx->filePath = zFilename;
srandomdev();
- break;
}
- case flockLockingStyle:
- /* flock locking doesn't need additional lockingContext information */
- pNew->pMethod = &sqlite3FlockLockingUnixIoMethod;
- break;
- case dotlockLockingStyle: {
- /* dotlock locking uses the file path so it needs to be included in
- ** the dotlockLockingContext */
- int nFilename;
- pNew->pMethod = &sqlite3DotlockLockingUnixIoMethod;
- pNew->lockingContext = sqlite3ThreadSafeMalloc(
- sizeof(dotlockLockingContext));
- nFilename = strlen(zFilename) + 6;
- ((dotlockLockingContext *)pNew->lockingContext)->lockPath =
- sqlite3ThreadSafeMalloc( nFilename );
- sqlite3_snprintf(nFilename,
- ((dotlockLockingContext *)pNew->lockingContext)->lockPath,
- "%s.lock", zFilename);
- break;
+ break;
+ }
+
+ case LOCKING_STYLE_DOTFILE: {
+ /* Dotfile locking uses the file path so it needs to be included in
+ ** the dotlockLockingContext
+ */
+ char *zLockFile;
+ int nFilename;
+ nFilename = strlen(zFilename) + 6;
+ zLockFile = (char *)sqlite3_malloc(nFilename);
+ if( zLockFile==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_snprintf(nFilename, zLockFile, "%s.lock", zFilename);
}
- case posixLockingStyle:
- /* posix locking doesn't need additional lockingContext information */
- pNew->pMethod = &sqlite3UnixIoMethod;
- break;
- case noLockingStyle:
- case unsupportedLockingStyle:
- default:
- pNew->pMethod = &sqlite3NolockLockingUnixIoMethod;
+ pNew->lockingContext = zLockFile;
+ break;
}
- *pId = (OsFile*)pNew;
+
+ case LOCKING_STYLE_FLOCK:
+ case LOCKING_STYLE_NONE:
+ break;
+#endif
+ }
+
+ if( rc!=SQLITE_OK ){
+ if( dirfd>=0 ) close(dirfd);
+ close(h);
+ }else{
+ pNew->pMethod = &aIoMethod[eLockingStyle-1];
OpenCounter(+1);
- return SQLITE_OK;
}
+ return rc;
}
-#else /* SQLITE_ENABLE_LOCKING_STYLE */
-static int allocateUnixFile(
- int h, /* Open file descriptor on file being opened */
- OsFile **pId, /* Write the resul unixFile structure here */
- const char *zFilename, /* Name of the file being opened */
- int delFlag /* If true, delete the file on or before closing */
+
+/*
+** Open a file descriptor to the directory containing file zFilename.
+** If successful, *pFd is set to the opened file descriptor and
+** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
+** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
+** value.
+**
+** If SQLITE_OK is returned, the caller is responsible for closing
+** the file descriptor *pFd using close().
+*/
+static int openDirectory(const char *zFilename, int *pFd){
+ int ii;
+ int fd = -1;
+ char zDirname[MAX_PATHNAME+1];
+
+ sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
+ for(ii=strlen(zDirname); ii>=0 && zDirname[ii]!='/'; ii--);
+ if( ii>0 ){
+ zDirname[ii] = '\0';
+ fd = open(zDirname, O_RDONLY|O_BINARY, 0);
+ if( fd>=0 ){
+#ifdef FD_CLOEXEC
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+ OSTRACE3("OPENDIR %-3d %s\n", fd, zDirname);
+ }
+ }
+ *pFd = fd;
+ return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN);
+}
+
+/*
+** Create a temporary file name in zBuf. zBuf must be allocated
+** by the calling process and must be big enough to hold at least
+** pVfs->mxPathname bytes.
+*/
+static int getTempname(int nBuf, char *zBuf){
+ static const char *azDirs[] = {
+ 0,
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ };
+ static const unsigned char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ int i, j;
+ struct stat buf;
+ const char *zDir = ".";
+
+ /* It's odd to simulate an io-error here, but really this is just
+ ** using the io-error infrastructure to test that SQLite handles this
+ ** function failing.
+ */
+ SimulateIOError( return SQLITE_IOERR );
+
+ azDirs[0] = sqlite3_temp_directory;
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
+ if( azDirs[i]==0 ) continue;
+ if( stat(azDirs[i], &buf) ) continue;
+ if( !S_ISDIR(buf.st_mode) ) continue;
+ if( access(azDirs[i], 07) ) continue;
+ zDir = azDirs[i];
+ break;
+ }
+
+ /* Check that the output buffer is large enough for the temporary file
+ ** name. If it is not, return SQLITE_ERROR.
+ */
+ if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){
+ return SQLITE_ERROR;
+ }
+
+ do{
+ sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
+ j = strlen(zBuf);
+ sqlite3_randomness(15, &zBuf[j]);
+ for(i=0; i<15; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ }while( access(zBuf,0)==0 );
+ return SQLITE_OK;
+}
+
+
+/*
+** Open the file zPath.
+**
+** Previously, the SQLite OS layer used three functions in place of this
+** one:
+**
+** sqlite3OsOpenReadWrite();
+** sqlite3OsOpenReadOnly();
+** sqlite3OsOpenExclusive();
+**
+** These calls correspond to the following combinations of flags:
+**
+** ReadWrite() -> (READWRITE | CREATE)
+** ReadOnly() -> (READONLY)
+** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
+**
+** The old OpenExclusive() accepted a boolean argument - "delFlag". If
+** true, the file was configured to be automatically deleted when the
+** file handle closed. To achieve the same effect using this new
+** interface, add the DELETEONCLOSE flag to those specified above for
+** OpenExclusive().
+*/
+static int unixOpen(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
){
- unixFile *pNew;
- unixFile f;
- int rc;
+ int fd = 0; /* File descriptor returned by open() */
+ int dirfd = -1; /* Directory file descriptor */
+ int oflags = 0; /* Flags to pass to open() */
+ int eType = flags&0xFFFFFF00; /* Type of file to open */
+ int noLock; /* True to omit locking primitives */
+
+ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
+ int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
+ int isCreate = (flags & SQLITE_OPEN_CREATE);
+ int isReadonly = (flags & SQLITE_OPEN_READONLY);
+ int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
+
+ /* If creating a master or main-file journal, this function will open
+ ** a file-descriptor on the directory too. The first time unixSync()
+ ** is called the directory file descriptor will be fsync()ed and close()d.
+ */
+ int isOpenDirectory = (isCreate &&
+ (eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL)
+ );
+
+ /* If argument zPath is a NULL pointer, this function is required to open
+ ** a temporary file. Use this buffer to store the file name in.
+ */
+ char zTmpname[MAX_PATHNAME+1];
+ const char *zName = zPath;
+
+ /* Check the following statements are true:
+ **
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
+ ** (b) if CREATE is set, then READWRITE must also be set, and
+ ** (c) if EXCLUSIVE is set, then CREATE must also be set.
+ ** (d) if DELETEONCLOSE is set, then CREATE must also be set.
+ */
+ assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
+ assert(isCreate==0 || isReadWrite);
+ assert(isExclusive==0 || isCreate);
+ assert(isDelete==0 || isCreate);
+
+ /* The main DB, main journal, and master journal are never automatically
+ ** deleted
+ */
+ assert( eType!=SQLITE_OPEN_MAIN_DB || !isDelete );
+ assert( eType!=SQLITE_OPEN_MAIN_JOURNAL || !isDelete );
+ assert( eType!=SQLITE_OPEN_MASTER_JOURNAL || !isDelete );
+
+ /* Assert that the upper layer has set one of the "file-type" flags. */
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
+ || eType==SQLITE_OPEN_TRANSIENT_DB
+ );
+
+ memset(pFile, 0, sizeof(unixFile));
+
+ if( !zName ){
+ int rc;
+ assert(isDelete && !isOpenDirectory);
+ rc = getTempname(MAX_PATHNAME+1, zTmpname);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ zName = zTmpname;
+ }
+
+ if( isReadonly ) oflags |= O_RDONLY;
+ if( isReadWrite ) oflags |= O_RDWR;
+ if( isCreate ) oflags |= O_CREAT;
+ if( isExclusive ) oflags |= (O_EXCL|O_NOFOLLOW);
+ oflags |= (O_LARGEFILE|O_BINARY);
+
+ fd = open(zName, oflags, isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS);
+ if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
+ /* Failed to open the file for read/write access. Try read-only. */
+ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
+ flags |= SQLITE_OPEN_READONLY;
+ return unixOpen(pVfs, zPath, pFile, flags, pOutFlags);
+ }
+ if( fd<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ if( isDelete ){
+ unlink(zName);
+ }
+ if( pOutFlags ){
+ *pOutFlags = flags;
+ }
+
+ assert(fd!=0);
+ if( isOpenDirectory ){
+ int rc = openDirectory(zPath, &dirfd);
+ if( rc!=SQLITE_OK ){
+ close(fd);
+ return rc;
+ }
+ }
#ifdef FD_CLOEXEC
- fcntl(h, F_SETFD, fcntl(h, F_GETFD, 0) | FD_CLOEXEC);
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
- memset(&f, 0, sizeof(f));
- sqlite3OsEnterMutex();
- rc = findLockInfo(h, &f.pLock, &f.pOpen);
- sqlite3OsLeaveMutex();
- if( delFlag ){
- unlink(zFilename);
+
+ noLock = eType!=SQLITE_OPEN_MAIN_DB;
+ return fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock);
+}
+
+/*
+** Delete the file at zPath. If the dirSync argument is true, fsync()
+** the directory after deleting the file.
+*/
+static int unixDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc = SQLITE_OK;
+ SimulateIOError(return SQLITE_IOERR_DELETE);
+ unlink(zPath);
+ if( dirSync ){
+ int fd;
+ rc = openDirectory(zPath, &fd);
+ if( rc==SQLITE_OK ){
+ if( fsync(fd) ){
+ rc = SQLITE_IOERR_DIR_FSYNC;
+ }
+ close(fd);
+ }
}
- if( rc ){
- close(h);
- return SQLITE_NOMEM;
- }
- OSTRACE3("OPEN %-3d %s\n", h, zFilename);
- f.dirfd = -1;
- f.h = h;
- SET_THREADID(&f);
- pNew = sqlite3ThreadSafeMalloc( sizeof(unixFile) );
- if( pNew==0 ){
- close(h);
- sqlite3OsEnterMutex();
- releaseLockInfo(f.pLock);
- releaseOpenCnt(f.pOpen);
- sqlite3OsLeaveMutex();
- *pId = 0;
- return SQLITE_NOMEM;
+ return rc;
+}
+
+/*
+** Test the existance of or access permissions of file zPath. The
+** test performed depends on the value of flags:
+**
+** SQLITE_ACCESS_EXISTS: Return 1 if the file exists
+** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
+** SQLITE_ACCESS_READONLY: Return 1 if the file is readable.
+**
+** Otherwise return 0.
+*/
+static int unixAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ int amode = 0;
+ SimulateIOError( return SQLITE_IOERR_ACCESS; );
+ switch( flags ){
+ case SQLITE_ACCESS_EXISTS:
+ amode = F_OK;
+ break;
+ case SQLITE_ACCESS_READWRITE:
+ amode = W_OK|R_OK;
+ break;
+ case SQLITE_ACCESS_READ:
+ amode = R_OK;
+ break;
+
+ default:
+ assert(!"Invalid flags argument");
+ }
+ *pResOut = (access(zPath, amode)==0);
+ return SQLITE_OK;
+}
+
+
+/*
+** Turn a relative pathname into a full pathname. The relative path
+** is stored as a nul-terminated string in the buffer pointed to by
+** zPath.
+**
+** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
+** (in this case, MAX_PATHNAME bytes). The full-path is written to
+** this buffer before returning.
+*/
+static int unixFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zPath, /* Possibly relative input path */
+ int nOut, /* Size of output buffer in bytes */
+ char *zOut /* Output buffer */
+){
+
+ /* It's odd to simulate an io-error here, but really this is just
+ ** using the io-error infrastructure to test that SQLite handles this
+ ** function failing. This function could fail if, for example, the
+ ** current working directly has been unlinked.
+ */
+ SimulateIOError( return SQLITE_ERROR );
+
+ assert( pVfs->mxPathname==MAX_PATHNAME );
+ zOut[nOut-1] = '\0';
+ if( zPath[0]=='/' ){
+ sqlite3_snprintf(nOut, zOut, "%s", zPath);
}else{
- *pNew = f;
- pNew->pMethod = &sqlite3UnixIoMethod;
- *pId = (OsFile*)pNew;
- OpenCounter(+1);
- return SQLITE_OK;
+ int nCwd;
+ if( getcwd(zOut, nOut-1)==0 ){
+ return SQLITE_CANTOPEN;
+ }
+ nCwd = strlen(zOut);
+ sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
}
-}
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+ return SQLITE_OK;
-#endif /* SQLITE_OMIT_DISKIO */
-/***************************************************************************
-** Everything above deals with file I/O. Everything that follows deals
-** with other miscellanous aspects of the operating system interface
-****************************************************************************/
+#if 0
+ /*
+ ** Remove "/./" path elements and convert "/A/./" path elements
+ ** to just "/".
+ */
+ if( zFull ){
+ int i, j;
+ for(i=j=0; zFull[i]; i++){
+ if( zFull[i]=='/' ){
+ if( zFull[i+1]=='/' ) continue;
+ if( zFull[i+1]=='.' && zFull[i+2]=='/' ){
+ i += 1;
+ continue;
+ }
+ if( zFull[i+1]=='.' && zFull[i+2]=='.' && zFull[i+3]=='/' ){
+ while( j>0 && zFull[j-1]!='/' ){ j--; }
+ i += 3;
+ continue;
+ }
+ }
+ zFull[j++] = zFull[i];
+ }
+ zFull[j] = 0;
+ }
+#endif
+}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
@@ -2631,23 +2563,46 @@ static int allocateUnixFile(
** within the shared library, and closing the shared library.
*/
#include <dlfcn.h>
-void *sqlite3UnixDlopen(const char *zFilename){
+static void *unixDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL);
}
-void *sqlite3UnixDlsym(void *pHandle, const char *zSymbol){
+
+/*
+** SQLite calls this function immediately after a call to unixDlSym() or
+** unixDlOpen() fails (returns a null pointer). If a more detailed error
+** message is available, it is written to zBufOut. If no error message
+** is available, zBufOut is left unmodified and SQLite uses a default
+** error message.
+*/
+static void unixDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
+ char *zErr;
+ enterMutex();
+ zErr = dlerror();
+ if( zErr ){
+ sqlite3_snprintf(nBuf, zBufOut, "%s", zErr);
+ }
+ leaveMutex();
+}
+static void *unixDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
return dlsym(pHandle, zSymbol);
}
-int sqlite3UnixDlclose(void *pHandle){
- return dlclose(pHandle);
+static void unixDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ dlclose(pHandle);
}
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
+ #define unixDlOpen 0
+ #define unixDlError 0
+ #define unixDlSym 0
+ #define unixDlClose 0
+#endif
/*
-** Get information to seed the random number generator. The seed
-** is written into the buffer zBuf[256]. The calling function must
-** supply a sufficiently large buffer.
+** Write nBuf bytes of random data to the supplied buffer zBuf.
*/
-int sqlite3UnixRandomSeed(char *zBuf){
+static int unixRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+
+ assert(nBuf>=(sizeof(time_t)+sizeof(int)));
+
/* We have to initialize zBuf to prevent valgrind from reporting
** errors. The reports issued by valgrind are incorrect - we would
** prefer that the randomness be increased by making use of the
@@ -2660,7 +2615,7 @@ int sqlite3UnixRandomSeed(char *zBuf){
** that we always use the same random number sequence. This makes the
** tests repeatable.
*/
- memset(zBuf, 0, 256);
+ memset(zBuf, 0, nBuf);
#if !defined(SQLITE_TEST)
{
int pid, fd;
@@ -2670,9 +2625,9 @@ int sqlite3UnixRandomSeed(char *zBuf){
time(&t);
memcpy(zBuf, &t, sizeof(t));
pid = getpid();
- memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid));
+ memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
}else{
- read(fd, zBuf, 256);
+ read(fd, zBuf, nBuf);
close(fd);
}
}
@@ -2680,224 +2635,23 @@ int sqlite3UnixRandomSeed(char *zBuf){
return SQLITE_OK;
}
+
/*
** Sleep for a little while. Return the amount of time slept.
-** The argument is the number of milliseconds we want to sleep.
+** The argument is the number of microseconds we want to sleep.
+** The return value is the number of microseconds of sleep actually
+** requested from the underlying operating system, a number which
+** might be greater than or equal to the argument, but not less
+** than the argument.
*/
-int sqlite3UnixSleep(int ms){
+static int unixSleep(sqlite3_vfs *pVfs, int microseconds){
#if defined(HAVE_USLEEP) && HAVE_USLEEP
- usleep(ms*1000);
- return ms;
-#else
- sleep((ms+999)/1000);
- return 1000*((ms+999)/1000);
-#endif
-}
-
-/*
-** Static variables used for thread synchronization.
-**
-** inMutex the nesting depth of the recursive mutex. The thread
-** holding mutexMain can read this variable at any time.
-** But is must hold mutexAux to change this variable. Other
-** threads must hold mutexAux to read the variable and can
-** never write.
-**
-** mutexOwner The thread id of the thread holding mutexMain. Same
-** access rules as for inMutex.
-**
-** mutexOwnerValid True if the value in mutexOwner is valid. The same
-** access rules apply as for inMutex.
-**
-** mutexMain The main mutex. Hold this mutex in order to get exclusive
-** access to SQLite data structures.
-**
-** mutexAux An auxiliary mutex needed to access variables defined above.
-**
-** Mutexes are always acquired in this order: mutexMain mutexAux. It
-** is not necessary to acquire mutexMain in order to get mutexAux - just
-** do not attempt to acquire them in the reverse order: mutexAux mutexMain.
-** Either get the mutexes with mutexMain first or get mutexAux only.
-**
-** When running on a platform where the three variables inMutex, mutexOwner,
-** and mutexOwnerValid can be set atomically, the mutexAux is not required.
-** On many systems, all three are 32-bit integers and writing to a 32-bit
-** integer is atomic. I think. But there are no guarantees. So it seems
-** safer to protect them using mutexAux.
-*/
-static int inMutex = 0;
-#ifdef SQLITE_UNIX_THREADS
-static pthread_t mutexOwner; /* Thread holding mutexMain */
-static int mutexOwnerValid = 0; /* True if mutexOwner is valid */
-static pthread_mutex_t mutexMain = PTHREAD_MUTEX_INITIALIZER; /* The mutex */
-static pthread_mutex_t mutexAux = PTHREAD_MUTEX_INITIALIZER; /* Aux mutex */
-#endif
-
-/*
-** The following pair of routine implement mutual exclusion for
-** multi-threaded processes. Only a single thread is allowed to
-** executed code that is surrounded by EnterMutex() and LeaveMutex().
-**
-** SQLite uses only a single Mutex. There is not much critical
-** code and what little there is executes quickly and without blocking.
-**
-** As of version 3.3.2, this mutex must be recursive.
-*/
-void sqlite3UnixEnterMutex(){
-#ifdef SQLITE_UNIX_THREADS
- pthread_mutex_lock(&mutexAux);
- if( !mutexOwnerValid || !pthread_equal(mutexOwner, pthread_self()) ){
- pthread_mutex_unlock(&mutexAux);
- pthread_mutex_lock(&mutexMain);
- assert( inMutex==0 );
- assert( !mutexOwnerValid );
- pthread_mutex_lock(&mutexAux);
- mutexOwner = pthread_self();
- mutexOwnerValid = 1;
- }
- inMutex++;
- pthread_mutex_unlock(&mutexAux);
+ usleep(microseconds);
+ return microseconds;
#else
- inMutex++;
-#endif
-}
-void sqlite3UnixLeaveMutex(){
- assert( inMutex>0 );
-#ifdef SQLITE_UNIX_THREADS
- pthread_mutex_lock(&mutexAux);
- inMutex--;
- assert( pthread_equal(mutexOwner, pthread_self()) );
- if( inMutex==0 ){
- assert( mutexOwnerValid );
- mutexOwnerValid = 0;
- pthread_mutex_unlock(&mutexMain);
- }
- pthread_mutex_unlock(&mutexAux);
-#else
- inMutex--;
-#endif
-}
-
-/*
-** Return TRUE if the mutex is currently held.
-**
-** If the thisThrd parameter is true, return true only if the
-** calling thread holds the mutex. If the parameter is false, return
-** true if any thread holds the mutex.
-*/
-int sqlite3UnixInMutex(int thisThrd){
-#ifdef SQLITE_UNIX_THREADS
- int rc;
- pthread_mutex_lock(&mutexAux);
- rc = inMutex>0 && (thisThrd==0 || pthread_equal(mutexOwner,pthread_self()));
- pthread_mutex_unlock(&mutexAux);
- return rc;
-#else
- return inMutex>0;
-#endif
-}
-
-/*
-** Remember the number of thread-specific-data blocks allocated.
-** Use this to verify that we are not leaking thread-specific-data.
-** Ticket #1601
-*/
-#ifdef SQLITE_TEST
-int sqlite3_tsd_count = 0;
-# ifdef SQLITE_UNIX_THREADS
- static pthread_mutex_t tsd_counter_mutex = PTHREAD_MUTEX_INITIALIZER;
-# define TSD_COUNTER(N) \
- pthread_mutex_lock(&tsd_counter_mutex); \
- sqlite3_tsd_count += N; \
- pthread_mutex_unlock(&tsd_counter_mutex);
-# else
-# define TSD_COUNTER(N) sqlite3_tsd_count += N
-# endif
-#else
-# define TSD_COUNTER(N) /* no-op */
-#endif
-
-/*
-** If called with allocateFlag>0, then return a pointer to thread
-** specific data for the current thread. Allocate and zero the
-** thread-specific data if it does not already exist.
-**
-** If called with allocateFlag==0, then check the current thread
-** specific data. Return it if it exists. If it does not exist,
-** then return NULL.
-**
-** If called with allocateFlag<0, check to see if the thread specific
-** data is allocated and is all zero. If it is then deallocate it.
-** Return a pointer to the thread specific data or NULL if it is
-** unallocated or gets deallocated.
-*/
-ThreadData *sqlite3UnixThreadSpecificData(int allocateFlag){
- static const ThreadData zeroData = {0}; /* Initializer to silence warnings
- ** from broken compilers */
-#ifdef SQLITE_UNIX_THREADS
- static pthread_key_t key;
- static int keyInit = 0;
- ThreadData *pTsd;
-
- if( !keyInit ){
- sqlite3OsEnterMutex();
- if( !keyInit ){
- int rc;
- rc = pthread_key_create(&key, 0);
- if( rc ){
- sqlite3OsLeaveMutex();
- return 0;
- }
- keyInit = 1;
- }
- sqlite3OsLeaveMutex();
- }
-
- pTsd = pthread_getspecific(key);
- if( allocateFlag>0 ){
- if( pTsd==0 ){
- if( !sqlite3TestMallocFail() ){
- pTsd = sqlite3OsMalloc(sizeof(zeroData));
- }
-#ifdef SQLITE_MEMDEBUG
- sqlite3_isFail = 0;
-#endif
- if( pTsd ){
- *pTsd = zeroData;
- pthread_setspecific(key, pTsd);
- TSD_COUNTER(+1);
- }
- }
- }else if( pTsd!=0 && allocateFlag<0
- && memcmp(pTsd, &zeroData, sizeof(ThreadData))==0 ){
- sqlite3OsFree(pTsd);
- pthread_setspecific(key, 0);
- TSD_COUNTER(-1);
- pTsd = 0;
- }
- return pTsd;
-#else
- static ThreadData *pTsd = 0;
- if( allocateFlag>0 ){
- if( pTsd==0 ){
- if( !sqlite3TestMallocFail() ){
- pTsd = sqlite3OsMalloc( sizeof(zeroData) );
- }
-#ifdef SQLITE_MEMDEBUG
- sqlite3_isFail = 0;
-#endif
- if( pTsd ){
- *pTsd = zeroData;
- TSD_COUNTER(+1);
- }
- }
- }else if( pTsd!=0 && allocateFlag<0
- && memcmp(pTsd, &zeroData, sizeof(ThreadData))==0 ){
- sqlite3OsFree(pTsd);
- TSD_COUNTER(-1);
- pTsd = 0;
- }
- return pTsd;
+ int seconds = (microseconds+999999)/1000000;
+ sleep(seconds);
+ return seconds*1000000;
#endif
}
@@ -2914,7 +2668,7 @@ int sqlite3_current_time = 0;
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
-int sqlite3UnixCurrentTime(double *prNow){
+static int unixCurrentTime(sqlite3_vfs *pVfs, double *prNow){
#ifdef NO_GETTOD
time_t t;
time(&t);
@@ -2932,4 +2686,65 @@ int sqlite3UnixCurrentTime(double *prNow){
return 0;
}
-#endif /* OS_UNIX */
+static int unixGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ return 0;
+}
+
+/*
+** Initialize the operating system interface.
+*/
+int sqlite3_os_init(void){
+ /* Macro to define the static contents of an sqlite3_vfs structure for
+ ** the unix backend. The two parameters are the values to use for
+ ** the sqlite3_vfs.zName and sqlite3_vfs.pAppData fields, respectively.
+ **
+ */
+ #define UNIXVFS(zVfsName, pVfsAppData) { \
+ 1, /* iVersion */ \
+ sizeof(unixFile), /* szOsFile */ \
+ MAX_PATHNAME, /* mxPathname */ \
+ 0, /* pNext */ \
+ zVfsName, /* zName */ \
+ (void *)pVfsAppData, /* pAppData */ \
+ unixOpen, /* xOpen */ \
+ unixDelete, /* xDelete */ \
+ unixAccess, /* xAccess */ \
+ unixFullPathname, /* xFullPathname */ \
+ unixDlOpen, /* xDlOpen */ \
+ unixDlError, /* xDlError */ \
+ unixDlSym, /* xDlSym */ \
+ unixDlClose, /* xDlClose */ \
+ unixRandomness, /* xRandomness */ \
+ unixSleep, /* xSleep */ \
+ unixCurrentTime, /* xCurrentTime */ \
+ unixGetLastError /* xGetLastError */ \
+ }
+
+ static sqlite3_vfs unixVfs = UNIXVFS("unix", 0);
+#ifdef SQLITE_ENABLE_LOCKING_STYLE
+#if 0
+ int i;
+ static sqlite3_vfs aVfs[] = {
+ UNIXVFS("unix-posix", LOCKING_STYLE_POSIX),
+ UNIXVFS("unix-afp", LOCKING_STYLE_AFP),
+ UNIXVFS("unix-flock", LOCKING_STYLE_FLOCK),
+ UNIXVFS("unix-dotfile", LOCKING_STYLE_DOTFILE),
+ UNIXVFS("unix-none", LOCKING_STYLE_NONE)
+ };
+ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
+ sqlite3_vfs_register(&aVfs[i], 0);
+ }
+#endif
+#endif
+ sqlite3_vfs_register(&unixVfs, 1);
+ return SQLITE_OK;
+}
+
+/*
+** Shutdown the operating system interface. This is a no-op for unix.
+*/
+int sqlite3_os_end(void){
+ return SQLITE_OK;
+}
+
+#endif /* SQLITE_OS_UNIX */
diff --git a/third_party/sqlite/os_win.c b/third_party/sqlite/src/os_win.c
index 9ebb9dc..ec30add 100644..100755
--- a/third_party/sqlite/os_win.c
+++ b/third_party/sqlite/src/os_win.c
@@ -11,10 +11,38 @@
******************************************************************************
**
** This file contains code that is specific to windows.
+**
+** $Id: os_win.c,v 1.132 2008/07/31 01:34:34 shane Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
-#if OS_WIN /* This file is used for windows only */
+#if SQLITE_OS_WIN /* This file is used for windows only */
+
+
+/*
+** A Note About Memory Allocation:
+**
+** This driver uses malloc()/free() directly rather than going through
+** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
+** are designed for use on embedded systems where memory is scarce and
+** malloc failures happen frequently. Win32 does not typically run on
+** embedded systems, and when it does the developers normally have bigger
+** problems to worry about than running out of memory. So there is not
+** a compelling need to use the wrappers.
+**
+** But there is a good reason to not use the wrappers. If we use the
+** wrappers then we will get simulated malloc() failures within this
+** driver. And that causes all kinds of problems for our tests. We
+** could enhance SQLite to deal with simulated malloc failures within
+** the OS driver, but the code to deal with those failure would not
+** be exercised on Linux (which does not need to malloc() in the driver)
+** and so we would have difficulty writing coverage tests for that
+** code. Better to leave the code out, we think.
+**
+** The point of this discussion is as follows: When creating a new
+** OS layer for an embedded system, if you use this file as an example,
+** avoid the use of malloc()/free(). Those routines work ok on windows
+** desktops but not so well in embedded systems.
+*/
#include <winbase.h>
@@ -38,18 +66,15 @@
** Determine if we are dealing with WindowsCE - which has a much
** reduced API.
*/
-#if defined(_WIN32_WCE)
-# define OS_WINCE 1
+#if defined(SQLITE_OS_WINCE)
# define AreFileApisANSI() 1
-#else
-# define OS_WINCE 0
#endif
/*
** WinCE lacks native support for file locking so we have to fake it
** with some code of our own.
*/
-#if OS_WINCE
+#if SQLITE_OS_WINCE
typedef struct winceLock {
int nReaders; /* Number of reader locks obtained */
BOOL bPending; /* Indicates a pending lock has been obtained */
@@ -59,16 +84,16 @@ typedef struct winceLock {
#endif
/*
-** The winFile structure is a subclass of OsFile specific to the win32
+** The winFile structure is a subclass of sqlite3_file* specific to the win32
** portability layer.
*/
typedef struct winFile winFile;
struct winFile {
- IoMethod const *pMethod;/* Must be first */
+ const sqlite3_io_methods *pMethod;/* Must be first */
HANDLE h; /* Handle for accessing the file */
unsigned char locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
-#if OS_WINCE
+#if SQLITE_OS_WINCE
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
HANDLE hMutex; /* Mutex used to control access to shared lock */
HANDLE hShared; /* Shared memory segment used for locking */
@@ -79,13 +104,6 @@ struct winFile {
/*
-** Do not include any of the File I/O interface procedures if the
-** SQLITE_OMIT_DISKIO macro is defined (indicating that there database
-** will be in-memory only)
-*/
-#ifndef SQLITE_OMIT_DISKIO
-
-/*
** The following variable is (normally) set once and never changes
** thereafter. It records whether the operating system is Win95
** or WinNT.
@@ -97,7 +115,11 @@ struct winFile {
** In order to facilitate testing on a WinNT system, the test fixture
** can manually set this value to 1 to emulate Win98 behavior.
*/
+#ifdef SQLITE_TEST
int sqlite3_os_type = 0;
+#else
+static int sqlite3_os_type = 0;
+#endif
/*
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
@@ -110,7 +132,7 @@ int sqlite3_os_type = 0;
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if OS_WINCE
+#if SQLITE_OS_WINCE
# define isNT() (1)
#else
static int isNT(void){
@@ -122,25 +144,25 @@ int sqlite3_os_type = 0;
}
return sqlite3_os_type==2;
}
-#endif /* OS_WINCE */
+#endif /* SQLITE_OS_WINCE */
/*
** Convert a UTF-8 string to microsoft unicode (UTF-16?).
**
-** Space to hold the returned string is obtained from sqliteMalloc.
+** Space to hold the returned string is obtained from malloc.
*/
static WCHAR *utf8ToUnicode(const char *zFilename){
int nChar;
WCHAR *zWideFilename;
nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
- zWideFilename = sqliteMalloc( nChar*sizeof(zWideFilename[0]) );
+ zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) );
if( zWideFilename==0 ){
return 0;
}
nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
if( nChar==0 ){
- sqliteFree(zWideFilename);
+ free(zWideFilename);
zWideFilename = 0;
}
return zWideFilename;
@@ -148,21 +170,21 @@ static WCHAR *utf8ToUnicode(const char *zFilename){
/*
** Convert microsoft unicode to UTF-8. Space to hold the returned string is
-** obtained from sqliteMalloc().
+** obtained from malloc().
*/
static char *unicodeToUtf8(const WCHAR *zWideFilename){
int nByte;
char *zFilename;
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = sqliteMalloc( nByte );
+ zFilename = malloc( nByte );
if( zFilename==0 ){
return 0;
}
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
0, 0);
if( nByte == 0 ){
- sqliteFree(zFilename);
+ free(zFilename);
zFilename = 0;
}
return zFilename;
@@ -173,7 +195,7 @@ static char *unicodeToUtf8(const WCHAR *zWideFilename){
** current codepage settings for file apis.
**
** Space to hold the returned string is obtained
-** from sqliteMalloc.
+** from malloc.
*/
static WCHAR *mbcsToUnicode(const char *zFilename){
int nByte;
@@ -181,13 +203,13 @@ static WCHAR *mbcsToUnicode(const char *zFilename){
int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR);
- zMbcsFilename = sqliteMalloc( nByte*sizeof(zMbcsFilename[0]) );
+ zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) );
if( zMbcsFilename==0 ){
return 0;
}
nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
if( nByte==0 ){
- sqliteFree(zMbcsFilename);
+ free(zMbcsFilename);
zMbcsFilename = 0;
}
return zMbcsFilename;
@@ -198,7 +220,7 @@ static WCHAR *mbcsToUnicode(const char *zFilename){
** user's Ansi codepage.
**
** Space to hold the returned string is obtained from
-** sqliteMalloc().
+** malloc().
*/
static char *unicodeToMbcs(const WCHAR *zWideFilename){
int nByte;
@@ -206,14 +228,14 @@ static char *unicodeToMbcs(const WCHAR *zWideFilename){
int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = sqliteMalloc( nByte );
+ zFilename = malloc( nByte );
if( zFilename==0 ){
return 0;
}
nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte,
0, 0);
if( nByte == 0 ){
- sqliteFree(zFilename);
+ free(zFilename);
zFilename = 0;
}
return zFilename;
@@ -221,7 +243,7 @@ static char *unicodeToMbcs(const WCHAR *zWideFilename){
/*
** Convert multibyte character string to UTF-8. Space to hold the
-** returned string is obtained from sqliteMalloc().
+** returned string is obtained from malloc().
*/
static char *mbcsToUtf8(const char *zFilename){
char *zFilenameUtf8;
@@ -232,13 +254,13 @@ static char *mbcsToUtf8(const char *zFilename){
return 0;
}
zFilenameUtf8 = unicodeToUtf8(zTmpWide);
- sqliteFree(zTmpWide);
+ free(zTmpWide);
return zFilenameUtf8;
}
/*
** Convert UTF-8 to multibyte character string. Space to hold the
-** returned string is obtained from sqliteMalloc().
+** returned string is obtained from malloc().
*/
static char *utf8ToMbcs(const char *zFilename){
char *zFilenameMbcs;
@@ -249,11 +271,11 @@ static char *utf8ToMbcs(const char *zFilename){
return 0;
}
zFilenameMbcs = unicodeToMbcs(zTmpWide);
- sqliteFree(zTmpWide);
+ free(zTmpWide);
return zFilenameMbcs;
}
-#if OS_WINCE
+#if SQLITE_OS_WINCE
/*************************************************************************
** This section contains code for WinCE only.
*/
@@ -267,7 +289,7 @@ struct tm *__cdecl localtime(const time_t *t)
static struct tm y;
FILETIME uTm, lTm;
SYSTEMTIME pTm;
- i64 t64;
+ sqlite3_int64 t64;
t64 = *t;
t64 = (t64 + 11644473600)*10000000;
uTm.dwLowDateTime = t64 & 0xFFFFFFFF;
@@ -329,7 +351,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
/* Create/open the named mutex */
pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
- sqliteFree(zName);
+ free(zName);
return FALSE;
}
@@ -351,7 +373,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
bInit = FALSE;
}
- sqliteFree(zName);
+ free(zName);
/* If we succeeded in making the shared memory handle, map it. */
if (pFile->hShared){
@@ -408,12 +430,6 @@ static void winceDestroyLock(winFile *pFile){
UnmapViewOfFile(pFile->shared);
CloseHandle(pFile->hShared);
- if( pFile->zDeleteOnClose ){
- DeleteFileW(pFile->zDeleteOnClose);
- sqliteFree(pFile->zDeleteOnClose);
- pFile->zDeleteOnClose = 0;
- }
-
/* Done with the mutex */
winceMutexRelease(pFile->hMutex);
CloseHandle(pFile->hMutex);
@@ -563,402 +579,12 @@ static BOOL winceLockFileEx(
/*
** End of the special code for wince
*****************************************************************************/
-#endif /* OS_WINCE */
+#endif /* SQLITE_OS_WINCE */
-/*
-** Convert a UTF-8 filename into whatever form the underlying
-** operating system wants filenames in. Space to hold the result
-** is obtained from sqliteMalloc and must be freed by the calling
-** function.
-*/
-static void *convertUtf8Filename(const char *zFilename){
- void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
- }else{
- zConverted = utf8ToMbcs(zFilename);
- }
- /* caller will handle out of memory */
- return zConverted;
-}
-
-/*
-** Delete the named file.
-**
-** Note that windows does not allow a file to be deleted if some other
-** process has it open. Sometimes a virus scanner or indexing program
-** will open a journal file shortly after it is created in order to do
-** whatever it is it does. While this other process is holding the
-** file open, we will be unable to delete it. To work around this
-** problem, we delay 100 milliseconds and try to delete again. Up
-** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
-** up and returning an error.
-*/
-#define MX_DELETION_ATTEMPTS 3
-int sqlite3WinDelete(const char *zFilename){
- int cnt = 0;
- int rc;
- void *zConverted = convertUtf8Filename(zFilename);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- SimulateIOError(return SQLITE_IOERR_DELETE);
- if( isNT() ){
- do{
- rc = DeleteFileW(zConverted);
- }while( rc==0 && GetFileAttributesW(zConverted)!=0xffffffff
- && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) );
- }else{
-#if OS_WINCE
- return SQLITE_NOMEM;
-#else
- do{
- rc = DeleteFileA(zConverted);
- }while( rc==0 && GetFileAttributesA(zConverted)!=0xffffffff
- && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) );
-#endif
- }
- sqliteFree(zConverted);
- OSTRACE2("DELETE \"%s\"\n", zFilename);
- return rc!=0 ? SQLITE_OK : SQLITE_IOERR;
-}
-
-/*
-** Return TRUE if the named file exists.
-*/
-int sqlite3WinFileExists(const char *zFilename){
- int exists = 0;
- void *zConverted = convertUtf8Filename(zFilename);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- if( isNT() ){
- exists = GetFileAttributesW((WCHAR*)zConverted) != 0xffffffff;
- }else{
-#if OS_WINCE
- return SQLITE_NOMEM;
-#else
- exists = GetFileAttributesA((char*)zConverted) != 0xffffffff;
-#endif
- }
- sqliteFree(zConverted);
- return exists;
-}
-
-/* Forward declaration */
-static int allocateWinFile(winFile *pInit, OsFile **pId);
-
-/*
-** Attempt to open a file for both reading and writing. If that
-** fails, try opening it read-only. If the file does not exist,
-** try to create it.
-**
-** On success, a handle for the open file is written to *id
-** and *pReadonly is set to 0 if the file was opened for reading and
-** writing or 1 if the file was opened read-only. The function returns
-** SQLITE_OK.
-**
-** On failure, the function returns SQLITE_CANTOPEN and leaves
-** *id and *pReadonly unchanged.
-*/
-int sqlite3WinOpenReadWrite(
- const char *zFilename,
- OsFile **pId,
- int *pReadonly
-){
- winFile f;
- HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- assert( *pId==0 );
-
- if( isNT() ){
- h = CreateFileW((WCHAR*)zConverted,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
- if( h==INVALID_HANDLE_VALUE ){
- h = CreateFileW((WCHAR*)zConverted,
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
- if( h==INVALID_HANDLE_VALUE ){
- sqliteFree(zConverted);
- return SQLITE_CANTOPEN;
- }
- *pReadonly = 1;
- }else{
- *pReadonly = 0;
- }
-#if OS_WINCE
- if (!winceCreateLock(zFilename, &f)){
- CloseHandle(h);
- sqliteFree(zConverted);
- return SQLITE_CANTOPEN;
- }
-#endif
- }else{
-#if OS_WINCE
- return SQLITE_NOMEM;
-#else
- h = CreateFileA((char*)zConverted,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
- if( h==INVALID_HANDLE_VALUE ){
- h = CreateFileA((char*)zConverted,
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
- if( h==INVALID_HANDLE_VALUE ){
- sqliteFree(zConverted);
- return SQLITE_CANTOPEN;
- }
- *pReadonly = 1;
- }else{
- *pReadonly = 0;
- }
-#endif /* OS_WINCE */
- }
-
- sqliteFree(zConverted);
-
- f.h = h;
-#if OS_WINCE
- f.zDeleteOnClose = 0;
-#endif
- OSTRACE3("OPEN R/W %d \"%s\"\n", h, zFilename);
- return allocateWinFile(&f, pId);
-}
-
-
-/*
-** Attempt to open a new file for exclusive access by this process.
-** The file will be opened for both reading and writing. To avoid
-** a potential security problem, we do not allow the file to have
-** previously existed. Nor do we allow the file to be a symbolic
-** link.
-**
-** If delFlag is true, then make arrangements to automatically delete
-** the file when it is closed.
-**
-** On success, write the file handle into *id and return SQLITE_OK.
-**
-** On failure, return SQLITE_CANTOPEN.
-**
-** Sometimes if we have just deleted a prior journal file, windows
-** will fail to open a new one because there is a "pending delete".
-** To work around this bug, we pause for 100 milliseconds and attempt
-** a second open after the first one fails. The whole operation only
-** fails if both open attempts are unsuccessful.
-*/
-int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
- winFile f;
- HANDLE h;
- DWORD fileflags;
- void *zConverted = convertUtf8Filename(zFilename);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- assert( *pId == 0 );
- fileflags = FILE_FLAG_RANDOM_ACCESS;
-#if !OS_WINCE
- if( delFlag ){
- fileflags |= FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
- }
-#endif
- if( isNT() ){
- int cnt = 0;
- do{
- h = CreateFileW((WCHAR*)zConverted,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- fileflags,
- NULL
- );
- }while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) );
- }else{
-#if OS_WINCE
- return SQLITE_NOMEM;
-#else
- int cnt = 0;
- do{
- h = CreateFileA((char*)zConverted,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- fileflags,
- NULL
- );
- }while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) );
-#endif /* OS_WINCE */
- }
-#if OS_WINCE
- if( delFlag && h!=INVALID_HANDLE_VALUE ){
- f.zDeleteOnClose = zConverted;
- zConverted = 0;
- }
- f.hMutex = NULL;
-#endif
- sqliteFree(zConverted);
- if( h==INVALID_HANDLE_VALUE ){
- return SQLITE_CANTOPEN;
- }
- f.h = h;
- OSTRACE3("OPEN EX %d \"%s\"\n", h, zFilename);
- return allocateWinFile(&f, pId);
-}
-
-/*
-** Attempt to open a new file for read-only access.
-**
-** On success, write the file handle into *id and return SQLITE_OK.
-**
-** On failure, return SQLITE_CANTOPEN.
-*/
-int sqlite3WinOpenReadOnly(const char *zFilename, OsFile **pId){
- winFile f;
- HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- assert( *pId==0 );
- if( isNT() ){
- h = CreateFileW((WCHAR*)zConverted,
- GENERIC_READ,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
- }else{
-#if OS_WINCE
- return SQLITE_NOMEM;
-#else
- h = CreateFileA((char*)zConverted,
- GENERIC_READ,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
- NULL
- );
-#endif
- }
- sqliteFree(zConverted);
- if( h==INVALID_HANDLE_VALUE ){
- return SQLITE_CANTOPEN;
- }
- f.h = h;
-#if OS_WINCE
- f.zDeleteOnClose = 0;
- f.hMutex = NULL;
-#endif
- OSTRACE3("OPEN RO %d \"%s\"\n", h, zFilename);
- return allocateWinFile(&f, pId);
-}
-
-/*
-** Attempt to open a file descriptor for the directory that contains a
-** file. This file descriptor can be used to fsync() the directory
-** in order to make sure the creation of a new file is actually written
-** to disk.
-**
-** This routine is only meaningful for Unix. It is a no-op under
-** windows since windows does not support hard links.
-**
-** On success, a handle for a previously open file is at *id is
-** updated with the new directory file descriptor and SQLITE_OK is
-** returned.
-**
-** On failure, the function returns SQLITE_CANTOPEN and leaves
-** *id unchanged.
-*/
-static int winOpenDirectory(
- OsFile *id,
- const char *zDirname
-){
- return SQLITE_OK;
-}
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at least SQLITE_TEMPNAME_SIZE characters.
-*/
-int sqlite3WinTempFileName(char *zBuf){
- static char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- char zTempPath[SQLITE_TEMPNAME_SIZE];
- if( sqlite3_temp_directory ){
- strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30);
- zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
- }else if( isNT() ){
- char *zMulti;
- WCHAR zWidePath[SQLITE_TEMPNAME_SIZE];
- GetTempPathW(SQLITE_TEMPNAME_SIZE-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
- if( zMulti ){
- strncpy(zTempPath, zMulti, SQLITE_TEMPNAME_SIZE-30);
- zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
- sqliteFree(zMulti);
- }else{
- return SQLITE_NOMEM;
- }
- }else{
- char *zUtf8;
- char zMbcsPath[SQLITE_TEMPNAME_SIZE];
- GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zMbcsPath);
- zUtf8 = mbcsToUtf8(zMbcsPath);
- if( zUtf8 ){
- strncpy(zTempPath, zUtf8, SQLITE_TEMPNAME_SIZE-30);
- zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
- sqliteFree(zUtf8);
- }else{
- return SQLITE_NOMEM;
- }
- }
- for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
- for(;;){
- sqlite3_snprintf(SQLITE_TEMPNAME_SIZE, zBuf,
- "%s\\"TEMP_FILE_PREFIX, zTempPath);
- j = strlen(zBuf);
- sqlite3Randomness(15, &zBuf[j]);
- for(i=0; i<15; i++, j++){
- zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
- if( !sqlite3OsFileExists(zBuf) ) break;
- }
- OSTRACE2("TEMP FILENAME: %s\n", zBuf);
- return SQLITE_OK;
-}
+/*****************************************************************************
+** The next group of routines implement the I/O methods specified
+** by the sqlite3_io_methods object.
+******************************************************************************/
/*
** Close a file.
@@ -971,36 +597,63 @@ int sqlite3WinTempFileName(char *zBuf){
** giving up and returning an error.
*/
#define MX_CLOSE_ATTEMPT 3
-static int winClose(OsFile **pId){
- winFile *pFile;
- int rc = 1;
- if( pId && (pFile = (winFile*)*pId)!=0 ){
- int rc, cnt = 0;
- OSTRACE2("CLOSE %d\n", pFile->h);
- do{
- rc = CloseHandle(pFile->h);
- }while( rc==0 && cnt++ < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
-#if OS_WINCE
- winceDestroyLock(pFile);
-#endif
- OpenCounter(-1);
- sqliteFree(pFile);
- *pId = 0;
+static int winClose(sqlite3_file *id){
+ int rc, cnt = 0;
+ winFile *pFile = (winFile*)id;
+ OSTRACE2("CLOSE %d\n", pFile->h);
+ do{
+ rc = CloseHandle(pFile->h);
+ }while( rc==0 && cnt++ < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
+#if SQLITE_OS_WINCE
+#define WINCE_DELETION_ATTEMPTS 3
+ winceDestroyLock(pFile);
+ if( pFile->zDeleteOnClose ){
+ int cnt = 0;
+ while(
+ DeleteFileW(pFile->zDeleteOnClose)==0
+ && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
+ && cnt++ < WINCE_DELETION_ATTEMPTS
+ ){
+ Sleep(100); /* Wait a little before trying again */
+ }
+ free(pFile->zDeleteOnClose);
}
+#endif
+ OpenCounter(-1);
return rc ? SQLITE_OK : SQLITE_IOERR;
}
/*
+** Some microsoft compilers lack this definition.
+*/
+#ifndef INVALID_SET_FILE_POINTER
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+/*
** Read data from a file into a buffer. Return SQLITE_OK if all
** bytes were read successfully and SQLITE_IOERR if anything goes
** wrong.
*/
-static int winRead(OsFile *id, void *pBuf, int amt){
+static int winRead(
+ sqlite3_file *id, /* File to read from */
+ void *pBuf, /* Write content into this buffer */
+ int amt, /* Number of bytes to read */
+ sqlite3_int64 offset /* Begin reading at this offset */
+){
+ LONG upperBits = (offset>>32) & 0x7fffffff;
+ LONG lowerBits = offset & 0xffffffff;
+ DWORD rc;
DWORD got;
+ winFile *pFile = (winFile*)id;
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_READ);
- OSTRACE3("READ %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype);
- if( !ReadFile(((winFile*)id)->h, pBuf, amt, &got, 0) ){
+ OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype);
+ rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+ if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){
+ return SQLITE_FULL;
+ }
+ if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){
return SQLITE_IOERR_READ;
}
if( got==(DWORD)amt ){
@@ -1015,16 +668,31 @@ static int winRead(OsFile *id, void *pBuf, int amt){
** Write data from a buffer into a file. Return SQLITE_OK on success
** or some other error code on failure.
*/
-static int winWrite(OsFile *id, const void *pBuf, int amt){
- int rc = 0;
+static int winWrite(
+ sqlite3_file *id, /* File to write into */
+ const void *pBuf, /* The bytes to be written */
+ int amt, /* Number of bytes to write */
+ sqlite3_int64 offset /* Offset into the file to begin writing at */
+){
+ LONG upperBits = (offset>>32) & 0x7fffffff;
+ LONG lowerBits = offset & 0xffffffff;
+ DWORD rc;
DWORD wrote;
+ winFile *pFile = (winFile*)id;
assert( id!=0 );
- SimulateIOError(return SQLITE_IOERR_READ);
+ SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL);
- OSTRACE3("WRITE %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype);
+ OSTRACE3("WRITE %d lock=%d\n", pFile->h, pFile->locktype);
+ rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+ if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){
+ return SQLITE_FULL;
+ }
assert( amt>0 );
- while( amt>0 && (rc = WriteFile(((winFile*)id)->h, pBuf, amt, &wrote, 0))!=0
- && wrote>0 ){
+ while(
+ amt>0
+ && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0
+ && wrote>0
+ ){
amt -= wrote;
pBuf = &((char*)pBuf)[wrote];
}
@@ -1035,38 +703,41 @@ static int winWrite(OsFile *id, const void *pBuf, int amt){
}
/*
-** Some microsoft compilers lack this definition.
+** Truncate an open file to a specified size
*/
-#ifndef INVALID_SET_FILE_POINTER
-# define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#endif
+static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
+ LONG upperBits = (nByte>>32) & 0x7fffffff;
+ LONG lowerBits = nByte & 0xffffffff;
+ winFile *pFile = (winFile*)id;
+ OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte);
+ SimulateIOError(return SQLITE_IOERR_TRUNCATE);
+ SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+ SetEndOfFile(pFile->h);
+ return SQLITE_OK;
+}
+#ifdef SQLITE_TEST
/*
-** Move the read/write pointer in a file.
+** Count the number of fullsyncs and normal syncs. This is used to test
+** that syncs and fullsyncs are occuring at the right times.
*/
-static int winSeek(OsFile *id, i64 offset){
- LONG upperBits = offset>>32;
- LONG lowerBits = offset & 0xffffffff;
- DWORD rc;
- assert( id!=0 );
-#ifdef SQLITE_TEST
- if( offset ) SimulateDiskfullError(return SQLITE_FULL);
+int sqlite3_sync_count = 0;
+int sqlite3_fullsync_count = 0;
#endif
- rc = SetFilePointer(((winFile*)id)->h, lowerBits, &upperBits, FILE_BEGIN);
- OSTRACE3("SEEK %d %lld\n", ((winFile*)id)->h, offset);
- if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){
- return SQLITE_FULL;
- }
- return SQLITE_OK;
-}
/*
** Make sure all writes to a particular file are committed to disk.
*/
-static int winSync(OsFile *id, int dataOnly){
- assert( id!=0 );
- OSTRACE3("SYNC %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype);
- if( FlushFileBuffers(((winFile*)id)->h) ){
+static int winSync(sqlite3_file *id, int flags){
+ winFile *pFile = (winFile*)id;
+ OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);
+#ifdef SQLITE_TEST
+ if( flags & SQLITE_SYNC_FULL ){
+ sqlite3_fullsync_count++;
+ }
+ sqlite3_sync_count++;
+#endif
+ if( FlushFileBuffers(pFile->h) ){
return SQLITE_OK;
}else{
return SQLITE_IOERR;
@@ -1074,36 +745,14 @@ static int winSync(OsFile *id, int dataOnly){
}
/*
-** Sync the directory zDirname. This is a no-op on operating systems other
-** than UNIX.
-*/
-int sqlite3WinSyncDirectory(const char *zDirname){
- SimulateIOError(return SQLITE_IOERR_READ);
- return SQLITE_OK;
-}
-
-/*
-** Truncate an open file to a specified size
-*/
-static int winTruncate(OsFile *id, i64 nByte){
- LONG upperBits = nByte>>32;
- assert( id!=0 );
- OSTRACE3("TRUNCATE %d %lld\n", ((winFile*)id)->h, nByte);
- SimulateIOError(return SQLITE_IOERR_TRUNCATE);
- SetFilePointer(((winFile*)id)->h, nByte, &upperBits, FILE_BEGIN);
- SetEndOfFile(((winFile*)id)->h);
- return SQLITE_OK;
-}
-
-/*
** Determine the current size of a file in bytes
*/
-static int winFileSize(OsFile *id, i64 *pSize){
+static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
+ winFile *pFile = (winFile*)id;
DWORD upperBits, lowerBits;
- assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
- lowerBits = GetFileSize(((winFile*)id)->h, &upperBits);
- *pSize = (((i64)upperBits)<<32) + lowerBits;
+ lowerBits = GetFileSize(pFile->h, &upperBits);
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
return SQLITE_OK;
}
@@ -1119,19 +768,20 @@ static int winFileSize(OsFile *id, i64 *pSize){
** Different API routines are called depending on whether or not this
** is Win95 or WinNT.
*/
-static int getReadLock(winFile *id){
+static int getReadLock(winFile *pFile){
int res;
if( isNT() ){
OVERLAPPED ovlp;
ovlp.Offset = SHARED_FIRST;
ovlp.OffsetHigh = 0;
ovlp.hEvent = 0;
- res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, 0, SHARED_SIZE,0,&ovlp);
+ res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,
+ 0, SHARED_SIZE, 0, &ovlp);
}else{
int lk;
- sqlite3Randomness(sizeof(lk), &lk);
- id->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
- res = LockFile(id->h, SHARED_FIRST+id->sharedLockByte, 0, 1, 0);
+ sqlite3_randomness(sizeof(lk), &lk);
+ pFile->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
+ res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
return res;
}
@@ -1149,39 +799,6 @@ static int unlockReadLock(winFile *pFile){
return res;
}
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-/*
-** Check that a given pathname is a directory and is writable
-**
-*/
-int sqlite3WinIsDirWritable(char *zDirname){
- int fileAttr;
- void *zConverted;
- if( zDirname==0 ) return 0;
- if( !isNT() && strlen(zDirname)>MAX_PATH ) return 0;
-
- zConverted = convertUtf8Filename(zDirname);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- if( isNT() ){
- fileAttr = GetFileAttributesW((WCHAR*)zConverted);
- }else{
-#if OS_WINCE
- return 0;
-#else
- fileAttr = GetFileAttributesA((char*)zConverted);
-#endif
- }
- sqliteFree(zConverted);
- if( fileAttr == 0xffffffff ) return 0;
- if( (fileAttr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ){
- return 0;
- }
- return 1;
-}
-#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
-
/*
** Lock the file with the lock specified by parameter locktype - one
** of the following:
@@ -1208,10 +825,10 @@ int sqlite3WinIsDirWritable(char *zDirname){
** It is not possible to lower the locking level one step at a time. You
** must go straight to locking level 0.
*/
-static int winLock(OsFile *id, int locktype){
+static int winLock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK; /* Return code from subroutines */
int res = 1; /* Result of a windows lock call */
- int newLocktype; /* Set id->locktype to this value before exiting */
+ int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
@@ -1320,7 +937,7 @@ static int winLock(OsFile *id, int locktype){
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.
*/
-static int winCheckReservedLock(OsFile *id){
+static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
int rc;
winFile *pFile = (winFile*)id;
assert( pFile!=0 );
@@ -1335,7 +952,8 @@ static int winCheckReservedLock(OsFile *id){
rc = !rc;
OSTRACE3("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc);
}
- return rc;
+ *pResOut = rc;
+ return SQLITE_OK;
}
/*
@@ -1349,10 +967,10 @@ static int winCheckReservedLock(OsFile *id){
** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
** might return SQLITE_IOERR;
*/
-static int winUnlock(OsFile *id, int locktype){
+static int winUnlock(sqlite3_file *id, int locktype){
int type;
- int rc = SQLITE_OK;
winFile *pFile = (winFile*)id;
+ int rc = SQLITE_OK;
assert( pFile!=0 );
assert( locktype<=SHARED_LOCK );
OSTRACE5("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype,
@@ -1380,75 +998,16 @@ static int winUnlock(OsFile *id, int locktype){
}
/*
-** Turn a relative pathname into a full pathname. Return a pointer
-** to the full pathname stored in space obtained from sqliteMalloc().
-** The calling function is responsible for freeing this space once it
-** is no longer needed.
+** Control and query of the open file handle.
*/
-char *sqlite3WinFullPathname(const char *zRelative){
- char *zFull;
-#if defined(__CYGWIN__)
- int nByte;
- nByte = strlen(zRelative) + MAX_PATH + 1001;
- zFull = sqliteMalloc( nByte );
- if( zFull==0 ) return 0;
- if( cygwin_conv_to_full_win32_path(zRelative, zFull) ) return 0;
-#elif OS_WINCE
- /* WinCE has no concept of a relative pathname, or so I am told. */
- zFull = sqliteStrDup(zRelative);
-#else
- int nByte;
- void *zConverted;
- zConverted = convertUtf8Filename(zRelative);
- if( isNT() ){
- WCHAR *zTemp;
- nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
- zTemp = sqliteMalloc( nByte*sizeof(zTemp[0]) );
- if( zTemp==0 ){
- sqliteFree(zConverted);
- return 0;
- }
- GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
- sqliteFree(zConverted);
- zFull = unicodeToUtf8(zTemp);
- sqliteFree(zTemp);
- }else{
- char *zTemp;
- nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
- zTemp = sqliteMalloc( nByte*sizeof(zTemp[0]) );
- if( zTemp==0 ){
- sqliteFree(zConverted);
- return 0;
+static int winFileControl(sqlite3_file *id, int op, void *pArg){
+ switch( op ){
+ case SQLITE_FCNTL_LOCKSTATE: {
+ *(int*)pArg = ((winFile*)id)->locktype;
+ return SQLITE_OK;
}
- GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
- sqliteFree(zConverted);
- zFull = mbcsToUtf8(zTemp);
- sqliteFree(zTemp);
}
-#endif
- return zFull;
-}
-
-/*
-** The fullSync option is meaningless on windows. This is a no-op.
-*/
-static void winSetFullSync(OsFile *id, int v){
- return;
-}
-
-/*
-** Return the underlying file handle for an OsFile
-*/
-static int winFileHandle(OsFile *id){
- return (int)((winFile*)id)->h;
-}
-
-/*
-** Return an integer that indices the type of lock currently held
-** by this handle. (Used for testing and analysis only.)
-*/
-static int winLockState(OsFile *id){
- return ((winFile*)id)->locktype;
+ return SQLITE_ERROR;
}
/*
@@ -1458,201 +1017,520 @@ static int winLockState(OsFile *id){
**
** SQLite code assumes this function cannot fail. It also assumes that
** if two files are created in the same file-system directory (i.e.
-** a database and it's journal file) that the sector size will be the
+** a database and its journal file) that the sector size will be the
** same for both.
*/
-static int winSectorSize(OsFile *id){
+static int winSectorSize(sqlite3_file *id){
return SQLITE_DEFAULT_SECTOR_SIZE;
}
/*
-** This vector defines all the methods that can operate on an OsFile
-** for win32.
+** Return a vector of device characteristics.
*/
-static const IoMethod sqlite3WinIoMethod = {
+static int winDeviceCharacteristics(sqlite3_file *id){
+ return 0;
+}
+
+/*
+** This vector defines all the methods that can operate on an
+** sqlite3_file for win32.
+*/
+static const sqlite3_io_methods winIoMethod = {
+ 1, /* iVersion */
winClose,
- winOpenDirectory,
winRead,
winWrite,
- winSeek,
winTruncate,
winSync,
- winSetFullSync,
- winFileHandle,
winFileSize,
winLock,
winUnlock,
- winLockState,
winCheckReservedLock,
+ winFileControl,
winSectorSize,
+ winDeviceCharacteristics
};
+/***************************************************************************
+** Here ends the I/O methods that form the sqlite3_io_methods object.
+**
+** The next block of code implements the VFS methods.
+****************************************************************************/
+
/*
-** Allocate memory for an OsFile. Initialize the new OsFile
-** to the value given in pInit and return a pointer to the new
-** OsFile. If we run out of memory, close the file and return NULL.
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in. Space to hold the result
+** is obtained from malloc and must be freed by the calling
+** function.
*/
-static int allocateWinFile(winFile *pInit, OsFile **pId){
- winFile *pNew;
- pNew = sqliteMalloc( sizeof(*pNew) );
- if( pNew==0 ){
- CloseHandle(pInit->h);
-#if OS_WINCE
- sqliteFree(pInit->zDeleteOnClose);
-#endif
- *pId = 0;
- return SQLITE_NOMEM;
+static void *convertUtf8Filename(const char *zFilename){
+ void *zConverted = 0;
+ if( isNT() ){
+ zConverted = utf8ToUnicode(zFilename);
}else{
- *pNew = *pInit;
- pNew->pMethod = &sqlite3WinIoMethod;
- pNew->locktype = NO_LOCK;
- pNew->sharedLockByte = 0;
- *pId = (OsFile*)pNew;
- OpenCounter(+1);
- return SQLITE_OK;
+ zConverted = utf8ToMbcs(zFilename);
}
+ /* caller will handle out of memory */
+ return zConverted;
}
+/*
+** Create a temporary file name in zBuf. zBuf must be big enough to
+** hold at pVfs->mxPathname characters.
+*/
+static int getTempname(int nBuf, char *zBuf){
+ static char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ size_t i, j;
+ char zTempPath[MAX_PATH+1];
+ if( sqlite3_temp_directory ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
+ }else if( isNT() ){
+ char *zMulti;
+ WCHAR zWidePath[MAX_PATH];
+ GetTempPathW(MAX_PATH-30, zWidePath);
+ zMulti = unicodeToUtf8(zWidePath);
+ if( zMulti ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
+ free(zMulti);
+ }else{
+ return SQLITE_NOMEM;
+ }
+ }else{
+ char *zUtf8;
+ char zMbcsPath[MAX_PATH];
+ GetTempPathA(MAX_PATH-30, zMbcsPath);
+ zUtf8 = mbcsToUtf8(zMbcsPath);
+ if( zUtf8 ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
+ free(zUtf8);
+ }else{
+ return SQLITE_NOMEM;
+ }
+ }
+ for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
+ zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-30, zBuf,
+ "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
+ j = strlen(zBuf);
+ sqlite3_randomness(20, &zBuf[j]);
+ for(i=0; i<20; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ OSTRACE2("TEMP FILENAME: %s\n", zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** The return value of getLastErrorMsg
+** is zero if the error message fits in the buffer, or non-zero
+** otherwise (if the message was truncated).
+*/
+static int getLastErrorMsg(int nBuf, char *zBuf){
+ DWORD error = GetLastError();
+
+#if SQLITE_OS_WINCE
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
+#else
+ /* FormatMessage returns 0 on failure. Otherwise it
+ ** returns the number of TCHARs written to the output
+ ** buffer, excluding the terminating null char.
+ */
+ if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ 0,
+ zBuf,
+ nBuf-1,
+ 0))
+ {
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
+ }
+#endif
+
+ return 0;
+}
-#endif /* SQLITE_OMIT_DISKIO */
-/***************************************************************************
-** Everything above deals with file I/O. Everything that follows deals
-** with other miscellanous aspects of the operating system interface
-****************************************************************************/
-#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
+** Open a file.
*/
-void *sqlite3WinDlopen(const char *zFilename){
+static int winOpen(
+ sqlite3_vfs *pVfs, /* Not used */
+ const char *zName, /* Name of the file (UTF-8) */
+ sqlite3_file *id, /* Write the SQLite file handle here */
+ int flags, /* Open mode flags */
+ int *pOutFlags /* Status return flags */
+){
HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
+ DWORD dwDesiredAccess;
+ DWORD dwShareMode;
+ DWORD dwCreationDisposition;
+ DWORD dwFlagsAndAttributes = 0;
+ int isTemp;
+ winFile *pFile = (winFile*)id;
+ void *zConverted; /* Filename in OS encoding */
+ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
+ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
+
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
+ */
+ if( !zUtf8Name ){
+ int rc = getTempname(MAX_PATH+1, zTmpname);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ zUtf8Name = zTmpname;
+ }
+
+ /* Convert the filename to the system encoding. */
+ zConverted = convertUtf8Filename(zUtf8Name);
if( zConverted==0 ){
- return 0;
+ return SQLITE_NOMEM;
+ }
+
+ if( flags & SQLITE_OPEN_READWRITE ){
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+ }else{
+ dwDesiredAccess = GENERIC_READ;
+ }
+ if( flags & SQLITE_OPEN_CREATE ){
+ dwCreationDisposition = OPEN_ALWAYS;
+ }else{
+ dwCreationDisposition = OPEN_EXISTING;
+ }
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ }else{
+ dwShareMode = 0;
+ }
+ if( flags & SQLITE_OPEN_DELETEONCLOSE ){
+#if SQLITE_OS_WINCE
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
+#else
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
+ | FILE_ATTRIBUTE_HIDDEN
+ | FILE_FLAG_DELETE_ON_CLOSE;
+#endif
+ isTemp = 1;
+ }else{
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ isTemp = 0;
}
+ /* Reports from the internet are that performance is always
+ ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */
+ dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
if( isNT() ){
- h = LoadLibraryW((WCHAR*)zConverted);
+ h = CreateFileW((WCHAR*)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL
+ );
}else{
-#if OS_WINCE
- return 0;
+#if SQLITE_OS_WINCE
+ free(zConverted);
+ return SQLITE_NOMEM;
#else
- h = LoadLibraryA((char*)zConverted);
+ h = CreateFileA((char*)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL
+ );
#endif
}
- sqliteFree(zConverted);
- return (void*)h;
-
+ if( h==INVALID_HANDLE_VALUE ){
+ free(zConverted);
+ if( flags & SQLITE_OPEN_READWRITE ){
+ return winOpen(0, zName, id,
+ ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
+ }else{
+ return SQLITE_CANTOPEN;
+ }
+ }
+ if( pOutFlags ){
+ if( flags & SQLITE_OPEN_READWRITE ){
+ *pOutFlags = SQLITE_OPEN_READWRITE;
+ }else{
+ *pOutFlags = SQLITE_OPEN_READONLY;
+ }
+ }
+ memset(pFile, 0, sizeof(*pFile));
+ pFile->pMethod = &winIoMethod;
+ pFile->h = h;
+#if SQLITE_OS_WINCE
+ if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
+ (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
+ && !winceCreateLock(zName, pFile)
+ ){
+ CloseHandle(h);
+ free(zConverted);
+ return SQLITE_CANTOPEN;
+ }
+ if( isTemp ){
+ pFile->zDeleteOnClose = zConverted;
+ }else
+#endif
+ {
+ free(zConverted);
+ }
+ OpenCounter(+1);
+ return SQLITE_OK;
}
-void *sqlite3WinDlsym(void *pHandle, const char *zSymbol){
-#if OS_WINCE
- /* The GetProcAddressA() routine is only available on wince. */
- return GetProcAddressA((HANDLE)pHandle, zSymbol);
+
+/*
+** Delete the named file.
+**
+** Note that windows does not allow a file to be deleted if some other
+** process has it open. Sometimes a virus scanner or indexing program
+** will open a journal file shortly after it is created in order to do
+** whatever it does. While this other process is holding the
+** file open, we will be unable to delete it. To work around this
+** problem, we delay 100 milliseconds and try to delete again. Up
+** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
+** up and returning an error.
+*/
+#define MX_DELETION_ATTEMPTS 5
+static int winDelete(
+ sqlite3_vfs *pVfs, /* Not used on win32 */
+ const char *zFilename, /* Name of file to delete */
+ int syncDir /* Not used on win32 */
+){
+ int cnt = 0;
+ int rc;
+ DWORD error;
+ void *zConverted = convertUtf8Filename(zFilename);
+ if( zConverted==0 ){
+ return SQLITE_NOMEM;
+ }
+ SimulateIOError(return SQLITE_IOERR_DELETE);
+ if( isNT() ){
+ do{
+ DeleteFileW(zConverted);
+ }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
+ || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
+ && (cnt++ < MX_DELETION_ATTEMPTS)
+ && (Sleep(100), 1) );
+ }else{
+#if SQLITE_OS_WINCE
+ free(zConverted);
+ return SQLITE_NOMEM;
#else
- /* All other windows platforms expect GetProcAddress() to take
- ** an Ansi string regardless of the _UNICODE setting */
- return GetProcAddress((HANDLE)pHandle, zSymbol);
+ do{
+ DeleteFileA(zConverted);
+ }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
+ || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
+ && (cnt++ < MX_DELETION_ATTEMPTS)
+ && (Sleep(100), 1) );
#endif
+ }
+ free(zConverted);
+ OSTRACE2("DELETE \"%s\"\n", zFilename);
+ return ( (rc==INVALID_FILE_ATTRIBUTES)
+ && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
}
-int sqlite3WinDlclose(void *pHandle){
- return FreeLibrary((HANDLE)pHandle);
-}
-#endif /* !SQLITE_OMIT_LOAD_EXTENSION */
/*
-** Get information to seed the random number generator. The seed
-** is written into the buffer zBuf[256]. The calling function must
-** supply a sufficiently large buffer.
+** Check the existance and status of a file.
*/
-int sqlite3WinRandomSeed(char *zBuf){
- /* We have to initialize zBuf to prevent valgrind from reporting
- ** errors. The reports issued by valgrind are incorrect - we would
- ** prefer that the randomness be increased by making use of the
- ** uninitialized space in zBuf - but valgrind errors tend to worry
- ** some users. Rather than argue, it seems easier just to initialize
- ** the whole array and silence valgrind, even if that means less randomness
- ** in the random seed.
- **
- ** When testing, initializing zBuf[] to zero is all we do. That means
- ** that we always use the same random number sequence.* This makes the
- ** tests repeatable.
- */
- memset(zBuf, 0, 256);
- GetSystemTime((LPSYSTEMTIME)zBuf);
+static int winAccess(
+ sqlite3_vfs *pVfs, /* Not used on win32 */
+ const char *zFilename, /* Name of file to check */
+ int flags, /* Type of test to make on this file */
+ int *pResOut /* OUT: Result */
+){
+ DWORD attr;
+ int rc;
+ void *zConverted = convertUtf8Filename(zFilename);
+ if( zConverted==0 ){
+ return SQLITE_NOMEM;
+ }
+ if( isNT() ){
+ attr = GetFileAttributesW((WCHAR*)zConverted);
+ }else{
+#if SQLITE_OS_WINCE
+ free(zConverted);
+ return SQLITE_NOMEM;
+#else
+ attr = GetFileAttributesA((char*)zConverted);
+#endif
+ }
+ free(zConverted);
+ switch( flags ){
+ case SQLITE_ACCESS_READ:
+ case SQLITE_ACCESS_EXISTS:
+ rc = attr!=INVALID_FILE_ATTRIBUTES;
+ break;
+ case SQLITE_ACCESS_READWRITE:
+ rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
+ break;
+ default:
+ assert(!"Invalid flags argument");
+ }
+ *pResOut = rc;
return SQLITE_OK;
}
+
/*
-** Sleep for a little while. Return the amount of time slept.
+** Turn a relative pathname into a full pathname. Write the full
+** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
+** bytes in size.
*/
-int sqlite3WinSleep(int ms){
- Sleep(ms);
- return ms;
+static int winFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zRelative, /* Possibly relative input path */
+ int nFull, /* Size of output buffer in bytes */
+ char *zFull /* Output buffer */
+){
+
+#if defined(__CYGWIN__)
+ cygwin_conv_to_full_win32_path(zRelative, zFull);
+ return SQLITE_OK;
+#endif
+
+#if SQLITE_OS_WINCE
+ /* WinCE has no concept of a relative pathname, or so I am told. */
+ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
+ return SQLITE_OK;
+#endif
+
+#if !SQLITE_OS_WINCE && !defined(__CYGWIN__)
+ int nByte;
+ void *zConverted;
+ char *zOut;
+ zConverted = convertUtf8Filename(zRelative);
+ if( isNT() ){
+ WCHAR *zTemp;
+ nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
+ zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ if( zTemp==0 ){
+ free(zConverted);
+ return SQLITE_NOMEM;
+ }
+ GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
+ free(zConverted);
+ zOut = unicodeToUtf8(zTemp);
+ free(zTemp);
+ }else{
+ char *zTemp;
+ nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
+ zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ if( zTemp==0 ){
+ free(zConverted);
+ return SQLITE_NOMEM;
+ }
+ GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
+ free(zConverted);
+ zOut = mbcsToUtf8(zTemp);
+ free(zTemp);
+ }
+ if( zOut ){
+ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
+ free(zOut);
+ return SQLITE_OK;
+ }else{
+ return SQLITE_NOMEM;
+ }
+#endif
}
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
-** Static variables used for thread synchronization
+** Interfaces for opening a shared library, finding entry points
+** within the shared library, and closing the shared library.
*/
-static int inMutex = 0;
-#ifdef SQLITE_W32_THREADS
- static DWORD mutexOwner;
- static CRITICAL_SECTION cs;
-#endif
-
/*
-** The following pair of routines implement mutual exclusion for
-** multi-threaded processes. Only a single thread is allowed to
-** executed code that is surrounded by EnterMutex() and LeaveMutex().
-**
-** SQLite uses only a single Mutex. There is not much critical
-** code and what little there is executes quickly and without blocking.
-**
-** Version 3.3.1 and earlier used a simple mutex. Beginning with
-** version 3.3.2, a recursive mutex is required.
+** Interfaces for opening a shared library, finding entry points
+** within the shared library, and closing the shared library.
*/
-void sqlite3WinEnterMutex(){
-#ifdef SQLITE_W32_THREADS
- static int isInit = 0;
- while( !isInit ){
- static long lock = 0;
- if( InterlockedIncrement(&lock)==1 ){
- InitializeCriticalSection(&cs);
- isInit = 1;
- }else{
- Sleep(1);
- }
+static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
+ HANDLE h;
+ void *zConverted = convertUtf8Filename(zFilename);
+ if( zConverted==0 ){
+ return 0;
}
- EnterCriticalSection(&cs);
- mutexOwner = GetCurrentThreadId();
-#endif
- inMutex++;
+ if( isNT() ){
+ h = LoadLibraryW((WCHAR*)zConverted);
+ }else{
+ h = LoadLibraryA((char*)zConverted);
+ }
+ free(zConverted);
+ return (void*)h;
+}
+static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
+ getLastErrorMsg(nBuf, zBufOut);
}
-void sqlite3WinLeaveMutex(){
- assert( inMutex );
- inMutex--;
-#ifdef SQLITE_W32_THREADS
- assert( mutexOwner==GetCurrentThreadId() );
- LeaveCriticalSection(&cs);
+void *winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+#if SQLITE_OS_WINCE
+ /* The GetProcAddressA() routine is only available on wince. */
+ return GetProcAddressA((HANDLE)pHandle, zSymbol);
+#else
+ /* All other windows platforms expect GetProcAddress() to take
+ ** an Ansi string regardless of the _UNICODE setting */
+ return GetProcAddress((HANDLE)pHandle, zSymbol);
#endif
}
+void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ FreeLibrary((HANDLE)pHandle);
+}
+#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
+ #define winDlOpen 0
+ #define winDlError 0
+ #define winDlSym 0
+ #define winDlClose 0
+#endif
+
/*
-** Return TRUE if the mutex is currently held.
-**
-** If the thisThreadOnly parameter is true, return true if and only if the
-** calling thread holds the mutex. If the parameter is false, return
-** true if any thread holds the mutex.
+** Write up to nBuf bytes of randomness into zBuf.
*/
-int sqlite3WinInMutex(int thisThreadOnly){
-#ifdef SQLITE_W32_THREADS
- return inMutex>0 && (thisThreadOnly==0 || mutexOwner==GetCurrentThreadId());
-#else
- return inMutex>0;
-#endif
+static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ int n = 0;
+ if( sizeof(SYSTEMTIME)<=nBuf-n ){
+ SYSTEMTIME x;
+ GetSystemTime(&x);
+ memcpy(&zBuf[n], &x, sizeof(x));
+ n += sizeof(x);
+ }
+ if( sizeof(DWORD)<=nBuf-n ){
+ DWORD pid = GetCurrentProcessId();
+ memcpy(&zBuf[n], &pid, sizeof(pid));
+ n += sizeof(pid);
+ }
+ if( sizeof(DWORD)<=nBuf-n ){
+ DWORD cnt = GetTickCount();
+ memcpy(&zBuf[n], &cnt, sizeof(cnt));
+ n += sizeof(cnt);
+ }
+ if( sizeof(LARGE_INTEGER)<=nBuf-n ){
+ LARGE_INTEGER i;
+ QueryPerformanceCounter(&i);
+ memcpy(&zBuf[n], &i, sizeof(i));
+ n += sizeof(i);
+ }
+ return n;
}
/*
+** Sleep for a little while. Return the amount of time slept.
+*/
+static int winSleep(sqlite3_vfs *pVfs, int microsec){
+ Sleep((microsec+999)/1000);
+ return ((microsec+999)/1000)*1000;
+}
+
+/*
** The following variable, if set to a non-zero value, becomes the result
** returned from sqlite3OsCurrentTime(). This is used for testing.
*/
@@ -1665,16 +1543,19 @@ int sqlite3_current_time = 0;
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
-int sqlite3WinCurrentTime(double *prNow){
+int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
FILETIME ft;
/* FILETIME structure is a 64-bit value representing the number of
100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
double now;
-#if OS_WINCE
+#if SQLITE_OS_WINCE
SYSTEMTIME time;
GetSystemTime(&time);
- SystemTimeToFileTime(&time,&ft);
+ /* if SystemTimeToFileTime() fails, it returns zero. */
+ if (!SystemTimeToFileTime(&time,&ft)){
+ return 1;
+ }
#else
GetSystemTimeAsFileTime( &ft );
#endif
@@ -1689,70 +1570,69 @@ int sqlite3WinCurrentTime(double *prNow){
}
/*
-** Remember the number of thread-specific-data blocks allocated.
-** Use this to verify that we are not leaking thread-specific-data.
-** Ticket #1601
-*/
-#ifdef SQLITE_TEST
-int sqlite3_tsd_count = 0;
-# define TSD_COUNTER_INCR InterlockedIncrement(&sqlite3_tsd_count)
-# define TSD_COUNTER_DECR InterlockedDecrement(&sqlite3_tsd_count)
-#else
-# define TSD_COUNTER_INCR /* no-op */
-# define TSD_COUNTER_DECR /* no-op */
-#endif
-
-
-
-/*
-** If called with allocateFlag>1, then return a pointer to thread
-** specific data for the current thread. Allocate and zero the
-** thread-specific data if it does not already exist necessary.
+** The idea is that this function works like a combination of
+** GetLastError() and FormatMessage() on windows (or errno and
+** strerror_r() on unix). After an error is returned by an OS
+** function, SQLite calls this function with zBuf pointing to
+** a buffer of nBuf bytes. The OS layer should populate the
+** buffer with a nul-terminated UTF-8 encoded error message
+** describing the last IO error to have occured within the calling
+** thread.
**
-** If called with allocateFlag==0, then check the current thread
-** specific data. Return it if it exists. If it does not exist,
-** then return NULL.
+** If the error message is too large for the supplied buffer,
+** it should be truncated. The return value of xGetLastError
+** is zero if the error message fits in the buffer, or non-zero
+** otherwise (if the message was truncated). If non-zero is returned,
+** then it is not necessary to include the nul-terminator character
+** in the output buffer.
**
-** If called with allocateFlag<0, check to see if the thread specific
-** data is allocated and is all zero. If it is then deallocate it.
-** Return a pointer to the thread specific data or NULL if it is
-** unallocated or gets deallocated.
+** Not supplying an error message will have no adverse effect
+** on SQLite. It is fine to have an implementation that never
+** returns an error message:
+**
+** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+** assert(zBuf[0]=='\0');
+** return 0;
+** }
+**
+** However if an error message is supplied, it will be incorporated
+** by sqlite into the error message available to the user using
+** sqlite3_errmsg(), possibly making IO errors easier to debug.
*/
-ThreadData *sqlite3WinThreadSpecificData(int allocateFlag){
- static int key;
- static int keyInit = 0;
- static const ThreadData zeroData = {0};
- ThreadData *pTsd;
-
- if( !keyInit ){
- sqlite3OsEnterMutex();
- if( !keyInit ){
- key = TlsAlloc();
- if( key==0xffffffff ){
- sqlite3OsLeaveMutex();
- return 0;
- }
- keyInit = 1;
- }
- sqlite3OsLeaveMutex();
- }
- pTsd = TlsGetValue(key);
- if( allocateFlag>0 ){
- if( !pTsd ){
- pTsd = sqlite3OsMalloc( sizeof(zeroData) );
- if( pTsd ){
- *pTsd = zeroData;
- TlsSetValue(key, pTsd);
- TSD_COUNTER_INCR;
- }
- }
- }else if( pTsd!=0 && allocateFlag<0
- && memcmp(pTsd, &zeroData, sizeof(ThreadData))==0 ){
- sqlite3OsFree(pTsd);
- TlsSetValue(key, 0);
- TSD_COUNTER_DECR;
- pTsd = 0;
- }
- return pTsd;
+static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ return getLastErrorMsg(nBuf, zBuf);
}
-#endif /* OS_WIN */
+
+/*
+** Initialize and deinitialize the operating system interface.
+*/
+int sqlite3_os_init(void){
+ static sqlite3_vfs winVfs = {
+ 1, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ MAX_PATH, /* mxPathname */
+ 0, /* pNext */
+ "win32", /* zName */
+ 0, /* pAppData */
+
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError /* xGetLastError */
+ };
+ sqlite3_vfs_register(&winVfs, 1);
+ return SQLITE_OK;
+}
+int sqlite3_os_end(void){
+ return SQLITE_OK;
+}
+
+#endif /* SQLITE_OS_WIN */
diff --git a/third_party/sqlite/pager.c b/third_party/sqlite/src/pager.c
index bd700c2..b9f50a4 100644..100755
--- a/third_party/sqlite/pager.c
+++ b/third_party/sqlite/src/pager.c
@@ -18,12 +18,10 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
-** @(#) $Id: pager.c,v 1.355 2007/08/11 00:26:21 drh Exp $
+** @(#) $Id: pager.c,v 1.469 2008/08/02 03:50:39 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
-#include "os.h"
-#include "pager.h"
#include <assert.h>
#include <string.h>
@@ -49,9 +47,9 @@
** The following two macros are used within the PAGERTRACEX() macros above
** to print out file-descriptors.
**
-** PAGERID() takes a pointer to a Pager struct as it's argument. The
-** associated file-descriptor is returned. FILEHANDLEID() takes an OsFile
-** struct as it's argument.
+** PAGERID() takes a pointer to a Pager struct as its argument. The
+** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
+** struct as its argument.
*/
#define PAGERID(p) ((int)(p->fd))
#define FILEHANDLEID(fd) ((int)fd)
@@ -133,6 +131,40 @@
*/
#define FORCE_ALIGNMENT(X) (((X)+7)&~7)
+typedef struct PgHdr PgHdr;
+
+/*
+** Each pager stores all currently unreferenced pages in a list sorted
+** in least-recently-used (LRU) order (i.e. the first item on the list has
+** not been referenced in a long time, the last item has been recently
+** used). An instance of this structure is included as part of each
+** pager structure for this purpose (variable Pager.lru).
+**
+** Additionally, if memory-management is enabled, all unreferenced pages
+** are stored in a global LRU list (global variable sqlite3LruPageList).
+**
+** In both cases, the PagerLruList.pFirstSynced variable points to
+** the first page in the corresponding list that does not require an
+** fsync() operation before its memory can be reclaimed. If no such
+** page exists, PagerLruList.pFirstSynced is set to NULL.
+*/
+typedef struct PagerLruList PagerLruList;
+struct PagerLruList {
+ PgHdr *pFirst; /* First page in LRU list */
+ PgHdr *pLast; /* Last page in LRU list (the most recently used) */
+ PgHdr *pFirstSynced; /* First page in list with PgHdr.needSync==0 */
+};
+
+/*
+** The following structure contains the next and previous pointers used
+** to link a PgHdr structure into a PagerLruList linked list.
+*/
+typedef struct PagerLruLink PagerLruLink;
+struct PagerLruLink {
+ PgHdr *pNext;
+ PgHdr *pPrev;
+};
+
/*
** Each in-memory image of a page begins with the following header.
** This header is only visible to this pager module. The client
@@ -181,9 +213,9 @@
** has been synced to disk. For pages that are in the original
** database file, the following expression should always be true:
**
-** inJournal = (pPager->aInJournal[(pgno-1)/8] & (1<<((pgno-1)%8))!=0
+** inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno)
**
-** The pPager->aInJournal[] array is only valid for the original
+** The pPager->pInJournal object is only valid for the original
** pages of the database, not new pages that are added to the end
** of the database, so obviously the above expression cannot be
** valid for new pages. For new pages inJournal is always 0.
@@ -223,12 +255,11 @@
** content is needed in the future, it should be read from the
** original database file.
*/
-typedef struct PgHdr PgHdr;
struct PgHdr {
Pager *pPager; /* The pager to which this page belongs */
Pgno pgno; /* The page number for this page */
PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
- PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */
+ PagerLruLink free; /* Next and previous free pages */
PgHdr *pNextAll; /* A list of all pages */
u8 inJournal; /* TRUE if has been written to journal */
u8 dirty; /* TRUE if we need to write back changes */
@@ -237,12 +268,15 @@ struct PgHdr {
u8 needRead; /* Read content if PagerWrite() is called */
short int nRef; /* Number of users of this page */
PgHdr *pDirty, *pPrevDirty; /* Dirty pages */
- u32 notUsed; /* Buffer space */
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ PgHdr *pPrevAll; /* A list of all pages */
+ PagerLruLink gfree; /* Global list of nRef==0 pages */
+#endif
#ifdef SQLITE_CHECK_PAGES
u32 pageHash;
#endif
- /* pPager->pageSize bytes of page data follow this header */
- /* Pager.nExtra bytes of local data follow the page data */
+ void *pData; /* Page data */
+ /* Pager.nExtra bytes of local data appended to this header */
};
/*
@@ -279,11 +313,10 @@ struct PgHistory {
** Convert a pointer to a PgHdr into a pointer to its data
** and back again.
*/
-#define PGHDR_TO_DATA(P) ((void*)(&(P)[1]))
-#define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1])
-#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->pageSize])
+#define PGHDR_TO_DATA(P) ((P)->pData)
+#define PGHDR_TO_EXTRA(G,P) ((void*)&((G)[1]))
#define PGHDR_TO_HIST(P,PGR) \
- ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra])
+ ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->nExtra])
/*
** A open page cache is an instance of the following structure.
@@ -297,6 +330,7 @@ struct PgHistory {
** APIs, they may still be used successfully.
*/
struct Pager {
+ sqlite3_vfs *pVfs; /* OS functions to use for IO */
u8 journalOpen; /* True if journal file descriptors is valid */
u8 journalStarted; /* True if header of journal is synced */
u8 useJournal; /* Use a rollback journal on this file */
@@ -306,7 +340,7 @@ struct Pager {
u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
- u8 full_fsync; /* Use F_FULLFSYNC when available */
+ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
@@ -317,7 +351,10 @@ struct Pager {
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSync; /* Boolean. While true, do not spill the cache */
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
+ u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */
+ u8 dbModified; /* True if there are any changes to the Db */
u8 changeCountDone; /* Set after incrementing the change-counter */
+ u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
int errCode; /* One of several kinds of errors */
int dbSize; /* Number of pages in the file */
int origDbSize; /* dbSize before the current change */
@@ -331,16 +368,15 @@ struct Pager {
int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
int mxPage; /* Maximum number of pages to hold in cache */
Pgno mxPgno; /* Maximum allowed size of the database */
- u8 *aInJournal; /* One bit for each page in the database file */
- u8 *aInStmt; /* One bit for each page in the database */
+ Bitvec *pInJournal; /* One bit for each page in the database file */
+ Bitvec *pInStmt; /* One bit for each page in the database */
char *zFilename; /* Name of the database file */
char *zJournal; /* Name of the journal file */
char *zDirectory; /* Directory hold database and journal files */
- OsFile *fd, *jfd; /* File descriptors for database and journal */
- OsFile *stfd; /* File descriptor for the statement subjournal*/
+ sqlite3_file *fd, *jfd; /* File descriptors for database and journal */
+ sqlite3_file *stfd; /* File descriptor for the statement subjournal*/
BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */
- PgHdr *pFirst, *pLast; /* List of free pages */
- PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */
+ PagerLruList lru; /* LRU list of free pages */
PgHdr *pAll; /* List of all pages */
PgHdr *pStmt; /* List of pages in the statement subjournal */
PgHdr *pDirty; /* List of all dirty pages */
@@ -363,10 +399,14 @@ struct Pager {
int nHash; /* Size of the pager hash table */
PgHdr **aHash; /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- Pager *pNext; /* Linked list of pagers in this thread */
+ Pager *pNext; /* Doubly linked list of pagers on which */
+ Pager *pPrev; /* sqlite3_release_memory() will work */
+ volatile int iInUseMM; /* Non-zero if unavailable to MM */
+ volatile int iInUseDB; /* Non-zero if in sqlite3_release_memory() */
#endif
char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
char dbFileVers[16]; /* Changes whenever database file changes */
+ i64 journalSizeLimit; /* Size limit for persistent journal files */
};
/*
@@ -384,6 +424,16 @@ int sqlite3_pager_pgfree_count = 0; /* Number of cache pages freed */
# define PAGER_INCR(v)
#endif
+/*
+** The following variable points to the head of a double-linked list
+** of all pagers that are eligible for page stealing by the
+** sqlite3_release_memory() interface. Access to this list is
+** protected by the SQLITE_MUTEX_STATIC_MEM2 mutex.
+*/
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+static Pager *sqlite3PagerList = 0;
+static PagerLruList sqlite3LruPageList = {0, 0, 0};
+#endif
/*
@@ -453,6 +503,7 @@ static const unsigned char aJournalMagic[] = {
*/
#define PAGER_MAX_PGNO 2147483647
+/* Begin preload-cache.patch for Chromium */
/* See comments above the definition. */
int sqlite3PagerAcquire2(
Pager *pPager,
@@ -460,26 +511,166 @@ int sqlite3PagerAcquire2(
DbPage **ppPage,
int noContent,
unsigned char *pDataToFill);
+/* End preload-cache.patch for Chromium */
/*
-** Enable reference count tracking (for debugging) here:
+** The pagerEnter() and pagerLeave() routines acquire and release
+** a mutex on each pager. The mutex is recursive.
+**
+** This is a special-purpose mutex. It only provides mutual exclusion
+** between the Btree and the Memory Management sqlite3_release_memory()
+** function. It does not prevent, for example, two Btrees from accessing
+** the same pager at the same time. Other general-purpose mutexes in
+** the btree layer handle that chore.
*/
-#ifdef SQLITE_DEBUG
- int pager3_refinfo_enable = 0;
- static void pager_refinfo(PgHdr *p){
- static int cnt = 0;
- if( !pager3_refinfo_enable ) return;
- sqlite3DebugPrintf(
- "REFCNT: %4d addr=%p nRef=%-3d total=%d\n",
- p->pgno, PGHDR_TO_DATA(p), p->nRef, p->pPager->nRef
- );
- cnt++; /* Something to set a breakpoint on */
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ static void pagerEnter(Pager *p){
+ p->iInUseDB++;
+ if( p->iInUseMM && p->iInUseDB==1 ){
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex;
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
+#endif
+ p->iInUseDB = 0;
+ sqlite3_mutex_enter(mutex);
+ p->iInUseDB = 1;
+ sqlite3_mutex_leave(mutex);
+ }
+ assert( p->iInUseMM==0 );
+ }
+ static void pagerLeave(Pager *p){
+ p->iInUseDB--;
+ assert( p->iInUseDB>=0 );
}
-# define REFINFO(X) pager_refinfo(X)
#else
-# define REFINFO(X)
+# define pagerEnter(X)
+# define pagerLeave(X)
+#endif
+
+/*
+** Add page pPg to the end of the linked list managed by structure
+** pList (pPg becomes the last entry in the list - the most recently
+** used). Argument pLink should point to either pPg->free or pPg->gfree,
+** depending on whether pPg is being added to the pager-specific or
+** global LRU list.
+*/
+static void listAdd(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){
+ pLink->pNext = 0;
+ pLink->pPrev = pList->pLast;
+
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ assert(pLink==&pPg->free || pLink==&pPg->gfree);
+ assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);
#endif
+ if( pList->pLast ){
+ int iOff = (char *)pLink - (char *)pPg;
+ PagerLruLink *pLastLink = (PagerLruLink *)(&((u8 *)pList->pLast)[iOff]);
+ pLastLink->pNext = pPg;
+ }else{
+ assert(!pList->pFirst);
+ pList->pFirst = pPg;
+ }
+
+ pList->pLast = pPg;
+ if( !pList->pFirstSynced && pPg->needSync==0 ){
+ pList->pFirstSynced = pPg;
+ }
+}
+
+/*
+** Remove pPg from the list managed by the structure pointed to by pList.
+**
+** Argument pLink should point to either pPg->free or pPg->gfree, depending
+** on whether pPg is being added to the pager-specific or global LRU list.
+*/
+static void listRemove(PagerLruList *pList, PagerLruLink *pLink, PgHdr *pPg){
+ int iOff = (char *)pLink - (char *)pPg;
+
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ assert(pLink==&pPg->free || pLink==&pPg->gfree);
+ assert(pLink==&pPg->gfree || pList!=&sqlite3LruPageList);
+#endif
+
+ if( pPg==pList->pFirst ){
+ pList->pFirst = pLink->pNext;
+ }
+ if( pPg==pList->pLast ){
+ pList->pLast = pLink->pPrev;
+ }
+ if( pLink->pPrev ){
+ PagerLruLink *pPrevLink = (PagerLruLink *)(&((u8 *)pLink->pPrev)[iOff]);
+ pPrevLink->pNext = pLink->pNext;
+ }
+ if( pLink->pNext ){
+ PagerLruLink *pNextLink = (PagerLruLink *)(&((u8 *)pLink->pNext)[iOff]);
+ pNextLink->pPrev = pLink->pPrev;
+ }
+ if( pPg==pList->pFirstSynced ){
+ PgHdr *p = pLink->pNext;
+ while( p && p->needSync ){
+ PagerLruLink *pL = (PagerLruLink *)(&((u8 *)p)[iOff]);
+ p = pL->pNext;
+ }
+ pList->pFirstSynced = p;
+ }
+
+ pLink->pNext = pLink->pPrev = 0;
+}
+
+/*
+** Add page pPg to the list of free pages for the pager. If
+** memory-management is enabled, also add the page to the global
+** list of free pages.
+*/
+static void lruListAdd(PgHdr *pPg){
+ listAdd(&pPg->pPager->lru, &pPg->free, pPg);
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ if( !pPg->pPager->memDb ){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ listAdd(&sqlite3LruPageList, &pPg->gfree, pPg);
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ }
+#endif
+}
+
+/*
+** Remove page pPg from the list of free pages for the associated pager.
+** If memory-management is enabled, also remove pPg from the global list
+** of free pages.
+*/
+static void lruListRemove(PgHdr *pPg){
+ listRemove(&pPg->pPager->lru, &pPg->free, pPg);
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ if( !pPg->pPager->memDb ){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ listRemove(&sqlite3LruPageList, &pPg->gfree, pPg);
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ }
+#endif
+}
+
+/*
+** This function is called just after the needSync flag has been cleared
+** from all pages managed by pPager (usually because the journal file
+** has just been synced). It updates the pPager->lru.pFirstSynced variable
+** and, if memory-management is enabled, the sqlite3LruPageList.pFirstSynced
+** variable also.
+*/
+static void lruListSetFirstSynced(Pager *pPager){
+ pPager->lru.pFirstSynced = pPager->lru.pFirst;
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ if( !pPager->memDb ){
+ PgHdr *p;
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ for(p=sqlite3LruPageList.pFirst; p && p->needSync; p=p->gfree.pNext);
+ assert(p==pPager->lru.pFirstSynced || p==sqlite3LruPageList.pFirstSynced);
+ sqlite3LruPageList.pFirstSynced = p;
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ }
+#endif
+}
+
/*
** Return true if page *pPg has already been written to the statement
** journal (or statement snapshot has been created, if *pPg is part
@@ -490,9 +681,7 @@ static int pageInStatement(PgHdr *pPg){
if( MEMDB ){
return PGHDR_TO_HIST(pPg, pPager)->inStmt;
}else{
- Pgno pgno = pPg->pgno;
- u8 *a = pPager->aInStmt;
- return (a && (int)pgno<=pPager->stmtSize && (a[pgno/8] & (1<<(pgno&7))));
+ return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
}
}
@@ -503,12 +692,22 @@ static int pageInStatement(PgHdr *pPg){
static void pager_resize_hash_table(Pager *pPager, int N){
PgHdr **aHash, *pPg;
assert( N>0 && (N&(N-1))==0 );
- aHash = sqliteMalloc( sizeof(aHash[0])*N );
+#ifdef SQLITE_MALLOC_SOFT_LIMIT
+ if( N*sizeof(aHash[0])>SQLITE_MALLOC_SOFT_LIMIT ){
+ N = SQLITE_MALLOC_SOFT_LIMIT/sizeof(aHash[0]);
+ }
+ if( N==pPager->nHash ) return;
+#endif
+ pagerLeave(pPager);
+ if( pPager->aHash!=0 ) sqlite3BeginBenignMalloc();
+ aHash = sqlite3MallocZero( sizeof(aHash[0])*N );
+ if( pPager->aHash!=0 ) sqlite3EndBenignMalloc();
+ pagerEnter(pPager);
if( aHash==0 ){
/* Failure to rehash is not an error. It is only a performance hit. */
return;
}
- sqliteFree(pPager->aHash);
+ sqlite3_free(pPager->aHash);
pPager->nHash = N;
pPager->aHash = aHash;
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
@@ -534,9 +733,9 @@ static void pager_resize_hash_table(Pager *pPager, int N){
**
** All values are stored on disk as big-endian.
*/
-static int read32bits(OsFile *fd, u32 *pRes){
+static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){
unsigned char ac[4];
- int rc = sqlite3OsRead(fd, ac, sizeof(ac));
+ int rc = sqlite3OsRead(fd, ac, sizeof(ac), offset);
if( rc==SQLITE_OK ){
*pRes = sqlite3Get4byte(ac);
}
@@ -552,22 +751,77 @@ static int read32bits(OsFile *fd, u32 *pRes){
** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
** on success or an error code is something goes wrong.
*/
-static int write32bits(OsFile *fd, u32 val){
+static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
char ac[4];
put32bits(ac, val);
- return sqlite3OsWrite(fd, ac, 4);
+ return sqlite3OsWrite(fd, ac, 4, offset);
+}
+
+/*
+** If file pFd is open, call sqlite3OsUnlock() on it.
+*/
+static int osUnlock(sqlite3_file *pFd, int eLock){
+ if( !pFd->pMethods ){
+ return SQLITE_OK;
+ }
+ return sqlite3OsUnlock(pFd, eLock);
}
/*
+** This function determines whether or not the atomic-write optimization
+** can be used with this pager. The optimization can be used if:
+**
+** (a) the value returned by OsDeviceCharacteristics() indicates that
+** a database page may be written atomically, and
+** (b) the value returned by OsSectorSize() is less than or equal
+** to the page size.
+**
+** If the optimization cannot be used, 0 is returned. If it can be used,
+** then the value returned is the size of the journal file when it
+** contains rollback data for exactly one page.
+*/
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+static int jrnlBufferSize(Pager *pPager){
+ int dc; /* Device characteristics */
+ int nSector; /* Sector size */
+ int szPage; /* Page size */
+ sqlite3_file *fd = pPager->fd;
+
+ if( fd->pMethods ){
+ dc = sqlite3OsDeviceCharacteristics(fd);
+ nSector = sqlite3OsSectorSize(fd);
+ szPage = pPager->pageSize;
+ }
+
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+
+ if( !fd->pMethods ||
+ (dc & (SQLITE_IOCAP_ATOMIC|(szPage>>8)) && nSector<=szPage) ){
+ return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
+ }
+ return 0;
+}
+#endif
+
+/*
** This function should be called when an error occurs within the pager
** code. The first argument is a pointer to the pager structure, the
** second the error-code about to be returned by a pager API function.
** The value returned is a copy of the second argument to this function.
**
** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL
-** the error becomes persistent. All subsequent API calls on this Pager
-** will immediately return the same error code.
+** the error becomes persistent. Until the persisten error is cleared,
+** subsequent API calls on this Pager will immediately return the same
+** error code.
+**
+** A persistent error indicates that the contents of the pager-cache
+** cannot be trusted. This state can be cleared by completely discarding
+** the contents of the pager-cache. If a transaction was active when
+** the persistent error occured, then the rollback journal may need
+** to be replayed.
*/
+static void pager_unlock(Pager *pPager);
static int pager_error(Pager *pPager, int rc){
int rc2 = rc & 0xff;
assert(
@@ -581,6 +835,13 @@ static int pager_error(Pager *pPager, int rc){
rc2==SQLITE_CORRUPT
){
pPager->errCode = rc;
+ if( pPager->state==PAGER_UNLOCK && pPager->nRef==0 ){
+ /* If the pager is already unlocked, call pager_unlock() now to
+ ** clear the error state and ensure that the pager-cache is
+ ** completely empty.
+ */
+ pager_unlock(pPager);
+ }
}
return rc;
}
@@ -628,66 +889,61 @@ static void checkPage(PgHdr *pPg){
/*
** When this is called the journal file for pager pPager must be open.
** The master journal file name is read from the end of the file and
-** written into memory obtained from sqliteMalloc(). *pzMaster is
-** set to point at the memory and SQLITE_OK returned. The caller must
-** sqliteFree() *pzMaster.
+** written into memory supplied by the caller.
+**
+** zMaster must point to a buffer of at least nMaster bytes allocated by
+** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is
+** enough space to write the master journal name). If the master journal
+** name in the journal is longer than nMaster bytes (including a
+** nul-terminator), then this is handled as if no master journal name
+** were present in the journal.
**
-** If no master journal file name is present *pzMaster is set to 0 and
+** If no master journal file name is present zMaster[0] is set to 0 and
** SQLITE_OK returned.
*/
-static int readMasterJournal(OsFile *pJrnl, char **pzMaster){
+static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, int nMaster){
int rc;
u32 len;
i64 szJ;
u32 cksum;
- int i;
+ u32 u; /* Unsigned loop counter */
unsigned char aMagic[8]; /* A buffer to hold the magic header */
- *pzMaster = 0;
+ zMaster[0] = '\0';
rc = sqlite3OsFileSize(pJrnl, &szJ);
if( rc!=SQLITE_OK || szJ<16 ) return rc;
- rc = sqlite3OsSeek(pJrnl, szJ-16);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = read32bits(pJrnl, &len);
+ rc = read32bits(pJrnl, szJ-16, &len);
if( rc!=SQLITE_OK ) return rc;
- rc = read32bits(pJrnl, &cksum);
+ if( len>=nMaster ){
+ return SQLITE_OK;
+ }
+
+ rc = read32bits(pJrnl, szJ-12, &cksum);
if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3OsRead(pJrnl, aMagic, 8);
+ rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8);
if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;
- rc = sqlite3OsSeek(pJrnl, szJ-16-len);
- if( rc!=SQLITE_OK ) return rc;
-
- *pzMaster = (char *)sqliteMalloc(len+1);
- if( !*pzMaster ){
- return SQLITE_NOMEM;
- }
- rc = sqlite3OsRead(pJrnl, *pzMaster, len);
+ rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len);
if( rc!=SQLITE_OK ){
- sqliteFree(*pzMaster);
- *pzMaster = 0;
return rc;
}
+ zMaster[len] = '\0';
/* See if the checksum matches the master journal name */
- for(i=0; i<len; i++){
- cksum -= (*pzMaster)[i];
- }
+ for(u=0; u<len; u++){
+ cksum -= zMaster[u];
+ }
if( cksum ){
/* If the checksum doesn't add up, then one or more of the disk sectors
** containing the master journal filename is corrupted. This means
** definitely roll back, so just return SQLITE_OK and report a (nul)
** master-journal filename.
*/
- sqliteFree(*pzMaster);
- *pzMaster = 0;
- }else{
- (*pzMaster)[len] = '\0';
+ zMaster[0] = '\0';
}
return SQLITE_OK;
@@ -708,7 +964,7 @@ static int readMasterJournal(OsFile *pJrnl, char **pzMaster){
** 2000 2048
**
*/
-static int seekJournalHdr(Pager *pPager){
+static void seekJournalHdr(Pager *pPager){
i64 offset = 0;
i64 c = pPager->journalOff;
if( c ){
@@ -718,7 +974,45 @@ static int seekJournalHdr(Pager *pPager){
assert( offset>=c );
assert( (offset-c)<JOURNAL_HDR_SZ(pPager) );
pPager->journalOff = offset;
- return sqlite3OsSeek(pPager->jfd, pPager->journalOff);
+}
+
+/*
+** Write zeros over the header of the journal file. This has the
+** effect of invalidating the journal file and committing the
+** transaction.
+*/
+static int zeroJournalHdr(Pager *pPager, int doTruncate){
+ int rc = SQLITE_OK;
+ static const char zeroHdr[28];
+
+ if( pPager->journalOff ){
+ i64 iLimit = pPager->journalSizeLimit;
+
+ IOTRACE(("JZEROHDR %p\n", pPager))
+ if( doTruncate || iLimit==0 ){
+ rc = sqlite3OsTruncate(pPager->jfd, 0);
+ }else{
+ rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
+ }
+ if( rc==SQLITE_OK && !pPager->noSync ){
+ rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
+ }
+
+ /* At this point the transaction is committed but the write lock
+ ** is still held on the file. If there is a size limit configured for
+ ** the persistent journal and the journal file currently consumes more
+ ** space than that limit allows for, truncate it now. There is no need
+ ** to sync the file following this operation.
+ */
+ if( rc==SQLITE_OK && iLimit>0 ){
+ i64 sz;
+ rc = sqlite3OsFileSize(pPager->jfd, &sz);
+ if( rc==SQLITE_OK && sz>iLimit ){
+ rc = sqlite3OsTruncate(pPager->jfd, iLimit);
+ }
+ }
+ }
+ return rc;
}
/*
@@ -732,54 +1026,76 @@ static int seekJournalHdr(Pager *pPager){
** - 4 bytes: Random number used for page hash.
** - 4 bytes: Initial database page count.
** - 4 bytes: Sector size used by the process that wrote this journal.
+** - 4 bytes: Database page size.
**
-** Followed by (JOURNAL_HDR_SZ - 24) bytes of unused space.
+** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
- char zHeader[sizeof(aJournalMagic)+16];
- int rc;
+ int rc = SQLITE_OK;
+ char *zHeader = pPager->pTmpSpace;
+ int nHeader = pPager->pageSize;
+ int nWrite;
+
+ if( nHeader>JOURNAL_HDR_SZ(pPager) ){
+ nHeader = JOURNAL_HDR_SZ(pPager);
+ }
if( pPager->stmtHdrOff==0 ){
pPager->stmtHdrOff = pPager->journalOff;
}
- rc = seekJournalHdr(pPager);
- if( rc ) return rc;
-
+ seekJournalHdr(pPager);
pPager->journalHdr = pPager->journalOff;
- pPager->journalOff += JOURNAL_HDR_SZ(pPager);
- /* FIX ME:
+ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
+
+ /*
+ ** Write the nRec Field - the number of page records that follow this
+ ** journal header. Normally, zero is written to this value at this time.
+ ** After the records are added to the journal (and the journal synced,
+ ** if in full-sync mode), the zero is overwritten with the true number
+ ** of records (see syncJournal()).
**
- ** Possibly for a pager not in no-sync mode, the journal magic should not
- ** be written until nRec is filled in as part of next syncJournal().
+ ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
+ ** reading the journal this value tells SQLite to assume that the
+ ** rest of the journal file contains valid page records. This assumption
+ ** is dangerous, as if a failure occured whilst writing to the journal
+ ** file it may contain some garbage data. There are two scenarios
+ ** where this risk can be ignored:
**
- ** Actually maybe the whole journal header should be delayed until that
- ** point. Think about this.
+ ** * When the pager is in no-sync mode. Corruption can follow a
+ ** power failure in this case anyway.
+ **
+ ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
+ ** that garbage data is never appended to the journal file.
*/
- memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
- /* The nRec Field. 0xFFFFFFFF for no-sync journals. */
- put32bits(&zHeader[sizeof(aJournalMagic)], pPager->noSync ? 0xffffffff : 0);
+ assert(pPager->fd->pMethods||pPager->noSync);
+ if( (pPager->noSync)
+ || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
+ ){
+ put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
+ }else{
+ put32bits(&zHeader[sizeof(aJournalMagic)], 0);
+ }
+
/* The random check-hash initialiser */
- sqlite3Randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
/* The initial database size */
put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbSize);
/* The assumed sector size for this process */
put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize);
- IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, sizeof(zHeader)))
- rc = sqlite3OsWrite(pPager->jfd, zHeader, sizeof(zHeader));
+ if( pPager->journalHdr==0 ){
+ /* The page size */
+ put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize);
+ }
- /* The journal header has been written successfully. Seek the journal
- ** file descriptor to the end of the journal header sector.
- */
- if( rc==SQLITE_OK ){
- IOTRACE(("JTAIL %p %lld\n", pPager, pPager->journalOff-1))
- rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff-1);
- if( rc==SQLITE_OK ){
- rc = sqlite3OsWrite(pPager->jfd, "\000", 1);
- }
+ for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){
+ IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader))
+ rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff);
+ pPager->journalOff += nHeader;
}
+
return rc;
}
@@ -807,28 +1123,41 @@ static int readJournalHdr(
){
int rc;
unsigned char aMagic[8]; /* A buffer to hold the magic header */
+ i64 jrnlOff;
+ int iPageSize;
- rc = seekJournalHdr(pPager);
- if( rc ) return rc;
-
+ seekJournalHdr(pPager);
if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){
return SQLITE_DONE;
}
+ jrnlOff = pPager->journalOff;
- rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic));
+ rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), jrnlOff);
if( rc ) return rc;
+ jrnlOff += sizeof(aMagic);
if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
return SQLITE_DONE;
}
- rc = read32bits(pPager->jfd, pNRec);
+ rc = read32bits(pPager->jfd, jrnlOff, pNRec);
+ if( rc ) return rc;
+
+ rc = read32bits(pPager->jfd, jrnlOff+4, &pPager->cksumInit);
if( rc ) return rc;
- rc = read32bits(pPager->jfd, &pPager->cksumInit);
+ rc = read32bits(pPager->jfd, jrnlOff+8, pDbSize);
if( rc ) return rc;
- rc = read32bits(pPager->jfd, pDbSize);
+ rc = read32bits(pPager->jfd, jrnlOff+16, (u32 *)&iPageSize);
+ if( rc==SQLITE_OK
+ && iPageSize>=512
+ && iPageSize<=SQLITE_MAX_PAGE_SIZE
+ && ((iPageSize-1)&iPageSize)==0
+ ){
+ u16 pagesize = iPageSize;
+ rc = sqlite3PagerSetPagesize(pPager, &pagesize);
+ }
if( rc ) return rc;
/* Update the assumed sector-size to match the value used by
@@ -837,12 +1166,11 @@ static int readJournalHdr(
** is being called from within pager_playback(). The local value
** of Pager.sectorSize is restored at the end of that routine.
*/
- rc = read32bits(pPager->jfd, (u32 *)&pPager->sectorSize);
+ rc = read32bits(pPager->jfd, jrnlOff+12, (u32 *)&pPager->sectorSize);
if( rc ) return rc;
pPager->journalOff += JOURNAL_HDR_SZ(pPager);
- rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff);
- return rc;
+ return SQLITE_OK;
}
@@ -869,6 +1197,8 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
int rc;
int len;
int i;
+ i64 jrnlOff;
+ i64 jrnlSize;
u32 cksum = 0;
char zBuf[sizeof(aJournalMagic)+2*4];
@@ -885,22 +1215,42 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
** the journal has already been synced.
*/
if( pPager->fullSync ){
- rc = seekJournalHdr(pPager);
- if( rc!=SQLITE_OK ) return rc;
+ seekJournalHdr(pPager);
}
+ jrnlOff = pPager->journalOff;
pPager->journalOff += (len+20);
- rc = write32bits(pPager->jfd, PAGER_MJ_PGNO(pPager));
+ rc = write32bits(pPager->jfd, jrnlOff, PAGER_MJ_PGNO(pPager));
if( rc!=SQLITE_OK ) return rc;
+ jrnlOff += 4;
- rc = sqlite3OsWrite(pPager->jfd, zMaster, len);
+ rc = sqlite3OsWrite(pPager->jfd, zMaster, len, jrnlOff);
if( rc!=SQLITE_OK ) return rc;
+ jrnlOff += len;
put32bits(zBuf, len);
put32bits(&zBuf[4], cksum);
memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic));
- rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic));
+ rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff);
+ jrnlOff += 8+sizeof(aJournalMagic);
pPager->needSync = !pPager->noSync;
+
+ /* If the pager is in peristent-journal mode, then the physical
+ ** journal-file may extend past the end of the master-journal name
+ ** and 8 bytes of magic data just written to the file. This is
+ ** dangerous because the code to rollback a hot-journal file
+ ** will not be able to find the master-journal name to determine
+ ** whether or not the journal is hot.
+ **
+ ** Easiest thing to do in this scenario is to truncate the journal
+ ** file to the required size.
+ */
+ if( (rc==SQLITE_OK)
+ && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK
+ && jrnlSize>jrnlOff
+ ){
+ rc = sqlite3OsTruncate(pPager->jfd, jrnlOff);
+ }
return rc;
}
@@ -943,38 +1293,6 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
}
/*
-** Unlock the database file.
-*/
-static void pager_unlock(Pager *pPager){
- if( !pPager->exclusiveMode ){
- if( !MEMDB ){
- sqlite3OsUnlock(pPager->fd, NO_LOCK);
- pPager->dbSize = -1;
- IOTRACE(("UNLOCK %p\n", pPager))
- }
- pPager->state = PAGER_UNLOCK;
- pPager->changeCountDone = 0;
- }
-}
-
-/*
-** Execute a rollback if a transaction is active and unlock the
-** database file. This is a no-op if the pager has already entered
-** the error-state.
-*/
-static void pagerUnlockAndRollback(Pager *p){
- if( p->errCode ) return;
- assert( p->state>=PAGER_RESERVED || p->journalOpen==0 );
- if( p->state>=PAGER_RESERVED ){
- sqlite3PagerRollback(p);
- }
- pager_unlock(p);
- assert( p->errCode || !p->journalOpen || (p->exclusiveMode&&!p->journalOff) );
- assert( p->errCode || !p->stmtOpen || p->exclusiveMode );
-}
-
-
-/*
** Clear the in-memory cache. This routine
** sets the state of the pager back to what it was when it was first
** opened. Any outstanding pages are invalidated and subsequent attempts
@@ -987,21 +1305,99 @@ static void pager_reset(Pager *pPager){
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
PAGER_INCR(sqlite3_pager_pgfree_count);
pNext = pPg->pNextAll;
- sqliteFree(pPg);
+ lruListRemove(pPg);
+ sqlite3PageFree(pPg->pData);
+ sqlite3_free(pPg);
}
+ assert(pPager->lru.pFirst==0);
+ assert(pPager->lru.pFirstSynced==0);
+ assert(pPager->lru.pLast==0);
pPager->pStmt = 0;
- pPager->pFirst = 0;
- pPager->pFirstSynced = 0;
- pPager->pLast = 0;
pPager->pAll = 0;
+ pPager->pDirty = 0;
pPager->nHash = 0;
- sqliteFree(pPager->aHash);
+ sqlite3_free(pPager->aHash);
pPager->nPage = 0;
pPager->aHash = 0;
pPager->nRef = 0;
}
/*
+** Unlock the database file.
+**
+** If the pager is currently in error state, discard the contents of
+** the cache and reset the Pager structure internal state. If there is
+** an open journal-file, then the next time a shared-lock is obtained
+** on the pager file (by this or any other process), it will be
+** treated as a hot-journal and rolled back.
+*/
+static void pager_unlock(Pager *pPager){
+ if( !pPager->exclusiveMode ){
+ if( !MEMDB ){
+ int rc = osUnlock(pPager->fd, NO_LOCK);
+ if( rc ) pPager->errCode = rc;
+ pPager->dbSize = -1;
+ IOTRACE(("UNLOCK %p\n", pPager))
+
+ /* Always close the journal file when dropping the database lock.
+ ** Otherwise, another connection with journal_mode=delete might
+ ** delete the file out from under us.
+ */
+ if( pPager->journalOpen ){
+ sqlite3OsClose(pPager->jfd);
+ pPager->journalOpen = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
+ }
+
+ /* If Pager.errCode is set, the contents of the pager cache cannot be
+ ** trusted. Now that the pager file is unlocked, the contents of the
+ ** cache can be discarded and the error code safely cleared.
+ */
+ if( pPager->errCode ){
+ if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK;
+ pager_reset(pPager);
+ if( pPager->stmtOpen ){
+ sqlite3OsClose(pPager->stfd);
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
+ }
+ pPager->stmtOpen = 0;
+ pPager->stmtInUse = 0;
+ pPager->journalOff = 0;
+ pPager->journalStarted = 0;
+ pPager->stmtAutoopen = 0;
+ pPager->origDbSize = 0;
+ }
+ }
+
+ if( !MEMDB || pPager->errCode==SQLITE_OK ){
+ pPager->state = PAGER_UNLOCK;
+ pPager->changeCountDone = 0;
+ }
+ }
+}
+
+/*
+** Execute a rollback if a transaction is active and unlock the
+** database file. If the pager has already entered the error state,
+** do not attempt the rollback.
+*/
+static void pagerUnlockAndRollback(Pager *p){
+ /* assert( p->state>=PAGER_RESERVED || p->journalOpen==0 ); */
+ if( p->errCode==SQLITE_OK && p->state>=PAGER_RESERVED ){
+ sqlite3BeginBenignMalloc();
+ sqlite3PagerRollback(p);
+ sqlite3EndBenignMalloc();
+ }
+ pager_unlock(p);
+#if 0
+ assert( p->errCode || !p->journalOpen || (p->exclusiveMode&&!p->journalOff) );
+ assert( p->errCode || !p->stmtOpen || p->exclusiveMode );
+#endif
+}
+
+/*
** This routine ends a transaction. A transaction is ended by either
** a COMMIT or a ROLLBACK.
**
@@ -1018,7 +1414,7 @@ static void pager_reset(Pager *pPager){
** This might give a performance improvement on windows where opening
** a file is an expensive operation.
*/
-static int pager_end_transaction(Pager *pPager){
+static int pager_end_transaction(Pager *pPager, int hasMaster){
PgHdr *pPg;
int rc = SQLITE_OK;
int rc2 = SQLITE_OK;
@@ -1028,24 +1424,26 @@ static int pager_end_transaction(Pager *pPager){
}
sqlite3PagerStmtCommit(pPager);
if( pPager->stmtOpen && !pPager->exclusiveMode ){
- sqlite3OsClose(&pPager->stfd);
+ sqlite3OsClose(pPager->stfd);
pPager->stmtOpen = 0;
}
if( pPager->journalOpen ){
if( pPager->exclusiveMode
- && (rc = sqlite3OsTruncate(pPager->jfd, 0))==SQLITE_OK ){;
- sqlite3OsSeek(pPager->jfd, 0);
+ || pPager->journalMode==PAGER_JOURNALMODE_PERSIST
+ ){
+ rc = zeroJournalHdr(pPager, hasMaster);
+ pager_error(pPager, rc);
pPager->journalOff = 0;
pPager->journalStarted = 0;
}else{
- sqlite3OsClose(&pPager->jfd);
+ sqlite3OsClose(pPager->jfd);
pPager->journalOpen = 0;
- if( rc==SQLITE_OK ){
- rc = sqlite3OsDelete(pPager->zJournal);
+ if( rc==SQLITE_OK && !pPager->tempFile ){
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
}
- sqliteFree( pPager->aInJournal );
- pPager->aInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
pPg->inJournal = 0;
pPg->dirty = 0;
@@ -1059,12 +1457,11 @@ static int pager_end_transaction(Pager *pPager){
pPager->dirtyCache = 0;
pPager->nRec = 0;
}else{
- assert( pPager->aInJournal==0 );
- assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
+ assert( pPager->pInJournal==0 );
}
if( !pPager->exclusiveMode ){
- rc2 = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
+ rc2 = osUnlock(pPager->fd, SHARED_LOCK);
pPager->state = PAGER_SHARED;
}else if( pPager->state==PAGER_SYNCED ){
pPager->state = PAGER_EXCLUSIVE;
@@ -1072,8 +1469,9 @@ static int pager_end_transaction(Pager *pPager){
pPager->origDbSize = 0;
pPager->setMaster = 0;
pPager->needSync = 0;
- pPager->pFirstSynced = pPager->pFirst;
+ lruListSetFirstSynced(pPager);
pPager->dbSize = -1;
+ pPager->dbModified = 0;
return (rc==SQLITE_OK?rc2:rc);
}
@@ -1119,7 +1517,12 @@ static void makeClean(PgHdr*);
** are not used in statement journals because statement journals do not
** need to survive power failures.
*/
-static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
+static int pager_playback_one_page(
+ Pager *pPager,
+ sqlite3_file *jfd,
+ i64 offset,
+ int useCksum
+){
int rc;
PgHdr *pPg; /* An existing page in the cache */
Pgno pgno; /* The page number of a page in journal */
@@ -1132,9 +1535,9 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
assert( jfd == (useCksum ? pPager->jfd : pPager->stfd) );
assert( aData );
- rc = read32bits(jfd, &pgno);
+ rc = read32bits(jfd, offset, &pgno);
if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3OsRead(jfd, aData, pPager->pageSize);
+ rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4);
if( rc!=SQLITE_OK ) return rc;
pPager->journalOff += pPager->pageSize + 4;
@@ -1150,7 +1553,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
return SQLITE_OK;
}
if( useCksum ){
- rc = read32bits(jfd, &cksum);
+ rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum);
if( rc ) return rc;
pPager->journalOff += 4;
if( pager_cksum(pPager, aData)!=cksum ){
@@ -1187,15 +1590,18 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
** locked. (2) we know that the original page content is fully synced
** in the main journal either because the page is not in cache or else
** the page is marked as needSync==0.
+ **
+ ** 2008-04-14: When attempting to vacuum a corrupt database file, it
+ ** is possible to fail a statement on a database that does not yet exist.
+ ** Do not attempt to write if database file has never been opened.
*/
pPg = pager_lookup(pPager, pgno);
PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
- if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){
- rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
- if( rc==SQLITE_OK ){
- rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize);
- }
+ if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0)
+ && pPager->fd->pMethods ){
+ i64 offset = (pgno-1)*(i64)pPager->pageSize;
+ rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, offset);
if( pPg ){
makeClean(pPg);
}
@@ -1235,67 +1641,84 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
** This routine checks if it is possible to delete the master journal file,
** and does so if it is.
**
+** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not
+** available for use within this function.
+**
+**
** The master journal file contains the names of all child journals.
** To tell if a master journal can be deleted, check to each of the
** children. If all children are either missing or do not refer to
** a different master journal, then this master journal can be deleted.
*/
-static int pager_delmaster(const char *zMaster){
+static int pager_delmaster(Pager *pPager, const char *zMaster){
+ sqlite3_vfs *pVfs = pPager->pVfs;
int rc;
int master_open = 0;
- OsFile *master = 0;
+ sqlite3_file *pMaster;
+ sqlite3_file *pJournal;
char *zMasterJournal = 0; /* Contents of master journal file */
i64 nMasterJournal; /* Size of master journal file */
/* Open the master journal file exclusively in case some other process
** is running this routine also. Not that it makes too much difference.
*/
- rc = sqlite3OsOpenReadOnly(zMaster, &master);
- assert( rc!=SQLITE_OK || master );
+ pMaster = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile * 2);
+ pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
+ if( !pMaster ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
+ }
if( rc!=SQLITE_OK ) goto delmaster_out;
master_open = 1;
- rc = sqlite3OsFileSize(master, &nMasterJournal);
+
+ rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
if( rc!=SQLITE_OK ) goto delmaster_out;
if( nMasterJournal>0 ){
char *zJournal;
char *zMasterPtr = 0;
+ int nMasterPtr = pPager->pVfs->mxPathname+1;
/* Load the entire master journal file into space obtained from
- ** sqliteMalloc() and pointed to by zMasterJournal.
+ ** sqlite3_malloc() and pointed to by zMasterJournal.
*/
- zMasterJournal = (char *)sqliteMalloc(nMasterJournal);
+ zMasterJournal = (char *)sqlite3Malloc(nMasterJournal + nMasterPtr);
if( !zMasterJournal ){
rc = SQLITE_NOMEM;
goto delmaster_out;
}
- rc = sqlite3OsRead(master, zMasterJournal, nMasterJournal);
+ zMasterPtr = &zMasterJournal[nMasterJournal];
+ rc = sqlite3OsRead(pMaster, zMasterJournal, nMasterJournal, 0);
if( rc!=SQLITE_OK ) goto delmaster_out;
zJournal = zMasterJournal;
while( (zJournal-zMasterJournal)<nMasterJournal ){
- if( sqlite3OsFileExists(zJournal) ){
+ int exists;
+ rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
+ }
+ if( exists ){
/* One of the journals pointed to by the master journal exists.
** Open it and check if it points at the master journal. If
** so, return without deleting the master journal file.
*/
- OsFile *journal = 0;
int c;
-
- rc = sqlite3OsOpenReadOnly(zJournal, &journal);
- assert( rc!=SQLITE_OK || journal );
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
if( rc!=SQLITE_OK ){
goto delmaster_out;
}
- rc = readMasterJournal(journal, &zMasterPtr);
- sqlite3OsClose(&journal);
+ rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
+ sqlite3OsClose(pJournal);
if( rc!=SQLITE_OK ){
goto delmaster_out;
}
- c = zMasterPtr!=0 && strcmp(zMasterPtr, zMaster)==0;
- sqliteFree(zMasterPtr);
+ c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0;
if( c ){
/* We have a match. Do not delete the master journal file. */
goto delmaster_out;
@@ -1305,15 +1728,16 @@ static int pager_delmaster(const char *zMaster){
}
}
- rc = sqlite3OsDelete(zMaster);
+ rc = sqlite3OsDelete(pVfs, zMaster, 0);
delmaster_out:
if( zMasterJournal ){
- sqliteFree(zMasterJournal);
+ sqlite3_free(zMasterJournal);
}
if( master_open ){
- sqlite3OsClose(&master);
+ sqlite3OsClose(pMaster);
}
+ sqlite3_free(pMaster);
return rc;
}
@@ -1323,11 +1747,29 @@ static void pager_truncate_cache(Pager *pPager);
/*
** Truncate the main file of the given pager to the number of pages
** indicated. Also truncate the cached representation of the file.
+**
+** Might might be the case that the file on disk is smaller than nPage.
+** This can happen, for example, if we are in the middle of a transaction
+** which has extended the file size and the new pages are still all held
+** in cache, then an INSERT or UPDATE does a statement rollback. Some
+** operating system implementations can get confused if you try to
+** truncate a file to some size that is larger than it currently is,
+** so detect this case and write a single zero byte to the end of the new
+** file instead.
*/
static int pager_truncate(Pager *pPager, int nPage){
int rc = SQLITE_OK;
- if( pPager->state>=PAGER_EXCLUSIVE ){
- rc = sqlite3OsTruncate(pPager->fd, pPager->pageSize*(i64)nPage);
+ if( pPager->state>=PAGER_EXCLUSIVE && pPager->fd->pMethods ){
+ i64 currentSize, newSize;
+ rc = sqlite3OsFileSize(pPager->fd, &currentSize);
+ newSize = pPager->pageSize*(i64)nPage;
+ if( rc==SQLITE_OK && currentSize!=newSize ){
+ if( currentSize>newSize ){
+ rc = sqlite3OsTruncate(pPager->fd, newSize);
+ }else{
+ rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
+ }
+ }
}
if( rc==SQLITE_OK ){
pPager->dbSize = nPage;
@@ -1339,13 +1781,20 @@ static int pager_truncate(Pager *pPager, int nPage){
/*
** Set the sectorSize for the given pager.
**
-** The sector size is the larger of the sector size reported
-** by sqlite3OsSectorSize() and the pageSize.
+** The sector size is at least as big as the sector size reported
+** by sqlite3OsSectorSize(). The minimum sector size is 512.
*/
static void setSectorSize(Pager *pPager){
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
- if( pPager->sectorSize<pPager->pageSize ){
- pPager->sectorSize = pPager->pageSize;
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( !pPager->tempFile ){
+ /* Sector size doesn't matter for temporary files. Also, the file
+ ** may not have been opened yet, in whcih case the OsSectorSize()
+ ** call will segfault.
+ */
+ pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
+ }
+ if( pPager->sectorSize<512 ){
+ pPager->sectorSize = 512;
}
}
@@ -1363,20 +1812,23 @@ static void setSectorSize(Pager *pPager){
** sanity checksum.
** (4) 4 byte integer which is the number of pages to truncate the
** database to during a rollback.
-** (5) 4 byte integer which is the number of bytes in the master journal
+** (5) 4 byte big-endian integer which is the sector size. The header
+** is this many bytes in size.
+** (6) 4 byte big-endian integer which is the page case.
+** (7) 4 byte integer which is the number of bytes in the master journal
** name. The value may be zero (indicate that there is no master
** journal.)
-** (6) N bytes of the master journal name. The name will be nul-terminated
+** (8) N bytes of the master journal name. The name will be nul-terminated
** and might be shorter than the value read from (5). If the first byte
** of the name is \000 then there is no master journal. The master
** journal name is stored in UTF-8.
-** (7) Zero or more pages instances, each as follows:
+** (9) Zero or more pages instances, each as follows:
** + 4 byte page number.
** + pPager->pageSize bytes of data.
** + 4 byte checksum
**
-** When we speak of the journal header, we mean the first 6 items above.
-** Each entry in the journal is an instance of the 7th item.
+** When we speak of the journal header, we mean the first 8 items above.
+** Each entry in the journal is an instance of the 9th item.
**
** Call the value from the second bullet "nRec". nRec is the number of
** valid page entries in the journal. In most cases, you can compute the
@@ -1403,11 +1855,13 @@ static void setSectorSize(Pager *pPager){
** and an error code is returned.
*/
static int pager_playback(Pager *pPager, int isHot){
+ sqlite3_vfs *pVfs = pPager->pVfs;
i64 szJ; /* Size of the journal file in bytes */
u32 nRec; /* Number of Records in the journal */
- int i; /* Loop counter */
+ u32 u; /* Unsigned loop counter */
Pgno mxPg = 0; /* Size of the original file in pages */
int rc; /* Result code of a subroutine */
+ int res = 1; /* Value returned by sqlite3OsAccess() */
char *zMaster = 0; /* Name of master journal file if any */
/* Figure out how many records are in the journal. Abort early if
@@ -1424,15 +1878,15 @@ static int pager_playback(Pager *pPager, int isHot){
** present on disk, then the journal is not hot and does not need to be
** played back.
*/
- rc = readMasterJournal(pPager->jfd, &zMaster);
- assert( rc!=SQLITE_DONE );
- if( rc!=SQLITE_OK || (zMaster && !sqlite3OsFileExists(zMaster)) ){
- sqliteFree(zMaster);
- zMaster = 0;
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ zMaster = pPager->pTmpSpace;
+ rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
+ if( rc==SQLITE_OK && zMaster[0] ){
+ rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
+ }
+ zMaster = 0;
+ if( rc!=SQLITE_OK || !res ){
goto end_playback;
}
- sqlite3OsSeek(pPager->jfd, 0);
pPager->journalOff = 0;
/* This loop terminates either when the readJournalHdr() call returns
@@ -1476,7 +1930,7 @@ static int pager_playback(Pager *pPager, int isHot){
}
/* If this is the first header read from the journal, truncate the
- ** database file back to it's original size.
+ ** database file back to its original size.
*/
if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
rc = pager_truncate(pPager, mxPg);
@@ -1487,8 +1941,8 @@ static int pager_playback(Pager *pPager, int isHot){
/* Copy original pages out of the journal and back into the database file.
*/
- for(i=0; i<nRec; i++){
- rc = pager_playback_one_page(pPager, pPager->jfd, 1);
+ for(u=0; u<nRec; u++){
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
@@ -1505,16 +1959,17 @@ static int pager_playback(Pager *pPager, int isHot){
end_playback:
if( rc==SQLITE_OK ){
- rc = pager_end_transaction(pPager);
+ zMaster = pPager->pTmpSpace;
+ rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
}
- if( zMaster ){
+ if( rc==SQLITE_OK ){
+ rc = pager_end_transaction(pPager, zMaster[0]!='\0');
+ }
+ if( rc==SQLITE_OK && zMaster[0] ){
/* If there was a master journal and this routine will return success,
** see if it is possible to delete the master journal.
*/
- if( rc==SQLITE_OK ){
- rc = pager_delmaster(zMaster);
- }
- sqliteFree(zMaster);
+ rc = pager_delmaster(pPager, zMaster);
}
/* The Pager.sectorSize variable may have been updated while rolling
@@ -1547,14 +2002,6 @@ static int pager_stmt_playback(Pager *pPager){
int rc;
szJ = pPager->journalOff;
-#ifndef NDEBUG
- {
- i64 os_szJ;
- rc = sqlite3OsFileSize(pPager->jfd, &os_szJ);
- if( rc!=SQLITE_OK ) return rc;
- assert( szJ==os_szJ );
- }
-#endif
/* Set hdrOff to be the offset just after the end of the last journal
** page written before the first journal-header for this statement
@@ -1575,7 +2022,6 @@ static int pager_stmt_playback(Pager *pPager){
/* Figure out how many records are in the statement journal.
*/
assert( pPager->stmtInUse && pPager->journalOpen );
- sqlite3OsSeek(pPager->stfd, 0);
nRec = pPager->stmtNRec;
/* Copy original pages out of the statement journal and back into the
@@ -1583,8 +2029,9 @@ static int pager_stmt_playback(Pager *pPager){
** each record since power-failure recovery is not important to statement
** journals.
*/
- for(i=nRec-1; i>=0; i--){
- rc = pager_playback_one_page(pPager, pPager->stfd, 0);
+ for(i=0; i<nRec; i++){
+ i64 offset = i*(4+pPager->pageSize);
+ rc = pager_playback_one_page(pPager, pPager->stfd, offset, 0);
assert( rc!=SQLITE_DONE );
if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
@@ -1597,14 +2044,10 @@ static int pager_stmt_playback(Pager *pPager){
** If it is not zero, then Pager.stmtHdrOff is the offset to the start
** of the first journal header written during this statement transaction.
*/
- rc = sqlite3OsSeek(pPager->jfd, pPager->stmtJSize);
- if( rc!=SQLITE_OK ){
- goto end_stmt_playback;
- }
pPager->journalOff = pPager->stmtJSize;
pPager->cksumInit = pPager->stmtCksum;
while( pPager->journalOff < hdrOff ){
- rc = pager_playback_one_page(pPager, pPager->jfd, 1);
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
assert( rc!=SQLITE_DONE );
if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
@@ -1621,7 +2064,7 @@ static int pager_stmt_playback(Pager *pPager){
nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8);
}
for(i=nJRec-1; i>=0 && pPager->journalOff < szJ; i--){
- rc = pager_playback_one_page(pPager, pPager->jfd, 1);
+ rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
assert( rc!=SQLITE_DONE );
if( rc!=SQLITE_OK ) goto end_stmt_playback;
}
@@ -1678,7 +2121,7 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int full_fsync){
pPager->noSync = level==1 || pPager->tempFile;
pPager->fullSync = level==3 && !pPager->tempFile;
- pPager->full_fsync = full_fsync;
+ pPager->sync_flags = (full_fsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
if( pPager->noSync ) pPager->needSync = 0;
}
#endif
@@ -1696,25 +2139,24 @@ int sqlite3_opentemp_count = 0;
** Open a temporary file.
**
** Write the file descriptor into *fd. Return SQLITE_OK on success or some
-** other error code if we fail.
-**
-** The OS will automatically delete the temporary file when it is
-** closed.
+** other error code if we fail. The OS will automatically delete the temporary
+** file when it is closed.
*/
-static int sqlite3PagerOpentemp(OsFile **pFd){
- int cnt = 8;
+static int sqlite3PagerOpentemp(
+ Pager *pPager, /* The pager object */
+ sqlite3_file *pFile, /* Write the file descriptor here */
+ int vfsFlags /* Flags passed through to the VFS */
+){
int rc;
- char zFile[SQLITE_TEMPNAME_SIZE];
#ifdef SQLITE_TEST
sqlite3_opentemp_count++; /* Used for testing and analysis only */
#endif
- do{
- cnt--;
- sqlite3OsTempFileName(zFile);
- rc = sqlite3OsOpenExclusive(zFile, pFd, 1);
- assert( rc!=SQLITE_OK || *pFd );
- }while( cnt>0 && rc!=SQLITE_OK && rc!=SQLITE_NOMEM );
+
+ vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
+ rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0);
+ assert( rc!=SQLITE_OK || pFile->pMethods );
return rc;
}
@@ -1733,15 +2175,15 @@ static int sqlite3PagerOpentemp(OsFile **pFd){
** in-memory database.
*/
int sqlite3PagerOpen(
+ sqlite3_vfs *pVfs, /* The virtual file system to use */
Pager **ppPager, /* Return the Pager structure here */
const char *zFilename, /* Name of the database file to open */
int nExtra, /* Extra bytes append to each in-memory page */
- int flags /* flags controlling this file */
+ int flags, /* flags controlling this file */
+ int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */
){
+ u8 *pPtr;
Pager *pPager = 0;
- char *zFullPathname = 0;
- int nameLen; /* Compiler is wrong. This is always initialized before use */
- OsFile *fd = 0;
int rc = SQLITE_OK;
int i;
int tempFile = 0;
@@ -1749,93 +2191,146 @@ int sqlite3PagerOpen(
int readOnly = 0;
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0;
int noReadlock = (flags & PAGER_NO_READLOCK)!=0;
- char zTemp[SQLITE_TEMPNAME_SIZE];
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- /* A malloc() cannot fail in sqlite3ThreadData() as one or more calls to
- ** malloc() must have already been made by this thread before it gets
- ** to this point. This means the ThreadData must have been allocated already
- ** so that ThreadData.nAlloc can be set. It would be nice to assert
- ** that ThreadData.nAlloc is non-zero, but alas this breaks test cases
- ** written to invoke the pager directly.
- */
- ThreadData *pTsd = sqlite3ThreadData();
- assert( pTsd );
-#endif
+ int journalFileSize = sqlite3JournalSize(pVfs);
+ int szPageDflt = SQLITE_DEFAULT_PAGE_SIZE;
+ char *zPathname = 0;
+ int nPathname = 0;
- /* We used to test if malloc() had already failed before proceeding.
- ** But the way this function is used in SQLite means that can never
- ** happen. Furthermore, if the malloc-failed flag is already set,
- ** either the call to sqliteStrDup() or sqliteMalloc() below will
- ** fail shortly and SQLITE_NOMEM returned anyway.
- */
+ /* The default return is a NULL pointer */
*ppPager = 0;
- /* Open the pager file and set zFullPathname to point at malloc()ed
- ** memory containing the complete filename (i.e. including the directory).
+ /* Compute and store the full pathname in an allocated buffer pointed
+ ** to by zPathname, length nPathname. Or, if this is a temporary file,
+ ** leave both nPathname and zPathname set to 0.
*/
if( zFilename && zFilename[0] ){
+ nPathname = pVfs->mxPathname+1;
+ zPathname = sqlite3Malloc(nPathname*2);
+ if( zPathname==0 ){
+ return SQLITE_NOMEM;
+ }
#ifndef SQLITE_OMIT_MEMORYDB
if( strcmp(zFilename,":memory:")==0 ){
memDb = 1;
- zFullPathname = sqliteStrDup("");
+ zPathname[0] = 0;
}else
#endif
{
- zFullPathname = sqlite3OsFullPathname(zFilename);
- if( zFullPathname ){
- rc = sqlite3OsOpenReadWrite(zFullPathname, &fd, &readOnly);
- assert( rc!=SQLITE_OK || fd );
- }
+ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
}
- }else{
- rc = sqlite3PagerOpentemp(&fd);
- sqlite3OsTempFileName(zTemp);
- zFilename = zTemp;
- zFullPathname = sqlite3OsFullPathname(zFilename);
- if( rc==SQLITE_OK ){
- tempFile = 1;
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zPathname);
+ return rc;
}
+ nPathname = strlen(zPathname);
}
- /* Allocate the Pager structure. As part of the same allocation, allocate
- ** space for the full paths of the file, directory and journal
- ** (Pager.zFilename, Pager.zDirectory and Pager.zJournal).
+ /* Allocate memory for the pager structure */
+ pPager = sqlite3MallocZero(
+ sizeof(*pPager) + /* Pager structure */
+ journalFileSize + /* The journal file structure */
+ pVfs->szOsFile * 3 + /* The main db and two journal files */
+ 3*nPathname + 40 /* zFilename, zDirectory, zJournal */
+ );
+ if( !pPager ){
+ sqlite3_free(zPathname);
+ return SQLITE_NOMEM;
+ }
+ pPtr = (u8 *)&pPager[1];
+ pPager->vfsFlags = vfsFlags;
+ pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0];
+ pPager->stfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*1];
+ pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile*2];
+ pPager->zFilename = (char*)&pPtr[pVfs->szOsFile*2+journalFileSize];
+ pPager->zDirectory = &pPager->zFilename[nPathname+1];
+ pPager->zJournal = &pPager->zDirectory[nPathname+1];
+ pPager->pVfs = pVfs;
+ if( zPathname ){
+ memcpy(pPager->zFilename, zPathname, nPathname+1);
+ sqlite3_free(zPathname);
+ }
+
+ /* Open the pager file.
*/
- if( zFullPathname ){
- nameLen = strlen(zFullPathname);
- pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 );
- if( pPager && rc==SQLITE_OK ){
- pPager->pTmpSpace = (char *)sqliteMallocRaw(SQLITE_DEFAULT_PAGE_SIZE);
+ if( zFilename && zFilename[0] && !memDb ){
+ if( nPathname>(pVfs->mxPathname - sizeof("-journal")) ){
+ rc = SQLITE_CANTOPEN;
+ }else{
+ int fout = 0;
+ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd,
+ pPager->vfsFlags, &fout);
+ readOnly = (fout&SQLITE_OPEN_READONLY);
+
+ /* If the file was successfully opened for read/write access,
+ ** choose a default page size in case we have to create the
+ ** database file. The default page size is the maximum of:
+ **
+ ** + SQLITE_DEFAULT_PAGE_SIZE,
+ ** + The value returned by sqlite3OsSectorSize()
+ ** + The largest page size that can be written atomically.
+ */
+ if( rc==SQLITE_OK && !readOnly ){
+ int iSectorSize = sqlite3OsSectorSize(pPager->fd);
+ if( szPageDflt<iSectorSize ){
+ szPageDflt = iSectorSize;
+ }
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ {
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ int ii;
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+ assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
+ for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
+ if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ) szPageDflt = ii;
+ }
+ }
+#endif
+ if( szPageDflt>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+ }
+ }
}
+ }else if( !memDb ){
+ /* If a temporary file is requested, it is not opened immediately.
+ ** In this case we accept the default page size and delay actually
+ ** opening the file until the first call to OsWrite().
+ */
+ tempFile = 1;
+ pPager->state = PAGER_EXCLUSIVE;
}
+ if( pPager && rc==SQLITE_OK ){
+ pPager->pTmpSpace = sqlite3PageMalloc(szPageDflt);
+ }
- /* If an error occured in either of the blocks above, free the memory
- ** pointed to by zFullPathname, free the Pager structure and close the
- ** file. Since the pager is not allocated there is no need to set
+ /* If an error occured in either of the blocks above.
+ ** Free the Pager structure and close the file.
+ ** Since the pager is not allocated there is no need to set
** any Pager.errMask variables.
*/
- if( !pPager || !zFullPathname || !pPager->pTmpSpace || rc!=SQLITE_OK ){
- sqlite3OsClose(&fd);
- sqliteFree(zFullPathname);
- sqliteFree(pPager);
+ if( !pPager || !pPager->pTmpSpace ){
+ sqlite3OsClose(pPager->fd);
+ sqlite3_free(pPager);
return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc);
}
- PAGERTRACE3("OPEN %d %s\n", FILEHANDLEID(fd), zFullPathname);
- IOTRACE(("OPEN %p %s\n", pPager, zFullPathname))
- pPager->zFilename = (char*)&pPager[1];
- pPager->zDirectory = &pPager->zFilename[nameLen+1];
- pPager->zJournal = &pPager->zDirectory[nameLen+1];
- memcpy(pPager->zFilename, zFullPathname, nameLen+1);
- memcpy(pPager->zDirectory, zFullPathname, nameLen+1);
+ PAGERTRACE3("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename);
+ IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
- for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){}
+ /* Fill in Pager.zDirectory[] */
+ memcpy(pPager->zDirectory, pPager->zFilename, nPathname+1);
+ for(i=strlen(pPager->zDirectory); i>0 && pPager->zDirectory[i-1]!='/'; i--){}
if( i>0 ) pPager->zDirectory[i-1] = 0;
- memcpy(pPager->zJournal, zFullPathname,nameLen);
- sqliteFree(zFullPathname);
- memcpy(&pPager->zJournal[nameLen], "-journal",sizeof("-journal"));
- pPager->fd = fd;
+
+ /* Fill in Pager.zJournal[] */
+ if( zPathname ){
+ memcpy(pPager->zJournal, pPager->zFilename, nPathname);
+ memcpy(&pPager->zJournal[nPathname], "-journal", 9);
+ }else{
+ pPager->zJournal = 0;
+ }
+
/* pPager->journalOpen = 0; */
pPager->useJournal = useJournal && !memDb;
pPager->noReadlock = noReadlock && readOnly;
@@ -1843,14 +2338,14 @@ int sqlite3PagerOpen(
/* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */
pPager->dbSize = memDb-1;
- pPager->pageSize = SQLITE_DEFAULT_PAGE_SIZE;
+ pPager->pageSize = szPageDflt;
/* pPager->stmtSize = 0; */
/* pPager->stmtJSize = 0; */
/* pPager->nPage = 0; */
pPager->mxPage = 100;
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
- assert( PAGER_UNLOCK==0 );
/* pPager->state = PAGER_UNLOCK; */
+ assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
/* pPager->errMask = 0; */
pPager->tempFile = tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
@@ -1862,11 +2357,13 @@ int sqlite3PagerOpen(
/* pPager->needSync = 0; */
pPager->noSync = pPager->tempFile || !useJournal;
pPager->fullSync = (pPager->noSync?0:1);
+ pPager->sync_flags = SQLITE_SYNC_NORMAL;
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
pPager->nExtra = FORCE_ALIGNMENT(nExtra);
- assert(fd||memDb);
+ pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
+ assert(pPager->fd->pMethods||memDb||tempFile);
if( !memDb ){
setSectorSize(pPager);
}
@@ -1874,8 +2371,22 @@ int sqlite3PagerOpen(
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
*ppPager = pPager;
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- pPager->pNext = pTsd->pPager;
- pTsd->pPager = pPager;
+ pPager->iInUseMM = 0;
+ pPager->iInUseDB = 0;
+ if( !memDb ){
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
+#endif
+ sqlite3_mutex_enter(mutex);
+ pPager->pNext = sqlite3PagerList;
+ if( sqlite3PagerList ){
+ assert( sqlite3PagerList->pPrev==0 );
+ sqlite3PagerList->pPrev = pPager;
+ }
+ pPager->pPrev = 0;
+ sqlite3PagerList = pPager;
+ sqlite3_mutex_leave(mutex);
+ }
#endif
return SQLITE_OK;
}
@@ -1911,18 +2422,44 @@ void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*,int)){
}
/*
-** Set the page size. Return the new size. If the suggest new page
-** size is inappropriate, then an alternative page size is selected
-** and returned.
+** Set the page size to *pPageSize. If the suggest new page size is
+** inappropriate, then an alternative page size is set to that
+** value before returning.
*/
-int sqlite3PagerSetPagesize(Pager *pPager, int pageSize){
- assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE );
- if( !pPager->memDb && pPager->nRef==0 ){
- pager_reset(pPager);
- pPager->pageSize = pageSize;
- pPager->pTmpSpace = sqlite3ReallocOrFree(pPager->pTmpSpace, pageSize);
+int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){
+ int rc = SQLITE_OK;
+ u16 pageSize = *pPageSize;
+ assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
+ if( pageSize && pageSize!=pPager->pageSize
+ && !pPager->memDb && pPager->nRef==0
+ ){
+ char *pNew = (char *)sqlite3PageMalloc(pageSize);
+ if( !pNew ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pagerEnter(pPager);
+ pager_reset(pPager);
+ pPager->pageSize = pageSize;
+ setSectorSize(pPager);
+ sqlite3PageFree(pPager->pTmpSpace);
+ pPager->pTmpSpace = pNew;
+ pagerLeave(pPager);
+ }
}
- return pPager->pageSize;
+ *pPageSize = pPager->pageSize;
+ return rc;
+}
+
+/*
+** Return a pointer to the "temporary page" buffer held internally
+** by the pager. This is a buffer that is big enough to hold the
+** entire content of a database page. This buffer is used internally
+** during rollback and will be overwritten whenever a rollback
+** occurs. But other modules are free to use it too, as long as
+** no rollbacks are happening.
+*/
+void *sqlite3PagerTempSpace(Pager *pPager){
+ return pPager->pTmpSpace;
}
/*
@@ -1936,7 +2473,7 @@ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
if( mxPage>0 ){
pPager->mxPgno = mxPage;
}
- sqlite3PagerPagecount(pPager);
+ sqlite3PagerPagecount(pPager, 0);
return pPager->mxPgno;
}
@@ -1977,12 +2514,10 @@ void enable_simulated_io_errors(void){
int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
int rc = SQLITE_OK;
memset(pDest, 0, N);
- if( MEMDB==0 ){
- disable_simulated_io_errors();
- sqlite3OsSeek(pPager->fd, 0);
- enable_simulated_io_errors();
+ assert(MEMDB||pPager->fd->pMethods||pPager->tempFile);
+ if( pPager->fd->pMethods ){
IOTRACE(("DBHDR %p 0 %d\n", pPager, N))
- rc = sqlite3OsRead(pPager->fd, pDest, N);
+ rc = sqlite3OsRead(pPager->fd, pDest, N, 0);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
@@ -1999,23 +2534,27 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the
** file is 4096 bytes, 5 is returned instead of 4.
*/
-int sqlite3PagerPagecount(Pager *pPager){
- i64 n;
+int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
+ i64 n = 0;
int rc;
assert( pPager!=0 );
if( pPager->errCode ){
- return 0;
+ return pPager->errCode;
}
if( pPager->dbSize>=0 ){
n = pPager->dbSize;
- }else{
- if( (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
+ } else {
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( (pPager->fd->pMethods)
+ && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){
+ pPager->nRef++;
pager_error(pPager, rc);
- return 0;
+ pPager->nRef--;
+ return rc;
}
if( n>0 && n<pPager->pageSize ){
n = 1;
- } else {
+ }else{
n /= pPager->pageSize;
}
if( pPager->state!=PAGER_UNLOCK ){
@@ -2028,7 +2567,10 @@ int sqlite3PagerPagecount(Pager *pPager){
if( n>pPager->mxPgno ){
pPager->mxPgno = n;
}
- return n;
+ if( pnPage ){
+ *pnPage = n;
+ }
+ return SQLITE_OK;
}
@@ -2037,8 +2579,8 @@ int sqlite3PagerPagecount(Pager *pPager){
** Clear a PgHistory block
*/
static void clearHistory(PgHistory *pHist){
- sqliteFree(pHist->pOrig);
- sqliteFree(pHist->pStmt);
+ sqlite3PageFree(pHist->pOrig);
+ sqlite3PageFree(pHist->pStmt);
pHist->pOrig = 0;
pHist->pStmt = 0;
}
@@ -2052,7 +2594,7 @@ static void clearHistory(PgHistory *pHist){
static int syncJournal(Pager*);
/*
-** Unlink pPg from it's hash chain. Also set the page number to 0 to indicate
+** Unlink pPg from its hash chain. Also set the page number to 0 to indicate
** that the page is not part of any hash chain. This is required because the
** sqlite3PagerMovepage() routine can leave a page in the
** pNextFree/pPrevFree list that is not a part of any hash-chain.
@@ -2086,27 +2628,8 @@ static void unlinkHashChain(Pager *pPager, PgHdr *pPg){
static void unlinkPage(PgHdr *pPg){
Pager *pPager = pPg->pPager;
- /* Keep the pFirstSynced pointer pointing at the first synchronized page */
- if( pPg==pPager->pFirstSynced ){
- PgHdr *p = pPg->pNextFree;
- while( p && p->needSync ){ p = p->pNextFree; }
- pPager->pFirstSynced = p;
- }
-
- /* Unlink from the freelist */
- if( pPg->pPrevFree ){
- pPg->pPrevFree->pNextFree = pPg->pNextFree;
- }else{
- assert( pPager->pFirst==pPg );
- pPager->pFirst = pPg->pNextFree;
- }
- if( pPg->pNextFree ){
- pPg->pNextFree->pPrevFree = pPg->pPrevFree;
- }else{
- assert( pPager->pLast==pPg );
- pPager->pLast = pPg->pPrevFree;
- }
- pPg->pNextFree = pPg->pPrevFree = 0;
+ /* Unlink from free page list */
+ lruListRemove(pPg);
/* Unlink from the pgno hash table */
unlinkHashChain(pPager, pPg);
@@ -2138,11 +2661,17 @@ static void pager_truncate_cache(Pager *pPager){
ppPg = &pPg->pNextAll;
}else{
*ppPg = pPg->pNextAll;
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ if( *ppPg ){
+ (*ppPg)->pPrevAll = pPg->pPrevAll;
+ }
+#endif
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
PAGER_INCR(sqlite3_pager_pgfree_count);
unlinkPage(pPg);
makeClean(pPg);
- sqliteFree(pPg);
+ sqlite3PageFree(pPg->pData);
+ sqlite3_free(pPg);
pPager->nPage--;
}
}
@@ -2170,6 +2699,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
if( pPager->state>=locktype ){
rc = SQLITE_OK;
}else{
+ if( pPager->pBusyHandler ) pPager->pBusyHandler->nBusy = 0;
do {
rc = sqlite3OsLock(pPager->fd, locktype);
}while( rc==SQLITE_BUSY && sqlite3InvokeBusyHandler(pPager->pBusyHandler) );
@@ -2187,7 +2717,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
int rc;
assert( pPager->state>=PAGER_SHARED || MEMDB );
- sqlite3PagerPagecount(pPager);
+ sqlite3PagerPagecount(pPager, 0);
if( pPager->errCode ){
rc = pPager->errCode;
return rc;
@@ -2200,13 +2730,17 @@ int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
pager_truncate_cache(pPager);
return SQLITE_OK;
}
+ pagerEnter(pPager);
rc = syncJournal(pPager);
+ pagerLeave(pPager);
if( rc!=SQLITE_OK ){
return rc;
}
/* Get an exclusive lock on the database before truncating. */
+ pagerEnter(pPager);
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ pagerLeave(pPager);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -2231,54 +2765,50 @@ int sqlite3PagerTruncate(Pager *pPager, Pgno nPage){
*/
int sqlite3PagerClose(Pager *pPager){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- /* A malloc() cannot fail in sqlite3ThreadData() as one or more calls to
- ** malloc() must have already been made by this thread before it gets
- ** to this point. This means the ThreadData must have been allocated already
- ** so that ThreadData.nAlloc can be set.
- */
- ThreadData *pTsd = sqlite3ThreadData();
- assert( pPager );
- assert( pTsd && pTsd->nAlloc );
+ if( !MEMDB ){
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
+#endif
+ sqlite3_mutex_enter(mutex);
+ if( pPager->pPrev ){
+ pPager->pPrev->pNext = pPager->pNext;
+ }else{
+ sqlite3PagerList = pPager->pNext;
+ }
+ if( pPager->pNext ){
+ pPager->pNext->pPrev = pPager->pPrev;
+ }
+ sqlite3_mutex_leave(mutex);
+ }
#endif
disable_simulated_io_errors();
+ sqlite3BeginBenignMalloc();
pPager->errCode = 0;
pPager->exclusiveMode = 0;
pager_reset(pPager);
pagerUnlockAndRollback(pPager);
enable_simulated_io_errors();
+ sqlite3EndBenignMalloc();
PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
IOTRACE(("CLOSE %p\n", pPager))
- assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
if( pPager->journalOpen ){
- sqlite3OsClose(&pPager->jfd);
+ sqlite3OsClose(pPager->jfd);
}
- sqliteFree(pPager->aInJournal);
+ sqlite3BitvecDestroy(pPager->pInJournal);
if( pPager->stmtOpen ){
- sqlite3OsClose(&pPager->stfd);
+ sqlite3OsClose(pPager->stfd);
}
- sqlite3OsClose(&pPager->fd);
+ sqlite3OsClose(pPager->fd);
/* Temp files are automatically deleted by the OS
** if( pPager->tempFile ){
** sqlite3OsDelete(pPager->zFilename);
** }
*/
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- /* Remove the pager from the linked list of pagers starting at
- ** ThreadData.pPager if memory-management is enabled.
- */
- if( pPager==pTsd->pPager ){
- pTsd->pPager = pPager->pNext;
- }else{
- Pager *pTmp;
- for(pTmp = pTsd->pPager; pTmp->pNext!=pPager; pTmp=pTmp->pNext){}
- pTmp->pNext = pPager->pNext;
- }
-#endif
- sqliteFree(pPager->aHash);
- sqliteFree(pPager->pTmpSpace);
- sqliteFree(pPager);
+ sqlite3_free(pPager->aHash);
+ sqlite3PageFree(pPager->pTmpSpace);
+ sqlite3_free(pPager);
return SQLITE_OK;
}
@@ -2303,25 +2833,10 @@ Pgno sqlite3PagerPagenumber(DbPage *p){
static void _page_ref(PgHdr *pPg){
if( pPg->nRef==0 ){
/* The page is currently on the freelist. Remove it. */
- if( pPg==pPg->pPager->pFirstSynced ){
- PgHdr *p = pPg->pNextFree;
- while( p && p->needSync ){ p = p->pNextFree; }
- pPg->pPager->pFirstSynced = p;
- }
- if( pPg->pPrevFree ){
- pPg->pPrevFree->pNextFree = pPg->pNextFree;
- }else{
- pPg->pPager->pFirst = pPg->pNextFree;
- }
- if( pPg->pNextFree ){
- pPg->pNextFree->pPrevFree = pPg->pPrevFree;
- }else{
- pPg->pPager->pLast = pPg->pPrevFree;
- }
+ lruListRemove(pPg);
pPg->pPager->nRef++;
}
pPg->nRef++;
- REFINFO(pPg);
}
#ifdef SQLITE_DEBUG
static void page_ref(PgHdr *pPg){
@@ -2329,7 +2844,6 @@ static void _page_ref(PgHdr *pPg){
_page_ref(pPg);
}else{
pPg->nRef++;
- REFINFO(pPg);
}
}
#else
@@ -2341,7 +2855,9 @@ static void _page_ref(PgHdr *pPg){
** a reference to the page data.
*/
int sqlite3PagerRef(DbPage *pPg){
+ pagerEnter(pPg->pPager);
page_ref(pPg);
+ pagerLeave(pPg->pPager);
return SQLITE_OK;
}
@@ -2360,7 +2876,12 @@ int sqlite3PagerRef(DbPage *pPg){
** is synced, then the nRec field is updated, then a second sync occurs.
**
** For temporary databases, we do not care if we are able to rollback
-** after a power failure, so sync occurs.
+** after a power failure, so no sync occurs.
+**
+** If the IOCAP_SEQUENTIAL flag is set for the persistent media on which
+** the database is stored, then OsSync() is never called on the journal
+** file. In this case all that is required is to update the nRec field in
+** the journal header.
**
** This routine clears the needSync field of every page current held in
** memory.
@@ -2374,47 +2895,42 @@ static int syncJournal(Pager *pPager){
*/
if( pPager->needSync ){
if( !pPager->tempFile ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
assert( pPager->journalOpen );
- /* assert( !pPager->noSync ); // noSync might be set if synchronous
- ** was turned off after the transaction was started. Ticket #615 */
-#ifndef NDEBUG
- {
- /* Make sure the pPager->nRec counter we are keeping agrees
- ** with the nRec computed from the size of the journal file.
- */
- i64 jSz;
- rc = sqlite3OsFileSize(pPager->jfd, &jSz);
- if( rc!=0 ) return rc;
- assert( pPager->journalOff==jSz );
- }
-#endif
- {
+
+ if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
/* Write the nRec value into the journal file header. If in
** full-synchronous mode, sync the journal first. This ensures that
** all data has really hit the disk before nRec is updated to mark
- ** it as a candidate for rollback.
+ ** it as a candidate for rollback.
+ **
+ ** This is not required if the persistent media supports the
+ ** SAFE_APPEND property. Because in this case it is not possible
+ ** for garbage data to be appended to the file, the nRec field
+ ** is populated with 0xFFFFFFFF when the journal header is written
+ ** and never needs to be updated.
*/
- if( pPager->fullSync ){
+ i64 jrnlOff;
+ if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
IOTRACE(("JSYNC %p\n", pPager))
- rc = sqlite3OsSync(pPager->jfd, 0);
+ rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
if( rc!=0 ) return rc;
}
- rc = sqlite3OsSeek(pPager->jfd,
- pPager->journalHdr + sizeof(aJournalMagic));
- if( rc ) return rc;
- IOTRACE(("JHDR %p %lld %d\n", pPager,
- pPager->journalHdr + sizeof(aJournalMagic), 4))
- rc = write32bits(pPager->jfd, pPager->nRec);
- if( rc ) return rc;
- rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff);
+ jrnlOff = pPager->journalHdr + sizeof(aJournalMagic);
+ IOTRACE(("JHDR %p %lld %d\n", pPager, jrnlOff, 4));
+ rc = write32bits(pPager->jfd, jrnlOff, pPager->nRec);
if( rc ) return rc;
}
- PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
- IOTRACE(("JSYNC %p\n", pPager))
- rc = sqlite3OsSync(pPager->jfd, pPager->full_fsync);
- if( rc!=0 ) return rc;
+ if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
+ PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager));
+ IOTRACE(("JSYNC %p\n", pPager))
+ rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
+ (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
+ );
+ if( rc!=0 ) return rc;
+ }
pPager->journalStarted = 1;
}
pPager->needSync = 0;
@@ -2424,7 +2940,7 @@ static int syncJournal(Pager *pPager){
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
pPg->needSync = 0;
}
- pPager->pFirstSynced = pPager->pFirst;
+ lruListSetFirstSynced(pPager);
}
#ifndef NDEBUG
@@ -2436,7 +2952,7 @@ static int syncJournal(Pager *pPager){
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
assert( pPg->needSync==0 );
}
- assert( pPager->pFirstSynced==pPager->pFirst );
+ assert( pPager->lru.pFirstSynced==pPager->lru.pFirst );
}
#endif
@@ -2524,6 +3040,7 @@ static PgHdr *sort_pagelist(PgHdr *pIn){
*/
static int pager_write_pagelist(PgHdr *pList){
Pager *pPager;
+ PgHdr *p;
int rc;
if( pList==0 ) return SQLITE_OK;
@@ -2551,21 +3068,31 @@ static int pager_write_pagelist(PgHdr *pList){
}
pList = sort_pagelist(pList);
+ for(p=pList; p; p=p->pDirty){
+ assert( p->dirty );
+ p->dirty = 0;
+ }
while( pList ){
- assert( pList->dirty );
- rc = sqlite3OsSeek(pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize);
- if( rc ) return rc;
+
+ /* If the file has not yet been opened, open it now. */
+ if( !pPager->fd->pMethods ){
+ assert(pPager->tempFile);
+ rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags);
+ if( rc ) return rc;
+ }
+
/* If there are dirty pages in the page cache with page numbers greater
** than Pager.dbSize, this means sqlite3PagerTruncate() was called to
** make the file smaller (presumably by auto-vacuum code). Do not write
** any such pages to the file.
*/
if( pList->pgno<=pPager->dbSize ){
+ i64 offset = (pList->pgno-1)*(i64)pPager->pageSize;
char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
PAGERTRACE4("STORE %d page %d hash(%08x)\n",
PAGERID(pPager), pList->pgno, pager_pagehash(pList));
IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
- rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize);
+ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
PAGER_INCR(sqlite3_pager_writedb_count);
PAGER_INCR(pPager->nWrite);
if( pList->pgno==1 ){
@@ -2578,7 +3105,6 @@ static int pager_write_pagelist(PgHdr *pList){
}
#endif
if( rc ) return rc;
- pList->dirty = 0;
#ifdef SQLITE_CHECK_PAGES
pList->pageHash = pager_pagehash(pList);
#endif
@@ -2593,31 +3119,68 @@ static int pager_write_pagelist(PgHdr *pList){
** collected even if they are still in use.
*/
static PgHdr *pager_get_all_dirty_pages(Pager *pPager){
+
+#ifndef NDEBUG
+ /* Verify the sanity of the dirty list when we are running
+ ** in debugging mode. This is expensive, so do not
+ ** do this on a normal build. */
+ int n1 = 0;
+ int n2 = 0;
+ PgHdr *p;
+ for(p=pPager->pAll; p; p=p->pNextAll){ if( p->dirty ) n1++; }
+ for(p=pPager->pDirty; p; p=p->pDirty){ n2++; }
+ assert( n1==n2 );
+#endif
+
return pPager->pDirty;
}
/*
-** Return TRUE if there is a hot journal on the given pager.
+** Return 1 if there is a hot journal on the given pager.
** A hot journal is one that needs to be played back.
**
** If the current size of the database file is 0 but a journal file
** exists, that is probably an old journal left over from a prior
** database with the same name. Just delete the journal.
+**
+** Return negative if unable to determine the status of the journal.
+**
+** This routine does not open the journal file to examine its
+** content. Hence, the journal might contain the name of a master
+** journal file that has been deleted, and hence not be hot. Or
+** the header of the journal might be zeroed out. This routine
+** does not discover these cases of a non-hot journal - if the
+** journal file exists and is not empty this routine assumes it
+** is hot. The pager_playback() routine will discover that the
+** journal file is not really hot and will no-op.
*/
-static int hasHotJournal(Pager *pPager){
- if( !pPager->useJournal ) return 0;
- if( !sqlite3OsFileExists(pPager->zJournal) ){
- return 0;
- }
- if( sqlite3OsCheckReservedLock(pPager->fd) ){
- return 0;
- }
- if( sqlite3PagerPagecount(pPager)==0 ){
- sqlite3OsDelete(pPager->zJournal);
- return 0;
- }else{
- return 1;
+static int hasHotJournal(Pager *pPager, int *pExists){
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ int rc = SQLITE_OK;
+ *pExists = 0;
+ if( pPager->useJournal && pPager->fd->pMethods ){
+ int exists;
+ int locked;
+
+ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ if( rc==SQLITE_OK && exists ){
+ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
+ }
+
+ if( rc==SQLITE_OK && exists && !locked ){
+ int nPage;
+ rc = sqlite3PagerPagecount(pPager, &nPage);
+ if( rc==SQLITE_OK ){
+ if( nPage==0 ){
+ sqlite3OsDelete(pVfs, pPager->zJournal, 0);
+ }else{
+ *pExists = 1;
+ }
+ }
+ }
}
+
+ return rc;
}
/*
@@ -2626,64 +3189,69 @@ static int hasHotJournal(Pager *pPager){
** This routine may return SQLITE_IOERR, SQLITE_FULL or SQLITE_OK. It
** does not set the pPager->errCode variable.
*/
-static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){
+static int pager_recycle(Pager *pPager, PgHdr **ppPg){
PgHdr *pPg;
*ppPg = 0;
+ /* It is illegal to call this function unless the pager object
+ ** pointed to by pPager has at least one free page (page with nRef==0).
+ */
assert(!MEMDB);
+ assert(pPager->lru.pFirst);
/* Find a page to recycle. Try to locate a page that does not
** require us to do an fsync() on the journal.
*/
- pPg = pPager->pFirstSynced;
+ pPg = pPager->lru.pFirstSynced;
/* If we could not find a page that does not require an fsync()
** on the journal file then fsync the journal file. This is a
** very slow operation, so we work hard to avoid it. But sometimes
** it can't be helped.
*/
- if( pPg==0 && pPager->pFirst && syncOk && !MEMDB){
- int rc = syncJournal(pPager);
- if( rc!=0 ){
- return rc;
- }
- if( pPager->fullSync ){
- /* If in full-sync mode, write a new journal header into the
- ** journal file. This is done to avoid ever modifying a journal
- ** header that is involved in the rollback of pages that have
- ** already been written to the database (in case the header is
- ** trashed when the nRec field is updated).
- */
- pPager->nRec = 0;
- assert( pPager->journalOff > 0 );
- assert( pPager->doNotSync==0 );
- rc = writeJournalHdr(pPager);
+ if( pPg==0 && pPager->lru.pFirst ){
+ if( !pPager->errCode ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ int rc = syncJournal(pPager);
if( rc!=0 ){
return rc;
}
+ if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
+ /* If in full-sync mode, write a new journal header into the
+ ** journal file. This is done to avoid ever modifying a journal
+ ** header that is involved in the rollback of pages that have
+ ** already been written to the database (in case the header is
+ ** trashed when the nRec field is updated).
+ */
+ pPager->nRec = 0;
+ assert( pPager->journalOff > 0 );
+ assert( pPager->doNotSync==0 );
+ rc = writeJournalHdr(pPager);
+ if( rc!=0 ){
+ return rc;
+ }
+ }
}
- pPg = pPager->pFirst;
- }
- if( pPg==0 ){
- return SQLITE_OK;
+ pPg = pPager->lru.pFirst;
}
assert( pPg->nRef==0 );
/* Write the page to the database file if it is dirty.
*/
- if( pPg->dirty ){
+ if( pPg->dirty && !pPager->errCode ){
int rc;
assert( pPg->needSync==0 );
makeClean(pPg);
pPg->dirty = 1;
pPg->pDirty = 0;
rc = pager_write_pagelist( pPg );
+ pPg->dirty = 0;
if( rc!=SQLITE_OK ){
return rc;
}
}
- assert( pPg->dirty==0 );
+ assert( pPg->dirty==0 || pPager->errCode );
/* If the page we are recycling is marked as alwaysRollback, then
** set the global alwaysRollback flag, thus disabling the
@@ -2707,109 +3275,152 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){
return SQLITE_OK;
}
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
** This function is called to free superfluous dynamically allocated memory
** held by the pager system. Memory in use by any SQLite pager allocated
-** by the current thread may be sqliteFree()ed.
+** by the current thread may be sqlite3_free()ed.
**
** nReq is the number of bytes of memory required. Once this much has
-** been released, the function returns. A negative value for nReq means
-** free as much memory as possible. The return value is the total number
+** been released, the function returns. The return value is the total number
** of bytes of memory released.
*/
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
int sqlite3PagerReleaseMemory(int nReq){
- const ThreadData *pTsdro = sqlite3ThreadDataReadOnly();
- int nReleased = 0;
- int i;
+ int nReleased = 0; /* Bytes of memory released so far */
+ Pager *pPager; /* For looping over pagers */
+ BusyHandler *savedBusy; /* Saved copy of the busy handler */
+ int rc = SQLITE_OK;
- /* If the the global mutex is held, this subroutine becomes a
- ** o-op; zero bytes of memory are freed. This is because
- ** some of the code invoked by this function may also
- ** try to obtain the mutex, resulting in a deadlock.
+ /* Acquire the memory-management mutex
*/
- if( sqlite3OsInMutex(0) ){
- return 0;
- }
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex; /* The MEM2 mutex */
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM2);
+#endif
+ sqlite3_mutex_enter(mutex);
- /* Outermost loop runs for at most two iterations. First iteration we
- ** try to find memory that can be released without calling fsync(). Second
- ** iteration (which only runs if the first failed to free nReq bytes of
- ** memory) is permitted to call fsync(). This is of course much more
- ** expensive.
+ /* Signal all database connections that memory management wants
+ ** to have access to the pagers.
*/
- for(i=0; i<=1; i++){
-
- /* Loop through all the SQLite pagers opened by the current thread. */
- Pager *pPager = pTsdro->pPager;
- for( ; pPager && (nReq<0 || nReleased<nReq); pPager=pPager->pNext){
- PgHdr *pPg;
- int rc;
+ for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
+ pPager->iInUseMM = 1;
+ }
- if( MEMDB ){
- continue;
+ while( rc==SQLITE_OK && (nReq<0 || nReleased<nReq) ){
+ PgHdr *pPg;
+ PgHdr *pRecycled;
+
+ /* Try to find a page to recycle that does not require a sync(). If
+ ** this is not possible, find one that does require a sync().
+ */
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
+ pPg = sqlite3LruPageList.pFirstSynced;
+ while( pPg && (pPg->needSync || pPg->pPager->iInUseDB) ){
+ pPg = pPg->gfree.pNext;
+ }
+ if( !pPg ){
+ pPg = sqlite3LruPageList.pFirst;
+ while( pPg && pPg->pPager->iInUseDB ){
+ pPg = pPg->gfree.pNext;
}
+ }
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU));
- /* For each pager, try to free as many pages as possible (without
- ** calling fsync() if this is the first iteration of the outermost
- ** loop).
- */
- while( SQLITE_OK==(rc = pager_recycle(pPager, i, &pPg)) && pPg) {
- /* We've found a page to free. At this point the page has been
- ** removed from the page hash-table, free-list and synced-list
- ** (pFirstSynced). It is still in the all pages (pAll) list.
- ** Remove it from this list before freeing.
- **
- ** Todo: Check the Pager.pStmt list to make sure this is Ok. It
- ** probably is though.
- */
- PgHdr *pTmp;
- assert( pPg );
- if( pPg==pPager->pAll ){
- pPager->pAll = pPg->pNextAll;
- }else{
- for( pTmp=pPager->pAll; pTmp->pNextAll!=pPg; pTmp=pTmp->pNextAll ){}
- pTmp->pNextAll = pPg->pNextAll;
- }
- nReleased += sqliteAllocSize(pPg);
- IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
- PAGER_INCR(sqlite3_pager_pgfree_count);
- sqliteFree(pPg);
- }
+ /* If pPg==0, then the block above has failed to find a page to
+ ** recycle. In this case return early - no further memory will
+ ** be released.
+ */
+ if( !pPg ) break;
- if( rc!=SQLITE_OK ){
- /* An error occured whilst writing to the database file or
- ** journal in pager_recycle(). The error is not returned to the
- ** caller of this function. Instead, set the Pager.errCode variable.
- ** The error will be returned to the user (or users, in the case
- ** of a shared pager cache) of the pager for which the error occured.
- */
- assert(
- (rc&0xff)==SQLITE_IOERR ||
- rc==SQLITE_FULL ||
- rc==SQLITE_BUSY
- );
- assert( pPager->state>=PAGER_RESERVED );
- pager_error(pPager, rc);
+ pPager = pPg->pPager;
+ assert(!pPg->needSync || pPg==pPager->lru.pFirst);
+ assert(pPg->needSync || pPg==pPager->lru.pFirstSynced);
+
+ savedBusy = pPager->pBusyHandler;
+ pPager->pBusyHandler = 0;
+ rc = pager_recycle(pPager, &pRecycled);
+ pPager->pBusyHandler = savedBusy;
+ assert(pRecycled==pPg || rc!=SQLITE_OK);
+ if( rc==SQLITE_OK ){
+ /* We've found a page to free. At this point the page has been
+ ** removed from the page hash-table, free-list and synced-list
+ ** (pFirstSynced). It is still in the all pages (pAll) list.
+ ** Remove it from this list before freeing.
+ **
+ ** Todo: Check the Pager.pStmt list to make sure this is Ok. It
+ ** probably is though.
+ */
+ PgHdr *pTmp;
+ assert( pPg );
+ if( pPg==pPager->pAll ){
+ assert(pPg->pPrevAll==0);
+ assert(pPg->pNextAll==0 || pPg->pNextAll->pPrevAll==pPg);
+ pPager->pAll = pPg->pNextAll;
+ if( pPager->pAll ){
+ pPager->pAll->pPrevAll = 0;
+ }
+ }else{
+ assert(pPg->pPrevAll);
+ assert(pPg->pPrevAll->pNextAll==pPg);
+ pTmp = pPg->pPrevAll;
+ pTmp->pNextAll = pPg->pNextAll;
+ if( pTmp->pNextAll ){
+ pTmp->pNextAll->pPrevAll = pTmp;
+ }
}
+ nReleased += (
+ sizeof(*pPg) + pPager->pageSize
+ + sizeof(u32) + pPager->nExtra
+ + MEMDB*sizeof(PgHistory)
+ );
+ IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
+ PAGER_INCR(sqlite3_pager_pgfree_count);
+ sqlite3PageFree(pPg->pData);
+ sqlite3_free(pPg);
+ pPager->nPage--;
+ }else{
+ /* An error occured whilst writing to the database file or
+ ** journal in pager_recycle(). The error is not returned to the
+ ** caller of this function. Instead, set the Pager.errCode variable.
+ ** The error will be returned to the user (or users, in the case
+ ** of a shared pager cache) of the pager for which the error occured.
+ */
+ assert(
+ (rc&0xff)==SQLITE_IOERR ||
+ rc==SQLITE_FULL ||
+ rc==SQLITE_BUSY
+ );
+ assert( pPager->state>=PAGER_RESERVED );
+ pager_error(pPager, rc);
}
}
+ /* Clear the memory management flags and release the mutex
+ */
+ for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
+ pPager->iInUseMM = 0;
+ }
+ sqlite3_mutex_leave(mutex);
+
+ /* Return the number of bytes released
+ */
return nReleased;
}
-#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT && !SQLITE_OMIT_DISKIO */
+#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
/*
** Read the content of page pPg out of the database file.
*/
static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
int rc;
+ i64 offset;
assert( MEMDB==0 );
- rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
- if( rc==SQLITE_OK ){
- rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg),
- pPager->pageSize);
+ assert(pPager->fd->pMethods||pPager->tempFile);
+ if( !pPager->fd->pMethods ){
+ return SQLITE_IOERR_SHORT_READ;
}
+ offset = (pgno-1)*(i64)pPager->pageSize;
+ rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize, offset);
PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
IOTRACE(("PGIN %p %d\n", pPager, pgno));
@@ -2835,13 +3446,38 @@ static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
*/
static int pagerSharedLock(Pager *pPager){
int rc = SQLITE_OK;
+ int isErrorReset = 0;
- if( pPager->state==PAGER_UNLOCK ){
+ /* If this database is opened for exclusive access, has no outstanding
+ ** page references and is in an error-state, now is the chance to clear
+ ** the error. Discard the contents of the pager-cache and treat any
+ ** open journal file as a hot-journal.
+ */
+ if( !MEMDB && pPager->exclusiveMode && pPager->nRef==0 && pPager->errCode ){
+ if( pPager->journalOpen ){
+ isErrorReset = 1;
+ }
+ pPager->errCode = SQLITE_OK;
+ pager_reset(pPager);
+ }
+
+ /* If the pager is still in an error state, do not proceed. The error
+ ** state will be cleared at some point in the future when all page
+ ** references are dropped and the cache can be discarded.
+ */
+ if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
+ return pPager->errCode;
+ }
+
+ if( pPager->state==PAGER_UNLOCK || isErrorReset ){
+ sqlite3_vfs *pVfs = pPager->pVfs;
if( !MEMDB ){
+ int isHotJournal;
assert( pPager->nRef==0 );
if( !pPager->noReadlock ){
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
+ assert( pPager->state==PAGER_UNLOCK );
return pager_error(pPager, rc);
}
assert( pPager->state>=SHARED_LOCK );
@@ -2850,7 +3486,13 @@ static int pagerSharedLock(Pager *pPager){
/* If a journal file exists, and there is no RESERVED lock on the
** database file, then it either needs to be played back or deleted.
*/
- if( hasHotJournal(pPager) ){
+ if( !isErrorReset ){
+ rc = hasHotJournal(pPager, &isHotJournal);
+ if( rc!=SQLITE_OK ){
+ goto failed;
+ }
+ }
+ if( isErrorReset || isHotJournal ){
/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
@@ -2860,43 +3502,51 @@ static int pagerSharedLock(Pager *pPager){
**
** Because the intermediate RESERVED lock is not requested, the
** second process will get to this point in the code and fail to
- ** obtain it's own EXCLUSIVE lock on the database file.
+ ** obtain its own EXCLUSIVE lock on the database file.
*/
- rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
- if( rc!=SQLITE_OK ){
- pager_unlock(pPager);
- return pager_error(pPager, rc);
+ if( pPager->state<EXCLUSIVE_LOCK ){
+ rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
+ if( rc!=SQLITE_OK ){
+ rc = pager_error(pPager, rc);
+ goto failed;
+ }
+ pPager->state = PAGER_EXCLUSIVE;
}
- pPager->state = PAGER_EXCLUSIVE;
- /* Open the journal for reading only. Return SQLITE_BUSY if
- ** we are unable to open the journal file.
- **
- ** The journal file does not need to be locked itself. The
- ** journal file is never open unless the main database file holds
- ** a write lock, so there is never any chance of two or more
- ** processes opening the journal at the same time.
- **
- ** Open the journal for read/write access. This is because in
+ /* Open the journal for read/write access. This is because in
** exclusive-access mode the file descriptor will be kept open and
** possibly used for a transaction later on. On some systems, the
** OsTruncate() call used in exclusive-access mode also requires
** a read/write file handle.
*/
- rc = SQLITE_BUSY;
- if( sqlite3OsFileExists(pPager->zJournal) ){
- int ro;
- assert( !pPager->tempFile );
- rc = sqlite3OsOpenReadWrite(pPager->zJournal, &pPager->jfd, &ro);
- assert( rc!=SQLITE_OK || pPager->jfd );
- if( ro ){
- rc = SQLITE_BUSY;
- sqlite3OsClose(&pPager->jfd);
+ if( !isErrorReset && pPager->journalOpen==0 ){
+ int res;
+ rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res);
+ if( rc==SQLITE_OK ){
+ if( res ){
+ int fout = 0;
+ int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
+ assert( !pPager->tempFile );
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
+ assert( rc!=SQLITE_OK || pPager->jfd->pMethods );
+ if( fout&SQLITE_OPEN_READONLY ){
+ rc = SQLITE_BUSY;
+ sqlite3OsClose(pPager->jfd);
+ }
+ }else{
+ /* If the journal does not exist, that means some other process
+ ** has already rolled it back */
+ rc = SQLITE_BUSY;
+ }
}
}
if( rc!=SQLITE_OK ){
- pager_unlock(pPager);
- return SQLITE_BUSY;
+ if( rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_UNLOCK
+ && rc!=SQLITE_IOERR_NOMEM
+ ){
+ rc = SQLITE_BUSY;
+ }
+ goto failed;
}
pPager->journalOpen = 1;
pPager->journalStarted = 0;
@@ -2909,7 +3559,8 @@ static int pagerSharedLock(Pager *pPager){
*/
rc = pager_playback(pPager, 1);
if( rc!=SQLITE_OK ){
- return pager_error(pPager, rc);
+ rc = pager_error(pPager, rc);
+ goto failed;
}
assert(pPager->state==PAGER_SHARED ||
(pPager->exclusiveMode && pPager->state>PAGER_SHARED)
@@ -2934,21 +3585,18 @@ static int pagerSharedLock(Pager *pPager){
** it can be neglected.
*/
char dbFileVers[sizeof(pPager->dbFileVers)];
- sqlite3PagerPagecount(pPager);
+ sqlite3PagerPagecount(pPager, 0);
if( pPager->errCode ){
- return pPager->errCode;
+ rc = pPager->errCode;
+ goto failed;
}
if( pPager->dbSize>0 ){
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
- rc = sqlite3OsSeek(pPager->fd, 24);
+ rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers));
- if( rc!=SQLITE_OK ){
- return rc;
+ goto failed;
}
}else{
memset(dbFileVers, 0, sizeof(dbFileVers));
@@ -2965,6 +3613,11 @@ static int pagerSharedLock(Pager *pPager){
}
}
+ failed:
+ if( rc!=SQLITE_OK ){
+ /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
+ pager_unlock(pPager);
+ }
return rc;
}
@@ -3003,14 +3656,16 @@ static int pagerSharedLock(Pager *pPager){
static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
int rc = SQLITE_OK;
PgHdr *pPg;
+ int nByteHdr;
/* Create a new PgHdr if any of the four conditions defined
- ** above is met: */
+ ** above are met: */
if( pPager->nPage<pPager->mxPage
- || pPager->pFirst==0
+ || pPager->lru.pFirst==0
|| MEMDB
- || (pPager->pFirstSynced==0 && pPager->doNotSync)
+ || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
){
+ void *pData;
if( pPager->nPage>=pPager->nHash ){
pager_resize_hash_table(pPager,
pPager->nHash<256 ? 256 : pPager->nHash*2);
@@ -3019,24 +3674,36 @@ static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
goto pager_allocate_out;
}
}
- pPg = sqliteMallocRaw( sizeof(*pPg) + pPager->pageSize
- + sizeof(u32) + pPager->nExtra
- + MEMDB*sizeof(PgHistory) );
+ pagerLeave(pPager);
+ nByteHdr = sizeof(*pPg) + sizeof(u32) + pPager->nExtra
+ + MEMDB*sizeof(PgHistory);
+ pPg = sqlite3Malloc( nByteHdr );
+ if( pPg ){
+ pData = sqlite3PageMalloc( pPager->pageSize );
+ if( pData==0 ){
+ sqlite3_free(pPg);
+ pPg = 0;
+ }
+ }
+ pagerEnter(pPager);
if( pPg==0 ){
rc = SQLITE_NOMEM;
goto pager_allocate_out;
}
- memset(pPg, 0, sizeof(*pPg));
- if( MEMDB ){
- memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
- }
+ memset(pPg, 0, nByteHdr);
+ pPg->pData = pData;
pPg->pPager = pPager;
pPg->pNextAll = pPager->pAll;
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ if( pPg->pNextAll ){
+ pPg->pNextAll->pPrevAll = pPg;
+ }
+#endif
pPager->pAll = pPg;
pPager->nPage++;
}else{
/* Recycle an existing page with a zero ref-count. */
- rc = pager_recycle(pPager, 1, &pPg);
+ rc = pager_recycle(pPager, &pPg);
if( rc==SQLITE_BUSY ){
rc = SQLITE_IOERR_BLOCKED;
}
@@ -3103,7 +3770,7 @@ static int pager_get_content(PgHdr *pPg){
** called again with noContent==0, that means that the content is needed
** and the disk read should occur at that point.
*/
-int sqlite3PagerAcquire(
+static int pagerAcquire(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
@@ -3144,9 +3811,6 @@ int sqlite3PagerAcquire2(
*/
assert( pPager!=0 );
*ppPage = 0;
- if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
- return pPager->errCode;
- }
/* If this is the first page accessed, then get a SHARED lock
** on the database file. pagerSharedLock() is a no-op if
@@ -3171,28 +3835,19 @@ int sqlite3PagerAcquire2(
pPg->pgno = pgno;
assert( !MEMDB || pgno>pPager->stmtSize );
- if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
- sqlite3CheckMemory(pPager->aInJournal, pgno/8);
- assert( pPager->journalOpen );
- pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
- pPg->needSync = 0;
- }else{
- pPg->inJournal = 0;
- pPg->needSync = 0;
- }
+ pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
+ pPg->needSync = 0;
makeClean(pPg);
pPg->nRef = 1;
- REFINFO(pPg);
pPager->nRef++;
if( pPager->nExtra>0 ){
memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
}
- nMax = sqlite3PagerPagecount(pPager);
- if( pPager->errCode ){
+ rc = sqlite3PagerPagecount(pPager, &nMax);
+ if( rc!=SQLITE_OK ){
sqlite3PagerUnref(pPg);
- rc = pPager->errCode;
return rc;
}
@@ -3252,6 +3907,19 @@ int sqlite3PagerAcquire2(
*ppPage = pPg;
return SQLITE_OK;
}
+int sqlite3PagerAcquire(
+ Pager *pPager, /* The pager open on the database file */
+ Pgno pgno, /* Page number to fetch */
+ DbPage **ppPage, /* Write a pointer to the page here */
+ int noContent /* Do not bother reading content from disk if true */
+){
+ int rc;
+ pagerEnter(pPager);
+ rc = pagerAcquire(pPager, pgno, ppPage, noContent);
+ pagerLeave(pPager);
+ return rc;
+}
+
/*
** Acquire a page if it is already in the in-memory cache. Do
@@ -3265,21 +3933,20 @@ int sqlite3PagerAcquire2(
** has ever happened.
*/
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
- PgHdr *pPg;
+ PgHdr *pPg = 0;
assert( pPager!=0 );
assert( pgno!=0 );
+ pagerEnter(pPager);
if( pPager->state==PAGER_UNLOCK ){
assert( !pPager->pAll || pPager->exclusiveMode );
- return 0;
- }
- if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
- return 0;
+ }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
+ /* Do nothing */
+ }else if( (pPg = pager_lookup(pPager, pgno))!=0 ){
+ page_ref(pPg);
}
- pPg = pager_lookup(pPager, pgno);
- if( pPg==0 ) return 0;
- page_ref(pPg);
+ pagerLeave(pPager);
return pPg;
}
@@ -3292,12 +3959,16 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
** removed.
*/
int sqlite3PagerUnref(DbPage *pPg){
+ Pager *pPager;
+
+ if( pPg==0 ) return SQLITE_OK;
+ pPager = pPg->pPager;
/* Decrement the reference count for this page
*/
assert( pPg->nRef>0 );
+ pagerEnter(pPg->pPager);
pPg->nRef--;
- REFINFO(pPg);
CHECK_PAGE(pPg);
@@ -3305,19 +3976,8 @@ int sqlite3PagerUnref(DbPage *pPg){
** destructor and add the page to the freelist.
*/
if( pPg->nRef==0 ){
- Pager *pPager;
- pPager = pPg->pPager;
- pPg->pNextFree = 0;
- pPg->pPrevFree = pPager->pLast;
- pPager->pLast = pPg;
- if( pPg->pPrevFree ){
- pPg->pPrevFree->pNextFree = pPg;
- }else{
- pPager->pFirst = pPg;
- }
- if( pPg->needSync==0 && pPager->pFirstSynced==0 ){
- pPager->pFirstSynced = pPg;
- }
+
+ lruListAdd(pPg);
if( pPager->xDestructor ){
pPager->xDestructor(pPg, pPager->pageSize);
}
@@ -3331,6 +3991,7 @@ int sqlite3PagerUnref(DbPage *pPg){
pagerUnlockAndRollback(pPager);
}
}
+ pagerLeave(pPager);
return SQLITE_OK;
}
@@ -3342,33 +4003,47 @@ int sqlite3PagerUnref(DbPage *pPg){
** write lock if anything goes wrong.
*/
static int pager_open_journal(Pager *pPager){
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ int flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_CREATE);
+
int rc;
assert( !MEMDB );
assert( pPager->state>=PAGER_RESERVED );
- assert( pPager->journalOpen==0 );
assert( pPager->useJournal );
- assert( pPager->aInJournal==0 );
- sqlite3PagerPagecount(pPager);
- pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
- if( pPager->aInJournal==0 ){
+ assert( pPager->pInJournal==0 );
+ sqlite3PagerPagecount(pPager, 0);
+ pagerLeave(pPager);
+ pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
+ pagerEnter(pPager);
+ if( pPager->pInJournal==0 ){
rc = SQLITE_NOMEM;
goto failed_to_open_journal;
}
- rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,
- pPager->tempFile);
- assert( rc!=SQLITE_OK || pPager->jfd );
- pPager->journalOff = 0;
- pPager->setMaster = 0;
- pPager->journalHdr = 0;
- if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ){
- sqlite3OsDelete(pPager->zJournal);
+
+ if( pPager->journalOpen==0 ){
+ if( pPager->tempFile ){
+ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
+ }else{
+ flags |= (SQLITE_OPEN_MAIN_JOURNAL);
+ }
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ rc = sqlite3JournalOpen(
+ pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
+ );
+#else
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
+#endif
+ assert( rc!=SQLITE_OK || pPager->jfd->pMethods );
+ pPager->journalOff = 0;
+ pPager->setMaster = 0;
+ pPager->journalHdr = 0;
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_NOMEM ){
+ sqlite3OsDelete(pVfs, pPager->zJournal, 0);
+ }
+ goto failed_to_open_journal;
}
- goto failed_to_open_journal;
}
- sqlite3OsSetFullSync(pPager->jfd, pPager->full_fsync);
- sqlite3OsSetFullSync(pPager->fd, pPager->full_fsync);
- sqlite3OsOpenDirectory(pPager->jfd, pPager->zDirectory);
pPager->journalOpen = 1;
pPager->journalStarted = 0;
pPager->needSync = 0;
@@ -3385,8 +4060,8 @@ static int pager_open_journal(Pager *pPager){
if( pPager->stmtAutoopen && rc==SQLITE_OK ){
rc = sqlite3PagerStmtBegin(pPager);
}
- if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
- rc = pager_end_transaction(pPager);
+ if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_NOMEM ){
+ rc = pager_end_transaction(pPager, 0);
if( rc==SQLITE_OK ){
rc = SQLITE_FULL;
}
@@ -3394,8 +4069,8 @@ static int pager_open_journal(Pager *pPager){
return rc;
failed_to_open_journal:
- sqliteFree(pPager->aInJournal);
- pPager->aInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
return rc;
}
@@ -3429,10 +4104,11 @@ failed_to_open_journal:
int sqlite3PagerBegin(DbPage *pPg, int exFlag){
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
+ pagerEnter(pPager);
assert( pPg->nRef>0 );
assert( pPager->state!=PAGER_UNLOCK );
if( pPager->state==PAGER_SHARED ){
- assert( pPager->aInJournal==0 );
+ assert( pPager->pInJournal==0 );
if( MEMDB ){
pPager->state = PAGER_EXCLUSIVE;
pPager->origDbSize = pPager->dbSize;
@@ -3445,26 +4121,31 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
}
}
if( rc!=SQLITE_OK ){
+ pagerLeave(pPager);
return rc;
}
pPager->dirtyCache = 0;
PAGERTRACE2("TRANSACTION %d\n", PAGERID(pPager));
- if( pPager->useJournal && !pPager->tempFile ){
+ if( pPager->useJournal && !pPager->tempFile
+ && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
rc = pager_open_journal(pPager);
}
}
}else if( pPager->journalOpen && pPager->journalOff==0 ){
- /* This happens when the pager was in exclusive-access mode last
+ /* This happens when the pager was in exclusive-access mode the last
** time a (read or write) transaction was successfully concluded
** by this connection. Instead of deleting the journal file it was
- ** kept open and truncated to 0 bytes.
+ ** kept open and either was truncated to 0 bytes or its header was
+ ** overwritten with zeros.
*/
assert( pPager->nRec==0 );
assert( pPager->origDbSize==0 );
- assert( pPager->aInJournal==0 );
- sqlite3PagerPagecount(pPager);
- pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
- if( !pPager->aInJournal ){
+ assert( pPager->pInJournal==0 );
+ sqlite3PagerPagecount(pPager, 0);
+ pagerLeave(pPager);
+ pPager->pInJournal = sqlite3BitvecCreate( pPager->dbSize );
+ pagerEnter(pPager);
+ if( !pPager->pInJournal ){
rc = SQLITE_NOMEM;
}else{
pPager->origDbSize = pPager->dbSize;
@@ -3472,6 +4153,7 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
}
}
assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK );
+ pagerLeave(pPager);
return rc;
}
@@ -3500,11 +4182,14 @@ static void makeClean(PgHdr *pPg){
if( pPg->dirty ){
pPg->dirty = 0;
if( pPg->pDirty ){
+ assert( pPg->pDirty->pPrevDirty==pPg );
pPg->pDirty->pPrevDirty = pPg->pPrevDirty;
}
if( pPg->pPrevDirty ){
+ assert( pPg->pPrevDirty->pDirty==pPg );
pPg->pPrevDirty->pDirty = pPg->pDirty;
}else{
+ assert( pPg->pPager->pDirty==pPg );
pPg->pPager->pDirty = pPg->pDirty;
}
}
@@ -3565,6 +4250,7 @@ static int pager_write(PgHdr *pPg){
makeDirty(pPg);
if( pPg->inJournal && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
pPager->dirtyCache = 1;
+ pPager->dbModified = 1;
}else{
/* If we get this far, it means that the page needs to be
@@ -3580,51 +4266,54 @@ static int pager_write(PgHdr *pPg){
return rc;
}
assert( pPager->state>=PAGER_RESERVED );
- if( !pPager->journalOpen && pPager->useJournal ){
+ if( !pPager->journalOpen && pPager->useJournal
+ && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
rc = pager_open_journal(pPager);
if( rc!=SQLITE_OK ) return rc;
}
- assert( pPager->journalOpen || !pPager->useJournal );
pPager->dirtyCache = 1;
+ pPager->dbModified = 1;
/* The transaction journal now exists and we have a RESERVED or an
** EXCLUSIVE lock on the main database file. Write the current page to
** the transaction journal if it is not there already.
*/
- if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){
+ if( !pPg->inJournal && (pPager->journalOpen || MEMDB) ){
if( (int)pPg->pgno <= pPager->origDbSize ){
- int szPg;
if( MEMDB ){
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
PAGERTRACE3("JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
assert( pHist->pOrig==0 );
- pHist->pOrig = sqliteMallocRaw( pPager->pageSize );
- if( pHist->pOrig ){
- memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
+ pHist->pOrig = sqlite3PageMalloc( pPager->pageSize );
+ if( !pHist->pOrig ){
+ return SQLITE_NOMEM;
}
+ memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
}else{
- u32 cksum, saved;
- char *pData2, *pEnd;
+ u32 cksum;
+ char *pData2;
+
/* We should never write to the journal file the page that
** contains the database locks. The following assert verifies
** that we do not. */
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
cksum = pager_cksum(pPager, (u8*)pData2);
- pEnd = pData2 + pPager->pageSize;
- pData2 -= 4;
- saved = *(u32*)pEnd;
- put32bits(pEnd, cksum);
- szPg = pPager->pageSize+8;
- put32bits(pData2, pPg->pgno);
- rc = sqlite3OsWrite(pPager->jfd, pData2, szPg);
- IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
- pPager->journalOff, szPg));
+ rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize,
+ pPager->journalOff + 4);
+ pPager->journalOff += pPager->pageSize+4;
+ }
+ if( rc==SQLITE_OK ){
+ rc = write32bits(pPager->jfd, pPager->journalOff, cksum);
+ pPager->journalOff += 4;
+ }
+ IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
+ pPager->journalOff, pPager->pageSize));
PAGER_INCR(sqlite3_pager_writej_count);
- pPager->journalOff += szPg;
PAGERTRACE5("JOURNAL %d page %d needSync=%d hash(%08x)\n",
PAGERID(pPager), pPg->pgno, pPg->needSync, pager_pagehash(pPg));
- *(u32*)pEnd = saved;
/* An error has occured writing to the journal file. The
** transaction will be rolled back by the layer above.
@@ -3634,11 +4323,11 @@ static int pager_write(PgHdr *pPg){
}
pPager->nRec++;
- assert( pPager->aInJournal!=0 );
- pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( pPager->pInJournal!=0 );
+ sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
pPg->needSync = !pPager->noSync;
if( pPager->stmtInUse ){
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
}
}else{
@@ -3665,23 +4354,26 @@ static int pager_write(PgHdr *pPg){
if( MEMDB ){
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
assert( pHist->pStmt==0 );
- pHist->pStmt = sqliteMallocRaw( pPager->pageSize );
+ pHist->pStmt = sqlite3PageMalloc( pPager->pageSize );
if( pHist->pStmt ){
memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
}
PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
page_add_to_stmt_list(pPg);
}else{
- char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7)-4;
- put32bits(pData2, pPg->pgno);
- rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize+4);
+ i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
+ char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
+ rc = write32bits(pPager->stfd, offset, pPg->pgno);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4);
+ }
PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
if( rc!=SQLITE_OK ){
return rc;
}
pPager->stmtNRec++;
- assert( pPager->aInStmt!=0 );
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( pPager->pInStmt!=0 );
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
}
}
@@ -3715,11 +4407,13 @@ int sqlite3PagerWrite(DbPage *pDbPage){
Pager *pPager = pPg->pPager;
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
+ pagerEnter(pPager);
if( !MEMDB && nPagePerSector>1 ){
Pgno nPageCount; /* Total number of pages in database file */
Pgno pg1; /* First page of the sector pPg is located on. */
int nPage; /* Number of pages starting at pg1 to journal */
int ii;
+ int needSync = 0;
/* Set the doNotSync flag to 1. This is because we cannot allow a journal
** header to be written between the pages journaled by this function.
@@ -3733,7 +4427,7 @@ int sqlite3PagerWrite(DbPage *pDbPage){
*/
pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
- nPageCount = sqlite3PagerPagecount(pPager);
+ sqlite3PagerPagecount(pPager, (int *)&nPageCount);
if( pPg->pgno>nPageCount ){
nPage = (pPg->pgno - pg1)+1;
}else if( (pg1+nPagePerSector-1)>nPageCount ){
@@ -3747,25 +4441,45 @@ int sqlite3PagerWrite(DbPage *pDbPage){
for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
Pgno pg = pg1+ii;
- if( !pPager->aInJournal || pg==pPg->pgno ||
- pg>pPager->origDbSize || !(pPager->aInJournal[pg/8]&(1<<(pg&7)))
- ) {
+ PgHdr *pPage;
+ if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
if( pg!=PAGER_MJ_PGNO(pPager) ){
- PgHdr *pPage;
rc = sqlite3PagerGet(pPager, pg, &pPage);
if( rc==SQLITE_OK ){
rc = pager_write(pPage);
+ if( pPage->needSync ){
+ needSync = 1;
+ }
sqlite3PagerUnref(pPage);
}
}
+ }else if( (pPage = pager_lookup(pPager, pg))!=0 ){
+ if( pPage->needSync ){
+ needSync = 1;
+ }
}
}
+ /* If the PgHdr.needSync flag is set for any of the nPage pages
+ ** starting at pg1, then it needs to be set for all of them. Because
+ ** writing to any of these nPage pages may damage the others, the
+ ** journal file must contain sync()ed copies of all of them
+ ** before any of them can be written out to the database file.
+ */
+ if( needSync ){
+ for(ii=0; ii<nPage && needSync; ii++){
+ PgHdr *pPage = pager_lookup(pPager, pg1+ii);
+ if( pPage ) pPage->needSync = 1;
+ }
+ assert(pPager->needSync);
+ }
+
assert( pPager->doNotSync==1 );
pPager->doNotSync = 0;
}else{
rc = pager_write(pDbPage);
}
+ pagerLeave(pPager);
return rc;
}
@@ -3780,27 +4494,6 @@ int sqlite3PagerIswriteable(DbPage *pPg){
}
#endif
-#ifndef SQLITE_OMIT_VACUUM
-/*
-** Replace the content of a single page with the information in the third
-** argument.
-*/
-int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void *pData){
- PgHdr *pPg;
- int rc;
-
- rc = sqlite3PagerGet(pPager, pgno, &pPg);
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerWrite(pPg);
- if( rc==SQLITE_OK ){
- memcpy(sqlite3PagerGetData(pPg), pData, pPager->pageSize);
- }
- sqlite3PagerUnref(pPg);
- }
- return rc;
-}
-#endif
-
/*
** A call to this routine tells the pager that it is not necessary to
** write the information on page pPg back to the disk, even though
@@ -3830,6 +4523,7 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
Pager *pPager = pPg->pPager;
if( MEMDB ) return;
+ pagerEnter(pPager);
pPg->alwaysRollback = 1;
if( pPg->dirty && !pPager->stmtInUse ){
assert( pPager->state>=PAGER_SHARED );
@@ -3851,6 +4545,7 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
#endif
}
}
+ pagerLeave(pPager);
}
/*
@@ -3867,28 +4562,48 @@ void sqlite3PagerDontWrite(DbPage *pDbPage){
void sqlite3PagerDontRollback(DbPage *pPg){
Pager *pPager = pPg->pPager;
+ pagerEnter(pPager);
assert( pPager->state>=PAGER_RESERVED );
- if( pPager->journalOpen==0 ) return;
- if( pPg->alwaysRollback || pPager->alwaysRollback || MEMDB ) return;
- if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
- assert( pPager->aInJournal!=0 );
- pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
- pPg->inJournal = 1;
- pPg->needRead = 0;
- if( pPager->stmtInUse ){
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
- }
- PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
- IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
+
+ /* If the journal file is not open, or DontWrite() has been called on
+ ** this page (DontWrite() sets the alwaysRollback flag), then this
+ ** function is a no-op.
+ */
+ if( pPager->journalOpen==0 || pPg->alwaysRollback || pPager->alwaysRollback ){
+ pagerLeave(pPager);
+ return;
}
- if( pPager->stmtInUse
- && !pageInStatement(pPg)
- && (int)pPg->pgno<=pPager->stmtSize
- ){
- assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
- assert( pPager->aInStmt!=0 );
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( !MEMDB ); /* For a memdb, pPager->journalOpen is always 0 */
+
+#ifdef SQLITE_SECURE_DELETE
+ if( pPg->inJournal || (int)pPg->pgno > pPager->origDbSize ){
+ return;
+ }
+#endif
+
+ /* If SECURE_DELETE is disabled, then there is no way that this
+ ** routine can be called on a page for which sqlite3PagerDontWrite()
+ ** has not been previously called during the same transaction.
+ ** And if DontWrite() has previously been called, the following
+ ** conditions must be met.
+ **
+ ** (Later:) Not true. If the database is corrupted by having duplicate
+ ** pages on the freelist (ex: corrupt9.test) then the following is not
+ ** necessarily true:
+ */
+ /* assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ); */
+
+ assert( pPager->pInJournal!=0 );
+ sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
+ pPg->inJournal = 1;
+ pPg->needRead = 0;
+ if( pPager->stmtInUse ){
+ assert( pPager->stmtSize >= pPager->origDbSize );
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
+ PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
+ IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
+ pagerLeave(pPager);
}
@@ -3896,27 +4611,55 @@ void sqlite3PagerDontRollback(DbPage *pPg){
** This routine is called to increment the database file change-counter,
** stored at byte 24 of the pager file.
*/
-static int pager_incr_changecounter(Pager *pPager){
+static int pager_incr_changecounter(Pager *pPager, int isDirect){
PgHdr *pPgHdr;
u32 change_counter;
- int rc;
+ int rc = SQLITE_OK;
+#ifndef SQLITE_ENABLE_ATOMIC_WRITE
+ assert( isDirect==0 ); /* isDirect is only true for atomic writes */
+#endif
if( !pPager->changeCountDone ){
/* Open page 1 of the file for writing. */
rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3PagerWrite(pPgHdr);
- if( rc!=SQLITE_OK ) return rc;
-
+
+ if( !isDirect ){
+ rc = sqlite3PagerWrite(pPgHdr);
+ if( rc!=SQLITE_OK ){
+ sqlite3PagerUnref(pPgHdr);
+ return rc;
+ }
+ }
+
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers);
change_counter++;
put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ if( isDirect && pPager->fd->pMethods ){
+ const void *zBuf = PGHDR_TO_DATA(pPgHdr);
+ rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
+ }
+#endif
+
/* Release the page reference. */
sqlite3PagerUnref(pPgHdr);
pPager->changeCountDone = 1;
}
- return SQLITE_OK;
+ return rc;
+}
+
+/*
+** Sync the pager file to disk.
+*/
+int sqlite3PagerSync(Pager *pPager){
+ int rc;
+ pagerEnter(pPager);
+ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
+ pagerLeave(pPager);
+ return rc;
}
/*
@@ -3935,19 +4678,84 @@ static int pager_incr_changecounter(Pager *pPager){
**
** If parameter nTrunc is non-zero, then the pager file is truncated to
** nTrunc pages (this is used by auto-vacuum databases).
+**
+** If the final parameter - noSync - is true, then the database file itself
+** is not synced. The caller must call sqlite3PagerSync() directly to
+** sync the database file before calling CommitPhaseTwo() to delete the
+** journal file in this case.
*/
-int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
+int sqlite3PagerCommitPhaseOne(
+ Pager *pPager,
+ const char *zMaster,
+ Pgno nTrunc,
+ int noSync
+){
int rc = SQLITE_OK;
+ if( pPager->errCode ){
+ return pPager->errCode;
+ }
+
+ /* If no changes have been made, we can leave the transaction early.
+ */
+ if( pPager->dbModified==0 &&
+ (pPager->journalMode!=PAGER_JOURNALMODE_DELETE ||
+ pPager->exclusiveMode!=0) ){
+ assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
+ return SQLITE_OK;
+ }
+
PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n",
pPager->zFilename, zMaster, nTrunc);
+ pagerEnter(pPager);
/* If this is an in-memory db, or no pages have been written to, or this
** function has already been called, it is a no-op.
*/
if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
PgHdr *pPg;
- assert( pPager->journalOpen );
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ /* The atomic-write optimization can be used if all of the
+ ** following are true:
+ **
+ ** + The file-system supports the atomic-write property for
+ ** blocks of size page-size, and
+ ** + This commit is not part of a multi-file transaction, and
+ ** + Exactly one page has been modified and store in the journal file.
+ **
+ ** If the optimization can be used, then the journal file will never
+ ** be created for this transaction.
+ */
+ int useAtomicWrite = (
+ !zMaster &&
+ pPager->journalOpen &&
+ pPager->journalOff==jrnlBufferSize(pPager) &&
+ nTrunc==0 &&
+ (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
+ );
+ assert( pPager->journalOpen || pPager->journalMode==PAGER_JOURNALMODE_OFF );
+ if( useAtomicWrite ){
+ /* Update the nRec field in the journal file. */
+ int offset = pPager->journalHdr + sizeof(aJournalMagic);
+ assert(pPager->nRec==1);
+ rc = write32bits(pPager->jfd, offset, pPager->nRec);
+
+ /* Update the db file change counter. The following call will modify
+ ** the in-memory representation of page 1 to include the updated
+ ** change counter and then write page 1 directly to the database
+ ** file. Because of the atomic-write property of the host file-system,
+ ** this is safe.
+ */
+ if( rc==SQLITE_OK ){
+ rc = pager_incr_changecounter(pPager, 1);
+ }
+ }else{
+ rc = sqlite3JournalCreate(pPager->jfd);
+ }
+
+ if( !useAtomicWrite && rc==SQLITE_OK )
+#endif
/* If a master journal file name has already been written to the
** journal file, then no sync is required. This happens when it is
@@ -3956,32 +4764,34 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
** transaction the m-j name will have already been written.
*/
if( !pPager->setMaster ){
- rc = pager_incr_changecounter(pPager);
+ rc = pager_incr_changecounter(pPager, 0);
if( rc!=SQLITE_OK ) goto sync_exit;
+ if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( nTrunc!=0 ){
- /* If this transaction has made the database smaller, then all pages
- ** being discarded by the truncation must be written to the journal
- ** file.
- */
- Pgno i;
- int iSkip = PAGER_MJ_PGNO(pPager);
- for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){
- if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){
- rc = sqlite3PagerGet(pPager, i, &pPg);
- if( rc!=SQLITE_OK ) goto sync_exit;
- rc = sqlite3PagerWrite(pPg);
- sqlite3PagerUnref(pPg);
- if( rc!=SQLITE_OK ) goto sync_exit;
- }
- }
- }
+ if( nTrunc!=0 ){
+ /* If this transaction has made the database smaller, then all pages
+ ** being discarded by the truncation must be written to the journal
+ ** file.
+ */
+ Pgno i;
+ int iSkip = PAGER_MJ_PGNO(pPager);
+ for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){
+ if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
+ rc = sqlite3PagerGet(pPager, i, &pPg);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ rc = sqlite3PagerWrite(pPg);
+ sqlite3PagerUnref(pPg);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ }
+ }
+ }
#endif
- rc = writeMasterJournal(pPager, zMaster);
- if( rc!=SQLITE_OK ) goto sync_exit;
- rc = syncJournal(pPager);
- if( rc!=SQLITE_OK ) goto sync_exit;
+ rc = writeMasterJournal(pPager, zMaster);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ rc = syncJournal(pPager);
+ }
}
+ if( rc!=SQLITE_OK ) goto sync_exit;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( nTrunc!=0 ){
@@ -3993,12 +4803,22 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
/* Write all dirty pages to the database file */
pPg = pager_get_all_dirty_pages(pPager);
rc = pager_write_pagelist(pPg);
- if( rc!=SQLITE_OK ) goto sync_exit;
+ if( rc!=SQLITE_OK ){
+ assert( rc!=SQLITE_IOERR_BLOCKED );
+ /* The error might have left the dirty list all fouled up here,
+ ** but that does not matter because if the if the dirty list did
+ ** get corrupted, then the transaction will roll back and
+ ** discard the dirty list. There is an assert in
+ ** pager_get_all_dirty_pages() that verifies that no attempt
+ ** is made to use an invalid dirty list.
+ */
+ goto sync_exit;
+ }
pPager->pDirty = 0;
/* Sync the database file. */
- if( !pPager->noSync ){
- rc = sqlite3OsSync(pPager->fd, 0);
+ if( !pPager->noSync && !noSync ){
+ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
}
IOTRACE(("DBSYNC %p\n", pPager))
@@ -4011,11 +4831,12 @@ sync_exit:
if( rc==SQLITE_IOERR_BLOCKED ){
/* pager_incr_changecounter() may attempt to obtain an exclusive
* lock to spill the cache and return IOERR_BLOCKED. But since
- * there is no chance the cache is inconsistent, it's
+ * there is no chance the cache is inconsistent, it is
* better to return SQLITE_BUSY.
*/
rc = SQLITE_BUSY;
}
+ pagerLeave(pPager);
return rc;
}
@@ -4037,6 +4858,13 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
if( pPager->state<PAGER_RESERVED ){
return SQLITE_ERROR;
}
+ if( pPager->dbModified==0 &&
+ (pPager->journalMode!=PAGER_JOURNALMODE_DELETE ||
+ pPager->exclusiveMode!=0) ){
+ assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
+ return SQLITE_OK;
+ }
+ pagerEnter(pPager);
PAGERTRACE2("COMMIT %d\n", PAGERID(pPager));
if( MEMDB ){
pPg = pager_get_all_dirty_pages(pPager);
@@ -4061,12 +4889,14 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
#endif
pPager->pStmt = 0;
pPager->state = PAGER_SHARED;
+ pagerLeave(pPager);
return SQLITE_OK;
}
- assert( pPager->journalOpen || !pPager->dirtyCache );
assert( pPager->state==PAGER_SYNCED || !pPager->dirtyCache );
- rc = pager_end_transaction(pPager);
- return pager_error(pPager, rc);
+ rc = pager_end_transaction(pPager, pPager->setMaster);
+ rc = pager_error(pPager, rc);
+ pagerLeave(pPager);
+ return rc;
}
/*
@@ -4120,8 +4950,10 @@ int sqlite3PagerRollback(Pager *pPager){
return SQLITE_OK;
}
+ pagerEnter(pPager);
if( !pPager->dirtyCache || !pPager->journalOpen ){
- rc = pager_end_transaction(pPager);
+ rc = pager_end_transaction(pPager, pPager->setMaster);
+ pagerLeave(pPager);
return rc;
}
@@ -4129,12 +4961,13 @@ int sqlite3PagerRollback(Pager *pPager){
if( pPager->state>=PAGER_EXCLUSIVE ){
pager_playback(pPager, 0);
}
+ pagerLeave(pPager);
return pPager->errCode;
}
if( pPager->state==PAGER_RESERVED ){
int rc2;
rc = pager_playback(pPager, 0);
- rc2 = pager_end_transaction(pPager);
+ rc2 = pager_end_transaction(pPager, pPager->setMaster);
if( rc==SQLITE_OK ){
rc = rc2;
}
@@ -4148,7 +4981,9 @@ int sqlite3PagerRollback(Pager *pPager){
** cache. So call pager_error() on the way out to make any error
** persistent.
*/
- return pager_error(pPager, rc);
+ rc = pager_error(pPager, rc);
+ pagerLeave(pPager);
+ return rc;
}
/*
@@ -4185,6 +5020,9 @@ int *sqlite3PagerStats(Pager *pPager){
a[10] = pPager->nWrite;
return a;
}
+int sqlite3PagerIsMemdb(Pager *pPager){
+ return MEMDB;
+}
#endif
/*
@@ -4194,7 +5032,7 @@ int *sqlite3PagerStats(Pager *pPager){
** open. A new statement journal is created that can be used to rollback
** changes of a single SQL command within a larger transaction.
*/
-int sqlite3PagerStmtBegin(Pager *pPager){
+static int pagerStmtBegin(Pager *pPager){
int rc;
assert( !pPager->stmtInUse );
assert( pPager->state>=PAGER_SHARED );
@@ -4210,23 +5048,23 @@ int sqlite3PagerStmtBegin(Pager *pPager){
return SQLITE_OK;
}
assert( pPager->journalOpen );
- pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 );
- if( pPager->aInStmt==0 ){
+ pagerLeave(pPager);
+ assert( pPager->pInStmt==0 );
+ pPager->pInStmt = sqlite3BitvecCreate(pPager->dbSize);
+ pagerEnter(pPager);
+ if( pPager->pInStmt==0 ){
/* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
return SQLITE_NOMEM;
}
-#ifndef NDEBUG
- rc = sqlite3OsFileSize(pPager->jfd, &pPager->stmtJSize);
- if( rc ) goto stmt_begin_failed;
- assert( pPager->stmtJSize == pPager->journalOff );
-#endif
pPager->stmtJSize = pPager->journalOff;
pPager->stmtSize = pPager->dbSize;
pPager->stmtHdrOff = 0;
pPager->stmtCksum = pPager->cksumInit;
if( !pPager->stmtOpen ){
- rc = sqlite3PagerOpentemp(&pPager->stfd);
- if( rc ) goto stmt_begin_failed;
+ rc = sqlite3PagerOpentemp(pPager, pPager->stfd, SQLITE_OPEN_SUBJOURNAL);
+ if( rc ){
+ goto stmt_begin_failed;
+ }
pPager->stmtOpen = 1;
pPager->stmtNRec = 0;
}
@@ -4234,25 +5072,32 @@ int sqlite3PagerStmtBegin(Pager *pPager){
return SQLITE_OK;
stmt_begin_failed:
- if( pPager->aInStmt ){
- sqliteFree(pPager->aInStmt);
- pPager->aInStmt = 0;
+ if( pPager->pInStmt ){
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}
return rc;
}
+int sqlite3PagerStmtBegin(Pager *pPager){
+ int rc;
+ pagerEnter(pPager);
+ rc = pagerStmtBegin(pPager);
+ pagerLeave(pPager);
+ return rc;
+}
/*
** Commit a statement.
*/
int sqlite3PagerStmtCommit(Pager *pPager){
+ pagerEnter(pPager);
if( pPager->stmtInUse ){
PgHdr *pPg, *pNext;
PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager));
if( !MEMDB ){
- sqlite3OsSeek(pPager->stfd, 0);
/* sqlite3OsTruncate(pPager->stfd, 0); */
- sqliteFree( pPager->aInStmt );
- pPager->aInStmt = 0;
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}else{
for(pPg=pPager->pStmt; pPg; pPg=pNext){
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
@@ -4260,7 +5105,7 @@ int sqlite3PagerStmtCommit(Pager *pPager){
assert( pHist->inStmt );
pHist->inStmt = 0;
pHist->pPrevStmt = pHist->pNextStmt = 0;
- sqliteFree(pHist->pStmt);
+ sqlite3PageFree(pHist->pStmt);
pHist->pStmt = 0;
}
}
@@ -4269,6 +5114,7 @@ int sqlite3PagerStmtCommit(Pager *pPager){
pPager->pStmt = 0;
}
pPager->stmtAutoopen = 0;
+ pagerLeave(pPager);
return SQLITE_OK;
}
@@ -4277,6 +5123,7 @@ int sqlite3PagerStmtCommit(Pager *pPager){
*/
int sqlite3PagerStmtRollback(Pager *pPager){
int rc;
+ pagerEnter(pPager);
if( pPager->stmtInUse ){
PAGERTRACE2("STMT-ROLLBACK %d\n", PAGERID(pPager));
if( MEMDB ){
@@ -4286,7 +5133,7 @@ int sqlite3PagerStmtRollback(Pager *pPager){
pHist = PGHDR_TO_HIST(pPg, pPager);
if( pHist->pStmt ){
memcpy(PGHDR_TO_DATA(pPg), pHist->pStmt, pPager->pageSize);
- sqliteFree(pHist->pStmt);
+ sqlite3PageFree(pHist->pStmt);
pHist->pStmt = 0;
}
}
@@ -4301,6 +5148,7 @@ int sqlite3PagerStmtRollback(Pager *pPager){
rc = SQLITE_OK;
}
pPager->stmtAutoopen = 0;
+ pagerLeave(pPager);
return rc;
}
@@ -4312,6 +5160,22 @@ const char *sqlite3PagerFilename(Pager *pPager){
}
/*
+** Return the VFS structure for the pager.
+*/
+const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
+ return pPager->pVfs;
+}
+
+/*
+** Return the file handle for the database file associated
+** with the pager. This might return NULL if the file has
+** not yet been opened.
+*/
+sqlite3_file *sqlite3PagerFile(Pager *pPager){
+ return pPager->fd;
+}
+
+/*
** Return the directory of the database file.
*/
const char *sqlite3PagerDirname(Pager *pPager){
@@ -4349,7 +5213,7 @@ void sqlite3PagerSetCodec(
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
-** Move the page pPg to location pgno in the file.
+** Move the page pPg to location pgno in the file.
**
** There must be no references to the page previously located at
** pgno (which we call pPgOld) though that page is allowed to be
@@ -4364,12 +5228,18 @@ void sqlite3PagerSetCodec(
** required that a statement transaction was not active, but this restriction
** has been removed (CREATE INDEX needs to move a page when a statement
** transaction is active).
+**
+** If the fourth argument, isCommit, is non-zero, then this page is being
+** moved as part of a database reorganization just before the transaction
+** is being committed. In this case, it is guaranteed that the database page
+** pPg refers to will not be written to again within this transaction.
*/
-int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
+int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
PgHdr *pPgOld; /* The page being overwritten. */
int h;
Pgno needSyncPgno = 0;
+ pagerEnter(pPager);
assert( pPg->nRef>0 );
PAGERTRACE5("MOVE %d page %d (needSync=%d) moves to %d\n",
@@ -4377,18 +5247,26 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno))
pager_get_content(pPg);
- if( pPg->needSync ){
+
+ /* If the journal needs to be sync()ed before page pPg->pgno can
+ ** be written to, store pPg->pgno in local variable needSyncPgno.
+ **
+ ** If the isCommit flag is set, there is no need to remember that
+ ** the journal needs to be sync()ed before database page pPg->pgno
+ ** can be written to. The caller has already promised not to write to it.
+ */
+ if( pPg->needSync && !isCommit ){
needSyncPgno = pPg->pgno;
assert( pPg->inJournal || (int)pgno>pPager->origDbSize );
assert( pPg->dirty );
assert( pPager->needSync );
}
- /* Unlink pPg from it's hash-chain */
+ /* Unlink pPg from its hash-chain */
unlinkHashChain(pPager, pPg);
/* If the cache contains a page with page-number pgno, remove it
- ** from it's hash chain. Also, if the PgHdr.needSync was set for
+ ** from its hash chain. Also, if the PgHdr.needSync was set for
** page pgno before the 'move' operation, it needs to be retained
** for the page moved there.
*/
@@ -4402,12 +5280,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
}else{
pPg->needSync = 0;
}
- if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
- pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
- }else{
- pPg->inJournal = 0;
- assert( pPg->needSync==0 || (int)pgno>pPager->origDbSize );
- }
+ pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
/* Change the page number for pPg and insert it into the new hash-chain. */
assert( pgno!=0 );
@@ -4423,13 +5296,22 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
makeDirty(pPg);
pPager->dirtyCache = 1;
+ pPager->dbModified = 1;
if( needSyncPgno ){
/* If needSyncPgno is non-zero, then the journal file needs to be
** sync()ed before any data is written to database file page needSyncPgno.
** Currently, no such page exists in the page-cache and the
- ** Pager.aInJournal bit has been set. This needs to be remedied by loading
- ** the page into the pager-cache and setting the PgHdr.needSync flag.
+ ** "is journaled" bitvec flag has been set. This needs to be remedied by
+ ** loading the page into the pager-cache and setting the PgHdr.needSync
+ ** flag.
+ **
+ ** If the attempt to load the page into the page-cache fails, (due
+ ** to a malloc() or IO failure), clear the bit in the pInJournal[]
+ ** array. Otherwise, if the page is loaded and written again in
+ ** this transaction, it may be written to the database file before
+ ** it is synced into the journal file. This way, it may end up in
+ ** the journal file twice, but that is not a problem.
**
** The sqlite3PagerGet() call may cause the journal to sync. So make
** sure the Pager.needSync flag is set too.
@@ -4438,7 +5320,13 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
PgHdr *pPgHdr;
assert( pPager->needSync );
rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ){
+ if( pPager->pInJournal && (int)needSyncPgno<=pPager->origDbSize ){
+ sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
+ }
+ pagerLeave(pPager);
+ return rc;
+ }
pPager->needSync = 1;
pPgHdr->needSync = 1;
pPgHdr->inJournal = 1;
@@ -4446,28 +5334,30 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
sqlite3PagerUnref(pPgHdr);
}
+ pagerLeave(pPager);
return SQLITE_OK;
}
#endif
+/* Begin preload-cache.patch for Chromium */
/**
** When making large allocations, there is no need to stress the heap and
** potentially hold its lock while we allocate a bunch of memory. If we know
** the allocation will be large, go directly to the OS instead of the heap.
**/
static void* allocLarge(size_t size) {
-#if OS_WIN
+#if SQLITE_OS_WIN
return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
- return sqliteMallocRaw(size);
+ return sqlite3Malloc(size);
#endif
}
static void freeLarge(void* ptr) {
-#if OS_WIN
+#if SQLITE_OS_WIN
VirtualFree(ptr, 0, MEM_RELEASE);
#else
- sqliteFree(ptr);
+ sqlite3_free(ptr);
#endif
}
@@ -4478,7 +5368,7 @@ static void freeLarge(void* ptr) {
** than loading a subset of the pages one at a time in random order.
**
** The pager must be initialized before this function is called. This means a
-** statement must be open that has initialized the pager and is keeping the
+* statement must be open that has initialized the pager and is keeping the
** cache in memory.
**/
int sqlite3PagerLoadall(Pager* pPager)
@@ -4501,15 +5391,11 @@ int sqlite3PagerLoadall(Pager* pPager)
loadPages = pPager->dbSize;
loadSize = loadPages * pPager->pageSize;
- rc = sqlite3OsSeek(pPager->fd, 0);
- if (rc != SQLITE_OK)
- return rc;
-
/* load the file as one chunk */
fileData = allocLarge(loadSize);
if (! fileData)
return SQLITE_NOMEM;
- rc = sqlite3OsRead(pPager->fd, fileData, loadSize);
+ rc = sqlite3OsRead(pPager->fd, fileData, loadSize, 0);
if (rc != SQLITE_OK) {
freeLarge(fileData);
return rc;
@@ -4532,6 +5418,7 @@ int sqlite3PagerLoadall(Pager* pPager)
freeLarge(fileData);
return SQLITE_OK;
}
+/* End preload-cache.patch for Chromium */
/*
** Return a pointer to the data for the specified page.
@@ -4571,29 +5458,37 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
return (int)pPager->exclusiveMode;
}
-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
-** Return the current state of the file lock for the given pager.
-** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK,
-** PENDING_LOCK, or EXCLUSIVE_LOCK.
+** Get/set the journal-mode for this pager. Parameter eMode must be one
+** of PAGER_JOURNALMODE_QUERY, PAGER_JOURNALMODE_DELETE or
+** PAGER_JOURNALMODE_PERSIST. If the parameter is not _QUERY, then
+** the journal-mode is set to the value specified.
+**
+** The returned value is either PAGER_JOURNALMODE_DELETE or
+** PAGER_JOURNALMODE_PERSIST, indicating the current (possibly updated)
+** journal-mode.
*/
-int sqlite3PagerLockstate(Pager *pPager){
- return sqlite3OsLockState(pPager->fd);
+int sqlite3PagerJournalMode(Pager *pPager, int eMode){
+ assert( eMode==PAGER_JOURNALMODE_QUERY
+ || eMode==PAGER_JOURNALMODE_DELETE
+ || eMode==PAGER_JOURNALMODE_PERSIST
+ || eMode==PAGER_JOURNALMODE_OFF );
+ assert( PAGER_JOURNALMODE_QUERY<0 );
+ assert( PAGER_JOURNALMODE_DELETE>=0 && PAGER_JOURNALMODE_PERSIST>=0 );
+ if( eMode>=0 ){
+ pPager->journalMode = eMode;
+ }
+ return (int)pPager->journalMode;
}
-#endif
-#ifdef SQLITE_DEBUG
/*
-** Print a listing of all referenced pages and their ref count.
+** Get/set the size-limit used for persistent journal files.
*/
-void sqlite3PagerRefdump(Pager *pPager){
- PgHdr *pPg;
- for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
- if( pPg->nRef<=0 ) continue;
- sqlite3DebugPrintf("PAGE %3d addr=%p nRef=%d\n",
- pPg->pgno, PGHDR_TO_DATA(pPg), pPg->nRef);
+i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){
+ if( iLimit>=-1 ){
+ pPager->journalSizeLimit = iLimit;
}
+ return pPager->journalSizeLimit;
}
-#endif
#endif /* SQLITE_OMIT_DISKIO */
diff --git a/third_party/sqlite/pager.h b/third_party/sqlite/src/pager.h
index be12324..9c989c3 100644..100755
--- a/third_party/sqlite/pager.h
+++ b/third_party/sqlite/src/pager.h
@@ -13,17 +13,25 @@
** subsystem. The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
-** @(#) $Id: pager.h,v 1.61 2007/05/08 21:45:28 drh Exp $
+** @(#) $Id: pager.h,v 1.77 2008/07/16 18:17:56 danielk1977 Exp $
*/
#ifndef _PAGER_H_
#define _PAGER_H_
/*
+** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
+** it must be turned on for each database using "PRAGMA auto_vacuum = 1".
+*/
+#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
+ #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1
+#endif
+
+/*
** The type used to represent a page number. The first page in a file
** is called page 1. 0 is used to represent "not a page".
*/
-typedef unsigned int Pgno;
+typedef u32 Pgno;
/*
** Each open file is managed by a separate instance of the "Pager" structure.
@@ -51,15 +59,22 @@ typedef struct PgHdr DbPage;
#define PAGER_LOCKINGMODE_EXCLUSIVE 1
/*
+** Valid values for the second argument to sqlite3PagerJournalMode().
+*/
+#define PAGER_JOURNALMODE_QUERY -1
+#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
+#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
+#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
+
+/*
** See source code comments for a detailed description of the following
** routines:
*/
-int sqlite3PagerOpen(Pager **ppPager, const char *zFilename,
- int nExtra, int flags);
+int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int);
void sqlite3PagerSetBusyhandler(Pager*, BusyHandler *pBusyHandler);
void sqlite3PagerSetDestructor(Pager*, void(*)(DbPage*,int));
void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*,int));
-int sqlite3PagerSetPagesize(Pager*, int);
+int sqlite3PagerSetPagesize(Pager*, u16*);
int sqlite3PagerMaxPageCount(Pager*, int);
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
void sqlite3PagerSetCachesize(Pager*, int);
@@ -70,11 +85,10 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
int sqlite3PagerRef(DbPage*);
int sqlite3PagerUnref(DbPage*);
int sqlite3PagerWrite(DbPage*);
-int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void*);
-int sqlite3PagerPagecount(Pager*);
+int sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerTruncate(Pager*,Pgno);
int sqlite3PagerBegin(DbPage*, int exFlag);
-int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno);
+int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno, int);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerIsreadonly(Pager*);
@@ -86,14 +100,21 @@ void sqlite3PagerDontWrite(DbPage*);
int sqlite3PagerRefcount(Pager*);
void sqlite3PagerSetSafetyLevel(Pager*,int,int);
const char *sqlite3PagerFilename(Pager*);
+const sqlite3_vfs *sqlite3PagerVfs(Pager*);
+sqlite3_file *sqlite3PagerFile(Pager*);
const char *sqlite3PagerDirname(Pager*);
const char *sqlite3PagerJournalname(Pager*);
int sqlite3PagerNosync(Pager*);
-int sqlite3PagerMovepage(Pager*,DbPage*,Pgno);
+int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
void *sqlite3PagerGetData(DbPage *);
void *sqlite3PagerGetExtra(DbPage *);
int sqlite3PagerLockingMode(Pager *, int);
+/* This function is for preload-cache.patch for Chromium: */
int sqlite3PagerLoadall(Pager*);
+int sqlite3PagerJournalMode(Pager *, int);
+i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
+void *sqlite3PagerTempSpace(Pager*);
+int sqlite3PagerSync(Pager *pPager);
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
int sqlite3PagerReleaseMemory(int);
@@ -108,14 +129,10 @@ int sqlite3PagerLoadall(Pager*);
int sqlite3PagerIswriteable(DbPage*);
#endif
-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
- int sqlite3PagerLockstate(Pager*);
-#endif
-
#ifdef SQLITE_TEST
int *sqlite3PagerStats(Pager*);
void sqlite3PagerRefdump(Pager*);
- int pager3_refinfo_enable;
+ int sqlite3PagerIsMemdb(Pager*);
#endif
#ifdef SQLITE_TEST
diff --git a/third_party/sqlite/src/parse.y b/third_party/sqlite/src/parse.y
new file mode 100755
index 0000000..3ff8d65
--- /dev/null
+++ b/third_party/sqlite/src/parse.y
@@ -0,0 +1,1122 @@
+/*
+** 2001 September 15
+**
+** 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 contains SQLite's grammar for SQL. Process this file
+** using the lemon parser generator to generate C code that runs
+** the parser. Lemon will also generate a header file containing
+** numeric codes for all of the tokens.
+**
+** @(#) $Id: parse.y,v 1.248 2008/07/31 01:40:42 shane Exp $
+*/
+
+// All token codes are small integers with #defines that begin with "TK_"
+%token_prefix TK_
+
+// The type of the data attached to each token is Token. This is also the
+// default type for non-terminals.
+//
+%token_type {Token}
+%default_type {Token}
+
+// The generated parser function takes a 4th argument as follows:
+%extra_argument {Parse *pParse}
+
+// This code runs whenever there is a syntax error
+//
+%syntax_error {
+ assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+ pParse->parseError = 1;
+}
+%stack_overflow {
+ sqlite3ErrorMsg(pParse, "parser stack overflow");
+ pParse->parseError = 1;
+}
+
+// The name of the generated procedure that implements the parser
+// is as follows:
+%name sqlite3Parser
+
+// The following text is included near the beginning of the C source
+// code file that implements the parser.
+//
+%include {
+#include "sqliteInt.h"
+
+/*
+** An instance of this structure holds information about the
+** LIMIT clause of a SELECT statement.
+*/
+struct LimitVal {
+ Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */
+ Expr *pOffset; /* The OFFSET expression. NULL if there is none */
+};
+
+/*
+** An instance of this structure is used to store the LIKE,
+** GLOB, NOT LIKE, and NOT GLOB operators.
+*/
+struct LikeOp {
+ Token eOperator; /* "like" or "glob" or "regexp" */
+ int not; /* True if the NOT keyword is present */
+};
+
+/*
+** An instance of the following structure describes the event of a
+** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
+** TK_DELETE, or TK_INSTEAD. If the event is of the form
+**
+** UPDATE ON (a,b,c)
+**
+** Then the "b" IdList records the list "a,b,c".
+*/
+struct TrigEvent { int a; IdList * b; };
+
+/*
+** An instance of this structure holds the ATTACH key and the key type.
+*/
+struct AttachKey { int type; Token key; };
+
+} // end %include
+
+// Input is a single SQL command
+input ::= cmdlist.
+cmdlist ::= cmdlist ecmd.
+cmdlist ::= ecmd.
+cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
+ecmd ::= SEMI.
+ecmd ::= explain cmdx SEMI.
+explain ::= . { sqlite3BeginParse(pParse, 0); }
+%ifndef SQLITE_OMIT_EXPLAIN
+explain ::= EXPLAIN. { sqlite3BeginParse(pParse, 1); }
+explain ::= EXPLAIN QUERY PLAN. { sqlite3BeginParse(pParse, 2); }
+%endif SQLITE_OMIT_EXPLAIN
+
+///////////////////// Begin and end transactions. ////////////////////////////
+//
+
+cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);}
+trans_opt ::= .
+trans_opt ::= TRANSACTION.
+trans_opt ::= TRANSACTION nm.
+%type transtype {int}
+%ifdef SQLITE_TRANSACTION_DEFAULT_IMMEDIATE
+transtype(A) ::= . {A = TK_IMMEDIATE;}
+%endif SQLITE_TRANSACTION_DEFAULT_IMMEDIATE
+%ifndef SQLITE_TRANSACTION_DEFAULT_IMMEDIATE
+transtype(A) ::= . {A = TK_DEFERRED;}
+%endif SQLITE_TRANSACTION_DEFAULT_IMMEDIATE
+transtype(A) ::= DEFERRED(X). {A = @X;}
+transtype(A) ::= IMMEDIATE(X). {A = @X;}
+transtype(A) ::= EXCLUSIVE(X). {A = @X;}
+cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);}
+cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);}
+cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);}
+
+///////////////////// The CREATE TABLE statement ////////////////////////////
+//
+cmd ::= create_table create_table_args.
+create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
+ sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
+}
+%type ifnotexists {int}
+ifnotexists(A) ::= . {A = 0;}
+ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
+%type temp {int}
+%ifndef SQLITE_OMIT_TEMPDB
+temp(A) ::= TEMP. {A = 1;}
+%endif SQLITE_OMIT_TEMPDB
+temp(A) ::= . {A = 0;}
+create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
+ sqlite3EndTable(pParse,&X,&Y,0);
+}
+create_table_args ::= AS select(S). {
+ sqlite3EndTable(pParse,0,0,S);
+ sqlite3SelectDelete(pParse->db, S);
+}
+columnlist ::= columnlist COMMA column.
+columnlist ::= column.
+
+// A "column" is a complete description of a single column in a
+// CREATE TABLE statement. This includes the column name, its
+// datatype, and other keywords such as PRIMARY KEY, UNIQUE, REFERENCES,
+// NOT NULL and so forth.
+//
+column(A) ::= columnid(X) type carglist. {
+ A.z = X.z;
+ A.n = (pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
+}
+columnid(A) ::= nm(X). {
+ sqlite3AddColumn(pParse,&X);
+ A = X;
+}
+
+
+// An IDENTIFIER can be a generic identifier, or one of several
+// keywords. Any non-standard keyword can also be an identifier.
+//
+%type id {Token}
+id(A) ::= ID(X). {A = X;}
+
+// The following directive causes tokens ABORT, AFTER, ASC, etc. to
+// fallback to ID if they will not parse as their original value.
+// This obviates the need for the "id" nonterminal.
+//
+%fallback ID
+ ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN CASCADE CAST CONFLICT
+ DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
+ IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN
+ QUERY KEY OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW
+ TEMP TRIGGER VACUUM VIEW VIRTUAL
+%ifdef SQLITE_OMIT_COMPOUND_SELECT
+ EXCEPT INTERSECT UNION
+%endif SQLITE_OMIT_COMPOUND_SELECT
+ REINDEX RENAME CTIME_KW IF
+ .
+%wildcard ANY.
+
+// Define operator precedence early so that this is the first occurance
+// of the operator tokens in the grammer. Keeping the operators together
+// causes them to be assigned integer values that are close together,
+// which keeps parser tables smaller.
+//
+// The token values assigned to these symbols is determined by the order
+// in which lemon first sees them. It must be the case that ISNULL/NOTNULL,
+// NE/EQ, GT/LE, and GE/LT are separated by only a single value. See
+// the sqlite3ExprIfFalse() routine for additional information on this
+// constraint.
+//
+%left OR.
+%left AND.
+%right NOT.
+%left IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
+%left GT LE LT GE.
+%right ESCAPE.
+%left BITAND BITOR LSHIFT RSHIFT.
+%left PLUS MINUS.
+%left STAR SLASH REM.
+%left CONCAT.
+%left COLLATE.
+%right UMINUS UPLUS BITNOT.
+
+// And "ids" is an identifer-or-string.
+//
+%type ids {Token}
+ids(A) ::= ID|STRING(X). {A = X;}
+
+// The name of a column or table can be any of the following:
+//
+%type nm {Token}
+nm(A) ::= ID(X). {A = X;}
+nm(A) ::= STRING(X). {A = X;}
+nm(A) ::= JOIN_KW(X). {A = X;}
+
+// A typetoken is really one or more tokens that form a type name such
+// as can be found after the column name in a CREATE TABLE statement.
+// Multiple tokens are concatenated to form the value of the typetoken.
+//
+%type typetoken {Token}
+type ::= .
+type ::= typetoken(X). {sqlite3AddColumnType(pParse,&X);}
+typetoken(A) ::= typename(X). {A = X;}
+typetoken(A) ::= typename(X) LP signed RP(Y). {
+ A.z = X.z;
+ A.n = &Y.z[Y.n] - X.z;
+}
+typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). {
+ A.z = X.z;
+ A.n = &Y.z[Y.n] - X.z;
+}
+%type typename {Token}
+typename(A) ::= ids(X). {A = X;}
+typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(Y.z-X.z);}
+signed ::= plus_num.
+signed ::= minus_num.
+
+// "carglist" is a list of additional constraints that come after the
+// column name and column type in a CREATE TABLE statement.
+//
+carglist ::= carglist carg.
+carglist ::= .
+carg ::= CONSTRAINT nm ccons.
+carg ::= ccons.
+ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);}
+ccons ::= DEFAULT MINUS term(X). {
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, X, 0, 0);
+ sqlite3AddDefaultValue(pParse,p);
+}
+ccons ::= DEFAULT id(X). {
+ Expr *p = sqlite3PExpr(pParse, TK_STRING, 0, 0, &X);
+ sqlite3AddDefaultValue(pParse,p);
+}
+
+// In addition to the type name, we also care about the primary key and
+// UNIQUE constraints.
+//
+ccons ::= NULL onconf.
+ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);}
+ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
+ {sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
+ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0);}
+ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X);}
+ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
+ {sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
+ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
+ccons ::= COLLATE ids(C). {sqlite3AddCollateType(pParse, &C);}
+
+// The optional AUTOINCREMENT keyword
+%type autoinc {int}
+autoinc(X) ::= . {X = 0;}
+autoinc(X) ::= AUTOINCR. {X = 1;}
+
+// The next group of rules parses the arguments to a REFERENCES clause
+// that determine if the referential integrity checking is deferred or
+// or immediate and which determine what action to take if a ref-integ
+// check fails.
+//
+%type refargs {int}
+refargs(A) ::= . { A = OE_Restrict * 0x010101; }
+refargs(A) ::= refargs(X) refarg(Y). { A = (X & Y.mask) | Y.value; }
+%type refarg {struct {int value; int mask;}}
+refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; }
+refarg(A) ::= ON DELETE refact(X). { A.value = X; A.mask = 0x0000ff; }
+refarg(A) ::= ON UPDATE refact(X). { A.value = X<<8; A.mask = 0x00ff00; }
+refarg(A) ::= ON INSERT refact(X). { A.value = X<<16; A.mask = 0xff0000; }
+%type refact {int}
+refact(A) ::= SET NULL. { A = OE_SetNull; }
+refact(A) ::= SET DEFAULT. { A = OE_SetDflt; }
+refact(A) ::= CASCADE. { A = OE_Cascade; }
+refact(A) ::= RESTRICT. { A = OE_Restrict; }
+%type defer_subclause {int}
+defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X). {A = X;}
+defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X). {A = X;}
+%type init_deferred_pred_opt {int}
+init_deferred_pred_opt(A) ::= . {A = 0;}
+init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;}
+init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;}
+
+// For the time being, the only constraint we care about is the primary
+// key and UNIQUE. Both create indices.
+//
+conslist_opt(A) ::= . {A.n = 0; A.z = 0;}
+conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
+conslist ::= conslist COMMA tcons.
+conslist ::= conslist tcons.
+conslist ::= tcons.
+tcons ::= CONSTRAINT nm.
+tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R).
+ {sqlite3AddPrimaryKey(pParse,X,R,I,0);}
+tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
+ {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0);}
+tcons ::= CHECK LP expr(E) RP onconf. {sqlite3AddCheckConstraint(pParse,E);}
+tcons ::= FOREIGN KEY LP idxlist(FA) RP
+ REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). {
+ sqlite3CreateForeignKey(pParse, FA, &T, TA, R);
+ sqlite3DeferForeignKey(pParse, D);
+}
+%type defer_subclause_opt {int}
+defer_subclause_opt(A) ::= . {A = 0;}
+defer_subclause_opt(A) ::= defer_subclause(X). {A = X;}
+
+// The following is a non-standard extension that allows us to declare the
+// default behavior when there is a constraint conflict.
+//
+%type onconf {int}
+%type orconf {int}
+%type resolvetype {int}
+onconf(A) ::= . {A = OE_Default;}
+onconf(A) ::= ON CONFLICT resolvetype(X). {A = X;}
+orconf(A) ::= . {A = OE_Default;}
+orconf(A) ::= OR resolvetype(X). {A = X;}
+resolvetype(A) ::= raisetype(X). {A = X;}
+resolvetype(A) ::= IGNORE. {A = OE_Ignore;}
+resolvetype(A) ::= REPLACE. {A = OE_Replace;}
+
+////////////////////////// The DROP TABLE /////////////////////////////////////
+//
+cmd ::= DROP TABLE ifexists(E) fullname(X). {
+ sqlite3DropTable(pParse, X, 0, E);
+}
+%type ifexists {int}
+ifexists(A) ::= IF EXISTS. {A = 1;}
+ifexists(A) ::= . {A = 0;}
+
+///////////////////// The CREATE VIEW statement /////////////////////////////
+//
+%ifndef SQLITE_OMIT_VIEW
+cmd ::= CREATE(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). {
+ sqlite3CreateView(pParse, &X, &Y, &Z, S, T, E);
+}
+cmd ::= DROP VIEW ifexists(E) fullname(X). {
+ sqlite3DropTable(pParse, X, 1, E);
+}
+%endif SQLITE_OMIT_VIEW
+
+//////////////////////// The SELECT statement /////////////////////////////////
+//
+cmd ::= select(X). {
+ SelectDest dest = {SRT_Callback, 0, 0, 0, 0};
+ sqlite3Select(pParse, X, &dest, 0, 0, 0);
+ sqlite3SelectDelete(pParse->db, X);
+}
+
+%type select {Select*}
+%destructor select {sqlite3SelectDelete(pParse->db, $$);}
+%type oneselect {Select*}
+%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
+
+select(A) ::= oneselect(X). {A = X;}
+%ifndef SQLITE_OMIT_COMPOUND_SELECT
+select(A) ::= select(X) multiselect_op(Y) oneselect(Z). {
+ if( Z ){
+ Z->op = Y;
+ Z->pPrior = X;
+ }else{
+ sqlite3SelectDelete(pParse->db, X);
+ }
+ A = Z;
+}
+%type multiselect_op {int}
+multiselect_op(A) ::= UNION(OP). {A = @OP;}
+multiselect_op(A) ::= UNION ALL. {A = TK_ALL;}
+multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP;}
+%endif SQLITE_OMIT_COMPOUND_SELECT
+oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
+ groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). {
+ A = sqlite3SelectNew(pParse,W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset);
+}
+
+// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
+// present and false (0) if it is not.
+//
+%type distinct {int}
+distinct(A) ::= DISTINCT. {A = 1;}
+distinct(A) ::= ALL. {A = 0;}
+distinct(A) ::= . {A = 0;}
+
+// selcollist is a list of expressions that are to become the return
+// values of the SELECT statement. The "*" in statements like
+// "SELECT * FROM ..." is encoded as a special expression with an
+// opcode of TK_ALL.
+//
+%type selcollist {ExprList*}
+%destructor selcollist {sqlite3ExprListDelete(pParse->db, $$);}
+%type sclp {ExprList*}
+%destructor sclp {sqlite3ExprListDelete(pParse->db, $$);}
+sclp(A) ::= selcollist(X) COMMA. {A = X;}
+sclp(A) ::= . {A = 0;}
+selcollist(A) ::= sclp(P) expr(X) as(Y). {
+ A = sqlite3ExprListAppend(pParse,P,X,Y.n?&Y:0);
+}
+selcollist(A) ::= sclp(P) STAR. {
+ Expr *p = sqlite3PExpr(pParse, TK_ALL, 0, 0, 0);
+ A = sqlite3ExprListAppend(pParse, P, p, 0);
+}
+selcollist(A) ::= sclp(P) nm(X) DOT STAR(Y). {
+ Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &Y);
+ Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
+ A = sqlite3ExprListAppend(pParse,P, pDot, 0);
+}
+
+// An option "AS <id>" phrase that can follow one of the expressions that
+// define the result set, or one of the tables in the FROM clause.
+//
+%type as {Token}
+as(X) ::= AS nm(Y). {X = Y;}
+as(X) ::= ids(Y). {X = Y;}
+as(X) ::= . {X.n = 0;}
+
+
+%type seltablist {SrcList*}
+%destructor seltablist {sqlite3SrcListDelete(pParse->db, $$);}
+%type stl_prefix {SrcList*}
+%destructor stl_prefix {sqlite3SrcListDelete(pParse->db, $$);}
+%type from {SrcList*}
+%destructor from {sqlite3SrcListDelete(pParse->db, $$);}
+
+// A complete FROM clause.
+//
+from(A) ::= . {A = sqlite3DbMallocZero(pParse->db, sizeof(*A));}
+from(A) ::= FROM seltablist(X). {
+ A = X;
+ sqlite3SrcListShiftJoinType(A);
+}
+
+// "seltablist" is a "Select Table List" - the content of the FROM clause
+// in a SELECT statement. "stl_prefix" is a prefix of this list.
+//
+stl_prefix(A) ::= seltablist(X) joinop(Y). {
+ A = X;
+ if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y;
+}
+stl_prefix(A) ::= . {A = 0;}
+seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
+ A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
+}
+%ifndef SQLITE_OMIT_SUBQUERY
+ seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP
+ as(Z) on_opt(N) using_opt(U). {
+ A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U);
+ }
+
+ // A seltablist_paren nonterminal represents anything in a FROM that
+ // is contained inside parentheses. This can be either a subquery or
+ // a grouping of table and subqueries.
+ //
+ %type seltablist_paren {Select*}
+ %destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);}
+ seltablist_paren(A) ::= select(S). {A = S;}
+ seltablist_paren(A) ::= seltablist(F). {
+ sqlite3SrcListShiftJoinType(F);
+ A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
+ }
+%endif SQLITE_OMIT_SUBQUERY
+
+%type dbnm {Token}
+dbnm(A) ::= . {A.z=0; A.n=0;}
+dbnm(A) ::= DOT nm(X). {A = X;}
+
+%type fullname {SrcList*}
+%destructor fullname {sqlite3SrcListDelete(pParse->db, $$);}
+fullname(A) ::= nm(X) dbnm(Y). {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);}
+
+%type joinop {int}
+%type joinop2 {int}
+joinop(X) ::= COMMA|JOIN. { X = JT_INNER; }
+joinop(X) ::= JOIN_KW(A) JOIN. { X = sqlite3JoinType(pParse,&A,0,0); }
+joinop(X) ::= JOIN_KW(A) nm(B) JOIN. { X = sqlite3JoinType(pParse,&A,&B,0); }
+joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
+ { X = sqlite3JoinType(pParse,&A,&B,&C); }
+
+%type on_opt {Expr*}
+%destructor on_opt {sqlite3ExprDelete(pParse->db, $$);}
+on_opt(N) ::= ON expr(E). {N = E;}
+on_opt(N) ::= . {N = 0;}
+
+%type using_opt {IdList*}
+%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
+using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
+using_opt(U) ::= . {U = 0;}
+
+
+%type orderby_opt {ExprList*}
+%destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);}
+%type sortlist {ExprList*}
+%destructor sortlist {sqlite3ExprListDelete(pParse->db, $$);}
+%type sortitem {Expr*}
+%destructor sortitem {sqlite3ExprDelete(pParse->db, $$);}
+
+orderby_opt(A) ::= . {A = 0;}
+orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
+sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
+ A = sqlite3ExprListAppend(pParse,X,Y,0);
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+sortlist(A) ::= sortitem(Y) sortorder(Z). {
+ A = sqlite3ExprListAppend(pParse,0,Y,0);
+ if( A && A->a ) A->a[0].sortOrder = Z;
+}
+sortitem(A) ::= expr(X). {A = X;}
+
+%type sortorder {int}
+
+sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;}
+sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;}
+sortorder(A) ::= . {A = SQLITE_SO_ASC;}
+
+%type groupby_opt {ExprList*}
+%destructor groupby_opt {sqlite3ExprListDelete(pParse->db, $$);}
+groupby_opt(A) ::= . {A = 0;}
+groupby_opt(A) ::= GROUP BY nexprlist(X). {A = X;}
+
+%type having_opt {Expr*}
+%destructor having_opt {sqlite3ExprDelete(pParse->db, $$);}
+having_opt(A) ::= . {A = 0;}
+having_opt(A) ::= HAVING expr(X). {A = X;}
+
+%type limit_opt {struct LimitVal}
+
+// The destructor for limit_opt will never fire in the current grammar.
+// The limit_opt non-terminal only occurs at the end of a single production
+// rule for SELECT statements. As soon as the rule that create the
+// limit_opt non-terminal reduces, the SELECT statement rule will also
+// reduce. So there is never a limit_opt non-terminal on the stack
+// except as a transient. So there is never anything to destroy.
+//
+//%destructor limit_opt {
+// sqlite3ExprDelete(pParse->db, $$.pLimit);
+// sqlite3ExprDelete(pParse->db, $$.pOffset);
+//}
+limit_opt(A) ::= . {A.pLimit = 0; A.pOffset = 0;}
+limit_opt(A) ::= LIMIT expr(X). {A.pLimit = X; A.pOffset = 0;}
+limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y).
+ {A.pLimit = X; A.pOffset = Y;}
+limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
+ {A.pOffset = X; A.pLimit = Y;}
+
+/////////////////////////// The DELETE statement /////////////////////////////
+//
+cmd ::= DELETE FROM fullname(X) where_opt(Y). {sqlite3DeleteFrom(pParse,X,Y);}
+
+%type where_opt {Expr*}
+%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
+
+where_opt(A) ::= . {A = 0;}
+where_opt(A) ::= WHERE expr(X). {A = X;}
+
+////////////////////////// The UPDATE command ////////////////////////////////
+//
+cmd ::= UPDATE orconf(R) fullname(X) SET setlist(Y) where_opt(Z). {
+ sqlite3ExprListCheckLength(pParse,Y,"set list");
+ sqlite3Update(pParse,X,Y,Z,R);
+}
+
+%type setlist {ExprList*}
+%destructor setlist {sqlite3ExprListDelete(pParse->db, $$);}
+
+setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y).
+ {A = sqlite3ExprListAppend(pParse,Z,Y,&X);}
+setlist(A) ::= nm(X) EQ expr(Y).
+ {A = sqlite3ExprListAppend(pParse,0,Y,&X);}
+
+////////////////////////// The INSERT command /////////////////////////////////
+//
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F)
+ VALUES LP itemlist(Y) RP.
+ {sqlite3Insert(pParse, X, Y, 0, F, R);}
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S).
+ {sqlite3Insert(pParse, X, 0, S, F, R);}
+cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
+ {sqlite3Insert(pParse, X, 0, 0, F, R);}
+
+%type insert_cmd {int}
+insert_cmd(A) ::= INSERT orconf(R). {A = R;}
+insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
+
+
+%type itemlist {ExprList*}
+%destructor itemlist {sqlite3ExprListDelete(pParse->db, $$);}
+
+itemlist(A) ::= itemlist(X) COMMA expr(Y).
+ {A = sqlite3ExprListAppend(pParse,X,Y,0);}
+itemlist(A) ::= expr(X).
+ {A = sqlite3ExprListAppend(pParse,0,X,0);}
+
+%type inscollist_opt {IdList*}
+%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);}
+%type inscollist {IdList*}
+%destructor inscollist {sqlite3IdListDelete(pParse->db, $$);}
+
+inscollist_opt(A) ::= . {A = 0;}
+inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
+inscollist(A) ::= inscollist(X) COMMA nm(Y).
+ {A = sqlite3IdListAppend(pParse->db,X,&Y);}
+inscollist(A) ::= nm(Y).
+ {A = sqlite3IdListAppend(pParse->db,0,&Y);}
+
+/////////////////////////// Expression Processing /////////////////////////////
+//
+
+%type expr {Expr*}
+%destructor expr {sqlite3ExprDelete(pParse->db, $$);}
+%type term {Expr*}
+%destructor term {sqlite3ExprDelete(pParse->db, $$);}
+
+expr(A) ::= term(X). {A = X;}
+expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqlite3ExprSpan(A,&B,&E); }
+term(A) ::= NULL(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+expr(A) ::= ID(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
+expr(A) ::= JOIN_KW(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
+expr(A) ::= nm(X) DOT nm(Y). {
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
+ A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
+}
+expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
+ Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
+ Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
+ Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Z);
+ Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
+ A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
+}
+term(A) ::= INTEGER|FLOAT|BLOB(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+term(A) ::= STRING(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
+expr(A) ::= REGISTER(X). {A = sqlite3RegisterExpr(pParse, &X);}
+expr(A) ::= VARIABLE(X). {
+ Token *pToken = &X;
+ Expr *pExpr = A = sqlite3PExpr(pParse, TK_VARIABLE, 0, 0, pToken);
+ sqlite3ExprAssignVarNumber(pParse, pExpr);
+}
+expr(A) ::= expr(E) COLLATE ids(C). {
+ A = sqlite3ExprSetColl(pParse, E, &C);
+}
+%ifndef SQLITE_OMIT_CAST
+expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
+ A = sqlite3PExpr(pParse, TK_CAST, E, 0, &T);
+ sqlite3ExprSpan(A,&X,&Y);
+}
+%endif SQLITE_OMIT_CAST
+expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
+ if( Y && Y->nExpr>SQLITE_MAX_FUNCTION_ARG ){
+ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
+ }
+ A = sqlite3ExprFunction(pParse, Y, &X);
+ sqlite3ExprSpan(A,&X,&E);
+ if( D && A ){
+ A->flags |= EP_Distinct;
+ }
+}
+expr(A) ::= ID(X) LP STAR RP(E). {
+ A = sqlite3ExprFunction(pParse, 0, &X);
+ sqlite3ExprSpan(A,&X,&E);
+}
+term(A) ::= CTIME_KW(OP). {
+ /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
+ ** treated as functions that return constants */
+ A = sqlite3ExprFunction(pParse, 0,&OP);
+ if( A ){
+ A->op = TK_CONST_FUNC;
+ A->span = OP;
+ }
+}
+expr(A) ::= expr(X) AND(OP) expr(Y). {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) OR(OP) expr(Y). {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y).
+ {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) EQ|NE(OP) expr(Y). {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y).
+ {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y).{A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
+ {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A = sqlite3PExpr(pParse,@OP,X,Y,0);}
+%type likeop {struct LikeOp}
+likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;}
+likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;}
+likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;}
+likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;}
+%type escape {Expr*}
+%destructor escape {sqlite3ExprDelete(pParse->db, $$);}
+escape(X) ::= ESCAPE expr(A). [ESCAPE] {X = A;}
+escape(X) ::= . [ESCAPE] {X = 0;}
+expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE_KW] {
+ ExprList *pList;
+ pList = sqlite3ExprListAppend(pParse,0, Y, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, X, 0);
+ if( E ){
+ pList = sqlite3ExprListAppend(pParse,pList, E, 0);
+ }
+ A = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
+ if( OP.not ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A, &X->span, &Y->span);
+ if( A ) A->flags |= EP_InfixFunc;
+}
+
+expr(A) ::= expr(X) ISNULL|NOTNULL(E). {
+ A = sqlite3PExpr(pParse, @E, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) IS NULL(E). {
+ A = sqlite3PExpr(pParse, TK_ISNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) NOT NULL(E). {
+ A = sqlite3PExpr(pParse, TK_NOTNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= expr(X) IS NOT NULL(E). {
+ A = sqlite3PExpr(pParse, TK_NOTNULL, X, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+}
+expr(A) ::= NOT(B) expr(X). {
+ A = sqlite3PExpr(pParse, @B, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= BITNOT(B) expr(X). {
+ A = sqlite3PExpr(pParse, @B, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= MINUS(B) expr(X). [UMINUS] {
+ A = sqlite3PExpr(pParse, TK_UMINUS, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+expr(A) ::= PLUS(B) expr(X). [UPLUS] {
+ A = sqlite3PExpr(pParse, TK_UPLUS, X, 0, 0);
+ sqlite3ExprSpan(A,&B,&X->span);
+}
+%type between_op {int}
+between_op(A) ::= BETWEEN. {A = 0;}
+between_op(A) ::= NOT BETWEEN. {A = 1;}
+expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, X, 0);
+ pList = sqlite3ExprListAppend(pParse,pList, Y, 0);
+ A = sqlite3PExpr(pParse, TK_BETWEEN, W, 0, 0);
+ if( A ){
+ A->pList = pList;
+ }else{
+ sqlite3ExprListDelete(pParse->db, pList);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&W->span,&Y->span);
+}
+%ifndef SQLITE_OMIT_SUBQUERY
+ %type in_op {int}
+ in_op(A) ::= IN. {A = 0;}
+ in_op(A) ::= NOT IN. {A = 1;}
+ expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] {
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pList = Y;
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3ExprListDelete(pParse->db, Y);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+ }
+ expr(A) ::= LP(B) select(X) RP(E). {
+ A = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
+ if( A ){
+ A->pSelect = X;
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3SelectDelete(pParse->db, X);
+ }
+ sqlite3ExprSpan(A,&B,&E);
+ }
+ expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] {
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pSelect = Y;
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3SelectDelete(pParse->db, Y);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,&E);
+ }
+ expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] {
+ SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
+ A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
+ if( A ){
+ A->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3SrcListDelete(pParse->db, pSrc);
+ }
+ if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0, 0);
+ sqlite3ExprSpan(A,&X->span,Z.z?&Z:&Y);
+ }
+ expr(A) ::= EXISTS(B) LP select(Y) RP(E). {
+ Expr *p = A = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
+ if( p ){
+ p->pSelect = Y;
+ sqlite3ExprSpan(p,&B,&E);
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3SelectDelete(pParse->db, Y);
+ }
+ }
+%endif SQLITE_OMIT_SUBQUERY
+
+/* CASE expressions */
+expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
+ A = sqlite3PExpr(pParse, TK_CASE, X, Z, 0);
+ if( A ){
+ A->pList = Y;
+ sqlite3ExprSetHeight(pParse, A);
+ }else{
+ sqlite3ExprListDelete(pParse->db, Y);
+ }
+ sqlite3ExprSpan(A, &C, &E);
+}
+%type case_exprlist {ExprList*}
+%destructor case_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
+case_exprlist(A) ::= case_exprlist(X) WHEN expr(Y) THEN expr(Z). {
+ A = sqlite3ExprListAppend(pParse,X, Y, 0);
+ A = sqlite3ExprListAppend(pParse,A, Z, 0);
+}
+case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). {
+ A = sqlite3ExprListAppend(pParse,0, Y, 0);
+ A = sqlite3ExprListAppend(pParse,A, Z, 0);
+}
+%type case_else {Expr*}
+%destructor case_else {sqlite3ExprDelete(pParse->db, $$);}
+case_else(A) ::= ELSE expr(X). {A = X;}
+case_else(A) ::= . {A = 0;}
+%type case_operand {Expr*}
+%destructor case_operand {sqlite3ExprDelete(pParse->db, $$);}
+case_operand(A) ::= expr(X). {A = X;}
+case_operand(A) ::= . {A = 0;}
+
+%type exprlist {ExprList*}
+%destructor exprlist {sqlite3ExprListDelete(pParse->db, $$);}
+%type nexprlist {ExprList*}
+%destructor nexprlist {sqlite3ExprListDelete(pParse->db, $$);}
+
+exprlist(A) ::= nexprlist(X). {A = X;}
+exprlist(A) ::= . {A = 0;}
+nexprlist(A) ::= nexprlist(X) COMMA expr(Y).
+ {A = sqlite3ExprListAppend(pParse,X,Y,0);}
+nexprlist(A) ::= expr(Y).
+ {A = sqlite3ExprListAppend(pParse,0,Y,0);}
+
+
+///////////////////////////// The CREATE INDEX command ///////////////////////
+//
+cmd ::= CREATE(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
+ ON nm(Y) LP idxlist(Z) RP(E). {
+ sqlite3CreateIndex(pParse, &X, &D,
+ sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
+ &S, &E, SQLITE_SO_ASC, NE);
+}
+
+%type uniqueflag {int}
+uniqueflag(A) ::= UNIQUE. {A = OE_Abort;}
+uniqueflag(A) ::= . {A = OE_None;}
+
+%type idxlist {ExprList*}
+%destructor idxlist {sqlite3ExprListDelete(pParse->db, $$);}
+%type idxlist_opt {ExprList*}
+%destructor idxlist_opt {sqlite3ExprListDelete(pParse->db, $$);}
+%type idxitem {Token}
+
+idxlist_opt(A) ::= . {A = 0;}
+idxlist_opt(A) ::= LP idxlist(X) RP. {A = X;}
+idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder(Z). {
+ Expr *p = 0;
+ if( C.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &C);
+ }
+ A = sqlite3ExprListAppend(pParse,X, p, &Y);
+ sqlite3ExprListCheckLength(pParse, A, "index");
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+idxlist(A) ::= idxitem(Y) collate(C) sortorder(Z). {
+ Expr *p = 0;
+ if( C.n>0 ){
+ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ sqlite3ExprSetColl(pParse, p, &C);
+ }
+ A = sqlite3ExprListAppend(pParse,0, p, &Y);
+ sqlite3ExprListCheckLength(pParse, A, "index");
+ if( A ) A->a[A->nExpr-1].sortOrder = Z;
+}
+idxitem(A) ::= nm(X). {A = X;}
+
+%type collate {Token}
+collate(C) ::= . {C.z = 0; C.n = 0;}
+collate(C) ::= COLLATE ids(X). {C = X;}
+
+
+///////////////////////////// The DROP INDEX command /////////////////////////
+//
+cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
+
+///////////////////////////// The VACUUM command /////////////////////////////
+//
+%ifndef SQLITE_OMIT_VACUUM
+%ifndef SQLITE_OMIT_ATTACH
+cmd ::= VACUUM. {sqlite3Vacuum(pParse);}
+cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
+%endif SQLITE_OMIT_ATTACH
+%endif SQLITE_OMIT_VACUUM
+
+///////////////////////////// The PRAGMA command /////////////////////////////
+//
+%ifndef SQLITE_OMIT_PARSER
+%ifndef SQLITE_OMIT_PRAGMA
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ DELETE(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). {
+ sqlite3Pragma(pParse,&X,&Z,&Y,1);
+}
+cmd ::= PRAGMA nm(X) dbnm(Z) LP nmnum(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
+cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);}
+nmnum(A) ::= plus_num(X). {A = X;}
+nmnum(A) ::= nm(X). {A = X;}
+%endif SQLITE_OMIT_PRAGMA
+%endif SQLITE_OMIT_PARSER
+plus_num(A) ::= plus_opt number(X). {A = X;}
+minus_num(A) ::= MINUS number(X). {A = X;}
+number(A) ::= INTEGER|FLOAT(X). {A = X;}
+plus_opt ::= PLUS.
+plus_opt ::= .
+
+//////////////////////////// The CREATE TRIGGER command /////////////////////
+
+%ifndef SQLITE_OMIT_TRIGGER
+
+cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
+ Token all;
+ all.z = A.z;
+ all.n = (Z.z - A.z) + Z.n;
+ sqlite3FinishTrigger(pParse, S, &all);
+}
+
+trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z)
+ trigger_time(C) trigger_event(D)
+ ON fullname(E) foreach_clause when_clause(G). {
+ sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR);
+ A = (Z.n==0?B:Z);
+}
+
+%type trigger_time {int}
+trigger_time(A) ::= BEFORE. { A = TK_BEFORE; }
+trigger_time(A) ::= AFTER. { A = TK_AFTER; }
+trigger_time(A) ::= INSTEAD OF. { A = TK_INSTEAD;}
+trigger_time(A) ::= . { A = TK_BEFORE; }
+
+%type trigger_event {struct TrigEvent}
+%destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);}
+trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;}
+trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;}
+trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;}
+
+foreach_clause ::= .
+foreach_clause ::= FOR EACH ROW.
+
+%type when_clause {Expr*}
+%destructor when_clause {sqlite3ExprDelete(pParse->db, $$);}
+when_clause(A) ::= . { A = 0; }
+when_clause(A) ::= WHEN expr(X). { A = X; }
+
+%type trigger_cmd_list {TriggerStep*}
+%destructor trigger_cmd_list {sqlite3DeleteTriggerStep(pParse->db, $$);}
+trigger_cmd_list(A) ::= trigger_cmd_list(Y) trigger_cmd(X) SEMI. {
+ if( Y ){
+ Y->pLast->pNext = X;
+ }else{
+ Y = X;
+ }
+ Y->pLast = X;
+ A = Y;
+}
+trigger_cmd_list(A) ::= . { A = 0; }
+
+%type trigger_cmd {TriggerStep*}
+%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
+// UPDATE
+trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z).
+ { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }
+
+// INSERT
+trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F)
+ VALUES LP itemlist(Y) RP.
+ {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);}
+
+trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S).
+ {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
+
+// DELETE
+trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y).
+ {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);}
+
+// SELECT
+trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); }
+
+// The special RAISE expression that may occur in trigger programs
+expr(A) ::= RAISE(X) LP IGNORE RP(Y). {
+ A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
+ if( A ){
+ A->iColumn = OE_Ignore;
+ sqlite3ExprSpan(A, &X, &Y);
+ }
+}
+expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). {
+ A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z);
+ if( A ) {
+ A->iColumn = T;
+ sqlite3ExprSpan(A, &X, &Y);
+ }
+}
+%endif !SQLITE_OMIT_TRIGGER
+
+%type raisetype {int}
+raisetype(A) ::= ROLLBACK. {A = OE_Rollback;}
+raisetype(A) ::= ABORT. {A = OE_Abort;}
+raisetype(A) ::= FAIL. {A = OE_Fail;}
+
+
+//////////////////////// DROP TRIGGER statement //////////////////////////////
+%ifndef SQLITE_OMIT_TRIGGER
+cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
+ sqlite3DropTrigger(pParse,X,NOERR);
+}
+%endif !SQLITE_OMIT_TRIGGER
+
+//////////////////////// ATTACH DATABASE file AS name /////////////////////////
+%ifndef SQLITE_OMIT_ATTACH
+cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). {
+ sqlite3Attach(pParse, F, D, K);
+}
+cmd ::= DETACH database_kw_opt expr(D). {
+ sqlite3Detach(pParse, D);
+}
+
+%type key_opt {Expr*}
+%destructor key_opt {sqlite3ExprDelete(pParse->db, $$);}
+key_opt(A) ::= . { A = 0; }
+key_opt(A) ::= KEY expr(X). { A = X; }
+
+database_kw_opt ::= DATABASE.
+database_kw_opt ::= .
+%endif SQLITE_OMIT_ATTACH
+
+////////////////////////// REINDEX collation //////////////////////////////////
+%ifndef SQLITE_OMIT_REINDEX
+cmd ::= REINDEX. {sqlite3Reindex(pParse, 0, 0);}
+cmd ::= REINDEX nm(X) dbnm(Y). {sqlite3Reindex(pParse, &X, &Y);}
+%endif SQLITE_OMIT_REINDEX
+
+/////////////////////////////////// ANALYZE ///////////////////////////////////
+%ifndef SQLITE_OMIT_ANALYZE
+cmd ::= ANALYZE. {sqlite3Analyze(pParse, 0, 0);}
+cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);}
+%endif
+
+//////////////////////// ALTER TABLE table ... ////////////////////////////////
+%ifndef SQLITE_OMIT_ALTERTABLE
+cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
+ sqlite3AlterRenameTable(pParse,X,&Z);
+}
+cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
+ sqlite3AlterFinishAddColumn(pParse, &Y);
+}
+add_column_fullname ::= fullname(X). {
+ sqlite3AlterBeginAddColumn(pParse, X);
+}
+kwcolumn_opt ::= .
+kwcolumn_opt ::= COLUMNKW.
+%endif SQLITE_OMIT_ALTERTABLE
+
+//////////////////////// CREATE VIRTUAL TABLE ... /////////////////////////////
+%ifndef SQLITE_OMIT_VIRTUALTABLE
+cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);}
+cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);}
+create_vtab ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
+ sqlite3VtabBeginParse(pParse, &X, &Y, &Z);
+}
+vtabarglist ::= vtabarg.
+vtabarglist ::= vtabarglist COMMA vtabarg.
+vtabarg ::= . {sqlite3VtabArgInit(pParse);}
+vtabarg ::= vtabarg vtabargtoken.
+vtabargtoken ::= ANY(X). {sqlite3VtabArgExtend(pParse,&X);}
+vtabargtoken ::= lp anylist RP(X). {sqlite3VtabArgExtend(pParse,&X);}
+lp ::= LP(X). {sqlite3VtabArgExtend(pParse,&X);}
+anylist ::= .
+anylist ::= anylist ANY(X). {sqlite3VtabArgExtend(pParse,&X);}
+%endif SQLITE_OMIT_VIRTUALTABLE
diff --git a/third_party/sqlite/pragma.c b/third_party/sqlite/src/pragma.c
index 9a9dd30..6ffc60d 100644..100755
--- a/third_party/sqlite/pragma.c
+++ b/third_party/sqlite/src/pragma.c
@@ -11,21 +11,15 @@
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
-** $Id: pragma.c,v 1.142 2007/06/26 10:38:55 danielk1977 Exp $
+** $Id: pragma.c,v 1.183 2008/07/28 19:34:53 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
/* Ignore this whole file if pragmas are disabled
*/
#if !defined(SQLITE_OMIT_PRAGMA) && !defined(SQLITE_OMIT_PARSER)
-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
-# include "pager.h"
-# include "btree.h"
-#endif
-
/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
@@ -117,7 +111,7 @@ static int getTempStore(const char *z){
static int invalidateTempStorage(Parse *pParse){
sqlite3 *db = pParse->db;
if( db->aDb[1].pBt!=0 ){
- if( !db->autoCommit ){
+ if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){
sqlite3ErrorMsg(pParse, "temporary storage cannot be changed "
"from within a transaction");
return SQLITE_ERROR;
@@ -133,7 +127,7 @@ static int invalidateTempStorage(Parse *pParse){
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
** If the TEMP database is open, close it and mark the database schema
-** as needing reloading. This must be done when using the TEMP_STORE
+** as needing reloading. This must be done when using the SQLITE_TEMP_STORE
** or DEFAULT_TEMP_STORE pragmas.
*/
static int changeTempStorage(Parse *pParse, const char *zStorageType){
@@ -153,12 +147,13 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
*/
static void returnSingleInt(Parse *pParse, const char *zLabel, int value){
Vdbe *v = sqlite3GetVdbe(pParse);
- sqlite3VdbeAddOp(v, OP_Integer, value, 0);
+ int mem = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, value, mem);
if( pParse->explain==0 ){
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, P4_STATIC);
}
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
}
#ifndef SQLITE_OMIT_FLAG_PRAGMAS
@@ -210,8 +205,15 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
}else{
db->flags &= ~p->mask;
}
+
+ /* Many of the flag-pragmas modify the code generated by the SQL
+ ** compiler (eg. count_changes). So add an opcode to expire all
+ ** compiled SQL statements after modifying a pragma value.
+ */
+ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
}
}
+
return 1;
}
}
@@ -248,8 +250,9 @@ void sqlite3Pragma(
int iDb; /* Database index for <database> */
sqlite3 *db = pParse->db;
Db *pDb;
- Vdbe *v = sqlite3GetVdbe(pParse);
+ Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db);
if( v==0 ) return;
+ pParse->nMem = 2;
/* Interpret the [database.] part of the pragma statement. iDb is the
** index of the database this pragma is being applied to in db.aDb[]. */
@@ -264,12 +267,12 @@ void sqlite3Pragma(
return;
}
- zLeft = sqlite3NameFromToken(pId);
+ zLeft = sqlite3NameFromToken(db, pId);
if( !zLeft ) return;
if( minusFlag ){
- zRight = sqlite3MPrintf("-%T", pValue);
+ zRight = sqlite3MPrintf(db, "-%T", pValue);
}else{
- zRight = sqlite3NameFromToken(pValue);
+ zRight = sqlite3NameFromToken(db, pValue);
}
zDb = ((iDb>0)?pDb->zName:0);
@@ -296,19 +299,21 @@ void sqlite3Pragma(
*/
if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){
static const VdbeOpList getCacheSize[] = {
- { OP_ReadCookie, 0, 2, 0}, /* 0 */
- { OP_AbsValue, 0, 0, 0},
- { OP_Dup, 0, 0, 0},
- { OP_Integer, 0, 0, 0},
- { OP_Ne, 0, 6, 0},
- { OP_Integer, 0, 0, 0}, /* 5 */
- { OP_Callback, 1, 0, 0},
+ { OP_ReadCookie, 0, 1, 2}, /* 0 */
+ { OP_IfPos, 1, 6, 0},
+ { OP_Integer, 0, 2, 0},
+ { OP_Subtract, 1, 2, 1},
+ { OP_IfPos, 1, 6, 0},
+ { OP_Integer, 0, 1, 0}, /* 5 */
+ { OP_ResultRow, 1, 1, 0},
};
int addr;
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ sqlite3VdbeUsesBtree(v, iDb);
if( !zRight ){
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", P4_STATIC);
+ pParse->nMem += 2;
addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
sqlite3VdbeChangeP1(v, addr, iDb);
sqlite3VdbeChangeP1(v, addr+5, SQLITE_DEFAULT_CACHE_SIZE);
@@ -316,12 +321,12 @@ void sqlite3Pragma(
int size = atoi(zRight);
if( size<0 ) size = -size;
sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3VdbeAddOp(v, OP_Integer, size, 0);
- sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 2);
- addr = sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
- sqlite3VdbeAddOp(v, OP_Ge, 0, addr+3);
- sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
- sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 2);
+ sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, 2, 2);
+ addr = sqlite3VdbeAddOp2(v, OP_IfPos, 2, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, -size, 1);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 2, 1);
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
@@ -342,7 +347,13 @@ void sqlite3Pragma(
int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
returnSingleInt(pParse, "page_size", size);
}else{
- sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
+ /* Malloc may fail when setting the page-size, as there is an internal
+ ** buffer that the pager module resizes using sqlite3_realloc().
+ */
+ db->nextPagesize = atoi(zRight);
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1) ){
+ db->mallocFailed = 1;
+ }
}
}else
@@ -368,6 +379,24 @@ void sqlite3Pragma(
}else
/*
+ ** PRAGMA [database.]page_count
+ **
+ ** Return the number of pages in the specified database.
+ */
+ if( sqlite3StrICmp(zLeft,"page_count")==0 ){
+ Vdbe *v;
+ int iReg;
+ v = sqlite3GetVdbe(pParse);
+ if( !v || sqlite3ReadSchema(pParse) ) goto pragma_out;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ iReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "page_count", P4_STATIC);
+ }else
+
+ /*
** PRAGMA [database.]locking_mode
** PRAGMA [database.]locking_mode = (normal|exclusive)
*/
@@ -409,10 +438,88 @@ void sqlite3Pragma(
zRet = "exclusive";
}
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P3_STATIC);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, zRet, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P4_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}else
+
+ /*
+ ** PRAGMA [database.]journal_mode
+ ** PRAGMA [database.]journal_mode = (delete|persist|off)
+ */
+ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
+ int eMode;
+ static const char *azModeName[] = {"delete", "persist", "off"};
+
+ if( zRight==0 ){
+ eMode = PAGER_JOURNALMODE_QUERY;
+ }else{
+ int n = strlen(zRight);
+ eMode = 2;
+ while( eMode>=0 && sqlite3StrNICmp(zRight, azModeName[eMode], n)!=0 ){
+ eMode--;
+ }
+ }
+ if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){
+ /* Simple "PRAGMA journal_mode;" statement. This is a query for
+ ** the current default journal mode (which may be different to
+ ** the journal-mode of the main database).
+ */
+ eMode = db->dfltJournalMode;
+ }else{
+ Pager *pPager;
+ if( pId2->n==0 ){
+ /* This indicates that no database name was specified as part
+ ** of the PRAGMA command. In this case the journal-mode must be
+ ** set on all attached databases, as well as the main db file.
+ **
+ ** Also, the sqlite3.dfltJournalMode variable is set so that
+ ** any subsequently attached databases also use the specified
+ ** journal mode.
+ */
+ int ii;
+ assert(pDb==&db->aDb[0]);
+ for(ii=1; ii<db->nDb; ii++){
+ if( db->aDb[ii].pBt ){
+ pPager = sqlite3BtreePager(db->aDb[ii].pBt);
+ sqlite3PagerJournalMode(pPager, eMode);
+ }
+ }
+ db->dfltJournalMode = eMode;
+ }
+ pPager = sqlite3BtreePager(pDb->pBt);
+ eMode = sqlite3PagerJournalMode(pPager, eMode);
+ }
+ assert( eMode==PAGER_JOURNALMODE_DELETE
+ || eMode==PAGER_JOURNALMODE_PERSIST
+ || eMode==PAGER_JOURNALMODE_OFF );
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", P4_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0,
+ azModeName[eMode], P4_STATIC);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ }else
+
+ /*
+ ** PRAGMA [database.]journal_size_limit
+ ** PRAGMA [database.]journal_size_limit=N
+ **
+ ** Get or set the (boolean) value of the database 'auto-vacuum' parameter.
+ */
+ if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
+ Pager *pPager = sqlite3BtreePager(pDb->pBt);
+ i64 iLimit = -2;
+ if( zRight ){
+ int iLimit32 = atoi(zRight);
+ if( iLimit32<-1 ){
+ iLimit32 = -1;
+ }
+ iLimit = iLimit32;
+ }
+ iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
+ returnSingleInt(pParse, "journal_size_limit", (int)iLimit);
+ }else
+
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
/*
@@ -433,6 +540,7 @@ void sqlite3Pragma(
returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
}else{
int eAuto = getAutoVacuum(zRight);
+ db->nextAutovac = eAuto;
if( eAuto>=0 ){
/* Call SetAutoVacuum() to set initialize the internal auto and
** incr-vacuum flags. This is required in case this connection
@@ -448,11 +556,11 @@ void sqlite3Pragma(
*/
static const VdbeOpList setMeta6[] = {
{ OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_ReadCookie, 0, 3, 0}, /* 1 */
- { OP_If, 0, 0, 0}, /* 2 */
+ { OP_ReadCookie, 0, 1, 3}, /* 1 */
+ { OP_If, 1, 0, 0}, /* 2 */
{ OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 0, 0}, /* 4 */
- { OP_SetCookie, 0, 6, 0}, /* 5 */
+ { OP_Integer, 0, 1, 0}, /* 4 */
+ { OP_SetCookie, 0, 6, 1}, /* 5 */
};
int iAddr;
iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6);
@@ -461,6 +569,7 @@ void sqlite3Pragma(
sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
sqlite3VdbeChangeP1(v, iAddr+5, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
}
}
}
@@ -482,11 +591,11 @@ void sqlite3Pragma(
iLimit = 0x7fffffff;
}
sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3VdbeAddOp(v, OP_MemInt, iLimit, 0);
- addr = sqlite3VdbeAddOp(v, OP_IncrVacuum, iDb, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 0, 0);
- sqlite3VdbeAddOp(v, OP_MemIncr, -1, 0);
- sqlite3VdbeAddOp(v, OP_IfMemPos, 0, addr);
+ sqlite3VdbeAddOp2(v, OP_Integer, iLimit, 1);
+ addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb);
+ sqlite3VdbeAddOp1(v, OP_ResultRow, 1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr);
sqlite3VdbeJumpHere(v, addr);
}else
#endif
@@ -552,25 +661,28 @@ void sqlite3Pragma(
if( sqlite3_temp_directory ){
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "temp_store_directory", P3_STATIC);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, sqlite3_temp_directory, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ "temp_store_directory", P4_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_temp_directory, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
}else{
- if( zRight[0] && !sqlite3OsIsDirWritable(zRight) ){
- sqlite3ErrorMsg(pParse, "not a writable directory");
- goto pragma_out;
+ if( zRight[0] ){
+ int res;
+ sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
+ if( res==0 ){
+ sqlite3ErrorMsg(pParse, "not a writable directory");
+ goto pragma_out;
+ }
}
- if( TEMP_STORE==0
- || (TEMP_STORE==1 && db->temp_store<=1)
- || (TEMP_STORE==2 && db->temp_store==1)
+ if( SQLITE_TEMP_STORE==0
+ || (SQLITE_TEMP_STORE==1 && db->temp_store<=1)
+ || (SQLITE_TEMP_STORE==2 && db->temp_store==1)
){
invalidateTempStorage(pParse);
}
- sqliteFree(sqlite3_temp_directory);
+ sqlite3_free(sqlite3_temp_directory);
if( zRight[0] ){
- sqlite3_temp_directory = zRight;
- zRight = 0;
+ sqlite3_temp_directory = sqlite3DbStrDup(0, zRight);
}else{
sqlite3_temp_directory = 0;
}
@@ -630,12 +742,13 @@ void sqlite3Pragma(
int nHidden = 0;
Column *pCol;
sqlite3VdbeSetNumCols(v, 6);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P3_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", P3_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", P3_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", P3_STATIC);
- sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", P3_STATIC);
+ pParse->nMem = 6;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", P4_STATIC);
+ sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", P4_STATIC);
+ sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", P4_STATIC);
+ sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", P4_STATIC);
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const Token *pDflt;
@@ -643,18 +756,18 @@ void sqlite3Pragma(
nHidden++;
continue;
}
- sqlite3VdbeAddOp(v, OP_Integer, i-nHidden, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pCol->zName, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ sqlite3VdbeAddOp2(v, OP_Integer, i-nHidden, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pCol->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
pCol->zType ? pCol->zType : "", 0);
- sqlite3VdbeAddOp(v, OP_Integer, pCol->notNull, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pCol->notNull, 4);
if( pCol->pDflt && (pDflt = &pCol->pDflt->span)->z ){
- sqlite3VdbeOp3(v, OP_String8, 0, 0, (char*)pDflt->z, pDflt->n);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n);
}else{
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
}
- sqlite3VdbeAddOp(v, OP_Integer, pCol->isPrimKey, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 6, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pCol->isPrimKey, 6);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
}
}else
@@ -668,16 +781,17 @@ void sqlite3Pragma(
int i;
pTab = pIdx->pTable;
sqlite3VdbeSetNumCols(v, 3);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", P3_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", P3_STATIC);
+ pParse->nMem = 3;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", P4_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", P4_STATIC);
for(i=0; i<pIdx->nColumn; i++){
int cnum = pIdx->aiColumn[i];
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- sqlite3VdbeAddOp(v, OP_Integer, cnum, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2);
assert( pTab->nCol>cnum );
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[cnum].zName, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
}else
@@ -693,14 +807,15 @@ void sqlite3Pragma(
if( pIdx ){
int i = 0;
sqlite3VdbeSetNumCols(v, 3);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P3_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", P3_STATIC);
+ pParse->nMem = 3;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", P4_STATIC);
while(pIdx){
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0);
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->onError!=OE_None, 3);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
++i;
pIdx = pIdx->pNext;
}
@@ -712,17 +827,18 @@ void sqlite3Pragma(
int i;
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 3);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P3_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", P3_STATIC);
+ pParse->nMem = 3;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", P4_STATIC);
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt==0 ) continue;
assert( db->aDb[i].zName!=0 );
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, db->aDb[i].zName, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, db->aDb[i].zName, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
- sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}else
@@ -730,13 +846,14 @@ void sqlite3Pragma(
int i = 0;
HashElem *p;
sqlite3VdbeSetNumCols(v, 2);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P3_STATIC);
+ pParse->nMem = 2;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){
CollSeq *pColl = (CollSeq *)sqliteHashData(p);
- sqlite3VdbeAddOp(v, OP_Integer, i++, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pColl->zName, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 2, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, i++, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
}else
#endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */
@@ -753,22 +870,23 @@ void sqlite3Pragma(
if( pFK ){
int i = 0;
sqlite3VdbeSetNumCols(v, 5);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", P3_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", P3_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", P3_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", P3_STATIC);
+ pParse->nMem = 5;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", P4_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", P4_STATIC);
+ sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", P4_STATIC);
+ sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", P4_STATIC);
while(pFK){
int j;
for(j=0; j<pFK->nCol; j++){
char *zCol = pFK->aCol[j].zCol;
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- sqlite3VdbeAddOp(v, OP_Integer, j, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0, pFK->zTo, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0,
- pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
- sqlite3VdbeOp3(v, zCol ? OP_String8 : OP_Null, 0, 0, zCol, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 5, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp2(v, OP_Integer, j, 2);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
+ pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
+ sqlite3VdbeAddOp4(v, zCol ? OP_String8 : OP_Null, 0, 5, 0, zCol, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5);
}
++i;
pFK = pFK->pNextFrom;
@@ -804,7 +922,13 @@ void sqlite3Pragma(
#endif
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
- if( sqlite3StrICmp(zLeft, "integrity_check")==0 ){
+ /* Pragma "quick_check" is an experimental reduced version of
+ ** integrity_check designed to detect most database corruption
+ ** without most of the overhead of a full integrity-check.
+ */
+ if( sqlite3StrICmp(zLeft, "integrity_check")==0
+ || sqlite3StrICmp(zLeft, "quick_check")==0
+ ){
int i, j, addr, mxErr;
/* Code that appears at the end of the integrity check. If no error
@@ -812,17 +936,19 @@ void sqlite3Pragma(
** error message
*/
static const VdbeOpList endCode[] = {
- { OP_MemLoad, 0, 0, 0},
- { OP_Integer, 0, 0, 0},
- { OP_Ne, 0, 0, 0}, /* 2 */
- { OP_String8, 0, 0, "ok"},
- { OP_Callback, 1, 0, 0},
+ { OP_AddImm, 1, 0, 0}, /* 0 */
+ { OP_IfNeg, 1, 0, 0}, /* 1 */
+ { OP_String8, 0, 3, 0}, /* 2 */
+ { OP_ResultRow, 3, 1, 0},
};
+ int isQuick = (zLeft[0]=='q');
+
/* Initialize the VDBE program */
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ pParse->nMem = 6;
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", P4_STATIC);
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
@@ -832,7 +958,7 @@ void sqlite3Pragma(
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
}
}
- sqlite3VdbeAddOp(v, OP_MemInt, mxErr, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, mxErr, 1); /* reg[1] holds errors left */
/* Do an integrity check on each database file */
for(i=0; i<db->nDb; i++){
@@ -843,100 +969,119 @@ void sqlite3Pragma(
if( OMIT_TEMPDB && i==1 ) continue;
sqlite3CodeVerifySchema(pParse, i);
- addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
- sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */
+ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
/* Do an integrity check of the B-Tree
+ **
+ ** Begin by filling registers 2, 3, ... with the root pages numbers
+ ** for all tables and indices in the database.
*/
pTbls = &db->aDb[i].pSchema->tblHash;
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
- sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
cnt++;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->tnum, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
cnt++;
}
}
if( cnt==0 ) continue;
- sqlite3VdbeAddOp(v, OP_IntegrityCk, 0, i);
- addr = sqlite3VdbeAddOp(v, OP_IsNull, -1, 0);
- sqlite3VdbeOp3(v, OP_String8, 0, 0,
- sqlite3MPrintf("*** in database %s ***\n", db->aDb[i].zName),
- P3_DYNAMIC);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
- sqlite3VdbeAddOp(v, OP_Concat, 0, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+
+ /* Make sure sufficient number of registers have been allocated */
+ if( pParse->nMem < cnt+4 ){
+ pParse->nMem = cnt+4;
+ }
+
+ /* Do the b-tree integrity checks */
+ sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
+ sqlite3VdbeChangeP5(v, i);
+ addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
+ sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
+ P4_DYNAMIC);
+ sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 2, 1);
sqlite3VdbeJumpHere(v, addr);
/* Make sure all the indices are constructed correctly.
*/
- for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
int loopTop;
if( pTab->pIndex==0 ) continue;
- addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
- sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */
+ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
- sqlite3VdbeAddOp(v, OP_MemInt, 0, 1);
- loopTop = sqlite3VdbeAddOp(v, OP_Rewind, 1, 0);
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, 1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 2); /* reg(2) will count entries */
+ loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int jmp2;
static const VdbeOpList idxErr[] = {
- { OP_MemIncr, -1, 0, 0},
- { OP_String8, 0, 0, "rowid "},
- { OP_Rowid, 1, 0, 0},
- { OP_String8, 0, 0, " missing from index "},
- { OP_String8, 0, 0, 0}, /* 4 */
- { OP_Concat, 2, 0, 0},
- { OP_Callback, 1, 0, 0},
+ { OP_AddImm, 1, -1, 0},
+ { OP_String8, 0, 3, 0}, /* 1 */
+ { OP_Rowid, 1, 4, 0},
+ { OP_String8, 0, 5, 0}, /* 3 */
+ { OP_String8, 0, 6, 0}, /* 4 */
+ { OP_Concat, 4, 3, 3},
+ { OP_Concat, 5, 3, 3},
+ { OP_Concat, 6, 3, 3},
+ { OP_ResultRow, 3, 1, 0},
+ { OP_IfPos, 1, 0, 0}, /* 9 */
+ { OP_Halt, 0, 0, 0},
};
- sqlite3GenerateIndexKey(v, pIdx, 1);
- jmp2 = sqlite3VdbeAddOp(v, OP_Found, j+2, 0);
+ sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 1);
+ jmp2 = sqlite3VdbeAddOp3(v, OP_Found, j+2, 0, 3);
addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
- sqlite3VdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC);
+ sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
+ sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
+ sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_STATIC);
+ sqlite3VdbeJumpHere(v, addr+9);
sqlite3VdbeJumpHere(v, jmp2);
}
- sqlite3VdbeAddOp(v, OP_Next, 1, loopTop+1);
+ sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop+1);
sqlite3VdbeJumpHere(v, loopTop);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
static const VdbeOpList cntIdx[] = {
- { OP_MemInt, 0, 2, 0},
+ { OP_Integer, 0, 3, 0},
{ OP_Rewind, 0, 0, 0}, /* 1 */
- { OP_MemIncr, 1, 2, 0},
+ { OP_AddImm, 3, 1, 0},
{ OP_Next, 0, 0, 0}, /* 3 */
- { OP_MemLoad, 1, 0, 0},
- { OP_MemLoad, 2, 0, 0},
- { OP_Eq, 0, 0, 0}, /* 6 */
- { OP_MemIncr, -1, 0, 0},
- { OP_String8, 0, 0, "wrong # of entries in index "},
- { OP_String8, 0, 0, 0}, /* 9 */
- { OP_Concat, 0, 0, 0},
- { OP_Callback, 1, 0, 0},
+ { OP_Eq, 2, 0, 3}, /* 4 */
+ { OP_AddImm, 1, -1, 0},
+ { OP_String8, 0, 2, 0}, /* 6 */
+ { OP_String8, 0, 3, 0}, /* 7 */
+ { OP_Concat, 3, 2, 2},
+ { OP_ResultRow, 2, 1, 0},
};
if( pIdx->tnum==0 ) continue;
- addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
- sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
+ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
sqlite3VdbeChangeP1(v, addr+1, j+2);
sqlite3VdbeChangeP2(v, addr+1, addr+4);
sqlite3VdbeChangeP1(v, addr+3, j+2);
sqlite3VdbeChangeP2(v, addr+3, addr+2);
- sqlite3VdbeJumpHere(v, addr+6);
- sqlite3VdbeChangeP3(v, addr+9, pIdx->zName, P3_STATIC);
+ sqlite3VdbeJumpHere(v, addr+4);
+ sqlite3VdbeChangeP4(v, addr+6,
+ "wrong # of entries in index ", P4_STATIC);
+ sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_STATIC);
}
}
}
addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
- sqlite3VdbeChangeP1(v, addr+1, mxErr);
- sqlite3VdbeJumpHere(v, addr+2);
+ sqlite3VdbeChangeP2(v, addr, -mxErr);
+ sqlite3VdbeJumpHere(v, addr+1);
+ sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
}else
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -945,7 +1090,7 @@ void sqlite3Pragma(
** PRAGMA encoding
** PRAGMA encoding = "utf-8"|"utf-16"|"utf-16le"|"utf-16be"
**
- ** In it's first form, this pragma returns the encoding of the main
+ ** In its first form, this pragma returns the encoding of the main
** database. If the database is not initialized, it is initialized now.
**
** The second form of this pragma is a no-op if the main database file
@@ -982,15 +1127,15 @@ void sqlite3Pragma(
if( !zRight ){ /* "PRAGMA encoding" */
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", P3_STATIC);
- sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", P4_STATIC);
+ sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
if( pEnc->enc==ENC(pParse->db) ){
- sqlite3VdbeChangeP3(v, -1, pEnc->zName, P3_STATIC);
+ sqlite3VdbeChangeP4(v, -1, pEnc->zName, P4_STATIC);
break;
}
}
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}else{ /* "PRAGMA encoding = XXX" */
/* Only change the value of sqlite.enc if the database handle is not
** initialized. If the main database exists, the new sqlite.enc value
@@ -1047,6 +1192,7 @@ void sqlite3Pragma(
){
int iCookie; /* Cookie index. 0 for schema-cookie, 6 for user-cookie. */
+ sqlite3VdbeUsesBtree(v, iDb);
switch( zLeft[0] ){
case 's': case 'S':
iCookie = 0;
@@ -1065,8 +1211,8 @@ void sqlite3Pragma(
/* Write the specified cookie value */
static const VdbeOpList setCookie[] = {
{ OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_Integer, 0, 0, 0}, /* 1 */
- { OP_SetCookie, 0, 0, 0}, /* 2 */
+ { OP_Integer, 0, 1, 0}, /* 1 */
+ { OP_SetCookie, 0, 0, 1}, /* 2 */
};
int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie);
sqlite3VdbeChangeP1(v, addr, iDb);
@@ -1076,14 +1222,14 @@ void sqlite3Pragma(
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
- { OP_ReadCookie, 0, 0, 0}, /* 0 */
- { OP_Callback, 1, 0, 0}
+ { OP_ReadCookie, 0, 1, 0}, /* 0 */
+ { OP_ResultRow, 1, 1, 0}
};
int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie);
sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP2(v, addr, iCookie);
+ sqlite3VdbeChangeP3(v, addr, iCookie);
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, P3_TRANSIENT);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, P4_TRANSIENT);
}
}else
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
@@ -1099,22 +1245,25 @@ void sqlite3Pragma(
int i;
Vdbe *v = sqlite3GetVdbe(pParse);
sqlite3VdbeSetNumCols(v, 2);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", P3_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", P3_STATIC);
+ pParse->nMem = 2;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", P4_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", P4_STATIC);
for(i=0; i<db->nDb; i++){
Btree *pBt;
Pager *pPager;
+ const char *zState = "unknown";
+ int j;
if( db->aDb[i].zName==0 ) continue;
- sqlite3VdbeOp3(v, OP_String8, 0, 0, db->aDb[i].zName, P3_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, db->aDb[i].zName, P4_STATIC);
pBt = db->aDb[i].pBt;
if( pBt==0 || (pPager = sqlite3BtreePager(pBt))==0 ){
- sqlite3VdbeOp3(v, OP_String8, 0, 0, "closed", P3_STATIC);
- }else{
- int j = sqlite3PagerLockstate(pPager);
- sqlite3VdbeOp3(v, OP_String8, 0, 0,
- (j>=0 && j<=4) ? azLockName[j] : "unknown", P3_STATIC);
+ zState = "closed";
+ }else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0,
+ SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
+ zState = azLockName[j];
}
- sqlite3VdbeAddOp(v, OP_Callback, 2, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
}else
#endif
@@ -1159,7 +1308,7 @@ void sqlite3Pragma(
** the VDBE implementing the pragma to expire. Most (all?) pragmas
** are only valid for a single execution.
*/
- sqlite3VdbeAddOp(v, OP_Expire, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_Expire, 1, 0);
/*
** Reset the safety level, in case the fullfsync flag or synchronous
@@ -1173,8 +1322,8 @@ void sqlite3Pragma(
#endif
}
pragma_out:
- sqliteFree(zLeft);
- sqliteFree(zRight);
+ sqlite3DbFree(db, zLeft);
+ sqlite3DbFree(db, zRight);
}
#endif /* SQLITE_OMIT_PRAGMA || SQLITE_OMIT_PARSER */
diff --git a/third_party/sqlite/prepare.c b/third_party/sqlite/src/prepare.c
index 4a040eb..ef49f0a 100644..100755
--- a/third_party/sqlite/prepare.c
+++ b/third_party/sqlite/src/prepare.c
@@ -13,20 +13,28 @@
** interface, and routines that contribute to loading the database schema
** from disk.
**
-** $Id: prepare.c,v 1.52 2007/08/13 14:41:19 danielk1977 Exp $
+** $Id: prepare.c,v 1.91 2008/08/02 03:50:39 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
/*
** Fill the InitData structure with an error message that indicates
** that the database is corrupt.
*/
-static void corruptSchema(InitData *pData, const char *zExtra){
- if( !sqlite3MallocFailed() ){
- sqlite3SetString(pData->pzErrMsg, "malformed database schema",
- zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0);
+static void corruptSchema(
+ InitData *pData, /* Initialization context */
+ const char *zObj, /* Object being parsed at the point of error */
+ const char *zExtra /* Error information */
+){
+ if( !pData->db->mallocFailed ){
+ if( zObj==0 ) zObj = "?";
+ sqlite3SetString(pData->pzErrMsg, pData->db,
+ "malformed database schema (%s)", zObj);
+ if( zExtra && zExtra[0] ){
+ *pData->pzErrMsg = sqlite3MAppendf(pData->db, *pData->pzErrMsg, "%s - %s",
+ *pData->pzErrMsg, zExtra);
+ }
}
pData->rc = SQLITE_CORRUPT;
}
@@ -48,17 +56,18 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
sqlite3 *db = pData->db;
int iDb = pData->iDb;
+ assert( sqlite3_mutex_held(db->mutex) );
pData->rc = SQLITE_OK;
DbClearProperty(db, iDb, DB_Empty);
- if( sqlite3MallocFailed() ){
- corruptSchema(pData, 0);
+ if( db->mallocFailed ){
+ corruptSchema(pData, argv[0], 0);
return SQLITE_NOMEM;
}
assert( argc==3 );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[1]==0 ){
- corruptSchema(pData, 0);
+ corruptSchema(pData, argv[0], 0);
return 1;
}
assert( iDb>=0 && iDb<db->nDb );
@@ -70,22 +79,28 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
*/
char *zErr;
int rc;
+ u8 lookasideEnabled;
assert( db->init.busy );
db->init.iDb = iDb;
db->init.newTnum = atoi(argv[1]);
+ lookasideEnabled = db->lookaside.bEnabled;
+ db->lookaside.bEnabled = 0;
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
db->init.iDb = 0;
+ db->lookaside.bEnabled = lookasideEnabled;
assert( rc!=SQLITE_OK || zErr==0 );
if( SQLITE_OK!=rc ){
pData->rc = rc;
if( rc==SQLITE_NOMEM ){
- sqlite3FailedMalloc();
+ db->mallocFailed = 1;
}else if( rc!=SQLITE_INTERRUPT ){
- corruptSchema(pData, zErr);
+ corruptSchema(pData, argv[0], zErr);
}
- sqlite3_free(zErr);
+ sqlite3DbFree(db, zErr);
return 1;
}
+ }else if( argv[0]==0 ){
+ corruptSchema(pData, 0, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -157,6 +172,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pSchema );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
/* zMasterSchema and zInitScript are set to point at the master schema
** and initialisation script appropriate for the database being
@@ -170,7 +187,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
zMasterName = SCHEMA_TABLE(iDb);
/* Construct the schema tables. */
- sqlite3SafetyOff(db);
azArg[0] = zMasterName;
azArg[1] = "1";
azArg[2] = zMasterSchema;
@@ -178,16 +194,17 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
initData.db = db;
initData.iDb = iDb;
initData.pzErrMsg = pzErrMsg;
+ (void)sqlite3SafetyOff(db);
rc = sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
+ (void)sqlite3SafetyOn(db);
if( rc ){
- sqlite3SafetyOn(db);
- return initData.rc;
+ rc = initData.rc;
+ goto error_out;
}
pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
if( pTab ){
pTab->readOnly = 1;
}
- sqlite3SafetyOn(db);
/* Create a cursor to hold the database open
*/
@@ -198,10 +215,16 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}
return SQLITE_OK;
}
- rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, 0, &curMain);
+ curMain = sqlite3MallocZero(sqlite3BtreeCursorSize());
+ if( !curMain ){
+ rc = SQLITE_NOMEM;
+ goto error_out;
+ }
+ sqlite3BtreeEnter(pDb->pBt);
+ rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain);
if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
- sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
- return rc;
+ sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
+ goto initone_error_out;
}
/* Get the database meta information.
@@ -223,13 +246,12 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
*/
if( rc==SQLITE_OK ){
int i;
- for(i=0; rc==SQLITE_OK && i<sizeof(meta)/sizeof(meta[0]); i++){
+ for(i=0; i<sizeof(meta)/sizeof(meta[0]); i++){
rc = sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
- }
- if( rc ){
- sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
- sqlite3BtreeCloseCursor(curMain);
- return rc;
+ if( rc ){
+ sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
+ goto initone_error_out;
+ }
}
}else{
memset(meta, 0, sizeof(meta));
@@ -249,10 +271,10 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( meta[4]!=ENC(db) ){
- sqlite3BtreeCloseCursor(curMain);
- sqlite3SetString(pzErrMsg, "attached databases must use the same"
- " text encoding as main database", (char*)0);
- return SQLITE_ERROR;
+ sqlite3SetString(pzErrMsg, db, "attached databases must use the same"
+ " text encoding as main database");
+ rc = SQLITE_ERROR;
+ goto initone_error_out;
}
}
}else{
@@ -260,10 +282,13 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}
pDb->pSchema->enc = ENC(db);
- size = meta[2];
- if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; }
- pDb->pSchema->cache_size = size;
- sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
+ if( pDb->pSchema->cache_size==0 ){
+ size = meta[2];
+ if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; }
+ if( size<0 ) size = -size;
+ pDb->pSchema->cache_size = size;
+ sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
+ }
/*
** file_format==1 Version 3.0.0.
@@ -276,11 +301,19 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
pDb->pSchema->file_format = 1;
}
if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
- sqlite3BtreeCloseCursor(curMain);
- sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
- return SQLITE_ERROR;
+ sqlite3SetString(pzErrMsg, db, "unsupported file format");
+ rc = SQLITE_ERROR;
+ goto initone_error_out;
}
+ /* Ticket #2804: When we open a database in the newer file format,
+ ** clear the legacy_file_format pragma flag so that a VACUUM will
+ ** not downgrade the database and thus invalidate any descending
+ ** indices that the user might have created.
+ */
+ if( iDb==0 && meta[1]>=4 ){
+ db->flags &= ~SQLITE_LegacyFileFmt;
+ }
/* Read the schema information out of the schema tables
*/
@@ -290,23 +323,31 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
rc = SQLITE_OK;
}else{
char *zSql;
- zSql = sqlite3MPrintf(
+ zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s",
db->aDb[iDb].zName, zMasterName);
- sqlite3SafetyOff(db);
- rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+ (void)sqlite3SafetyOff(db);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
+ xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+ rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+ }
+#endif
if( rc==SQLITE_ABORT ) rc = initData.rc;
- sqlite3SafetyOn(db);
- sqliteFree(zSql);
+ (void)sqlite3SafetyOn(db);
+ sqlite3DbFree(db, zSql);
#ifndef SQLITE_OMIT_ANALYZE
if( rc==SQLITE_OK ){
sqlite3AnalysisLoad(db, iDb);
}
#endif
- sqlite3BtreeCloseCursor(curMain);
}
- if( sqlite3MallocFailed() ){
- /* sqlite3SetString(pzErrMsg, "out of memory", (char*)0); */
+ if( db->mallocFailed ){
rc = SQLITE_NOMEM;
sqlite3ResetInternalSchema(db, 0);
}
@@ -317,11 +358,25 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
** will attempt to compile the supplied statement against whatever subset
** of the schema was loaded before the error occured. The primary
** purpose of this is to allow access to the sqlite_master table
- ** even when it's contents have been corrupted.
+ ** even when its contents have been corrupted.
*/
DbSetProperty(db, iDb, DB_SchemaLoaded);
rc = SQLITE_OK;
}
+
+ /* Jump here for an error that occurs after successfully allocating
+ ** curMain and calling sqlite3BtreeEnter(). For an error that occurs
+ ** before that point, jump to error_out.
+ */
+initone_error_out:
+ sqlite3BtreeCloseCursor(curMain);
+ sqlite3_free(curMain);
+ sqlite3BtreeLeave(pDb->pBt);
+
+error_out:
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
+ db->mallocFailed = 1;
+ }
return rc;
}
@@ -339,6 +394,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
int i, rc;
int commit_internal = !(db->flags&SQLITE_InternChanges);
+ assert( sqlite3_mutex_held(db->mutex) );
if( db->init.busy ) return SQLITE_OK;
rc = SQLITE_OK;
db->init.busy = 1;
@@ -378,6 +434,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
int sqlite3ReadSchema(Parse *pParse){
int rc = SQLITE_OK;
sqlite3 *db = pParse->db;
+ assert( sqlite3_mutex_held(db->mutex) );
if( !db->init.busy ){
rc = sqlite3Init(db, &pParse->zErrMsg);
}
@@ -400,19 +457,32 @@ static int schemaIsValid(sqlite3 *db){
int cookie;
int allOk = 1;
- for(iDb=0; allOk && iDb<db->nDb; iDb++){
- Btree *pBt;
- pBt = db->aDb[iDb].pBt;
- if( pBt==0 ) continue;
- rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
- if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
- allOk = 0;
+ curTemp = (BtCursor *)sqlite3Malloc(sqlite3BtreeCursorSize());
+ if( curTemp ){
+ assert( sqlite3_mutex_held(db->mutex) );
+ for(iDb=0; allOk && iDb<db->nDb; iDb++){
+ Btree *pBt;
+ pBt = db->aDb[iDb].pBt;
+ if( pBt==0 ) continue;
+ memset(curTemp, 0, sqlite3BtreeCursorSize());
+ rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, curTemp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
+ if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
+ allOk = 0;
+ }
+ sqlite3BtreeCloseCursor(curTemp);
+ }
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
+ db->mallocFailed = 1;
}
- sqlite3BtreeCloseCursor(curTemp);
}
+ sqlite3_free(curTemp);
+ }else{
+ allOk = 0;
+ db->mallocFailed = 1;
}
+
return allOk;
}
@@ -436,6 +506,7 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
** more likely to cause a segfault than -1 (of course there are assert()
** statements too, but it never hurts to play the odds).
*/
+ assert( sqlite3_mutex_held(db->mutex) );
if( pSchema ){
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pSchema==pSchema ){
@@ -450,7 +521,7 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
-int sqlite3Prepare(
+static int sqlite3Prepare(
sqlite3 *db, /* Database handle. */
const char *zSql, /* UTF-8 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
@@ -463,46 +534,54 @@ int sqlite3Prepare(
int rc = SQLITE_OK;
int i;
- /* Assert that malloc() has not failed */
- assert( !sqlite3MallocFailed() );
-
assert( ppStmt );
*ppStmt = 0;
if( sqlite3SafetyOn(db) ){
return SQLITE_MISUSE;
}
+ assert( !db->mallocFailed );
+ assert( sqlite3_mutex_held(db->mutex) );
/* If any attached database schemas are locked, do not proceed with
** compilation. Instead return SQLITE_LOCKED immediately.
*/
for(i=0; i<db->nDb; i++) {
Btree *pBt = db->aDb[i].pBt;
- if( pBt && sqlite3BtreeSchemaLocked(pBt) ){
- const char *zDb = db->aDb[i].zName;
- sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
- sqlite3SafetyOff(db);
- return SQLITE_LOCKED;
+ if( pBt ){
+ int rc;
+ rc = sqlite3BtreeSchemaLocked(pBt);
+ if( rc ){
+ const char *zDb = db->aDb[i].zName;
+ sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
+ (void)sqlite3SafetyOff(db);
+ return sqlite3ApiExit(db, SQLITE_LOCKED);
+ }
}
}
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
- if( nBytes>=0 && zSql[nBytes]!=0 ){
+ if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
- if( nBytes>SQLITE_MAX_SQL_LENGTH ){
- return SQLITE_TOOBIG;
+ int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
+ if( nBytes>mxLen ){
+ sqlite3Error(db, SQLITE_TOOBIG, "statement too long");
+ (void)sqlite3SafetyOff(db);
+ return sqlite3ApiExit(db, SQLITE_TOOBIG);
}
- zSqlCopy = sqlite3StrNDup(zSql, nBytes);
+ zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
- sqliteFree(zSqlCopy);
+ sqlite3DbFree(db, zSqlCopy);
+ sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
+ }else{
+ sParse.zTail = &zSql[nBytes];
}
- sParse.zTail = &zSql[nBytes];
}else{
sqlite3RunParser(&sParse, zSql, &zErrMsg);
}
- if( sqlite3MallocFailed() ){
+ if( db->mallocFailed ){
sParse.rc = SQLITE_NOMEM;
}
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
@@ -512,7 +591,7 @@ int sqlite3Prepare(
if( sParse.rc==SQLITE_SCHEMA ){
sqlite3ResetInternalSchema(db, 0);
}
- if( sqlite3MallocFailed() ){
+ if( db->mallocFailed ){
sParse.rc = SQLITE_NOMEM;
}
if( pzTail ){
@@ -524,16 +603,19 @@ int sqlite3Prepare(
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
if( sParse.explain==2 ){
sqlite3VdbeSetNumCols(sParse.pVdbe, 3);
- sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "order", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "from", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "detail", P3_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "order", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "from", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "detail", P4_STATIC);
}else{
- sqlite3VdbeSetNumCols(sParse.pVdbe, 5);
- sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "addr", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "opcode", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "p1", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P3_STATIC);
- sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P3_STATIC);
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
+ sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "addr", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "opcode", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "p1", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 5, COLNAME_NAME, "p4", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 6, COLNAME_NAME, "p5", P4_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 7, COLNAME_NAME, "comment",P4_STATIC);
}
}
#endif
@@ -545,7 +627,7 @@ int sqlite3Prepare(
if( saveSqlFlag ){
sqlite3VdbeSetSql(sParse.pVdbe, zSql, sParse.zTail - zSql);
}
- if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
+ if( rc!=SQLITE_OK || db->mallocFailed ){
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
assert(!(*ppStmt));
}else{
@@ -554,16 +636,34 @@ int sqlite3Prepare(
if( zErrMsg ){
sqlite3Error(db, rc, "%s", zErrMsg);
- sqliteFree(zErrMsg);
+ sqlite3DbFree(db, zErrMsg);
}else{
sqlite3Error(db, rc, 0);
}
rc = sqlite3ApiExit(db, rc);
- sqlite3ReleaseThreadData();
assert( (rc&db->errMask)==rc );
return rc;
}
+static int sqlite3LockAndPrepare(
+ sqlite3 *db, /* Database handle. */
+ const char *zSql, /* UTF-8 encoded SQL statement. */
+ int nBytes, /* Length of zSql in bytes. */
+ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */
+ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
+ const char **pzTail /* OUT: End of parsed string */
+){
+ int rc;
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE;
+ }
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail);
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
/*
** Rerun the compilation of a statement after a schema change.
@@ -575,14 +675,17 @@ int sqlite3Reprepare(Vdbe *p){
sqlite3_stmt *pNew;
const char *zSql;
sqlite3 *db;
-
- zSql = sqlite3VdbeGetSql(p);
- if( zSql==0 ){
- return 0;
- }
+
+ assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) );
+ zSql = sqlite3_sql((sqlite3_stmt *)p);
+ assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */
db = sqlite3VdbeDb(p);
- rc = sqlite3Prepare(db, zSql, -1, 0, &pNew, 0);
+ assert( sqlite3_mutex_held(db->mutex) );
+ rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0);
if( rc ){
+ if( rc==SQLITE_NOMEM ){
+ db->mallocFailed = 1;
+ }
assert( pNew==0 );
return 0;
}else{
@@ -611,7 +714,10 @@ int sqlite3_prepare(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const char **pzTail /* OUT: End of parsed string */
){
- return sqlite3Prepare(db,zSql,nBytes,0,ppStmt,pzTail);
+ int rc;
+ rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail);
+ assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */
+ return rc;
}
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle. */
@@ -620,7 +726,10 @@ int sqlite3_prepare_v2(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const char **pzTail /* OUT: End of parsed string */
){
- return sqlite3Prepare(db,zSql,nBytes,1,ppStmt,pzTail);
+ int rc;
+ rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,ppStmt,pzTail);
+ assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */
+ return rc;
}
@@ -644,12 +753,13 @@ static int sqlite3Prepare16(
const char *zTail8 = 0;
int rc = SQLITE_OK;
- if( sqlite3SafetyCheck(db) ){
+ if( !sqlite3SafetyCheckOk(db) ){
return SQLITE_MISUSE;
}
- zSql8 = sqlite3Utf16to8(zSql, nBytes);
+ sqlite3_mutex_enter(db->mutex);
+ zSql8 = sqlite3Utf16to8(db, zSql, nBytes);
if( zSql8 ){
- rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
+ rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
}
if( zTail8 && pzTail ){
@@ -661,8 +771,10 @@ static int sqlite3Prepare16(
int chars_parsed = sqlite3Utf8CharLen(zSql8, zTail8-zSql8);
*pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed);
}
- sqliteFree(zSql8);
- return sqlite3ApiExit(db, rc);
+ sqlite3DbFree(db, zSql8);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
/*
@@ -680,7 +792,10 @@ int sqlite3_prepare16(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */
){
- return sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail);
+ int rc;
+ rc = sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail);
+ assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */
+ return rc;
}
int sqlite3_prepare16_v2(
sqlite3 *db, /* Database handle. */
@@ -689,7 +804,10 @@ int sqlite3_prepare16_v2(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */
){
- return sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail);
+ int rc;
+ rc = sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail);
+ assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */
+ return rc;
}
#endif /* SQLITE_OMIT_UTF16 */
diff --git a/third_party/sqlite/printf.c b/third_party/sqlite/src/printf.c
index 01ed7a1..b88a7d8 100644..100755
--- a/third_party/sqlite/printf.c
+++ b/third_party/sqlite/src/printf.c
@@ -5,6 +5,8 @@
** an historical reference. Most of the "enhancements" have been backed
** out so that the functionality is now the same as standard printf().
**
+** $Id: printf.c,v 1.93 2008/07/28 19:34:53 drh Exp $
+**
**************************************************************************
**
** The following modules is an enhanced replacement for the "printf" subroutines
@@ -51,7 +53,6 @@
**
*/
#include "sqliteInt.h"
-#include <math.h>
/*
** Conversion types fall into various categories as defined by the
@@ -67,14 +68,14 @@
#define etPERCENT 8 /* Percent symbol. %% */
#define etCHARX 9 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
-#define etCHARLIT 10 /* Literal characters. %' */
-#define etSQLESCAPE 11 /* Strings with '\'' doubled. %q */
-#define etSQLESCAPE2 12 /* Strings with '\'' doubled and enclosed in '',
+#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */
+#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '',
NULL pointers replaced by SQL NULL. %Q */
-#define etTOKEN 13 /* a pointer to a Token structure */
-#define etSRCLIST 14 /* a pointer to a SrcList */
-#define etPOINTER 15 /* The %p conversion */
-#define etSQLESCAPE3 16 /* %w -> Strings with '\"' doubled */
+#define etTOKEN 12 /* a pointer to a Token structure */
+#define etSRCLIST 13 /* a pointer to a SrcList */
+#define etPOINTER 14 /* The %p conversion */
+#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
+#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
/*
@@ -113,7 +114,7 @@ static const et_info fmtinfo[] = {
{ 'd', 10, 1, etRADIX, 0, 0 },
{ 's', 0, 4, etSTRING, 0, 0 },
{ 'g', 0, 1, etGENERIC, 30, 0 },
- { 'z', 0, 6, etDYNSTRING, 0, 0 },
+ { 'z', 0, 4, etDYNSTRING, 0, 0 },
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
{ 'w', 0, 4, etSQLESCAPE3, 0, 0 },
@@ -134,6 +135,7 @@ static const et_info fmtinfo[] = {
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ 'T', 0, 2, etTOKEN, 0, 0 },
{ 'S', 0, 2, etSRCLIST, 0, 0 },
+ { 'r', 10, 3, etORDINAL, 0, 0 },
};
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
@@ -168,6 +170,20 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
+** Append N space characters to the given string buffer.
+*/
+static void appendSpace(StrAccum *pAccum, int N){
+ static const char zSpaces[] = " ";
+ while( N>=sizeof(zSpaces)-1 ){
+ sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
+ N -= sizeof(zSpaces)-1;
+ }
+ if( N>0 ){
+ sqlite3StrAccumAppend(pAccum, zSpaces, N);
+ }
+}
+
+/*
** On machines with a small stack size, you can redefine the
** SQLITE_PRINT_BUF_SIZE to be less than 350. But beware - for
** smaller values some %f conversions may go into an infinite loop.
@@ -204,9 +220,8 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
** seems to make a big difference in determining how fast this beast
** will run.
*/
-static int vxprintf(
- void (*func)(void*,const char*,int), /* Consumer of text */
- void *arg, /* First argument to the consumer */
+void sqlite3VXPrintf(
+ StrAccum *pAccum, /* Accumulate results here */
int useExtended, /* Allow extended %-conversions */
const char *fmt, /* Format string */
va_list ap /* arguments */
@@ -216,7 +231,6 @@ static int vxprintf(
int precision; /* Precision of the current field */
int length; /* Length of the field */
int idx; /* A general purpose loop counter */
- int count; /* Total number of characters output */
int width; /* Width of the current field */
etByte flag_leftjustify; /* True if "-" flag is present */
etByte flag_plussign; /* True if "+" flag is present */
@@ -235,9 +249,6 @@ static int vxprintf(
etByte errorflag = 0; /* True if an error is encountered */
etByte xtype; /* Conversion paradigm */
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
- static const char spaces[] =
- " ";
-#define etSPACESIZE (sizeof(spaces)-1)
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
double rounder; /* Used for rounding floating point values */
@@ -247,8 +258,7 @@ static int vxprintf(
int nsd; /* Number of significant digits returned */
#endif
- func(arg,"",0);
- count = length = 0;
+ length = 0;
bufpt = 0;
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
@@ -256,14 +266,12 @@ static int vxprintf(
bufpt = (char *)fmt;
amt = 1;
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
- (*func)(arg,bufpt,amt);
- count += amt;
+ sqlite3StrAccumAppend(pAccum, bufpt, amt);
if( c==0 ) break;
}
if( (c=(*++fmt))==0 ){
errorflag = 1;
- (*func)(arg,"%",1);
- count++;
+ sqlite3StrAccumAppend(pAccum, "%", 1);
break;
}
/* Find out what flags are present */
@@ -337,14 +345,14 @@ static int vxprintf(
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
- return -1;
+ return;
}
break;
}
}
zExtra = 0;
if( infop==0 ){
- return -1;
+ return;
}
@@ -379,6 +387,7 @@ static int vxprintf(
flag_longlong = sizeof(char*)==sizeof(i64);
flag_long = sizeof(char*)==sizeof(long int);
/* Fall through into the next case */
+ case etORDINAL:
case etRADIX:
if( infop->flags & FLAG_SIGNED ){
i64 v;
@@ -405,6 +414,16 @@ static int vxprintf(
precision = width-(prefix!=0);
}
bufpt = &buf[etBUFSIZE-1];
+ if( xtype==etORDINAL ){
+ static const char zOrd[] = "thstndrd";
+ int x = longvalue % 10;
+ if( x>=4 || (longvalue/10)%10==1 ){
+ x = 0;
+ }
+ buf[etBUFSIZE-3] = zOrd[x*2];
+ buf[etBUFSIZE-2] = zOrd[x*2+1];
+ bufpt -= 2;
+ }
{
register const char *cset; /* Use registers for speed */
register int base;
@@ -424,9 +443,7 @@ static int vxprintf(
const char *pre;
char x;
pre = &aPrefix[infop->prefix];
- if( *bufpt!=pre[0] ){
- for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
- }
+ for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
}
length = &buf[etBUFSIZE-1]-bufpt;
break;
@@ -456,7 +473,7 @@ static int vxprintf(
if( xtype==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
- if( sqlite3_isnan(realvalue) ){
+ if( sqlite3IsNaN(realvalue) ){
bufpt = "NaN";
length = 3;
break;
@@ -465,9 +482,9 @@ static int vxprintf(
while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
- while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
- while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
- if( exp>350 || exp<-350 ){
+ while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
+ while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
+ if( exp>350 ){
if( prefix=='-' ){
bufpt = "-Inf";
}else if( prefix=='+' ){
@@ -525,7 +542,8 @@ static int vxprintf(
}
/* "0" digits after the decimal point but before the first
** significant digit of the number */
- for(e2++; e2<0 && precision>0; precision--, e2++){
+ for(e2++; e2<0; precision--, e2++){
+ assert( precision>0 );
*(bufpt++) = '0';
}
/* Significant digits after the decimal point */
@@ -545,7 +563,7 @@ static int vxprintf(
}
}
/* Add the "eNNN" suffix */
- if( flag_exp || (xtype==etEXP && exp) ){
+ if( flag_exp || xtype==etEXP ){
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
@@ -582,7 +600,7 @@ static int vxprintf(
#endif
break;
case etSIZE:
- *(va_arg(ap,int*)) = count;
+ *(va_arg(ap,int*)) = pAccum->nChar;
length = width = 0;
break;
case etPERCENT:
@@ -590,9 +608,8 @@ static int vxprintf(
bufpt = buf;
length = 1;
break;
- case etCHARLIT:
case etCHARX:
- c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
+ c = buf[0] = va_arg(ap,int);
if( precision>=0 ){
for(idx=1; idx<precision; idx++) buf[idx] = c;
length = precision;
@@ -609,8 +626,11 @@ static int vxprintf(
}else if( xtype==etDYNSTRING ){
zExtra = bufpt;
}
- length = strlen(bufpt);
- if( precision>=0 && precision<length ) length = precision;
+ if( precision>=0 ){
+ for(length=0; length<precision && bufpt[length]; length++){}
+ }else{
+ length = strlen(bufpt);
+ }
break;
case etSQLESCAPE:
case etSQLESCAPE2:
@@ -627,8 +647,8 @@ static int vxprintf(
needQuote = !isnull && xtype==etSQLESCAPE2;
n += i + 1 + needQuote*2;
if( n>etBUFSIZE ){
- bufpt = zExtra = sqliteMalloc( n );
- if( bufpt==0 ) return -1;
+ bufpt = zExtra = sqlite3Malloc( n );
+ if( bufpt==0 ) return;
}else{
bufpt = buf;
}
@@ -647,8 +667,8 @@ static int vxprintf(
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
- if( pToken && pToken->z ){
- (*func)(arg, (char*)pToken->z, pToken->n);
+ if( pToken ){
+ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
break;
@@ -658,11 +678,11 @@ static int vxprintf(
int k = va_arg(ap, int);
struct SrcList_item *pItem = &pSrc->a[k];
assert( k>=0 && k<pSrc->nSrc );
- if( pItem->zDatabase && pItem->zDatabase[0] ){
- (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
- (*func)(arg, ".", 1);
+ if( pItem->zDatabase ){
+ sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
+ sqlite3StrAccumAppend(pAccum, ".", 1);
}
- (*func)(arg, pItem->zName, strlen(pItem->zName));
+ sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
length = width = 0;
break;
}
@@ -676,151 +696,163 @@ static int vxprintf(
register int nspace;
nspace = width-length;
if( nspace>0 ){
- count += nspace;
- while( nspace>=etSPACESIZE ){
- (*func)(arg,spaces,etSPACESIZE);
- nspace -= etSPACESIZE;
- }
- if( nspace>0 ) (*func)(arg,spaces,nspace);
+ appendSpace(pAccum, nspace);
}
}
if( length>0 ){
- (*func)(arg,bufpt,length);
- count += length;
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
}
if( flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
- count += nspace;
- while( nspace>=etSPACESIZE ){
- (*func)(arg,spaces,etSPACESIZE);
- nspace -= etSPACESIZE;
- }
- if( nspace>0 ) (*func)(arg,spaces,nspace);
+ appendSpace(pAccum, nspace);
}
}
if( zExtra ){
- sqliteFree(zExtra);
+ sqlite3_free(zExtra);
}
}/* End for loop over the format string */
- return errorflag ? -1 : count;
} /* End of function */
-
-/* This structure is used to store state information about the
-** write to memory that is currently in progress.
-*/
-struct sgMprintf {
- char *zBase; /* A base allocation */
- char *zText; /* The string collected so far */
- int nChar; /* Length of the string so far */
- int nTotal; /* Output size if unconstrained */
- int nAlloc; /* Amount of space allocated in zText */
- void *(*xRealloc)(void*,int); /* Function used to realloc memory */
-};
-
-/*
-** This function implements the callback from vxprintf.
-**
-** This routine add nNewChar characters of text in zNewText to
-** the sgMprintf structure pointed to by "arg".
+/*
+** Append N bytes of text from z to the StrAccum object.
*/
-static void mout(void *arg, const char *zNewText, int nNewChar){
- struct sgMprintf *pM = (struct sgMprintf*)arg;
- pM->nTotal += nNewChar;
- if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
- if( pM->xRealloc==0 ){
- nNewChar = pM->nAlloc - pM->nChar - 1;
+void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
+ if( p->tooBig | p->mallocFailed ){
+ return;
+ }
+ if( N<0 ){
+ N = strlen(z);
+ }
+ if( N==0 ){
+ return;
+ }
+ if( p->nChar+N >= p->nAlloc ){
+ char *zNew;
+ if( !p->useMalloc ){
+ p->tooBig = 1;
+ N = p->nAlloc - p->nChar - 1;
+ if( N<=0 ){
+ return;
+ }
}else{
- int nAlloc = pM->nChar + nNewChar*2 + 1;
- if( pM->zText==pM->zBase ){
- pM->zText = pM->xRealloc(0, nAlloc);
- if( pM->zText && pM->nChar ){
- memcpy(pM->zText, pM->zBase, pM->nChar);
- }
+ i64 szNew = p->nChar;
+ szNew += N + 1;
+ if( szNew > p->mxAlloc ){
+ sqlite3StrAccumReset(p);
+ p->tooBig = 1;
+ return;
}else{
- char *zNew;
- zNew = pM->xRealloc(pM->zText, nAlloc);
- if( zNew ){
- pM->zText = zNew;
- }else{
- return;
- }
+ p->nAlloc = szNew;
+ }
+ zNew = sqlite3DbMallocRaw(p->db, p->nAlloc );
+ if( zNew ){
+ memcpy(zNew, p->zText, p->nChar);
+ sqlite3StrAccumReset(p);
+ p->zText = zNew;
+ }else{
+ p->mallocFailed = 1;
+ sqlite3StrAccumReset(p);
+ return;
}
- pM->nAlloc = nAlloc;
- }
- }
- if( pM->zText ){
- if( nNewChar>0 ){
- memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
- pM->nChar += nNewChar;
}
- pM->zText[pM->nChar] = 0;
}
+ memcpy(&p->zText[p->nChar], z, N);
+ p->nChar += N;
}
/*
-** This routine is a wrapper around xprintf() that invokes mout() as
-** the consumer.
+** Finish off a string by making sure it is zero-terminated.
+** Return a pointer to the resulting string. Return a NULL
+** pointer if any kind of error was encountered.
*/
-static char *base_vprintf(
- void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */
- int useInternal, /* Use internal %-conversions if true */
- char *zInitBuf, /* Initially write here, before mallocing */
- int nInitBuf, /* Size of zInitBuf[] */
- const char *zFormat, /* format string */
- va_list ap /* arguments */
-){
- struct sgMprintf sM;
- sM.zBase = sM.zText = zInitBuf;
- sM.nChar = sM.nTotal = 0;
- sM.nAlloc = nInitBuf;
- sM.xRealloc = xRealloc;
- vxprintf(mout, &sM, useInternal, zFormat, ap);
- if( xRealloc ){
- if( sM.zText==sM.zBase ){
- sM.zText = xRealloc(0, sM.nChar+1);
- if( sM.zText ){
- memcpy(sM.zText, sM.zBase, sM.nChar+1);
- }
- }else if( sM.nAlloc>sM.nChar+10 ){
- char *zNew = xRealloc(sM.zText, sM.nChar+1);
- if( zNew ){
- sM.zText = zNew;
+char *sqlite3StrAccumFinish(StrAccum *p){
+ if( p->zText ){
+ p->zText[p->nChar] = 0;
+ if( p->useMalloc && p->zText==p->zBase ){
+ p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
+ if( p->zText ){
+ memcpy(p->zText, p->zBase, p->nChar+1);
+ }else{
+ p->mallocFailed = 1;
}
}
}
- return sM.zText;
+ return p->zText;
+}
+
+/*
+** Reset an StrAccum string. Reclaim all malloced memory.
+*/
+void sqlite3StrAccumReset(StrAccum *p){
+ if( p->zText!=p->zBase ){
+ sqlite3DbFree(p->db, p->zText);
+ }
+ p->zText = 0;
}
/*
-** Realloc that is a real function, not a macro.
+** Initialize a string accumulator
*/
-static void *printf_realloc(void *old, int size){
- return sqliteRealloc(old,size);
+void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){
+ p->zText = p->zBase = zBase;
+ p->db = 0;
+ p->nChar = 0;
+ p->nAlloc = n;
+ p->mxAlloc = mx;
+ p->useMalloc = 1;
+ p->tooBig = 0;
+ p->mallocFailed = 0;
}
/*
** Print into memory obtained from sqliteMalloc(). Use the internal
** %-conversion extensions.
*/
-char *sqlite3VMPrintf(const char *zFormat, va_list ap){
+char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
+ char *z;
char zBase[SQLITE_PRINT_BUF_SIZE];
- return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
+ StrAccum acc;
+ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
+ db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
+ acc.db = db;
+ sqlite3VXPrintf(&acc, 1, zFormat, ap);
+ z = sqlite3StrAccumFinish(&acc);
+ if( acc.mallocFailed && db ){
+ db->mallocFailed = 1;
+ }
+ return z;
}
/*
** Print into memory obtained from sqliteMalloc(). Use the internal
** %-conversion extensions.
*/
-char *sqlite3MPrintf(const char *zFormat, ...){
+char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
+ va_list ap;
+ char *z;
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(db, zFormat, ap);
+ va_end(ap);
+ return z;
+}
+
+/*
+** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
+** the string and before returnning. This routine is intended to be used
+** to modify an existing string. For example:
+**
+** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
+**
+*/
+char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
va_list ap;
char *z;
- char zBase[SQLITE_PRINT_BUF_SIZE];
va_start(ap, zFormat);
- z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
+ z = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
+ sqlite3DbFree(db, zStr);
return z;
}
@@ -829,8 +861,16 @@ char *sqlite3MPrintf(const char *zFormat, ...){
** %-conversion extensions.
*/
char *sqlite3_vmprintf(const char *zFormat, va_list ap){
+ char *z;
char zBase[SQLITE_PRINT_BUF_SIZE];
- return base_vprintf(sqlite3_realloc, 0, zBase, sizeof(zBase), zFormat, ap);
+ StrAccum acc;
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ z = sqlite3StrAccumFinish(&acc);
+ return z;
}
/*
@@ -840,6 +880,9 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
char *sqlite3_mprintf(const char *zFormat, ...){
va_list ap;
char *z;
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
@@ -855,30 +898,36 @@ char *sqlite3_mprintf(const char *zFormat, ...){
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
char *z;
va_list ap;
+ StrAccum acc;
if( n<=0 ){
return zBuf;
}
- zBuf[0] = 0;
+ sqlite3StrAccumInit(&acc, zBuf, n, 0);
+ acc.useMalloc = 0;
va_start(ap,zFormat);
- z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
va_end(ap);
+ z = sqlite3StrAccumFinish(&acc);
return z;
}
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) || defined(SQLITE_MEMDEBUG)
+#if defined(SQLITE_DEBUG)
/*
** A version of printf() that understands %lld. Used for debugging.
** The printf() built into some versions of windows does not understand %lld
** and segfaults if you give it a long long int.
*/
void sqlite3DebugPrintf(const char *zFormat, ...){
- extern int getpid(void);
va_list ap;
+ StrAccum acc;
char zBuf[500];
- va_start(ap, zFormat);
- base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
+ sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0);
+ acc.useMalloc = 0;
+ va_start(ap,zFormat);
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
va_end(ap);
+ sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
fflush(stdout);
}
diff --git a/third_party/sqlite/random.c b/third_party/sqlite/src/random.c
index 03ed7a4..f328232 100644..100755
--- a/third_party/sqlite/random.c
+++ b/third_party/sqlite/src/random.c
@@ -15,12 +15,20 @@
** Random numbers are used by some of the database backends in order
** to generate random integer keys for tables or random filenames.
**
-** $Id: random.c,v 1.16 2007/01/05 14:38:56 drh Exp $
+** $Id: random.c,v 1.25 2008/06/19 01:03:18 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
+/* All threads share a single random number generator.
+** This structure is the current state of the generator.
+*/
+static struct sqlite3PrngType {
+ unsigned char isInit; /* True if initialized */
+ unsigned char i, j; /* State variables */
+ unsigned char s[256]; /* State variables */
+} sqlite3Prng;
+
/*
** Get a single 8-bit random value from the RC4 PRNG. The Mutex
** must be held while executing this routine.
@@ -40,14 +48,6 @@
static int randomByte(void){
unsigned char t;
- /* All threads share a single random number generator.
- ** This structure is the current state of the generator.
- */
- static struct {
- unsigned char isInit; /* True if initialized */
- unsigned char i, j; /* State variables */
- unsigned char s[256]; /* State variables */
- } prng;
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
@@ -58,43 +58,65 @@ static int randomByte(void){
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
- if( !prng.isInit ){
+ if( !sqlite3Prng.isInit ){
int i;
char k[256];
- prng.j = 0;
- prng.i = 0;
- sqlite3OsRandomSeed(k);
+ sqlite3Prng.j = 0;
+ sqlite3Prng.i = 0;
+ sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
for(i=0; i<256; i++){
- prng.s[i] = i;
+ sqlite3Prng.s[i] = i;
}
for(i=0; i<256; i++){
- prng.j += prng.s[i] + k[i];
- t = prng.s[prng.j];
- prng.s[prng.j] = prng.s[i];
- prng.s[i] = t;
+ sqlite3Prng.j += sqlite3Prng.s[i] + k[i];
+ t = sqlite3Prng.s[sqlite3Prng.j];
+ sqlite3Prng.s[sqlite3Prng.j] = sqlite3Prng.s[i];
+ sqlite3Prng.s[i] = t;
}
- prng.isInit = 1;
+ sqlite3Prng.isInit = 1;
}
/* Generate and return single random byte
*/
- prng.i++;
- t = prng.s[prng.i];
- prng.j += t;
- prng.s[prng.i] = prng.s[prng.j];
- prng.s[prng.j] = t;
- t += prng.s[prng.i];
- return prng.s[t];
+ sqlite3Prng.i++;
+ t = sqlite3Prng.s[sqlite3Prng.i];
+ sqlite3Prng.j += t;
+ sqlite3Prng.s[sqlite3Prng.i] = sqlite3Prng.s[sqlite3Prng.j];
+ sqlite3Prng.s[sqlite3Prng.j] = t;
+ t += sqlite3Prng.s[sqlite3Prng.i];
+ return sqlite3Prng.s[t];
}
/*
** Return N random bytes.
*/
-void sqlite3Randomness(int N, void *pBuf){
+void sqlite3_randomness(int N, void *pBuf){
unsigned char *zBuf = pBuf;
- sqlite3OsEnterMutex();
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
+#endif
+ sqlite3_mutex_enter(mutex);
while( N-- ){
*(zBuf++) = randomByte();
}
- sqlite3OsLeaveMutex();
+ sqlite3_mutex_leave(mutex);
+}
+
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+/*
+** For testing purposes, we sometimes want to preserve the state of
+** PRNG and restore the PRNG to its saved state at a later time.
+** The sqlite3_test_control() interface calls these routines to
+** control the PRNG.
+*/
+static struct sqlite3PrngType sqlite3SavedPrng;
+void sqlite3PrngSaveState(void){
+ memcpy(&sqlite3SavedPrng, &sqlite3Prng, sizeof(sqlite3Prng));
+}
+void sqlite3PrngRestoreState(void){
+ memcpy(&sqlite3Prng, &sqlite3SavedPrng, sizeof(sqlite3Prng));
+}
+void sqlite3PrngResetState(void){
+ sqlite3Prng.isInit = 0;
}
+#endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --git a/third_party/sqlite/select.c b/third_party/sqlite/src/select.c
index 8c29b57..5a8ddf0 100644..100755
--- a/third_party/sqlite/select.c
+++ b/third_party/sqlite/src/select.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.354 2007/07/18 18:17:12 drh Exp $
+** $Id: select.c,v 1.463 2008/08/04 03:51:24 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -21,16 +21,27 @@
** Delete all the content of a Select structure but do not deallocate
** the select structure itself.
*/
-static void clearSelect(Select *p){
- sqlite3ExprListDelete(p->pEList);
- sqlite3SrcListDelete(p->pSrc);
- sqlite3ExprDelete(p->pWhere);
- sqlite3ExprListDelete(p->pGroupBy);
- sqlite3ExprDelete(p->pHaving);
- sqlite3ExprListDelete(p->pOrderBy);
- sqlite3SelectDelete(p->pPrior);
- sqlite3ExprDelete(p->pLimit);
- sqlite3ExprDelete(p->pOffset);
+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);
+}
+
+/*
+** Initialize a SelectDest structure.
+*/
+void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
+ pDest->eDest = eDest;
+ pDest->iParm = iParm;
+ pDest->affinity = 0;
+ pDest->iMem = 0;
+ pDest->nMem = 0;
}
@@ -39,6 +50,7 @@ static void clearSelect(Select *p){
** structure.
*/
Select *sqlite3SelectNew(
+ Parse *pParse, /* Parsing context */
ExprList *pEList, /* which columns to include in the result */
SrcList *pSrc, /* the FROM clause -- which tables to scan */
Expr *pWhere, /* the WHERE clause */
@@ -51,14 +63,15 @@ Select *sqlite3SelectNew(
){
Select *pNew;
Select standin;
- pNew = sqliteMalloc( sizeof(*pNew) );
+ sqlite3 *db = pParse->db;
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
assert( !pOffset || pLimit ); /* Can't have OFFSET without LIMIT. */
if( pNew==0 ){
pNew = &standin;
memset(pNew, 0, sizeof(*pNew));
}
if( pEList==0 ){
- pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_ALL,0,0,0), 0);
+ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0,0,0), 0);
}
pNew->pEList = pEList;
pNew->pSrc = pSrc;
@@ -71,13 +84,11 @@ Select *sqlite3SelectNew(
assert( pOffset==0 || pLimit!=0 );
pNew->pLimit = pLimit;
pNew->pOffset = pOffset;
- pNew->iLimit = -1;
- pNew->iOffset = -1;
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
pNew->addrOpenEphm[2] = -1;
if( pNew==&standin) {
- clearSelect(pNew);
+ clearSelect(db, pNew);
pNew = 0;
}
return pNew;
@@ -86,10 +97,10 @@ Select *sqlite3SelectNew(
/*
** Delete the given Select structure and all of its substructures.
*/
-void sqlite3SelectDelete(Select *p){
+void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( p ){
- clearSelect(p);
- sqliteFree(p);
+ clearSelect(db, p);
+ sqlite3DbFree(db, p);
}
}
@@ -149,12 +160,11 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
(jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) ||
(jointype & JT_ERROR)!=0
){
- const char *zSp1 = " ";
- const char *zSp2 = " ";
- if( pB==0 ){ zSp1++; }
- if( pC==0 ){ zSp2++; }
+ const char *zSp = " ";
+ assert( pB!=0 );
+ if( pC==0 ){ zSp++; }
sqlite3ErrorMsg(pParse, "unknown or unsupported join type: "
- "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC);
+ "%T %T%s%T", pA, pB, zSp, pC);
jointype = JT_INNER;
}else if( jointype & JT_RIGHT ){
sqlite3ErrorMsg(pParse,
@@ -191,62 +201,80 @@ static void setToken(Token *p, const char *z){
**
** {a"bc} -> {"a""bc"}
*/
-static void setQuotedToken(Token *p, const char *z){
- p->z = (u8 *)sqlite3MPrintf("\"%w\"", z);
- p->dyn = 1;
- if( p->z ){
- p->n = strlen((char *)p->z);
+static void setQuotedToken(Parse *pParse, Token *p, const char *z){
+
+ /* Check if the string contains any " characters. If it does, then
+ ** this function will malloc space to create a quoted version of
+ ** the string in. Otherwise, save a call to sqlite3MPrintf() by
+ ** just copying the pointer to the string.
+ */
+ const char *z2 = z;
+ while( *z2 ){
+ if( *z2=='"' ) break;
+ z2++;
+ }
+
+ if( *z2 ){
+ /* String contains " characters - copy and quote the string. */
+ p->z = (u8 *)sqlite3MPrintf(pParse->db, "\"%w\"", z);
+ if( p->z ){
+ p->n = strlen((char *)p->z);
+ p->dyn = 1;
+ }
+ }else{
+ /* String contains no " characters - copy the pointer. */
+ p->z = (u8*)z;
+ p->n = (z2 - z);
+ p->dyn = 0;
}
}
/*
** Create an expression node for an identifier with the name of zName
*/
-Expr *sqlite3CreateIdExpr(const char *zName){
+Expr *sqlite3CreateIdExpr(Parse *pParse, const char *zName){
Token dummy;
setToken(&dummy, zName);
- return sqlite3Expr(TK_ID, 0, 0, &dummy);
+ return sqlite3PExpr(pParse, TK_ID, 0, 0, &dummy);
}
-
/*
** Add a term to the WHERE expression in *ppExpr that requires the
** zCol column to be equal in the two tables pTab1 and pTab2.
*/
static void addWhereTerm(
+ Parse *pParse, /* Parsing context */
const char *zCol, /* Name of the column */
const Table *pTab1, /* First table */
const char *zAlias1, /* Alias for first table. May be NULL */
const Table *pTab2, /* Second table */
const char *zAlias2, /* Alias for second table. May be NULL */
int iRightJoinTable, /* VDBE cursor for the right table */
- Expr **ppExpr /* Add the equality term to this expression */
+ Expr **ppExpr, /* Add the equality term to this expression */
+ int isOuterJoin /* True if dealing with an OUTER join */
){
Expr *pE1a, *pE1b, *pE1c;
Expr *pE2a, *pE2b, *pE2c;
Expr *pE;
- pE1a = sqlite3CreateIdExpr(zCol);
- pE2a = sqlite3CreateIdExpr(zCol);
+ pE1a = sqlite3CreateIdExpr(pParse, zCol);
+ pE2a = sqlite3CreateIdExpr(pParse, zCol);
if( zAlias1==0 ){
zAlias1 = pTab1->zName;
}
- pE1b = sqlite3CreateIdExpr(zAlias1);
+ pE1b = sqlite3CreateIdExpr(pParse, zAlias1);
if( zAlias2==0 ){
zAlias2 = pTab2->zName;
}
- pE2b = sqlite3CreateIdExpr(zAlias2);
- pE1c = sqlite3ExprOrFree(TK_DOT, pE1b, pE1a, 0);
- pE2c = sqlite3ExprOrFree(TK_DOT, pE2b, pE2a, 0);
- pE = sqlite3ExprOrFree(TK_EQ, pE1c, pE2c, 0);
- if( pE ){
+ pE2b = sqlite3CreateIdExpr(pParse, zAlias2);
+ pE1c = sqlite3PExpr(pParse, TK_DOT, pE1b, pE1a, 0);
+ pE2c = sqlite3PExpr(pParse, TK_DOT, pE2b, pE2a, 0);
+ pE = sqlite3PExpr(pParse, TK_EQ, pE1c, pE2c, 0);
+ if( pE && isOuterJoin ){
ExprSetProperty(pE, EP_FromJoin);
pE->iRightJoinTable = iRightJoinTable;
}
- pE = sqlite3ExprAnd(*ppExpr, pE);
- if( pE ){
- *ppExpr = pE;
- }
+ *ppExpr = sqlite3ExprAnd(pParse->db,*ppExpr, pE);
}
/*
@@ -310,8 +338,10 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
Table *pLeftTab = pLeft->pTab;
Table *pRightTab = pRight->pTab;
+ int isOuter;
if( pLeftTab==0 || pRightTab==0 ) continue;
+ isOuter = (pRight->jointype & JT_OUTER)!=0;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
@@ -325,9 +355,9 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
for(j=0; j<pLeftTab->nCol; j++){
char *zName = pLeftTab->aCol[j].zName;
if( columnIndex(pRightTab, zName)>=0 ){
- addWhereTerm(zName, pLeftTab, pLeft->zAlias,
+ addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias,
pRightTab, pRight->zAlias,
- pRight->iCursor, &p->pWhere);
+ pRight->iCursor, &p->pWhere, isOuter);
}
}
@@ -345,8 +375,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
** an AND operator.
*/
if( pRight->pOn ){
- setJoinExpr(pRight->pOn, pRight->iCursor);
- p->pWhere = sqlite3ExprAnd(p->pWhere, pRight->pOn);
+ if( isOuter ) setJoinExpr(pRight->pOn, pRight->iCursor);
+ p->pWhere = sqlite3ExprAnd(pParse->db, p->pWhere, pRight->pOn);
pRight->pOn = 0;
}
@@ -366,9 +396,9 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
"not present in both tables", zName);
return 1;
}
- addWhereTerm(zName, pLeftTab, pLeft->zAlias,
+ addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias,
pRightTab, pRight->zAlias,
- pRight->iCursor, &p->pWhere);
+ pRight->iCursor, &p->pWhere, isOuter);
}
}
}
@@ -382,24 +412,36 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
static void pushOntoSorter(
Parse *pParse, /* Parser context */
ExprList *pOrderBy, /* The ORDER BY clause */
- Select *pSelect /* The whole SELECT statement */
+ Select *pSelect, /* The whole SELECT statement */
+ int regData /* Register holding data to be sorted */
){
Vdbe *v = pParse->pVdbe;
- sqlite3ExprCodeExprList(pParse, pOrderBy);
- sqlite3VdbeAddOp(v, OP_Sequence, pOrderBy->iECursor, 0);
- sqlite3VdbeAddOp(v, OP_Pull, pOrderBy->nExpr + 1, 0);
- sqlite3VdbeAddOp(v, OP_MakeRecord, pOrderBy->nExpr + 2, 0);
- sqlite3VdbeAddOp(v, OP_IdxInsert, pOrderBy->iECursor, 0);
- if( pSelect->iLimit>=0 ){
+ int nExpr = pOrderBy->nExpr;
+ int regBase = sqlite3GetTempRange(pParse, nExpr+2);
+ int regRecord = sqlite3GetTempReg(pParse);
+ sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
+ sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
+ sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord);
+ sqlite3ReleaseTempReg(pParse, regRecord);
+ sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
+ if( pSelect->iLimit ){
int addr1, addr2;
- addr1 = sqlite3VdbeAddOp(v, OP_IfMemZero, pSelect->iLimit+1, 0);
- sqlite3VdbeAddOp(v, OP_MemIncr, -1, pSelect->iLimit+1);
- addr2 = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
+ int iLimit;
+ if( pSelect->iOffset ){
+ iLimit = pSelect->iOffset+1;
+ }else{
+ iLimit = pSelect->iLimit;
+ }
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit);
+ sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1);
+ addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp(v, OP_Last, pOrderBy->iECursor, 0);
- sqlite3VdbeAddOp(v, OP_Delete, pOrderBy->iECursor, 0);
+ sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
+ sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
sqlite3VdbeJumpHere(v, addr2);
- pSelect->iLimit = -1;
+ pSelect->iLimit = 0;
}
}
@@ -409,25 +451,21 @@ static void pushOntoSorter(
static void codeOffset(
Vdbe *v, /* Generate code into this VM */
Select *p, /* The SELECT statement being coded */
- int iContinue, /* Jump here to skip the current record */
- int nPop /* Number of times to pop stack when jumping */
+ int iContinue /* Jump here to skip the current record */
){
- if( p->iOffset>=0 && iContinue!=0 ){
+ if( p->iOffset && iContinue!=0 ){
int addr;
- sqlite3VdbeAddOp(v, OP_MemIncr, -1, p->iOffset);
- addr = sqlite3VdbeAddOp(v, OP_IfMemNeg, p->iOffset, 0);
- if( nPop>0 ){
- sqlite3VdbeAddOp(v, OP_Pop, nPop, 0);
- }
- sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
- VdbeComment((v, "# skip OFFSET records"));
+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1);
+ addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
+ VdbeComment((v, "skip OFFSET records"));
sqlite3VdbeJumpHere(v, addr);
}
}
/*
-** Add code that will check to make sure the top N elements of the
-** stack are distinct. iTab is a sorting index that holds previously
+** Add code that will check to make sure the N registers starting at iMem
+** form a distinct entry. iTab is a sorting index that holds previously
** seen combinations of the N values. A new entry is made in iTab
** if the current N values are new.
**
@@ -435,17 +473,21 @@ static void codeOffset(
** stack if the top N elements are not distinct.
*/
static void codeDistinct(
- Vdbe *v, /* Generate code into this VM */
+ Parse *pParse, /* Parsing and code generating context */
int iTab, /* A sorting index used to test for distinctness */
int addrRepeat, /* Jump to here if not distinct */
- int N /* The top N elements of the stack must be distinct */
+ int N, /* Number of elements */
+ int iMem /* First element */
){
- sqlite3VdbeAddOp(v, OP_MakeRecord, -N, 0);
- sqlite3VdbeAddOp(v, OP_Distinct, iTab, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeAddOp(v, OP_Pop, N+1, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, addrRepeat);
- VdbeComment((v, "# skip indistinct records"));
- sqlite3VdbeAddOp(v, OP_IdxInsert, iTab, 0);
+ Vdbe *v;
+ int r1;
+
+ v = pParse->pVdbe;
+ r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
+ sqlite3VdbeAddOp3(v, OP_Found, iTab, addrRepeat, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
}
/*
@@ -454,7 +496,12 @@ static void codeDistinct(
** column. We do this in a subroutine because the error occurs in multiple
** places.
*/
-static int checkForMultiColumnSelectError(Parse *pParse, int eDest, int nExpr){
+static int checkForMultiColumnSelectError(
+ Parse *pParse, /* Parse context. */
+ SelectDest *pDest, /* Destination of SELECT results */
+ int nExpr /* Number of result columns returned by SELECT */
+){
+ int eDest = pDest->eDest;
if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
sqlite3ErrorMsg(pParse, "only a single result allowed for "
"a SELECT that is part of an expression");
@@ -473,7 +520,7 @@ static int checkForMultiColumnSelectError(Parse *pParse, int eDest, int nExpr){
** then data is pulled from srcTab and pEList is used only to get the
** datatypes for each column.
*/
-static int selectInnerLoop(
+static void selectInnerLoop(
Parse *pParse, /* The parser context */
Select *p, /* The complete select statement being coded */
ExprList *pEList, /* List of values being extracted */
@@ -481,37 +528,54 @@ static int selectInnerLoop(
int nColumn, /* Number of columns in the source table */
ExprList *pOrderBy, /* If not NULL, sort results using this key */
int distinct, /* If >=0, make sure results are distinct */
- int eDest, /* How to dispose of the results */
- int iParm, /* An argument to the disposal method */
+ SelectDest *pDest, /* How to dispose of the results */
int iContinue, /* Jump here to continue with next row */
- int iBreak, /* Jump here to break out of the inner loop */
- char *aff /* affinity string if eDest is SRT_Union */
+ int iBreak /* Jump here to break out of the inner loop */
){
Vdbe *v = pParse->pVdbe;
int i;
int hasDistinct; /* True if the DISTINCT keyword is present */
+ int regResult; /* Start of memory holding result set */
+ int eDest = pDest->eDest; /* How to dispose of results */
+ int iParm = pDest->iParm; /* First argument to disposal method */
+ int nResultCol; /* Number of result columns */
- if( v==0 ) return 0;
+ if( v==0 ) return;
assert( pEList!=0 );
-
- /* If there was a LIMIT clause on the SELECT statement, then do the check
- ** to see if this row should be output.
- */
- hasDistinct = distinct>=0 && pEList->nExpr>0;
+ hasDistinct = distinct>=0;
if( pOrderBy==0 && !hasDistinct ){
- codeOffset(v, p, iContinue, 0);
+ codeOffset(v, p, iContinue);
}
/* Pull the requested columns.
*/
if( nColumn>0 ){
+ nResultCol = nColumn;
+ }else{
+ nResultCol = pEList->nExpr;
+ }
+ if( pDest->iMem==0 ){
+ pDest->iMem = pParse->nMem+1;
+ pDest->nMem = nResultCol;
+ pParse->nMem += nResultCol;
+ }else if( pDest->nMem!=nResultCol ){
+ /* This happens when two SELECTs of a compound SELECT have differing
+ ** numbers of result columns. The error message will be generated by
+ ** a higher-level routine. */
+ return;
+ }
+ regResult = pDest->iMem;
+ if( nColumn>0 ){
for(i=0; i<nColumn; i++){
- sqlite3VdbeAddOp(v, OP_Column, srcTab, i);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
}
- }else{
- nColumn = pEList->nExpr;
- sqlite3ExprCodeExprList(pParse, pEList);
+ }else if( eDest!=SRT_Exists ){
+ /* If the destination is an EXISTS(...) expression, the actual
+ ** values returned by the SELECT are not required.
+ */
+ sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Callback);
}
+ nColumn = nResultCol;
/* If the DISTINCT keyword was present on the SELECT statement
** and this row has been seen before, then do not make this row
@@ -520,14 +584,14 @@ static int selectInnerLoop(
if( hasDistinct ){
assert( pEList!=0 );
assert( pEList->nExpr==nColumn );
- codeDistinct(v, distinct, iContinue, nColumn);
+ codeDistinct(pParse, distinct, iContinue, nColumn, regResult);
if( pOrderBy==0 ){
- codeOffset(v, p, iContinue, nColumn);
+ codeOffset(v, p, iContinue);
}
}
- if( checkForMultiColumnSelectError(pParse, eDest, pEList->nExpr) ){
- return 0;
+ if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
+ return;
}
switch( eDest ){
@@ -536,11 +600,11 @@ static int selectInnerLoop(
*/
#ifndef SQLITE_OMIT_COMPOUND_SELECT
case SRT_Union: {
- sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
- if( aff ){
- sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
- }
- sqlite3VdbeAddOp(v, OP_IdxInsert, iParm, 0);
+ int r1;
+ r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -549,11 +613,7 @@ static int selectInnerLoop(
** the temporary table iParm.
*/
case SRT_Except: {
- int addr;
- addr = sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
- sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
- sqlite3VdbeAddOp(v, OP_NotFound, iParm, addr+3);
- sqlite3VdbeAddOp(v, OP_Delete, iParm, 0);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn);
break;
}
#endif
@@ -562,14 +622,18 @@ static int selectInnerLoop(
*/
case SRT_Table:
case SRT_EphemTab: {
- sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p);
+ pushOntoSorter(pParse, pOrderBy, p, r1);
}else{
- sqlite3VdbeAddOp(v, OP_NewRowid, iParm, 0);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
- sqlite3VdbeAddOp(v, OP_Insert, iParm, OPFLAG_APPEND);
+ int r2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
+ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+ sqlite3ReleaseTempReg(pParse, r2);
}
+ sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -579,33 +643,28 @@ static int selectInnerLoop(
** item into the set table with bogus data.
*/
case SRT_Set: {
- int addr1 = sqlite3VdbeCurrentAddr(v);
- int addr2;
-
assert( nColumn==1 );
- sqlite3VdbeAddOp(v, OP_NotNull, -1, addr1+3);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- addr2 = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
- p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr,(iParm>>16)&0xff);
+ p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affinity);
if( pOrderBy ){
/* At first glance you would think we could optimize out the
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
- pushOntoSorter(pParse, pOrderBy, p);
+ pushOntoSorter(pParse, pOrderBy, p, regResult);
}else{
- sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &p->affinity, 1);
- sqlite3VdbeAddOp(v, OP_IdxInsert, (iParm&0x0000FFFF), 0);
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, 1, r1, &p->affinity, 1);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
}
- sqlite3VdbeJumpHere(v, addr2);
break;
}
/* If any row exist in the result set, record that fact and abort.
*/
case SRT_Exists: {
- sqlite3VdbeAddOp(v, OP_MemInt, 1, iParm);
- sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, iParm);
/* The LIMIT clause will terminate the loop for us */
break;
}
@@ -617,9 +676,9 @@ static int selectInnerLoop(
case SRT_Mem: {
assert( nColumn==1 );
if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p);
+ pushOntoSorter(pParse, pOrderBy, p, regResult);
}else{
- sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
+ sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
/* The LIMIT clause will jump out of the loop for us */
}
break;
@@ -630,15 +689,18 @@ static int selectInnerLoop(
** case of a subroutine, the subroutine itself is responsible for
** popping the data from the stack.
*/
- case SRT_Subroutine:
+ case SRT_Coroutine:
case SRT_Callback: {
if( pOrderBy ){
- sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
- pushOntoSorter(pParse, pOrderBy, p);
- }else if( eDest==SRT_Subroutine ){
- sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm);
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
+ pushOntoSorter(pParse, pOrderBy, p, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
+ }else if( eDest==SRT_Coroutine ){
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
}else{
- sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn);
}
break;
}
@@ -651,7 +713,6 @@ static int selectInnerLoop(
*/
default: {
assert( eDest==SRT_Discard );
- sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
break;
}
#endif
@@ -659,11 +720,12 @@ static int selectInnerLoop(
/* Jump to the end of the loop if the LIMIT is reached.
*/
- if( p->iLimit>=0 && pOrderBy==0 ){
- sqlite3VdbeAddOp(v, OP_MemIncr, -1, p->iLimit);
- sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, iBreak);
+ if( p->iLimit ){
+ assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to
+ ** pushOntoSorter() would have cleared p->iLimit */
+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
+ sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak);
}
- return 0;
}
/*
@@ -678,8 +740,8 @@ static int selectInnerLoop(
**
** Space to hold the KeyInfo structure is obtain from malloc. The calling
** function is responsible for seeing that this structure is eventually
-** freed. Add the KeyInfo structure to the P3 field of an opcode using
-** P3_KEYINFO_HANDOFF is the usual way of dealing with this.
+** freed. Add the KeyInfo structure to the P4 field of an opcode using
+** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
*/
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
sqlite3 *db = pParse->db;
@@ -689,7 +751,7 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
int i;
nExpr = pList->nExpr;
- pInfo = sqliteMalloc( sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) );
+ pInfo = sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) );
if( pInfo ){
pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr];
pInfo->nField = nExpr;
@@ -715,12 +777,11 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
** routine generates the code needed to do that.
*/
static void generateSortTail(
- Parse *pParse, /* Parsing context */
- Select *p, /* The SELECT statement */
- Vdbe *v, /* Generate code into this VDBE */
- int nColumn, /* Number of columns of data */
- int eDest, /* Write the sorted results here */
- int iParm /* Optional parameter associated with eDest */
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The SELECT statement */
+ Vdbe *v, /* Generate code into this VDBE */
+ int nColumn, /* Number of columns of data */
+ SelectDest *pDest /* Write the sorted results here */
){
int brk = sqlite3VdbeMakeLabel(v);
int cont = sqlite3VdbeMakeLabel(v);
@@ -729,54 +790,60 @@ static void generateSortTail(
int pseudoTab = 0;
ExprList *pOrderBy = p->pOrderBy;
+ int eDest = pDest->eDest;
+ int iParm = pDest->iParm;
+
+ int regRow;
+ int regRowid;
+
iTab = pOrderBy->iECursor;
- if( eDest==SRT_Callback || eDest==SRT_Subroutine ){
+ if( eDest==SRT_Callback || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++;
- sqlite3VdbeAddOp(v, OP_OpenPseudo, pseudoTab, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, pseudoTab, nColumn);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn);
+ sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Callback);
}
- addr = 1 + sqlite3VdbeAddOp(v, OP_Sort, iTab, brk);
- codeOffset(v, p, cont, 0);
- if( eDest==SRT_Callback || eDest==SRT_Subroutine ){
- sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
- }
- sqlite3VdbeAddOp(v, OP_Column, iTab, pOrderBy->nExpr + 1);
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, brk);
+ codeOffset(v, p, cont);
+ regRow = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow);
switch( eDest ){
case SRT_Table:
case SRT_EphemTab: {
- sqlite3VdbeAddOp(v, OP_NewRowid, iParm, 0);
- sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
- sqlite3VdbeAddOp(v, OP_Insert, iParm, OPFLAG_APPEND);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
case SRT_Set: {
assert( nColumn==1 );
- sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
- sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &p->affinity, 1);
- sqlite3VdbeAddOp(v, OP_IdxInsert, (iParm&0x0000FFFF), 0);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, &p->affinity, 1);
+ sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
break;
}
case SRT_Mem: {
assert( nColumn==1 );
- sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
+ sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
/* The LIMIT clause will terminate the loop for us */
break;
}
#endif
case SRT_Callback:
- case SRT_Subroutine: {
+ case SRT_Coroutine: {
int i;
- sqlite3VdbeAddOp(v, OP_Insert, pseudoTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, pseudoTab, regRow, regRowid);
for(i=0; i<nColumn; i++){
- sqlite3VdbeAddOp(v, OP_Column, pseudoTab, i);
+ assert( regRow!=pDest->iMem+i );
+ sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i);
}
if( eDest==SRT_Callback ){
- sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn);
}else{
- sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
}
break;
}
@@ -785,21 +852,20 @@ static void generateSortTail(
break;
}
}
+ sqlite3ReleaseTempReg(pParse, regRow);
+ sqlite3ReleaseTempReg(pParse, regRowid);
- /* Jump to the end of the loop when the LIMIT is reached
+ /* LIMIT has been implemented by the pushOntoSorter() routine.
*/
- if( p->iLimit>=0 ){
- sqlite3VdbeAddOp(v, OP_MemIncr, -1, p->iLimit);
- sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, brk);
- }
+ assert( p->iLimit==0 );
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, cont);
- sqlite3VdbeAddOp(v, OP_Next, iTab, addr);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
sqlite3VdbeResolveLabel(v, brk);
- if( eDest==SRT_Callback || eDest==SRT_Subroutine ){
- sqlite3VdbeAddOp(v, OP_Close, pseudoTab, 0);
+ if( eDest==SRT_Callback || eDest==SRT_Coroutine ){
+ sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
}
}
@@ -946,6 +1012,7 @@ static void generateColumnTypes(
SrcList *pTabList, /* List of tables */
ExprList *pEList /* Expressions defining the result set */
){
+#ifndef SQLITE_OMIT_DECLTYPE
Vdbe *v = pParse->pVdbe;
int i;
NameContext sNC;
@@ -953,20 +1020,26 @@ static void generateColumnTypes(
sNC.pParse = pParse;
for(i=0; i<pEList->nExpr; i++){
Expr *p = pEList->a[i].pExpr;
+ const char *zType;
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
const char *zOrigDb = 0;
const char *zOrigTab = 0;
const char *zOrigCol = 0;
- const char *zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
- /* The vdbe must make it's own copy of the column-type and other
+ /* The vdbe must make its own copy of the column-type and other
** column specific strings, in case the schema is reset before this
** virtual machine is deleted.
*/
- sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, P3_TRANSIENT);
- sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, P3_TRANSIENT);
- sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, P3_TRANSIENT);
- sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, P3_TRANSIENT);
+ sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, P4_TRANSIENT);
+ sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, P4_TRANSIENT);
+ sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, P4_TRANSIENT);
+#else
+ zType = columnType(&sNC, p, 0, 0, 0);
+#endif
+ sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, P4_TRANSIENT);
}
+#endif /* SQLITE_OMIT_DECLTYPE */
}
/*
@@ -992,7 +1065,7 @@ static void generateColumnNames(
#endif
assert( v!=0 );
- if( pParse->colNamesSet || v==0 || sqlite3MallocFailed() ) return;
+ if( pParse->colNamesSet || v==0 || db->mallocFailed ) return;
pParse->colNamesSet = 1;
fullNames = (db->flags & SQLITE_FullColNames)!=0;
shortNames = (db->flags & SQLITE_ShortColNames)!=0;
@@ -1004,9 +1077,7 @@ static void generateColumnNames(
if( pEList->a[i].zName ){
char *zName = pEList->a[i].zName;
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, strlen(zName));
- continue;
- }
- if( p->op==TK_COLUMN && pTabList ){
+ }else if( p->op==TK_COLUMN && pTabList ){
Table *pTab;
char *zCol;
int iCol = p->iColumn;
@@ -1020,7 +1091,7 @@ static void generateColumnNames(
}else{
zCol = pTab->aCol[iCol].zName;
}
- if( !shortNames && !fullNames && p->span.z && p->span.z[0] ){
+ if( !shortNames && !fullNames ){
sqlite3VdbeSetColName(v, i, COLNAME_NAME, (char*)p->span.z, p->span.n);
}else if( fullNames || (!shortNames && pTabList->nSrc>1) ){
char *zName = 0;
@@ -1028,19 +1099,13 @@ static void generateColumnNames(
zTab = pTabList->a[j].zAlias;
if( fullNames || zTab==0 ) zTab = pTab->zName;
- sqlite3SetString(&zName, zTab, ".", zCol, (char*)0);
- sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, P3_DYNAMIC);
+ zName = sqlite3MPrintf(db, "%s.%s", zTab, zCol);
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, P4_DYNAMIC);
}else{
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, strlen(zCol));
}
- }else if( p->span.z && p->span.z[0] ){
- sqlite3VdbeSetColName(v, i, COLNAME_NAME, (char*)p->span.z, p->span.n);
- /* sqlite3VdbeCompressSpace(v, addr); */
}else{
- char zName[30];
- assert( p->op!=TK_COLUMN || pTabList==0 );
- sqlite3_snprintf(sizeof(zName), zName, "column%d", i+1);
- sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, 0);
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, (char*)p->span.z, p->span.n);
}
}
generateColumnTypes(pParse, pTabList, pEList);
@@ -1073,29 +1138,41 @@ static int prepSelectStmt(Parse*, Select*);
*/
Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
Table *pTab;
- int i, j;
+ int i, j, rc;
ExprList *pEList;
Column *aCol, *pCol;
-
- while( pSelect->pPrior ) pSelect = pSelect->pPrior;
- if( prepSelectStmt(pParse, pSelect) ){
- return 0;
+ sqlite3 *db = pParse->db;
+ int savedFlags;
+
+ savedFlags = db->flags;
+ db->flags &= ~SQLITE_FullColNames;
+ db->flags |= SQLITE_ShortColNames;
+ rc = sqlite3SelectResolve(pParse, pSelect, 0);
+ if( rc==SQLITE_OK ){
+ while( pSelect->pPrior ) pSelect = pSelect->pPrior;
+ rc = prepSelectStmt(pParse, pSelect);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3SelectResolve(pParse, pSelect, 0);
+ }
}
- if( sqlite3SelectResolve(pParse, pSelect, 0) ){
+ db->flags = savedFlags;
+ if( rc ){
return 0;
}
- pTab = sqliteMalloc( sizeof(Table) );
+ pTab = sqlite3DbMallocZero(db, sizeof(Table) );
if( pTab==0 ){
return 0;
}
+ pTab->db = db;
pTab->nRef = 1;
- pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0;
+ pTab->zName = zTabName ? sqlite3DbStrDup(db, zTabName) : 0;
pEList = pSelect->pEList;
pTab->nCol = pEList->nExpr;
assert( pTab->nCol>0 );
- pTab->aCol = aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol );
+ pTab->aCol = aCol = sqlite3DbMallocZero(db, sizeof(pTab->aCol[0])*pTab->nCol);
+ testcase( aCol==0 );
for(i=0, pCol=aCol; i<pTab->nCol; i++, pCol++){
- Expr *p, *pR;
+ Expr *p;
char *zType;
char *zName;
int nName;
@@ -1109,24 +1186,21 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
assert( p->pRight==0 || p->pRight->token.z==0 || p->pRight->token.z[0]!=0 );
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
- zName = sqliteStrDup(zName);
- }else if( p->op==TK_DOT
- && (pR=p->pRight)!=0 && pR->token.z && pR->token.z[0] ){
- /* For columns of the from A.B use B as the name */
- zName = sqlite3MPrintf("%T", &pR->token);
- }else if( p->span.z && p->span.z[0] ){
- /* Use the original text of the column expression as its name */
- zName = sqlite3MPrintf("%T", &p->span);
+ zName = sqlite3DbStrDup(db, zName);
+ }else if( p->op==TK_COLUMN && p->pTab ){
+ /* For columns use the column name name */
+ int iCol = p->iColumn;
+ if( iCol<0 ) iCol = p->pTab->iPKey;
+ zName = sqlite3MPrintf(db, "%s", p->pTab->aCol[iCol].zName);
}else{
- /* If all else fails, make up a name */
- zName = sqlite3MPrintf("column%d", i+1);
+ /* Use the original text of the column expression as its name */
+ zName = sqlite3MPrintf(db, "%T", &p->span);
}
- sqlite3Dequote(zName);
- if( sqlite3MallocFailed() ){
- sqliteFree(zName);
- sqlite3DeleteTable(pTab);
- return 0;
+ if( db->mallocFailed ){
+ sqlite3DbFree(db, zName);
+ break;
}
+ sqlite3Dequote(zName);
/* Make sure the column name is unique. If the name is not unique,
** append a integer to the name so that it becomes unique.
@@ -1134,8 +1208,11 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
nName = strlen(zName);
for(j=cnt=0; j<i; j++){
if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
+ char *zNewName;
zName[nName] = 0;
- zName = sqlite3MPrintf("%z:%d", zName, ++cnt);
+ zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
+ sqlite3DbFree(db, zName);
+ zName = zNewName;
j = -1;
if( zName==0 ) break;
}
@@ -1147,15 +1224,19 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
*/
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
- zType = sqliteStrDup(columnType(&sNC, p, 0, 0, 0));
+ zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
pCol->zType = zType;
pCol->affinity = sqlite3ExprAffinity(p);
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl ){
- pCol->zColl = sqliteStrDup(pColl->zName);
+ pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
}
}
pTab->iPKey = -1;
+ if( db->mallocFailed ){
+ sqlite3DeleteTable(pTab);
+ return 0;
+ }
return pTab;
}
@@ -1190,8 +1271,9 @@ static int prepSelectStmt(Parse *pParse, Select *p){
SrcList *pTabList;
ExprList *pEList;
struct SrcList_item *pFrom;
+ sqlite3 *db = pParse->db;
- if( p==0 || p->pSrc==0 || sqlite3MallocFailed() ){
+ if( p==0 || p->pSrc==0 || db->mallocFailed ){
return 1;
}
pTabList = p->pSrc;
@@ -1220,7 +1302,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){
assert( pFrom->pSelect!=0 );
if( pFrom->zAlias==0 ){
pFrom->zAlias =
- sqlite3MPrintf("sqlite_subquery_%p_", (void*)pFrom->pSelect);
+ sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pFrom->pSelect);
}
assert( pFrom->pTab==0 );
pFrom->pTab = pTab =
@@ -1238,7 +1320,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){
/* An ordinary table or view name in the FROM clause */
assert( pFrom->pTab==0 );
pFrom->pTab = pTab =
- sqlite3LocateTable(pParse,pFrom->zName,pFrom->zDatabase);
+ sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase);
if( pTab==0 ){
return 1;
}
@@ -1255,7 +1337,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){
** in the inner view.
*/
if( pFrom->pSelect==0 ){
- pFrom->pSelect = sqlite3SelectDup(pTab->pSelect);
+ pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect);
}
}
#endif
@@ -1292,8 +1374,8 @@ static int prepSelectStmt(Parse *pParse, Select *p){
struct ExprList_item *a = pEList->a;
ExprList *pNew = 0;
int flags = pParse->db->flags;
- int longNames = (flags & SQLITE_FullColNames)!=0 &&
- (flags & SQLITE_ShortColNames)==0;
+ int longNames = (flags & SQLITE_FullColNames)!=0
+ && (flags & SQLITE_ShortColNames)==0;
for(k=0; k<pEList->nExpr; k++){
Expr *pE = a[k].pExpr;
@@ -1301,7 +1383,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){
(pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
/* This particular expression does not need to be expanded.
*/
- pNew = sqlite3ExprListAppend(pNew, a[k].pExpr, 0);
+ pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr, 0);
if( pNew ){
pNew->a[pNew->nExpr-1].zName = a[k].zName;
}else{
@@ -1315,7 +1397,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){
int tableSeen = 0; /* Set to 1 when TABLE matches */
char *zTName; /* text of name of TABLE */
if( pE->op==TK_DOT && pE->pLeft ){
- zTName = sqlite3NameFromToken(&pE->pLeft->token);
+ zTName = sqlite3NameFromToken(db, &pE->pLeft->token);
}else{
zTName = 0;
}
@@ -1325,8 +1407,8 @@ static int prepSelectStmt(Parse *pParse, Select *p){
if( zTabName==0 || zTabName[0]==0 ){
zTabName = pTab->zName;
}
- if( zTName && (zTabName==0 || zTabName[0]==0 ||
- sqlite3StrICmp(zTName, zTabName)!=0) ){
+ assert( zTabName );
+ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
}
tableSeen = 1;
@@ -1357,16 +1439,22 @@ static int prepSelectStmt(Parse *pParse, Select *p){
continue;
}
}
- pRight = sqlite3Expr(TK_ID, 0, 0, 0);
+ pRight = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
if( pRight==0 ) break;
- setQuotedToken(&pRight->token, zName);
- if( zTabName && (longNames || pTabList->nSrc>1) ){
- Expr *pLeft = sqlite3Expr(TK_ID, 0, 0, 0);
- pExpr = sqlite3Expr(TK_DOT, pLeft, pRight, 0);
+ setQuotedToken(pParse, &pRight->token, zName);
+ if( longNames || pTabList->nSrc>1 ){
+ Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, 0);
+ pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
if( pExpr==0 ) break;
- setQuotedToken(&pLeft->token, zTabName);
- setToken(&pExpr->span, sqlite3MPrintf("%s.%s", zTabName, zName));
+ setQuotedToken(pParse, &pLeft->token, zTabName);
+#if 1
+ setToken(&pExpr->span,
+ sqlite3MPrintf(db, "%s.%s", zTabName, zName));
pExpr->span.dyn = 1;
+#else
+ pExpr->span = pRight->token;
+ pExpr->span.dyn = 0;
+#endif
pExpr->token.z = 0;
pExpr->token.n = 0;
pExpr->token.dyn = 0;
@@ -1376,9 +1464,9 @@ static int prepSelectStmt(Parse *pParse, Select *p){
pExpr->span.dyn = 0;
}
if( longNames ){
- pNew = sqlite3ExprListAppend(pNew, pExpr, &pExpr->span);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pExpr->span);
}else{
- pNew = sqlite3ExprListAppend(pNew, pExpr, &pRight->token);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pExpr, &pRight->token);
}
}
}
@@ -1390,112 +1478,259 @@ static int prepSelectStmt(Parse *pParse, Select *p){
}
rc = 1;
}
- sqliteFree(zTName);
+ sqlite3DbFree(db, zTName);
}
}
- sqlite3ExprListDelete(pEList);
+ sqlite3ExprListDelete(db, pEList);
p->pEList = pNew;
}
- if( p->pEList && p->pEList->nExpr>SQLITE_MAX_COLUMN ){
+#if SQLITE_MAX_COLUMN
+ if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns in result set");
rc = SQLITE_ERROR;
}
- if( sqlite3MallocFailed() ){
+#endif
+ if( db->mallocFailed ){
rc = SQLITE_NOMEM;
}
return rc;
}
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
-** This routine associates entries in an ORDER BY expression list with
-** columns in a result. For each ORDER BY expression, the opcode of
-** the top-level node is changed to TK_COLUMN and the iColumn value of
-** the top-level node is filled in with column number and the iTable
-** value of the top-level node is filled with iTable parameter.
+** pE is a pointer to an expression which is a single term in
+** ORDER BY or GROUP BY clause.
+**
+** At the point this routine is called, we already know that the
+** ORDER BY term is not an integer index into the result set. That
+** casee is handled by the calling routine.
**
-** If there are prior SELECT clauses, they are processed first. A match
-** in an earlier SELECT takes precedence over a later SELECT.
+** If pE is a well-formed expression and the SELECT statement
+** is not compound, then return 0. This indicates to the
+** caller that it should sort by the value of the ORDER BY
+** expression.
**
-** Any entry that does not match is flagged as an error. The number
-** of errors is returned.
+** If the SELECT is compound, then attempt to match pE against
+** result set columns in the left-most SELECT statement. Return
+** the index i of the matching column, as an indication to the
+** caller that it should sort by the i-th column. If there is
+** no match, return -1 and leave an error message in pParse.
*/
-static int matchOrderbyToColumn(
- Parse *pParse, /* A place to leave error messages */
- Select *pSelect, /* Match to result columns of this SELECT */
- ExprList *pOrderBy, /* The ORDER BY values to match against columns */
- int iTable, /* Insert this value in iTable */
- int mustComplete /* If TRUE all ORDER BYs must match */
+static int matchOrderByTermToExprList(
+ Parse *pParse, /* Parsing context for error messages */
+ Select *pSelect, /* The SELECT statement with the ORDER BY clause */
+ Expr *pE, /* The specific ORDER BY term */
+ int idx, /* When ORDER BY term is this */
+ int isCompound, /* True if this is a compound SELECT */
+ u8 *pHasAgg /* True if expression contains aggregate functions */
){
- int nErr = 0;
- int i, j;
- ExprList *pEList;
+ int i; /* Loop counter */
+ ExprList *pEList; /* The columns of the result set */
+ NameContext nc; /* Name context for resolving pE */
- if( pSelect==0 || pOrderBy==0 ) return 1;
- if( mustComplete ){
- for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; }
+ assert( sqlite3ExprIsInteger(pE, &i)==0 );
+ pEList = pSelect->pEList;
+
+ /* If the term is a simple identifier that try to match that identifier
+ ** against a column name in the result set.
+ */
+ if( pE->op==TK_ID || (pE->op==TK_STRING && pE->token.z[0]!='\'') ){
+ sqlite3 *db = pParse->db;
+ char *zCol = sqlite3NameFromToken(db, &pE->token);
+ if( zCol==0 ){
+ return -1;
+ }
+ for(i=0; i<pEList->nExpr; i++){
+ char *zAs = pEList->a[i].zName;
+ if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ sqlite3DbFree(db, zCol);
+ return i+1;
+ }
+ }
+ sqlite3DbFree(db, zCol);
}
- if( prepSelectStmt(pParse, pSelect) ){
- return 1;
+
+ /* Resolve all names in the ORDER BY term expression
+ */
+ memset(&nc, 0, sizeof(nc));
+ nc.pParse = pParse;
+ nc.pSrcList = pSelect->pSrc;
+ nc.pEList = pEList;
+ nc.allowAgg = 1;
+ nc.nErr = 0;
+ if( sqlite3ExprResolveNames(&nc, pE) ){
+ if( isCompound ){
+ sqlite3ErrorClear(pParse);
+ return 0;
+ }else{
+ return -1;
+ }
}
- if( pSelect->pPrior ){
- if( matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0) ){
- return 1;
+ if( nc.hasAgg && pHasAgg ){
+ *pHasAgg = 1;
+ }
+
+ /* For a compound SELECT, we need to try to match the ORDER BY
+ ** expression against an expression in the result set
+ */
+ if( isCompound ){
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprCompare(pEList->a[i].pExpr, pE) ){
+ return i+1;
+ }
}
}
+ return 0;
+}
+
+
+/*
+** Analyze and ORDER BY or GROUP BY clause in a simple SELECT statement.
+** Return the number of errors seen.
+**
+** Every term of the ORDER BY or GROUP BY clause needs to be an
+** expression. If any expression is an integer constant, then
+** that expression is replaced by the corresponding
+** expression from the result set.
+*/
+static int processOrderGroupBy(
+ Parse *pParse, /* Parsing context. Leave error messages here */
+ Select *pSelect, /* The SELECT statement containing the clause */
+ ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */
+ int isOrder, /* 1 for ORDER BY. 0 for GROUP BY */
+ u8 *pHasAgg /* Set to TRUE if any term contains an aggregate */
+){
+ int i;
+ sqlite3 *db = pParse->db;
+ ExprList *pEList;
+
+ if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
+#if SQLITE_MAX_COLUMN
+ if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ const char *zType = isOrder ? "ORDER" : "GROUP";
+ sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
+ return 1;
+ }
+#endif
pEList = pSelect->pEList;
+ if( pEList==0 ){
+ return 0;
+ }
for(i=0; i<pOrderBy->nExpr; i++){
- struct ExprList_item *pItem;
+ int iCol;
Expr *pE = pOrderBy->a[i].pExpr;
- int iCol = -1;
- char *zLabel;
-
- if( pOrderBy->a[i].done ) continue;
if( sqlite3ExprIsInteger(pE, &iCol) ){
if( iCol<=0 || iCol>pEList->nExpr ){
- sqlite3ErrorMsg(pParse,
- "ORDER BY position %d should be between 1 and %d",
- iCol, pEList->nExpr);
- nErr++;
- break;
+ const char *zType = isOrder ? "ORDER" : "GROUP";
+ sqlite3ErrorMsg(pParse,
+ "%r %s BY term out of range - should be "
+ "between 1 and %d", i+1, zType, pEList->nExpr);
+ return 1;
+ }
+ }else{
+ iCol = matchOrderByTermToExprList(pParse, pSelect, pE, i+1, 0, pHasAgg);
+ if( iCol<0 ){
+ return 1;
}
- if( !mustComplete ) continue;
- iCol--;
}
- if( iCol<0 && (zLabel = sqlite3NameFromToken(&pE->token))!=0 ){
- for(j=0, pItem=pEList->a; j<pEList->nExpr; j++, pItem++){
- char *zName;
- int isMatch;
- if( pItem->zName ){
- zName = sqlite3StrDup(pItem->zName);
- }else{
- zName = sqlite3NameFromToken(&pItem->pExpr->token);
+ if( iCol>0 ){
+ CollSeq *pColl = pE->pColl;
+ int flags = pE->flags & EP_ExpCollate;
+ sqlite3ExprDelete(db, pE);
+ pE = sqlite3ExprDup(db, pEList->a[iCol-1].pExpr);
+ pOrderBy->a[i].pExpr = pE;
+ if( pE && pColl && flags ){
+ pE->pColl = pColl;
+ pE->flags |= flags;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return
+** the number of errors seen.
+**
+** If iTable>0 then make the N-th term of the ORDER BY clause refer to
+** the N-th column of table iTable.
+**
+** If iTable==0 then transform each term of the ORDER BY clause to refer
+** to a column of the result set by number.
+*/
+static int processCompoundOrderBy(
+ Parse *pParse, /* Parsing context. Leave error messages here */
+ Select *pSelect /* The SELECT statement containing the ORDER BY */
+){
+ int i;
+ ExprList *pOrderBy;
+ ExprList *pEList;
+ sqlite3 *db;
+ int moreToDo = 1;
+
+ pOrderBy = pSelect->pOrderBy;
+ if( pOrderBy==0 ) return 0;
+ db = pParse->db;
+#if SQLITE_MAX_COLUMN
+ if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
+ return 1;
+ }
+#endif
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].done = 0;
+ }
+ while( pSelect->pPrior ){
+ pSelect = pSelect->pPrior;
+ }
+ while( pSelect && moreToDo ){
+ moreToDo = 0;
+ pEList = pSelect->pEList;
+ if( pEList==0 ){
+ return 1;
+ }
+ for(i=0; i<pOrderBy->nExpr; i++){
+ int iCol = -1;
+ Expr *pE, *pDup;
+ if( pOrderBy->a[i].done ) continue;
+ pE = pOrderBy->a[i].pExpr;
+ if( sqlite3ExprIsInteger(pE, &iCol) ){
+ if( iCol<0 || iCol>pEList->nExpr ){
+ sqlite3ErrorMsg(pParse,
+ "%r ORDER BY term out of range - should be "
+ "between 1 and %d", i+1, pEList->nExpr);
+ return 1;
}
- isMatch = zName && sqlite3StrICmp(zName, zLabel)==0;
- sqliteFree(zName);
- if( isMatch ){
- iCol = j;
- break;
+ }else{
+ pDup = sqlite3ExprDup(db, pE);
+ if( !db->mallocFailed ){
+ assert(pDup);
+ iCol = matchOrderByTermToExprList(pParse, pSelect, pDup, i+1, 1, 0);
+ }
+ sqlite3ExprDelete(db, pDup);
+ if( iCol<0 ){
+ return 1;
}
}
- sqliteFree(zLabel);
- }
- if( iCol>=0 ){
- pE->op = TK_COLUMN;
- pE->iColumn = iCol;
- pE->iTable = iTable;
- pE->iAgg = -1;
- pOrderBy->a[i].done = 1;
- }else if( mustComplete ){
- sqlite3ErrorMsg(pParse,
- "ORDER BY term number %d does not match any result column", i+1);
- nErr++;
- break;
+ if( iCol>0 ){
+ pE->op = TK_INTEGER;
+ pE->flags |= EP_IntValue;
+ pE->iTable = iCol;
+ pOrderBy->a[i].done = 1;
+ }else{
+ moreToDo = 1;
+ }
+ }
+ pSelect = pSelect->pNext;
+ }
+ for(i=0; i<pOrderBy->nExpr; i++){
+ if( pOrderBy->a[i].done==0 ){
+ sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any "
+ "column in the result set", i+1);
+ return 1;
}
}
- return nErr;
+ return 0;
}
-#endif /* #ifndef SQLITE_OMIT_COMPOUND_SELECT */
/*
** Get a VDBE for the given parser context. Create a new one if necessary.
@@ -1505,6 +1740,11 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
Vdbe *v = pParse->pVdbe;
if( v==0 ){
v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db);
+#ifndef SQLITE_OMIT_TRACE
+ if( v ){
+ sqlite3VdbeAddOp0(v, OP_Trace);
+ }
+#endif
}
return v;
}
@@ -1532,7 +1772,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
Vdbe *v = 0;
int iLimit = 0;
int iOffset;
- int addr1, addr2;
+ int addr1;
+ if( p->iLimit ) return;
/*
** "LIMIT -1" always shows all rows. There is some
@@ -1541,58 +1782,35 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
** no rows.
*/
if( p->pLimit ){
- p->iLimit = iLimit = pParse->nMem;
- pParse->nMem += 2;
+ p->iLimit = iLimit = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
- sqlite3ExprCode(pParse, p->pLimit);
- sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, iLimit, 1);
- VdbeComment((v, "# LIMIT counter"));
- sqlite3VdbeAddOp(v, OP_IfMemZero, iLimit, iBreak);
- sqlite3VdbeAddOp(v, OP_MemLoad, iLimit, 0);
+ sqlite3ExprCode(pParse, p->pLimit, iLimit);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit);
+ VdbeComment((v, "LIMIT counter"));
+ sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak);
}
if( p->pOffset ){
- p->iOffset = iOffset = pParse->nMem++;
+ p->iOffset = iOffset = ++pParse->nMem;
+ if( p->pLimit ){
+ pParse->nMem++; /* Allocate an extra register for limit+offset */
+ }
v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
- sqlite3ExprCode(pParse, p->pOffset);
- sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, iOffset, p->pLimit==0);
- VdbeComment((v, "# OFFSET counter"));
- addr1 = sqlite3VdbeAddOp(v, OP_IfMemPos, iOffset, 0);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3ExprCode(pParse, p->pOffset, iOffset);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset);
+ VdbeComment((v, "OFFSET counter"));
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
sqlite3VdbeJumpHere(v, addr1);
if( p->pLimit ){
- sqlite3VdbeAddOp(v, OP_Add, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
+ VdbeComment((v, "LIMIT+OFFSET"));
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
+ sqlite3VdbeJumpHere(v, addr1);
}
}
- if( p->pLimit ){
- addr1 = sqlite3VdbeAddOp(v, OP_IfMemPos, iLimit, 0);
- sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
- sqlite3VdbeAddOp(v, OP_MemInt, -1, iLimit+1);
- addr2 = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
- sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp(v, OP_MemStore, iLimit+1, 1);
- VdbeComment((v, "# LIMIT+OFFSET"));
- sqlite3VdbeJumpHere(v, addr2);
- }
-}
-
-/*
-** Allocate a virtual index to use for sorting.
-*/
-static void createSortingIndex(Parse *pParse, Select *p, ExprList *pOrderBy){
- if( pOrderBy ){
- int addr;
- assert( pOrderBy->iECursor==0 );
- pOrderBy->iECursor = pParse->nTab++;
- addr = sqlite3VdbeAddOp(pParse->pVdbe, OP_OpenEphemeral,
- pOrderBy->iECursor, pOrderBy->nExpr+1);
- assert( p->addrOpenEphm[2] == -1 );
- p->addrOpenEphm[2] = addr;
- }
}
#ifndef SQLITE_OMIT_COMPOUND_SELECT
@@ -1618,10 +1836,19 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
+/* Forward reference */
+static int multiSelectOrderBy(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The right-most of SELECTs to be coded */
+ SelectDest *pDest /* What to do with query results */
+);
+
+
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
-** This routine is called to process a query that is really the union
-** or intersection of two or more separate queries.
+** This routine is called to process a compound query form from
+** two or more separate queries using UNION, UNION ALL, EXCEPT, or
+** INTERSECT
**
** "p" points to the right-most of the two queries. the query on the
** left is p->pPrior. The left query could also be a compound query
@@ -1652,25 +1879,20 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
static int multiSelect(
Parse *pParse, /* Parsing context */
Select *p, /* The right-most of SELECTs to be coded */
- int eDest, /* \___ Store query results as specified */
- int iParm, /* / by these two parameters. */
- char *aff /* If eDest is SRT_Union, the affinity string */
+ SelectDest *pDest /* What to do with query results */
){
int rc = SQLITE_OK; /* Success code from a subroutine */
Select *pPrior; /* Another SELECT immediately to our left */
Vdbe *v; /* Generate code to this VDBE */
- int nCol; /* Number of columns in the result set */
- ExprList *pOrderBy; /* The ORDER BY clause on p */
- int aSetP2[2]; /* Set P2 value of these op to number of columns */
- int nSetP2 = 0; /* Number of slots in aSetP2[] used */
+ SelectDest dest; /* Alternative data destination */
+ Select *pDelete = 0; /* Chain of simple selects to delete */
+ sqlite3 *db; /* Database connection */
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
*/
- if( p==0 || p->pPrior==0 ){
- rc = 1;
- goto multi_select_end;
- }
+ assert( p && p->pPrior ); /* Calling function guarantees this much */
+ db = pParse->db;
pPrior = p->pPrior;
assert( pPrior->pRightmost!=pPrior );
assert( pPrior->pRightmost==p->pRightmost );
@@ -1687,57 +1909,66 @@ static int multiSelect(
goto multi_select_end;
}
- /* Make sure we have a valid query engine. If not, create a new one.
- */
v = sqlite3GetVdbe(pParse);
- if( v==0 ){
+ assert( v!=0 ); /* The VDBE already created by calling function */
+
+ /* Create the destination temporary table if necessary
+ */
+ dest = *pDest;
+ if( dest.eDest==SRT_EphemTab ){
+ assert( p->pEList );
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
+ dest.eDest = SRT_Table;
+ }
+
+ /* 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 ){
+ sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
+ " do not have the same number of result columns", selectOpName(p->op));
rc = 1;
goto multi_select_end;
}
- /* Create the destination temporary table if necessary
+ /* Compound SELECTs that have an ORDER BY clause are handled separately.
*/
- if( eDest==SRT_EphemTab ){
- assert( p->pEList );
- assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
- aSetP2[nSetP2++] = sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, 0);
- eDest = SRT_Table;
+ if( p->pOrderBy ){
+ return multiSelectOrderBy(pParse, p, pDest);
}
/* Generate code for the left and right SELECT statements.
*/
- pOrderBy = p->pOrderBy;
switch( p->op ){
case TK_ALL: {
- if( pOrderBy==0 ){
- int addr = 0;
- assert( !pPrior->pLimit );
- pPrior->pLimit = p->pLimit;
- pPrior->pOffset = p->pOffset;
- rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff);
- p->pLimit = 0;
- p->pOffset = 0;
- if( rc ){
- goto multi_select_end;
- }
- p->pPrior = 0;
- p->iLimit = pPrior->iLimit;
- p->iOffset = pPrior->iOffset;
- if( p->iLimit>=0 ){
- addr = sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, 0);
- VdbeComment((v, "# Jump ahead if LIMIT reached"));
- }
- rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff);
- p->pPrior = pPrior;
- if( rc ){
- goto multi_select_end;
- }
- if( addr ){
- sqlite3VdbeJumpHere(v, addr);
- }
- break;
+ int addr = 0;
+ assert( !pPrior->pLimit );
+ pPrior->pLimit = p->pLimit;
+ pPrior->pOffset = p->pOffset;
+ rc = sqlite3Select(pParse, pPrior, &dest, 0, 0, 0);
+ p->pLimit = 0;
+ p->pOffset = 0;
+ if( rc ){
+ goto multi_select_end;
+ }
+ p->pPrior = 0;
+ p->iLimit = pPrior->iLimit;
+ p->iOffset = pPrior->iOffset;
+ if( p->iLimit ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
+ VdbeComment((v, "Jump ahead if LIMIT reached"));
}
- /* For UNION ALL ... ORDER BY fall through to the next case */
+ rc = sqlite3Select(pParse, p, &dest, 0, 0, 0);
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ if( rc ){
+ goto multi_select_end;
+ }
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ }
+ break;
}
case TK_EXCEPT:
case TK_UNION: {
@@ -1746,68 +1977,63 @@ static int multiSelect(
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
int addr;
+ SelectDest uniondest;
- priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
- if( eDest==priorOp && pOrderBy==0 && !p->pLimit && !p->pOffset ){
+ priorOp = SRT_Union;
+ if( dest.eDest==priorOp && !p->pLimit && !p->pOffset ){
/* We can reuse a temporary table generated by a SELECT to our
** right.
*/
- unionTab = iParm;
+ unionTab = dest.iParm;
}else{
/* We will need to create our own temporary table to hold the
** intermediate results.
*/
unionTab = pParse->nTab++;
- if( pOrderBy && matchOrderbyToColumn(pParse, p, pOrderBy, unionTab,1) ){
- rc = 1;
- goto multi_select_end;
- }
- addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, unionTab, 0);
- if( priorOp==SRT_Table ){
- assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
- aSetP2[nSetP2++] = addr;
- }else{
- assert( p->addrOpenEphm[0] == -1 );
- p->addrOpenEphm[0] = addr;
- p->pRightmost->usesEphm = 1;
- }
- createSortingIndex(pParse, p, pOrderBy);
+ assert( p->pOrderBy==0 );
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
+ assert( p->addrOpenEphm[0] == -1 );
+ p->addrOpenEphm[0] = addr;
+ p->pRightmost->usesEphm = 1;
assert( p->pEList );
}
/* Code the SELECT statements to our left
*/
assert( !pPrior->pOrderBy );
- rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff);
+ sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
+ rc = sqlite3Select(pParse, pPrior, &uniondest, 0, 0, 0);
if( rc ){
goto multi_select_end;
}
/* Code the current SELECT statement
*/
- switch( p->op ){
- case TK_EXCEPT: op = SRT_Except; break;
- case TK_UNION: op = SRT_Union; break;
- case TK_ALL: op = SRT_Table; break;
+ if( p->op==TK_EXCEPT ){
+ op = SRT_Except;
+ }else{
+ assert( p->op==TK_UNION );
+ op = SRT_Union;
}
p->pPrior = 0;
- p->pOrderBy = 0;
- p->disallowOrderBy = pOrderBy!=0;
+ p->disallowOrderBy = 0;
pLimit = p->pLimit;
p->pLimit = 0;
pOffset = p->pOffset;
p->pOffset = 0;
- rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff);
+ uniondest.eDest = op;
+ rc = sqlite3Select(pParse, p, &uniondest, 0, 0, 0);
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
- sqlite3ExprListDelete(p->pOrderBy);
+ sqlite3ExprListDelete(db, p->pOrderBy);
+ pDelete = p->pPrior;
p->pPrior = pPrior;
- p->pOrderBy = pOrderBy;
- sqlite3ExprDelete(p->pLimit);
+ p->pOrderBy = 0;
+ sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
- p->iLimit = -1;
- p->iOffset = -1;
+ p->iLimit = 0;
+ p->iOffset = 0;
if( rc ){
goto multi_select_end;
}
@@ -1816,10 +2042,10 @@ static int multiSelect(
/* Convert the data in the temporary table into whatever form
** it is that we currently need.
*/
- if( eDest!=priorOp || unionTab!=iParm ){
+ if( dest.eDest!=priorOp || unionTab!=dest.iParm ){
int iCont, iBreak, iStart;
assert( p->pEList );
- if( eDest==SRT_Callback ){
+ if( dest.eDest==SRT_Callback ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
generateColumnNames(pParse, 0, pFirst->pEList);
@@ -1827,19 +2053,14 @@ static int multiSelect(
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp(v, OP_Rewind, unionTab, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak);
iStart = sqlite3VdbeCurrentAddr(v);
- rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
- pOrderBy, -1, eDest, iParm,
- iCont, iBreak, 0);
- if( rc ){
- rc = 1;
- goto multi_select_end;
- }
+ selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
+ 0, -1, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp(v, OP_Next, unionTab, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart);
sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp(v, OP_Close, unionTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
}
break;
}
@@ -1848,6 +2069,8 @@ static int multiSelect(
int iCont, iBreak, iStart;
Expr *pLimit, *pOffset;
int addr;
+ SelectDest intersectdest;
+ int r1;
/* INTERSECT is different from the others since it requires
** two temporary tables. Hence it has its own case. Begin
@@ -1855,13 +2078,9 @@ static int multiSelect(
*/
tab1 = pParse->nTab++;
tab2 = pParse->nTab++;
- if( pOrderBy && matchOrderbyToColumn(pParse,p,pOrderBy,tab1,1) ){
- rc = 1;
- goto multi_select_end;
- }
- createSortingIndex(pParse, p, pOrderBy);
+ assert( p->pOrderBy==0 );
- addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, tab1, 0);
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
p->pRightmost->usesEphm = 1;
@@ -1869,14 +2088,15 @@ static int multiSelect(
/* Code the SELECTs to our left into temporary table "tab1".
*/
- rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff);
+ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
+ rc = sqlite3Select(pParse, pPrior, &intersectdest, 0, 0, 0);
if( rc ){
goto multi_select_end;
}
/* Code the current SELECT into temporary table "tab2"
*/
- addr = sqlite3VdbeAddOp(v, OP_OpenEphemeral, tab2, 0);
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
assert( p->addrOpenEphm[1] == -1 );
p->addrOpenEphm[1] = addr;
p->pPrior = 0;
@@ -1884,9 +2104,11 @@ static int multiSelect(
p->pLimit = 0;
pOffset = p->pOffset;
p->pOffset = 0;
- rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff);
+ intersectdest.iParm = tab2;
+ rc = sqlite3Select(pParse, p, &intersectdest, 0, 0, 0);
+ pDelete = p->pPrior;
p->pPrior = pPrior;
- sqlite3ExprDelete(p->pLimit);
+ sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
if( rc ){
@@ -1897,7 +2119,7 @@ static int multiSelect(
** tables.
*/
assert( p->pEList );
- if( eDest==SRT_Callback ){
+ if( dest.eDest==SRT_Callback ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
generateColumnNames(pParse, 0, pFirst->pEList);
@@ -1905,76 +2127,54 @@ static int multiSelect(
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp(v, OP_Rewind, tab1, iBreak);
- iStart = sqlite3VdbeAddOp(v, OP_RowKey, tab1, 0);
- sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont);
- rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
- pOrderBy, -1, eDest, iParm,
- iCont, iBreak, 0);
- if( rc ){
- rc = 1;
- goto multi_select_end;
- }
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak);
+ r1 = sqlite3GetTempReg(pParse);
+ iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1);
+ sqlite3VdbeAddOp3(v, OP_NotFound, tab2, iCont, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
+ selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
+ 0, -1, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp(v, OP_Next, tab1, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart);
sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp(v, OP_Close, tab2, 0);
- sqlite3VdbeAddOp(v, OP_Close, tab1, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
break;
}
}
- /* 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 ){
- sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
- " do not have the same number of result columns", selectOpName(p->op));
- rc = 1;
- goto multi_select_end;
- }
-
- /* Set the number of columns in temporary tables
- */
- nCol = p->pEList->nExpr;
- while( nSetP2 ){
- sqlite3VdbeChangeP2(v, aSetP2[--nSetP2], nCol);
- }
-
- /* Compute collating sequences used by either the ORDER BY clause or
- ** by any temporary tables needed to implement the compound select.
- ** Attach the KeyInfo structure to all temporary tables. Invoke the
- ** ORDER BY processing if there is an ORDER BY clause.
+ /* Compute collating sequences used by
+ ** temporary tables needed to implement the compound select.
+ ** Attach the KeyInfo structure to all temporary tables.
**
** This section is run by the right-most SELECT statement only.
** SELECT statements to the left always skip this part. The right-most
** SELECT might also skip this part if it has no ORDER BY clause and
** no temp tables are required.
*/
- if( pOrderBy || p->usesEphm ){
+ if( p->usesEphm ){
int i; /* Loop counter */
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
Select *pLoop; /* For looping through SELECT statements */
- int nKeyCol; /* Number of entries in pKeyInfo->aCol[] */
CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */
- CollSeq **aCopy; /* A copy of pKeyInfo->aColl[] */
+ int nCol; /* Number of columns in result set */
assert( p->pRightmost==p );
- nKeyCol = nCol + (pOrderBy ? pOrderBy->nExpr : 0);
- pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nKeyCol*(sizeof(CollSeq*) + 1));
+ nCol = p->pEList->nExpr;
+ pKeyInfo = sqlite3DbMallocZero(db,
+ sizeof(*pKeyInfo)+nCol*(sizeof(CollSeq*) + 1));
if( !pKeyInfo ){
rc = SQLITE_NOMEM;
goto multi_select_end;
}
- pKeyInfo->enc = ENC(pParse->db);
+ pKeyInfo->enc = ENC(db);
pKeyInfo->nField = nCol;
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
*apColl = multiSelectCollSeq(pParse, p, i);
if( 0==*apColl ){
- *apColl = pParse->db->pDfltColl;
+ *apColl = db->pDfltColl;
}
}
@@ -1988,64 +2188,619 @@ static int multiSelect(
break;
}
sqlite3VdbeChangeP2(v, addr, nCol);
- sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO);
+ sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO);
pLoop->addrOpenEphm[i] = -1;
}
}
+ sqlite3DbFree(db, pKeyInfo);
+ }
- if( pOrderBy ){
- struct ExprList_item *pOTerm = pOrderBy->a;
- int nOrderByExpr = pOrderBy->nExpr;
- int addr;
- u8 *pSortOrder;
-
- /* Reuse the same pKeyInfo for the ORDER BY as was used above for
- ** the compound select statements. Except we have to change out the
- ** pKeyInfo->aColl[] values. Some of the aColl[] values will be
- ** reused when constructing the pKeyInfo for the ORDER BY, so make
- ** a copy. Sufficient space to hold both the nCol entries for
- ** the compound select and the nOrderbyExpr entries for the ORDER BY
- ** was allocated above. But we need to move the compound select
- ** entries out of the way before constructing the ORDER BY entries.
- ** Move the compound select entries into aCopy[] where they can be
- ** accessed and reused when constructing the ORDER BY entries.
- ** Because nCol might be greater than or less than nOrderByExpr
- ** we have to use memmove() when doing the copy.
- */
- aCopy = &pKeyInfo->aColl[nOrderByExpr];
- pSortOrder = pKeyInfo->aSortOrder = (u8*)&aCopy[nCol];
- memmove(aCopy, pKeyInfo->aColl, nCol*sizeof(CollSeq*));
-
- apColl = pKeyInfo->aColl;
- for(i=0; i<nOrderByExpr; i++, pOTerm++, apColl++, pSortOrder++){
- Expr *pExpr = pOTerm->pExpr;
- if( (pExpr->flags & EP_ExpCollate) ){
- assert( pExpr->pColl!=0 );
- *apColl = pExpr->pColl;
+multi_select_end:
+ pDest->iMem = dest.iMem;
+ pDest->nMem = dest.nMem;
+ sqlite3SelectDelete(db, pDelete);
+ return rc;
+}
+#endif /* SQLITE_OMIT_COMPOUND_SELECT */
+
+/*
+** Code an output subroutine for a coroutine implementation of a
+** SELECT statment.
+**
+** The data to be output is contained in pIn->iMem. There are
+** pIn->nMem columns to be output. pDest is where the output should
+** be sent.
+**
+** regReturn is the number of the register holding the subroutine
+** return address.
+**
+** If regPrev>0 then it is a the first register in a vector that
+** records the previous output. mem[regPrev] is a flag that is false
+** if there has been no previous output. If regPrev>0 then code is
+** generated to suppress duplicates. pKeyInfo is used for comparing
+** keys.
+**
+** If the LIMIT found in p->iLimit is reached, jump immediately to
+** iBreak.
+*/
+static int generateOutputSubroutine(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The SELECT statement */
+ SelectDest *pIn, /* Coroutine supplying data */
+ SelectDest *pDest, /* Where to send the data */
+ int regReturn, /* The return address register */
+ int regPrev, /* Previous result register. No uniqueness if 0 */
+ KeyInfo *pKeyInfo, /* For comparing with previous entry */
+ int p4type, /* The p4 type for pKeyInfo */
+ int iBreak /* Jump here if we hit the LIMIT */
+){
+ Vdbe *v = pParse->pVdbe;
+ int iContinue;
+ int addr;
+
+ addr = sqlite3VdbeCurrentAddr(v);
+ iContinue = sqlite3VdbeMakeLabel(v);
+
+ /* Suppress duplicates for UNION, EXCEPT, and INTERSECT
+ */
+ if( regPrev ){
+ int j1, j2;
+ j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev);
+ j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iMem, regPrev+1, pIn->nMem,
+ (char*)pKeyInfo, p4type);
+ sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3ExprCodeCopy(pParse, pIn->iMem, regPrev+1, pIn->nMem);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
+ }
+ if( pParse->db->mallocFailed ) return 0;
+
+ /* Suppress the the first OFFSET entries if there is an OFFSET clause
+ */
+ codeOffset(v, p, iContinue);
+
+ switch( pDest->eDest ){
+ /* Store the result as data using a unique key.
+ */
+ case SRT_Table:
+ case SRT_EphemTab: {
+ int r1 = sqlite3GetTempReg(pParse);
+ int r2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iMem, pIn->nMem, r1);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iParm, r2);
+ sqlite3VdbeAddOp3(v, OP_Insert, pDest->iParm, r1, r2);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+ sqlite3ReleaseTempReg(pParse, r2);
+ sqlite3ReleaseTempReg(pParse, r1);
+ break;
+ }
+
+#ifndef SQLITE_OMIT_SUBQUERY
+ /* If we are creating a set for an "expr IN (SELECT ...)" construct,
+ ** then there should be a single item on the stack. Write this
+ ** item into the set table with bogus data.
+ */
+ case SRT_Set: {
+ int r1;
+ assert( pIn->nMem==1 );
+ p->affinity =
+ sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affinity);
+ r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iMem, 1, r1, &p->affinity, 1);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iParm, r1);
+ sqlite3ReleaseTempReg(pParse, r1);
+ break;
+ }
+
+#if 0 /* Never occurs on an ORDER BY query */
+ /* If any row exist in the result set, record that fact and abort.
+ */
+ case SRT_Exists: {
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iParm);
+ /* The LIMIT clause will terminate the loop for us */
+ break;
+ }
+#endif
+
+ /* If this is a scalar select that is part of an expression, then
+ ** store the results in the appropriate memory cell and break out
+ ** of the scan loop.
+ */
+ case SRT_Mem: {
+ assert( pIn->nMem==1 );
+ sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iParm, 1);
+ /* The LIMIT clause will jump out of the loop for us */
+ break;
+ }
+#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
+
+ /* Send the data to the callback function or to a subroutine. In the
+ ** case of a subroutine, the subroutine itself is responsible for
+ ** popping the data from the stack.
+ */
+ case SRT_Coroutine: {
+ if( pDest->iMem==0 ){
+ pDest->iMem = sqlite3GetTempRange(pParse, pIn->nMem);
+ pDest->nMem = pIn->nMem;
+ }
+ sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iMem, pDest->nMem);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ break;
+ }
+
+ case SRT_Callback: {
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iMem, pIn->nMem);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, pIn->nMem);
+ break;
+ }
+
+#if !defined(SQLITE_OMIT_TRIGGER)
+ /* Discard the results. This is used for SELECT statements inside
+ ** the body of a TRIGGER. The purpose of such selects is to call
+ ** user-defined functions that have side effects. We do not care
+ ** about the actual results of the select.
+ */
+ default: {
+ break;
+ }
+#endif
+ }
+
+ /* Jump to the end of the loop if the LIMIT is reached.
+ */
+ if( p->iLimit ){
+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
+ sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak);
+ }
+
+ /* Generate the subroutine return
+ */
+ sqlite3VdbeResolveLabel(v, iContinue);
+ sqlite3VdbeAddOp1(v, OP_Return, regReturn);
+
+ return addr;
+}
+
+/*
+** Alternative compound select code generator for cases when there
+** is an ORDER BY clause.
+**
+** We assume a query of the following form:
+**
+** <selectA> <operator> <selectB> ORDER BY <orderbylist>
+**
+** <operator> is one of UNION ALL, UNION, EXCEPT, or INTERSECT. The idea
+** is to code both <selectA> and <selectB> with the ORDER BY clause as
+** co-routines. Then run the co-routines in parallel and merge the results
+** into the output. In addition to the two coroutines (called selectA and
+** selectB) there are 7 subroutines:
+**
+** outA: Move the output of the selectA coroutine into the output
+** of the compound query.
+**
+** outB: Move the output of the selectB coroutine into the output
+** of the compound query. (Only generated for UNION and
+** UNION ALL. EXCEPT and INSERTSECT never output a row that
+** appears only in B.)
+**
+** AltB: Called when there is data from both coroutines and A<B.
+**
+** AeqB: Called when there is data from both coroutines and A==B.
+**
+** AgtB: Called when there is data from both coroutines and A>B.
+**
+** EofA: Called when data is exhausted from selectA.
+**
+** EofB: Called when data is exhausted from selectB.
+**
+** The implementation of the latter five subroutines depend on which
+** <operator> is used:
+**
+**
+** UNION ALL UNION EXCEPT INTERSECT
+** ------------- ----------------- -------------- -----------------
+** AltB: outA, nextA outA, nextA outA, nextA nextA
+**
+** AeqB: outA, nextA nextA nextA outA, nextA
+**
+** AgtB: outB, nextB outB, nextB nextB nextB
+**
+** EofA: outB, nextB outB, nextB halt halt
+**
+** EofB: outA, nextA outA, nextA outA, nextA halt
+**
+** In the AltB, AeqB, and AgtB subroutines, an EOF on A following nextA
+** causes an immediate jump to EofA and an EOF on B following nextB causes
+** an immediate jump to EofB. Within EofA and EofB, and EOF on entry or
+** following nextX causes a jump to the end of the select processing.
+**
+** Duplicate removal in the UNION, EXCEPT, and INTERSECT cases is handled
+** within the output subroutine. The regPrev register set holds the previously
+** output value. A comparison is made against this value and the output
+** is skipped if the next results would be the same as the previous.
+**
+** The implementation plan is to implement the two coroutines and seven
+** subroutines first, then put the control logic at the bottom. Like this:
+**
+** goto Init
+** coA: coroutine for left query (A)
+** coB: coroutine for right query (B)
+** outA: output one row of A
+** outB: output one row of B (UNION and UNION ALL only)
+** EofA: ...
+** EofB: ...
+** AltB: ...
+** AeqB: ...
+** AgtB: ...
+** Init: initialize coroutine registers
+** yield coA
+** if eof(A) goto EofA
+** yield coB
+** if eof(B) goto EofB
+** Cmpr: Compare A, B
+** Jump AltB, AeqB, AgtB
+** End: ...
+**
+** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not
+** actually called using Gosub and they do not Return. EofA and EofB loop
+** until all data is exhausted then jump to the "end" labe. AltB, AeqB,
+** and AgtB jump to either L2 or to one of EofA or EofB.
+*/
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
+static int multiSelectOrderBy(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The right-most of SELECTs to be coded */
+ SelectDest *pDest /* What to do with query results */
+){
+ int i, j; /* Loop counters */
+ Select *pPrior; /* Another SELECT immediately to our left */
+ Vdbe *v; /* Generate code to this VDBE */
+ SelectDest destA; /* Destination for coroutine A */
+ SelectDest destB; /* Destination for coroutine B */
+ int regAddrA; /* Address register for select-A coroutine */
+ int regEofA; /* Flag to indicate when select-A is complete */
+ int regAddrB; /* Address register for select-B coroutine */
+ int regEofB; /* Flag to indicate when select-B is complete */
+ int addrSelectA; /* Address of the select-A coroutine */
+ int addrSelectB; /* Address of the select-B coroutine */
+ int regOutA; /* Address register for the output-A subroutine */
+ int regOutB; /* Address register for the output-B subroutine */
+ int addrOutA; /* Address of the output-A subroutine */
+ int addrOutB; /* Address of the output-B subroutine */
+ int addrEofA; /* Address of the select-A-exhausted subroutine */
+ int addrEofB; /* Address of the select-B-exhausted subroutine */
+ int addrAltB; /* Address of the A<B subroutine */
+ int addrAeqB; /* Address of the A==B subroutine */
+ int addrAgtB; /* Address of the A>B subroutine */
+ int regLimitA; /* Limit register for select-A */
+ int regLimitB; /* Limit register for select-A */
+ int regPrev; /* A range of registers to hold previous output */
+ int savedLimit; /* Saved value of p->iLimit */
+ int savedOffset; /* Saved value of p->iOffset */
+ int labelCmpr; /* Label for the start of the merge algorithm */
+ int labelEnd; /* Label for the end of the overall SELECT stmt */
+ int j1; /* Jump instructions that get retargetted */
+ int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */
+ KeyInfo *pKeyDup; /* Comparison information for duplicate removal */
+ KeyInfo *pKeyMerge; /* Comparison information for merging rows */
+ sqlite3 *db; /* Database connection */
+ ExprList *pOrderBy; /* The ORDER BY clause */
+ int nOrderBy; /* Number of terms in the ORDER BY clause */
+ int *aPermute; /* Mapping from ORDER BY terms to result set columns */
+ u8 NotUsed; /* Dummy variables */
+
+ assert( p->pOrderBy!=0 );
+ db = pParse->db;
+ v = pParse->pVdbe;
+ if( v==0 ) return SQLITE_NOMEM;
+ labelEnd = sqlite3VdbeMakeLabel(v);
+ labelCmpr = sqlite3VdbeMakeLabel(v);
+
+
+ /* Patch up the ORDER BY clause
+ */
+ op = p->op;
+ pPrior = p->pPrior;
+ assert( pPrior->pOrderBy==0 );
+ pOrderBy = p->pOrderBy;
+ assert( pOrderBy );
+ if( processCompoundOrderBy(pParse, p) ){
+ return SQLITE_ERROR;
+ }
+ nOrderBy = pOrderBy->nExpr;
+
+ /* For operators other than UNION ALL we have to make sure that
+ ** the ORDER BY clause covers every term of the result set. Add
+ ** terms to the ORDER BY clause as necessary.
+ */
+ if( op!=TK_ALL ){
+ for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
+ for(j=0; j<nOrderBy; j++){
+ Expr *pTerm = pOrderBy->a[j].pExpr;
+ assert( pTerm->op==TK_INTEGER );
+ assert( (pTerm->flags & EP_IntValue)!=0 );
+ if( pTerm->iTable==i ) break;
+ }
+ if( j==nOrderBy ){
+ Expr *pNew = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, 0);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ pNew->flags |= EP_IntValue;
+ pNew->iTable = i;
+ pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew, 0);
+ nOrderBy++;
+ }
+ }
+ }
+
+ /* Compute the comparison permutation and keyinfo that is used with
+ ** the permutation in order to comparisons to determine if the next
+ ** row of results comes from selectA or selectB. Also add explicit
+ ** collations to the ORDER BY clause terms so that when the subqueries
+ ** to the right and the left are evaluated, they use the correct
+ ** collation.
+ */
+ aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
+ if( aPermute ){
+ for(i=0; i<nOrderBy; i++){
+ Expr *pTerm = pOrderBy->a[i].pExpr;
+ assert( pTerm->op==TK_INTEGER );
+ assert( (pTerm->flags & EP_IntValue)!=0 );
+ aPermute[i] = pTerm->iTable-1;
+ assert( aPermute[i]>=0 && aPermute[i]<p->pEList->nExpr );
+ }
+ pKeyMerge =
+ sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
+ if( pKeyMerge ){
+ pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy];
+ pKeyMerge->nField = nOrderBy;
+ pKeyMerge->enc = ENC(db);
+ for(i=0; i<nOrderBy; i++){
+ CollSeq *pColl;
+ Expr *pTerm = pOrderBy->a[i].pExpr;
+ if( pTerm->flags & EP_ExpCollate ){
+ pColl = pTerm->pColl;
}else{
- *apColl = aCopy[pExpr->iColumn];
+ pColl = multiSelectCollSeq(pParse, p, aPermute[i]);
+ pTerm->flags |= EP_ExpCollate;
+ pTerm->pColl = pColl;
}
- *pSortOrder = pOTerm->sortOrder;
+ pKeyMerge->aColl[i] = pColl;
+ pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder;
}
- assert( p->pRightmost==p );
- assert( p->addrOpenEphm[2]>=0 );
- addr = p->addrOpenEphm[2];
- sqlite3VdbeChangeP2(v, addr, p->pOrderBy->nExpr+2);
- pKeyInfo->nField = nOrderByExpr;
- sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
- pKeyInfo = 0;
- generateSortTail(pParse, p, v, p->pEList->nExpr, eDest, iParm);
}
+ }else{
+ pKeyMerge = 0;
+ }
+
+ /* Reattach the ORDER BY clause to the query.
+ */
+ p->pOrderBy = pOrderBy;
+ pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy);
- sqliteFree(pKeyInfo);
+ /* Allocate a range of temporary registers and the KeyInfo needed
+ ** for the logic that removes duplicate result rows when the
+ ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL).
+ */
+ if( op==TK_ALL ){
+ regPrev = 0;
+ }else{
+ int nExpr = p->pEList->nExpr;
+ assert( nOrderBy>=nExpr );
+ regPrev = sqlite3GetTempRange(pParse, nExpr+1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev);
+ pKeyDup = sqlite3DbMallocZero(db,
+ sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) );
+ if( pKeyDup ){
+ pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr];
+ pKeyDup->nField = nExpr;
+ pKeyDup->enc = ENC(db);
+ for(i=0; i<nExpr; i++){
+ pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
+ pKeyDup->aSortOrder[i] = 0;
+ }
+ }
+ }
+
+ /* Separate the left and the right query from one another
+ */
+ p->pPrior = 0;
+ pPrior->pRightmost = 0;
+ processOrderGroupBy(pParse, p, p->pOrderBy, 1, &NotUsed);
+ if( pPrior->pPrior==0 ){
+ processOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, 1, &NotUsed);
}
-multi_select_end:
- return rc;
+ /* Compute the limit registers */
+ computeLimitRegisters(pParse, p, labelEnd);
+ if( p->iLimit && op==TK_ALL ){
+ regLimitA = ++pParse->nMem;
+ regLimitB = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Copy, p->iOffset ? p->iOffset+1 : p->iLimit,
+ regLimitA);
+ sqlite3VdbeAddOp2(v, OP_Copy, regLimitA, regLimitB);
+ }else{
+ regLimitA = regLimitB = 0;
+ }
+ sqlite3ExprDelete(db, p->pLimit);
+ p->pLimit = 0;
+ sqlite3ExprDelete(db, p->pOffset);
+ p->pOffset = 0;
+
+ regAddrA = ++pParse->nMem;
+ regEofA = ++pParse->nMem;
+ regAddrB = ++pParse->nMem;
+ regEofB = ++pParse->nMem;
+ regOutA = ++pParse->nMem;
+ regOutB = ++pParse->nMem;
+ sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
+ sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+
+ /* Jump past the various subroutines and coroutines to the main
+ ** merge loop
+ */
+ j1 = sqlite3VdbeAddOp0(v, OP_Goto);
+ addrSelectA = sqlite3VdbeCurrentAddr(v);
+
+
+ /* Generate a coroutine to evaluate the SELECT statement to the
+ ** left of the compound operator - the "A" select.
+ */
+ VdbeNoopComment((v, "Begin coroutine for left SELECT"));
+ pPrior->iLimit = regLimitA;
+ sqlite3Select(pParse, pPrior, &destA, 0, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
+ VdbeNoopComment((v, "End coroutine for left SELECT"));
+
+ /* Generate a coroutine to evaluate the SELECT statement on
+ ** the right - the "B" select
+ */
+ addrSelectB = sqlite3VdbeCurrentAddr(v);
+ VdbeNoopComment((v, "Begin coroutine for right SELECT"));
+ savedLimit = p->iLimit;
+ savedOffset = p->iOffset;
+ p->iLimit = regLimitB;
+ p->iOffset = 0;
+ sqlite3Select(pParse, p, &destB, 0, 0, 0);
+ p->iLimit = savedLimit;
+ p->iOffset = savedOffset;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB);
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
+ VdbeNoopComment((v, "End coroutine for right SELECT"));
+
+ /* Generate a subroutine that outputs the current row of the A
+ ** select as the next output row of the compound select.
+ */
+ VdbeNoopComment((v, "Output routine for A"));
+ addrOutA = generateOutputSubroutine(pParse,
+ p, &destA, pDest, regOutA,
+ regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd);
+
+ /* Generate a subroutine that outputs the current row of the B
+ ** select as the next output row of the compound select.
+ */
+ if( op==TK_ALL || op==TK_UNION ){
+ VdbeNoopComment((v, "Output routine for B"));
+ addrOutB = generateOutputSubroutine(pParse,
+ p, &destB, pDest, regOutB,
+ regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd);
+ }
+
+ /* Generate a subroutine to run when the results from select A
+ ** are exhausted and only data in select B remains.
+ */
+ VdbeNoopComment((v, "eof-A subroutine"));
+ if( op==TK_EXCEPT || op==TK_INTERSECT ){
+ addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd);
+ }else{
+ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
+ }
+
+ /* Generate a subroutine to run when the results from select B
+ ** are exhausted and only data in select A remains.
+ */
+ if( op==TK_INTERSECT ){
+ addrEofB = addrEofA;
+ }else{
+ VdbeNoopComment((v, "eof-B subroutine"));
+ addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB);
+ }
+
+ /* Generate code to handle the case of A<B
+ */
+ VdbeNoopComment((v, "A-lt-B subroutine"));
+ addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
+ sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+
+ /* Generate code to handle the case of A==B
+ */
+ if( op==TK_ALL ){
+ addrAeqB = addrAltB;
+ }else if( op==TK_INTERSECT ){
+ addrAeqB = addrAltB;
+ addrAltB++;
+ }else{
+ VdbeNoopComment((v, "A-eq-B subroutine"));
+ addrAeqB =
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
+ sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ }
+
+ /* Generate code to handle the case of A>B
+ */
+ VdbeNoopComment((v, "A-gt-B subroutine"));
+ addrAgtB = sqlite3VdbeCurrentAddr(v);
+ if( op==TK_ALL || op==TK_UNION ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+ }
+ sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
+ sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+
+ /* This code runs once to initialize everything.
+ */
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofA);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofB);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regAddrA, addrSelectA);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regAddrB, addrSelectB);
+ sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
+ sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB);
+
+ /* Implement the main merge loop
+ */
+ sqlite3VdbeResolveLabel(v, labelCmpr);
+ sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
+ sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, nOrderBy,
+ (char*)pKeyMerge, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
+
+ /* Release temporary registers
+ */
+ if( regPrev ){
+ sqlite3ReleaseTempRange(pParse, regPrev, nOrderBy+1);
+ }
+
+ /* Jump to the this point in order to terminate the query.
+ */
+ sqlite3VdbeResolveLabel(v, labelEnd);
+
+ /* Set the number of output columns
+ */
+ if( pDest->eDest==SRT_Callback ){
+ Select *pFirst = pPrior;
+ while( pFirst->pPrior ) pFirst = pFirst->pPrior;
+ generateColumnNames(pParse, 0, pFirst->pEList);
+ }
+
+ /* Reassembly the compound query so that it will be freed correctly
+ ** by the calling function */
+ if( p->pPrior ){
+ sqlite3SelectDelete(db, p->pPrior);
+ }
+ p->pPrior = pPrior;
+
+ /*** TBD: Insert subroutine calls to close cursors on incomplete
+ **** subqueries ****/
+ return SQLITE_OK;
}
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
+#endif
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/* Forward Declarations */
+static void substExprList(sqlite3*, ExprList*, int, ExprList*);
+static void substSelect(sqlite3*, Select *, int, ExprList *);
-#ifndef SQLITE_OMIT_VIEW
/*
** Scan through the expression pExpr. Replace every reference to
** a column in table number iTable with a copy of the iColumn-th
@@ -2059,9 +2814,12 @@ multi_select_end:
** changes to pExpr so that it refers directly to the source table
** of the subquery rather the result set of the subquery.
*/
-static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */
-static void substSelect(Select *, int, ExprList *); /* Forward Decl */
-static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){
+static void substExpr(
+ sqlite3 *db, /* Report malloc errors to this connection */
+ Expr *pExpr, /* Expr in which substitution occurs */
+ int iTable, /* Table to be substituted */
+ ExprList *pEList /* Substitute expressions */
+){
if( pExpr==0 ) return;
if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){
if( pExpr->iColumn<0 ){
@@ -2074,46 +2832,56 @@ static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){
assert( pNew!=0 );
pExpr->op = pNew->op;
assert( pExpr->pLeft==0 );
- pExpr->pLeft = sqlite3ExprDup(pNew->pLeft);
+ pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft);
assert( pExpr->pRight==0 );
- pExpr->pRight = sqlite3ExprDup(pNew->pRight);
+ pExpr->pRight = sqlite3ExprDup(db, pNew->pRight);
assert( pExpr->pList==0 );
- pExpr->pList = sqlite3ExprListDup(pNew->pList);
+ pExpr->pList = sqlite3ExprListDup(db, pNew->pList);
pExpr->iTable = pNew->iTable;
pExpr->pTab = pNew->pTab;
pExpr->iColumn = pNew->iColumn;
pExpr->iAgg = pNew->iAgg;
- sqlite3TokenCopy(&pExpr->token, &pNew->token);
- sqlite3TokenCopy(&pExpr->span, &pNew->span);
- pExpr->pSelect = sqlite3SelectDup(pNew->pSelect);
+ sqlite3TokenCopy(db, &pExpr->token, &pNew->token);
+ sqlite3TokenCopy(db, &pExpr->span, &pNew->span);
+ pExpr->pSelect = sqlite3SelectDup(db, pNew->pSelect);
pExpr->flags = pNew->flags;
}
}else{
- substExpr(pExpr->pLeft, iTable, pEList);
- substExpr(pExpr->pRight, iTable, pEList);
- substSelect(pExpr->pSelect, iTable, pEList);
- substExprList(pExpr->pList, iTable, pEList);
+ substExpr(db, pExpr->pLeft, iTable, pEList);
+ substExpr(db, pExpr->pRight, iTable, pEList);
+ substSelect(db, pExpr->pSelect, iTable, pEList);
+ substExprList(db, pExpr->pList, iTable, pEList);
}
}
-static void substExprList(ExprList *pList, int iTable, ExprList *pEList){
+static void substExprList(
+ sqlite3 *db, /* Report malloc errors here */
+ ExprList *pList, /* List to scan and in which to make substitutes */
+ int iTable, /* Table to be substituted */
+ ExprList *pEList /* Substitute values */
+){
int i;
if( pList==0 ) return;
for(i=0; i<pList->nExpr; i++){
- substExpr(pList->a[i].pExpr, iTable, pEList);
+ substExpr(db, pList->a[i].pExpr, iTable, pEList);
}
}
-static void substSelect(Select *p, int iTable, ExprList *pEList){
+static void substSelect(
+ sqlite3 *db, /* Report malloc errors here */
+ Select *p, /* SELECT statement in which to make substitutions */
+ int iTable, /* Table to be replaced */
+ ExprList *pEList /* Substitute values */
+){
if( !p ) return;
- substExprList(p->pEList, iTable, pEList);
- substExprList(p->pGroupBy, iTable, pEList);
- substExprList(p->pOrderBy, iTable, pEList);
- substExpr(p->pHaving, iTable, pEList);
- substExpr(p->pWhere, iTable, pEList);
- substSelect(p->pPrior, iTable, pEList);
+ substExprList(db, p->pEList, iTable, pEList);
+ substExprList(db, p->pGroupBy, iTable, pEList);
+ substExprList(db, p->pOrderBy, iTable, pEList);
+ substExpr(db, p->pHaving, iTable, pEList);
+ substExpr(db, p->pWhere, iTable, pEList);
+ substSelect(db, p->pPrior, iTable, pEList);
}
-#endif /* !defined(SQLITE_OMIT_VIEW) */
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
-#ifndef SQLITE_OMIT_VIEW
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries in order to speed
** execution. It returns 1 if it makes changes and 0 if no flattening
@@ -2181,6 +2949,26 @@ static void substSelect(Select *p, int iTable, ExprList *pEList){
** subquery does not have both an ORDER BY and a LIMIT clause.
** (See ticket #2339)
**
+** (16) The outer query is not an aggregate or the subquery does
+** not contain ORDER BY. (Ticket #2942) This used to not matter
+** until we introduced the group_concat() function.
+**
+** (17) The sub-query is not a compound select, or it is a UNION ALL
+** compound clause made up entirely of non-aggregate queries, and
+** the parent query:
+**
+** * is not itself part of a compound select,
+** * is not an aggregate or DISTINCT query, and
+** * has no other tables or sub-selects in the FROM clause.
+**
+** The parent and sub-query may contain WHERE clauses. Subject to
+** rules (11), (13) and (14), they may also contain ORDER BY,
+** LIMIT and OFFSET clauses.
+**
+** (18) If the sub-query is a compound select, then all terms of the
+** ORDER by clause of the parent must be simple references to
+** columns of the sub-query.
+**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
@@ -2192,12 +2980,16 @@ static void substSelect(Select *p, int iTable, ExprList *pEList){
** the subquery before this routine runs.
*/
static int flattenSubquery(
+ Parse *pParse, /* Parsing context */
Select *p, /* The parent or outer SELECT statement */
int iFrom, /* Index in p->pSrc->a[] of the inner subquery */
int isAgg, /* True if outer SELECT uses aggregate functions */
int subqueryIsAgg /* True if the subquery uses aggregate functions */
){
+ const char *zSavedAuthContext = pParse->zAuthContext;
+ Select *pParent;
Select *pSub; /* The inner query or "subquery" */
+ Select *pSub1; /* Pointer to the rightmost select in sub-query */
SrcList *pSrc; /* The FROM clause of the outer query */
SrcList *pSubSrc; /* The FROM clause of the subquery */
ExprList *pList; /* The result set of the outer query */
@@ -2205,6 +2997,7 @@ static int flattenSubquery(
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
struct SrcList_item *pSubitem; /* The subquery */
+ sqlite3 *db = pParse->db;
/* Check to see if flattening is permitted. Return 0 if not.
*/
@@ -2212,6 +3005,7 @@ static int flattenSubquery(
pSrc = p->pSrc;
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
pSubitem = &pSrc->a[iFrom];
+ iParent = pSubitem->iCursor;
pSub = pSubitem->pSelect;
assert( pSub!=0 );
if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */
@@ -2237,6 +3031,7 @@ static int flattenSubquery(
if( (p->disallowOrderBy || p->pOrderBy) && pSub->pOrderBy ){
return 0; /* Restriction (11) */
}
+ if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */
/* Restriction 3: If the subquery is a join, make sure the subquery is
** not used as the right operand of an outer join. Examples of why this
@@ -2271,33 +3066,114 @@ static int flattenSubquery(
return 0;
}
- /* If we reach this point, it means flattening is permitted for the
- ** iFrom-th entry of the FROM clause in the outer query.
+ /* Restriction 17: If the sub-query is a compound SELECT, then it must
+ ** use only the UNION ALL operator. And none of the simple select queries
+ ** that make up the compound SELECT are allowed to be aggregate or distinct
+ ** queries.
*/
+ if( pSub->pPrior ){
+ if( p->pPrior || isAgg || p->isDistinct || pSrc->nSrc!=1 ){
+ return 0;
+ }
+ for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
+ if( pSub1->isAgg || pSub1->isDistinct
+ || (pSub1->pPrior && pSub1->op!=TK_ALL)
+ || !pSub1->pSrc || pSub1->pSrc->nSrc!=1
+ ){
+ return 0;
+ }
+ }
+
+ /* Restriction 18. */
+ if( p->pOrderBy ){
+ int ii;
+ for(ii=0; ii<p->pOrderBy->nExpr; ii++){
+ Expr *pExpr = p->pOrderBy->a[ii].pExpr;
+ if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iParent ){
+ return 0;
+ }
+ }
+ }
+ }
- /* Move all of the FROM elements of the subquery into the
- ** the FROM clause of the outer query. Before doing this, remember
- ** the cursor number for the original outer query FROM element in
- ** iParent. The iParent cursor will never be used. Subsequent code
- ** will scan expressions looking for iParent references and replace
- ** those references with expressions that resolve to the subquery FROM
- ** elements we are now copying in.
+ pParse->zAuthContext = pSubitem->zName;
+ sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
+ pParse->zAuthContext = zSavedAuthContext;
+
+ /* If the sub-query is a compound SELECT statement, then it must be
+ ** a UNION ALL and the parent query must be of the form:
+ **
+ ** SELECT <expr-list> FROM (<sub-query>) <where-clause>
+ **
+ ** followed by any ORDER BY, LIMIT and/or OFFSET clauses. This block
+ ** creates N copies of the parent query without any ORDER BY, LIMIT or
+ ** OFFSET clauses and joins them to the left-hand-side of the original
+ ** using UNION ALL operators. In this case N is the number of simple
+ ** select statements in the compound sub-query.
*/
- iParent = pSubitem->iCursor;
- {
- int nSubSrc = pSubSrc->nSrc;
- int jointype = pSubitem->jointype;
+ for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){
+ Select *pNew;
+ ExprList *pOrderBy = p->pOrderBy;
+ Expr *pLimit = p->pLimit;
+ Expr *pOffset = p->pOffset;
+ Select *pPrior = p->pPrior;
+ p->pOrderBy = 0;
+ p->pSrc = 0;
+ p->pPrior = 0;
+ p->pLimit = 0;
+ pNew = sqlite3SelectDup(db, p);
+ pNew->pPrior = pPrior;
+ p->pPrior = pNew;
+ p->pOrderBy = pOrderBy;
+ p->op = TK_ALL;
+ p->pSrc = pSrc;
+ p->pLimit = pLimit;
+ p->pOffset = pOffset;
+ p->pRightmost = 0;
+ pNew->pRightmost = 0;
+ }
- sqlite3DeleteTable(pSubitem->pTab);
- sqliteFree(pSubitem->zDatabase);
- sqliteFree(pSubitem->zName);
- sqliteFree(pSubitem->zAlias);
- if( nSubSrc>1 ){
+ /* If we reach this point, it means flattening is permitted for the
+ ** iFrom-th entry of the FROM clause in the outer query.
+ */
+ pSub = pSub1 = pSubitem->pSelect;
+ for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
+ int nSubSrc = pSubSrc->nSrc;
+ int jointype = 0;
+ pSubSrc = pSub->pSrc;
+ pSrc = pParent->pSrc;
+
+ /* Move all of the FROM elements of the subquery into the
+ ** the FROM clause of the outer query. Before doing this, remember
+ ** the cursor number for the original outer query FROM element in
+ ** iParent. The iParent cursor will never be used. Subsequent code
+ ** will scan expressions looking for iParent references and replace
+ ** those references with expressions that resolve to the subquery FROM
+ ** elements we are now copying in.
+ */
+ if( pSrc ){
+ pSubitem = &pSrc->a[iFrom];
+ nSubSrc = pSubSrc->nSrc;
+ jointype = pSubitem->jointype;
+ sqlite3DeleteTable(pSubitem->pTab);
+ sqlite3DbFree(db, pSubitem->zDatabase);
+ sqlite3DbFree(db, pSubitem->zName);
+ sqlite3DbFree(db, pSubitem->zAlias);
+ pSubitem->pTab = 0;
+ pSubitem->zDatabase = 0;
+ pSubitem->zName = 0;
+ pSubitem->zAlias = 0;
+ }
+ if( nSubSrc!=1 || !pSrc ){
int extra = nSubSrc - 1;
- for(i=1; i<nSubSrc; i++){
- pSrc = sqlite3SrcListAppend(pSrc, 0, 0);
+ for(i=(pSrc?1:0); i<nSubSrc; i++){
+ pSrc = sqlite3SrcListAppend(db, pSrc, 0, 0);
+ if( pSrc==0 ){
+ pParent->pSrc = 0;
+ return 1;
+ }
}
- p->pSrc = pSrc;
+ pParent->pSrc = pSrc;
for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){
pSrc->a[i] = pSrc->a[i-extra];
}
@@ -2307,287 +3183,111 @@ static int flattenSubquery(
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].jointype = jointype;
- }
-
- /* Now begin substituting subquery result set expressions for
- ** references to the iParent in the outer query.
- **
- ** Example:
- **
- ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
- ** \ \_____________ subquery __________/ /
- ** \_____________________ outer query ______________________________/
- **
- ** We look at every expression in the outer query and every place we see
- ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
- */
- pList = p->pEList;
- for(i=0; i<pList->nExpr; i++){
- Expr *pExpr;
- if( pList->a[i].zName==0 && (pExpr = pList->a[i].pExpr)->span.z!=0 ){
- pList->a[i].zName = sqliteStrNDup((char*)pExpr->span.z, pExpr->span.n);
+
+ /* Now begin substituting subquery result set expressions for
+ ** references to the iParent in the outer query.
+ **
+ ** Example:
+ **
+ ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
+ ** \ \_____________ subquery __________/ /
+ ** \_____________________ outer query ______________________________/
+ **
+ ** We look at every expression in the outer query and every place we see
+ ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
+ */
+ pList = pParent->pEList;
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pExpr;
+ if( pList->a[i].zName==0 && (pExpr = pList->a[i].pExpr)->span.z!=0 ){
+ pList->a[i].zName =
+ sqlite3DbStrNDup(db, (char*)pExpr->span.z, pExpr->span.n);
+ }
+ }
+ substExprList(db, pParent->pEList, iParent, pSub->pEList);
+ if( isAgg ){
+ substExprList(db, pParent->pGroupBy, iParent, pSub->pEList);
+ substExpr(db, pParent->pHaving, iParent, pSub->pEList);
+ }
+ if( pSub->pOrderBy ){
+ assert( pParent->pOrderBy==0 );
+ pParent->pOrderBy = pSub->pOrderBy;
+ pSub->pOrderBy = 0;
+ }else if( pParent->pOrderBy ){
+ substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
+ }
+ if( pSub->pWhere ){
+ pWhere = sqlite3ExprDup(db, pSub->pWhere);
+ }else{
+ pWhere = 0;
+ }
+ if( subqueryIsAgg ){
+ assert( pParent->pHaving==0 );
+ pParent->pHaving = pParent->pWhere;
+ pParent->pWhere = pWhere;
+ substExpr(db, pParent->pHaving, iParent, pSub->pEList);
+ pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
+ sqlite3ExprDup(db, pSub->pHaving));
+ assert( pParent->pGroupBy==0 );
+ pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy);
+ }else{
+ substExpr(db, pParent->pWhere, iParent, pSub->pEList);
+ pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
+ }
+
+ /* The flattened query is distinct if either the inner or the
+ ** outer query is distinct.
+ */
+ pParent->isDistinct = pParent->isDistinct || pSub->isDistinct;
+
+ /*
+ ** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
+ **
+ ** One is tempted to try to add a and b to combine the limits. But this
+ ** does not work if either limit is negative.
+ */
+ if( pSub->pLimit ){
+ pParent->pLimit = pSub->pLimit;
+ pSub->pLimit = 0;
}
- }
- substExprList(p->pEList, iParent, pSub->pEList);
- if( isAgg ){
- substExprList(p->pGroupBy, iParent, pSub->pEList);
- substExpr(p->pHaving, iParent, pSub->pEList);
- }
- if( pSub->pOrderBy ){
- assert( p->pOrderBy==0 );
- p->pOrderBy = pSub->pOrderBy;
- pSub->pOrderBy = 0;
- }else if( p->pOrderBy ){
- substExprList(p->pOrderBy, iParent, pSub->pEList);
- }
- if( pSub->pWhere ){
- pWhere = sqlite3ExprDup(pSub->pWhere);
- }else{
- pWhere = 0;
- }
- if( subqueryIsAgg ){
- assert( p->pHaving==0 );
- p->pHaving = p->pWhere;
- p->pWhere = pWhere;
- substExpr(p->pHaving, iParent, pSub->pEList);
- p->pHaving = sqlite3ExprAnd(p->pHaving, sqlite3ExprDup(pSub->pHaving));
- assert( p->pGroupBy==0 );
- p->pGroupBy = sqlite3ExprListDup(pSub->pGroupBy);
- }else{
- substExpr(p->pWhere, iParent, pSub->pEList);
- p->pWhere = sqlite3ExprAnd(p->pWhere, pWhere);
- }
-
- /* The flattened query is distinct if either the inner or the
- ** outer query is distinct.
- */
- p->isDistinct = p->isDistinct || pSub->isDistinct;
-
- /*
- ** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
- **
- ** One is tempted to try to add a and b to combine the limits. But this
- ** does not work if either limit is negative.
- */
- if( pSub->pLimit ){
- p->pLimit = pSub->pLimit;
- pSub->pLimit = 0;
}
/* Finially, delete what is left of the subquery and return
** success.
*/
- sqlite3SelectDelete(pSub);
+ sqlite3SelectDelete(db, pSub1);
+
return 1;
}
-#endif /* SQLITE_OMIT_VIEW */
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
-** Analyze the SELECT statement passed in as an argument to see if it
-** is a simple min() or max() query. If it is and this query can be
-** satisfied using a single seek to the beginning or end of an index,
-** then generate the code for this SELECT and return 1. If this is not a
-** simple min() or max() query, then return 0;
-**
-** A simply min() or max() query looks like this:
+** Analyze the SELECT statement passed as an argument to see if it
+** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
+** it is, or 0 otherwise. At present, a query is considered to be
+** a min()/max() query if:
**
-** SELECT min(a) FROM table;
-** SELECT max(a) FROM table;
+** 1. There is a single object in the FROM clause.
**
-** The query may have only a single table in its FROM argument. There
-** can be no GROUP BY or HAVING or WHERE clauses. The result set must
-** be the min() or max() of a single column of the table. The column
-** in the min() or max() function must be indexed.
-**
-** The parameters to this routine are the same as for sqlite3Select().
-** See the header comment on that routine for additional information.
+** 2. There is a single expression in the result set, and it is
+** either min(x) or max(x), where x is a column reference.
*/
-static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
+static int minMaxQuery(Parse *pParse, Select *p){
Expr *pExpr;
- int iCol;
- Table *pTab;
- Index *pIdx;
- int base;
- Vdbe *v;
- int seekOp;
- ExprList *pEList, *pList, eList;
- struct ExprList_item eListItem;
- SrcList *pSrc;
- int brk;
- int iDb;
+ ExprList *pEList = p->pEList;
- /* Check to see if this query is a simple min() or max() query. Return
- ** zero if it is not.
- */
- if( p->pGroupBy || p->pHaving || p->pWhere ) return 0;
- pSrc = p->pSrc;
- if( pSrc->nSrc!=1 ) return 0;
- pEList = p->pEList;
- if( pEList->nExpr!=1 ) return 0;
+ if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
pExpr = pEList->a[0].pExpr;
- if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
- pList = pExpr->pList;
- if( pList==0 || pList->nExpr!=1 ) return 0;
- if( pExpr->token.n!=3 ) return 0;
+ pEList = pExpr->pList;
+ if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0;
+ if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
+ if( pExpr->token.n!=3 ) return WHERE_ORDERBY_NORMAL;
if( sqlite3StrNICmp((char*)pExpr->token.z,"min",3)==0 ){
- seekOp = OP_Rewind;
+ return WHERE_ORDERBY_MIN;
}else if( sqlite3StrNICmp((char*)pExpr->token.z,"max",3)==0 ){
- seekOp = OP_Last;
- }else{
- return 0;
- }
- pExpr = pList->a[0].pExpr;
- if( pExpr->op!=TK_COLUMN ) return 0;
- iCol = pExpr->iColumn;
- pTab = pSrc->a[0].pTab;
-
- /* This optimization cannot be used with virtual tables. */
- if( IsVirtual(pTab) ) return 0;
-
- /* If we get to here, it means the query is of the correct form.
- ** Check to make sure we have an index and make pIdx point to the
- ** appropriate index. If the min() or max() is on an INTEGER PRIMARY
- ** key column, no index is necessary so set pIdx to NULL. If no
- ** usable index is found, return 0.
- */
- if( iCol<0 ){
- pIdx = 0;
- }else{
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);
- if( pColl==0 ) return 0;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- assert( pIdx->nColumn>=1 );
- if( pIdx->aiColumn[0]==iCol &&
- 0==sqlite3StrICmp(pIdx->azColl[0], pColl->zName) ){
- break;
- }
- }
- if( pIdx==0 ) return 0;
- }
-
- /* Identify column types if we will be using the callback. This
- ** step is skipped if the output is going to a table or a memory cell.
- ** The column names have already been generated in the calling function.
- */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ) return 0;
-
- /* If the output is destined for a temporary table, open that table.
- */
- if( eDest==SRT_EphemTab ){
- sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, 1);
- }
-
- /* Generating code to find the min or the max. Basically all we have
- ** to do is find the first or the last entry in the chosen index. If
- ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
- ** or last entry in the main table.
- */
- iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- assert( iDb>=0 || pTab->isEphem );
- sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- base = pSrc->a[0].iCursor;
- brk = sqlite3VdbeMakeLabel(v);
- computeLimitRegisters(pParse, p, brk);
- if( pSrc->a[0].pSelect==0 ){
- sqlite3OpenTable(pParse, base, iDb, pTab, OP_OpenRead);
+ return WHERE_ORDERBY_MAX;
}
- if( pIdx==0 ){
- sqlite3VdbeAddOp(v, seekOp, base, 0);
- }else{
- /* Even though the cursor used to open the index here is closed
- ** as soon as a single value has been read from it, allocate it
- ** using (pParse->nTab++) to prevent the cursor id from being
- ** reused. This is important for statements of the form
- ** "INSERT INTO x SELECT max() FROM x".
- */
- int iIdx;
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- iIdx = pParse->nTab++;
- assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- sqlite3VdbeOp3(v, OP_OpenRead, iIdx, pIdx->tnum,
- (char*)pKey, P3_KEYINFO_HANDOFF);
- if( seekOp==OP_Rewind ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
- sqlite3VdbeAddOp(v, OP_MakeRecord, 1, 0);
- seekOp = OP_MoveGt;
- }
- if( pIdx->aSortOrder[0]==SQLITE_SO_DESC ){
- /* Ticket #2514: invert the seek operator if we are using
- ** a descending index. */
- if( seekOp==OP_Last ){
- seekOp = OP_Rewind;
- }else{
- assert( seekOp==OP_MoveGt );
- seekOp = OP_MoveLt;
- }
- }
- sqlite3VdbeAddOp(v, seekOp, iIdx, 0);
- sqlite3VdbeAddOp(v, OP_IdxRowid, iIdx, 0);
- sqlite3VdbeAddOp(v, OP_Close, iIdx, 0);
- sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
- }
- eList.nExpr = 1;
- memset(&eListItem, 0, sizeof(eListItem));
- eList.a = &eListItem;
- eList.a[0].pExpr = pExpr;
- selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, brk, brk, 0);
- sqlite3VdbeResolveLabel(v, brk);
- sqlite3VdbeAddOp(v, OP_Close, base, 0);
-
- return 1;
-}
-
-/*
-** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return
-** the number of errors seen.
-**
-** An ORDER BY or GROUP BY is a list of expressions. If any expression
-** is an integer constant, then that expression is replaced by the
-** corresponding entry in the result set.
-*/
-static int processOrderGroupBy(
- NameContext *pNC, /* Name context of the SELECT statement. */
- ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */
- const char *zType /* Either "ORDER" or "GROUP", as appropriate */
-){
- int i;
- ExprList *pEList = pNC->pEList; /* The result set of the SELECT */
- Parse *pParse = pNC->pParse; /* The result set of the SELECT */
- assert( pEList );
-
- if( pOrderBy==0 ) return 0;
- if( pOrderBy->nExpr>SQLITE_MAX_COLUMN ){
- sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
- return 1;
- }
- for(i=0; i<pOrderBy->nExpr; i++){
- int iCol;
- Expr *pE = pOrderBy->a[i].pExpr;
- if( sqlite3ExprIsInteger(pE, &iCol) ){
- if( iCol>0 && iCol<=pEList->nExpr ){
- CollSeq *pColl = pE->pColl;
- int flags = pE->flags & EP_ExpCollate;
- sqlite3ExprDelete(pE);
- pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr);
- if( pColl && flags ){
- pE->pColl = pColl;
- pE->flags |= flags;
- }
- }else{
- sqlite3ErrorMsg(pParse,
- "%s BY column number %d out of range - should be "
- "between 1 and %d", zType, iCol, pEList->nExpr);
- return 1;
- }
- }
- if( sqlite3ExprResolveNames(pNC, pE) ){
- return 1;
- }
- }
- return 0;
+ return WHERE_ORDERBY_NORMAL;
}
/*
@@ -2684,13 +3384,15 @@ int sqlite3SelectResolve(
return SQLITE_ERROR;
}
if( p->pPrior==0 ){
- if( processOrderGroupBy(&sNC, p->pOrderBy, "ORDER") ||
- processOrderGroupBy(&sNC, pGroupBy, "GROUP") ){
+ if( processOrderGroupBy(pParse, p, p->pOrderBy, 1, &sNC.hasAgg) ){
return SQLITE_ERROR;
}
}
+ if( processOrderGroupBy(pParse, p, pGroupBy, 0, &sNC.hasAgg) ){
+ return SQLITE_ERROR;
+ }
- if( sqlite3MallocFailed() ){
+ if( pParse->db->mallocFailed ){
return SQLITE_NOMEM;
}
@@ -2733,10 +3435,10 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
return;
}
for(i=0; i<pAggInfo->nColumn; i++){
- sqlite3VdbeAddOp(v, OP_MemNull, pAggInfo->aCol[i].iMem, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem);
}
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
- sqlite3VdbeAddOp(v, OP_MemNull, pFunc->iMem, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pExpr;
if( pE->pList==0 || pE->pList->nExpr!=1 ){
@@ -2745,8 +3447,8 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
pFunc->iDistinct = -1;
}else{
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList);
- sqlite3VdbeOp3(v, OP_OpenEphemeral, pFunc->iDistinct, 0,
- (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
+ (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
}
}
}
@@ -2762,8 +3464,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
struct AggInfo_func *pF;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
ExprList *pList = pF->pExpr->pList;
- sqlite3VdbeOp3(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0,
- (void*)pF->pFunc, P3_FUNCDEF);
+ sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0,
+ (void*)pF->pFunc, P4_FUNCDEF);
}
}
@@ -2781,17 +3483,20 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
int addrNext = 0;
+ int regAgg;
ExprList *pList = pF->pExpr->pList;
if( pList ){
nArg = pList->nExpr;
- sqlite3ExprCodeExprList(pParse, pList);
+ regAgg = sqlite3GetTempRange(pParse, nArg);
+ sqlite3ExprCodeExprList(pParse, pList, regAgg, 0);
}else{
nArg = 0;
+ regAgg = 0;
}
if( pF->iDistinct>=0 ){
addrNext = sqlite3VdbeMakeLabel(v);
assert( nArg==1 );
- codeDistinct(v, pF->iDistinct, addrNext, 1);
+ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
}
if( pF->pFunc->needCollSeq ){
CollSeq *pColl = 0;
@@ -2804,44 +3509,59 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( !pColl ){
pColl = pParse->db->pDfltColl;
}
- sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeOp3(v, OP_AggStep, pF->iMem, nArg, (void*)pF->pFunc, P3_FUNCDEF);
+ sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem,
+ (void*)pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, nArg);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
+ sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
}
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
- sqlite3ExprCode(pParse, pC->pExpr);
- sqlite3VdbeAddOp(v, OP_MemStore, pC->iMem, 1);
+ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
}
pAggInfo->directMode = 0;
}
-
/*
** Generate code for the given SELECT statement.
**
** The results are distributed in various ways depending on the
-** value of eDest and iParm.
+** contents of the SelectDest structure pointed to by argument pDest
+** as follows:
**
-** eDest Value Result
+** pDest->eDest Result
** ------------ -------------------------------------------
** SRT_Callback Invoke the callback for each row of the result.
**
-** SRT_Mem Store first result in memory cell iParm
+** SRT_Mem Store first result in memory cell pDest->iParm
+**
+** SRT_Set Store results as keys of table pDest->iParm.
+** Apply the affinity pDest->affinity before storing them.
+**
+** SRT_Union Store results as a key in a temporary table pDest->iParm.
**
-** SRT_Set Store results as keys of table iParm.
+** SRT_Except Remove results from the temporary table pDest->iParm.
**
-** SRT_Union Store results as a key in a temporary table iParm
+** SRT_Table Store results in temporary table pDest->iParm
**
-** SRT_Except Remove results from the temporary table iParm.
+** SRT_EphemTab Create an temporary table pDest->iParm and store
+** the result there. The cursor is left open after
+** returning.
**
-** SRT_Table Store results in temporary table iParm
+** SRT_Coroutine Invoke a co-routine to compute a single row of
+** the result
**
-** The table above is incomplete. Additional eDist value have be added
-** since this comment was written. See the selectInnerLoop() function for
-** a complete listing of the allowed values of eDest and their meanings.
+** SRT_Exists Store a 1 in memory cell pDest->iParm if the result
+** set is not empty.
+**
+** SRT_Discard Throw the results away.
+**
+** See the selectInnerLoop() function for a canonical listing of the
+** allowed values of eDest and their meanings.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
@@ -2874,12 +3594,10 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
int sqlite3Select(
Parse *pParse, /* The parser context */
Select *p, /* The SELECT statement being coded. */
- int eDest, /* How to dispose of the results */
- int iParm, /* A parameter used by the eDest disposal method */
+ SelectDest *pDest, /* What to do with the query results */
Select *pParent, /* Another SELECT for which this is a sub-query */
int parentTab, /* Index in pParent->pSrc of this query */
- int *pParentAgg, /* True if pParent uses aggregate functions */
- char *aff /* If eDest is SRT_Union, the affinity string */
+ int *pParentAgg /* True if pParent uses aggregate functions */
){
int i, j; /* Loop counters */
WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
@@ -2897,49 +3615,36 @@ int sqlite3Select(
int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
+ sqlite3 *db; /* The database connection */
- if( p==0 || sqlite3MallocFailed() || pParse->nErr ){
+ db = pParse->db;
+ if( p==0 || db->mallocFailed || pParse->nErr ){
return 1;
}
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
memset(&sAggInfo, 0, sizeof(sAggInfo));
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
- /* If there is are a sequence of queries, do the earlier ones first.
- */
- if( p->pPrior ){
- if( p->pRightmost==0 ){
- Select *pLoop;
- int cnt = 0;
- for(pLoop=p; pLoop; pLoop=pLoop->pPrior, cnt++){
- pLoop->pRightmost = p;
- }
- if( SQLITE_MAX_COMPOUND_SELECT>0 && cnt>SQLITE_MAX_COMPOUND_SELECT ){
- sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
- return 1;
- }
- }
- return multiSelect(pParse, p, eDest, iParm, aff);
- }
-#endif
-
pOrderBy = p->pOrderBy;
- if( IgnorableOrderby(eDest) ){
+ if( IgnorableOrderby(pDest) ){
p->pOrderBy = 0;
+
+ /* In these cases the DISTINCT operator makes no difference to the
+ ** results, so remove it if it were specified.
+ */
+ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
+ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard);
+ p->isDistinct = 0;
}
if( sqlite3SelectResolve(pParse, p, 0) ){
goto select_end;
}
p->pOrderBy = pOrderBy;
+
/* Make local copies of the parameters for this query.
*/
pTabList = p->pSrc;
- pWhere = p->pWhere;
- pGroupBy = p->pGroupBy;
- pHaving = p->pHaving;
isAgg = p->isAgg;
- isDistinct = p->isDistinct;
pEList = p->pEList;
if( pEList==0 ) goto select_end;
@@ -2949,18 +3654,9 @@ int sqlite3Select(
*/
if( pParse->nErr>0 ) goto select_end;
- /* If writing to memory or generating a set
- ** only a single column may be output.
- */
-#ifndef SQLITE_OMIT_SUBQUERY
- if( checkForMultiColumnSelectError(pParse, eDest, pEList->nExpr) ){
- goto select_end;
- }
-#endif
-
/* ORDER BY is ignored for some destinations.
*/
- if( IgnorableOrderby(eDest) ){
+ if( IgnorableOrderby(pDest) ){
pOrderBy = 0;
}
@@ -2972,20 +3668,24 @@ int sqlite3Select(
/* Generate code for all sub-queries in the FROM clause
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
- for(i=0; i<pTabList->nSrc; i++){
- const char *zSavedAuthContext = 0;
- int needRestoreContext;
+ for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
struct SrcList_item *pItem = &pTabList->a[i];
-
- if( pItem->pSelect==0 || pItem->isPopulated ) continue;
- if( pItem->zName!=0 ){
- zSavedAuthContext = pParse->zAuthContext;
- pParse->zAuthContext = pItem->zName;
- needRestoreContext = 1;
- }else{
- needRestoreContext = 0;
+ SelectDest dest;
+ Select *pSub = pItem->pSelect;
+ int isAggSub;
+ char *zName = pItem->zName;
+
+ if( pSub==0 || pItem->isPopulated ) continue;
+ if( zName!=0 ){ /* An sql view */
+ const char *zSavedAuthContext = pParse->zAuthContext;
+ pParse->zAuthContext = zName;
+ rc = sqlite3SelectResolve(pParse, pSub, 0);
+ pParse->zAuthContext = zSavedAuthContext;
+ if( rc ){
+ goto select_end;
+ }
}
-#if SQLITE_MAX_EXPR_DEPTH>0
+
/* Increment Parse.nHeight by the height of the largest expression
** tree refered to by this, the parent select. The child select
** may contain expression trees of at most
@@ -2994,45 +3694,76 @@ int sqlite3Select(
** an exact limit.
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
-#endif
- sqlite3Select(pParse, pItem->pSelect, SRT_EphemTab,
- pItem->iCursor, p, i, &isAgg, 0);
-#if SQLITE_MAX_EXPR_DEPTH>0
- pParse->nHeight -= sqlite3SelectExprHeight(p);
-#endif
- if( needRestoreContext ){
- pParse->zAuthContext = zSavedAuthContext;
+
+ /* Check to see if the subquery can be absorbed into the parent. */
+ isAggSub = pSub->isAgg;
+ if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
+ if( isAggSub ){
+ p->isAgg = isAgg = 1;
+ }
+ i = -1;
+ }else{
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
+ sqlite3Select(pParse, pSub, &dest, p, i, &isAgg);
+ }
+ if( pParse->nErr || db->mallocFailed ){
+ goto select_end;
}
+ pParse->nHeight -= sqlite3SelectExprHeight(p);
pTabList = p->pSrc;
- pWhere = p->pWhere;
- if( !IgnorableOrderby(eDest) ){
+ if( !IgnorableOrderby(pDest) ){
pOrderBy = p->pOrderBy;
}
- pGroupBy = p->pGroupBy;
- pHaving = p->pHaving;
- isDistinct = p->isDistinct;
}
+ pEList = p->pEList;
#endif
+ pWhere = p->pWhere;
+ pGroupBy = p->pGroupBy;
+ pHaving = p->pHaving;
+ isDistinct = p->isDistinct;
- /* Check for the special case of a min() or max() function by itself
- ** in the result set.
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
+ /* If there is are a sequence of queries, do the earlier ones first.
*/
- if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
- rc = 0;
- goto select_end;
+ if( p->pPrior ){
+ if( p->pRightmost==0 ){
+ Select *pLoop, *pRight = 0;
+ int cnt = 0;
+ int mxSelect;
+ for(pLoop=p; pLoop; pLoop=pLoop->pPrior, cnt++){
+ pLoop->pRightmost = p;
+ pLoop->pNext = pRight;
+ pRight = pLoop;
+ }
+ mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
+ if( mxSelect && cnt>mxSelect ){
+ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
+ return 1;
+ }
+ }
+ return multiSelect(pParse, p, pDest);
}
+#endif
- /* Check to see if this is a subquery that can be "flattened" into its parent.
- ** If flattening is a possiblity, do so and return immediately.
+ /* If writing to memory or generating a set
+ ** only a single column may be output.
*/
-#ifndef SQLITE_OMIT_VIEW
- if( pParent && pParentAgg &&
- flattenSubquery(pParent, parentTab, *pParentAgg, isAgg) ){
- if( isAgg ) *pParentAgg = 1;
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
goto select_end;
}
#endif
+ /* If possible, rewrite the query to use GROUP BY instead of DISTINCT.
+ ** GROUP BY may use an index, DISTINCT never does.
+ */
+ if( p->isDistinct && !p->isAgg && !p->pGroupBy ){
+ p->pGroupBy = sqlite3ExprListDup(db, p->pEList);
+ pGroupBy = p->pGroupBy;
+ p->isDistinct = 0;
+ isDistinct = 0;
+ }
+
/* If there is an ORDER BY clause, then this sorting
** index might end up being unused if the data can be
** extracted in pre-sorted order. If that is the case, then the
@@ -3042,21 +3773,20 @@ int sqlite3Select(
*/
if( pOrderBy ){
KeyInfo *pKeyInfo;
- if( pParse->nErr ){
- goto select_end;
- }
pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
pOrderBy->iECursor = pParse->nTab++;
p->addrOpenEphm[2] = addrSortIndex =
- sqlite3VdbeOp3(v, OP_OpenEphemeral, pOrderBy->iECursor, pOrderBy->nExpr+2, (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
+ (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
}else{
addrSortIndex = -1;
}
/* If the output is destined for a temporary table, open that table.
*/
- if( eDest==SRT_EphemTab ){
- sqlite3VdbeAddOp(v, OP_OpenEphemeral, iParm, pEList->nExpr);
+ if( pDest->eDest==SRT_EphemTab ){
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr);
}
/* Set the limiter.
@@ -3068,10 +3798,11 @@ int sqlite3Select(
*/
if( isDistinct ){
KeyInfo *pKeyInfo;
+ assert( isAgg || pGroupBy );
distinct = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
- sqlite3VdbeOp3(v, OP_OpenEphemeral, distinct, 0,
- (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
+ (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
}else{
distinct = -1;
}
@@ -3081,7 +3812,7 @@ int sqlite3Select(
/* This case is for non-aggregate queries
** Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
if( pWInfo==0 ) goto select_end;
/* If sorting index that was created by a prior OP_OpenEphemeral
@@ -3095,10 +3826,9 @@ int sqlite3Select(
/* Use the standard inner loop
*/
- if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
- iParm, pWInfo->iContinue, pWInfo->iBreak, aff) ){
- goto select_end;
- }
+ assert(!isDistinct);
+ selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
+ pWInfo->iContinue, pWInfo->iBreak);
/* End the database scan loop.
*/
@@ -3118,14 +3848,14 @@ int sqlite3Select(
/* The following variables hold addresses or labels for parts of the
** virtual machine program we are putting together */
int addrOutputRow; /* Start of subroutine that outputs a result row */
+ int regOutputRow; /* Return address register for output subroutine */
int addrSetAbort; /* Set the abort flag and return */
int addrInitializeLoop; /* Start of code that initializes the input loop */
int addrTopOfLoop; /* Top of the input loop */
- int addrGroupByChange; /* Code that runs when any GROUP BY term changes */
- int addrProcessRow; /* Code to process a single input row */
int addrEnd; /* End of all processing */
int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */
int addrReset; /* Subroutine for resetting the accumulator */
+ int regReset; /* Return address register for reset subroutine */
addrEnd = sqlite3VdbeMakeLabel(v);
@@ -3139,35 +3869,27 @@ int sqlite3Select(
sNC.pAggInfo = &sAggInfo;
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
sAggInfo.pGroupBy = pGroupBy;
- if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
- goto select_end;
- }
- if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
- goto select_end;
- }
- if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
- goto select_end;
+ sqlite3ExprAnalyzeAggList(&sNC, pEList);
+ sqlite3ExprAnalyzeAggList(&sNC, pOrderBy);
+ if( pHaving ){
+ sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
}
sAggInfo.nAccumulator = sAggInfo.nColumn;
for(i=0; i<sAggInfo.nFunc; i++){
- if( sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList) ){
- goto select_end;
- }
+ sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList);
}
- if( sqlite3MallocFailed() ) goto select_end;
+ if( db->mallocFailed ) goto select_end;
/* Processing for aggregates with GROUP BY is very different and
- ** much more complex tha aggregates without a GROUP BY.
+ ** much more complex than aggregates without a GROUP BY.
*/
if( pGroupBy ){
KeyInfo *pKeyInfo; /* Keying information for the group by clause */
+ int j1;
/* Create labels that we will be needing
*/
-
addrInitializeLoop = sqlite3VdbeMakeLabel(v);
- addrGroupByChange = sqlite3VdbeMakeLabel(v);
- addrProcessRow = sqlite3VdbeMakeLabel(v);
/* If there is a GROUP BY clause we might need a sorting index to
** implement it. Allocate that sorting index now. If it turns out
@@ -3176,24 +3898,23 @@ int sqlite3Select(
*/
sAggInfo.sortingIdx = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
- addrSortingIdx =
- sqlite3VdbeOp3(v, OP_OpenEphemeral, sAggInfo.sortingIdx,
- sAggInfo.nSortingColumn,
- (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+ addrSortingIdx = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
+ 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
/* Initialize memory locations used by GROUP BY aggregate processing
*/
- iUseFlag = pParse->nMem++;
- iAbortFlag = pParse->nMem++;
- iAMem = pParse->nMem;
+ iUseFlag = ++pParse->nMem;
+ iAbortFlag = ++pParse->nMem;
+ iAMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
- iBMem = pParse->nMem;
+ iBMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, iAbortFlag);
- VdbeComment((v, "# clear abort flag"));
- sqlite3VdbeAddOp(v, OP_MemInt, 0, iUseFlag);
- VdbeComment((v, "# indicate accumulator empty"));
- sqlite3VdbeAddOp(v, OP_Goto, 0, addrInitializeLoop);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
+ VdbeComment((v, "clear abort flag"));
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
+ VdbeComment((v, "indicate accumulator empty"));
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrInitializeLoop);
/* Generate a subroutine that outputs a single row of the result
** set. This subroutine first looks at the iUseFlag. If iUseFlag
@@ -3203,31 +3924,30 @@ int sqlite3Select(
** order to signal the caller to abort.
*/
addrSetAbort = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_MemInt, 1, iAbortFlag);
- VdbeComment((v, "# set abort flag"));
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, iAbortFlag);
+ VdbeComment((v, "set abort flag"));
+ regOutputRow = ++pParse->nMem;
+ sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
addrOutputRow = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_IfMemPos, iUseFlag, addrOutputRow+2);
- VdbeComment((v, "# Groupby result generator entry point"));
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
+ VdbeComment((v, "Groupby result generator entry point"));
+ sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
finalizeAggFunctions(pParse, &sAggInfo);
if( pHaving ){
- sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, 1);
- }
- rc = selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
- distinct, eDest, iParm,
- addrOutputRow+1, addrSetAbort, aff);
- if( rc ){
- goto select_end;
+ sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
}
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
- VdbeComment((v, "# end groupby result generator"));
+ selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
+ distinct, pDest,
+ addrOutputRow+1, addrSetAbort);
+ sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
+ VdbeComment((v, "end groupby result generator"));
/* Generate a subroutine that will reset the group-by accumulator
*/
addrReset = sqlite3VdbeCurrentAddr(v);
+ regReset = ++pParse->nMem;
resetAccumulator(pParse, &sAggInfo);
- sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ sqlite3VdbeAddOp1(v, OP_Return, regReset);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
@@ -3235,8 +3955,8 @@ int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeResolveLabel(v, addrInitializeLoop);
- sqlite3VdbeAddOp(v, OP_Gosub, 0, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
if( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so
@@ -3251,21 +3971,53 @@ int sqlite3Select(
** then loop over the sorting index in order to get the output
** in sorted order
*/
+ int regBase;
+ int regRecord;
+ int nCol;
+ int nGroupBy;
+
groupBySort = 1;
- sqlite3ExprCodeExprList(pParse, pGroupBy);
- sqlite3VdbeAddOp(v, OP_Sequence, sAggInfo.sortingIdx, 0);
- j = pGroupBy->nExpr+1;
+ nGroupBy = pGroupBy->nExpr;
+ nCol = nGroupBy + 1;
+ j = nGroupBy+1;
+ for(i=0; i<sAggInfo.nColumn; i++){
+ if( sAggInfo.aCol[i].iSorterColumn>=j ){
+ nCol++;
+ j++;
+ }
+ }
+ regBase = sqlite3GetTempRange(pParse, nCol);
+ sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
+ sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
+ j = nGroupBy+1;
for(i=0; i<sAggInfo.nColumn; i++){
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
- if( pCol->iSorterColumn<j ) continue;
- sqlite3ExprCodeGetColumn(v, pCol->pTab, pCol->iColumn, pCol->iTable);
- j++;
+ if( pCol->iSorterColumn>=j ){
+ int r1 = j + regBase;
+#ifndef NDEBUG
+ int r2 =
+#endif
+ sqlite3ExprCodeGetColumn(pParse,
+ pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
+ j++;
+
+ /* sAggInfo.aCol[] only contains one entry per column. So
+ ** The reference to pCol->iColumn,pCol->iTable must have been
+ ** the first reference to that column. Hence,
+ ** sqliteExprCodeGetColumn is guaranteed to put the result in
+ ** the column requested.
+ */
+ assert( r1==r2 );
+ }
}
- sqlite3VdbeAddOp(v, OP_MakeRecord, j, 0);
- sqlite3VdbeAddOp(v, OP_IdxInsert, sAggInfo.sortingIdx, 0);
+ regRecord = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord);
+ sqlite3ReleaseTempReg(pParse, regRecord);
+ sqlite3ReleaseTempRange(pParse, regBase, nCol);
sqlite3WhereEnd(pWInfo);
- sqlite3VdbeAddOp(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
- VdbeComment((v, "# GROUP BY sort"));
+ sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
+ VdbeComment((v, "GROUP BY sort"));
sAggInfo.useSortingIdx = 1;
}
@@ -3277,28 +4029,19 @@ int sqlite3Select(
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
- sqlite3VdbeAddOp(v, OP_Column, sAggInfo.sortingIdx, j);
+ sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j);
}else{
sAggInfo.directMode = 1;
- sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr);
+ sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
}
- sqlite3VdbeAddOp(v, OP_MemStore, iBMem+j, j<pGroupBy->nExpr-1);
- }
- for(j=pGroupBy->nExpr-1; j>=0; j--){
- if( j<pGroupBy->nExpr-1 ){
- sqlite3VdbeAddOp(v, OP_MemLoad, iBMem+j, 0);
- }
- sqlite3VdbeAddOp(v, OP_MemLoad, iAMem+j, 0);
- if( j==0 ){
- sqlite3VdbeAddOp(v, OP_Eq, 0x200, addrProcessRow);
- }else{
- sqlite3VdbeAddOp(v, OP_Ne, 0x200, addrGroupByChange);
- }
- sqlite3VdbeChangeP3(v, -1, (void*)pKeyInfo->aColl[j], P3_COLLSEQ);
}
+ sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
+ (char*)pKeyInfo, P4_KEYINFO);
+ j1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1);
/* Generate code that runs whenever the GROUP BY changes.
- ** Change in the GROUP BY are detected by the previous code
+ ** Changes in the GROUP BY are detected by the previous code
** block. If there were no changes, this block is skipped.
**
** This code copies current group by terms in b0,b1,b2,...
@@ -3306,29 +4049,26 @@ int sqlite3Select(
** and resets the aggregate accumulator registers in preparation
** for the next GROUP BY batch.
*/
- sqlite3VdbeResolveLabel(v, addrGroupByChange);
- for(j=0; j<pGroupBy->nExpr; j++){
- sqlite3VdbeAddOp(v, OP_MemMove, iAMem+j, iBMem+j);
- }
- sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
- VdbeComment((v, "# output one row"));
- sqlite3VdbeAddOp(v, OP_IfMemPos, iAbortFlag, addrEnd);
- VdbeComment((v, "# check abort flag"));
- sqlite3VdbeAddOp(v, OP_Gosub, 0, addrReset);
- VdbeComment((v, "# reset accumulator"));
+ sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
+ VdbeComment((v, "output one row"));
+ sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd);
+ VdbeComment((v, "check abort flag"));
+ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
+ VdbeComment((v, "reset accumulator"));
/* Update the aggregate accumulators based on the content of
** the current row
*/
- sqlite3VdbeResolveLabel(v, addrProcessRow);
+ sqlite3VdbeJumpHere(v, j1);
updateAccumulator(pParse, &sAggInfo);
- sqlite3VdbeAddOp(v, OP_MemInt, 1, iUseFlag);
- VdbeComment((v, "# indicate data in accumulator"));
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
+ VdbeComment((v, "indicate data in accumulator"));
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
}else{
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx, 1);
@@ -3336,27 +4076,74 @@ int sqlite3Select(
/* Output the final row of result
*/
- sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
- VdbeComment((v, "# output final row"));
+ sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
+ VdbeComment((v, "output final row"));
} /* endif pGroupBy */
else {
+ ExprList *pMinMax = 0;
+ ExprList *pDel = 0;
+ u8 flag;
+
+ /* Check if the query is of one of the following forms:
+ **
+ ** SELECT min(x) FROM ...
+ ** SELECT max(x) FROM ...
+ **
+ ** If it is, then ask the code in where.c to attempt to sort results
+ ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
+ ** If where.c is able to produce results sorted in this order, then
+ ** add vdbe code to break out of the processing loop after the
+ ** first iteration (since the first iteration of the loop is
+ ** guaranteed to operate on the row with the minimum or maximum
+ ** value of x, the only row required).
+ **
+ ** A special flag must be passed to sqlite3WhereBegin() to slightly
+ ** modify behaviour as follows:
+ **
+ ** + If the query is a "SELECT min(x)", then the loop coded by
+ ** where.c should not iterate over any values with a NULL value
+ ** for x.
+ **
+ ** + The optimizer code in where.c (the thing that decides which
+ ** index or indices to use) should place a different priority on
+ ** satisfying the 'ORDER BY' clause than it does in other cases.
+ ** Refer to code and comments in where.c for details.
+ */
+ flag = minMaxQuery(pParse, p);
+ if( flag ){
+ pDel = pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->pList);
+ if( pMinMax && !db->mallocFailed ){
+ pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN;
+ pMinMax->a[0].pExpr->op = TK_COLUMN;
+ }
+ }
+
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
- if( pWInfo==0 ) goto select_end;
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
+ if( pWInfo==0 ){
+ sqlite3ExprListDelete(db, pDel);
+ goto select_end;
+ }
updateAccumulator(pParse, &sAggInfo);
+ if( !pMinMax && flag ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
+ VdbeComment((v, "%s() by index",(flag==WHERE_ORDERBY_MIN?"min":"max")));
+ }
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, &sAggInfo);
pOrderBy = 0;
if( pHaving ){
- sqlite3ExprIfFalse(pParse, pHaving, addrEnd, 1);
+ sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
}
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
- eDest, iParm, addrEnd, addrEnd, aff);
+ pDest, addrEnd, addrEnd);
+
+ sqlite3ExprListDelete(db, pDel);
}
sqlite3VdbeResolveLabel(v, addrEnd);
@@ -3366,7 +4153,7 @@ int sqlite3Select(
** and send them to the callback one by one.
*/
if( pOrderBy ){
- generateSortTail(pParse, p, v, pEList->nExpr, eDest, iParm);
+ generateSortTail(pParse, p, v, pEList->nExpr, pDest);
}
#ifndef SQLITE_OMIT_SUBQUERY
@@ -3399,12 +4186,12 @@ select_end:
/* Identify column names if we will be using them in a callback. This
** step is skipped if the output is going to some other destination.
*/
- if( rc==SQLITE_OK && eDest==SRT_Callback ){
+ if( rc==SQLITE_OK && pDest->eDest==SRT_Callback ){
generateColumnNames(pParse, pTabList, pEList);
}
- sqliteFree(sAggInfo.aCol);
- sqliteFree(sAggInfo.aFunc);
+ sqlite3DbFree(db, sAggInfo.aCol);
+ sqlite3DbFree(db, sAggInfo.aFunc);
return rc;
}
diff --git a/third_party/sqlite/shell.c b/third_party/sqlite/src/shell.c
index aee1ce69..77ec143 100644..100755
--- a/third_party/sqlite/shell.c
+++ b/third_party/sqlite/src/shell.c
@@ -12,7 +12,7 @@
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
-** $Id: shell.c,v 1.166 2007/07/30 20:41:53 drh Exp $
+** $Id: shell.c,v 1.184 2008/07/11 17:23:25 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
@@ -22,22 +22,13 @@
#include <ctype.h>
#include <stdarg.h>
-#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) && !defined(__OS2__)
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
# include <signal.h>
# include <pwd.h>
# include <unistd.h>
# include <sys/types.h>
#endif
-#ifdef __MACOS__
-# include <console.h>
-# include <signal.h>
-# include <unistd.h>
-# include <extras.h>
-# include <Files.h>
-# include <Folders.h>
-#endif
-
#ifdef __OS2__
# include <unistd.h>
#endif
@@ -61,6 +52,61 @@
extern int isatty();
#endif
+#if defined(_WIN32_WCE)
+/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty()
+ * thus we always assume that we have a console. That can be
+ * overridden with the -batch command line option.
+ */
+#define isatty(x) 1
+#endif
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
+#include <sys/time.h>
+#include <sys/resource.h>
+
+/* Saved resource information for the beginning of an operation */
+static struct rusage sBegin;
+
+/* True if the timer is enabled */
+static int enableTimer = 0;
+
+/*
+** Begin timing an operation
+*/
+static void beginTimer(void){
+ if( enableTimer ){
+ getrusage(RUSAGE_SELF, &sBegin);
+ }
+}
+
+/* Return the difference of two time_structs in seconds */
+static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
+ return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
+ (double)(pEnd->tv_sec - pStart->tv_sec);
+}
+
+/*
+** Print the timing results.
+*/
+static void endTimer(void){
+ if( enableTimer ){
+ struct rusage sEnd;
+ getrusage(RUSAGE_SELF, &sEnd);
+ printf("CPU Time: user %f sys %f\n",
+ timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
+ timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
+ }
+}
+#define BEGIN_TIMER beginTimer()
+#define END_TIMER endTimer()
+#define HAS_TIMER 1
+#else
+#define BEGIN_TIMER
+#define END_TIMER
+#define HAS_TIMER 0
+#endif
+
+
/*
** If the following flag is set, then command execution stops
** at an error if we are not interactive.
@@ -290,9 +336,9 @@ struct callback_data {
#define MODE_Insert 5 /* Generate SQL "insert" statements */
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
#define MODE_Csv 7 /* Quote strings, numbers are plain */
-#define MODE_NUM_OF 8 /* The number of modes (not a mode itself) */
+#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */
-static const char *modeDescr[MODE_NUM_OF] = {
+static const char *modeDescr[] = {
"line",
"column",
"list",
@@ -301,6 +347,7 @@ static const char *modeDescr[MODE_NUM_OF] = {
"insert",
"tcl",
"csv",
+ "explain",
};
/*
@@ -423,8 +470,11 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
fprintf(out,"%s",p->nullvalue);
}else{
int i;
+ int nSep = strlen(p->separator);
for(i=0; z[i]; i++){
- if( needCsvQuote[((unsigned char*)z)[i]] ){
+ if( needCsvQuote[((unsigned char*)z)[i]]
+ || (z[i]==p->separator[0] &&
+ (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
i = 0;
break;
}
@@ -441,7 +491,7 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
}
}
if( bSep ){
- fprintf(p->out, p->separator);
+ fprintf(p->out, "%s", p->separator);
}
}
@@ -477,14 +527,15 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
}
break;
}
+ case MODE_Explain:
case MODE_Column: {
if( p->cnt++==0 ){
for(i=0; i<nArg; i++){
int w, n;
if( i<ArraySize(p->colWidth) ){
- w = p->colWidth[i];
+ w = p->colWidth[i];
}else{
- w = 0;
+ w = 0;
}
if( w<=0 ){
w = strlen(azCol[i] ? azCol[i] : "");
@@ -521,6 +572,9 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
}else{
w = 10;
}
+ if( p->mode==MODE_Explain && azArg[i] && strlen(azArg[i])>w ){
+ w = strlen(azArg[i]);
+ }
fprintf(p->out,"%-*.*s%s",w,w,
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
}
@@ -884,6 +938,9 @@ static char zHelp[] =
".show Show the current values for various settings\n"
".tables ?PATTERN? List names of tables matching a LIKE pattern\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
+#if HAS_TIMER
+ ".timer ON|OFF Turn the CPU timer measurement on or off\n"
+#endif
".width NUM NUM ... Set column widths for \"column\" mode\n"
;
@@ -898,9 +955,11 @@ static void open_db(struct callback_data *p){
if( p->db==0 ){
sqlite3_open(p->zDbFilename, &p->db);
db = p->db;
- sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
- shellstaticFunc, 0, 0);
- if( SQLITE_OK!=sqlite3_errcode(db) ){
+ if( db && sqlite3_errcode(db)==SQLITE_OK ){
+ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
+ shellstaticFunc, 0, 0);
+ }
+ if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){
fprintf(stderr,"Unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(db));
exit(1);
@@ -1093,14 +1152,17 @@ static int do_meta_command(char *zLine, struct callback_data *p){
** did an .explain followed by a .width, .mode or .header
** command.
*/
- p->mode = MODE_Column;
+ p->mode = MODE_Explain;
p->showHeader = 1;
memset(p->colWidth,0,ArraySize(p->colWidth));
- p->colWidth[0] = 4;
- p->colWidth[1] = 14;
- p->colWidth[2] = 10;
- p->colWidth[3] = 10;
- p->colWidth[4] = 33;
+ p->colWidth[0] = 4; /* addr */
+ p->colWidth[1] = 13; /* opcode */
+ p->colWidth[2] = 4; /* P1 */
+ p->colWidth[3] = 4; /* P2 */
+ p->colWidth[4] = 4; /* P3 */
+ p->colWidth[5] = 13; /* P4 */
+ p->colWidth[6] = 2; /* P5 */
+ p->colWidth[7] = 13; /* Comment */
}else if (p->explainPrev.valid) {
p->explainPrev.valid = 0;
p->mode = p->explainPrev.mode;
@@ -1251,21 +1313,21 @@ static int do_meta_command(char *zLine, struct callback_data *p){
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){
- extern void (*sqlite3_io_trace)(const char*, ...);
+ extern void (*sqlite3IoTrace)(const char*, ...);
if( iotrace && iotrace!=stdout ) fclose(iotrace);
iotrace = 0;
if( nArg<2 ){
- sqlite3_io_trace = 0;
+ sqlite3IoTrace = 0;
}else if( strcmp(azArg[1], "-")==0 ){
- sqlite3_io_trace = iotracePrintf;
+ sqlite3IoTrace = iotracePrintf;
iotrace = stdout;
}else{
iotrace = fopen(azArg[1], "w");
if( iotrace==0 ){
fprintf(stderr, "cannot open \"%s\"\n", azArg[1]);
- sqlite3_io_trace = 0;
+ sqlite3IoTrace = 0;
}else{
- sqlite3_io_trace = iotracePrintf;
+ sqlite3IoTrace = iotracePrintf;
}
}
}else
@@ -1515,10 +1577,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_free_table(azResult);
}else
- if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
+ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
open_db(p);
sqlite3_busy_timeout(p->db, atoi(azArg[1]));
}else
+
+#if HAS_TIMER
+ if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg>1 ){
+ enableTimer = booleanValue(azArg[1]);
+ }else
+#endif
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
@@ -1528,6 +1596,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
+
{
fprintf(stderr, "unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
@@ -1537,12 +1606,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
/*
-** Return TRUE if the last non-whitespace character in z[] is a semicolon.
-** z[] is N characters long.
+** Return TRUE if a semicolon occurs anywhere in the first N characters
+** of string z[].
*/
-static int _ends_with_semicolon(const char *z, int N){
- while( N>0 && isspace((unsigned char)z[N-1]) ){ N--; }
- return N>0 && z[N-1]==';';
+static int _contains_semicolon(const char *z, int N){
+ int i;
+ for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
+ return 0;
}
/*
@@ -1597,6 +1667,7 @@ static int process_input(struct callback_data *p, FILE *in){
char *zLine = 0;
char *zSql = 0;
int nSql = 0;
+ int nSqlPrior = 0;
char *zErrMsg;
int rc;
int errCnt = 0;
@@ -1629,6 +1700,7 @@ static int process_input(struct callback_data *p, FILE *in){
if( _is_command_terminator(zLine) ){
memcpy(zLine,";",2);
}
+ nSqlPrior = nSql;
if( zSql==0 ){
int i;
for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
@@ -1653,10 +1725,13 @@ static int process_input(struct callback_data *p, FILE *in){
memcpy(&zSql[nSql], zLine, len+1);
nSql += len;
}
- if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){
+ if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
+ && sqlite3_complete(zSql) ){
p->cnt = 0;
open_db(p);
+ BEGIN_TIMER;
rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg);
+ END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
@@ -1696,7 +1771,7 @@ static int process_input(struct callback_data *p, FILE *in){
static char *find_home_dir(void){
char *home_dir = NULL;
-#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) && !defined(__OS2__)
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE)
struct passwd *pwent;
uid_t uid = getuid();
if( (pwent=getpwuid(uid)) != NULL) {
@@ -1704,10 +1779,11 @@ static char *find_home_dir(void){
}
#endif
-#ifdef __MACOS__
- char home_path[_MAX_PATH+1];
- home_dir = getcwd(home_path, _MAX_PATH);
-#endif
+#if defined(_WIN32_WCE)
+ /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
+ */
+ home_dir = strdup("/");
+#else
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
if (!home_dir) {
@@ -1736,6 +1812,8 @@ static char *find_home_dir(void){
}
#endif
+#endif /* !_WIN32_WCE */
+
if( home_dir ){
int n = strlen(home_dir) + 1;
char *z = malloc( n );
@@ -1840,17 +1918,17 @@ int main(int argc, char **argv){
int i;
int rc = 0;
- // Begin evanm patch.
+ /* Begin evanm patch. */
+#ifdef SQLITE_GEARS_DISABLE_SHELL_ICU
+ /* Gears doesn't use this. */
+#else
extern int sqlite_shell_init_icu();
if( !sqlite_shell_init_icu() ){
fprintf(stderr, "%s: warning: couldn't find icudt38.dll; "
"queries against ICU FTS tables will fail.\n", argv[0]);
}
- // End evanm patch.
-
-#ifdef __MACOS__
- argc = ccommand(&argv);
#endif
+ /* End evanm patch. */
Argv0 = argv[0];
main_init(&data);
@@ -1880,7 +1958,11 @@ int main(int argc, char **argv){
}
}
if( i<argc ){
+#if defined(SQLITE_OS_OS2) && SQLITE_OS_OS2
+ data.zDbFilename = (const char *)convertCpPathToUtf8( argv[i++] );
+#else
data.zDbFilename = argv[i++];
+#endif
}else{
#ifndef SQLITE_OMIT_MEMORYDB
data.zDbFilename = ":memory:";
@@ -1992,7 +2074,8 @@ int main(int argc, char **argv){
int nHistory;
printf(
"SQLite version %s\n"
- "Enter \".help\" for instructions\n",
+ "Enter \".help\" for instructions\n"
+ "Enter SQL statements terminated with a \";\"\n",
sqlite3_libversion()
);
zHome = find_home_dir();
diff --git a/third_party/sqlite/shell_icu.c b/third_party/sqlite/src/shell_icu.c
index b4eb142..d68cf8f 100644..100755
--- a/third_party/sqlite/shell_icu.c
+++ b/third_party/sqlite/src/shell_icu.c
@@ -1,11 +1,14 @@
-// Copyright 2007 Google Inc. All Rights Reserved.
+/* Copyright 2007 Google Inc. All Rights Reserved.
+**/
#include <windows.h>
#include "unicode/udata.h"
-// This function attempts to load the ICU data tables from a DLL.
-// Returns 0 on failure, nonzero on success.
-// This a hack job of icu_utils.cc:Initialize(). It's Chrome-specific code.
+/*
+** This function attempts to load the ICU data tables from a DLL.
+** Returns 0 on failure, nonzero on success.
+** This a hack job of icu_utils.cc:Initialize(). It's Chrome-specific code.
+*/
int sqlite_shell_init_icu() {
HMODULE module;
FARPROC addr;
diff --git a/third_party/sqlite/src/sqlite.h.in b/third_party/sqlite/src/sqlite.h.in
new file mode 100755
index 0000000..d37b251
--- /dev/null
+++ b/third_party/sqlite/src/sqlite.h.in
@@ -0,0 +1,6294 @@
+/*
+** 2001 September 15
+**
+** 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 header file defines the interface that the SQLite library
+** presents to client programs. If a C-function, structure, datatype,
+** or constant definition does not appear in this file, then it is
+** not a published API of SQLite, is subject to change without
+** notice, and should not be referenced by programs that use SQLite.
+**
+** Some of the definitions that are in this file are marked as
+** "experimental". Experimental interfaces are normally new
+** features recently added to SQLite. We do not anticipate changes
+** to experimental interfaces but reserve to make minor changes if
+** experience from use "in the wild" suggest such changes are prudent.
+**
+** The official C-language API documentation for SQLite is derived
+** from comments in this file. This file is the authoritative source
+** on how SQLite interfaces are suppose to operate.
+**
+** The name of this file under configuration management is "sqlite.h.in".
+** The makefile makes some minor changes to this file (such as inserting
+** the version number) and changes its name to "sqlite3.h" as
+** part of the build process.
+**
+** @(#) $Id: sqlite.h.in,v 1.388 2008/08/06 13:40:13 danielk1977 Exp $
+*/
+#ifndef _SQLITE3_H_
+#define _SQLITE3_H_
+#include <stdarg.h> /* Needed for the definition of va_list */
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** Add the ability to override 'extern'
+*/
+#ifndef SQLITE_EXTERN
+# define SQLITE_EXTERN extern
+#endif
+
+/*
+** Ensure these symbols were not defined by some previous header file.
+*/
+#ifdef SQLITE_VERSION
+# undef SQLITE_VERSION
+#endif
+#ifdef SQLITE_VERSION_NUMBER
+# undef SQLITE_VERSION_NUMBER
+#endif
+
+/*
+** CAPI3REF: Compile-Time Library Version Numbers {H10010} <S60100>
+**
+** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in
+** the sqlite3.h file specify the version of SQLite with which
+** that header file is associated.
+**
+** The "version" of SQLite is a string of the form "X.Y.Z".
+** The phrase "alpha" or "beta" might be appended after the Z.
+** The X value is major version number always 3 in SQLite3.
+** The X value only changes when backwards compatibility is
+** broken and we intend to never break backwards compatibility.
+** The Y value is the minor version number and only changes when
+** there are major feature enhancements that are forwards compatible
+** but not backwards compatible.
+** The Z value is the release number and is incremented with
+** each release but resets back to 0 whenever Y is incremented.
+**
+** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()].
+**
+** INVARIANTS:
+**
+** {H10011} The SQLITE_VERSION #define in the sqlite3.h header file shall
+** evaluate to a string literal that is the SQLite version
+** with which the header file is associated.
+**
+** {H10014} The SQLITE_VERSION_NUMBER #define shall resolve to an integer
+** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z
+** are the major version, minor version, and release number.
+*/
+#define SQLITE_VERSION "--VERS--"
+#define SQLITE_VERSION_NUMBER --VERSION-NUMBER--
+
+/*
+** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
+** KEYWORDS: sqlite3_version
+**
+** These features provide the same information as the [SQLITE_VERSION]
+** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated
+** with the library instead of the header file. Cautious programmers might
+** include a check in their application to verify that
+** sqlite3_libversion_number() always returns the value
+** [SQLITE_VERSION_NUMBER].
+**
+** The sqlite3_libversion() function returns the same information as is
+** in the sqlite3_version[] string constant. The function is provided
+** for use in DLLs since DLL users usually do not have direct access to string
+** constants within the DLL.
+**
+** INVARIANTS:
+**
+** {H10021} The [sqlite3_libversion_number()] interface shall return
+** an integer equal to [SQLITE_VERSION_NUMBER].
+**
+** {H10022} The [sqlite3_version] string constant shall contain
+** the text of the [SQLITE_VERSION] string.
+**
+** {H10023} The [sqlite3_libversion()] function shall return
+** a pointer to the [sqlite3_version] string constant.
+*/
+SQLITE_EXTERN const char sqlite3_version[];
+const char *sqlite3_libversion(void);
+int sqlite3_libversion_number(void);
+
+/*
+** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} <S60100>
+**
+** SQLite can be compiled with or without mutexes. When
+** the [SQLITE_THREADSAFE] C preprocessor macro is true, mutexes
+** are enabled and SQLite is threadsafe. When that macro is false,
+** the mutexes are omitted. Without the mutexes, it is not safe
+** to use SQLite concurrently from more than one thread.
+**
+** Enabling mutexes incurs a measurable performance penalty.
+** So if speed is of utmost importance, it makes sense to disable
+** the mutexes. But for maximum safety, mutexes should be enabled.
+** The default behavior is for mutexes to be enabled.
+**
+** This interface can be used by a program to make sure that the
+** version of SQLite that it is linking against was compiled with
+** the desired setting of the [SQLITE_THREADSAFE] macro.
+**
+** This interface only reports on the compile-time mutex setting
+** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with
+** SQLITE_THREADSAFE=1 then mutexes are enabled by default but
+** can be fully or partially disabled using a call to [sqlite3_config()]
+** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],
+** or [SQLITE_CONFIG_MUTEX]. The return value of this function shows
+** only the default compile-time setting, not any run-time changes
+** to that setting.
+**
+** INVARIANTS:
+**
+** {H10101} The [sqlite3_threadsafe()] function shall return nonzero if
+** SQLite was compiled with the its mutexes enabled by default
+** or zero if SQLite was compiled such that mutexes are
+** permanently disabled.
+**
+** {H10102} The value returned by the [sqlite3_threadsafe()] function
+** shall not change when mutex setting are modified at
+** runtime using the [sqlite3_config()] interface and
+** especially the [SQLITE_CONFIG_SINGLETHREAD],
+** [SQLITE_CONFIG_MULTITHREAD], [SQLITE_CONFIG_SERIALIZED],
+** and [SQLITE_CONFIG_MUTEX] verbs.
+*/
+int sqlite3_threadsafe(void);
+
+/*
+** CAPI3REF: Database Connection Handle {H12000} <S40200>
+** KEYWORDS: {database connection} {database connections}
+**
+** Each open SQLite database is represented by a pointer to an instance of
+** the opaque structure named "sqlite3". It is useful to think of an sqlite3
+** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
+** is its destructor. There are many other interfaces (such as
+** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
+** [sqlite3_busy_timeout()] to name but three) that are methods on an
+** sqlite3 object.
+*/
+typedef struct sqlite3 sqlite3;
+
+/*
+** CAPI3REF: 64-Bit Integer Types {H10200} <S10110>
+** KEYWORDS: sqlite_int64 sqlite_uint64
+**
+** Because there is no cross-platform way to specify 64-bit integer types
+** SQLite includes typedefs for 64-bit signed and unsigned integers.
+**
+** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions.
+** The sqlite_int64 and sqlite_uint64 types are supported for backwards
+** compatibility only.
+**
+** INVARIANTS:
+**
+** {H10201} The [sqlite_int64] and [sqlite3_int64] type shall specify
+** a 64-bit signed integer.
+**
+** {H10202} The [sqlite_uint64] and [sqlite3_uint64] type shall specify
+** a 64-bit unsigned integer.
+*/
+#ifdef SQLITE_INT64_TYPE
+ typedef SQLITE_INT64_TYPE sqlite_int64;
+ typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
+#elif defined(_MSC_VER) || defined(__BORLANDC__)
+ typedef __int64 sqlite_int64;
+ typedef unsigned __int64 sqlite_uint64;
+#else
+ typedef long long int sqlite_int64;
+ typedef unsigned long long int sqlite_uint64;
+#endif
+typedef sqlite_int64 sqlite3_int64;
+typedef sqlite_uint64 sqlite3_uint64;
+
+/*
+** If compiling for a processor that lacks floating point support,
+** substitute integer for floating-point.
+*/
+#ifdef SQLITE_OMIT_FLOATING_POINT
+# define double sqlite3_int64
+#endif
+
+/*
+** CAPI3REF: Closing A Database Connection {H12010} <S30100><S40200>
+**
+** This routine is the destructor for the [sqlite3] object.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements]
+** and [sqlite3_blob_close | close] all [BLOB handles] associated with
+** the [sqlite3] object prior to attempting to close the object.
+** The [sqlite3_next_stmt()] interface can be used to locate all
+** [prepared statements] associated with a [database connection] if desired.
+** Typical code might look like this:
+**
+** <blockquote><pre>
+** sqlite3_stmt *pStmt;
+** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
+** &nbsp; sqlite3_finalize(pStmt);
+** }
+** </pre></blockquote>
+**
+** If [sqlite3_close()] is invoked while a transaction is open,
+** the transaction is automatically rolled back.
+**
+** INVARIANTS:
+**
+** {H12011} A successful call to [sqlite3_close(C)] shall destroy the
+** [database connection] object C.
+**
+** {H12012} A successful call to [sqlite3_close(C)] shall return SQLITE_OK.
+**
+** {H12013} A successful call to [sqlite3_close(C)] shall release all
+** memory and system resources associated with [database connection]
+** C.
+**
+** {H12014} A call to [sqlite3_close(C)] on a [database connection] C that
+** has one or more open [prepared statements] shall fail with
+** an [SQLITE_BUSY] error code.
+**
+** {H12015} A call to [sqlite3_close(C)] where C is a NULL pointer shall
+** return SQLITE_OK.
+**
+** {H12019} When [sqlite3_close(C)] is invoked on a [database connection] C
+** that has a pending transaction, the transaction shall be
+** rolled back.
+**
+** ASSUMPTIONS:
+**
+** {A12016} The C parameter to [sqlite3_close(C)] must be either a NULL
+** pointer or an [sqlite3] object pointer obtained
+** from [sqlite3_open()], [sqlite3_open16()], or
+** [sqlite3_open_v2()], and not previously closed.
+*/
+int sqlite3_close(sqlite3 *);
+
+/*
+** The type for a callback function.
+** This is legacy and deprecated. It is included for historical
+** compatibility and is not documented.
+*/
+typedef int (*sqlite3_callback)(void*,int,char**, char**);
+
+/*
+** CAPI3REF: One-Step Query Execution Interface {H12100} <S10000>
+**
+** The sqlite3_exec() interface is a convenient way of running one or more
+** SQL statements without having to write a lot of C code. The UTF-8 encoded
+** SQL statements are passed in as the second parameter to sqlite3_exec().
+** The statements are evaluated one by one until either an error or
+** an interrupt is encountered, or until they are all done. The 3rd parameter
+** is an optional callback that is invoked once for each row of any query
+** results produced by the SQL statements. The 5th parameter tells where
+** to write any error messages.
+**
+** The error message passed back through the 5th parameter is held
+** in memory obtained from [sqlite3_malloc()]. To avoid a memory leak,
+** the calling application should call [sqlite3_free()] on any error
+** message returned through the 5th parameter when it has finished using
+** the error message.
+**
+** If the SQL statement in the 2nd parameter is NULL or an empty string
+** or a string containing only whitespace and comments, then no SQL
+** statements are evaluated and the database is not changed.
+**
+** The sqlite3_exec() interface is implemented in terms of
+** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()].
+** The sqlite3_exec() routine does nothing to the database that cannot be done
+** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()].
+**
+** INVARIANTS:
+**
+** {H12101} A successful invocation of [sqlite3_exec(D,S,C,A,E)]
+** shall sequentially evaluate all of the UTF-8 encoded,
+** semicolon-separated SQL statements in the zero-terminated
+** string S within the context of the [database connection] D.
+**
+** {H12102} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL then
+** the actions of the interface shall be the same as if the
+** S parameter were an empty string.
+**
+** {H12104} The return value of [sqlite3_exec()] shall be [SQLITE_OK] if all
+** SQL statements run successfully and to completion.
+**
+** {H12105} The return value of [sqlite3_exec()] shall be an appropriate
+** non-zero [error code] if any SQL statement fails.
+**
+** {H12107} If one or more of the SQL statements handed to [sqlite3_exec()]
+** return results and the 3rd parameter is not NULL, then
+** the callback function specified by the 3rd parameter shall be
+** invoked once for each row of result.
+**
+** {H12110} If the callback returns a non-zero value then [sqlite3_exec()]
+** shall abort the SQL statement it is currently evaluating,
+** skip all subsequent SQL statements, and return [SQLITE_ABORT].
+**
+** {H12113} The [sqlite3_exec()] routine shall pass its 4th parameter through
+** as the 1st parameter of the callback.
+**
+** {H12116} The [sqlite3_exec()] routine shall set the 2nd parameter of its
+** callback to be the number of columns in the current row of
+** result.
+**
+** {H12119} The [sqlite3_exec()] routine shall set the 3rd parameter of its
+** callback to be an array of pointers to strings holding the
+** values for each column in the current result set row as
+** obtained from [sqlite3_column_text()].
+**
+** {H12122} The [sqlite3_exec()] routine shall set the 4th parameter of its
+** callback to be an array of pointers to strings holding the
+** names of result columns as obtained from [sqlite3_column_name()].
+**
+** {H12125} If the 3rd parameter to [sqlite3_exec()] is NULL then
+** [sqlite3_exec()] shall silently discard query results.
+**
+** {H12131} If an error occurs while parsing or evaluating any of the SQL
+** statements in the S parameter of [sqlite3_exec(D,S,C,A,E)] and if
+** the E parameter is not NULL, then [sqlite3_exec()] shall store
+** in *E an appropriate error message written into memory obtained
+** from [sqlite3_malloc()].
+**
+** {H12134} The [sqlite3_exec(D,S,C,A,E)] routine shall set the value of
+** *E to NULL if E is not NULL and there are no errors.
+**
+** {H12137} The [sqlite3_exec(D,S,C,A,E)] function shall set the [error code]
+** and message accessible via [sqlite3_errcode()],
+** [sqlite3_errmsg()], and [sqlite3_errmsg16()].
+**
+** {H12138} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL or an
+** empty string or contains nothing other than whitespace, comments,
+** and/or semicolons, then results of [sqlite3_errcode()],
+** [sqlite3_errmsg()], and [sqlite3_errmsg16()]
+** shall reset to indicate no errors.
+**
+** ASSUMPTIONS:
+**
+** {A12141} The first parameter to [sqlite3_exec()] must be an valid and open
+** [database connection].
+**
+** {A12142} The database connection must not be closed while
+** [sqlite3_exec()] is running.
+**
+** {A12143} The calling function should use [sqlite3_free()] to free
+** the memory that *errmsg is left pointing at once the error
+** message is no longer needed.
+**
+** {A12145} The SQL statement text in the 2nd parameter to [sqlite3_exec()]
+** must remain unchanged while [sqlite3_exec()] is running.
+*/
+int sqlite3_exec(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be evaluated */
+ int (*callback)(void*,int,char**,char**), /* Callback function */
+ void *, /* 1st argument to callback */
+ char **errmsg /* Error msg written here */
+);
+
+/*
+** CAPI3REF: Result Codes {H10210} <S10700>
+** KEYWORDS: SQLITE_OK {error code} {error codes}
+** KEYWORDS: {result code} {result codes}
+**
+** Many SQLite functions return an integer result code from the set shown
+** here in order to indicates success or failure.
+**
+** New error codes may be added in future versions of SQLite.
+**
+** See also: [SQLITE_IOERR_READ | extended result codes]
+*/
+#define SQLITE_OK 0 /* Successful result */
+/* beginning-of-error-codes */
+#define SQLITE_ERROR 1 /* SQL error or missing database */
+#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */
+#define SQLITE_PERM 3 /* Access permission denied */
+#define SQLITE_ABORT 4 /* Callback routine requested an abort */
+#define SQLITE_BUSY 5 /* The database file is locked */
+#define SQLITE_LOCKED 6 /* A table in the database is locked */
+#define SQLITE_NOMEM 7 /* A malloc() failed */
+#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
+#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
+#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
+#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
+#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */
+#define SQLITE_FULL 13 /* Insertion failed because database is full */
+#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
+#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */
+#define SQLITE_EMPTY 16 /* Database is empty */
+#define SQLITE_SCHEMA 17 /* The database schema changed */
+#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
+#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
+#define SQLITE_MISMATCH 20 /* Data type mismatch */
+#define SQLITE_MISUSE 21 /* Library used incorrectly */
+#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
+#define SQLITE_AUTH 23 /* Authorization denied */
+#define SQLITE_FORMAT 24 /* Auxiliary database format error */
+#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
+#define SQLITE_NOTADB 26 /* File opened that is not a database file */
+#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
+#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
+/* end-of-error-codes */
+
+/*
+** CAPI3REF: Extended Result Codes {H10220} <S10700>
+** KEYWORDS: {extended error code} {extended error codes}
+** KEYWORDS: {extended result code} {extended result codes}
+**
+** In its default configuration, SQLite API routines return one of 26 integer
+** [SQLITE_OK | result codes]. However, experience has shown that many of
+** these result codes are too coarse-grained. They do not provide as
+** much information about problems as programmers might like. In an effort to
+** address this, newer versions of SQLite (version 3.3.8 and later) include
+** support for additional result codes that provide more detailed information
+** about errors. The extended result codes are enabled or disabled
+** on a per database connection basis using the
+** [sqlite3_extended_result_codes()] API.
+**
+** Some of the available extended result codes are listed here.
+** One may expect the number of extended result codes will be expand
+** over time. Software that uses extended result codes should expect
+** to see new result codes in future releases of SQLite.
+**
+** The SQLITE_OK result code will never be extended. It will always
+** be exactly zero.
+**
+** INVARIANTS:
+**
+** {H10223} The symbolic name for an extended result code shall contains
+** a related primary result code as a prefix.
+**
+** {H10224} Primary result code names shall contain a single "_" character.
+**
+** {H10225} Extended result code names shall contain two or more "_" characters.
+**
+** {H10226} The numeric value of an extended result code shall contain the
+** numeric value of its corresponding primary result code in
+** its least significant 8 bits.
+*/
+#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
+#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
+#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
+#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
+#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
+#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8))
+#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8))
+#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8))
+#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8))
+#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8))
+#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8))
+#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
+#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
+#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
+
+/*
+** CAPI3REF: Flags For File Open Operations {H10230} <H11120> <H12700>
+**
+** These bit values are intended for use in the
+** 3rd parameter to the [sqlite3_open_v2()] interface and
+** in the 4th parameter to the xOpen method of the
+** [sqlite3_vfs] object.
+*/
+#define SQLITE_OPEN_READONLY 0x00000001
+#define SQLITE_OPEN_READWRITE 0x00000002
+#define SQLITE_OPEN_CREATE 0x00000004
+#define SQLITE_OPEN_DELETEONCLOSE 0x00000008
+#define SQLITE_OPEN_EXCLUSIVE 0x00000010
+#define SQLITE_OPEN_MAIN_DB 0x00000100
+#define SQLITE_OPEN_TEMP_DB 0x00000200
+#define SQLITE_OPEN_TRANSIENT_DB 0x00000400
+#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800
+#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000
+#define SQLITE_OPEN_SUBJOURNAL 0x00002000
+#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000
+#define SQLITE_OPEN_NOMUTEX 0x00008000
+
+/*
+** CAPI3REF: Device Characteristics {H10240} <H11120>
+**
+** The xDeviceCapabilities method of the [sqlite3_io_methods]
+** object returns an integer which is a vector of the these
+** bit values expressing I/O characteristics of the mass storage
+** device that holds the file that the [sqlite3_io_methods]
+** refers to.
+**
+** The SQLITE_IOCAP_ATOMIC property means that all writes of
+** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().
+*/
+#define SQLITE_IOCAP_ATOMIC 0x00000001
+#define SQLITE_IOCAP_ATOMIC512 0x00000002
+#define SQLITE_IOCAP_ATOMIC1K 0x00000004
+#define SQLITE_IOCAP_ATOMIC2K 0x00000008
+#define SQLITE_IOCAP_ATOMIC4K 0x00000010
+#define SQLITE_IOCAP_ATOMIC8K 0x00000020
+#define SQLITE_IOCAP_ATOMIC16K 0x00000040
+#define SQLITE_IOCAP_ATOMIC32K 0x00000080
+#define SQLITE_IOCAP_ATOMIC64K 0x00000100
+#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
+#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
+
+/*
+** CAPI3REF: File Locking Levels {H10250} <H11120> <H11310>
+**
+** SQLite uses one of these integer values as the second
+** argument to calls it makes to the xLock() and xUnlock() methods
+** of an [sqlite3_io_methods] object.
+*/
+#define SQLITE_LOCK_NONE 0
+#define SQLITE_LOCK_SHARED 1
+#define SQLITE_LOCK_RESERVED 2
+#define SQLITE_LOCK_PENDING 3
+#define SQLITE_LOCK_EXCLUSIVE 4
+
+/*
+** CAPI3REF: Synchronization Type Flags {H10260} <H11120>
+**
+** When SQLite invokes the xSync() method of an
+** [sqlite3_io_methods] object it uses a combination of
+** these integer values as the second argument.
+**
+** When the SQLITE_SYNC_DATAONLY flag is used, it means that the
+** sync operation only needs to flush data to mass storage. Inode
+** information need not be flushed. The SQLITE_SYNC_NORMAL flag means
+** to use normal fsync() semantics. The SQLITE_SYNC_FULL flag means
+** to use Mac OS-X style fullsync instead of fsync().
+*/
+#define SQLITE_SYNC_NORMAL 0x00002
+#define SQLITE_SYNC_FULL 0x00003
+#define SQLITE_SYNC_DATAONLY 0x00010
+
+/*
+** CAPI3REF: OS Interface Open File Handle {H11110} <S20110>
+**
+** An [sqlite3_file] object represents an open file in the OS
+** interface layer. Individual OS interface implementations will
+** want to subclass this object by appending additional fields
+** for their own use. The pMethods entry is a pointer to an
+** [sqlite3_io_methods] object that defines methods for performing
+** I/O operations on the open file.
+*/
+typedef struct sqlite3_file sqlite3_file;
+struct sqlite3_file {
+ const struct sqlite3_io_methods *pMethods; /* Methods for an open file */
+};
+
+/*
+** CAPI3REF: OS Interface File Virtual Methods Object {H11120} <S20110>
+**
+** Every file opened by the [sqlite3_vfs] xOpen method populates an
+** [sqlite3_file] object (or, more commonly, a subclass of the
+** [sqlite3_file] object) with a pointer to an instance of this object.
+** This object defines the methods used to perform various operations
+** against the open file represented by the [sqlite3_file] object.
+**
+** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
+** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
+** The second choice is a Mac OS-X style fullsync. The [SQLITE_SYNC_DATAONLY]
+** flag may be ORed in to indicate that only the data of the file
+** and not its inode needs to be synced.
+**
+** The integer values to xLock() and xUnlock() are one of
+** <ul>
+** <li> [SQLITE_LOCK_NONE],
+** <li> [SQLITE_LOCK_SHARED],
+** <li> [SQLITE_LOCK_RESERVED],
+** <li> [SQLITE_LOCK_PENDING], or
+** <li> [SQLITE_LOCK_EXCLUSIVE].
+** </ul>
+** xLock() increases the lock. xUnlock() decreases the lock.
+** The xCheckReservedLock() method checks whether any database connection,
+** either in this process or in some other process, is holding a RESERVED,
+** PENDING, or EXCLUSIVE lock on the file. It returns true
+** if such a lock exists and false otherwise.
+**
+** The xFileControl() method is a generic interface that allows custom
+** VFS implementations to directly control an open file using the
+** [sqlite3_file_control()] interface. The second "op" argument is an
+** integer opcode. The third argument is a generic pointer intended to
+** point to a structure that may contain arguments or space in which to
+** write return values. Potential uses for xFileControl() might be
+** functions to enable blocking locks with timeouts, to change the
+** locking strategy (for example to use dot-file locks), to inquire
+** about the status of a lock, or to break stale locks. The SQLite
+** core reserves all opcodes less than 100 for its own use.
+** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
+** Applications that define a custom xFileControl method should use opcodes
+** greater than 100 to avoid conflicts.
+**
+** The xSectorSize() method returns the sector size of the
+** device that underlies the file. The sector size is the
+** minimum write that can be performed without disturbing
+** other bytes in the file. The xDeviceCharacteristics()
+** method returns a bit vector describing behaviors of the
+** underlying device:
+**
+** <ul>
+** <li> [SQLITE_IOCAP_ATOMIC]
+** <li> [SQLITE_IOCAP_ATOMIC512]
+** <li> [SQLITE_IOCAP_ATOMIC1K]
+** <li> [SQLITE_IOCAP_ATOMIC2K]
+** <li> [SQLITE_IOCAP_ATOMIC4K]
+** <li> [SQLITE_IOCAP_ATOMIC8K]
+** <li> [SQLITE_IOCAP_ATOMIC16K]
+** <li> [SQLITE_IOCAP_ATOMIC32K]
+** <li> [SQLITE_IOCAP_ATOMIC64K]
+** <li> [SQLITE_IOCAP_SAFE_APPEND]
+** <li> [SQLITE_IOCAP_SEQUENTIAL]
+** </ul>
+**
+** The SQLITE_IOCAP_ATOMIC property means that all writes of
+** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().
+*/
+typedef struct sqlite3_io_methods sqlite3_io_methods;
+struct sqlite3_io_methods {
+ int iVersion;
+ int (*xClose)(sqlite3_file*);
+ int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+ int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+ int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);
+ int (*xSync)(sqlite3_file*, int flags);
+ int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
+ int (*xLock)(sqlite3_file*, int);
+ int (*xUnlock)(sqlite3_file*, int);
+ int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);
+ int (*xFileControl)(sqlite3_file*, int op, void *pArg);
+ int (*xSectorSize)(sqlite3_file*);
+ int (*xDeviceCharacteristics)(sqlite3_file*);
+ /* Additional methods may be added in future releases */
+};
+
+/*
+** CAPI3REF: Standard File Control Opcodes {H11310} <S30800>
+**
+** These integer constants are opcodes for the xFileControl method
+** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]
+** interface.
+**
+** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This
+** opcode causes the xFileControl method to write the current state of
+** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
+** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
+** into an integer that the pArg argument points to. This capability
+** is used during testing and only needs to be supported when SQLITE_TEST
+** is defined.
+*/
+#define SQLITE_FCNTL_LOCKSTATE 1
+
+/*
+** CAPI3REF: Mutex Handle {H17110} <S20130>
+**
+** The mutex module within SQLite defines [sqlite3_mutex] to be an
+** abstract type for a mutex object. The SQLite core never looks
+** at the internal representation of an [sqlite3_mutex]. It only
+** deals with pointers to the [sqlite3_mutex] object.
+**
+** Mutexes are created using [sqlite3_mutex_alloc()].
+*/
+typedef struct sqlite3_mutex sqlite3_mutex;
+
+/*
+** CAPI3REF: OS Interface Object {H11140} <S20100>
+**
+** An instance of the sqlite3_vfs object defines the interface between
+** the SQLite core and the underlying operating system. The "vfs"
+** in the name of the object stands for "virtual file system".
+**
+** The value of the iVersion field is initially 1 but may be larger in
+** future versions of SQLite. Additional fields may be appended to this
+** object when the iVersion value is increased. Note that the structure
+** of the sqlite3_vfs object changes in the transaction between
+** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
+** modified.
+**
+** The szOsFile field is the size of the subclassed [sqlite3_file]
+** structure used by this VFS. mxPathname is the maximum length of
+** a pathname in this VFS.
+**
+** Registered sqlite3_vfs objects are kept on a linked list formed by
+** the pNext pointer. The [sqlite3_vfs_register()]
+** and [sqlite3_vfs_unregister()] interfaces manage this list
+** in a thread-safe way. The [sqlite3_vfs_find()] interface
+** searches the list. Neither the application code nor the VFS
+** implementation should use the pNext pointer.
+**
+** The pNext field is the only field in the sqlite3_vfs
+** structure that SQLite will ever modify. SQLite will only access
+** or modify this field while holding a particular static mutex.
+** The application should never modify anything within the sqlite3_vfs
+** object once the object has been registered.
+**
+** The zName field holds the name of the VFS module. The name must
+** be unique across all VFS modules.
+**
+** {H11141} SQLite will guarantee that the zFilename parameter to xOpen
+** is either a NULL pointer or string obtained
+** from xFullPathname(). SQLite further guarantees that
+** the string will be valid and unchanged until xClose() is
+** called. {END} Because of the previous sentense,
+** the [sqlite3_file] can safely store a pointer to the
+** filename if it needs to remember the filename for some reason.
+** If the zFilename parameter is xOpen is a NULL pointer then xOpen
+** must invite its own temporary name for the file. Whenever the
+** xFilename parameter is NULL it will also be the case that the
+** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].
+**
+** {H11142} The flags argument to xOpen() includes all bits set in
+** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()]
+** or [sqlite3_open16()] is used, then flags includes at least
+** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. {END}
+** If xOpen() opens a file read-only then it sets *pOutFlags to
+** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set.
+**
+** {H11143} SQLite will also add one of the following flags to the xOpen()
+** call, depending on the object being opened:
+**
+** <ul>
+** <li> [SQLITE_OPEN_MAIN_DB]
+** <li> [SQLITE_OPEN_MAIN_JOURNAL]
+** <li> [SQLITE_OPEN_TEMP_DB]
+** <li> [SQLITE_OPEN_TEMP_JOURNAL]
+** <li> [SQLITE_OPEN_TRANSIENT_DB]
+** <li> [SQLITE_OPEN_SUBJOURNAL]
+** <li> [SQLITE_OPEN_MASTER_JOURNAL]
+** </ul> {END}
+**
+** The file I/O implementation can use the object type flags to
+** change the way it deals with files. For example, an application
+** that does not care about crash recovery or rollback might make
+** the open of a journal file a no-op. Writes to this journal would
+** also be no-ops, and any attempt to read the journal would return
+** SQLITE_IOERR. Or the implementation might recognize that a database
+** file will be doing page-aligned sector reads and writes in a random
+** order and set up its I/O subsystem accordingly.
+**
+** SQLite might also add one of the following flags to the xOpen method:
+**
+** <ul>
+** <li> [SQLITE_OPEN_DELETEONCLOSE]
+** <li> [SQLITE_OPEN_EXCLUSIVE]
+** </ul>
+**
+** {H11145} The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be
+** deleted when it is closed. {H11146} The [SQLITE_OPEN_DELETEONCLOSE]
+** will be set for TEMP databases, journals and for subjournals.
+**
+** {H11147} The [SQLITE_OPEN_EXCLUSIVE] flag means the file should be opened
+** for exclusive access. This flag is set for all files except
+** for the main database file.
+**
+** {H11148} At least szOsFile bytes of memory are allocated by SQLite
+** to hold the [sqlite3_file] structure passed as the third
+** argument to xOpen. {END} The xOpen method does not have to
+** allocate the structure; it should just fill it in.
+**
+** {H11149} The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
+** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
+** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
+** to test whether a file is at least readable. {END} The file can be a
+** directory.
+**
+** {H11150} SQLite will always allocate at least mxPathname+1 bytes for the
+** output buffer xFullPathname. {H11151} The exact size of the output buffer
+** is also passed as a parameter to both methods. {END} If the output buffer
+** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is
+** handled as a fatal error by SQLite, vfs implementations should endeavor
+** to prevent this by setting mxPathname to a sufficiently large value.
+**
+** The xRandomness(), xSleep(), and xCurrentTime() interfaces
+** are not strictly a part of the filesystem, but they are
+** included in the VFS structure for completeness.
+** The xRandomness() function attempts to return nBytes bytes
+** of good-quality randomness into zOut. The return value is
+** the actual number of bytes of randomness obtained.
+** The xSleep() method causes the calling thread to sleep for at
+** least the number of microseconds given. The xCurrentTime()
+** method returns a Julian Day Number for the current date and time.
+*/
+typedef struct sqlite3_vfs sqlite3_vfs;
+struct sqlite3_vfs {
+ int iVersion; /* Structure version number */
+ int szOsFile; /* Size of subclassed sqlite3_file */
+ int mxPathname; /* Maximum file pathname length */
+ sqlite3_vfs *pNext; /* Next registered VFS */
+ const char *zName; /* Name of this virtual file system */
+ void *pAppData; /* Pointer to application-specific data */
+ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int flags, int *pOutFlags);
+ int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
+ int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
+ int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
+ void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
+ void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
+ void *(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol);
+ void (*xDlClose)(sqlite3_vfs*, void*);
+ int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
+ int (*xSleep)(sqlite3_vfs*, int microseconds);
+ int (*xCurrentTime)(sqlite3_vfs*, double*);
+ int (*xGetLastError)(sqlite3_vfs*, int, char *);
+ /* New fields may be appended in figure versions. The iVersion
+ ** value will increment whenever this happens. */
+};
+
+/*
+** CAPI3REF: Flags for the xAccess VFS method {H11190} <H11140>
+**
+** {H11191} These integer constants can be used as the third parameter to
+** the xAccess method of an [sqlite3_vfs] object. {END} They determine
+** what kind of permissions the xAccess method is looking for.
+** {H11192} With SQLITE_ACCESS_EXISTS, the xAccess method
+** simply checks whether the file exists.
+** {H11193} With SQLITE_ACCESS_READWRITE, the xAccess method
+** checks whether the file is both readable and writable.
+** {H11194} With SQLITE_ACCESS_READ, the xAccess method
+** checks whether the file is readable.
+*/
+#define SQLITE_ACCESS_EXISTS 0
+#define SQLITE_ACCESS_READWRITE 1
+#define SQLITE_ACCESS_READ 2
+
+/*
+** CAPI3REF: Initialize The SQLite Library {H10130} <S20000><S30100>
+**
+** The sqlite3_initialize() routine initializes the
+** SQLite library. The sqlite3_shutdown() routine
+** deallocates any resources that were allocated by sqlite3_initialize().
+**
+** A call to sqlite3_initialize() is an "effective" call if it is
+** the first time sqlite3_initialize() is invoked during the lifetime of
+** the process, or if it is the first time sqlite3_initialize() is invoked
+** following a call to sqlite3_shutdown(). Only an effective call
+** of sqlite3_initialize() does any initialization. All other calls
+** are harmless no-ops.
+**
+** Among other things, sqlite3_initialize() shall invoke
+** sqlite3_os_init(). Similarly, sqlite3_shutdown()
+** shall invoke sqlite3_os_end().
+**
+** The sqlite3_initialize() routine returns SQLITE_OK on success.
+** If for some reason, sqlite3_initialize() is unable to initialize
+** the library (perhaps it is unable to allocate a needed resource such
+** as a mutex) it returns an [error code] other than SQLITE_OK.
+**
+** The sqlite3_initialize() routine is called internally by many other
+** SQLite interfaces so that an application usually does not need to
+** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
+** calls sqlite3_initialize() so the SQLite library will be automatically
+** initialized when [sqlite3_open()] is called if it has not be initialized
+** already. However, if SQLite is compiled with the SQLITE_OMIT_AUTOINIT
+** compile-time option, then the automatic calls to sqlite3_initialize()
+** are omitted and the application must call sqlite3_initialize() directly
+** prior to using any other SQLite interface. For maximum portability,
+** it is recommended that applications always invoke sqlite3_initialize()
+** directly prior to using any other SQLite interface. Future releases
+** of SQLite may require this. In other words, the behavior exhibited
+** when SQLite is compiled with SQLITE_OMIT_AUTOINIT might become the
+** default behavior in some future release of SQLite.
+**
+** The sqlite3_os_init() routine does operating-system specific
+** initialization of the SQLite library. The sqlite3_os_end()
+** routine undoes the effect of sqlite3_os_init(). Typical tasks
+** performed by these routines include allocation or deallocation
+** of static resources, initialization of global variables,
+** setting up a default [sqlite3_vfs] module, or setting up
+** a default configuration using [sqlite3_config()].
+**
+** The application should never invoke either sqlite3_os_init()
+** or sqlite3_os_end() directly. The application should only invoke
+** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init()
+** interface is called automatically by sqlite3_initialize() and
+** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate
+** implementations for sqlite3_os_init() and sqlite3_os_end()
+** are built into SQLite when it is compiled for unix, windows, or os/2.
+** When built for other platforms (using the SQLITE_OS_OTHER=1 compile-time
+** option) the application must supply a suitable implementation for
+** sqlite3_os_init() and sqlite3_os_end(). An application-supplied
+** implementation of sqlite3_os_init() or sqlite3_os_end()
+** must return SQLITE_OK on success and some other [error code] upon
+** failure.
+*/
+int sqlite3_initialize(void);
+int sqlite3_shutdown(void);
+int sqlite3_os_init(void);
+int sqlite3_os_end(void);
+
+/*
+** CAPI3REF: Configuring The SQLite Library {H10145} <S20000><S30200>
+** EXPERIMENTAL
+**
+** The sqlite3_config() interface is used to make global configuration
+** changes to SQLite in order to tune SQLite to the specific needs of
+** the application. The default configuration is recommended for most
+** applications and so this routine is usually not necessary. It is
+** provided to support rare applications with unusual needs.
+**
+** The sqlite3_config() interface is not threadsafe. The application
+** must insure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** may only be invoked prior to library initialization using
+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
+** Note, however, that sqlite3_config() can be called as part of the
+** implementation of an application-defined [sqlite3_os_init()].
+**
+** The first argument to sqlite3_config() is an integer
+** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines
+** what property of SQLite is to be configured. Subsequent arguments
+** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option]
+** in the first argument.
+**
+** When a configuration option is set, sqlite3_config() returns SQLITE_OK.
+** If the option is unknown or SQLite is unable to set the option
+** then this routine returns a non-zero [error code].
+*/
+int sqlite3_config(int, ...);
+
+/*
+** CAPI3REF: Configure database connections {H10180} <S20000>
+** EXPERIMENTAL
+**
+** The sqlite3_db_config() interface is used to make configuration
+** changes to a [database connection]. The interface is similar to
+** [sqlite3_config()] except that the changes apply to a single
+** [database connection] (specified in the first argument). The
+** sqlite3_db_config() interface can only be used immediately after
+** the database connection is created using [sqlite3_open()],
+** [sqlite3_open16()], or [sqlite3_open_v2()].
+**
+** The second argument to sqlite3_db_config(D,V,...) is the
+** configuration verb - an integer code that indicates what
+** aspect of the [database connection] is being configured.
+** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE].
+** New verbs are likely to be added in future releases of SQLite.
+** Additional arguments depend on the verb.
+*/
+int sqlite3_db_config(sqlite3*, int op, ...);
+
+/*
+** CAPI3REF: Memory Allocation Routines {H10155} <S20120>
+** EXPERIMENTAL
+**
+** An instance of this object defines the interface between SQLite
+** and low-level memory allocation routines.
+**
+** This object is used in only one place in the SQLite interface.
+** A pointer to an instance of this object is the argument to
+** [sqlite3_config()] when the configuration option is
+** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object
+** and passing it to [sqlite3_config()] during configuration, an
+** application can specify an alternative memory allocation subsystem
+** for SQLite to use for all of its dynamic memory needs.
+**
+** Note that SQLite comes with a built-in memory allocator that is
+** perfectly adequate for the overwhelming majority of applications
+** and that this object is only useful to a tiny minority of applications
+** with specialized memory allocation requirements. This object is
+** also used during testing of SQLite in order to specify an alternative
+** memory allocator that simulates memory out-of-memory conditions in
+** order to verify that SQLite recovers gracefully from such
+** conditions.
+**
+** The xMalloc, xFree, and xRealloc methods must work like the
+** malloc(), free(), and realloc() functions from the standard library.
+**
+** xSize should return the allocated size of a memory allocation
+** previously obtained from xMalloc or xRealloc. The allocated size
+** is always at least as big as the requested size but may be larger.
+**
+** The xRoundup method returns what would be the allocated size of
+** a memory allocation given a particular requested size. Most memory
+** allocators round up memory allocations at least to the next multiple
+** of 8. Some allocators round up to a larger multiple or to a power of 2.
+**
+** The xInit method initializes the memory allocator. (For example,
+** it might allocate any require mutexes or initialize internal data
+** structures. The xShutdown method is invoked (indirectly) by
+** [sqlite3_shutdown()] and should deallocate any resources acquired
+** by xInit. The pAppData pointer is used as the only parameter to
+** xInit and xShutdown.
+*/
+typedef struct sqlite3_mem_methods sqlite3_mem_methods;
+struct sqlite3_mem_methods {
+ void *(*xMalloc)(int); /* Memory allocation function */
+ void (*xFree)(void*); /* Free a prior allocation */
+ void *(*xRealloc)(void*,int); /* Resize an allocation */
+ int (*xSize)(void*); /* Return the size of an allocation */
+ int (*xRoundup)(int); /* Round up request size to allocation size */
+ int (*xInit)(void*); /* Initialize the memory allocator */
+ void (*xShutdown)(void*); /* Deinitialize the memory allocator */
+ void *pAppData; /* Argument to xInit() and xShutdown() */
+};
+
+/*
+** CAPI3REF: Configuration Options {H10160} <S20000>
+** EXPERIMENTAL
+**
+** These constants are the available integer configuration options that
+** can be passed as the first argument to the [sqlite3_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued. Applications
+** should check the return code from [sqlite3_config()] to make sure that
+** the call worked. The [sqlite3_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
+** <dd>There are no arguments to this option. This option disables
+** all mutexing and puts SQLite into a mode where it can only be used
+** by a single thread.</dd>
+**
+** <dt>SQLITE_CONFIG_MULTITHREAD</dt>
+** <dd>There are no arguments to this option. This option disables
+** mutexing on [database connection] and [prepared statement] objects.
+** The application is responsible for serializing access to
+** [database connections] and [prepared statements]. But other mutexes
+** are enabled so that SQLite will be safe to use in a multi-threaded
+** environment.</dd>
+**
+** <dt>SQLITE_CONFIG_SERIALIZED</dt>
+** <dd>There are no arguments to this option. This option enables
+** all mutexes including the recursive
+** mutexes on [database connection] and [prepared statement] objects.
+** In this mode (which is the default when SQLite is compiled with
+** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access
+** to [database connections] and [prepared statements] so that the
+** application is free to use the same [database connection] or the
+** same [prepared statement] in different threads at the same time.
+**
+** <p>This configuration option merely sets the default mutex
+** behavior to serialize access to [database connections]. Individual
+** [database connections] can override this setting
+** using the [SQLITE_OPEN_NOMUTEX] flag to [sqlite3_open_v2()].</p></dd>
+**
+** <dt>SQLITE_CONFIG_MALLOC</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mem_methods] structure. The argument specifies
+** alternative low-level memory allocation routines to be used in place of
+** the memory allocation routines built into SQLite.</dd>
+**
+** <dt>SQLITE_CONFIG_GETMALLOC</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
+** structure is filled with the currently defined memory allocation routines.
+** This option can be used to overload the default memory allocation
+** routines with a wrapper that simulations memory allocation failure or
+** tracks memory usage, for example.</dd>
+**
+** <dt>SQLITE_CONFIG_MEMSTATUS</dt>
+** <dd>This option takes single argument of type int, interpreted as a
+** boolean, which enables or disables the collection of memory allocation
+** statistics. When disabled, the following SQLite interfaces become
+** non-operational:
+** <ul>
+** <li> [sqlite3_memory_used()]
+** <li> [sqlite3_memory_highwater()]
+** <li> [sqlite3_soft_heap_limit()]
+** <li> [sqlite3_status()]
+** </ul>
+** </dd>
+**
+** <dt>SQLITE_CONFIG_SCRATCH</dt>
+** <dd>This option specifies a static memory buffer that SQLite can use for
+** scratch memory. There are three arguments: A pointer to the memory, the
+** size of each scratch buffer (sz), and the number of buffers (N). The sz
+** argument must be a multiple of 16. The sz parameter should be a few bytes
+** larger than the actual scratch space required due internal overhead.
+** The first
+** argument should point to an allocation of at least sz*N bytes of memory.
+** SQLite will use no more than one scratch buffer at once per thread, so
+** N should be set to the expected maximum number of threads. The sz
+** parameter should be 6 times the size of the largest database page size.
+** Scratch buffers are used as part of the btree balance operation. If
+** The btree balancer needs additional memory beyond what is provided by
+** scratch buffers or if no scratch buffer space is specified, then SQLite
+** goes to [sqlite3_malloc()] to obtain the memory it needs.</dd>
+**
+** <dt>SQLITE_CONFIG_PAGECACHE</dt>
+** <dd>This option specifies a static memory buffer that SQLite can use for
+** the database page cache. There are three arguments: A pointer to the
+** memory, the size of each page buffer (sz), and the number of pages (N).
+** The sz argument must be a power of two between 512 and 32768. The first
+** argument should point to an allocation of at least sz*N bytes of memory.
+** SQLite will use the memory provided by the first argument to satisfy its
+** memory needs for the first N pages that it adds to cache. If additional
+** page cache memory is needed beyond what is provided by this option, then
+** SQLite goes to [sqlite3_malloc()] for the additional storage space.
+** The implementation might use one or more of the N buffers to hold
+** memory accounting information. </dd>
+**
+** <dt>SQLITE_CONFIG_HEAP</dt>
+** <dd>This option specifies a static memory buffer that SQLite will use
+** for all of its dynamic memory allocation needs beyond those provided
+** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
+** There are three arguments: A pointer to the memory, the number of
+** bytes in the memory buffer, and the minimum allocation size. If
+** the first pointer (the memory pointer) is NULL, then SQLite reverts
+** to using its default memory allocator (the system malloc() implementation),
+** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. If the
+** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
+** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
+** allocator is engaged to handle all of SQLites memory allocation needs.</dd>
+**
+** <dt>SQLITE_CONFIG_MUTEX</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mutex_methods] structure. The argument specifies
+** alternative low-level mutex routines to be used in place
+** the mutex routines built into SQLite.</dd>
+**
+** <dt>SQLITE_CONFIG_GETMUTEX</dt>
+** <dd>This option takes a single argument which is a pointer to an
+** instance of the [sqlite3_mutex_methods] structure. The
+** [sqlite3_mutex_methods]
+** structure is filled with the currently defined mutex routines.
+** This option can be used to overload the default mutex allocation
+** routines with a wrapper used to track mutex usage for performance
+** profiling or testing, for example.</dd>
+**
+** <dt>SQLITE_CONFIG_LOOKASIDE</dt>
+** <dd>This option takes two arguments that determine the default
+** memory allcation lookaside optimization. The first argument is the
+** size of each lookaside buffer slot and the second is the number of
+** slots allocated to each database connection.</dd>
+**
+** </dl>
+*/
+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */
+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_CHUNKALLOC 12 /* int threshold */
+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
+
+/*
+** CAPI3REF: Configuration Options {H10170} <S20000>
+** EXPERIMENTAL
+**
+** These constants are the available integer configuration options that
+** can be passed as the second argument to the [sqlite3_db_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued. Applications
+** should check the return code from [sqlite3_db_config()] to make sure that
+** the call worked. The [sqlite3_db_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
+** <dd>This option takes three additional arguments that determine the
+** [lookaside memory allocator] configuration for the [database connection].
+** The first argument (the third parameter to [sqlite3_db_config()] is a
+** pointer to a memory buffer to use for lookaside memory. The first
+** argument may be NULL in which case SQLite will allocate the lookaside
+** buffer itself using [sqlite3_malloc()]. The second argument is the
+** size of each lookaside buffer slot and the third argument is the number of
+** slots. The size of the buffer in the first argument must be greater than
+** or equal to the product of the second and third arguments.</dd>
+**
+** </dl>
+*/
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+
+
+/*
+** CAPI3REF: Enable Or Disable Extended Result Codes {H12200} <S10700>
+**
+** The sqlite3_extended_result_codes() routine enables or disables the
+** [extended result codes] feature of SQLite. The extended result
+** codes are disabled by default for historical compatibility considerations.
+**
+** INVARIANTS:
+**
+** {H12201} Each new [database connection] shall have the
+** [extended result codes] feature disabled by default.
+**
+** {H12202} The [sqlite3_extended_result_codes(D,F)] interface shall enable
+** [extended result codes] for the [database connection] D
+** if the F parameter is true, or disable them if F is false.
+*/
+int sqlite3_extended_result_codes(sqlite3*, int onoff);
+
+/*
+** CAPI3REF: Last Insert Rowid {H12220} <S10700>
+**
+** Each entry in an SQLite table has a unique 64-bit signed
+** integer key called the "rowid". The rowid is always available
+** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
+** names are not also used by explicitly declared columns. If
+** the table has a column of type INTEGER PRIMARY KEY then that column
+** is another alias for the rowid.
+**
+** This routine returns the rowid of the most recent
+** successful INSERT into the database from the [database connection]
+** in the first argument. If no successful INSERTs
+** have ever occurred on that database connection, zero is returned.
+**
+** If an INSERT occurs within a trigger, then the rowid of the inserted
+** row is returned by this routine as long as the trigger is running.
+** But once the trigger terminates, the value returned by this routine
+** reverts to the last value inserted before the trigger fired.
+**
+** An INSERT that fails due to a constraint violation is not a
+** successful INSERT and does not change the value returned by this
+** routine. Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK,
+** and INSERT OR ABORT make no changes to the return value of this
+** routine when their insertion fails. When INSERT OR REPLACE
+** encounters a constraint violation, it does not fail. The
+** INSERT continues to completion after deleting rows that caused
+** the constraint problem so INSERT OR REPLACE will always change
+** the return value of this interface.
+**
+** For the purposes of this routine, an INSERT is considered to
+** be successful even if it is subsequently rolled back.
+**
+** INVARIANTS:
+**
+** {H12221} The [sqlite3_last_insert_rowid()] function returns the rowid
+** of the most recent successful INSERT performed on the same
+** [database connection] and within the same or higher level
+** trigger context, or zero if there have been no qualifying inserts.
+**
+** {H12223} The [sqlite3_last_insert_rowid()] function returns the
+** same value when called from the same trigger context
+** immediately before and after a ROLLBACK.
+**
+** ASSUMPTIONS:
+**
+** {A12232} If a separate thread performs a new INSERT on the same
+** database connection while the [sqlite3_last_insert_rowid()]
+** function is running and thus changes the last insert rowid,
+** then the value returned by [sqlite3_last_insert_rowid()] is
+** unpredictable and might not equal either the old or the new
+** last insert rowid.
+*/
+sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
+
+/*
+** CAPI3REF: Count The Number Of Rows Modified {H12240} <S10600>
+**
+** This function returns the number of database rows that were changed
+** or inserted or deleted by the most recently completed SQL statement
+** on the [database connection] specified by the first parameter.
+** Only changes that are directly specified by the INSERT, UPDATE,
+** or DELETE statement are counted. Auxiliary changes caused by
+** triggers are not counted. Use the [sqlite3_total_changes()] function
+** to find the total number of changes including changes caused by triggers.
+**
+** A "row change" is a change to a single row of a single table
+** caused by an INSERT, DELETE, or UPDATE statement. Rows that
+** are changed as side effects of REPLACE constraint resolution,
+** rollback, ABORT processing, DROP TABLE, or by any other
+** mechanisms do not count as direct row changes.
+**
+** A "trigger context" is a scope of execution that begins and
+** ends with the script of a trigger. Most SQL statements are
+** evaluated outside of any trigger. This is the "top level"
+** trigger context. If a trigger fires from the top level, a
+** new trigger context is entered for the duration of that one
+** trigger. Subtriggers create subcontexts for their duration.
+**
+** Calling [sqlite3_exec()] or [sqlite3_step()] recursively does
+** not create a new trigger context.
+**
+** This function returns the number of direct row changes in the
+** most recent INSERT, UPDATE, or DELETE statement within the same
+** trigger context.
+**
+** Thus, when called from the top level, this function returns the
+** number of changes in the most recent INSERT, UPDATE, or DELETE
+** that also occurred at the top level. Within the body of a trigger,
+** the sqlite3_changes() interface can be called to find the number of
+** changes in the most recently completed INSERT, UPDATE, or DELETE
+** statement within the body of the same trigger.
+** However, the number returned does not include changes
+** caused by subtriggers since those have their own context.
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements from the table.) Because of this
+** optimization, the deletions in "DELETE FROM table" are not row changes and
+** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
+** functions, regardless of the number of elements that were originally
+** in the table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+**
+** INVARIANTS:
+**
+** {H12241} The [sqlite3_changes()] function shall return the number of
+** row changes caused by the most recent INSERT, UPDATE,
+** or DELETE statement on the same database connection and
+** within the same or higher trigger context, or zero if there have
+** not been any qualifying row changes.
+**
+** {H12243} Statements of the form "DELETE FROM tablename" with no
+** WHERE clause shall cause subsequent calls to
+** [sqlite3_changes()] to return zero, regardless of the
+** number of rows originally in the table.
+**
+** ASSUMPTIONS:
+**
+** {A12252} If a separate thread makes changes on the same database connection
+** while [sqlite3_changes()] is running then the value returned
+** is unpredictable and not meaningful.
+*/
+int sqlite3_changes(sqlite3*);
+
+/*
+** CAPI3REF: Total Number Of Rows Modified {H12260} <S10600>
+**
+** This function returns the number of row changes caused by INSERT,
+** UPDATE or DELETE statements since the [database connection] was opened.
+** The count includes all changes from all trigger contexts. However,
+** the count does not include changes used to implement REPLACE constraints,
+** do rollbacks or ABORT processing, or DROP table processing.
+** The changes are counted as soon as the statement that makes them is
+** completed (when the statement handle is passed to [sqlite3_reset()] or
+** [sqlite3_finalize()]).
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements from the table.) Because of this
+** optimization, the deletions in "DELETE FROM table" are not row changes and
+** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()]
+** functions, regardless of the number of elements that were originally
+** in the table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+**
+** See also the [sqlite3_changes()] interface.
+**
+** INVARIANTS:
+**
+** {H12261} The [sqlite3_total_changes()] returns the total number
+** of row changes caused by INSERT, UPDATE, and/or DELETE
+** statements on the same [database connection], in any
+** trigger context, since the database connection was created.
+**
+** {H12263} Statements of the form "DELETE FROM tablename" with no
+** WHERE clause shall not change the value returned
+** by [sqlite3_total_changes()].
+**
+** ASSUMPTIONS:
+**
+** {A12264} If a separate thread makes changes on the same database connection
+** while [sqlite3_total_changes()] is running then the value
+** returned is unpredictable and not meaningful.
+*/
+int sqlite3_total_changes(sqlite3*);
+
+/*
+** CAPI3REF: Interrupt A Long-Running Query {H12270} <S30500>
+**
+** This function causes any pending database operation to abort and
+** return at its earliest opportunity. This routine is typically
+** called in response to a user action such as pressing "Cancel"
+** or Ctrl-C where the user wants a long query operation to halt
+** immediately.
+**
+** It is safe to call this routine from a thread different from the
+** thread that is currently running the database operation. But it
+** is not safe to call this routine with a [database connection] that
+** is closed or might close before sqlite3_interrupt() returns.
+**
+** If an SQL operation is very nearly finished at the time when
+** sqlite3_interrupt() is called, then it might not have an opportunity
+** to be interrupted and might continue to completion.
+**
+** An SQL operation that is interrupted will return [SQLITE_INTERRUPT].
+** If the interrupted SQL operation is an INSERT, UPDATE, or DELETE
+** that is inside an explicit transaction, then the entire transaction
+** will be rolled back automatically.
+**
+** A call to sqlite3_interrupt() has no effect on SQL statements
+** that are started after sqlite3_interrupt() returns.
+**
+** INVARIANTS:
+**
+** {H12271} The [sqlite3_interrupt()] interface will force all running
+** SQL statements associated with the same database connection
+** to halt after processing at most one additional row of data.
+**
+** {H12272} Any SQL statement that is interrupted by [sqlite3_interrupt()]
+** will return [SQLITE_INTERRUPT].
+**
+** ASSUMPTIONS:
+**
+** {A12279} If the database connection closes while [sqlite3_interrupt()]
+** is running then bad things will likely happen.
+*/
+void sqlite3_interrupt(sqlite3*);
+
+/*
+** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} <S70200>
+**
+** These routines are useful for command-line input to determine if the
+** currently entered text seems to form complete a SQL statement or
+** if additional input is needed before sending the text into
+** SQLite for parsing. These routines return true if the input string
+** appears to be a complete SQL statement. A statement is judged to be
+** complete if it ends with a semicolon token and is not a fragment of a
+** CREATE TRIGGER statement. Semicolons that are embedded within
+** string literals or quoted identifier names or comments are not
+** independent tokens (they are part of the token in which they are
+** embedded) and thus do not count as a statement terminator.
+**
+** These routines do not parse the SQL statements thus
+** will not detect syntactically incorrect SQL.
+**
+** INVARIANTS:
+**
+** {H10511} A successful evaluation of [sqlite3_complete()] or
+** [sqlite3_complete16()] functions shall
+** return a numeric 1 if and only if the last non-whitespace
+** token in their input is a semicolon that is not in between
+** the BEGIN and END of a CREATE TRIGGER statement.
+**
+** {H10512} If a memory allocation error occurs during an invocation
+** of [sqlite3_complete()] or [sqlite3_complete16()] then the
+** routine shall return [SQLITE_NOMEM].
+**
+** ASSUMPTIONS:
+**
+** {A10512} The input to [sqlite3_complete()] must be a zero-terminated
+** UTF-8 string.
+**
+** {A10513} The input to [sqlite3_complete16()] must be a zero-terminated
+** UTF-16 string in native byte order.
+*/
+int sqlite3_complete(const char *sql);
+int sqlite3_complete16(const void *sql);
+
+/*
+** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors {H12310} <S40400>
+**
+** This routine sets a callback function that might be invoked whenever
+** an attempt is made to open a database table that another thread
+** or process has locked.
+**
+** If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]
+** is returned immediately upon encountering the lock. If the busy callback
+** is not NULL, then the callback will be invoked with two arguments.
+**
+** The first argument to the handler is a copy of the void* pointer which
+** is the third argument to sqlite3_busy_handler(). The second argument to
+** the handler callback is the number of times that the busy handler has
+** been invoked for this locking event. If the
+** busy callback returns 0, then no additional attempts are made to
+** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.
+** If the callback returns non-zero, then another attempt
+** is made to open the database for reading and the cycle repeats.
+**
+** The presence of a busy handler does not guarantee that it will be invoked
+** when there is lock contention. If SQLite determines that invoking the busy
+** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
+** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler.
+** Consider a scenario where one process is holding a read lock that
+** it is trying to promote to a reserved lock and
+** a second process is holding a reserved lock that it is trying
+** to promote to an exclusive lock. The first process cannot proceed
+** because it is blocked by the second and the second process cannot
+** proceed because it is blocked by the first. If both processes
+** invoke the busy handlers, neither will make any progress. Therefore,
+** SQLite returns [SQLITE_BUSY] for the first process, hoping that this
+** will induce the first process to release its read lock and allow
+** the second process to proceed.
+**
+** The default busy callback is NULL.
+**
+** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED]
+** when SQLite is in the middle of a large transaction where all the
+** changes will not fit into the in-memory cache. SQLite will
+** already hold a RESERVED lock on the database file, but it needs
+** to promote this lock to EXCLUSIVE so that it can spill cache
+** pages into the database file without harm to concurrent
+** readers. If it is unable to promote the lock, then the in-memory
+** cache will be left in an inconsistent state and so the error
+** code is promoted from the relatively benign [SQLITE_BUSY] to
+** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion
+** forces an automatic rollback of the changes. See the
+** <a href="/cvstrac/wiki?p=CorruptionFollowingBusyError">
+** CorruptionFollowingBusyError</a> wiki page for a discussion of why
+** this is important.
+**
+** There can only be a single busy handler defined for each
+** [database connection]. Setting a new busy handler clears any
+** previously set handler. Note that calling [sqlite3_busy_timeout()]
+** will also set or clear the busy handler.
+**
+** INVARIANTS:
+**
+** {H12311} The [sqlite3_busy_handler(D,C,A)] function shall replace
+** busy callback in the [database connection] D with a new
+** a new busy handler C and application data pointer A.
+**
+** {H12312} Newly created [database connections] shall have a busy
+** handler of NULL.
+**
+** {H12314} When two or more [database connections] share a
+** [sqlite3_enable_shared_cache | common cache],
+** the busy handler for the database connection currently using
+** the cache shall be invoked when the cache encounters a lock.
+**
+** {H12316} If a busy handler callback returns zero, then the SQLite interface
+** that provoked the locking event shall return [SQLITE_BUSY].
+**
+** {H12318} SQLite shall invokes the busy handler with two arguments which
+** are a copy of the pointer supplied by the 3rd parameter to
+** [sqlite3_busy_handler()] and a count of the number of prior
+** invocations of the busy handler for the same locking event.
+**
+** ASSUMPTIONS:
+**
+** {A12319} A busy handler must not close the database connection
+** or [prepared statement] that invoked the busy handler.
+*/
+int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+
+/*
+** CAPI3REF: Set A Busy Timeout {H12340} <S40410>
+**
+** This routine sets a [sqlite3_busy_handler | busy handler] that sleeps
+** for a specified amount of time when a table is locked. The handler
+** will sleep multiple times until at least "ms" milliseconds of sleeping
+** have accumulated. {H12343} After "ms" milliseconds of sleeping,
+** the handler returns 0 which causes [sqlite3_step()] to return
+** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
+**
+** Calling this routine with an argument less than or equal to zero
+** turns off all busy handlers.
+**
+** There can only be a single busy handler for a particular
+** [database connection] any any given moment. If another busy handler
+** was defined (using [sqlite3_busy_handler()]) prior to calling
+** this routine, that other busy handler is cleared.
+**
+** INVARIANTS:
+**
+** {H12341} The [sqlite3_busy_timeout()] function shall override any prior
+** [sqlite3_busy_timeout()] or [sqlite3_busy_handler()] setting
+** on the same [database connection].
+**
+** {H12343} If the 2nd parameter to [sqlite3_busy_timeout()] is less than
+** or equal to zero, then the busy handler shall be cleared so that
+** all subsequent locking events immediately return [SQLITE_BUSY].
+**
+** {H12344} If the 2nd parameter to [sqlite3_busy_timeout()] is a positive
+** number N, then a busy handler shall be set that repeatedly calls
+** the xSleep() method in the [sqlite3_vfs | VFS interface] until
+** either the lock clears or until the cumulative sleep time
+** reported back by xSleep() exceeds N milliseconds.
+*/
+int sqlite3_busy_timeout(sqlite3*, int ms);
+
+/*
+** CAPI3REF: Convenience Routines For Running Queries {H12370} <S10000>
+**
+** Definition: A <b>result table</b> is memory data structure created by the
+** [sqlite3_get_table()] interface. A result table records the
+** complete query results from one or more queries.
+**
+** The table conceptually has a number of rows and columns. But
+** these numbers are not part of the result table itself. These
+** numbers are obtained separately. Let N be the number of rows
+** and M be the number of columns.
+**
+** A result table is an array of pointers to zero-terminated UTF-8 strings.
+** There are (N+1)*M elements in the array. The first M pointers point
+** to zero-terminated strings that contain the names of the columns.
+** The remaining entries all point to query results. NULL values result
+** in NULL pointers. All other values are in their UTF-8 zero-terminated
+** string representation as returned by [sqlite3_column_text()].
+**
+** A result table might consist of one or more memory allocations.
+** It is not safe to pass a result table directly to [sqlite3_free()].
+** A result table should be deallocated using [sqlite3_free_table()].
+**
+** As an example of the result table format, suppose a query result
+** is as follows:
+**
+** <blockquote><pre>
+** Name | Age
+** -----------------------
+** Alice | 43
+** Bob | 28
+** Cindy | 21
+** </pre></blockquote>
+**
+** There are two column (M==2) and three rows (N==3). Thus the
+** result table has 8 entries. Suppose the result table is stored
+** in an array names azResult. Then azResult holds this content:
+**
+** <blockquote><pre>
+** azResult&#91;0] = "Name";
+** azResult&#91;1] = "Age";
+** azResult&#91;2] = "Alice";
+** azResult&#91;3] = "43";
+** azResult&#91;4] = "Bob";
+** azResult&#91;5] = "28";
+** azResult&#91;6] = "Cindy";
+** azResult&#91;7] = "21";
+** </pre></blockquote>
+**
+** The sqlite3_get_table() function evaluates one or more
+** semicolon-separated SQL statements in the zero-terminated UTF-8
+** string of its 2nd parameter. It returns a result table to the
+** pointer given in its 3rd parameter.
+**
+** After the calling function has finished using the result, it should
+** pass the pointer to the result table to sqlite3_free_table() in order to
+** release the memory that was malloced. Because of the way the
+** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling
+** function must not try to call [sqlite3_free()] directly. Only
+** [sqlite3_free_table()] is able to release the memory properly and safely.
+**
+** The sqlite3_get_table() interface is implemented as a wrapper around
+** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access
+** to any internal data structures of SQLite. It uses only the public
+** interface defined here. As a consequence, errors that occur in the
+** wrapper layer outside of the internal [sqlite3_exec()] call are not
+** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()].
+**
+** INVARIANTS:
+**
+** {H12371} If a [sqlite3_get_table()] fails a memory allocation, then
+** it shall free the result table under construction, abort the
+** query in process, skip any subsequent queries, set the
+** *pazResult output pointer to NULL and return [SQLITE_NOMEM].
+**
+** {H12373} If the pnColumn parameter to [sqlite3_get_table()] is not NULL
+** then a successful invocation of [sqlite3_get_table()] shall
+** write the number of columns in the
+** result set of the query into *pnColumn.
+**
+** {H12374} If the pnRow parameter to [sqlite3_get_table()] is not NULL
+** then a successful invocation of [sqlite3_get_table()] shall
+** writes the number of rows in the
+** result set of the query into *pnRow.
+**
+** {H12376} A successful invocation of [sqlite3_get_table()] that computes
+** N rows of result with C columns per row shall make *pazResult
+** point to an array of pointers to (N+1)*C strings where the first
+** C strings are column names as obtained from
+** [sqlite3_column_name()] and the rest are column result values
+** obtained from [sqlite3_column_text()].
+**
+** {H12379} The values in the pazResult array returned by [sqlite3_get_table()]
+** shall remain valid until cleared by [sqlite3_free_table()].
+**
+** {H12382} When an error occurs during evaluation of [sqlite3_get_table()]
+** the function shall set *pazResult to NULL, write an error message
+** into memory obtained from [sqlite3_malloc()], make
+** **pzErrmsg point to that error message, and return a
+** appropriate [error code].
+*/
+int sqlite3_get_table(
+ sqlite3 *db, /* An open database */
+ const char *zSql, /* SQL to be evaluated */
+ char ***pazResult, /* Results of the query */
+ int *pnRow, /* Number of result rows written here */
+ int *pnColumn, /* Number of result columns written here */
+ char **pzErrmsg /* Error msg written here */
+);
+void sqlite3_free_table(char **result);
+
+/*
+** CAPI3REF: Formatted String Printing Functions {H17400} <S70000><S20000>
+**
+** These routines are workalikes of the "printf()" family of functions
+** from the standard C library.
+**
+** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
+** results into memory obtained from [sqlite3_malloc()].
+** The strings returned by these two routines should be
+** released by [sqlite3_free()]. Both routines return a
+** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
+** memory to hold the resulting string.
+**
+** In sqlite3_snprintf() routine is similar to "snprintf()" from
+** the standard C library. The result is written into the
+** buffer supplied as the second parameter whose size is given by
+** the first parameter. Note that the order of the
+** first two parameters is reversed from snprintf(). This is an
+** historical accident that cannot be fixed without breaking
+** backwards compatibility. Note also that sqlite3_snprintf()
+** returns a pointer to its buffer instead of the number of
+** characters actually written into the buffer. We admit that
+** the number of characters written would be a more useful return
+** value but we cannot change the implementation of sqlite3_snprintf()
+** now without breaking compatibility.
+**
+** As long as the buffer size is greater than zero, sqlite3_snprintf()
+** guarantees that the buffer is always zero-terminated. The first
+** parameter "n" is the total size of the buffer, including space for
+** the zero terminator. So the longest string that can be completely
+** written will be n-1 characters.
+**
+** These routines all implement some additional formatting
+** options that are useful for constructing SQL statements.
+** All of the usual printf() formatting options apply. In addition, there
+** is are "%q", "%Q", and "%z" options.
+**
+** The %q option works like %s in that it substitutes a null-terminated
+** string from the argument list. But %q also doubles every '\'' character.
+** %q is designed for use inside a string literal. By doubling each '\''
+** character it escapes that character and allows it to be inserted into
+** the string.
+**
+** For example, assume the string variable zText contains text as follows:
+**
+** <blockquote><pre>
+** char *zText = "It's a happy day!";
+** </pre></blockquote>
+**
+** One can use this text in an SQL statement as follows:
+**
+** <blockquote><pre>
+** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
+** sqlite3_exec(db, zSQL, 0, 0, 0);
+** sqlite3_free(zSQL);
+** </pre></blockquote>
+**
+** Because the %q format string is used, the '\'' character in zText
+** is escaped and the SQL generated is as follows:
+**
+** <blockquote><pre>
+** INSERT INTO table1 VALUES('It''s a happy day!')
+** </pre></blockquote>
+**
+** This is correct. Had we used %s instead of %q, the generated SQL
+** would have looked like this:
+**
+** <blockquote><pre>
+** INSERT INTO table1 VALUES('It's a happy day!');
+** </pre></blockquote>
+**
+** This second example is an SQL syntax error. As a general rule you should
+** always use %q instead of %s when inserting text into a string literal.
+**
+** The %Q option works like %q except it also adds single quotes around
+** the outside of the total string. Additionally, if the parameter in the
+** argument list is a NULL pointer, %Q substitutes the text "NULL" (without
+** single quotes) in place of the %Q option. So, for example, one could say:
+**
+** <blockquote><pre>
+** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
+** sqlite3_exec(db, zSQL, 0, 0, 0);
+** sqlite3_free(zSQL);
+** </pre></blockquote>
+**
+** The code above will render a correct SQL statement in the zSQL
+** variable even if the zText variable is a NULL pointer.
+**
+** The "%z" formatting option works exactly like "%s" with the
+** addition that after the string has been read and copied into
+** the result, [sqlite3_free()] is called on the input string. {END}
+**
+** INVARIANTS:
+**
+** {H17403} The [sqlite3_mprintf()] and [sqlite3_vmprintf()] interfaces
+** return either pointers to zero-terminated UTF-8 strings held in
+** memory obtained from [sqlite3_malloc()] or NULL pointers if
+** a call to [sqlite3_malloc()] fails.
+**
+** {H17406} The [sqlite3_snprintf()] interface writes a zero-terminated
+** UTF-8 string into the buffer pointed to by the second parameter
+** provided that the first parameter is greater than zero.
+**
+** {H17407} The [sqlite3_snprintf()] interface does not write slots of
+** its output buffer (the second parameter) outside the range
+** of 0 through N-1 (where N is the first parameter)
+** regardless of the length of the string
+** requested by the format specification.
+*/
+char *sqlite3_mprintf(const char*,...);
+char *sqlite3_vmprintf(const char*, va_list);
+char *sqlite3_snprintf(int,char*,const char*, ...);
+
+/*
+** CAPI3REF: Memory Allocation Subsystem {H17300} <S20000>
+**
+** The SQLite core uses these three routines for all of its own
+** internal memory allocation needs. "Core" in the previous sentence
+** does not include operating-system specific VFS implementation. The
+** Windows VFS uses native malloc() and free() for some operations.
+**
+** The sqlite3_malloc() routine returns a pointer to a block
+** of memory at least N bytes in length, where N is the parameter.
+** If sqlite3_malloc() is unable to obtain sufficient free
+** memory, it returns a NULL pointer. If the parameter N to
+** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
+** a NULL pointer.
+**
+** Calling sqlite3_free() with a pointer previously returned
+** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
+** that it might be reused. The sqlite3_free() routine is
+** a no-op if is called with a NULL pointer. Passing a NULL pointer
+** to sqlite3_free() is harmless. After being freed, memory
+** should neither be read nor written. Even reading previously freed
+** memory might result in a segmentation fault or other severe error.
+** Memory corruption, a segmentation fault, or other severe error
+** might result if sqlite3_free() is called with a non-NULL pointer that
+** was not obtained from sqlite3_malloc() or sqlite3_free().
+**
+** The sqlite3_realloc() interface attempts to resize a
+** prior memory allocation to be at least N bytes, where N is the
+** second parameter. The memory allocation to be resized is the first
+** parameter. If the first parameter to sqlite3_realloc()
+** is a NULL pointer then its behavior is identical to calling
+** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
+** If the second parameter to sqlite3_realloc() is zero or
+** negative then the behavior is exactly the same as calling
+** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
+** sqlite3_realloc() returns a pointer to a memory allocation
+** of at least N bytes in size or NULL if sufficient memory is unavailable.
+** If M is the size of the prior allocation, then min(N,M) bytes
+** of the prior allocation are copied into the beginning of buffer returned
+** by sqlite3_realloc() and the prior allocation is freed.
+** If sqlite3_realloc() returns NULL, then the prior allocation
+** is not freed.
+**
+** The memory returned by sqlite3_malloc() and sqlite3_realloc()
+** is always aligned to at least an 8 byte boundary. {END}
+**
+** The default implementation of the memory allocation subsystem uses
+** the malloc(), realloc() and free() provided by the standard C library.
+** {H17382} However, if SQLite is compiled with the
+** SQLITE_MEMORY_SIZE=<i>NNN</i> C preprocessor macro (where <i>NNN</i>
+** is an integer), then SQLite create a static array of at least
+** <i>NNN</i> bytes in size and uses that array for all of its dynamic
+** memory allocation needs. {END} Additional memory allocator options
+** may be added in future releases.
+**
+** In SQLite version 3.5.0 and 3.5.1, it was possible to define
+** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
+** implementation of these routines to be omitted. That capability
+** is no longer provided. Only built-in memory allocators can be used.
+**
+** The Windows OS interface layer calls
+** the system malloc() and free() directly when converting
+** filenames between the UTF-8 encoding used by SQLite
+** and whatever filename encoding is used by the particular Windows
+** installation. Memory allocation errors are detected, but
+** they are reported back as [SQLITE_CANTOPEN] or
+** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
+**
+** INVARIANTS:
+**
+** {H17303} The [sqlite3_malloc(N)] interface returns either a pointer to
+** a newly checked-out block of at least N bytes of memory
+** that is 8-byte aligned, or it returns NULL if it is unable
+** to fulfill the request.
+**
+** {H17304} The [sqlite3_malloc(N)] interface returns a NULL pointer if
+** N is less than or equal to zero.
+**
+** {H17305} The [sqlite3_free(P)] interface releases memory previously
+** returned from [sqlite3_malloc()] or [sqlite3_realloc()],
+** making it available for reuse.
+**
+** {H17306} A call to [sqlite3_free(NULL)] is a harmless no-op.
+**
+** {H17310} A call to [sqlite3_realloc(0,N)] is equivalent to a call
+** to [sqlite3_malloc(N)].
+**
+** {H17312} A call to [sqlite3_realloc(P,0)] is equivalent to a call
+** to [sqlite3_free(P)].
+**
+** {H17315} The SQLite core uses [sqlite3_malloc()], [sqlite3_realloc()],
+** and [sqlite3_free()] for all of its memory allocation and
+** deallocation needs.
+**
+** {H17318} The [sqlite3_realloc(P,N)] interface returns either a pointer
+** to a block of checked-out memory of at least N bytes in size
+** that is 8-byte aligned, or a NULL pointer.
+**
+** {H17321} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first
+** copies the first K bytes of content from P into the newly
+** allocated block, where K is the lesser of N and the size of
+** the buffer P.
+**
+** {H17322} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first
+** releases the buffer P.
+**
+** {H17323} When [sqlite3_realloc(P,N)] returns NULL, the buffer P is
+** not modified or released.
+**
+** ASSUMPTIONS:
+**
+** {A17350} The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
+** must be either NULL or else pointers obtained from a prior
+** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
+** not yet been released.
+**
+** {A17351} The application must not read or write any part of
+** a block of memory after it has been released using
+** [sqlite3_free()] or [sqlite3_realloc()].
+*/
+void *sqlite3_malloc(int);
+void *sqlite3_realloc(void*, int);
+void sqlite3_free(void*);
+
+/*
+** CAPI3REF: Memory Allocator Statistics {H17370} <S30210>
+**
+** SQLite provides these two interfaces for reporting on the status
+** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()]
+** routines, which form the built-in memory allocation subsystem.
+**
+** INVARIANTS:
+**
+** {H17371} The [sqlite3_memory_used()] routine returns the number of bytes
+** of memory currently outstanding (malloced but not freed).
+**
+** {H17373} The [sqlite3_memory_highwater()] routine returns the maximum
+** value of [sqlite3_memory_used()] since the high-water mark
+** was last reset.
+**
+** {H17374} The values returned by [sqlite3_memory_used()] and
+** [sqlite3_memory_highwater()] include any overhead
+** added by SQLite in its implementation of [sqlite3_malloc()],
+** but not overhead added by the any underlying system library
+** routines that [sqlite3_malloc()] may call.
+**
+** {H17375} The memory high-water mark is reset to the current value of
+** [sqlite3_memory_used()] if and only if the parameter to
+** [sqlite3_memory_highwater()] is true. The value returned
+** by [sqlite3_memory_highwater(1)] is the high-water mark
+** prior to the reset.
+*/
+sqlite3_int64 sqlite3_memory_used(void);
+sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
+
+/*
+** CAPI3REF: Pseudo-Random Number Generator {H17390} <S20000>
+**
+** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
+** select random ROWIDs when inserting new records into a table that
+** already uses the largest possible ROWID. The PRNG is also used for
+** the build-in random() and randomblob() SQL functions. This interface allows
+** applications to access the same PRNG for other purposes.
+**
+** A call to this routine stores N bytes of randomness into buffer P.
+**
+** The first time this routine is invoked (either internally or by
+** the application) the PRNG is seeded using randomness obtained
+** from the xRandomness method of the default [sqlite3_vfs] object.
+** On all subsequent invocations, the pseudo-randomness is generated
+** internally and without recourse to the [sqlite3_vfs] xRandomness
+** method.
+**
+** INVARIANTS:
+**
+** {H17392} The [sqlite3_randomness(N,P)] interface writes N bytes of
+** high-quality pseudo-randomness into buffer P.
+*/
+void sqlite3_randomness(int N, void *P);
+
+/*
+** CAPI3REF: Compile-Time Authorization Callbacks {H12500} <S70100>
+**
+** This routine registers a authorizer callback with a particular
+** [database connection], supplied in the first argument.
+** The authorizer callback is invoked as SQL statements are being compiled
+** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
+** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. At various
+** points during the compilation process, as logic is being created
+** to perform various actions, the authorizer callback is invoked to
+** see if those actions are allowed. The authorizer callback should
+** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
+** specific action but allow the SQL statement to continue to be
+** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
+** rejected with an error. If the authorizer callback returns
+** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
+** then the [sqlite3_prepare_v2()] or equivalent call that triggered
+** the authorizer will fail with an error message.
+**
+** When the callback returns [SQLITE_OK], that means the operation
+** requested is ok. When the callback returns [SQLITE_DENY], the
+** [sqlite3_prepare_v2()] or equivalent call that triggered the
+** authorizer will fail with an error message explaining that
+** access is denied. If the authorizer code is [SQLITE_READ]
+** and the callback returns [SQLITE_IGNORE] then the
+** [prepared statement] statement is constructed to substitute
+** a NULL value in place of the table column that would have
+** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
+** return can be used to deny an untrusted user access to individual
+** columns of a table.
+**
+** The first parameter to the authorizer callback is a copy of the third
+** parameter to the sqlite3_set_authorizer() interface. The second parameter
+** to the callback is an integer [SQLITE_COPY | action code] that specifies
+** the particular action to be authorized. The third through sixth parameters
+** to the callback are zero-terminated strings that contain additional
+** details about the action to be authorized.
+**
+** An authorizer is used when [sqlite3_prepare | preparing]
+** SQL statements from an untrusted source, to ensure that the SQL statements
+** do not try to access data they are not allowed to see, or that they do not
+** try to execute malicious statements that damage the database. For
+** example, an application may allow a user to enter arbitrary
+** SQL queries for evaluation by a database. But the application does
+** not want the user to be able to make arbitrary changes to the
+** database. An authorizer could then be put in place while the
+** user-entered SQL is being [sqlite3_prepare | prepared] that
+** disallows everything except [SELECT] statements.
+**
+** Applications that need to process SQL from untrusted sources
+** might also consider lowering resource limits using [sqlite3_limit()]
+** and limiting database size using the [max_page_count] [PRAGMA]
+** in addition to using an authorizer.
+**
+** Only a single authorizer can be in place on a database connection
+** at a time. Each call to sqlite3_set_authorizer overrides the
+** previous call. Disable the authorizer by installing a NULL callback.
+** The authorizer is disabled by default.
+**
+** Note that the authorizer callback is invoked only during
+** [sqlite3_prepare()] or its variants. Authorization is not
+** performed during statement evaluation in [sqlite3_step()].
+**
+** INVARIANTS:
+**
+** {H12501} The [sqlite3_set_authorizer(D,...)] interface registers a
+** authorizer callback with database connection D.
+**
+** {H12502} The authorizer callback is invoked as SQL statements are
+** being compiled.
+**
+** {H12503} If the authorizer callback returns any value other than
+** [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY], then
+** the [sqlite3_prepare_v2()] or equivalent call that caused
+** the authorizer callback to run shall fail with an
+** [SQLITE_ERROR] error code and an appropriate error message.
+**
+** {H12504} When the authorizer callback returns [SQLITE_OK], the operation
+** described is processed normally.
+**
+** {H12505} When the authorizer callback returns [SQLITE_DENY], the
+** [sqlite3_prepare_v2()] or equivalent call that caused the
+** authorizer callback to run shall fail
+** with an [SQLITE_ERROR] error code and an error message
+** explaining that access is denied.
+**
+** {H12506} If the authorizer code (the 2nd parameter to the authorizer
+** callback) is [SQLITE_READ] and the authorizer callback returns
+** [SQLITE_IGNORE], then the prepared statement is constructed to
+** insert a NULL value in place of the table column that would have
+** been read if [SQLITE_OK] had been returned.
+**
+** {H12507} If the authorizer code (the 2nd parameter to the authorizer
+** callback) is anything other than [SQLITE_READ], then
+** a return of [SQLITE_IGNORE] has the same effect as [SQLITE_DENY].
+**
+** {H12510} The first parameter to the authorizer callback is a copy of
+** the third parameter to the [sqlite3_set_authorizer()] interface.
+**
+** {H12511} The second parameter to the callback is an integer
+** [SQLITE_COPY | action code] that specifies the particular action
+** to be authorized.
+**
+** {H12512} The third through sixth parameters to the callback are
+** zero-terminated strings that contain
+** additional details about the action to be authorized.
+**
+** {H12520} Each call to [sqlite3_set_authorizer()] overrides
+** any previously installed authorizer.
+**
+** {H12521} A NULL authorizer means that no authorization
+** callback is invoked.
+**
+** {H12522} The default authorizer is NULL.
+*/
+int sqlite3_set_authorizer(
+ sqlite3*,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pUserData
+);
+
+/*
+** CAPI3REF: Authorizer Return Codes {H12590} <H12500>
+**
+** The [sqlite3_set_authorizer | authorizer callback function] must
+** return either [SQLITE_OK] or one of these two constants in order
+** to signal SQLite whether or not the action is permitted. See the
+** [sqlite3_set_authorizer | authorizer documentation] for additional
+** information.
+*/
+#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
+#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
+
+/*
+** CAPI3REF: Authorizer Action Codes {H12550} <H12500>
+**
+** The [sqlite3_set_authorizer()] interface registers a callback function
+** that is invoked to authorize certain SQL statement actions. The
+** second parameter to the callback is an integer code that specifies
+** what action is being authorized. These are the integer action codes that
+** the authorizer callback may be passed.
+**
+** These action code values signify what kind of operation is to be
+** authorized. The 3rd and 4th parameters to the authorization
+** callback function will be parameters or NULL depending on which of these
+** codes is used as the second parameter. The 5th parameter to the
+** authorizer callback is the name of the database ("main", "temp",
+** etc.) if applicable. The 6th parameter to the authorizer callback
+** is the name of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** top-level SQL code.
+**
+** INVARIANTS:
+**
+** {H12551} The second parameter to an
+** [sqlite3_set_authorizer | authorizer callback] is always an integer
+** [SQLITE_COPY | authorizer code] that specifies what action
+** is being authorized.
+**
+** {H12552} The 3rd and 4th parameters to the
+** [sqlite3_set_authorizer | authorization callback]
+** will be parameters or NULL depending on which
+** [SQLITE_COPY | authorizer code] is used as the second parameter.
+**
+** {H12553} The 5th parameter to the
+** [sqlite3_set_authorizer | authorizer callback] is the name
+** of the database (example: "main", "temp", etc.) if applicable.
+**
+** {H12554} The 6th parameter to the
+** [sqlite3_set_authorizer | authorizer callback] is the name
+** of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** top-level SQL code.
+*/
+/******************************************* 3rd ************ 4th ***********/
+#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
+#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
+#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
+#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
+#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
+#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
+#define SQLITE_DELETE 9 /* Table Name NULL */
+#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
+#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
+#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
+#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
+#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
+#define SQLITE_DROP_VIEW 17 /* View Name NULL */
+#define SQLITE_INSERT 18 /* Table Name NULL */
+#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
+#define SQLITE_READ 20 /* Table Name Column Name */
+#define SQLITE_SELECT 21 /* NULL NULL */
+#define SQLITE_TRANSACTION 22 /* NULL NULL */
+#define SQLITE_UPDATE 23 /* Table Name Column Name */
+#define SQLITE_ATTACH 24 /* Filename NULL */
+#define SQLITE_DETACH 25 /* Database Name NULL */
+#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */
+#define SQLITE_REINDEX 27 /* Index Name NULL */
+#define SQLITE_ANALYZE 28 /* Table Name NULL */
+#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */
+#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */
+#define SQLITE_FUNCTION 31 /* Function Name NULL */
+#define SQLITE_COPY 0 /* No longer used */
+
+/*
+** CAPI3REF: Tracing And Profiling Functions {H12280} <S60400>
+** EXPERIMENTAL
+**
+** These routines register callback functions that can be used for
+** tracing and profiling the execution of SQL statements.
+**
+** The callback function registered by sqlite3_trace() is invoked at
+** various times when an SQL statement is being run by [sqlite3_step()].
+** The callback returns a UTF-8 rendering of the SQL statement text
+** as the statement first begins executing. Additional callbacks occur
+** as each triggered subprogram is entered. The callbacks for triggers
+** contain a UTF-8 SQL comment that identifies the trigger.
+**
+** The callback function registered by sqlite3_profile() is invoked
+** as each SQL statement finishes. The profile callback contains
+** the original statement text and an estimate of wall-clock time
+** of how long that statement took to run.
+**
+** INVARIANTS:
+**
+** {H12281} The callback function registered by [sqlite3_trace()] is
+** whenever an SQL statement first begins to execute and
+** whenever a trigger subprogram first begins to run.
+**
+** {H12282} Each call to [sqlite3_trace()] overrides the previously
+** registered trace callback.
+**
+** {H12283} A NULL trace callback disables tracing.
+**
+** {H12284} The first argument to the trace callback is a copy of
+** the pointer which was the 3rd argument to [sqlite3_trace()].
+**
+** {H12285} The second argument to the trace callback is a
+** zero-terminated UTF-8 string containing the original text
+** of the SQL statement as it was passed into [sqlite3_prepare_v2()]
+** or the equivalent, or an SQL comment indicating the beginning
+** of a trigger subprogram.
+**
+** {H12287} The callback function registered by [sqlite3_profile()] is invoked
+** as each SQL statement finishes.
+**
+** {H12288} The first parameter to the profile callback is a copy of
+** the 3rd parameter to [sqlite3_profile()].
+**
+** {H12289} The second parameter to the profile callback is a
+** zero-terminated UTF-8 string that contains the complete text of
+** the SQL statement as it was processed by [sqlite3_prepare_v2()]
+** or the equivalent.
+**
+** {H12290} The third parameter to the profile callback is an estimate
+** of the number of nanoseconds of wall-clock time required to
+** run the SQL statement from start to finish.
+*/
+void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+void *sqlite3_profile(sqlite3*,
+ void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
+
+/*
+** CAPI3REF: Query Progress Callbacks {H12910} <S60400>
+**
+** This routine configures a callback function - the
+** progress callback - that is invoked periodically during long
+** running calls to [sqlite3_exec()], [sqlite3_step()] and
+** [sqlite3_get_table()]. An example use for this
+** interface is to keep a GUI updated during a large query.
+**
+** If the progress callback returns non-zero, the operation is
+** interrupted. This feature can be used to implement a
+** "Cancel" button on a GUI dialog box.
+**
+** INVARIANTS:
+**
+** {H12911} The callback function registered by sqlite3_progress_handler()
+** is invoked periodically during long running calls to
+** [sqlite3_step()].
+**
+** {H12912} The progress callback is invoked once for every N virtual
+** machine opcodes, where N is the second argument to
+** the [sqlite3_progress_handler()] call that registered
+** the callback. If N is less than 1, sqlite3_progress_handler()
+** acts as if a NULL progress handler had been specified.
+**
+** {H12913} The progress callback itself is identified by the third
+** argument to sqlite3_progress_handler().
+**
+** {H12914} The fourth argument to sqlite3_progress_handler() is a
+** void pointer passed to the progress callback
+** function each time it is invoked.
+**
+** {H12915} If a call to [sqlite3_step()] results in fewer than N opcodes
+** being executed, then the progress callback is never invoked.
+**
+** {H12916} Every call to [sqlite3_progress_handler()]
+** overwrites any previously registered progress handler.
+**
+** {H12917} If the progress handler callback is NULL then no progress
+** handler is invoked.
+**
+** {H12918} If the progress callback returns a result other than 0, then
+** the behavior is a if [sqlite3_interrupt()] had been called.
+** <S30500>
+*/
+void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
+
+/*
+** CAPI3REF: Opening A New Database Connection {H12700} <S40200>
+**
+** These routines open an SQLite database file whose name is given by the
+** filename argument. The filename argument is interpreted as UTF-8 for
+** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
+** order for sqlite3_open16(). A [database connection] handle is usually
+** returned in *ppDb, even if an error occurs. The only exception is that
+** if SQLite is unable to allocate memory to hold the [sqlite3] object,
+** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]
+** object. If the database is opened (and/or created) successfully, then
+** [SQLITE_OK] is returned. Otherwise an [error code] is returned. The
+** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain
+** an English language description of the error.
+**
+** The default encoding for the database will be UTF-8 if
+** sqlite3_open() or sqlite3_open_v2() is called and
+** UTF-16 in the native byte order if sqlite3_open16() is used.
+**
+** Whether or not an error occurs when it is opened, resources
+** associated with the [database connection] handle should be released by
+** passing it to [sqlite3_close()] when it is no longer required.
+**
+** The sqlite3_open_v2() interface works like sqlite3_open()
+** except that it accepts two additional parameters for additional control
+** over the new database connection. The flags parameter can take one of
+** the following three values, optionally combined with the
+** [SQLITE_OPEN_NOMUTEX] flag:
+**
+** <dl>
+** <dt>[SQLITE_OPEN_READONLY]</dt>
+** <dd>The database is opened in read-only mode. If the database does not
+** already exist, an error is returned.</dd>
+**
+** <dt>[SQLITE_OPEN_READWRITE]</dt>
+** <dd>The database is opened for reading and writing if possible, or reading
+** only if the file is write protected by the operating system. In either
+** case the database must already exist, otherwise an error is returned.</dd>
+**
+** <dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
+** <dd>The database is opened for reading and writing, and is creates it if
+** it does not already exist. This is the behavior that is always used for
+** sqlite3_open() and sqlite3_open16().</dd>
+** </dl>
+**
+** If the 3rd parameter to sqlite3_open_v2() is not one of the
+** combinations shown above or one of the combinations shown above combined
+** with the [SQLITE_OPEN_NOMUTEX] flag, then the behavior is undefined.
+**
+** If the [SQLITE_OPEN_NOMUTEX] flag is set, then mutexes on the
+** opened [database connection] are disabled and the appliation must
+** insure that access to the [database connection] and its associated
+** [prepared statements] is serialized. The [SQLITE_OPEN_NOMUTEX] flag
+** is the default behavior is SQLite is configured using the
+** [SQLITE_CONFIG_MULTITHREAD] or [SQLITE_CONFIG_SINGLETHREAD] options
+** to [sqlite3_config()]. The [SQLITE_OPEN_NOMUTEX] flag only makes a
+** difference when SQLite is in its default [SQLITE_CONFIG_SERIALIZED] mode.
+**
+** If the filename is ":memory:", then a private, temporary in-memory database
+** is created for the connection. This in-memory database will vanish when
+** the database connection is closed. Future versions of SQLite might
+** make use of additional special filenames that begin with the ":" character.
+** It is recommended that when a database filename actually does begin with
+** a ":" character you should prefix the filename with a pathname such as
+** "./" to avoid ambiguity.
+**
+** If the filename is an empty string, then a private, temporary
+** on-disk database will be created. This private database will be
+** automatically deleted as soon as the database connection is closed.
+**
+** The fourth parameter to sqlite3_open_v2() is the name of the
+** [sqlite3_vfs] object that defines the operating system interface that
+** the new database connection should use. If the fourth parameter is
+** a NULL pointer then the default [sqlite3_vfs] object is used.
+**
+** <b>Note to Windows users:</b> The encoding used for the filename argument
+** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever
+** codepage is currently defined. Filenames containing international
+** characters must be converted to UTF-8 prior to passing them into
+** sqlite3_open() or sqlite3_open_v2().
+**
+** INVARIANTS:
+**
+** {H12701} The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces create a new
+** [database connection] associated with
+** the database file given in their first parameter.
+**
+** {H12702} The filename argument is interpreted as UTF-8
+** for [sqlite3_open()] and [sqlite3_open_v2()] and as UTF-16
+** in the native byte order for [sqlite3_open16()].
+**
+** {H12703} A successful invocation of [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()] writes a pointer to a new
+** [database connection] into *ppDb.
+**
+** {H12704} The [sqlite3_open()], [sqlite3_open16()], and
+** [sqlite3_open_v2()] interfaces return [SQLITE_OK] upon success,
+** or an appropriate [error code] on failure.
+**
+** {H12706} The default text encoding for a new database created using
+** [sqlite3_open()] or [sqlite3_open_v2()] will be UTF-8.
+**
+** {H12707} The default text encoding for a new database created using
+** [sqlite3_open16()] will be UTF-16.
+**
+** {H12709} The [sqlite3_open(F,D)] interface is equivalent to
+** [sqlite3_open_v2(F,D,G,0)] where the G parameter is
+** [SQLITE_OPEN_READWRITE]|[SQLITE_OPEN_CREATE].
+**
+** {H12711} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_READONLY] then the database is opened
+** for reading only.
+**
+** {H12712} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_READWRITE] then the database is opened
+** reading and writing if possible, or for reading only if the
+** file is write protected by the operating system.
+**
+** {H12713} If the G parameter to [sqlite3_open_v2(F,D,G,V)] omits the
+** bit value [SQLITE_OPEN_CREATE] and the database does not
+** previously exist, an error is returned.
+**
+** {H12714} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the
+** bit value [SQLITE_OPEN_CREATE] and the database does not
+** previously exist, then an attempt is made to create and
+** initialize the database.
+**
+** {H12717} If the filename argument to [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()] is ":memory:", then an private,
+** ephemeral, in-memory database is created for the connection.
+** <todo>Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required
+** in sqlite3_open_v2()?</todo>
+**
+** {H12719} If the filename is NULL or an empty string, then a private,
+** ephemeral on-disk database will be created.
+** <todo>Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required
+** in sqlite3_open_v2()?</todo>
+**
+** {H12721} The [database connection] created by [sqlite3_open_v2(F,D,G,V)]
+** will use the [sqlite3_vfs] object identified by the V parameter,
+** or the default [sqlite3_vfs] object if V is a NULL pointer.
+**
+** {H12723} Two [database connections] will share a common cache if both were
+** opened with the same VFS while [shared cache mode] was enabled and
+** if both filenames compare equal using memcmp() after having been
+** processed by the [sqlite3_vfs | xFullPathname] method of the VFS.
+*/
+int sqlite3_open(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open16(
+ const void *filename, /* Database filename (UTF-16) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open_v2(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb, /* OUT: SQLite db handle */
+ int flags, /* Flags */
+ const char *zVfs /* Name of VFS module to use */
+);
+
+/*
+** CAPI3REF: Error Codes And Messages {H12800} <S60200>
+**
+** The sqlite3_errcode() interface returns the numeric [result code] or
+** [extended result code] for the most recent failed sqlite3_* API call
+** associated with a [database connection]. If a prior API call failed
+** but the most recent API call succeeded, the return value from
+** sqlite3_errcode() is undefined.
+**
+** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
+** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** Memory to hold the error message string is managed internally.
+** The application does not need to worry about freeing the result.
+** However, the error string might be overwritten or deallocated by
+** subsequent calls to other SQLite interface functions.
+**
+** If an interface fails with SQLITE_MISUSE, that means the interface
+** was invoked incorrectly by the application. In that case, the
+** error code and message may or may not be set.
+**
+** INVARIANTS:
+**
+** {H12801} The [sqlite3_errcode(D)] interface returns the numeric
+** [result code] or [extended result code] for the most recently
+** failed interface call associated with the [database connection] D.
+**
+** {H12803} The [sqlite3_errmsg(D)] and [sqlite3_errmsg16(D)]
+** interfaces return English-language text that describes
+** the error in the mostly recently failed interface call,
+** encoded as either UTF-8 or UTF-16 respectively.
+**
+** {H12807} The strings returned by [sqlite3_errmsg()] and [sqlite3_errmsg16()]
+** are valid until the next SQLite interface call.
+**
+** {H12808} Calls to API routines that do not return an error code
+** (example: [sqlite3_data_count()]) do not
+** change the error code or message returned by
+** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()].
+**
+** {H12809} Interfaces that are not associated with a specific
+** [database connection] (examples:
+** [sqlite3_mprintf()] or [sqlite3_enable_shared_cache()]
+** do not change the values returned by
+** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()].
+*/
+int sqlite3_errcode(sqlite3 *db);
+const char *sqlite3_errmsg(sqlite3*);
+const void *sqlite3_errmsg16(sqlite3*);
+
+/*
+** CAPI3REF: SQL Statement Object {H13000} <H13010>
+** KEYWORDS: {prepared statement} {prepared statements}
+**
+** An instance of this object represents a single SQL statement.
+** This object is variously known as a "prepared statement" or a
+** "compiled SQL statement" or simply as a "statement".
+**
+** The life of a statement object goes something like this:
+**
+** <ol>
+** <li> Create the object using [sqlite3_prepare_v2()] or a related
+** function.
+** <li> Bind values to [host parameters] using the sqlite3_bind_*()
+** interfaces.
+** <li> Run the SQL by calling [sqlite3_step()] one or more times.
+** <li> Reset the statement using [sqlite3_reset()] then go back
+** to step 2. Do this zero or more times.
+** <li> Destroy the object using [sqlite3_finalize()].
+** </ol>
+**
+** Refer to documentation on individual methods above for additional
+** information.
+*/
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+/*
+** CAPI3REF: Run-time Limits {H12760} <S20600>
+**
+** This interface allows the size of various constructs to be limited
+** on a connection by connection basis. The first parameter is the
+** [database connection] whose limit is to be set or queried. The
+** second parameter is one of the [limit categories] that define a
+** class of constructs to be size limited. The third parameter is the
+** new limit for that construct. The function returns the old limit.
+**
+** If the new limit is a negative number, the limit is unchanged.
+** For the limit category of SQLITE_LIMIT_XYZ there is a hard upper
+** bound set by a compile-time C preprocessor macro named SQLITE_MAX_XYZ.
+** (The "_LIMIT_" in the name is changed to "_MAX_".)
+** Attempts to increase a limit above its hard upper bound are
+** silently truncated to the hard upper limit.
+**
+** Run time limits are intended for use in applications that manage
+** both their own internal database and also databases that are controlled
+** by untrusted external sources. An example application might be a
+** webbrowser that has its own databases for storing history and
+** separate databases controlled by JavaScript applications downloaded
+** off the Internet. The internal databases can be given the
+** large, default limits. Databases managed by external sources can
+** be given much smaller limits designed to prevent a denial of service
+** attack. Developers might also want to use the [sqlite3_set_authorizer()]
+** interface to further control untrusted SQL. The size of the database
+** created by an untrusted script can be contained using the
+** [max_page_count] [PRAGMA].
+**
+** New run-time limit categories may be added in future releases.
+**
+** INVARIANTS:
+**
+** {H12762} A successful call to [sqlite3_limit(D,C,V)] where V is
+** positive changes the limit on the size of construct C in the
+** [database connection] D to the lesser of V and the hard upper
+** bound on the size of C that is set at compile-time.
+**
+** {H12766} A successful call to [sqlite3_limit(D,C,V)] where V is negative
+** leaves the state of the [database connection] D unchanged.
+**
+** {H12769} A successful call to [sqlite3_limit(D,C,V)] returns the
+** value of the limit on the size of construct C in the
+** [database connection] D as it was prior to the call.
+*/
+int sqlite3_limit(sqlite3*, int id, int newVal);
+
+/*
+** CAPI3REF: Run-Time Limit Categories {H12790} <H12760>
+** KEYWORDS: {limit category} {limit categories}
+**
+** These constants define various aspects of a [database connection]
+** that can be limited in size by calls to [sqlite3_limit()].
+** The meanings of the various limits are as follows:
+**
+** <dl>
+** <dt>SQLITE_LIMIT_LENGTH</dt>
+** <dd>The maximum size of any string or BLOB or table row.<dd>
+**
+** <dt>SQLITE_LIMIT_SQL_LENGTH</dt>
+** <dd>The maximum length of an SQL statement.</dd>
+**
+** <dt>SQLITE_LIMIT_COLUMN</dt>
+** <dd>The maximum number of columns in a table definition or in the
+** result set of a SELECT or the maximum number of columns in an index
+** or in an ORDER BY or GROUP BY clause.</dd>
+**
+** <dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
+** <dd>The maximum depth of the parse tree on any expression.</dd>
+**
+** <dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
+** <dd>The maximum number of terms in a compound SELECT statement.</dd>
+**
+** <dt>SQLITE_LIMIT_VDBE_OP</dt>
+** <dd>The maximum number of instructions in a virtual machine program
+** used to implement an SQL statement.</dd>
+**
+** <dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
+** <dd>The maximum number of arguments on a function.</dd>
+**
+** <dt>SQLITE_LIMIT_ATTACHED</dt>
+** <dd>The maximum number of attached databases.</dd>
+**
+** <dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>
+** <dd>The maximum length of the pattern argument to the LIKE or
+** GLOB operators.</dd>
+**
+** <dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>
+** <dd>The maximum number of variables in an SQL statement that can
+** be bound.</dd>
+** </dl>
+*/
+#define SQLITE_LIMIT_LENGTH 0
+#define SQLITE_LIMIT_SQL_LENGTH 1
+#define SQLITE_LIMIT_COLUMN 2
+#define SQLITE_LIMIT_EXPR_DEPTH 3
+#define SQLITE_LIMIT_COMPOUND_SELECT 4
+#define SQLITE_LIMIT_VDBE_OP 5
+#define SQLITE_LIMIT_FUNCTION_ARG 6
+#define SQLITE_LIMIT_ATTACHED 7
+#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
+#define SQLITE_LIMIT_VARIABLE_NUMBER 9
+
+/*
+** CAPI3REF: Compiling An SQL Statement {H13010} <S10000>
+** KEYWORDS: {SQL statement compiler}
+**
+** To execute an SQL query, it must first be compiled into a byte-code
+** program using one of these routines.
+**
+** The first argument, "db", is a [database connection] obtained from a
+** prior call to [sqlite3_open()], [sqlite3_open_v2()] or [sqlite3_open16()].
+**
+** The second argument, "zSql", is the statement to be compiled, encoded
+** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2()
+** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
+** use UTF-16.
+**
+** If the nByte argument is less than zero, then zSql is read up to the
+** first zero terminator. If nByte is non-negative, then it is the maximum
+** number of bytes read from zSql. When nByte is non-negative, the
+** zSql string ends at either the first '\000' or '\u0000' character or
+** the nByte-th byte, whichever comes first. If the caller knows
+** that the supplied string is nul-terminated, then there is a small
+** performance advantage to be gained by passing an nByte parameter that
+** is equal to the number of bytes in the input string <i>including</i>
+** the nul-terminator bytes.
+**
+** *pzTail is made to point to the first byte past the end of the
+** first SQL statement in zSql. These routines only compile the first
+** statement in zSql, so *pzTail is left pointing to what remains
+** uncompiled.
+**
+** *ppStmt is left pointing to a compiled [prepared statement] that can be
+** executed using [sqlite3_step()]. If there is an error, *ppStmt is set
+** to NULL. If the input text contains no SQL (if the input is an empty
+** string or a comment) then *ppStmt is set to NULL.
+** {A13018} The calling procedure is responsible for deleting the compiled
+** SQL statement using [sqlite3_finalize()] after it has finished with it.
+**
+** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned.
+**
+** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
+** recommended for all new programs. The two older interfaces are retained
+** for backwards compatibility, but their use is discouraged.
+** In the "v2" interfaces, the prepared statement
+** that is returned (the [sqlite3_stmt] object) contains a copy of the
+** original SQL text. This causes the [sqlite3_step()] interface to
+** behave a differently in two ways:
+**
+** <ol>
+** <li>
+** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
+** always used to do, [sqlite3_step()] will automatically recompile the SQL
+** statement and try to run it again. If the schema has changed in
+** a way that makes the statement no longer valid, [sqlite3_step()] will still
+** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is
+** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the
+** error go away. Note: use [sqlite3_errmsg()] to find the text
+** of the parsing error that results in an [SQLITE_SCHEMA] return.
+** </li>
+**
+** <li>
+** When an error occurs, [sqlite3_step()] will return one of the detailed
+** [error codes] or [extended error codes]. The legacy behavior was that
+** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code
+** and you would have to make a second call to [sqlite3_reset()] in order
+** to find the underlying cause of the problem. With the "v2" prepare
+** interfaces, the underlying reason for the error is returned immediately.
+** </li>
+** </ol>
+**
+** INVARIANTS:
+**
+** {H13011} The [sqlite3_prepare(db,zSql,...)] and
+** [sqlite3_prepare_v2(db,zSql,...)] interfaces interpret the
+** text in their zSql parameter as UTF-8.
+**
+** {H13012} The [sqlite3_prepare16(db,zSql,...)] and
+** [sqlite3_prepare16_v2(db,zSql,...)] interfaces interpret the
+** text in their zSql parameter as UTF-16 in the native byte order.
+**
+** {H13013} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)]
+** and its variants is less than zero, the SQL text is
+** read from zSql is read up to the first zero terminator.
+**
+** {H13014} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)]
+** and its variants is non-negative, then at most nBytes bytes of
+** SQL text is read from zSql.
+**
+** {H13015} In [sqlite3_prepare_v2(db,zSql,N,P,pzTail)] and its variants
+** if the zSql input text contains more than one SQL statement
+** and pzTail is not NULL, then *pzTail is made to point to the
+** first byte past the end of the first SQL statement in zSql.
+** <todo>What does *pzTail point to if there is one statement?</todo>
+**
+** {H13016} A successful call to [sqlite3_prepare_v2(db,zSql,N,ppStmt,...)]
+** or one of its variants writes into *ppStmt a pointer to a new
+** [prepared statement] or a pointer to NULL if zSql contains
+** nothing other than whitespace or comments.
+**
+** {H13019} The [sqlite3_prepare_v2()] interface and its variants return
+** [SQLITE_OK] or an appropriate [error code] upon failure.
+**
+** {H13021} Before [sqlite3_prepare(db,zSql,nByte,ppStmt,pzTail)] or its
+** variants returns an error (any value other than [SQLITE_OK]),
+** they first set *ppStmt to NULL.
+*/
+int sqlite3_prepare(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare_v2(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16_v2(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nByte, /* Maximum length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+
+/*
+** CAPIREF: Retrieving Statement SQL {H13100} <H13000>
+**
+** This interface can be used to retrieve a saved copy of the original
+** SQL text used to create a [prepared statement] if that statement was
+** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+**
+** INVARIANTS:
+**
+** {H13101} If the [prepared statement] passed as the argument to
+** [sqlite3_sql()] was compiled using either [sqlite3_prepare_v2()] or
+** [sqlite3_prepare16_v2()], then [sqlite3_sql()] returns
+** a pointer to a zero-terminated string containing a UTF-8 rendering
+** of the original SQL statement.
+**
+** {H13102} If the [prepared statement] passed as the argument to
+** [sqlite3_sql()] was compiled using either [sqlite3_prepare()] or
+** [sqlite3_prepare16()], then [sqlite3_sql()] returns a NULL pointer.
+**
+** {H13103} The string returned by [sqlite3_sql(S)] is valid until the
+** [prepared statement] S is deleted using [sqlite3_finalize(S)].
+*/
+const char *sqlite3_sql(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Dynamically Typed Value Object {H15000} <S20200>
+** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
+**
+** SQLite uses the sqlite3_value object to represent all values
+** that can be stored in a database table. SQLite uses dynamic typing
+** for the values it stores. Values stored in sqlite3_value objects
+** can be integers, floating point values, strings, BLOBs, or NULL.
+**
+** An sqlite3_value object may be either "protected" or "unprotected".
+** Some interfaces require a protected sqlite3_value. Other interfaces
+** will accept either a protected or an unprotected sqlite3_value.
+** Every interface that accepts sqlite3_value arguments specifies
+** whether or not it requires a protected sqlite3_value.
+**
+** The terms "protected" and "unprotected" refer to whether or not
+** a mutex is held. A internal mutex is held for a protected
+** sqlite3_value object but no mutex is held for an unprotected
+** sqlite3_value object. If SQLite is compiled to be single-threaded
+** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
+** or if SQLite is run in one of reduced mutex modes
+** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
+** then there is no distinction between protected and unprotected
+** sqlite3_value objects and they can be used interchangeably. However,
+** for maximum code portability it is recommended that applications
+** still make the distinction between between protected and unprotected
+** sqlite3_value objects even when not strictly required.
+**
+** The sqlite3_value objects that are passed as parameters into the
+** implementation of [application-defined SQL functions] are protected.
+** The sqlite3_value object returned by
+** [sqlite3_column_value()] is unprotected.
+** Unprotected sqlite3_value objects may only be used with
+** [sqlite3_result_value()] and [sqlite3_bind_value()].
+** The [sqlite3_value_blob | sqlite3_value_type()] family of
+** interfaces require protected sqlite3_value objects.
+*/
+typedef struct Mem sqlite3_value;
+
+/*
+** CAPI3REF: SQL Function Context Object {H16001} <S20200>
+**
+** The context in which an SQL function executes is stored in an
+** sqlite3_context object. A pointer to an sqlite3_context object
+** is always first parameter to [application-defined SQL functions].
+** The application-defined SQL function implementation will pass this
+** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
+** [sqlite3_aggregate_context()], [sqlite3_user_data()],
+** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()],
+** and/or [sqlite3_set_auxdata()].
+*/
+typedef struct sqlite3_context sqlite3_context;
+
+/*
+** CAPI3REF: Binding Values To Prepared Statements {H13500} <S70300>
+** KEYWORDS: {host parameter} {host parameters} {host parameter name}
+** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
+**
+** In the SQL strings input to [sqlite3_prepare_v2()] and its variants,
+** literals may be replaced by a parameter in one of these forms:
+**
+** <ul>
+** <li> ?
+** <li> ?NNN
+** <li> :VVV
+** <li> @VVV
+** <li> $VVV
+** </ul>
+**
+** In the parameter forms shown above NNN is an integer literal,
+** and VVV is an alpha-numeric parameter name. The values of these
+** parameters (also called "host parameter names" or "SQL parameters")
+** can be set using the sqlite3_bind_*() routines defined here.
+**
+** The first argument to the sqlite3_bind_*() routines is always
+** a pointer to the [sqlite3_stmt] object returned from
+** [sqlite3_prepare_v2()] or its variants.
+**
+** The second argument is the index of the SQL parameter to be set.
+** The leftmost SQL parameter has an index of 1. When the same named
+** SQL parameter is used more than once, second and subsequent
+** occurrences have the same index as the first occurrence.
+** The index for named parameters can be looked up using the
+** [sqlite3_bind_parameter_index()] API if desired. The index
+** for "?NNN" parameters is the value of NNN.
+** The NNN value must be between 1 and the [sqlite3_limit()]
+** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
+**
+** The third argument is the value to bind to the parameter.
+**
+** In those routines that have a fourth argument, its value is the
+** number of bytes in the parameter. To be clear: the value is the
+** number of <u>bytes</u> in the value, not the number of characters.
+** If the fourth parameter is negative, the length of the string is
+** the number of bytes up to the first zero terminator.
+**
+** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
+** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+** string after SQLite has finished with it. If the fifth argument is
+** the special value [SQLITE_STATIC], then SQLite assumes that the
+** information is in static, unmanaged space and does not need to be freed.
+** If the fifth argument has the value [SQLITE_TRANSIENT], then
+** SQLite makes its own private copy of the data immediately, before
+** the sqlite3_bind_*() routine returns.
+**
+** The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
+** is filled with zeroes. A zeroblob uses a fixed amount of memory
+** (just an integer to hold its size) while it is being processed.
+** Zeroblobs are intended to serve as placeholders for BLOBs whose
+** content is later written using
+** [sqlite3_blob_open | incremental BLOB I/O] routines.
+** A negative value for the zeroblob results in a zero-length BLOB.
+**
+** The sqlite3_bind_*() routines must be called after
+** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and
+** before [sqlite3_step()].
+** Bindings are not cleared by the [sqlite3_reset()] routine.
+** Unbound parameters are interpreted as NULL.
+**
+** These routines return [SQLITE_OK] on success or an error code if
+** anything goes wrong. [SQLITE_RANGE] is returned if the parameter
+** index is out of range. [SQLITE_NOMEM] is returned if malloc() fails.
+** [SQLITE_MISUSE] might be returned if these routines are called on a
+** virtual machine that is the wrong state or which has already been finalized.
+** Detection of misuse is unreliable. Applications should not depend
+** on SQLITE_MISUSE returns. SQLITE_MISUSE is intended to indicate a
+** a logic error in the application. Future versions of SQLite might
+** panic rather than return SQLITE_MISUSE.
+**
+** See also: [sqlite3_bind_parameter_count()],
+** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13506} The [SQL statement compiler] recognizes tokens of the forms
+** "?", "?NNN", "$VVV", ":VVV", and "@VVV" as SQL parameters,
+** where NNN is any sequence of one or more digits
+** and where VVV is any sequence of one or more alphanumeric
+** characters or "::" optionally followed by a string containing
+** no spaces and contained within parentheses.
+**
+** {H13509} The initial value of an SQL parameter is NULL.
+**
+** {H13512} The index of an "?" SQL parameter is one larger than the
+** largest index of SQL parameter to the left, or 1 if
+** the "?" is the leftmost SQL parameter.
+**
+** {H13515} The index of an "?NNN" SQL parameter is the integer NNN.
+**
+** {H13518} The index of an ":VVV", "$VVV", or "@VVV" SQL parameter is
+** the same as the index of leftmost occurrences of the same
+** parameter, or one more than the largest index over all
+** parameters to the left if this is the first occurrence
+** of this parameter, or 1 if this is the leftmost parameter.
+**
+** {H13521} The [SQL statement compiler] fails with an [SQLITE_RANGE]
+** error if the index of an SQL parameter is less than 1
+** or greater than the compile-time SQLITE_MAX_VARIABLE_NUMBER
+** parameter.
+**
+** {H13524} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,V,...)]
+** associate the value V with all SQL parameters having an
+** index of N in the [prepared statement] S.
+**
+** {H13527} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,...)]
+** override prior calls with the same values of S and N.
+**
+** {H13530} Bindings established by [sqlite3_bind_text | sqlite3_bind(S,...)]
+** persist across calls to [sqlite3_reset(S)].
+**
+** {H13533} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds the first L
+** bytes of the BLOB or string pointed to by V, when L
+** is non-negative.
+**
+** {H13536} In calls to [sqlite3_bind_text(S,N,V,L,D)] or
+** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds characters
+** from V through the first zero character when L is negative.
+**
+** {H13539} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special
+** constant [SQLITE_STATIC], SQLite assumes that the value V
+** is held in static unmanaged space that will not change
+** during the lifetime of the binding.
+**
+** {H13542} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special
+** constant [SQLITE_TRANSIENT], the routine makes a
+** private copy of the value V before it returns.
+**
+** {H13545} In calls to [sqlite3_bind_blob(S,N,V,L,D)],
+** [sqlite3_bind_text(S,N,V,L,D)], or
+** [sqlite3_bind_text16(S,N,V,L,D)] when D is a pointer to
+** a function, SQLite invokes that function to destroy the
+** value V after it has finished using the value V.
+**
+** {H13548} In calls to [sqlite3_bind_zeroblob(S,N,V,L)] the value bound
+** is a BLOB of L bytes, or a zero-length BLOB if L is negative.
+**
+** {H13551} In calls to [sqlite3_bind_value(S,N,V)] the V argument may
+** be either a [protected sqlite3_value] object or an
+** [unprotected sqlite3_value] object.
+*/
+int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+int sqlite3_bind_double(sqlite3_stmt*, int, double);
+int sqlite3_bind_int(sqlite3_stmt*, int, int);
+int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
+int sqlite3_bind_null(sqlite3_stmt*, int);
+int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+
+/*
+** CAPI3REF: Number Of SQL Parameters {H13600} <S70300>
+**
+** This routine can be used to find the number of [SQL parameters]
+** in a [prepared statement]. SQL parameters are tokens of the
+** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as
+** placeholders for values that are [sqlite3_bind_blob | bound]
+** to the parameters at a later time.
+**
+** This routine actually returns the index of the largest (rightmost)
+** parameter. For all forms except ?NNN, this will correspond to the
+** number of unique parameters. If parameters of the ?NNN are used,
+** there may be gaps in the list.
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_name()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13601} The [sqlite3_bind_parameter_count(S)] interface returns
+** the largest index of all SQL parameters in the
+** [prepared statement] S, or 0 if S contains no SQL parameters.
+*/
+int sqlite3_bind_parameter_count(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Name Of A Host Parameter {H13620} <S70300>
+**
+** This routine returns a pointer to the name of the n-th
+** [SQL parameter] in a [prepared statement].
+** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** respectively.
+** In other words, the initial ":" or "$" or "@" or "?"
+** is included as part of the name.
+** Parameters of the form "?" without a following integer have no name
+** and are also referred to as "anonymous parameters".
+**
+** The first host parameter has an index of 1, not 0.
+**
+** If the value n is out of range or if the n-th parameter is
+** nameless, then NULL is returned. The returned string is
+** always in UTF-8 encoding even if the named parameter was
+** originally specified as UTF-16 in [sqlite3_prepare16()] or
+** [sqlite3_prepare16_v2()].
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_count()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13621} The [sqlite3_bind_parameter_name(S,N)] interface returns
+** a UTF-8 rendering of the name of the SQL parameter in
+** the [prepared statement] S having index N, or
+** NULL if there is no SQL parameter with index N or if the
+** parameter with index N is an anonymous parameter "?".
+*/
+const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
+
+/*
+** CAPI3REF: Index Of A Parameter With A Given Name {H13640} <S70300>
+**
+** Return the index of an SQL parameter given its name. The
+** index value returned is suitable for use as the second
+** parameter to [sqlite3_bind_blob|sqlite3_bind()]. A zero
+** is returned if no matching parameter is found. The parameter
+** name must be given in UTF-8 even if the original statement
+** was prepared from UTF-16 text using [sqlite3_prepare16_v2()].
+**
+** See also: [sqlite3_bind_blob|sqlite3_bind()],
+** [sqlite3_bind_parameter_count()], and
+** [sqlite3_bind_parameter_index()].
+**
+** INVARIANTS:
+**
+** {H13641} The [sqlite3_bind_parameter_index(S,N)] interface returns
+** the index of SQL parameter in the [prepared statement]
+** S whose name matches the UTF-8 string N, or 0 if there is
+** no match.
+*/
+int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
+
+/*
+** CAPI3REF: Reset All Bindings On A Prepared Statement {H13660} <S70300>
+**
+** Contrary to the intuition of many, [sqlite3_reset()] does not reset
+** the [sqlite3_bind_blob | bindings] on a [prepared statement].
+** Use this routine to reset all host parameters to NULL.
+**
+** INVARIANTS:
+**
+** {H13661} The [sqlite3_clear_bindings(S)] interface resets all SQL
+** parameter bindings in the [prepared statement] S back to NULL.
+*/
+int sqlite3_clear_bindings(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Number Of Columns In A Result Set {H13710} <S10700>
+**
+** Return the number of columns in the result set returned by the
+** [prepared statement]. This routine returns 0 if pStmt is an SQL
+** statement that does not return data (for example an [UPDATE]).
+**
+** INVARIANTS:
+**
+** {H13711} The [sqlite3_column_count(S)] interface returns the number of
+** columns in the result set generated by the [prepared statement] S,
+** or 0 if S does not generate a result set.
+*/
+int sqlite3_column_count(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Column Names In A Result Set {H13720} <S10700>
+**
+** These routines return the name assigned to a particular column
+** in the result set of a [SELECT] statement. The sqlite3_column_name()
+** interface returns a pointer to a zero-terminated UTF-8 string
+** and sqlite3_column_name16() returns a pointer to a zero-terminated
+** UTF-16 string. The first parameter is the [prepared statement]
+** that implements the [SELECT] statement. The second parameter is the
+** column number. The leftmost column is number 0.
+**
+** The returned string pointer is valid until either the [prepared statement]
+** is destroyed by [sqlite3_finalize()] or until the next call to
+** sqlite3_column_name() or sqlite3_column_name16() on the same column.
+**
+** If sqlite3_malloc() fails during the processing of either routine
+** (for example during a conversion from UTF-8 to UTF-16) then a
+** NULL pointer is returned.
+**
+** The name of a result column is the value of the "AS" clause for
+** that column, if there is an AS clause. If there is no AS clause
+** then the name of the column is unspecified and may change from
+** one release of SQLite to the next.
+**
+** INVARIANTS:
+**
+** {H13721} A successful invocation of the [sqlite3_column_name(S,N)]
+** interface returns the name of the Nth column (where 0 is
+** the leftmost column) for the result set of the
+** [prepared statement] S as a zero-terminated UTF-8 string.
+**
+** {H13723} A successful invocation of the [sqlite3_column_name16(S,N)]
+** interface returns the name of the Nth column (where 0 is
+** the leftmost column) for the result set of the
+** [prepared statement] S as a zero-terminated UTF-16 string
+** in the native byte order.
+**
+** {H13724} The [sqlite3_column_name()] and [sqlite3_column_name16()]
+** interfaces return a NULL pointer if they are unable to
+** allocate memory to hold their normal return strings.
+**
+** {H13725} If the N parameter to [sqlite3_column_name(S,N)] or
+** [sqlite3_column_name16(S,N)] is out of range, then the
+** interfaces return a NULL pointer.
+**
+** {H13726} The strings returned by [sqlite3_column_name(S,N)] and
+** [sqlite3_column_name16(S,N)] are valid until the next
+** call to either routine with the same S and N parameters
+** or until [sqlite3_finalize(S)] is called.
+**
+** {H13727} When a result column of a [SELECT] statement contains
+** an AS clause, the name of that column is the identifier
+** to the right of the AS keyword.
+*/
+const char *sqlite3_column_name(sqlite3_stmt*, int N);
+const void *sqlite3_column_name16(sqlite3_stmt*, int N);
+
+/*
+** CAPI3REF: Source Of Data In A Query Result {H13740} <S10700>
+**
+** These routines provide a means to determine what column of what
+** table in which database a result of a [SELECT] statement comes from.
+** The name of the database or table or column can be returned as
+** either a UTF-8 or UTF-16 string. The _database_ routines return
+** the database name, the _table_ routines return the table name, and
+** the origin_ routines return the column name.
+** The returned string is valid until the [prepared statement] is destroyed
+** using [sqlite3_finalize()] or until the same information is requested
+** again in a different encoding.
+**
+** The names returned are the original un-aliased names of the
+** database, table, and column.
+**
+** The first argument to the following calls is a [prepared statement].
+** These functions return information about the Nth column returned by
+** the statement, where N is the second function argument.
+**
+** If the Nth column returned by the statement is an expression or
+** subquery and is not a column value, then all of these functions return
+** NULL. These routine might also return NULL if a memory allocation error
+** occurs. Otherwise, they return the name of the attached database, table
+** and column that query result column was extracted from.
+**
+** As with all other SQLite APIs, those postfixed with "16" return
+** UTF-16 encoded strings, the other functions return UTF-8. {END}
+**
+** These APIs are only available if the library was compiled with the
+** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+**
+** {A13751}
+** If two or more threads call one or more of these routines against the same
+** prepared statement and column at the same time then the results are
+** undefined.
+**
+** INVARIANTS:
+**
+** {H13741} The [sqlite3_column_database_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the database from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13742} The [sqlite3_column_database_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the database
+** from which the Nth result column of the [prepared statement] S is
+** extracted, or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13743} The [sqlite3_column_table_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the table from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13744} The [sqlite3_column_table_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the table
+** from which the Nth result column of the [prepared statement] S is
+** extracted, or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13745} The [sqlite3_column_origin_name(S,N)] interface returns either
+** the UTF-8 zero-terminated name of the table column from which the
+** Nth result column of the [prepared statement] S is extracted,
+** or NULL if the Nth column of S is a general expression
+** or if unable to allocate memory to store the name.
+**
+** {H13746} The [sqlite3_column_origin_name16(S,N)] interface returns either
+** the UTF-16 native byte order zero-terminated name of the table
+** column from which the Nth result column of the
+** [prepared statement] S is extracted, or NULL if the Nth column
+** of S is a general expression or if unable to allocate memory
+** to store the name.
+**
+** {H13748} The return values from
+** [sqlite3_column_database_name | column metadata interfaces]
+** are valid for the lifetime of the [prepared statement]
+** or until the encoding is changed by another metadata
+** interface call for the same prepared statement and column.
+**
+** ASSUMPTIONS:
+**
+** {A13751} If two or more threads call one or more
+** [sqlite3_column_database_name | column metadata interfaces]
+** for the same [prepared statement] and result column
+** at the same time then the results are undefined.
+*/
+const char *sqlite3_column_database_name(sqlite3_stmt*,int);
+const void *sqlite3_column_database_name16(sqlite3_stmt*,int);
+const char *sqlite3_column_table_name(sqlite3_stmt*,int);
+const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
+const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
+const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
+
+/*
+** CAPI3REF: Declared Datatype Of A Query Result {H13760} <S10700>
+**
+** The first parameter is a [prepared statement].
+** If this statement is a [SELECT] statement and the Nth column of the
+** returned result set of that [SELECT] is a table column (not an
+** expression or subquery) then the declared type of the table
+** column is returned. If the Nth column of the result set is an
+** expression or subquery, then a NULL pointer is returned.
+** The returned string is always UTF-8 encoded. {END}
+**
+** For example, given the database schema:
+**
+** CREATE TABLE t1(c1 VARIANT);
+**
+** and the following statement to be compiled:
+**
+** SELECT c1 + 1, c1 FROM t1;
+**
+** this routine would return the string "VARIANT" for the second result
+** column (i==1), and a NULL pointer for the first result column (i==0).
+**
+** SQLite uses dynamic run-time typing. So just because a column
+** is declared to contain a particular type does not mean that the
+** data stored in that column is of the declared type. SQLite is
+** strongly typed, but the typing is dynamic not static. Type
+** is associated with individual values, not with the containers
+** used to hold those values.
+**
+** INVARIANTS:
+**
+** {H13761} A successful call to [sqlite3_column_decltype(S,N)] returns a
+** zero-terminated UTF-8 string containing the declared datatype
+** of the table column that appears as the Nth column (numbered
+** from 0) of the result set to the [prepared statement] S.
+**
+** {H13762} A successful call to [sqlite3_column_decltype16(S,N)]
+** returns a zero-terminated UTF-16 native byte order string
+** containing the declared datatype of the table column that appears
+** as the Nth column (numbered from 0) of the result set to the
+** [prepared statement] S.
+**
+** {H13763} If N is less than 0 or N is greater than or equal to
+** the number of columns in the [prepared statement] S,
+** or if the Nth column of S is an expression or subquery rather
+** than a table column, or if a memory allocation failure
+** occurs during encoding conversions, then
+** calls to [sqlite3_column_decltype(S,N)] or
+** [sqlite3_column_decltype16(S,N)] return NULL.
+*/
+const char *sqlite3_column_decltype(sqlite3_stmt*,int);
+const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
+
+/*
+** CAPI3REF: Evaluate An SQL Statement {H13200} <S10000>
+**
+** After a [prepared statement] has been prepared using either
+** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy
+** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function
+** must be called one or more times to evaluate the statement.
+**
+** The details of the behavior of the sqlite3_step() interface depend
+** on whether the statement was prepared using the newer "v2" interface
+** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy
+** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the
+** new "v2" interface is recommended for new applications but the legacy
+** interface will continue to be supported.
+**
+** In the legacy interface, the return value will be either [SQLITE_BUSY],
+** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
+** With the "v2" interface, any of the other [result codes] or
+** [extended result codes] might be returned as well.
+**
+** [SQLITE_BUSY] means that the database engine was unable to acquire the
+** database locks it needs to do its job. If the statement is a [COMMIT]
+** or occurs outside of an explicit transaction, then you can retry the
+** statement. If the statement is not a [COMMIT] and occurs within a
+** explicit transaction then you should rollback the transaction before
+** continuing.
+**
+** [SQLITE_DONE] means that the statement has finished executing
+** successfully. sqlite3_step() should not be called again on this virtual
+** machine without first calling [sqlite3_reset()] to reset the virtual
+** machine back to its initial state.
+**
+** If the SQL statement being executed returns any data, then [SQLITE_ROW]
+** is returned each time a new row of data is ready for processing by the
+** caller. The values may be accessed using the [column access functions].
+** sqlite3_step() is called again to retrieve the next row of data.
+**
+** [SQLITE_ERROR] means that a run-time error (such as a constraint
+** violation) has occurred. sqlite3_step() should not be called again on
+** the VM. More information may be found by calling [sqlite3_errmsg()].
+** With the legacy interface, a more specific error code (for example,
+** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)
+** can be obtained by calling [sqlite3_reset()] on the
+** [prepared statement]. In the "v2" interface,
+** the more specific error code is returned directly by sqlite3_step().
+**
+** [SQLITE_MISUSE] means that the this routine was called inappropriately.
+** Perhaps it was called on a [prepared statement] that has
+** already been [sqlite3_finalize | finalized] or on one that had
+** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could
+** be the case that the same database connection is being used by two or
+** more threads at the same moment in time.
+**
+** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()
+** API always returns a generic error code, [SQLITE_ERROR], following any
+** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call
+** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the
+** specific [error codes] that better describes the error.
+** We admit that this is a goofy design. The problem has been fixed
+** with the "v2" interface. If you prepare all of your SQL statements
+** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead
+** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,
+** then the more specific [error codes] are returned directly
+** by sqlite3_step(). The use of the "v2" interface is recommended.
+**
+** INVARIANTS:
+**
+** {H13202} If the [prepared statement] S is ready to be run, then
+** [sqlite3_step(S)] advances that prepared statement until
+** completion or until it is ready to return another row of the
+** result set, or until an [sqlite3_interrupt | interrupt]
+** or a run-time error occurs.
+**
+** {H15304} When a call to [sqlite3_step(S)] causes the [prepared statement]
+** S to run to completion, the function returns [SQLITE_DONE].
+**
+** {H15306} When a call to [sqlite3_step(S)] stops because it is ready to
+** return another row of the result set, it returns [SQLITE_ROW].
+**
+** {H15308} If a call to [sqlite3_step(S)] encounters an
+** [sqlite3_interrupt | interrupt] or a run-time error,
+** it returns an appropriate error code that is not one of
+** [SQLITE_OK], [SQLITE_ROW], or [SQLITE_DONE].
+**
+** {H15310} If an [sqlite3_interrupt | interrupt] or a run-time error
+** occurs during a call to [sqlite3_step(S)]
+** for a [prepared statement] S created using
+** legacy interfaces [sqlite3_prepare()] or
+** [sqlite3_prepare16()], then the function returns either
+** [SQLITE_ERROR], [SQLITE_BUSY], or [SQLITE_MISUSE].
+*/
+int sqlite3_step(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Number of columns in a result set {H13770} <S10700>
+**
+** Returns the number of values in the current row of the result set.
+**
+** INVARIANTS:
+**
+** {H13771} After a call to [sqlite3_step(S)] that returns [SQLITE_ROW],
+** the [sqlite3_data_count(S)] routine will return the same value
+** as the [sqlite3_column_count(S)] function.
+**
+** {H13772} After [sqlite3_step(S)] has returned any value other than
+** [SQLITE_ROW] or before [sqlite3_step(S)] has been called on the
+** [prepared statement] for the first time since it was
+** [sqlite3_prepare | prepared] or [sqlite3_reset | reset],
+** the [sqlite3_data_count(S)] routine returns zero.
+*/
+int sqlite3_data_count(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Fundamental Datatypes {H10265} <S10110><S10120>
+** KEYWORDS: SQLITE_TEXT
+**
+** {H10266} Every value in SQLite has one of five fundamental datatypes:
+**
+** <ul>
+** <li> 64-bit signed integer
+** <li> 64-bit IEEE floating point number
+** <li> string
+** <li> BLOB
+** <li> NULL
+** </ul> {END}
+**
+** These constants are codes for each of those types.
+**
+** Note that the SQLITE_TEXT constant was also used in SQLite version 2
+** for a completely different meaning. Software that links against both
+** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not
+** SQLITE_TEXT.
+*/
+#define SQLITE_INTEGER 1
+#define SQLITE_FLOAT 2
+#define SQLITE_BLOB 4
+#define SQLITE_NULL 5
+#ifdef SQLITE_TEXT
+# undef SQLITE_TEXT
+#else
+# define SQLITE_TEXT 3
+#endif
+#define SQLITE3_TEXT 3
+
+/*
+** CAPI3REF: Result Values From A Query {H13800} <S10700>
+** KEYWORDS: {column access functions}
+**
+** These routines form the "result set query" interface.
+**
+** These routines return information about a single column of the current
+** result row of a query. In every case the first argument is a pointer
+** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*]
+** that was returned from [sqlite3_prepare_v2()] or one of its variants)
+** and the second argument is the index of the column for which information
+** should be returned. The leftmost column of the result set has the index 0.
+**
+** If the SQL statement does not currently point to a valid row, or if the
+** column index is out of range, the result is undefined.
+** These routines may only be called when the most recent call to
+** [sqlite3_step()] has returned [SQLITE_ROW] and neither
+** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently.
+** If any of these routines are called after [sqlite3_reset()] or
+** [sqlite3_finalize()] or after [sqlite3_step()] has returned
+** something other than [SQLITE_ROW], the results are undefined.
+** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()]
+** are called from a different thread while any of these routines
+** are pending, then the results are undefined.
+**
+** The sqlite3_column_type() routine returns the
+** [SQLITE_INTEGER | datatype code] for the initial data type
+** of the result column. The returned value is one of [SQLITE_INTEGER],
+** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value
+** returned by sqlite3_column_type() is only meaningful if no type
+** conversions have occurred as described below. After a type conversion,
+** the value returned by sqlite3_column_type() is undefined. Future
+** versions of SQLite may change the behavior of sqlite3_column_type()
+** following a type conversion.
+**
+** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()
+** routine returns the number of bytes in that BLOB or string.
+** If the result is a UTF-16 string, then sqlite3_column_bytes() converts
+** the string to UTF-8 and then returns the number of bytes.
+** If the result is a numeric value then sqlite3_column_bytes() uses
+** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns
+** the number of bytes in that string.
+** The value returned does not include the zero terminator at the end
+** of the string. For clarity: the value returned is the number of
+** bytes in the string, not the number of characters.
+**
+** Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
+** even empty strings, are always zero terminated. The return
+** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary
+** pointer, possibly even a NULL pointer.
+**
+** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes()
+** but leaves the result in UTF-16 in native byte order instead of UTF-8.
+** The zero terminator is not included in this count.
+**
+** The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
+** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** If the [unprotected sqlite3_value] object returned by
+** [sqlite3_column_value()] is used in any other way, including calls
+** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
+** or [sqlite3_value_bytes()], then the behavior is undefined.
+**
+** These routines attempt to convert the value where appropriate. For
+** example, if the internal representation is FLOAT and a text result
+** is requested, [sqlite3_snprintf()] is used internally to perform the
+** conversion automatically. The following table details the conversions
+** that are applied:
+**
+** <blockquote>
+** <table border="1">
+** <tr><th> Internal<br>Type <th> Requested<br>Type <th> Conversion
+**
+** <tr><td> NULL <td> INTEGER <td> Result is 0
+** <tr><td> NULL <td> FLOAT <td> Result is 0.0
+** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
+** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
+** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
+** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
+** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT
+** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
+** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
+** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
+** <tr><td> TEXT <td> INTEGER <td> Use atoi()
+** <tr><td> TEXT <td> FLOAT <td> Use atof()
+** <tr><td> TEXT <td> BLOB <td> No change
+** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
+** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
+** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
+** </table>
+** </blockquote>
+**
+** The table above makes reference to standard C library functions atoi()
+** and atof(). SQLite does not really use these functions. It has its
+** own equivalent internal routines. The atoi() and atof() names are
+** used in the table for brevity and because they are familiar to most
+** C programmers.
+**
+** Note that when type conversions occur, pointers returned by prior
+** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
+** sqlite3_column_text16() may be invalidated.
+** Type conversions and pointer invalidations might occur
+** in the following cases:
+**
+** <ul>
+** <li> The initial content is a BLOB and sqlite3_column_text() or
+** sqlite3_column_text16() is called. A zero-terminator might
+** need to be added to the string.</li>
+** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or
+** sqlite3_column_text16() is called. The content must be converted
+** to UTF-16.</li>
+** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or
+** sqlite3_column_text() is called. The content must be converted
+** to UTF-8.</li>
+** </ul>
+**
+** Conversions between UTF-16be and UTF-16le are always done in place and do
+** not invalidate a prior pointer, though of course the content of the buffer
+** that the prior pointer points to will have been modified. Other kinds
+** of conversion are done in place when it is possible, but sometimes they
+** are not possible and in those cases prior pointers are invalidated.
+**
+** The safest and easiest to remember policy is to invoke these routines
+** in one of the following ways:
+**
+** <ul>
+** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>
+** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>
+** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>
+** </ul>
+**
+** In other words, you should call sqlite3_column_text(),
+** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result
+** into the desired format, then invoke sqlite3_column_bytes() or
+** sqlite3_column_bytes16() to find the size of the result. Do not mix calls
+** to sqlite3_column_text() or sqlite3_column_blob() with calls to
+** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16()
+** with calls to sqlite3_column_bytes().
+**
+** The pointers returned are valid until a type conversion occurs as
+** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
+** [sqlite3_finalize()] is called. The memory space used to hold strings
+** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
+** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
+** [sqlite3_free()].
+**
+** If a memory allocation error occurs during the evaluation of any
+** of these routines, a default value is returned. The default value
+** is either the integer 0, the floating point number 0.0, or a NULL
+** pointer. Subsequent calls to [sqlite3_errcode()] will return
+** [SQLITE_NOMEM].
+**
+** INVARIANTS:
+**
+** {H13803} The [sqlite3_column_blob(S,N)] interface converts the
+** Nth column in the current row of the result set for
+** the [prepared statement] S into a BLOB and then returns a
+** pointer to the converted value.
+**
+** {H13806} The [sqlite3_column_bytes(S,N)] interface returns the
+** number of bytes in the BLOB or string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_column_blob(S,N)] or
+** [sqlite3_column_text(S,N)].
+**
+** {H13809} The [sqlite3_column_bytes16(S,N)] interface returns the
+** number of bytes in the string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_column_text16(S,N)].
+**
+** {H13812} The [sqlite3_column_double(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a floating point value and
+** returns a copy of that value.
+**
+** {H13815} The [sqlite3_column_int(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a 64-bit signed integer and
+** returns the lower 32 bits of that integer.
+**
+** {H13818} The [sqlite3_column_int64(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a 64-bit signed integer and
+** returns a copy of that integer.
+**
+** {H13821} The [sqlite3_column_text(S,N)] interface converts the
+** Nth column in the current row of the result set for
+** the [prepared statement] S into a zero-terminated UTF-8
+** string and returns a pointer to that string.
+**
+** {H13824} The [sqlite3_column_text16(S,N)] interface converts the
+** Nth column in the current row of the result set for the
+** [prepared statement] S into a zero-terminated 2-byte
+** aligned UTF-16 native byte order string and returns
+** a pointer to that string.
+**
+** {H13827} The [sqlite3_column_type(S,N)] interface returns
+** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT],
+** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for
+** the Nth column in the current row of the result set for
+** the [prepared statement] S.
+**
+** {H13830} The [sqlite3_column_value(S,N)] interface returns a
+** pointer to an [unprotected sqlite3_value] object for the
+** Nth column in the current row of the result set for
+** the [prepared statement] S.
+*/
+const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
+double sqlite3_column_double(sqlite3_stmt*, int iCol);
+int sqlite3_column_int(sqlite3_stmt*, int iCol);
+sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
+const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
+int sqlite3_column_type(sqlite3_stmt*, int iCol);
+sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
+
+/*
+** CAPI3REF: Destroy A Prepared Statement Object {H13300} <S70300><S30100>
+**
+** The sqlite3_finalize() function is called to delete a [prepared statement].
+** If the statement was executed successfully or not executed at all, then
+** SQLITE_OK is returned. If execution of the statement failed then an
+** [error code] or [extended error code] is returned.
+**
+** This routine can be called at any point during the execution of the
+** [prepared statement]. If the virtual machine has not
+** completed execution when this routine is called, that is like
+** encountering an error or an [sqlite3_interrupt | interrupt].
+** Incomplete updates may be rolled back and transactions canceled,
+** depending on the circumstances, and the
+** [error code] returned will be [SQLITE_ABORT].
+**
+** INVARIANTS:
+**
+** {H11302} The [sqlite3_finalize(S)] interface destroys the
+** [prepared statement] S and releases all
+** memory and file resources held by that object.
+**
+** {H11304} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S returned an error,
+** then [sqlite3_finalize(S)] returns that same error.
+*/
+int sqlite3_finalize(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Reset A Prepared Statement Object {H13330} <S70300>
+**
+** The sqlite3_reset() function is called to reset a [prepared statement]
+** object back to its initial state, ready to be re-executed.
+** Any SQL statement variables that had values bound to them using
+** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values.
+** Use [sqlite3_clear_bindings()] to reset the bindings.
+**
+** {H11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S
+** back to the beginning of its program.
+**
+** {H11334} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],
+** or if [sqlite3_step(S)] has never before been called on S,
+** then [sqlite3_reset(S)] returns [SQLITE_OK].
+**
+** {H11336} If the most recent call to [sqlite3_step(S)] for the
+** [prepared statement] S indicated an error, then
+** [sqlite3_reset(S)] returns an appropriate [error code].
+**
+** {H11338} The [sqlite3_reset(S)] interface does not change the values
+** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
+*/
+int sqlite3_reset(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Create Or Redefine SQL Functions {H16100} <S20200>
+** KEYWORDS: {function creation routines}
+** KEYWORDS: {application-defined SQL function}
+** KEYWORDS: {application-defined SQL functions}
+**
+** These two functions (collectively known as "function creation routines")
+** are used to add SQL functions or aggregates or to redefine the behavior
+** of existing SQL functions or aggregates. The only difference between the
+** two is that the second parameter, the name of the (scalar) function or
+** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16
+** for sqlite3_create_function16().
+**
+** The first parameter is the [database connection] to which the SQL
+** function is to be added. If a single program uses more than one database
+** connection internally, then SQL functions must be added individually to
+** each database connection.
+**
+** The second parameter is the name of the SQL function to be created or
+** redefined. The length of the name is limited to 255 bytes, exclusive of
+** the zero-terminator. Note that the name length limit is in bytes, not
+** characters. Any attempt to create a function with a longer name
+** will result in [SQLITE_ERROR] being returned.
+**
+** The third parameter is the number of arguments that the SQL function or
+** aggregate takes. If this parameter is negative, then the SQL function or
+** aggregate may take any number of arguments.
+**
+** The fourth parameter, eTextRep, specifies what
+** [SQLITE_UTF8 | text encoding] this SQL function prefers for
+** its parameters. Any SQL function implementation should be able to work
+** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
+** more efficient with one encoding than another. It is allowed to
+** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
+** times with the same function but with different values of eTextRep.
+** When multiple implementations of the same function are available, SQLite
+** will pick the one that involves the least amount of data conversion.
+** If there is only a single implementation which does not care what text
+** encoding is used, then the fourth argument should be [SQLITE_ANY].
+**
+** The fifth parameter is an arbitrary pointer. The implementation of the
+** function can gain access to this pointer using [sqlite3_user_data()].
+**
+** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
+** pointers to C-language functions that implement the SQL function or
+** aggregate. A scalar SQL function requires an implementation of the xFunc
+** callback only, NULL pointers should be passed as the xStep and xFinal
+** parameters. An aggregate SQL function requires an implementation of xStep
+** and xFinal and NULL should be passed for xFunc. To delete an existing
+** SQL function or aggregate, pass NULL for all three function callbacks.
+**
+** It is permitted to register multiple implementations of the same
+** functions with the same name but with either differing numbers of
+** arguments or differing preferred text encodings. SQLite will use
+** the implementation most closely matches the way in which the
+** SQL function is used.
+**
+** INVARIANTS:
+**
+** {H16103} The [sqlite3_create_function16()] interface behaves exactly
+** like [sqlite3_create_function()] in every way except that it
+** interprets the zFunctionName argument as zero-terminated UTF-16
+** native byte order instead of as zero-terminated UTF-8.
+**
+** {H16106} A successful invocation of
+** the [sqlite3_create_function(D,X,N,E,...)] interface registers
+** or replaces callback functions in the [database connection] D
+** used to implement the SQL function named X with N parameters
+** and having a preferred text encoding of E.
+**
+** {H16109} A successful call to [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** replaces the P, F, S, and L values from any prior calls with
+** the same D, X, N, and E values.
+**
+** {H16112} The [sqlite3_create_function(D,X,...)] interface fails with
+** a return code of [SQLITE_ERROR] if the SQL function name X is
+** longer than 255 bytes exclusive of the zero terminator.
+**
+** {H16118} Either F must be NULL and S and L are non-NULL or else F
+** is non-NULL and S and L are NULL, otherwise
+** [sqlite3_create_function(D,X,N,E,P,F,S,L)] returns [SQLITE_ERROR].
+**
+** {H16121} The [sqlite3_create_function(D,...)] interface fails with an
+** error code of [SQLITE_BUSY] if there exist [prepared statements]
+** associated with the [database connection] D.
+**
+** {H16124} The [sqlite3_create_function(D,X,N,...)] interface fails with an
+** error code of [SQLITE_ERROR] if parameter N (specifying the number
+** of arguments to the SQL function being registered) is less
+** than -1 or greater than 127.
+**
+** {H16127} When N is non-negative, the [sqlite3_create_function(D,X,N,...)]
+** interface causes callbacks to be invoked for the SQL function
+** named X when the number of arguments to the SQL function is
+** exactly N.
+**
+** {H16130} When N is -1, the [sqlite3_create_function(D,X,N,...)]
+** interface causes callbacks to be invoked for the SQL function
+** named X with any number of arguments.
+**
+** {H16133} When calls to [sqlite3_create_function(D,X,N,...)]
+** specify multiple implementations of the same function X
+** and when one implementation has N>=0 and the other has N=(-1)
+** the implementation with a non-zero N is preferred.
+**
+** {H16136} When calls to [sqlite3_create_function(D,X,N,E,...)]
+** specify multiple implementations of the same function X with
+** the same number of arguments N but with different
+** encodings E, then the implementation where E matches the
+** database encoding is preferred.
+**
+** {H16139} For an aggregate SQL function created using
+** [sqlite3_create_function(D,X,N,E,P,0,S,L)] the finalizer
+** function L will always be invoked exactly once if the
+** step function S is called one or more times.
+**
+** {H16142} When SQLite invokes either the xFunc or xStep function of
+** an application-defined SQL function or aggregate created
+** by [sqlite3_create_function()] or [sqlite3_create_function16()],
+** then the array of [sqlite3_value] objects passed as the
+** third parameter are always [protected sqlite3_value] objects.
+*/
+int sqlite3_create_function(
+ sqlite3 *db,
+ const char *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pApp,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+int sqlite3_create_function16(
+ sqlite3 *db,
+ const void *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pApp,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+
+/*
+** CAPI3REF: Text Encodings {H10267} <S50200> <H16100>
+**
+** These constant define integer codes that represent the various
+** text encodings supported by SQLite.
+*/
+#define SQLITE_UTF8 1
+#define SQLITE_UTF16LE 2
+#define SQLITE_UTF16BE 3
+#define SQLITE_UTF16 4 /* Use native byte order */
+#define SQLITE_ANY 5 /* sqlite3_create_function only */
+#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
+
+/*
+** CAPI3REF: Deprecated Functions
+** DEPRECATED
+**
+** These functions are [deprecated]. In order to maintain
+** backwards compatibility with older code, these functions continue
+** to be supported. However, new applications should avoid
+** the use of these functions. To help encourage people to avoid
+** using these functions, we are not going to tell you want they do.
+*/
+int sqlite3_aggregate_count(sqlite3_context*);
+int sqlite3_expired(sqlite3_stmt*);
+int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
+int sqlite3_global_recover(void);
+void sqlite3_thread_cleanup(void);
+int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+
+/*
+** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} <S20200>
+**
+** The C-language implementation of SQL functions and aggregates uses
+** this set of interface routines to access the parameter values on
+** the function or aggregate.
+**
+** The xFunc (for scalar functions) or xStep (for aggregates) parameters
+** to [sqlite3_create_function()] and [sqlite3_create_function16()]
+** define callbacks that implement the SQL functions and aggregates.
+** The 4th parameter to these callbacks is an array of pointers to
+** [protected sqlite3_value] objects. There is one [sqlite3_value] object for
+** each parameter to the SQL function. These routines are used to
+** extract values from the [sqlite3_value] objects.
+**
+** These routines work only with [protected sqlite3_value] objects.
+** Any attempt to use these routines on an [unprotected sqlite3_value]
+** object results in undefined behavior.
+**
+** These routines work just like the corresponding [column access functions]
+** except that these routines take a single [protected sqlite3_value] object
+** pointer instead of a [sqlite3_stmt*] pointer and an integer column number.
+**
+** The sqlite3_value_text16() interface extracts a UTF-16 string
+** in the native byte-order of the host machine. The
+** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
+** extract UTF-16 strings as big-endian and little-endian respectively.
+**
+** The sqlite3_value_numeric_type() interface attempts to apply
+** numeric affinity to the value. This means that an attempt is
+** made to convert the value to an integer or floating point. If
+** such a conversion is possible without loss of information (in other
+** words, if the value is a string that looks like a number)
+** then the conversion is performed. Otherwise no conversion occurs.
+** The [SQLITE_INTEGER | datatype] after conversion is returned.
+**
+** Please pay particular attention to the fact that the pointer returned
+** from [sqlite3_value_blob()], [sqlite3_value_text()], or
+** [sqlite3_value_text16()] can be invalidated by a subsequent call to
+** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()],
+** or [sqlite3_value_text16()].
+**
+** These routines must be called from the same thread as
+** the SQL function that supplied the [sqlite3_value*] parameters.
+**
+** INVARIANTS:
+**
+** {H15103} The [sqlite3_value_blob(V)] interface converts the
+** [protected sqlite3_value] object V into a BLOB and then
+** returns a pointer to the converted value.
+**
+** {H15106} The [sqlite3_value_bytes(V)] interface returns the
+** number of bytes in the BLOB or string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_value_blob(V)] or
+** [sqlite3_value_text(V)].
+**
+** {H15109} The [sqlite3_value_bytes16(V)] interface returns the
+** number of bytes in the string (exclusive of the
+** zero terminator on the string) that was returned by the
+** most recent call to [sqlite3_value_text16(V)],
+** [sqlite3_value_text16be(V)], or [sqlite3_value_text16le(V)].
+**
+** {H15112} The [sqlite3_value_double(V)] interface converts the
+** [protected sqlite3_value] object V into a floating point value and
+** returns a copy of that value.
+**
+** {H15115} The [sqlite3_value_int(V)] interface converts the
+** [protected sqlite3_value] object V into a 64-bit signed integer and
+** returns the lower 32 bits of that integer.
+**
+** {H15118} The [sqlite3_value_int64(V)] interface converts the
+** [protected sqlite3_value] object V into a 64-bit signed integer and
+** returns a copy of that integer.
+**
+** {H15121} The [sqlite3_value_text(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated UTF-8
+** string and returns a pointer to that string.
+**
+** {H15124} The [sqlite3_value_text16(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 native byte order
+** string and returns a pointer to that string.
+**
+** {H15127} The [sqlite3_value_text16be(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 big-endian
+** string and returns a pointer to that string.
+**
+** {H15130} The [sqlite3_value_text16le(V)] interface converts the
+** [protected sqlite3_value] object V into a zero-terminated 2-byte
+** aligned UTF-16 little-endian
+** string and returns a pointer to that string.
+**
+** {H15133} The [sqlite3_value_type(V)] interface returns
+** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT],
+** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for
+** the [sqlite3_value] object V.
+**
+** {H15136} The [sqlite3_value_numeric_type(V)] interface converts
+** the [protected sqlite3_value] object V into either an integer or
+** a floating point value if it can do so without loss of
+** information, and returns one of [SQLITE_NULL],
+** [SQLITE_INTEGER], [SQLITE_FLOAT], [SQLITE_TEXT], or
+** [SQLITE_BLOB] as appropriate for the
+** [protected sqlite3_value] object V after the conversion attempt.
+*/
+const void *sqlite3_value_blob(sqlite3_value*);
+int sqlite3_value_bytes(sqlite3_value*);
+int sqlite3_value_bytes16(sqlite3_value*);
+double sqlite3_value_double(sqlite3_value*);
+int sqlite3_value_int(sqlite3_value*);
+sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
+const unsigned char *sqlite3_value_text(sqlite3_value*);
+const void *sqlite3_value_text16(sqlite3_value*);
+const void *sqlite3_value_text16le(sqlite3_value*);
+const void *sqlite3_value_text16be(sqlite3_value*);
+int sqlite3_value_type(sqlite3_value*);
+int sqlite3_value_numeric_type(sqlite3_value*);
+
+/*
+** CAPI3REF: Obtain Aggregate Function Context {H16210} <S20200>
+**
+** The implementation of aggregate SQL functions use this routine to allocate
+** a structure for storing their state.
+**
+** The first time the sqlite3_aggregate_context() routine is called for a
+** particular aggregate, SQLite allocates nBytes of memory, zeroes out that
+** memory, and returns a pointer to it. On second and subsequent calls to
+** sqlite3_aggregate_context() for the same aggregate function index,
+** the same buffer is returned. The implementation of the aggregate can use
+** the returned buffer to accumulate data.
+**
+** SQLite automatically frees the allocated buffer when the aggregate
+** query concludes.
+**
+** The first parameter should be a copy of the
+** [sqlite3_context | SQL function context] that is the first parameter
+** to the callback routine that implements the aggregate function.
+**
+** This routine must be called from the same thread in which
+** the aggregate SQL function is running.
+**
+** INVARIANTS:
+**
+** {H16211} The first invocation of [sqlite3_aggregate_context(C,N)] for
+** a particular instance of an aggregate function (for a particular
+** context C) causes SQLite to allocate N bytes of memory,
+** zero that memory, and return a pointer to the allocated memory.
+**
+** {H16213} If a memory allocation error occurs during
+** [sqlite3_aggregate_context(C,N)] then the function returns 0.
+**
+** {H16215} Second and subsequent invocations of
+** [sqlite3_aggregate_context(C,N)] for the same context pointer C
+** ignore the N parameter and return a pointer to the same
+** block of memory returned by the first invocation.
+**
+** {H16217} The memory allocated by [sqlite3_aggregate_context(C,N)] is
+** automatically freed on the next call to [sqlite3_reset()]
+** or [sqlite3_finalize()] for the [prepared statement] containing
+** the aggregate function associated with context C.
+*/
+void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
+
+/*
+** CAPI3REF: User Data For Functions {H16240} <S20200>
+**
+** The sqlite3_user_data() interface returns a copy of
+** the pointer that was the pUserData parameter (the 5th parameter)
+** of the [sqlite3_create_function()]
+** and [sqlite3_create_function16()] routines that originally
+** registered the application defined function. {END}
+**
+** This routine must be called from the same thread in which
+** the application-defined function is running.
+**
+** INVARIANTS:
+**
+** {H16243} The [sqlite3_user_data(C)] interface returns a copy of the
+** P pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that
+** registered the SQL function associated with [sqlite3_context] C.
+*/
+void *sqlite3_user_data(sqlite3_context*);
+
+/*
+** CAPI3REF: Database Connection For Functions {H16250} <S60600><S20200>
+**
+** The sqlite3_context_db_handle() interface returns a copy of
+** the pointer to the [database connection] (the 1st parameter)
+** of the [sqlite3_create_function()]
+** and [sqlite3_create_function16()] routines that originally
+** registered the application defined function.
+**
+** INVARIANTS:
+**
+** {H16253} The [sqlite3_context_db_handle(C)] interface returns a copy of the
+** D pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)]
+** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that
+** registered the SQL function associated with [sqlite3_context] C.
+*/
+sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
+
+/*
+** CAPI3REF: Function Auxiliary Data {H16270} <S20200>
+**
+** The following two functions may be used by scalar SQL functions to
+** associate metadata with argument values. If the same value is passed to
+** multiple invocations of the same SQL function during query execution, under
+** some circumstances the associated metadata may be preserved. This may
+** be used, for example, to add a regular-expression matching scalar
+** function. The compiled version of the regular expression is stored as
+** metadata associated with the SQL value passed as the regular expression
+** pattern. The compiled regular expression can be reused on multiple
+** invocations of the same function so that the original pattern string
+** does not need to be recompiled on each invocation.
+**
+** The sqlite3_get_auxdata() interface returns a pointer to the metadata
+** associated by the sqlite3_set_auxdata() function with the Nth argument
+** value to the application-defined function. If no metadata has been ever
+** been set for the Nth argument of the function, or if the corresponding
+** function parameter has changed since the meta-data was set,
+** then sqlite3_get_auxdata() returns a NULL pointer.
+**
+** The sqlite3_set_auxdata() interface saves the metadata
+** pointed to by its 3rd parameter as the metadata for the N-th
+** argument of the application-defined function. Subsequent
+** calls to sqlite3_get_auxdata() might return this data, if it has
+** not been destroyed.
+** If it is not NULL, SQLite will invoke the destructor
+** function given by the 4th parameter to sqlite3_set_auxdata() on
+** the metadata when the corresponding function parameter changes
+** or when the SQL statement completes, whichever comes first.
+**
+** SQLite is free to call the destructor and drop metadata on any
+** parameter of any function at any time. The only guarantee is that
+** the destructor will be called before the metadata is dropped.
+**
+** In practice, metadata is preserved between function calls for
+** expressions that are constant at compile time. This includes literal
+** values and SQL variables.
+**
+** These routines must be called from the same thread in which
+** the SQL function is running.
+**
+** INVARIANTS:
+**
+** {H16272} The [sqlite3_get_auxdata(C,N)] interface returns a pointer
+** to metadata associated with the Nth parameter of the SQL function
+** whose context is C, or NULL if there is no metadata associated
+** with that parameter.
+**
+** {H16274} The [sqlite3_set_auxdata(C,N,P,D)] interface assigns a metadata
+** pointer P to the Nth parameter of the SQL function with context C.
+**
+** {H16276} SQLite will invoke the destructor D with a single argument
+** which is the metadata pointer P following a call to
+** [sqlite3_set_auxdata(C,N,P,D)] when SQLite ceases to hold
+** the metadata.
+**
+** {H16277} SQLite ceases to hold metadata for an SQL function parameter
+** when the value of that parameter changes.
+**
+** {H16278} When [sqlite3_set_auxdata(C,N,P,D)] is invoked, the destructor
+** is called for any prior metadata associated with the same function
+** context C and parameter N.
+**
+** {H16279} SQLite will call destructors for any metadata it is holding
+** in a particular [prepared statement] S when either
+** [sqlite3_reset(S)] or [sqlite3_finalize(S)] is called.
+*/
+void *sqlite3_get_auxdata(sqlite3_context*, int N);
+void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+
+
+/*
+** CAPI3REF: Constants Defining Special Destructor Behavior {H10280} <S30100>
+**
+** These are special values for the destructor that is passed in as the
+** final argument to routines like [sqlite3_result_blob()]. If the destructor
+** argument is SQLITE_STATIC, it means that the content pointer is constant
+** and will never change. It does not need to be destroyed. The
+** SQLITE_TRANSIENT value means that the content will likely change in
+** the near future and that SQLite should make its own private copy of
+** the content before returning.
+**
+** The typedef is necessary to work around problems in certain
+** C++ compilers. See ticket #2191.
+*/
+typedef void (*sqlite3_destructor_type)(void*);
+#define SQLITE_STATIC ((sqlite3_destructor_type)0)
+#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
+
+/*
+** CAPI3REF: Setting The Result Of An SQL Function {H16400} <S20200>
+**
+** These routines are used by the xFunc or xFinal callbacks that
+** implement SQL functions and aggregates. See
+** [sqlite3_create_function()] and [sqlite3_create_function16()]
+** for additional information.
+**
+** These functions work very much like the [parameter binding] family of
+** functions used to bind values to host parameters in prepared statements.
+** Refer to the [SQL parameter] documentation for additional information.
+**
+** The sqlite3_result_blob() interface sets the result from
+** an application-defined function to be the BLOB whose content is pointed
+** to by the second parameter and which is N bytes long where N is the
+** third parameter.
+**
+** The sqlite3_result_zeroblob() interfaces set the result of
+** the application-defined function to be a BLOB containing all zero
+** bytes and N bytes in size, where N is the value of the 2nd parameter.
+**
+** The sqlite3_result_double() interface sets the result from
+** an application-defined function to be a floating point value specified
+** by its 2nd argument.
+**
+** The sqlite3_result_error() and sqlite3_result_error16() functions
+** cause the implemented SQL function to throw an exception.
+** SQLite uses the string pointed to by the
+** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
+** as the text of an error message. SQLite interprets the error
+** message string from sqlite3_result_error() as UTF-8. SQLite
+** interprets the string from sqlite3_result_error16() as UTF-16 in native
+** byte order. If the third parameter to sqlite3_result_error()
+** or sqlite3_result_error16() is negative then SQLite takes as the error
+** message all text up through the first zero character.
+** If the third parameter to sqlite3_result_error() or
+** sqlite3_result_error16() is non-negative then SQLite takes that many
+** bytes (not characters) from the 2nd parameter as the error message.
+** The sqlite3_result_error() and sqlite3_result_error16()
+** routines make a private copy of the error message text before
+** they return. Hence, the calling function can deallocate or
+** modify the text after they return without harm.
+** The sqlite3_result_error_code() function changes the error code
+** returned by SQLite as a result of an error in a function. By default,
+** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error()
+** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
+**
+** The sqlite3_result_toobig() interface causes SQLite to throw an error
+** indicating that a string or BLOB is to long to represent.
+**
+** The sqlite3_result_nomem() interface causes SQLite to throw an error
+** indicating that a memory allocation failed.
+**
+** The sqlite3_result_int() interface sets the return value
+** of the application-defined function to be the 32-bit signed integer
+** value given in the 2nd argument.
+** The sqlite3_result_int64() interface sets the return value
+** of the application-defined function to be the 64-bit signed integer
+** value given in the 2nd argument.
+**
+** The sqlite3_result_null() interface sets the return value
+** of the application-defined function to be NULL.
+**
+** The sqlite3_result_text(), sqlite3_result_text16(),
+** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces
+** set the return value of the application-defined function to be
+** a text string which is represented as UTF-8, UTF-16 native byte order,
+** UTF-16 little endian, or UTF-16 big endian, respectively.
+** SQLite takes the text result from the application from
+** the 2nd parameter of the sqlite3_result_text* interfaces.
+** If the 3rd parameter to the sqlite3_result_text* interfaces
+** is negative, then SQLite takes result text from the 2nd parameter
+** through the first zero character.
+** If the 3rd parameter to the sqlite3_result_text* interfaces
+** is non-negative, then as many bytes (not characters) of the text
+** pointed to by the 2nd parameter are taken as the application-defined
+** function result.
+** If the 4th parameter to the sqlite3_result_text* interfaces
+** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
+** function as the destructor on the text or BLOB result when it has
+** finished using that result.
+** If the 4th parameter to the sqlite3_result_text* interfaces or
+** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite
+** assumes that the text or BLOB result is in constant space and does not
+** copy the it or call a destructor when it has finished using that result.
+** If the 4th parameter to the sqlite3_result_text* interfaces
+** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
+** then SQLite makes a copy of the result into space obtained from
+** from [sqlite3_malloc()] before it returns.
+**
+** The sqlite3_result_value() interface sets the result of
+** the application-defined function to be a copy the
+** [unprotected sqlite3_value] object specified by the 2nd parameter. The
+** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
+** so that the [sqlite3_value] specified in the parameter may change or
+** be deallocated after sqlite3_result_value() returns without harm.
+** A [protected sqlite3_value] object may always be used where an
+** [unprotected sqlite3_value] object is required, so either
+** kind of [sqlite3_value] object can be used with this interface.
+**
+** If these routines are called from within the different thread
+** than the one containing the application-defined function that received
+** the [sqlite3_context] pointer, the results are undefined.
+**
+** INVARIANTS:
+**
+** {H16403} The default return value from any SQL function is NULL.
+**
+** {H16406} The [sqlite3_result_blob(C,V,N,D)] interface changes the
+** return value of function C to be a BLOB that is N bytes
+** in length and with content pointed to by V.
+**
+** {H16409} The [sqlite3_result_double(C,V)] interface changes the
+** return value of function C to be the floating point value V.
+**
+** {H16412} The [sqlite3_result_error(C,V,N)] interface changes the return
+** value of function C to be an exception with error code
+** [SQLITE_ERROR] and a UTF-8 error message copied from V up to the
+** first zero byte or until N bytes are read if N is positive.
+**
+** {H16415} The [sqlite3_result_error16(C,V,N)] interface changes the return
+** value of function C to be an exception with error code
+** [SQLITE_ERROR] and a UTF-16 native byte order error message
+** copied from V up to the first zero terminator or until N bytes
+** are read if N is positive.
+**
+** {H16418} The [sqlite3_result_error_toobig(C)] interface changes the return
+** value of the function C to be an exception with error code
+** [SQLITE_TOOBIG] and an appropriate error message.
+**
+** {H16421} The [sqlite3_result_error_nomem(C)] interface changes the return
+** value of the function C to be an exception with error code
+** [SQLITE_NOMEM] and an appropriate error message.
+**
+** {H16424} The [sqlite3_result_error_code(C,E)] interface changes the return
+** value of the function C to be an exception with error code E.
+** The error message text is unchanged.
+**
+** {H16427} The [sqlite3_result_int(C,V)] interface changes the
+** return value of function C to be the 32-bit integer value V.
+**
+** {H16430} The [sqlite3_result_int64(C,V)] interface changes the
+** return value of function C to be the 64-bit integer value V.
+**
+** {H16433} The [sqlite3_result_null(C)] interface changes the
+** return value of function C to be NULL.
+**
+** {H16436} The [sqlite3_result_text(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-8 string
+** V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16439} The [sqlite3_result_text16(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 native byte order
+** string V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16442} The [sqlite3_result_text16be(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 big-endian
+** string V up to the first zero if N is negative
+** or the first N bytes or V if N is non-negative.
+**
+** {H16445} The [sqlite3_result_text16le(C,V,N,D)] interface changes the
+** return value of function C to be the UTF-16 little-endian
+** string V up to the first zero if N is negative
+** or the first N bytes of V if N is non-negative.
+**
+** {H16448} The [sqlite3_result_value(C,V)] interface changes the
+** return value of function C to be the [unprotected sqlite3_value]
+** object V.
+**
+** {H16451} The [sqlite3_result_zeroblob(C,N)] interface changes the
+** return value of function C to be an N-byte BLOB of all zeros.
+**
+** {H16454} The [sqlite3_result_error()] and [sqlite3_result_error16()]
+** interfaces make a copy of their error message strings before
+** returning.
+**
+** {H16457} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is the constant [SQLITE_STATIC]
+** then no destructor is ever called on the pointer V and SQLite
+** assumes that V is immutable.
+**
+** {H16460} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is the constant
+** [SQLITE_TRANSIENT] then the interfaces makes a copy of the
+** content of V and retains the copy.
+**
+** {H16463} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)],
+** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)],
+** [sqlite3_result_text16be(C,V,N,D)], or
+** [sqlite3_result_text16le(C,V,N,D)] is some value other than
+** the constants [SQLITE_STATIC] and [SQLITE_TRANSIENT] then
+** SQLite will invoke the destructor D with V as its only argument
+** when it has finished with the V value.
+*/
+void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_double(sqlite3_context*, double);
+void sqlite3_result_error(sqlite3_context*, const char*, int);
+void sqlite3_result_error16(sqlite3_context*, const void*, int);
+void sqlite3_result_error_toobig(sqlite3_context*);
+void sqlite3_result_error_nomem(sqlite3_context*);
+void sqlite3_result_error_code(sqlite3_context*, int);
+void sqlite3_result_int(sqlite3_context*, int);
+void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
+void sqlite3_result_null(sqlite3_context*);
+void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
+void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+void sqlite3_result_zeroblob(sqlite3_context*, int n);
+
+/*
+** CAPI3REF: Define New Collating Sequences {H16600} <S20300>
+**
+** These functions are used to add new collation sequences to the
+** [database connection] specified as the first argument.
+**
+** The name of the new collation sequence is specified as a UTF-8 string
+** for sqlite3_create_collation() and sqlite3_create_collation_v2()
+** and a UTF-16 string for sqlite3_create_collation16(). In all cases
+** the name is passed as the second function argument.
+**
+** The third argument may be one of the constants [SQLITE_UTF8],
+** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied
+** routine expects to be passed pointers to strings encoded using UTF-8,
+** UTF-16 little-endian, or UTF-16 big-endian, respectively. The
+** third argument might also be [SQLITE_UTF16_ALIGNED] to indicate that
+** the routine expects pointers to 16-bit word aligned strings
+** of UTF-16 in the native byte order of the host computer.
+**
+** A pointer to the user supplied routine must be passed as the fifth
+** argument. If it is NULL, this is the same as deleting the collation
+** sequence (so that SQLite cannot call it anymore).
+** Each time the application supplied function is invoked, it is passed
+** as its first parameter a copy of the void* passed as the fourth argument
+** to sqlite3_create_collation() or sqlite3_create_collation16().
+**
+** The remaining arguments to the application-supplied routine are two strings,
+** each represented by a (length, data) pair and encoded in the encoding
+** that was passed as the third argument when the collation sequence was
+** registered. {END} The application defined collation routine should
+** return negative, zero or positive if the first string is less than,
+** equal to, or greater than the second string. i.e. (STRING1 - STRING2).
+**
+** The sqlite3_create_collation_v2() works like sqlite3_create_collation()
+** except that it takes an extra argument which is a destructor for
+** the collation. The destructor is called when the collation is
+** destroyed and is passed a copy of the fourth parameter void* pointer
+** of the sqlite3_create_collation_v2().
+** Collations are destroyed when they are overridden by later calls to the
+** collation creation functions or when the [database connection] is closed
+** using [sqlite3_close()].
+**
+** INVARIANTS:
+**
+** {H16603} A successful call to the
+** [sqlite3_create_collation_v2(B,X,E,P,F,D)] interface
+** registers function F as the comparison function used to
+** implement collation X on the [database connection] B for
+** databases having encoding E.
+**
+** {H16604} SQLite understands the X parameter to
+** [sqlite3_create_collation_v2(B,X,E,P,F,D)] as a zero-terminated
+** UTF-8 string in which case is ignored for ASCII characters and
+** is significant for non-ASCII characters.
+**
+** {H16606} Successive calls to [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** with the same values for B, X, and E, override prior values
+** of P, F, and D.
+**
+** {H16609} If the destructor D in [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** is not NULL then it is called with argument P when the
+** collating function is dropped by SQLite.
+**
+** {H16612} A collating function is dropped when it is overloaded.
+**
+** {H16615} A collating function is dropped when the database connection
+** is closed using [sqlite3_close()].
+**
+** {H16618} The pointer P in [sqlite3_create_collation_v2(B,X,E,P,F,D)]
+** is passed through as the first parameter to the comparison
+** function F for all subsequent invocations of F.
+**
+** {H16621} A call to [sqlite3_create_collation(B,X,E,P,F)] is exactly
+** the same as a call to [sqlite3_create_collation_v2()] with
+** the same parameters and a NULL destructor.
+**
+** {H16624} Following a [sqlite3_create_collation_v2(B,X,E,P,F,D)],
+** SQLite uses the comparison function F for all text comparison
+** operations on the [database connection] B on text values that
+** use the collating sequence named X.
+**
+** {H16627} The [sqlite3_create_collation16(B,X,E,P,F)] works the same
+** as [sqlite3_create_collation(B,X,E,P,F)] except that the
+** collation name X is understood as UTF-16 in native byte order
+** instead of UTF-8.
+**
+** {H16630} When multiple comparison functions are available for the same
+** collating sequence, SQLite chooses the one whose text encoding
+** requires the least amount of conversion from the default
+** text encoding of the database.
+*/
+int sqlite3_create_collation(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+int sqlite3_create_collation_v2(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*),
+ void(*xDestroy)(void*)
+);
+int sqlite3_create_collation16(
+ sqlite3*,
+ const void *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+
+/*
+** CAPI3REF: Collation Needed Callbacks {H16700} <S20300>
+**
+** To avoid having to register all collation sequences before a database
+** can be used, a single callback function may be registered with the
+** [database connection] to be called whenever an undefined collation
+** sequence is required.
+**
+** If the function is registered using the sqlite3_collation_needed() API,
+** then it is passed the names of undefined collation sequences as strings
+** encoded in UTF-8. {H16703} If sqlite3_collation_needed16() is used,
+** the names are passed as UTF-16 in machine native byte order.
+** A call to either function replaces any existing callback.
+**
+** When the callback is invoked, the first argument passed is a copy
+** of the second argument to sqlite3_collation_needed() or
+** sqlite3_collation_needed16(). The second argument is the database
+** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE],
+** or [SQLITE_UTF16LE], indicating the most desirable form of the collation
+** sequence function required. The fourth parameter is the name of the
+** required collation sequence.
+**
+** The callback function should register the desired collation using
+** [sqlite3_create_collation()], [sqlite3_create_collation16()], or
+** [sqlite3_create_collation_v2()].
+**
+** INVARIANTS:
+**
+** {H16702} A successful call to [sqlite3_collation_needed(D,P,F)]
+** or [sqlite3_collation_needed16(D,P,F)] causes
+** the [database connection] D to invoke callback F with first
+** parameter P whenever it needs a comparison function for a
+** collating sequence that it does not know about.
+**
+** {H16704} Each successful call to [sqlite3_collation_needed()] or
+** [sqlite3_collation_needed16()] overrides the callback registered
+** on the same [database connection] by prior calls to either
+** interface.
+**
+** {H16706} The name of the requested collating function passed in the
+** 4th parameter to the callback is in UTF-8 if the callback
+** was registered using [sqlite3_collation_needed()] and
+** is in UTF-16 native byte order if the callback was
+** registered using [sqlite3_collation_needed16()].
+*/
+int sqlite3_collation_needed(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const char*)
+);
+int sqlite3_collation_needed16(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const void*)
+);
+
+/*
+** Specify the key for an encrypted database. This routine should be
+** called right after sqlite3_open().
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_key(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The key */
+);
+
+/*
+** Change the key on an open database. If the current database is not
+** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
+** database is decrypted.
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_rekey(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The new key */
+);
+
+/*
+** CAPI3REF: Suspend Execution For A Short Time {H10530} <S40410>
+**
+** The sqlite3_sleep() function causes the current thread to suspend execution
+** for at least a number of milliseconds specified in its parameter.
+**
+** If the operating system does not support sleep requests with
+** millisecond time resolution, then the time will be rounded up to
+** the nearest second. The number of milliseconds of sleep actually
+** requested from the operating system is returned.
+**
+** SQLite implements this interface by calling the xSleep()
+** method of the default [sqlite3_vfs] object.
+**
+** INVARIANTS:
+**
+** {H10533} The [sqlite3_sleep(M)] interface invokes the xSleep
+** method of the default [sqlite3_vfs|VFS] in order to
+** suspend execution of the current thread for at least
+** M milliseconds.
+**
+** {H10536} The [sqlite3_sleep(M)] interface returns the number of
+** milliseconds of sleep actually requested of the operating
+** system, which might be larger than the parameter M.
+*/
+int sqlite3_sleep(int);
+
+/*
+** CAPI3REF: Name Of The Folder Holding Temporary Files {H10310} <S20000>
+**
+** If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all temporary files
+** created by SQLite will be placed in that directory. If this variable
+** is a NULL pointer, then SQLite performs a search for an appropriate
+** temporary file directory.
+**
+** It is not safe to modify this variable once a [database connection]
+** has been opened. It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been call and remain unchanged thereafter.
+*/
+SQLITE_EXTERN char *sqlite3_temp_directory;
+
+/*
+** CAPI3REF: Test For Auto-Commit Mode {H12930} <S60200>
+** KEYWORDS: {autocommit mode}
+**
+** The sqlite3_get_autocommit() interface returns non-zero or
+** zero if the given database connection is or is not in autocommit mode,
+** respectively. Autocommit mode is on by default.
+** Autocommit mode is disabled by a [BEGIN] statement.
+** Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK].
+**
+** If certain kinds of errors occur on a statement within a multi-statement
+** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR],
+** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the
+** transaction might be rolled back automatically. The only way to
+** find out whether SQLite automatically rolled back the transaction after
+** an error is to use this function.
+**
+** INVARIANTS:
+**
+** {H12931} The [sqlite3_get_autocommit(D)] interface returns non-zero or
+** zero if the [database connection] D is or is not in autocommit
+** mode, respectively.
+**
+** {H12932} Autocommit mode is on by default.
+**
+** {H12933} Autocommit mode is disabled by a successful [BEGIN] statement.
+**
+** {H12934} Autocommit mode is enabled by a successful [COMMIT] or [ROLLBACK]
+** statement.
+**
+** ASSUMPTIONS:
+**
+** {A12936} If another thread changes the autocommit status of the database
+** connection while this routine is running, then the return value
+** is undefined.
+*/
+int sqlite3_get_autocommit(sqlite3*);
+
+/*
+** CAPI3REF: Find The Database Handle Of A Prepared Statement {H13120} <S60600>
+**
+** The sqlite3_db_handle interface returns the [database connection] handle
+** to which a [prepared statement] belongs. The database handle returned by
+** sqlite3_db_handle is the same database handle that was the first argument
+** to the [sqlite3_prepare_v2()] call (or its variants) that was used to
+** create the statement in the first place.
+**
+** INVARIANTS:
+**
+** {H13123} The [sqlite3_db_handle(S)] interface returns a pointer
+** to the [database connection] associated with the
+** [prepared statement] S.
+*/
+sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Find the next prepared statement {H13140} <S60600>
+**
+** This interface returns a pointer to the next [prepared statement] after
+** pStmt associated with the [database connection] pDb. If pStmt is NULL
+** then this interface returns a pointer to the first prepared statement
+** associated with the database connection pDb. If no prepared statement
+** satisfies the conditions of this routine, it returns NULL.
+**
+** INVARIANTS:
+**
+** {H13143} If D is a [database connection] that holds one or more
+** unfinalized [prepared statements] and S is a NULL pointer,
+** then [sqlite3_next_stmt(D, S)] routine shall return a pointer
+** to one of the prepared statements associated with D.
+**
+** {H13146} If D is a [database connection] that holds no unfinalized
+** [prepared statements] and S is a NULL pointer, then
+** [sqlite3_next_stmt(D, S)] routine shall return a NULL pointer.
+**
+** {H13149} If S is a [prepared statement] in the [database connection] D
+** and S is not the last prepared statement in D, then
+** [sqlite3_next_stmt(D, S)] routine shall return a pointer
+** to the next prepared statement in D after S.
+**
+** {H13152} If S is the last [prepared statement] in the
+** [database connection] D then the [sqlite3_next_stmt(D, S)]
+** routine shall return a NULL pointer.
+**
+** ASSUMPTIONS:
+**
+** {A13154} The [database connection] pointer D in a call to
+** [sqlite3_next_stmt(D,S)] must refer to an open database
+** connection and in particular must not be a NULL pointer.
+*/
+sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Commit And Rollback Notification Callbacks {H12950} <S60400>
+**
+** The sqlite3_commit_hook() interface registers a callback
+** function to be invoked whenever a transaction is committed.
+** Any callback set by a previous call to sqlite3_commit_hook()
+** for the same database connection is overridden.
+** The sqlite3_rollback_hook() interface registers a callback
+** function to be invoked whenever a transaction is committed.
+** Any callback set by a previous call to sqlite3_commit_hook()
+** for the same database connection is overridden.
+** The pArg argument is passed through to the callback.
+** If the callback on a commit hook function returns non-zero,
+** then the commit is converted into a rollback.
+**
+** If another function was previously registered, its
+** pArg value is returned. Otherwise NULL is returned.
+**
+** Registering a NULL function disables the callback.
+**
+** For the purposes of this API, a transaction is said to have been
+** rolled back if an explicit "ROLLBACK" statement is executed, or
+** an error or constraint causes an implicit rollback to occur.
+** The rollback callback is not invoked if a transaction is
+** automatically rolled back because the database connection is closed.
+** The rollback callback is not invoked if a transaction is
+** rolled back because a commit callback returned non-zero.
+** <todo> Check on this </todo>
+**
+** INVARIANTS:
+**
+** {H12951} The [sqlite3_commit_hook(D,F,P)] interface registers the
+** callback function F to be invoked with argument P whenever
+** a transaction commits on the [database connection] D.
+**
+** {H12952} The [sqlite3_commit_hook(D,F,P)] interface returns the P argument
+** from the previous call with the same [database connection] D,
+** or NULL on the first call for a particular database connection D.
+**
+** {H12953} Each call to [sqlite3_commit_hook()] overwrites the callback
+** registered by prior calls.
+**
+** {H12954} If the F argument to [sqlite3_commit_hook(D,F,P)] is NULL
+** then the commit hook callback is canceled and no callback
+** is invoked when a transaction commits.
+**
+** {H12955} If the commit callback returns non-zero then the commit is
+** converted into a rollback.
+**
+** {H12961} The [sqlite3_rollback_hook(D,F,P)] interface registers the
+** callback function F to be invoked with argument P whenever
+** a transaction rolls back on the [database connection] D.
+**
+** {H12962} The [sqlite3_rollback_hook(D,F,P)] interface returns the P
+** argument from the previous call with the same
+** [database connection] D, or NULL on the first call
+** for a particular database connection D.
+**
+** {H12963} Each call to [sqlite3_rollback_hook()] overwrites the callback
+** registered by prior calls.
+**
+** {H12964} If the F argument to [sqlite3_rollback_hook(D,F,P)] is NULL
+** then the rollback hook callback is canceled and no callback
+** is invoked when a transaction rolls back.
+*/
+void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
+void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
+
+/*
+** CAPI3REF: Data Change Notification Callbacks {H12970} <S60400>
+**
+** The sqlite3_update_hook() interface registers a callback function
+** with the [database connection] identified by the first argument
+** to be invoked whenever a row is updated, inserted or deleted.
+** Any callback set by a previous call to this function
+** for the same database connection is overridden.
+**
+** The second argument is a pointer to the function to invoke when a
+** row is updated, inserted or deleted.
+** The first argument to the callback is a copy of the third argument
+** to sqlite3_update_hook().
+** The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
+** or [SQLITE_UPDATE], depending on the operation that caused the callback
+** to be invoked.
+** The third and fourth arguments to the callback contain pointers to the
+** database and table name containing the affected row.
+** The final callback parameter is the rowid of the row. In the case of
+** an update, this is the rowid after the update takes place.
+**
+** The update hook is not invoked when internal system tables are
+** modified (i.e. sqlite_master and sqlite_sequence).
+**
+** If another function was previously registered, its pArg value
+** is returned. Otherwise NULL is returned.
+**
+** INVARIANTS:
+**
+** {H12971} The [sqlite3_update_hook(D,F,P)] interface causes the callback
+** function F to be invoked with first parameter P whenever
+** a table row is modified, inserted, or deleted on
+** the [database connection] D.
+**
+** {H12973} The [sqlite3_update_hook(D,F,P)] interface returns the value
+** of P for the previous call on the same [database connection] D,
+** or NULL for the first call.
+**
+** {H12975} If the update hook callback F in [sqlite3_update_hook(D,F,P)]
+** is NULL then the no update callbacks are made.
+**
+** {H12977} Each call to [sqlite3_update_hook(D,F,P)] overrides prior calls
+** to the same interface on the same [database connection] D.
+**
+** {H12979} The update hook callback is not invoked when internal system
+** tables such as sqlite_master and sqlite_sequence are modified.
+**
+** {H12981} The second parameter to the update callback
+** is one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
+** depending on the operation that caused the callback to be invoked.
+**
+** {H12983} The third and fourth arguments to the callback contain pointers
+** to zero-terminated UTF-8 strings which are the names of the
+** database and table that is being updated.
+
+** {H12985} The final callback parameter is the rowid of the row after
+** the change occurs.
+*/
+void *sqlite3_update_hook(
+ sqlite3*,
+ void(*)(void *,int ,char const *,char const *,sqlite3_int64),
+ void*
+);
+
+/*
+** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} <S30900>
+** KEYWORDS: {shared cache} {shared cache mode}
+**
+** This routine enables or disables the sharing of the database cache
+** and schema data structures between [database connection | connections]
+** to the same database. Sharing is enabled if the argument is true
+** and disabled if the argument is false.
+**
+** Cache sharing is enabled and disabled for an entire process. {END}
+** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** sharing was enabled or disabled for each thread separately.
+**
+** The cache sharing mode set by this interface effects all subsequent
+** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()].
+** Existing database connections continue use the sharing mode
+** that was in effect at the time they were opened.
+**
+** Virtual tables cannot be used with a shared cache. When shared
+** cache is enabled, the [sqlite3_create_module()] API used to register
+** virtual tables will always return an error.
+**
+** This routine returns [SQLITE_OK] if shared cache was enabled or disabled
+** successfully. An [error code] is returned otherwise.
+**
+** Shared cache is disabled by default. But this might change in
+** future releases of SQLite. Applications that care about shared
+** cache setting should set it explicitly.
+**
+** INVARIANTS:
+**
+** {H10331} A successful invocation of [sqlite3_enable_shared_cache(B)]
+** will enable or disable shared cache mode for any subsequently
+** created [database connection] in the same process.
+**
+** {H10336} When shared cache is enabled, the [sqlite3_create_module()]
+** interface will always return an error.
+**
+** {H10337} The [sqlite3_enable_shared_cache(B)] interface returns
+** [SQLITE_OK] if shared cache was enabled or disabled successfully.
+**
+** {H10339} Shared cache is disabled by default.
+*/
+int sqlite3_enable_shared_cache(int);
+
+/*
+** CAPI3REF: Attempt To Free Heap Memory {H17340} <S30220>
+**
+** The sqlite3_release_memory() interface attempts to free N bytes
+** of heap memory by deallocating non-essential memory allocations
+** held by the database library. {END} Memory used to cache database
+** pages to improve performance is an example of non-essential memory.
+** sqlite3_release_memory() returns the number of bytes actually freed,
+** which might be more or less than the amount requested.
+**
+** INVARIANTS:
+**
+** {H17341} The [sqlite3_release_memory(N)] interface attempts to
+** free N bytes of heap memory by deallocating non-essential
+** memory allocations held by the database library.
+**
+** {H16342} The [sqlite3_release_memory(N)] returns the number
+** of bytes actually freed, which might be more or less
+** than the amount requested.
+*/
+int sqlite3_release_memory(int);
+
+/*
+** CAPI3REF: Impose A Limit On Heap Size {H17350} <S30220>
+**
+** The sqlite3_soft_heap_limit() interface places a "soft" limit
+** on the amount of heap memory that may be allocated by SQLite.
+** If an internal allocation is requested that would exceed the
+** soft heap limit, [sqlite3_release_memory()] is invoked one or
+** more times to free up some space before the allocation is performed.
+**
+** The limit is called "soft", because if [sqlite3_release_memory()]
+** cannot free sufficient memory to prevent the limit from being exceeded,
+** the memory is allocated anyway and the current operation proceeds.
+**
+** A negative or zero value for N means that there is no soft heap limit and
+** [sqlite3_release_memory()] will only be called when memory is exhausted.
+** The default value for the soft heap limit is zero.
+**
+** SQLite makes a best effort to honor the soft heap limit.
+** But if the soft heap limit cannot be honored, execution will
+** continue without error or notification. This is why the limit is
+** called a "soft" limit. It is advisory only.
+**
+** Prior to SQLite version 3.5.0, this routine only constrained the memory
+** allocated by a single thread - the same thread in which this routine
+** runs. Beginning with SQLite version 3.5.0, the soft heap limit is
+** applied to all threads. The value specified for the soft heap limit
+** is an upper bound on the total memory allocation for all threads. In
+** version 3.5.0 there is no mechanism for limiting the heap usage for
+** individual threads.
+**
+** INVARIANTS:
+**
+** {H16351} The [sqlite3_soft_heap_limit(N)] interface places a soft limit
+** of N bytes on the amount of heap memory that may be allocated
+** using [sqlite3_malloc()] or [sqlite3_realloc()] at any point
+** in time.
+**
+** {H16352} If a call to [sqlite3_malloc()] or [sqlite3_realloc()] would
+** cause the total amount of allocated memory to exceed the
+** soft heap limit, then [sqlite3_release_memory()] is invoked
+** in an attempt to reduce the memory usage prior to proceeding
+** with the memory allocation attempt.
+**
+** {H16353} Calls to [sqlite3_malloc()] or [sqlite3_realloc()] that trigger
+** attempts to reduce memory usage through the soft heap limit
+** mechanism continue even if the attempt to reduce memory
+** usage is unsuccessful.
+**
+** {H16354} A negative or zero value for N in a call to
+** [sqlite3_soft_heap_limit(N)] means that there is no soft
+** heap limit and [sqlite3_release_memory()] will only be
+** called when memory is completely exhausted.
+**
+** {H16355} The default value for the soft heap limit is zero.
+**
+** {H16358} Each call to [sqlite3_soft_heap_limit(N)] overrides the
+** values set by all prior calls.
+*/
+void sqlite3_soft_heap_limit(int);
+
+/*
+** CAPI3REF: Extract Metadata About A Column Of A Table {H12850} <S60300>
+**
+** This routine returns metadata about a specific column of a specific
+** database table accessible using the [database connection] handle
+** passed as the first function argument.
+**
+** The column is identified by the second, third and fourth parameters to
+** this function. The second parameter is either the name of the database
+** (i.e. "main", "temp" or an attached database) containing the specified
+** table or NULL. If it is NULL, then all attached databases are searched
+** for the table using the same algorithm used by the database engine to
+** resolve unqualified table references.
+**
+** The third and fourth parameters to this function are the table and column
+** name of the desired column, respectively. Neither of these parameters
+** may be NULL.
+**
+** Metadata is returned by writing to the memory locations passed as the 5th
+** and subsequent parameters to this function. Any of these arguments may be
+** NULL, in which case the corresponding element of metadata is omitted.
+**
+** <blockquote>
+** <table border="1">
+** <tr><th> Parameter <th> Output<br>Type <th> Description
+**
+** <tr><td> 5th <td> const char* <td> Data type
+** <tr><td> 6th <td> const char* <td> Name of default collation sequence
+** <tr><td> 7th <td> int <td> True if column has a NOT NULL constraint
+** <tr><td> 8th <td> int <td> True if column is part of the PRIMARY KEY
+** <tr><td> 9th <td> int <td> True if column is AUTOINCREMENT
+** </table>
+** </blockquote>
+**
+** The memory pointed to by the character pointers returned for the
+** declaration type and collation sequence is valid only until the next
+** call to any SQLite API function.
+**
+** If the specified table is actually a view, an [error code] is returned.
+**
+** If the specified column is "rowid", "oid" or "_rowid_" and an
+** INTEGER PRIMARY KEY column has been explicitly declared, then the output
+** parameters are set for the explicitly declared column. If there is no
+** explicitly declared INTEGER PRIMARY KEY column, then the output
+** parameters are set as follows:
+**
+** <pre>
+** data type: "INTEGER"
+** collation sequence: "BINARY"
+** not null: 0
+** primary key: 1
+** auto increment: 0
+** </pre>
+**
+** This function may load one or more schemas from database files. If an
+** error occurs during this process, or if the requested table or column
+** cannot be found, an [error code] is returned and an error message left
+** in the [database connection] (to be retrieved using sqlite3_errmsg()).
+**
+** This API is only available if the library was compiled with the
+** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+*/
+int sqlite3_table_column_metadata(
+ sqlite3 *db, /* Connection handle */
+ const char *zDbName, /* Database name or NULL */
+ const char *zTableName, /* Table name */
+ const char *zColumnName, /* Column name */
+ char const **pzDataType, /* OUTPUT: Declared data type */
+ char const **pzCollSeq, /* OUTPUT: Collation sequence name */
+ int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
+ int *pPrimaryKey, /* OUTPUT: True if column part of PK */
+ int *pAutoinc /* OUTPUT: True if column is auto-increment */
+);
+
+/*
+** CAPI3REF: Load An Extension {H12600} <S20500>
+**
+** This interface loads an SQLite extension library from the named file.
+**
+** {H12601} The sqlite3_load_extension() interface attempts to load an
+** SQLite extension library contained in the file zFile.
+**
+** {H12602} The entry point is zProc.
+**
+** {H12603} zProc may be 0, in which case the name of the entry point
+** defaults to "sqlite3_extension_init".
+**
+** {H12604} The sqlite3_load_extension() interface shall return
+** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
+**
+** {H12605} If an error occurs and pzErrMsg is not 0, then the
+** [sqlite3_load_extension()] interface shall attempt to
+** fill *pzErrMsg with error message text stored in memory
+** obtained from [sqlite3_malloc()]. {END} The calling function
+** should free this memory by calling [sqlite3_free()].
+**
+** {H12606} Extension loading must be enabled using
+** [sqlite3_enable_load_extension()] prior to calling this API,
+** otherwise an error will be returned.
+*/
+int sqlite3_load_extension(
+ sqlite3 *db, /* Load the extension into this database connection */
+ const char *zFile, /* Name of the shared library containing extension */
+ const char *zProc, /* Entry point. Derived from zFile if 0 */
+ char **pzErrMsg /* Put error message here if not 0 */
+);
+
+/*
+** CAPI3REF: Enable Or Disable Extension Loading {H12620} <S20500>
+**
+** So as not to open security holes in older applications that are
+** unprepared to deal with extension loading, and as a means of disabling
+** extension loading while evaluating user-entered SQL, the following API
+** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
+**
+** Extension loading is off by default. See ticket #1863.
+**
+** {H12621} Call the sqlite3_enable_load_extension() routine with onoff==1
+** to turn extension loading on and call it with onoff==0 to turn
+** it back off again.
+**
+** {H12622} Extension loading is off by default.
+*/
+int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
+
+/*
+** CAPI3REF: Automatically Load An Extensions {H12640} <S20500>
+**
+** This API can be invoked at program startup in order to register
+** one or more statically linked extensions that will be available
+** to all new [database connections]. {END}
+**
+** This routine stores a pointer to the extension in an array that is
+** obtained from [sqlite3_malloc()]. If you run a memory leak checker
+** on your program and it reports a leak because of this array, invoke
+** [sqlite3_reset_auto_extension()] prior to shutdown to free the memory.
+**
+** {H12641} This function registers an extension entry point that is
+** automatically invoked whenever a new [database connection]
+** is opened using [sqlite3_open()], [sqlite3_open16()],
+** or [sqlite3_open_v2()].
+**
+** {H12642} Duplicate extensions are detected so calling this routine
+** multiple times with the same extension is harmless.
+**
+** {H12643} This routine stores a pointer to the extension in an array
+** that is obtained from [sqlite3_malloc()].
+**
+** {H12644} Automatic extensions apply across all threads.
+*/
+int sqlite3_auto_extension(void *xEntryPoint);
+
+/*
+** CAPI3REF: Reset Automatic Extension Loading {H12660} <S20500>
+**
+** This function disables all previously registered automatic
+** extensions. {END} It undoes the effect of all prior
+** [sqlite3_auto_extension()] calls.
+**
+** {H12661} This function disables all previously registered
+** automatic extensions.
+**
+** {H12662} This function disables automatic extensions in all threads.
+*/
+void sqlite3_reset_auto_extension(void);
+
+/*
+****** EXPERIMENTAL - subject to change without notice **************
+**
+** The interface to the virtual-table mechanism is currently considered
+** to be experimental. The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+*/
+
+/*
+** Structures used by the virtual table interface
+*/
+typedef struct sqlite3_vtab sqlite3_vtab;
+typedef struct sqlite3_index_info sqlite3_index_info;
+typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
+typedef struct sqlite3_module sqlite3_module;
+
+/*
+** CAPI3REF: Virtual Table Object {H18000} <S20400>
+** KEYWORDS: sqlite3_module
+** EXPERIMENTAL
+**
+** A module is a class of virtual tables. Each module is defined
+** by an instance of the following structure. This structure consists
+** mostly of methods for the module.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_module {
+ int iVersion;
+ int (*xCreate)(sqlite3*, void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab, char**);
+ int (*xConnect)(sqlite3*, void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVTab, char**);
+ int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+ int (*xDisconnect)(sqlite3_vtab *pVTab);
+ int (*xDestroy)(sqlite3_vtab *pVTab);
+ int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+ int (*xClose)(sqlite3_vtab_cursor*);
+ int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+ int (*xNext)(sqlite3_vtab_cursor*);
+ int (*xEof)(sqlite3_vtab_cursor*);
+ int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
+ int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid);
+ int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *);
+ int (*xBegin)(sqlite3_vtab *pVTab);
+ int (*xSync)(sqlite3_vtab *pVTab);
+ int (*xCommit)(sqlite3_vtab *pVTab);
+ int (*xRollback)(sqlite3_vtab *pVTab);
+ int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg);
+ int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+};
+
+/*
+** CAPI3REF: Virtual Table Indexing Information {H18100} <S20400>
+** KEYWORDS: sqlite3_index_info
+** EXPERIMENTAL
+**
+** The sqlite3_index_info structure and its substructures is used to
+** pass information into and receive the reply from the xBestIndex
+** method of an sqlite3_module. The fields under **Inputs** are the
+** inputs to xBestIndex and are read-only. xBestIndex inserts its
+** results into the **Outputs** fields.
+**
+** The aConstraint[] array records WHERE clause constraints of the form:
+**
+** <pre>column OP expr</pre>
+**
+** where OP is =, &lt;, &lt;=, &gt;, or &gt;=. The particular operator is
+** stored in aConstraint[].op. The index of the column is stored in
+** aConstraint[].iColumn. aConstraint[].usable is TRUE if the
+** expr on the right-hand side can be evaluated (and thus the constraint
+** is usable) and false if it cannot.
+**
+** The optimizer automatically inverts terms of the form "expr OP column"
+** and makes other simplifications to the WHERE clause in an attempt to
+** get as many WHERE clause terms into the form shown above as possible.
+** The aConstraint[] array only reports WHERE clause terms in the correct
+** form that refer to the particular virtual table being queried.
+**
+** Information about the ORDER BY clause is stored in aOrderBy[].
+** Each term of aOrderBy records a column of the ORDER BY clause.
+**
+** The xBestIndex method must fill aConstraintUsage[] with information
+** about what parameters to pass to xFilter. If argvIndex>0 then
+** the right-hand side of the corresponding aConstraint[] is evaluated
+** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit
+** is true, then the constraint is assumed to be fully handled by the
+** virtual table and is not checked again by SQLite.
+**
+** The idxNum and idxPtr values are recorded and passed into xFilter.
+** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true.
+**
+** The orderByConsumed means that output from xFilter will occur in
+** the correct order to satisfy the ORDER BY clause so that no separate
+** sorting step is required.
+**
+** The estimatedCost value is an estimate of the cost of doing the
+** particular lookup. A full scan of a table with N entries should have
+** a cost of N. A binary search of a table of N entries should have a
+** cost of approximately log(N).
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_index_info {
+ /* Inputs */
+ int nConstraint; /* Number of entries in aConstraint */
+ struct sqlite3_index_constraint {
+ int iColumn; /* Column on left-hand side of constraint */
+ unsigned char op; /* Constraint operator */
+ unsigned char usable; /* True if this constraint is usable */
+ int iTermOffset; /* Used internally - xBestIndex should ignore */
+ } *aConstraint; /* Table of WHERE clause constraints */
+ int nOrderBy; /* Number of terms in the ORDER BY clause */
+ struct sqlite3_index_orderby {
+ int iColumn; /* Column number */
+ unsigned char desc; /* True for DESC. False for ASC. */
+ } *aOrderBy; /* The ORDER BY clause */
+ /* Outputs */
+ struct sqlite3_index_constraint_usage {
+ int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ unsigned char omit; /* Do not code a test for this constraint */
+ } *aConstraintUsage;
+ int idxNum; /* Number used to identify the index */
+ char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ int orderByConsumed; /* True if output is already ordered */
+ double estimatedCost; /* Estimated cost of using this index */
+};
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+
+/*
+** CAPI3REF: Register A Virtual Table Implementation {H18200} <S20400>
+** EXPERIMENTAL
+**
+** This routine is used to register a new module name with a
+** [database connection]. Module names must be registered before
+** creating new virtual tables on the module, or before using
+** preexisting virtual tables of the module.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+int sqlite3_create_module(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *, /* Methods for the module */
+ void * /* Client data for xCreate/xConnect */
+);
+
+/*
+** CAPI3REF: Register A Virtual Table Implementation {H18210} <S20400>
+** EXPERIMENTAL
+**
+** This routine is identical to the [sqlite3_create_module()] method above,
+** except that it allows a destructor function to be specified. It is
+** even more experimental than the rest of the virtual tables API.
+*/
+int sqlite3_create_module_v2(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *, /* Methods for the module */
+ void *, /* Client data for xCreate/xConnect */
+ void(*xDestroy)(void*) /* Module destructor function */
+);
+
+/*
+** CAPI3REF: Virtual Table Instance Object {H18010} <S20400>
+** KEYWORDS: sqlite3_vtab
+** EXPERIMENTAL
+**
+** Every module implementation uses a subclass of the following structure
+** to describe a particular instance of the module. Each subclass will
+** be tailored to the specific needs of the module implementation.
+** The purpose of this superclass is to define certain fields that are
+** common to all module implementations.
+**
+** Virtual tables methods can set an error message by assigning a
+** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should
+** take care that any prior string is freed by a call to [sqlite3_free()]
+** prior to assigning a new string to zErrMsg. After the error message
+** is delivered up to the client application, the string will be automatically
+** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note
+** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field
+** since virtual tables are commonly implemented in loadable extensions which
+** do not have access to sqlite3MPrintf() or sqlite3Free().
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_vtab {
+ const sqlite3_module *pModule; /* The module for this virtual table */
+ int nRef; /* Used internally */
+ char *zErrMsg; /* Error message from sqlite3_mprintf() */
+ /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Virtual Table Cursor Object {H18020} <S20400>
+** KEYWORDS: sqlite3_vtab_cursor
+** EXPERIMENTAL
+**
+** Every module implementation uses a subclass of the following structure
+** to describe cursors that point into the virtual table and are used
+** to loop through the virtual table. Cursors are created using the
+** xOpen method of the module. Each module implementation will define
+** the content of a cursor structure to suit its own needs.
+**
+** This superclass exists in order to define fields of the cursor that
+** are common to all implementations.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+struct sqlite3_vtab_cursor {
+ sqlite3_vtab *pVtab; /* Virtual table of this cursor */
+ /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} <S20400>
+** EXPERIMENTAL
+**
+** The xCreate and xConnect methods of a module use the following API
+** to declare the format (the names and datatypes of the columns) of
+** the virtual tables they implement.
+**
+** This interface is experimental and is subject to change or
+** removal in future releases of SQLite.
+*/
+int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
+
+/*
+** CAPI3REF: Overload A Function For A Virtual Table {H18300} <S20400>
+** EXPERIMENTAL
+**
+** Virtual tables can provide alternative implementations of functions
+** using the xFindFunction method. But global versions of those functions
+** must exist in order to be overloaded.
+**
+** This API makes sure a global version of a function with a particular
+** name and number of parameters exists. If no such function exists
+** before this API is called, a new function is created. The implementation
+** of the new function always causes an exception to be thrown. So
+** the new function is not good for anything by itself. Its only
+** purpose is to be a placeholder function that can be overloaded
+** by virtual tables.
+**
+** This API should be considered part of the virtual table interface,
+** which is experimental and subject to change.
+*/
+int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
+
+/*
+** The interface to the virtual-table mechanism defined above (back up
+** to a comment remarkably similar to this one) is currently considered
+** to be experimental. The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+**
+****** EXPERIMENTAL - subject to change without notice **************
+*/
+
+/*
+** CAPI3REF: A Handle To An Open BLOB {H17800} <S30230>
+** KEYWORDS: {BLOB handle} {BLOB handles}
+**
+** An instance of this object represents an open BLOB on which
+** [sqlite3_blob_open | incremental BLOB I/O] can be performed.
+** Objects of this type are created by [sqlite3_blob_open()]
+** and destroyed by [sqlite3_blob_close()].
+** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces
+** can be used to read or write small subsections of the BLOB.
+** The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes.
+*/
+typedef struct sqlite3_blob sqlite3_blob;
+
+/*
+** CAPI3REF: Open A BLOB For Incremental I/O {H17810} <S30230>
+**
+** This interfaces opens a [BLOB handle | handle] to the BLOB located
+** in row iRow, column zColumn, table zTable in database zDb;
+** in other words, the same BLOB that would be selected by:
+**
+** <pre>
+** SELECT zColumn FROM zDb.zTable WHERE rowid = iRow;
+** </pre> {END}
+**
+** If the flags parameter is non-zero, the the BLOB is opened for read
+** and write access. If it is zero, the BLOB is opened for read access.
+**
+** Note that the database name is not the filename that contains
+** the database but rather the symbolic name of the database that
+** is assigned when the database is connected using [ATTACH].
+** For the main database file, the database name is "main".
+** For TEMP tables, the database name is "temp".
+**
+** On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
+** to *ppBlob. Otherwise an [error code] is returned and any value written
+** to *ppBlob should not be used by the caller.
+** This function sets the [database connection] error code and message
+** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()].
+**
+** If the row that a BLOB handle points to is modified by an
+** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
+** then the BLOB handle is marked as "expired".
+** This is true if any column of the row is changed, even a column
+** other than the one the BLOB handle is open on.
+** Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for
+** a expired BLOB handle fail with an return code of [SQLITE_ABORT].
+** Changes written into a BLOB prior to the BLOB expiring are not
+** rollback by the expiration of the BLOB. Such changes will eventually
+** commit if the transaction continues to completion.
+**
+** INVARIANTS:
+**
+** {H17813} A successful invocation of the [sqlite3_blob_open(D,B,T,C,R,F,P)]
+** interface shall open an [sqlite3_blob] object P on the BLOB
+** in column C of the table T in the database B on
+** the [database connection] D.
+**
+** {H17814} A successful invocation of [sqlite3_blob_open(D,...)] shall start
+** a new transaction on the [database connection] D if that
+** connection is not already in a transaction.
+**
+** {H17816} The [sqlite3_blob_open(D,B,T,C,R,F,P)] interface shall open
+** the BLOB for read and write access if and only if the F
+** parameter is non-zero.
+**
+** {H17819} The [sqlite3_blob_open()] interface shall return [SQLITE_OK] on
+** success and an appropriate [error code] on failure.
+**
+** {H17821} If an error occurs during evaluation of [sqlite3_blob_open(D,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error.
+**
+** {H17824} If any column in the row that a [sqlite3_blob] has open is
+** changed by a separate [UPDATE] or [DELETE] statement or by
+** an [ON CONFLICT] side effect, then the [sqlite3_blob] shall
+** be marked as invalid.
+*/
+int sqlite3_blob_open(
+ sqlite3*,
+ const char *zDb,
+ const char *zTable,
+ const char *zColumn,
+ sqlite3_int64 iRow,
+ int flags,
+ sqlite3_blob **ppBlob
+);
+
+/*
+** CAPI3REF: Close A BLOB Handle {H17830} <S30230>
+**
+** Closes an open [BLOB handle].
+**
+** Closing a BLOB shall cause the current transaction to commit
+** if there are no other BLOBs, no pending prepared statements, and the
+** database connection is in [autocommit mode].
+** If any writes were made to the BLOB, they might be held in cache
+** until the close operation if they will fit. {END}
+**
+** Closing the BLOB often forces the changes
+** out to disk and so if any I/O errors occur, they will likely occur
+** at the time when the BLOB is closed. {H17833} Any errors that occur during
+** closing are reported as a non-zero return value.
+**
+** The BLOB is closed unconditionally. Even if this routine returns
+** an error code, the BLOB is still closed.
+**
+** INVARIANTS:
+**
+** {H17833} The [sqlite3_blob_close(P)] interface closes an [sqlite3_blob]
+** object P previously opened using [sqlite3_blob_open()].
+**
+** {H17836} Closing an [sqlite3_blob] object using
+** [sqlite3_blob_close()] shall cause the current transaction to
+** commit if there are no other open [sqlite3_blob] objects
+** or [prepared statements] on the same [database connection] and
+** the database connection is in [autocommit mode].
+**
+** {H17839} The [sqlite3_blob_close(P)] interfaces shall close the
+** [sqlite3_blob] object P unconditionally, even if
+** [sqlite3_blob_close(P)] returns something other than [SQLITE_OK].
+*/
+int sqlite3_blob_close(sqlite3_blob *);
+
+/*
+** CAPI3REF: Return The Size Of An Open BLOB {H17840} <S30230>
+**
+** Returns the size in bytes of the BLOB accessible via the open
+** []BLOB handle] in its only argument.
+**
+** INVARIANTS:
+**
+** {H17843} The [sqlite3_blob_bytes(P)] interface returns the size
+** in bytes of the BLOB that the [sqlite3_blob] object P
+** refers to.
+*/
+int sqlite3_blob_bytes(sqlite3_blob *);
+
+/*
+** CAPI3REF: Read Data From A BLOB Incrementally {H17850} <S30230>
+**
+** This function is used to read data from an open [BLOB handle] into a
+** caller-supplied buffer. N bytes of data are copied into buffer Z
+** from the open BLOB, starting at offset iOffset.
+**
+** If offset iOffset is less than N bytes from the end of the BLOB,
+** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is
+** less than zero, [SQLITE_ERROR] is returned and no data is read.
+**
+** An attempt to read from an expired [BLOB handle] fails with an
+** error code of [SQLITE_ABORT].
+**
+** On success, SQLITE_OK is returned.
+** Otherwise, an [error code] or an [extended error code] is returned.
+**
+** INVARIANTS:
+**
+** {H17853} A successful invocation of [sqlite3_blob_read(P,Z,N,X)]
+** shall reads N bytes of data out of the BLOB referenced by
+** [BLOB handle] P beginning at offset X and store those bytes
+** into buffer Z.
+**
+** {H17856} In [sqlite3_blob_read(P,Z,N,X)] if the size of the BLOB
+** is less than N+X bytes, then the function shall leave the
+** Z buffer unchanged and return [SQLITE_ERROR].
+**
+** {H17859} In [sqlite3_blob_read(P,Z,N,X)] if X or N is less than zero
+** then the function shall leave the Z buffer unchanged
+** and return [SQLITE_ERROR].
+**
+** {H17862} The [sqlite3_blob_read(P,Z,N,X)] interface shall return [SQLITE_OK]
+** if N bytes are successfully read into buffer Z.
+**
+** {H17863} If the [BLOB handle] P is expired and X and N are within bounds
+** then [sqlite3_blob_read(P,Z,N,X)] shall leave the Z buffer
+** unchanged and return [SQLITE_ABORT].
+**
+** {H17865} If the requested read could not be completed,
+** the [sqlite3_blob_read(P,Z,N,X)] interface shall return an
+** appropriate [error code] or [extended error code].
+**
+** {H17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error, where D is the
+** [database connection] that was used to open the [BLOB handle] P.
+*/
+int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
+
+/*
+** CAPI3REF: Write Data Into A BLOB Incrementally {H17870} <S30230>
+**
+** This function is used to write data into an open [BLOB handle] from a
+** caller-supplied buffer. N bytes of data are copied from the buffer Z
+** into the open BLOB, starting at offset iOffset.
+**
+** If the [BLOB handle] passed as the first argument was not opened for
+** writing (the flags parameter to [sqlite3_blob_open()] was zero),
+** this function returns [SQLITE_READONLY].
+**
+** This function may only modify the contents of the BLOB; it is
+** not possible to increase the size of a BLOB using this API.
+** If offset iOffset is less than N bytes from the end of the BLOB,
+** [SQLITE_ERROR] is returned and no data is written. If N is
+** less than zero [SQLITE_ERROR] is returned and no data is written.
+**
+** An attempt to write to an expired [BLOB handle] fails with an
+** error code of [SQLITE_ABORT]. Writes to the BLOB that occurred
+** before the [BLOB handle] expired are not rolled back by the
+** expiration of the handle, though of course those changes might
+** have been overwritten by the statement that expired the BLOB handle
+** or by other independent statements.
+**
+** On success, SQLITE_OK is returned.
+** Otherwise, an [error code] or an [extended error code] is returned.
+**
+** INVARIANTS:
+**
+** {H17873} A successful invocation of [sqlite3_blob_write(P,Z,N,X)]
+** shall write N bytes of data from buffer Z into the BLOB
+** referenced by [BLOB handle] P beginning at offset X into
+** the BLOB.
+**
+** {H17874} In the absence of other overridding changes, the changes
+** written to a BLOB by [sqlite3_blob_write()] shall
+** remain in effect after the associated [BLOB handle] expires.
+**
+** {H17875} If the [BLOB handle] P was opened for reading only then
+** an invocation of [sqlite3_blob_write(P,Z,N,X)] shall leave
+** the referenced BLOB unchanged and return [SQLITE_READONLY].
+**
+** {H17876} If the size of the BLOB referenced by [BLOB handle] P is
+** less than N+X bytes then [sqlite3_blob_write(P,Z,N,X)] shall
+** leave the BLOB unchanged and return [SQLITE_ERROR].
+**
+** {H17877} If the [BLOB handle] P is expired and X and N are within bounds
+** then [sqlite3_blob_read(P,Z,N,X)] shall leave the BLOB
+** unchanged and return [SQLITE_ABORT].
+**
+** {H17879} If X or N are less than zero then [sqlite3_blob_write(P,Z,N,X)]
+** shall leave the BLOB referenced by [BLOB handle] P unchanged
+** and return [SQLITE_ERROR].
+**
+** {H17882} The [sqlite3_blob_write(P,Z,N,X)] interface shall return
+** [SQLITE_OK] if N bytes where successfully written into the BLOB.
+**
+** {H17885} If the requested write could not be completed,
+** the [sqlite3_blob_write(P,Z,N,X)] interface shall return an
+** appropriate [error code] or [extended error code].
+**
+** {H17888} If an error occurs during evaluation of [sqlite3_blob_write(D,...)]
+** then subsequent calls to [sqlite3_errcode(D)],
+** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return
+** information appropriate for that error.
+*/
+int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
+
+/* Begin preload-cache.patch for Chromium */
+/*
+** Preload the databases into the pager cache, up to the maximum size of the
+** pager cache.
+**
+** For a database to be loaded successfully, the pager must be active. That is,
+** there must be an open statement on that database. See sqlite3pager_loadall
+**
+** There might be many databases attached to the given connection. We iterate
+** them all and try to load them. If none are loadable successfully, we return
+** an error. Otherwise, we return OK.
+*/
+int sqlite3Preload(sqlite3 *db);
+/* End preload-cache.patch for Chromium */
+
+/*
+** CAPI3REF: Virtual File System Objects {H11200} <S20100>
+**
+** A virtual filesystem (VFS) is an [sqlite3_vfs] object
+** that SQLite uses to interact
+** with the underlying operating system. Most SQLite builds come with a
+** single default VFS that is appropriate for the host computer.
+** New VFSes can be registered and existing VFSes can be unregistered.
+** The following interfaces are provided.
+**
+** The sqlite3_vfs_find() interface returns a pointer to a VFS given its name.
+** Names are case sensitive.
+** Names are zero-terminated UTF-8 strings.
+** If there is no match, a NULL pointer is returned.
+** If zVfsName is NULL then the default VFS is returned.
+**
+** New VFSes are registered with sqlite3_vfs_register().
+** Each new VFS becomes the default VFS if the makeDflt flag is set.
+** The same VFS can be registered multiple times without injury.
+** To make an existing VFS into the default VFS, register it again
+** with the makeDflt flag set. If two different VFSes with the
+** same name are registered, the behavior is undefined. If a
+** VFS is registered with a name that is NULL or an empty string,
+** then the behavior is undefined.
+**
+** Unregister a VFS with the sqlite3_vfs_unregister() interface.
+** If the default VFS is unregistered, another VFS is chosen as
+** the default. The choice for the new VFS is arbitrary.
+**
+** INVARIANTS:
+**
+** {H11203} The [sqlite3_vfs_find(N)] interface returns a pointer to the
+** registered [sqlite3_vfs] object whose name exactly matches
+** the zero-terminated UTF-8 string N, or it returns NULL if
+** there is no match.
+**
+** {H11206} If the N parameter to [sqlite3_vfs_find(N)] is NULL then
+** the function returns a pointer to the default [sqlite3_vfs]
+** object if there is one, or NULL if there is no default
+** [sqlite3_vfs] object.
+**
+** {H11209} The [sqlite3_vfs_register(P,F)] interface registers the
+** well-formed [sqlite3_vfs] object P using the name given
+** by the zName field of the object.
+**
+** {H11212} Using the [sqlite3_vfs_register(P,F)] interface to register
+** the same [sqlite3_vfs] object multiple times is a harmless no-op.
+**
+** {H11215} The [sqlite3_vfs_register(P,F)] interface makes the [sqlite3_vfs]
+** object P the default [sqlite3_vfs] object if F is non-zero.
+**
+** {H11218} The [sqlite3_vfs_unregister(P)] interface unregisters the
+** [sqlite3_vfs] object P so that it is no longer returned by
+** subsequent calls to [sqlite3_vfs_find()].
+*/
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
+int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
+int sqlite3_vfs_unregister(sqlite3_vfs*);
+
+/*
+** CAPI3REF: Mutexes {H17000} <S20000>
+**
+** The SQLite core uses these routines for thread
+** synchronization. Though they are intended for internal
+** use by SQLite, code that links against SQLite is
+** permitted to use any of these routines.
+**
+** The SQLite source code contains multiple implementations
+** of these mutex routines. An appropriate implementation
+** is selected automatically at compile-time. The following
+** implementations are available in the SQLite core:
+**
+** <ul>
+** <li> SQLITE_MUTEX_OS2
+** <li> SQLITE_MUTEX_PTHREAD
+** <li> SQLITE_MUTEX_W32
+** <li> SQLITE_MUTEX_NOOP
+** </ul>
+**
+** The SQLITE_MUTEX_NOOP implementation is a set of routines
+** that does no real locking and is appropriate for use in
+** a single-threaded application. The SQLITE_MUTEX_OS2,
+** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
+** are appropriate for use on OS/2, Unix, and Windows.
+**
+** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
+** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
+** implementation is included with the library. In this case the
+** application must supply a custom mutex implementation using the
+** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
+** before calling sqlite3_initialize() or any other public sqlite3_
+** function that calls sqlite3_initialize().
+**
+** {H17011} The sqlite3_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. {H17012} If it returns NULL
+** that means that a mutex could not be allocated. {H17013} SQLite
+** will unwind its stack and return an error. {H17014} The argument
+** to sqlite3_mutex_alloc() is one of these integer constants:
+**
+** <ul>
+** <li> SQLITE_MUTEX_FAST
+** <li> SQLITE_MUTEX_RECURSIVE
+** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MEM
+** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_PRNG
+** <li> SQLITE_MUTEX_STATIC_LRU
+** <li> SQLITE_MUTEX_STATIC_LRU2
+** </ul>
+**
+** {H17015} The first two constants cause sqlite3_mutex_alloc() to create
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END}
+** The mutex implementation does not need to make a distinction
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
+** not want to. {H17016} But SQLite will only request a recursive mutex in
+** cases where it really needs one. {END} If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to SQLITE_MUTEX_FAST.
+**
+** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return
+** a pointer to a static preexisting mutex. {END} Four static mutexes are
+** used by the current version of SQLite. Future versions of SQLite
+** may add additional static mutexes. Static mutexes are for internal
+** use by SQLite only. Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
+** SQLITE_MUTEX_RECURSIVE.
+**
+** {H17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
+** returns a different mutex on every call. {H17034} But for the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+**
+** {H17019} The sqlite3_mutex_free() routine deallocates a previously
+** allocated dynamic mutex. {H17020} SQLite is careful to deallocate every
+** dynamic mutex that it allocates. {A17021} The dynamic mutexes must not be in
+** use when they are deallocated. {A17022} Attempting to deallocate a static
+** mutex results in undefined behavior. {H17023} SQLite never deallocates
+** a static mutex. {END}
+**
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
+** to enter a mutex. {H17024} If another thread is already within the mutex,
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
+** SQLITE_BUSY. {H17025} The sqlite3_mutex_try() interface returns [SQLITE_OK]
+** upon successful entry. {H17026} Mutexes created using
+** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread.
+** {H17027} In such cases the,
+** mutex must be exited an equal number of times before another thread
+** can enter. {A17028} If the same thread tries to enter any other
+** kind of mutex more than once, the behavior is undefined.
+** {H17029} SQLite will never exhibit
+** such behavior in its own use of mutexes.
+**
+** Some systems (for example, Windows 95) do not support the operation
+** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
+** will always return SQLITE_BUSY. {H17030} The SQLite core only ever uses
+** sqlite3_mutex_try() as an optimization so this is acceptable behavior.
+**
+** {H17031} The sqlite3_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread. {A17032} The behavior
+** is undefined if the mutex is not currently entered by the
+** calling thread or is not currently allocated. {H17033} SQLite will
+** never do either. {END}
+**
+** If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
+** sqlite3_mutex_leave() is a NULL pointer, then all three routines
+** behave as no-ops.
+**
+** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
+*/
+sqlite3_mutex *sqlite3_mutex_alloc(int);
+void sqlite3_mutex_free(sqlite3_mutex*);
+void sqlite3_mutex_enter(sqlite3_mutex*);
+int sqlite3_mutex_try(sqlite3_mutex*);
+void sqlite3_mutex_leave(sqlite3_mutex*);
+
+/*
+** CAPI3REF: Mutex Methods Object {H17120} <S20130>
+** EXPERIMENTAL
+**
+** An instance of this structure defines the low-level routines
+** used to allocate and use mutexes.
+**
+** Usually, the default mutex implementations provided by SQLite are
+** sufficient, however the user has the option of substituting a custom
+** implementation for specialized deployments or systems for which SQLite
+** does not provide a suitable implementation. In this case, the user
+** creates and populates an instance of this structure to pass
+** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.
+** Additionally, an instance of this structure can be used as an
+** output variable when querying the system for the current mutex
+** implementation, using the [SQLITE_CONFIG_GETMUTEX] option.
+**
+** The xMutexInit method defined by this structure is invoked as
+** part of system initialization by the sqlite3_initialize() function.
+** {H17001} The xMutexInit routine shall be called by SQLite once for each
+** effective call to [sqlite3_initialize()].
+**
+** The xMutexEnd method defined by this structure is invoked as
+** part of system shutdown by the sqlite3_shutdown() function. The
+** implementation of this method is expected to release all outstanding
+** resources obtained by the mutex methods implementation, especially
+** those obtained by the xMutexInit method. {H17003} The xMutexEnd()
+** interface shall be invoked once for each call to [sqlite3_shutdown()].
+**
+** The remaining seven methods defined by this structure (xMutexAlloc,
+** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and
+** xMutexNotheld) implement the following interfaces (respectively):
+**
+** <ul>
+** <li> [sqlite3_mutex_alloc()] </li>
+** <li> [sqlite3_mutex_free()] </li>
+** <li> [sqlite3_mutex_enter()] </li>
+** <li> [sqlite3_mutex_try()] </li>
+** <li> [sqlite3_mutex_leave()] </li>
+** <li> [sqlite3_mutex_held()] </li>
+** <li> [sqlite3_mutex_notheld()] </li>
+** </ul>
+**
+** The only difference is that the public sqlite3_XXX functions enumerated
+** above silently ignore any invocations that pass a NULL pointer instead
+** of a valid mutex handle. The implementations of the methods defined
+** by this structure are not required to handle this case, the results
+** of passing a NULL pointer instead of a valid mutex handle are undefined
+** (i.e. it is acceptable to provide an implementation that segfaults if
+** it is passed a NULL pointer).
+*/
+typedef struct sqlite3_mutex_methods sqlite3_mutex_methods;
+struct sqlite3_mutex_methods {
+ int (*xMutexInit)(void);
+ int (*xMutexEnd)(void);
+ sqlite3_mutex *(*xMutexAlloc)(int);
+ void (*xMutexFree)(sqlite3_mutex *);
+ void (*xMutexEnter)(sqlite3_mutex *);
+ int (*xMutexTry)(sqlite3_mutex *);
+ void (*xMutexLeave)(sqlite3_mutex *);
+ int (*xMutexHeld)(sqlite3_mutex *);
+ int (*xMutexNotheld)(sqlite3_mutex *);
+};
+
+/*
+** CAPI3REF: Mutex Verification Routines {H17080} <S20130> <S30800>
+**
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
+** are intended for use inside assert() statements. {H17081} The SQLite core
+** never uses these routines except inside an assert() and applications
+** are advised to follow the lead of the core. {H17082} The core only
+** provides implementations for these routines when it is compiled
+** with the SQLITE_DEBUG flag. {A17087} External mutex implementations
+** are only required to provide these routines if SQLITE_DEBUG is
+** defined and if NDEBUG is not defined.
+**
+** {H17083} These routines should return true if the mutex in their argument
+** is held or not held, respectively, by the calling thread.
+**
+** {X17084} The implementation is not required to provided versions of these
+** routines that actually work. If the implementation does not provide working
+** versions of these routines, it should at least provide stubs that always
+** return true so that one does not get spurious assertion failures.
+**
+** {H17085} If the argument to sqlite3_mutex_held() is a NULL pointer then
+** the routine should return 1. {END} This seems counter-intuitive since
+** clearly the mutex cannot be held if it does not exist. But the
+** the reason the mutex does not exist is because the build is not
+** using mutexes. And we do not want the assert() containing the
+** call to sqlite3_mutex_held() to fail, so a non-zero return is
+** the appropriate thing to do. {H17086} The sqlite3_mutex_notheld()
+** interface should also return 1 when given a NULL pointer.
+*/
+int sqlite3_mutex_held(sqlite3_mutex*);
+int sqlite3_mutex_notheld(sqlite3_mutex*);
+
+/*
+** CAPI3REF: Mutex Types {H17001} <H17000>
+**
+** The [sqlite3_mutex_alloc()] interface takes a single argument
+** which is one of these integer constants.
+**
+** The set of static mutexes may change from one SQLite release to the
+** next. Applications that override the built-in mutex logic must be
+** prepared to accommodate additional static mutexes.
+*/
+#define SQLITE_MUTEX_FAST 0
+#define SQLITE_MUTEX_RECURSIVE 1
+#define SQLITE_MUTEX_STATIC_MASTER 2
+#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
+#define SQLITE_MUTEX_STATIC_MEM2 4 /* sqlite3_release_memory() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
+#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */
+
+/*
+** CAPI3REF: Low-Level Control Of Database Files {H11300} <S30800>
+**
+** {H11301} The [sqlite3_file_control()] interface makes a direct call to the
+** xFileControl method for the [sqlite3_io_methods] object associated
+** with a particular database identified by the second argument. {H11302} The
+** name of the database is the name assigned to the database by the
+** <a href="lang_attach.html">ATTACH</a> SQL command that opened the
+** database. {H11303} To control the main database file, use the name "main"
+** or a NULL pointer. {H11304} The third and fourth parameters to this routine
+** are passed directly through to the second and third parameters of
+** the xFileControl method. {H11305} The return value of the xFileControl
+** method becomes the return value of this routine.
+**
+** {H11306} If the second parameter (zDbName) does not match the name of any
+** open database file, then SQLITE_ERROR is returned. {H11307} This error
+** code is not remembered and will not be recalled by [sqlite3_errcode()]
+** or [sqlite3_errmsg()]. {A11308} The underlying xFileControl method might
+** also return SQLITE_ERROR. {A11309} There is no way to distinguish between
+** an incorrect zDbName and an SQLITE_ERROR return from the underlying
+** xFileControl method. {END}
+**
+** See also: [SQLITE_FCNTL_LOCKSTATE]
+*/
+int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
+
+/*
+** CAPI3REF: Testing Interface {H11400} <S30800>
+**
+** The sqlite3_test_control() interface is used to read out internal
+** state of SQLite and to inject faults into SQLite for testing
+** purposes. The first parameter is an operation code that determines
+** the number, meaning, and operation of all subsequent parameters.
+**
+** This interface is not for use by applications. It exists solely
+** for verifying the correct operation of the SQLite library. Depending
+** on how the SQLite library is compiled, this interface might not exist.
+**
+** The details of the operation codes, their meanings, the parameters
+** they take, and what they do are all subject to change without notice.
+** Unlike most of the SQLite API, this function is not guaranteed to
+** operate consistently from one release to the next.
+*/
+int sqlite3_test_control(int op, ...);
+
+/*
+** CAPI3REF: Testing Interface Operation Codes {H11410} <H11400>
+**
+** These constants are the valid operation code parameters used
+** as the first argument to [sqlite3_test_control()].
+**
+** These parameters and their meanings are subject to change
+** without notice. These values are for testing purposes only.
+** Applications should not use any of these parameters or the
+** [sqlite3_test_control()] interface.
+*/
+#define SQLITE_TESTCTRL_PRNG_SAVE 5
+#define SQLITE_TESTCTRL_PRNG_RESTORE 6
+#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_BITVEC_TEST 8
+#define SQLITE_TESTCTRL_FAULT_INSTALL 9
+#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
+
+/*
+** CAPI3REF: SQLite Runtime Status {H17200} <S60200>
+** EXPERIMENTAL
+**
+** This interface is used to retrieve runtime status information
+** about the preformance of SQLite, and optionally to reset various
+** highwater marks. The first argument is an integer code for
+** the specific parameter to measure. Recognized integer codes
+** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].
+** The current value of the parameter is returned into *pCurrent.
+** The highest recorded value is returned in *pHighwater. If the
+** resetFlag is true, then the highest record value is reset after
+** *pHighwater is written. Some parameters do not record the highest
+** value. For those parameters
+** nothing is written into *pHighwater and the resetFlag is ignored.
+** Other parameters record only the highwater mark and not the current
+** value. For these latter parameters nothing is written into *pCurrent.
+**
+** This routine returns SQLITE_OK on success and a non-zero
+** [error code] on failure.
+**
+** This routine is threadsafe but is not atomic. This routine can
+** called while other threads are running the same or different SQLite
+** interfaces. However the values returned in *pCurrent and
+** *pHighwater reflect the status of SQLite at different points in time
+** and it is possible that another thread might change the parameter
+** in between the times when *pCurrent and *pHighwater are written.
+**
+** See also: [sqlite3_db_status()]
+*/
+int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+
+/*
+** CAPI3REF: Database Connection Status {H17201} <S60200>
+** EXPERIMENTAL
+**
+** This interface is used to retrieve runtime status information
+** about a single [database connection]. The first argument is the
+** database connection object to be interrogated. The second argument
+** is the parameter to interrogate. Currently, the only allowed value
+** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED].
+** Additional options will likely appear in future releases of SQLite.
+**
+** The current value of the request parameter is written into *pCur
+** and the highest instantaneous value is written into *pHiwtr. If
+** the resetFlg is true, then the highest instantaneous value is
+** reset back down to the current value.
+**
+** See also: [sqlite3_status()].
+*/
+int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
+
+/*
+** CAPI3REF: Status Parameters {H17250} <H17200>
+** EXPERIMENTAL
+**
+** These integer constants designate various run-time status parameters
+** that can be returned by [sqlite3_status()].
+**
+** <dl>
+** <dt>SQLITE_STATUS_MEMORY_USED</dt>
+** <dd>This parameter is the current amount of memory checked out
+** using [sqlite3_malloc()], either directly or indirectly. The
+** figure includes calls made to [sqlite3_malloc()] by the application
+** and internal memory usage by the SQLite library. Scratch memory
+** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
+** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
+** this parameter. The amount returned is the sum of the allocation
+** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>
+**
+** <dt>SQLITE_STATUS_MALLOC_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
+** internal equivalents). Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_USED</dt>
+** <dd>This parameter returns the number of pages used out of the
+** [pagecache memory allocator] that was configured using
+** [SQLITE_CONFIG_PAGECACHE]. The
+** value returned is in pages, not in bytes.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of page cache
+** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE]
+** buffer and where forced to overflow to [sqlite3_malloc()]. The
+** returned value includes allocations that overflowed because they
+** where too large (they were larger than the "sz" parameter to
+** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
+** no space was left in the page cache.</dd>
+**
+** <dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [pagecache memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_USED</dt>
+** <dd>This parameter returns the number of allocations used out of the
+** [scratch memory allocator] configured using
+** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
+** in bytes. Since a single thread may only have one scratch allocation
+** outstanding at time, this parameter also reports the number of threads
+** using scratch memory at the same time.</dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of scratch memory
+** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH]
+** buffer and where forced to overflow to [sqlite3_malloc()]. The values
+** returned include overflows because the requested allocation was too
+** larger (that is, because the requested allocation was larger than the
+** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer
+** slots were available.
+** </dd>
+**
+** <dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [scratch memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
+** The value written into the *pCurrent parameter is undefined.</dd>
+**
+** <dt>SQLITE_STATUS_PARSER_STACK</dt>
+** <dd>This parameter records the deepest parser stack. It is only
+** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>
+** </dl>
+**
+** New status parameters may be added from time to time.
+*/
+#define SQLITE_STATUS_MEMORY_USED 0
+#define SQLITE_STATUS_PAGECACHE_USED 1
+#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2
+#define SQLITE_STATUS_SCRATCH_USED 3
+#define SQLITE_STATUS_SCRATCH_OVERFLOW 4
+#define SQLITE_STATUS_MALLOC_SIZE 5
+#define SQLITE_STATUS_PARSER_STACK 6
+#define SQLITE_STATUS_PAGECACHE_SIZE 7
+#define SQLITE_STATUS_SCRATCH_SIZE 8
+
+/*
+** CAPI3REF: Status Parameters for database connections {H17275} <H17200>
+** EXPERIMENTAL
+**
+** Status verbs for [sqlite3_db_status()].
+**
+** <dl>
+** <dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
+** <dd>This parameter returns the number of lookaside memory slots currently
+** checked out.</dd>
+** </dl>
+*/
+#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
+
+/*
+** Undo the hack that converts floating point types to integer for
+** builds on processors without floating point support.
+*/
+#ifdef SQLITE_OMIT_FLOATING_POINT
+# undef double
+#endif
+
+#ifdef __cplusplus
+} /* End of the 'extern "C"' block */
+#endif
+#endif
diff --git a/third_party/sqlite/sqlite3ext.h b/third_party/sqlite/src/sqlite3ext.h
index ba6d378..a4da1d9 100644..100755
--- a/third_party/sqlite/sqlite3ext.h
+++ b/third_party/sqlite/src/sqlite3ext.h
@@ -15,7 +15,7 @@
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
**
-** @(#) $Id: sqlite3ext.h,v 1.12 2007/07/20 10:48:36 drh Exp $
+** @(#) $Id: sqlite3ext.h,v 1.24 2008/06/30 15:09:29 danielk1977 Exp $
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
@@ -24,13 +24,13 @@
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
-** The following structure hold pointers to all of the SQLite API
+** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
-** versions of SQLite will not be able to load each others shared
+** versions of SQLite will not be able to load each others' shared
** libraries!
*/
struct sqlite3_api_routines {
@@ -78,7 +78,7 @@ struct sqlite3_api_routines {
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
- int (*create_collation16)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
+ int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
@@ -149,11 +149,50 @@ struct sqlite3_api_routines {
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
+ /* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
+ /* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
+ /* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
+ /* Added by 3.5.0 */
+ int (*bind_zeroblob)(sqlite3_stmt*,int,int);
+ int (*blob_bytes)(sqlite3_blob*);
+ int (*blob_close)(sqlite3_blob*);
+ int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
+ int (*blob_read)(sqlite3_blob*,void*,int,int);
+ int (*blob_write)(sqlite3_blob*,const void*,int,int);
+ int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
+ int (*file_control)(sqlite3*,const char*,int,void*);
+ sqlite3_int64 (*memory_highwater)(int);
+ sqlite3_int64 (*memory_used)(void);
+ sqlite3_mutex *(*mutex_alloc)(int);
+ void (*mutex_enter)(sqlite3_mutex*);
+ void (*mutex_free)(sqlite3_mutex*);
+ void (*mutex_leave)(sqlite3_mutex*);
+ int (*mutex_try)(sqlite3_mutex*);
+ int (*open_v2)(const char*,sqlite3**,int,const char*);
+ int (*release_memory)(int);
+ void (*result_error_nomem)(sqlite3_context*);
+ void (*result_error_toobig)(sqlite3_context*);
+ int (*sleep)(int);
+ void (*soft_heap_limit)(int);
+ sqlite3_vfs *(*vfs_find)(const char*);
+ int (*vfs_register)(sqlite3_vfs*,int);
+ int (*vfs_unregister)(sqlite3_vfs*);
+ int (*xthreadsafe)(void);
+ void (*result_zeroblob)(sqlite3_context*,int);
+ void (*result_error_code)(sqlite3_context*,int);
+ int (*test_control)(int, ...);
+ void (*randomness)(int,void*);
+ sqlite3 *(*context_db_handle)(sqlite3_context*);
+ int (*extended_result_codes)(sqlite3*,int);
+ int (*limit)(sqlite3*,int,int);
+ sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
+ const char *(*sql)(sqlite3_stmt*);
+ int (*status)(int,int*,int*,int);
};
/*
@@ -290,9 +329,50 @@ struct sqlite3_api_routines {
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
-#endif /* SQLITE_CORE */
+#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
+#define sqlite3_blob_bytes sqlite3_api->blob_bytes
+#define sqlite3_blob_close sqlite3_api->blob_close
+#define sqlite3_blob_open sqlite3_api->blob_open
+#define sqlite3_blob_read sqlite3_api->blob_read
+#define sqlite3_blob_write sqlite3_api->blob_write
+#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
+#define sqlite3_file_control sqlite3_api->file_control
+#define sqlite3_memory_highwater sqlite3_api->memory_highwater
+#define sqlite3_memory_used sqlite3_api->memory_used
+#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
+#define sqlite3_mutex_enter sqlite3_api->mutex_enter
+#define sqlite3_mutex_free sqlite3_api->mutex_free
+#define sqlite3_mutex_leave sqlite3_api->mutex_leave
+#define sqlite3_mutex_try sqlite3_api->mutex_try
+#define sqlite3_open_v2 sqlite3_api->open_v2
+#define sqlite3_release_memory sqlite3_api->release_memory
+#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
+#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
+#define sqlite3_sleep sqlite3_api->sleep
+#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
+#define sqlite3_vfs_find sqlite3_api->vfs_find
+#define sqlite3_vfs_register sqlite3_api->vfs_register
+#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
+#define sqlite3_threadsafe sqlite3_api->xthreadsafe
+#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
+#define sqlite3_result_error_code sqlite3_api->result_error_code
+#define sqlite3_test_control sqlite3_api->test_control
+#define sqlite3_randomness sqlite3_api->randomness
+#define sqlite3_context_db_handle sqlite3_api->context_db_handle
+#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
+#define sqlite3_limit sqlite3_api->limit
+#define sqlite3_next_stmt sqlite3_api->next_stmt
+#define sqlite3_sql sqlite3_api->sql
+#define sqlite3_status sqlite3_api->status
-#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api;
+#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
+#else
+
+#define SQLITE_EXTENSION_INIT1
+#define SQLITE_EXTENSION_INIT2(v)
+
+#endif /* SQLITE_CORE */
+
#endif /* _SQLITE3EXT_H_ */
diff --git a/third_party/sqlite/sqliteInt.h b/third_party/sqlite/src/sqliteInt.h
index 21e5dbd..68de4a1 100644..100755
--- a/third_party/sqlite/sqliteInt.h
+++ b/third_party/sqlite/src/sqliteInt.h
@@ -11,33 +11,120 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.585 2007/08/08 12:11:21 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.752 2008/08/04 20:13:27 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
+
+/*
+** Include the configuration header output by 'configure' if we're using the
+** autoconf-based build
+*/
+#ifdef _HAVE_SQLITE_CONFIG_H
+#include "config.h"
+#endif
+
#include "sqliteLimit.h"
+/* Disable nuisance warnings on Borland compilers */
+#if defined(__BORLANDC__)
+#pragma warn -rch /* unreachable code */
+#pragma warn -ccc /* Condition is always true or false */
+#pragma warn -aus /* Assigned value is never used */
+#pragma warn -csu /* Comparing signed and unsigned */
+#pragma warn -spa /* Suspicous pointer arithmetic */
+#endif
-#if defined(SQLITE_TCL) || defined(TCLSH)
-# include <tcl.h>
+/* Needed for various definitions... */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
#endif
/*
-** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
-** Setting NDEBUG makes the code smaller and run faster. So the following
-** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1
-** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out
-** feature.
+** Include standard header files as necessary
*/
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
-# define NDEBUG 1
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
#endif
/*
+** A macro used to aid in coverage testing. When doing coverage
+** testing, the condition inside the argument must be evaluated
+** both true and false in order to get full branch coverage.
+** This macro can be inserted to ensure adequate test coverage
+** in places where simple condition/decision coverage is inadequate.
+*/
+#ifdef SQLITE_COVERAGE_TEST
+ void sqlite3Coverage(int);
+# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
+#else
+# define testcase(X)
+#endif
+
+/*
+** The ALWAYS and NEVER macros surround boolean expressions which
+** are intended to always be true or false, respectively. Such
+** expressions could be omitted from the code completely. But they
+** are included in a few cases in order to enhance the resilience
+** of SQLite to unexpected behavior - to make the code "self-healing"
+** or "ductile" rather than being "brittle" and crashing at the first
+** hint of unplanned behavior.
+**
+** When doing coverage testing ALWAYS and NEVER are hard-coded to
+** be true and false so that the unreachable code then specify will
+** not be counted as untested code.
+*/
+#ifdef SQLITE_COVERAGE_TEST
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+
+/*
+** The macro unlikely() is a hint that surrounds a boolean
+** expression that is usually false. Macro likely() surrounds
+** a boolean expression that is usually true. GCC is able to
+** use these hints to generate better code, sometimes.
+*/
+#if defined(__GNUC__) && 0
+# define likely(X) __builtin_expect((X),1)
+# define unlikely(X) __builtin_expect((X),0)
+#else
+# define likely(X) !!(X)
+# define unlikely(X) !!(X)
+#endif
+
+/*
+ * This macro is used to "hide" some ugliness in casting an int
+ * value to a ptr value under the MSVC 64-bit compiler. Casting
+ * non 64-bit values to ptr types results in a "hard" error with
+ * the MSVC 64-bit compiler which this attempts to avoid.
+ *
+ * A simple compiler pragma or casting sequence could not be found
+ * to correct this in all situations, so this macro was introduced.
+ *
+ * It could be argued that the intptr_t type could be used in this
+ * case, but that type is not available on all compilers, or
+ * requires the #include of specific headers which differs between
+ * platforms.
+ */
+#define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
+#define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
+
+/*
** These #defines should enable >2GB file support on Posix if the
** underlying operating system supports it. If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
**
+** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any
+** system #includes. Hence, this block of code must be the very first
+** code in all source files.
+**
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
** on the compiler command line. This is necessary if you are compiling
** on a recent machine (ex: RedHat 7.2) but you want your code to work
@@ -56,6 +143,86 @@
# define _LARGEFILE_SOURCE 1
#endif
+
+/*
+** The SQLITE_THREADSAFE macro must be defined as either 0 or 1.
+** Older versions of SQLite used an optional THREADSAFE macro.
+** We support that for legacy
+*/
+#if !defined(SQLITE_THREADSAFE)
+#if defined(THREADSAFE)
+# define SQLITE_THREADSAFE THREADSAFE
+#else
+# define SQLITE_THREADSAFE 1
+#endif
+#endif
+
+/*
+** Exactly one of the following macros must be defined in order to
+** specify which memory allocation subsystem to use.
+**
+** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
+** SQLITE_MEMDEBUG // Debugging version of system malloc()
+** SQLITE_MEMORY_SIZE // internal allocator #1
+** SQLITE_MMAP_HEAP_SIZE // internal mmap() allocator
+** SQLITE_POW2_MEMORY_SIZE // internal power-of-two allocator
+**
+** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
+** the default.
+*/
+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\
+ defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\
+ defined(SQLITE_POW2_MEMORY_SIZE)>1
+# error "At most one of the following compile-time configuration options\
+ is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG, SQLITE_MEMORY_SIZE,\
+ SQLITE_MMAP_HEAP_SIZE, SQLITE_POW2_MEMORY_SIZE"
+#endif
+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\
+ defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\
+ defined(SQLITE_POW2_MEMORY_SIZE)==0
+# define SQLITE_SYSTEM_MALLOC 1
+#endif
+
+/*
+** If SQLITE_MALLOC_SOFT_LIMIT is defined, then try to keep the
+** sizes of memory allocations below this value where possible.
+*/
+#if defined(SQLITE_POW2_MEMORY_SIZE) && !defined(SQLITE_MALLOC_SOFT_LIMIT)
+# define SQLITE_MALLOC_SOFT_LIMIT 1024
+#endif
+
+/*
+** We need to define _XOPEN_SOURCE as follows in order to enable
+** recursive mutexes on most unix systems. But Mac OS X is different.
+** The _XOPEN_SOURCE define causes problems for Mac OS X we are told,
+** so it is omitted there. See ticket #2673.
+**
+** Later we learn that _XOPEN_SOURCE is poorly or incorrectly
+** implemented on some systems. So we avoid defining it at all
+** if it is already defined or if it is unneeded because we are
+** not doing a threadsafe build. Ticket #2681.
+**
+** See also ticket #2741.
+*/
+#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__) && SQLITE_THREADSAFE
+# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */
+#endif
+
+#if defined(SQLITE_TCL) || defined(TCLSH)
+# include <tcl.h>
+#endif
+
+/*
+** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
+** Setting NDEBUG makes the code smaller and run faster. So the following
+** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1
+** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out
+** feature.
+*/
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+
#include "sqlite3.h"
#include "hash.h"
#include "parse.h"
@@ -65,8 +232,6 @@
#include <assert.h>
#include <stddef.h>
-#define sqlite3_isnan(X) ((X)!=(X))
-
/*
** If compiling for a processor that lacks floating point support,
** substitute integer for floating-point
@@ -122,11 +287,11 @@
#endif
/*
-** Provide a default value for TEMP_STORE in case it is not specified
+** Provide a default value for SQLITE_TEMP_STORE in case it is not specified
** on the command-line
*/
-#ifndef TEMP_STORE
-# define TEMP_STORE 1
+#ifndef SQLITE_TEMP_STORE
+# define SQLITE_TEMP_STORE 1
#endif
/*
@@ -155,19 +320,39 @@
** cc '-DUINTPTR_TYPE=long long int' ...
*/
#ifndef UINT32_TYPE
-# define UINT32_TYPE unsigned int
+# ifdef HAVE_UINT32_T
+# define UINT32_TYPE uint32_t
+# else
+# define UINT32_TYPE unsigned int
+# endif
#endif
#ifndef UINT16_TYPE
-# define UINT16_TYPE unsigned short int
+# ifdef HAVE_UINT16_T
+# define UINT16_TYPE uint16_t
+# else
+# define UINT16_TYPE unsigned short int
+# endif
#endif
#ifndef INT16_TYPE
-# define INT16_TYPE short int
+# ifdef HAVE_INT16_T
+# define INT16_TYPE int16_t
+# else
+# define INT16_TYPE short int
+# endif
#endif
#ifndef UINT8_TYPE
-# define UINT8_TYPE unsigned char
+# ifdef HAVE_UINT8_T
+# define UINT8_TYPE uint8_t
+# else
+# define UINT8_TYPE unsigned char
+# endif
#endif
#ifndef INT8_TYPE
-# define INT8_TYPE signed char
+# ifdef HAVE_INT8_T
+# define INT8_TYPE int8_t
+# else
+# define INT8_TYPE signed char
+# endif
#endif
#ifndef LONGDOUBLE_TYPE
# define LONGDOUBLE_TYPE long double
@@ -184,7 +369,11 @@ typedef UINT8_TYPE i8; /* 1-byte signed integer */
** Macros to determine whether the machine is big or little endian,
** evaluated at runtime.
*/
+#ifdef SQLITE_AMALGAMATION
+const int sqlite3one;
+#else
extern const int sqlite3one;
+#endif
#if defined(i386) || defined(__i386__) || defined(_M_IX86)
# define SQLITE_BIGENDIAN 0
# define SQLITE_LITTLEENDIAN 1
@@ -196,6 +385,14 @@ extern const int sqlite3one;
#endif
/*
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
+
+/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
@@ -212,83 +409,6 @@ struct BusyHandler {
};
/*
-** Defer sourcing vdbe.h and btree.h until after the "u8" and
-** "BusyHandler typedefs.
-*/
-#include "vdbe.h"
-#include "btree.h"
-#include "pager.h"
-
-#ifdef SQLITE_MEMDEBUG
-/*
-** The following global variables are used for testing and debugging
-** only. They only work if SQLITE_MEMDEBUG is defined.
-*/
-extern int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
-extern int sqlite3_nFree; /* Number of sqliteFree() calls */
-extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
-extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */
-
-extern void *sqlite3_pFirst; /* Pointer to linked list of allocations */
-extern int sqlite3_nMaxAlloc; /* High water mark of ThreadData.nAlloc */
-extern int sqlite3_mallocDisallowed; /* assert() in sqlite3Malloc() if set */
-extern int sqlite3_isFail; /* True if all malloc calls should fail */
-extern const char *sqlite3_zFile; /* Filename to associate debug info with */
-extern int sqlite3_iLine; /* Line number for debug info */
-
-#define ENTER_MALLOC (sqlite3_zFile = __FILE__, sqlite3_iLine = __LINE__)
-#define sqliteMalloc(x) (ENTER_MALLOC, sqlite3Malloc(x,1))
-#define sqliteMallocRaw(x) (ENTER_MALLOC, sqlite3MallocRaw(x,1))
-#define sqliteRealloc(x,y) (ENTER_MALLOC, sqlite3Realloc(x,y))
-#define sqliteStrDup(x) (ENTER_MALLOC, sqlite3StrDup(x))
-#define sqliteStrNDup(x,y) (ENTER_MALLOC, sqlite3StrNDup(x,y))
-#define sqliteReallocOrFree(x,y) (ENTER_MALLOC, sqlite3ReallocOrFree(x,y))
-
-#else
-
-#define ENTER_MALLOC 0
-#define sqliteMalloc(x) sqlite3Malloc(x,1)
-#define sqliteMallocRaw(x) sqlite3MallocRaw(x,1)
-#define sqliteRealloc(x,y) sqlite3Realloc(x,y)
-#define sqliteStrDup(x) sqlite3StrDup(x)
-#define sqliteStrNDup(x,y) sqlite3StrNDup(x,y)
-#define sqliteReallocOrFree(x,y) sqlite3ReallocOrFree(x,y)
-
-#endif
-
-/* Variable sqlite3MallocHasFailed is set to true after a malloc()
-** failure occurs.
-**
-** The sqlite3MallocFailed() macro returns true if a malloc has failed
-** in this thread since the last call to sqlite3ApiExit(), or false
-** otherwise.
-*/
-extern int sqlite3MallocHasFailed;
-#define sqlite3MallocFailed() (sqlite3MallocHasFailed && sqlite3OsInMutex(1))
-
-#define sqliteFree(x) sqlite3FreeX(x)
-#define sqliteAllocSize(x) sqlite3AllocSize(x)
-
-/*
-** An instance of this structure might be allocated to store information
-** specific to a single thread.
-*/
-struct ThreadData {
- int dummy; /* So that this structure is never empty */
-
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- int nSoftHeapLimit; /* Suggested max mem allocation. No limit if <0 */
- int nAlloc; /* Number of bytes currently allocated */
- Pager *pPager; /* Linked list of all pagers in this thread */
-#endif
-
-#ifndef SQLITE_OMIT_SHARED_CACHE
- u8 useSharedData; /* True if shared pagers and schemas are enabled */
- BtShared *pBtree; /* Linked list of all currently open BTrees */
-#endif
-};
-
-/*
** Name of the master database table. The master database table
** is a special table that holds the names and attributes of all
** user tables and indices.
@@ -313,10 +433,17 @@ struct ThreadData {
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
/*
+** The following value as a destructor means to use sqlite3DbFree().
+** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT.
+*/
+#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree)
+
+/*
** Forward references to structures
*/
typedef struct AggInfo AggInfo;
typedef struct AuthContext AuthContext;
+typedef struct Bitvec Bitvec;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
typedef struct Db Db;
@@ -329,12 +456,14 @@ typedef struct IdList IdList;
typedef struct Index Index;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
+typedef struct Lookaside Lookaside;
+typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
typedef struct Select Select;
typedef struct SrcList SrcList;
-typedef struct ThreadData ThreadData;
+typedef struct StrAccum StrAccum;
typedef struct Table Table;
typedef struct TableLock TableLock;
typedef struct Token Token;
@@ -344,7 +473,18 @@ typedef struct Trigger Trigger;
typedef struct WhereInfo WhereInfo;
typedef struct WhereLevel WhereLevel;
+/*
+** Defer sourcing vdbe.h and btree.h until after the "u8" and
+** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
+** pointer types (i.e. FuncDef) defined above.
+*/
+#include "btree.h"
+#include "vdbe.h"
+#include "pager.h"
+
#include "os.h"
+#include "mutex.h"
+
/*
** Each database file to be accessed by the system is an instance
@@ -413,6 +553,38 @@ struct Schema {
#define DB_UnresetViews 0x0002 /* Some views have defined column names */
#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */
+/*
+** The number of different kinds of things that can be limited
+** using the sqlite3_limit() interface.
+*/
+#define SQLITE_N_LIMIT (SQLITE_LIMIT_VARIABLE_NUMBER+1)
+
+/*
+** Lookaside malloc is a set of fixed-size buffers that can be used
+** to satisify small transient memory allocation requests for objects
+** associated with a particular database connection. The use of
+** lookaside malloc provides a significant performance enhancement
+** (approx 10%) by avoiding numerous malloc/free requests while parsing
+** SQL statements.
+**
+** The Lookaside structure holds configuration information about the
+** lookaside malloc subsystem. Each available memory allocation in
+** the lookaside subsystem is stored on a linked list of LookasideSlot
+** objects.
+*/
+struct Lookaside {
+ u16 sz; /* Size of each buffer in bytes */
+ u8 bEnabled; /* True if use lookaside. False to ignore it */
+ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
+ int nOut; /* Number of buffers currently checked out */
+ int mxOut; /* Highwater mark for nOut */
+ LookasideSlot *pFree; /* List if available buffers */
+ void *pStart; /* First byte of available memory space */
+ void *pEnd; /* First byte past end of available space */
+};
+struct LookasideSlot {
+ LookasideSlot *pNext; /* Next buffer in the list of free buffers */
+};
/*
** Each database is an instance of the following structure.
@@ -441,13 +613,20 @@ struct Schema {
** consistently.
*/
struct sqlite3 {
+ sqlite3_vfs *pVfs; /* OS Interface */
int nDb; /* Number of backends currently in use */
Db *aDb; /* All backends */
int flags; /* Miscellanous flags. See below */
+ int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
u8 autoCommit; /* The auto-commit flag. */
u8 temp_store; /* 1: file 2: memory 0: default */
+ u8 mallocFailed; /* True if we have seen a malloc failure */
+ u8 dfltLockMode; /* Default locking-mode for attached dbs */
+ u8 dfltJournalMode; /* Default journal mode for attached dbs */
+ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
+ int nextPagesize; /* Pagesize after VACUUM if >0 */
int nTable; /* Number of tables in the database */
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
i64 lastRowid; /* ROWID of most recent insert (see above) */
@@ -455,6 +634,8 @@ struct sqlite3 {
int magic; /* Magic number for detect library misuse */
int nChange; /* Value returned by sqlite3_changes() */
int nTotalChange; /* Value returned by sqlite3_total_changes() */
+ sqlite3_mutex *mutex; /* Connection mutex */
+ int aLimit[SQLITE_N_LIMIT]; /* Limits */
struct sqlite3InitInfo { /* Information used during initialization */
int iDb; /* When back is being initialized */
int newTnum; /* Rootpage of table being initialized */
@@ -484,6 +665,7 @@ struct sqlite3 {
int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
} u1;
+ Lookaside lookaside; /* Lookaside malloc configuration */
#ifndef SQLITE_OMIT_AUTHORIZATION
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
/* Access authorization function */
@@ -508,7 +690,6 @@ struct sqlite3 {
#ifdef SQLITE_SSE
sqlite3_stmt *pFetch; /* Used by SSE to fetch stored statements */
#endif
- u8 dfltLockMode; /* Default locking-mode for attached dbs */
};
/*
@@ -545,6 +726,8 @@ struct sqlite3 {
#define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */
#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */
+#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */
+#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */
/*
** Possible values for the sqlite.magic field.
@@ -553,6 +736,7 @@ struct sqlite3 {
*/
#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */
#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */
+#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
@@ -664,7 +848,7 @@ struct CollSeq {
**
** But rather than start with 0 or 1, we begin with 'a'. That way,
** when multiple affinity types are concatenated into a string and
-** used as the P3 operand, they will be more readable.
+** used as the P4 operand, they will be more readable.
**
** Note also that the numeric types are grouped together so that testing
** for a numeric type is a single comparison.
@@ -678,6 +862,19 @@ struct CollSeq {
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
/*
+** The SQLITE_AFF_MASK values masks off the significant bits of an
+** affinity value.
+*/
+#define SQLITE_AFF_MASK 0x67
+
+/*
+** Additional bit values that can be ORed with an affinity without
+** changing the affinity.
+*/
+#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */
+#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */
+
+/*
** Each SQL table is represented in memory by an instance of the
** following structure.
**
@@ -708,6 +905,7 @@ struct CollSeq {
** of a SELECT statement.
*/
struct Table {
+ sqlite3 *db; /* Associated database connection. Might be NULL. */
char *zName; /* Name of the table */
int nCol; /* Number of columns in this table */
Column *aCol; /* Information about each column */
@@ -738,7 +936,7 @@ struct Table {
int nModuleArg; /* Number of arguments to the module */
char **azModuleArg; /* Text of all module args. [0] is module name */
#endif
- Schema *pSchema;
+ Schema *pSchema; /* Schema that contains this table */
};
/*
@@ -796,7 +994,7 @@ struct FKey {
};
/*
-** SQLite supports many different ways to resolve a contraint
+** SQLite supports many different ways to resolve a constraint
** error. ROLLBACK processing means that a constraint violation
** causes the operation in process to fail and for the current transaction
** to be rolled back. ABORT processing means the operation in process
@@ -845,8 +1043,10 @@ struct FKey {
** were larger.
*/
struct KeyInfo {
+ sqlite3 *db; /* The database connection */
u8 enc; /* Text encoding - one of the TEXT_Utf* values */
u8 incrKey; /* Increase 2nd key by epsilon before comparison */
+ u8 prefixIsEqual; /* Treat a prefix as equal */
int nField; /* Number of entries in aColl[] */
u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
@@ -1016,7 +1216,6 @@ struct Expr {
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
Table *pTab; /* Table for OP_Column expressions. */
- Schema *pSchema;
#if SQLITE_MAX_EXPR_DEPTH>0
int nHeight; /* Height of the tree headed by this node */
#endif
@@ -1025,16 +1224,18 @@ struct Expr {
/*
** The following are the meanings of bits in the Expr.flags field.
*/
-#define EP_FromJoin 0x01 /* Originated in ON or USING clause of a join */
-#define EP_Agg 0x02 /* Contains one or more aggregate functions */
-#define EP_Resolved 0x04 /* IDs have been resolved to COLUMNs */
-#define EP_Error 0x08 /* Expression contains one or more errors */
-#define EP_Distinct 0x10 /* Aggregate function with DISTINCT keyword */
-#define EP_VarSelect 0x20 /* pSelect is correlated, not constant */
-#define EP_Dequoted 0x40 /* True if the string has been dequoted */
-#define EP_InfixFunc 0x80 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_ExpCollate 0x100 /* Collating sequence specified explicitly */
-
+#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */
+#define EP_Agg 0x0002 /* Contains one or more aggregate functions */
+#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */
+#define EP_Error 0x0008 /* Expression contains one or more errors */
+#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */
+#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */
+#define EP_Dequoted 0x0040 /* True if the string has been dequoted */
+#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */
+#define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */
+#define EP_FixedDest 0x0400 /* Result needed in a specific register */
+#define EP_IntValue 0x0800 /* Integer value contained in iTable */
/*
** These macros can be used to test, set, or clear bits in the
** Expr.flags field.
@@ -1191,6 +1392,14 @@ struct WhereLevel {
};
/*
+** Flags appropriate for the wflags parameter of sqlite3WhereBegin().
+*/
+#define WHERE_ORDERBY_NORMAL 0 /* No-op */
+#define WHERE_ORDERBY_MIN 1 /* ORDER BY processing for min() func */
+#define WHERE_ORDERBY_MAX 2 /* ORDER BY processing for max() func */
+#define WHERE_ONEPASS_DESIRED 4 /* Want to do one-pass UPDATE/DELETE */
+
+/*
** The WHERE clause processing routine has two halves. The
** first part does the start of the WHERE loop and the second
** half does the tail of the WHERE loop. An instance of
@@ -1198,7 +1407,8 @@ struct WhereLevel {
** into the second half to give some continuity.
*/
struct WhereInfo {
- Parse *pParse;
+ Parse *pParse; /* Parsing and code generating context */
+ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
SrcList *pTabList; /* List of tables in the join */
int iTop; /* The very beginning of the WHERE loop */
int iContinue; /* Jump here to continue with next record */
@@ -1255,7 +1465,7 @@ struct NameContext {
**
** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes.
** These addresses must be stored so that we can go back and fill in
-** the P3_KEYINFO and P2 parameters later. Neither the KeyInfo nor
+** the P4_KEYINFO and P2 parameters later. Neither the KeyInfo nor
** the number of columns in P2 can be computed at the same time
** as the OP_OpenEphm instruction is coded because not
** enough information about the compound query is known at that point.
@@ -1278,6 +1488,7 @@ struct Select {
Expr *pHaving; /* The HAVING clause */
ExprList *pOrderBy; /* The ORDER BY clause */
Select *pPrior; /* Prior select in a compound select statement */
+ Select *pNext; /* Next select to the left in a compound */
Select *pRightmost; /* Right-most select in a compound select statement */
Expr *pLimit; /* LIMIT expression. NULL means not used. */
Expr *pOffset; /* OFFSET expression. NULL means not used. */
@@ -1290,18 +1501,31 @@ struct Select {
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
-#define SRT_Discard 3 /* Do not save the results anywhere */
+#define SRT_Exists 3 /* Store 1 if the result is not empty */
+#define SRT_Discard 4 /* Do not save the results anywhere */
/* The ORDER BY clause is ignored for all of the above */
-#define IgnorableOrderby(X) (X<=SRT_Discard)
-
-#define SRT_Callback 4 /* Invoke a callback with each row of result */
-#define SRT_Mem 5 /* Store result in a memory cell */
-#define SRT_Set 6 /* Store non-null results as keys in an index */
-#define SRT_Table 7 /* Store result as data with an automatic rowid */
-#define SRT_EphemTab 8 /* Create transient tab and store like SRT_Table */
-#define SRT_Subroutine 9 /* Call a subroutine to handle results */
-#define SRT_Exists 10 /* Store 1 if the result is not empty */
+#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard)
+
+#define SRT_Callback 5 /* Invoke a callback with each row of result */
+#define SRT_Mem 6 /* Store result in a memory cell */
+#define SRT_Set 7 /* Store results as keys in an index */
+#define SRT_Table 8 /* Store result as data with an automatic rowid */
+#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */
+#define SRT_Coroutine 10 /* Generate a single row of result */
+
+/*
+** A structure used to customize the behaviour of sqlite3Select(). See
+** comments above sqlite3Select() for details.
+*/
+typedef struct SelectDest SelectDest;
+struct SelectDest {
+ u8 eDest; /* How to dispose of the results */
+ u8 affinity; /* Affinity used when eDest==SRT_Set */
+ int iParm; /* A parameter used by the eDest disposal method */
+ int iMem; /* Base register where results are written */
+ int nMem; /* Number of registers allocated */
+};
/*
** An SQL parser context. A copy of this structure is passed through
@@ -1329,11 +1553,25 @@ struct Parse {
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
u8 parseError; /* True after a parsing error. Ticket #1794 */
+ u8 nTempReg; /* Number of temporary registers in aTempReg[] */
+ u8 nTempInUse; /* Number of aTempReg[] currently checked out */
+ int aTempReg[8]; /* Holding area for temporary registers */
+ int nRangeReg; /* Size of the temporary register block */
+ int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
- int ckOffset; /* Stack offset to data used by CHECK constraints */
+ int ckBase; /* Base register of data during check constraints */
+ int disableColCache; /* True to disable adding to column cache */
+ int nColCache; /* Number of entries in the column cache */
+ int iColCache; /* Next entry of the cache to replace */
+ struct yColCache {
+ int iTable; /* Table cursor number */
+ int iColumn; /* Table column number */
+ char affChange; /* True if this register has had an affinity change */
+ int iReg; /* Register holding value of this column */
+ } aColCache[10]; /* One for each valid column cache entry */
u32 writeMask; /* Start a write transaction on these databases */
u32 cookieMask; /* Bitmask of schema verified databases */
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
@@ -1342,6 +1580,8 @@ struct Parse {
int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */
#endif
+ int regRowid; /* Register holding rowid of CREATE TABLE entry */
+ int regRoot; /* Register holding root page number for new objects */
/* Above is constant between recursions. Below is reset before and after
** each recursion */
@@ -1363,11 +1603,10 @@ struct Parse {
#ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
- Table *pVirtualLock; /* Require virtual table lock on this table */
+ int nVtabLock; /* Number of virtual tables to lock */
+ Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
-#if SQLITE_MAX_EXPR_DEPTH>0
int nHeight; /* Expression tree height of current sub-select */
-#endif
};
#ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -1518,6 +1757,8 @@ struct TriggerStack {
Table *pTab; /* Table that triggers are currently being coded on */
int newIdx; /* Index of vdbe cursor to "new" temp table */
int oldIdx; /* Index of vdbe cursor to "old" temp table */
+ u32 newColMask;
+ u32 oldColMask;
int orconf; /* Current orconf policy */
int ignoreJump; /* where to jump to for a RAISE(IGNORE) */
Trigger *pTrigger; /* The trigger currently being coded */
@@ -1538,6 +1779,22 @@ struct DbFixer {
};
/*
+** An objected used to accumulate the text of a string where we
+** do not necessarily know how big the string will be in the end.
+*/
+struct StrAccum {
+ sqlite3 *db; /* Optional database for lookaside. Can be NULL */
+ char *zBase; /* A base allocation. Not from malloc. */
+ char *zText; /* The string collected so far */
+ int nChar; /* Length of the string so far */
+ int nAlloc; /* Amount of space allocated in zText */
+ int mxAlloc; /* Maximum allowed string length */
+ u8 mallocFailed; /* Becomes true if any memory allocation fails */
+ u8 useMalloc; /* True if zText is enlargable using realloc */
+ u8 tooBig; /* Becomes true if string size exceeds limits */
+};
+
+/*
** A pointer to this structure is used to communicate information
** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback.
*/
@@ -1549,6 +1806,36 @@ typedef struct {
} InitData;
/*
+** Structure containing global configuration data for the SQLite library.
+**
+** This structure also contains some state information.
+*/
+struct Sqlite3Config {
+ int bMemstat; /* True to enable memory status */
+ int bCoreMutex; /* True to enable core mutexing */
+ int bFullMutex; /* True to enable full mutexing */
+ int mxStrlen; /* Maximum string length */
+ int szLookaside; /* Default lookaside buffer size */
+ int nLookaside; /* Default lookaside buffer count */
+ sqlite3_mem_methods m; /* Low-level memory allocation interface */
+ sqlite3_mutex_methods mutex; /* Low-level mutex interface */
+ void *pHeap; /* Heap storage space */
+ int nHeap; /* Size of pHeap[] */
+ int mnReq, mxReq; /* Min and max heap requests sizes */
+ void *pScratch; /* Scratch memory */
+ int szScratch; /* Size of each scratch buffer */
+ int nScratch; /* Number of scratch buffers */
+ void *pPage; /* Page cache memory */
+ int szPage; /* Size of each page in pPage[] */
+ int nPage; /* Number of pages in pPage[] */
+ int isInit; /* True after initialization has finished */
+ int isMallocInit; /* True after malloc is initialized */
+ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
+ int nSmall; /* alloc size threshold used by mem6.c */
+ int mxParserStack; /* maximum depth of the parser stack */
+};
+
+/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
*/
@@ -1577,44 +1864,79 @@ typedef struct {
int sqlite3StrICmp(const char *, const char *);
int sqlite3StrNICmp(const char *, const char *, int);
int sqlite3IsNumber(const char*, int*, u8);
+int sqlite3Strlen(sqlite3*, const char*);
+
+int sqlite3MallocInit(void);
+void sqlite3MallocEnd(void);
+void *sqlite3Malloc(int);
+void *sqlite3MallocZero(int);
+void *sqlite3DbMallocZero(sqlite3*, int);
+void *sqlite3DbMallocRaw(sqlite3*, int);
+char *sqlite3DbStrDup(sqlite3*,const char*);
+char *sqlite3DbStrNDup(sqlite3*,const char*, int);
+void *sqlite3Realloc(void*, int);
+void *sqlite3DbReallocOrFree(sqlite3 *, void *, int);
+void *sqlite3DbRealloc(sqlite3 *, void *, int);
+void sqlite3DbFree(sqlite3*, void*);
+int sqlite3MallocSize(void*);
+int sqlite3DbMallocSize(sqlite3*, void*);
+void *sqlite3ScratchMalloc(int);
+void sqlite3ScratchFree(void*);
+void *sqlite3PageMalloc(int);
+void sqlite3PageFree(void*);
+void sqlite3MemSetDefault(void);
+const sqlite3_mem_methods *sqlite3MemGetDefault(void);
+const sqlite3_mem_methods *sqlite3MemGetMemsys5(void);
+const sqlite3_mem_methods *sqlite3MemGetMemsys3(void);
+const sqlite3_mem_methods *sqlite3MemGetMemsys6(void);
+void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
+
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex_methods *sqlite3DefaultMutex(void);
+ sqlite3_mutex *sqlite3MutexAlloc(int);
+ int sqlite3MutexInit(void);
+ int sqlite3MutexEnd(void);
+#endif
+
+void sqlite3StatusReset(void);
+int sqlite3StatusValue(int);
+void sqlite3StatusAdd(int, int);
+void sqlite3StatusSet(int, int);
-void *sqlite3Malloc(int,int);
-void *sqlite3MallocRaw(int,int);
-void *sqlite3Realloc(void*,int);
-char *sqlite3StrDup(const char*);
-char *sqlite3StrNDup(const char*, int);
-# define sqlite3CheckMemory(a,b)
-void *sqlite3ReallocOrFree(void*,int);
-void sqlite3FreeX(void*);
-void *sqlite3MallocX(int);
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- int sqlite3AllocSize(void *);
-#endif
-
-char *sqlite3MPrintf(const char*, ...);
-char *sqlite3VMPrintf(const char*, va_list);
+int sqlite3IsNaN(double);
+
+void sqlite3VXPrintf(StrAccum*, int, const char*, va_list);
+char *sqlite3MPrintf(sqlite3*,const char*, ...);
+char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
+char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
void sqlite3DebugPrintf(const char*, ...);
- void *sqlite3TextToPtr(const char*);
#endif
-void sqlite3SetString(char **, ...);
+#if defined(SQLITE_TEST)
+ void *sqlite3TestTextToPtr(const char*);
+#endif
+void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...);
void sqlite3ErrorClear(Parse*);
void sqlite3Dequote(char*);
-void sqlite3DequoteExpr(Expr*);
+void sqlite3DequoteExpr(sqlite3*, Expr*);
int sqlite3KeywordCode(const unsigned char*, int);
int sqlite3RunParser(Parse*, const char*, char **);
void sqlite3FinishCoding(Parse*);
-Expr *sqlite3Expr(int, Expr*, Expr*, const Token*);
-Expr *sqlite3ExprOrFree(int, Expr*, Expr*, const Token*);
+int sqlite3GetTempReg(Parse*);
+void sqlite3ReleaseTempReg(Parse*,int);
+int sqlite3GetTempRange(Parse*,int);
+void sqlite3ReleaseTempRange(Parse*,int,int);
+Expr *sqlite3Expr(sqlite3*, int, Expr*, Expr*, const Token*);
+Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*);
Expr *sqlite3RegisterExpr(Parse*,Token*);
-Expr *sqlite3ExprAnd(Expr*, Expr*);
+Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
void sqlite3ExprSpan(Expr*,Token*,Token*);
-Expr *sqlite3ExprFunction(ExprList*, Token*);
+Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
void sqlite3ExprAssignVarNumber(Parse*, Expr*);
-void sqlite3ExprDelete(Expr*);
-ExprList *sqlite3ExprListAppend(ExprList*,Expr*,Token*);
-void sqlite3ExprListDelete(ExprList*);
+void sqlite3ExprDelete(sqlite3*, Expr*);
+ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*,Token*);
+void sqlite3ExprListDelete(sqlite3*, ExprList*);
int sqlite3Init(sqlite3*, char**);
int sqlite3InitCallback(void*, int, char**, char**);
void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
@@ -1630,9 +1952,16 @@ void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
void sqlite3AddCheckConstraint(Parse*, Expr*);
void sqlite3AddColumnType(Parse*,Token*);
void sqlite3AddDefaultValue(Parse*,Expr*);
-void sqlite3AddCollateType(Parse*, const char*, int);
+void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+Bitvec *sqlite3BitvecCreate(u32);
+int sqlite3BitvecTest(Bitvec*, u32);
+int sqlite3BitvecSet(Bitvec*, u32);
+void sqlite3BitvecClear(Bitvec*, u32);
+void sqlite3BitvecDestroy(Bitvec*);
+int sqlite3BitvecBuiltinTest(int,int*);
+
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
@@ -1644,52 +1973,62 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
void sqlite3DropTable(Parse*, SrcList*, int, int);
void sqlite3DeleteTable(Table*);
void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
-void *sqlite3ArrayAllocate(void*,int,int,int*,int*,int*);
-IdList *sqlite3IdListAppend(IdList*, Token*);
+void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*);
+IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
-SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
-SrcList *sqlite3SrcListAppendFromTerm(SrcList*, Token*, Token*, Token*,
+SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
+SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*,
Select*, Expr*, IdList*);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
-void sqlite3IdListDelete(IdList*);
-void sqlite3SrcListDelete(SrcList*);
+void sqlite3IdListDelete(sqlite3*, IdList*);
+void sqlite3SrcListDelete(sqlite3*, SrcList*);
void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Token*, int, int);
void sqlite3DropIndex(Parse*, SrcList*, int);
-int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff);
-Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
- int,Expr*,Expr*);
-void sqlite3SelectDelete(Select*);
+int sqlite3Select(Parse*, Select*, SelectDest*, Select*, int, int*);
+Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
+ Expr*,ExprList*,int,Expr*,Expr*);
+void sqlite3SelectDelete(sqlite3*, Select*);
Table *sqlite3SrcListLookup(Parse*, SrcList*);
int sqlite3IsReadOnly(Parse*, Table*, int);
void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**);
+WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
void sqlite3WhereEnd(WhereInfo*);
-void sqlite3ExprCodeGetColumn(Vdbe*, Table*, int, int);
-void sqlite3ExprCode(Parse*, Expr*);
-void sqlite3ExprCodeAndCache(Parse*, Expr*);
-int sqlite3ExprCodeExprList(Parse*, ExprList*);
+int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int);
+void sqlite3ExprCodeMove(Parse*, int, int, int);
+void sqlite3ExprCodeCopy(Parse*, int, int, int);
+void sqlite3ExprClearColumnCache(Parse*, int);
+void sqlite3ExprCacheAffinityChange(Parse*, int, int);
+int sqlite3ExprWritableRegister(Parse*,int,int);
+void sqlite3ExprHardCopy(Parse*,int,int);
+int sqlite3ExprCode(Parse*, Expr*, int);
+int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
+int sqlite3ExprCodeTarget(Parse*, Expr*, int);
+int sqlite3ExprCodeAndCache(Parse*, Expr*, int);
+void sqlite3ExprCodeConstants(Parse*, Expr*);
+int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int);
void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
Table *sqlite3FindTable(sqlite3*,const char*, const char*);
-Table *sqlite3LocateTable(Parse*,const char*, const char*);
+Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
void sqlite3Vacuum(Parse*);
int sqlite3RunVacuum(char**, sqlite3*);
-char *sqlite3NameFromToken(Token*);
+char *sqlite3NameFromToken(sqlite3*, Token*);
int sqlite3ExprCompare(Expr*, Expr*);
-int sqliteFuncId(Token*);
int sqlite3ExprResolveNames(NameContext *, Expr *);
-int sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
-int sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
+void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
+void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
Vdbe *sqlite3GetVdbe(Parse*);
-Expr *sqlite3CreateIdExpr(const char*);
-void sqlite3Randomness(int, void*);
+Expr *sqlite3CreateIdExpr(Parse *, const char*);
+void sqlite3PrngSaveState(void);
+void sqlite3PrngRestoreState(void);
+void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3BeginTransaction(Parse*, int);
@@ -1700,26 +2039,34 @@ int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3IsRowid(const char*);
-void sqlite3GenerateRowDelete(sqlite3*, Vdbe*, Table*, int, int);
-void sqlite3GenerateRowIndexDelete(Vdbe*, Table*, int, char*);
-void sqlite3GenerateIndexKey(Vdbe*, Index*, int);
-void sqlite3GenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
-void sqlite3CompleteInsertion(Parse*, Table*, int, char*, int, int, int, int);
-void sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
+void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
+void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
+int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
+void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
+ int*,int,int,int,int);
+void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*,int,int,int,int);
+int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
void sqlite3BeginWriteOperation(Parse*, int, int);
-Expr *sqlite3ExprDup(Expr*);
-void sqlite3TokenCopy(Token*, Token*);
-ExprList *sqlite3ExprListDup(ExprList*);
-SrcList *sqlite3SrcListDup(SrcList*);
-IdList *sqlite3IdListDup(IdList*);
-Select *sqlite3SelectDup(Select*);
+Expr *sqlite3ExprDup(sqlite3*,Expr*);
+void sqlite3TokenCopy(sqlite3*,Token*, Token*);
+ExprList *sqlite3ExprListDup(sqlite3*,ExprList*);
+SrcList *sqlite3SrcListDup(sqlite3*,SrcList*);
+IdList *sqlite3IdListDup(sqlite3*,IdList*);
+Select *sqlite3SelectDup(sqlite3*,Select*);
FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
void sqlite3RegisterBuiltinFunctions(sqlite3*);
void sqlite3RegisterDateTimeFunctions(sqlite3*);
-int sqlite3SafetyOn(sqlite3*);
-int sqlite3SafetyOff(sqlite3*);
-int sqlite3SafetyCheck(sqlite3*);
-void sqlite3ChangeCookie(sqlite3*, Vdbe*, int);
+#ifdef SQLITE_DEBUG
+ int sqlite3SafetyOn(sqlite3*);
+ int sqlite3SafetyOff(sqlite3*);
+#else
+# define sqlite3SafetyOn(A) 0
+# define sqlite3SafetyOff(A) 0
+#endif
+int sqlite3SafetyCheckOk(sqlite3*);
+int sqlite3SafetyCheckSickOrOk(sqlite3*);
+void sqlite3ChangeCookie(Parse*, int);
+void sqlite3MaterializeView(Parse*, Select*, Expr*, int);
#ifndef SQLITE_OMIT_TRIGGER
void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
@@ -1729,33 +2076,34 @@ void sqlite3ChangeCookie(sqlite3*, Vdbe*, int);
void sqlite3DropTriggerPtr(Parse*, Trigger*);
int sqlite3TriggersExist(Parse*, Table*, int, ExprList*);
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
- int, int);
+ int, int, u32*, u32*);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
- void sqlite3DeleteTriggerStep(TriggerStep*);
- TriggerStep *sqlite3TriggerSelectStep(Select*);
- TriggerStep *sqlite3TriggerInsertStep(Token*, IdList*, ExprList*,Select*,int);
- TriggerStep *sqlite3TriggerUpdateStep(Token*, ExprList*, Expr*, int);
- TriggerStep *sqlite3TriggerDeleteStep(Token*, Expr*);
- void sqlite3DeleteTrigger(Trigger*);
+ void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
+ TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
+ TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
+ ExprList*,Select*,int);
+ TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, int);
+ TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
+ void sqlite3DeleteTrigger(sqlite3*, Trigger*);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
#else
# define sqlite3TriggersExist(A,B,C,D,E,F) 0
-# define sqlite3DeleteTrigger(A)
+# define sqlite3DeleteTrigger(A,B)
# define sqlite3DropTriggerPtr(A,B)
# define sqlite3UnlinkAndDeleteTrigger(A,B,C)
-# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I) 0
+# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K) 0
#endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
void sqlite3DeferForeignKey(Parse*, int);
#ifndef SQLITE_OMIT_AUTHORIZATION
- void sqlite3AuthRead(Parse*,Expr*,SrcList*);
+ void sqlite3AuthRead(Parse*,Expr*,Schema*,SrcList*);
int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*);
void sqlite3AuthContextPush(Parse*, AuthContext*, const char*);
void sqlite3AuthContextPop(AuthContext*);
#else
-# define sqlite3AuthRead(a,b,c)
+# define sqlite3AuthRead(a,b,c,d)
# define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK
# define sqlite3AuthContextPush(a,b,c)
# define sqlite3AuthContextPop(a) ((void)(a))
@@ -1763,7 +2111,7 @@ void sqlite3DeferForeignKey(Parse*, int);
void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
void sqlite3Detach(Parse*, Expr*);
int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename,
- int omitJournal, int nCache, Btree **ppBtree);
+ int omitJournal, int nCache, int flags, Btree **ppBtree);
int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqlite3FixSrcList(DbFixer*, SrcList*);
int sqlite3FixSelect(DbFixer*, Select*);
@@ -1773,14 +2121,47 @@ int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
int sqlite3AtoF(const char *z, double*);
char *sqlite3_snprintf(int,char*,const char*,...);
int sqlite3GetInt32(const char *, int*);
-int sqlite3FitsIn64Bits(const char *);
+int sqlite3FitsIn64Bits(const char *, int);
int sqlite3Utf16ByteLen(const void *pData, int nChar);
int sqlite3Utf8CharLen(const char *pData, int nByte);
int sqlite3Utf8Read(const u8*, const u8*, const u8**);
-int sqlite3PutVarint(unsigned char *, u64);
+
+/*
+** Routines to read and write variable-length integers. These used to
+** be defined locally, but now we use the varint routines in the util.c
+** file. Code should use the MACRO forms below, as the Varint32 versions
+** are coded to assume the single byte case is already handled (which
+** the MACRO form does).
+*/
+int sqlite3PutVarint(unsigned char*, u64);
+int sqlite3PutVarint32(unsigned char*, u32);
int sqlite3GetVarint(const unsigned char *, u64 *);
int sqlite3GetVarint32(const unsigned char *, u32 *);
int sqlite3VarintLen(u64 v);
+
+/*
+** The header of a record consists of a sequence variable-length integers.
+** These integers are almost always small and are encoded as a single byte.
+** The following macros take advantage this fact to provide a fast encode
+** and decode of the integers in a record header. It is faster for the common
+** case where the integer is a single byte. It is a little slower when the
+** integer is two or more bytes. But overall it is faster.
+**
+** The following expressions are equivalent:
+**
+** x = sqlite3GetVarint32( A, &B );
+** x = sqlite3PutVarint32( A, B );
+**
+** x = getVarint32( A, B );
+** x = putVarint32( A, B );
+**
+*/
+#define getVarint32(A,B) ((*(A)<(unsigned char)0x80) ? ((B) = (u32)*(A)),1 : sqlite3GetVarint32((A), &(B)))
+#define putVarint32(A,B) (((B)<(u32)0x80) ? (*(A) = (unsigned char)(B)),1 : sqlite3PutVarint32((A), (B)))
+#define getVarint sqlite3GetVarint
+#define putVarint sqlite3PutVarint
+
+
void sqlite3IndexAffinityStr(Vdbe *, Index *);
void sqlite3TableAffinityStr(Vdbe *, Table *);
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
@@ -1788,7 +2169,7 @@ int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3Atoi64(const char*, i64*);
void sqlite3Error(sqlite3*, int, const char*,...);
-void *sqlite3HexToBlob(const char *z);
+void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
const char *sqlite3ErrStr(int);
int sqlite3ReadSchema(Parse *pParse);
@@ -1802,13 +2183,17 @@ void sqlite3VdbeSetChanges(sqlite3 *, int);
const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueBytes(sqlite3_value*, u8);
-void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*));
+void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
+ void(*)(void*));
void sqlite3ValueFree(sqlite3_value*);
-sqlite3_value *sqlite3ValueNew(void);
-char *sqlite3Utf16to8(const void*, int);
-int sqlite3ValueFromExpr(Expr *, u8, u8, sqlite3_value **);
+sqlite3_value *sqlite3ValueNew(sqlite3 *);
+char *sqlite3Utf16to8(sqlite3 *, const void*, int);
+int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
+#ifndef SQLITE_AMALGAMATION
extern const unsigned char sqlite3UpperToLower[];
+extern struct Sqlite3Config sqlite3Config;
+#endif
void sqlite3RootPageMoved(Db*, int, int);
void sqlite3Reindex(Parse*, Token*, Token*);
void sqlite3AlterFunctions(sqlite3*);
@@ -1816,7 +2201,7 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...);
void sqlite3ExpirePreparedStatements(sqlite3*);
-void sqlite3CodeSubselect(Parse *, Expr *);
+void sqlite3CodeSubselect(Parse *, Expr *, int);
int sqlite3SelectResolve(Parse *, Select *, NameContext *);
void sqlite3ColumnDefault(Vdbe *, Table *, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
@@ -1830,36 +2215,39 @@ int sqlite3AnalysisLoad(sqlite3*,int iDB);
void sqlite3DefaultRowEst(Index*);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
-ThreadData *sqlite3ThreadData(void);
-const ThreadData *sqlite3ThreadDataReadOnly(void);
-void sqlite3ReleaseThreadData(void);
void sqlite3AttachFunctions(sqlite3 *);
void sqlite3MinimumFileFormat(Parse*, int, int);
void sqlite3SchemaFree(void *);
-Schema *sqlite3SchemaGet(Btree *);
+Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*));
int sqlite3ApiExit(sqlite3 *db, int);
-void sqlite3FailedMalloc(void);
-void sqlite3AbortOtherActiveVdbes(sqlite3 *, Vdbe *);
int sqlite3OpenTempDatabase(Parse *);
+void sqlite3StrAccumInit(StrAccum*, char*, int, int);
+void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+char *sqlite3StrAccumFinish(StrAccum*);
+void sqlite3StrAccumReset(StrAccum*);
+void sqlite3SelectDestInit(SelectDest*,int,int);
+
/*
** The interface to the LEMON-generated parser
*/
void *sqlite3ParserAlloc(void*(*)(size_t));
void sqlite3ParserFree(void*, void(*)(void*));
void sqlite3Parser(void*, int, Token, Parse*);
+#ifdef YYTRACKMAXSTACKDEPTH
+ int sqlite3ParserStackPeak(void*);
+#endif
+int sqlite3AutoLoadExtensions(sqlite3*);
#ifndef SQLITE_OMIT_LOAD_EXTENSION
void sqlite3CloseExtensions(sqlite3*);
- int sqlite3AutoLoadExtensions(sqlite3*);
#else
# define sqlite3CloseExtensions(X)
-# define sqlite3AutoLoadExtensions(X) SQLITE_OK
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -1872,35 +2260,18 @@ void sqlite3Parser(void*, int, Token, Parse*);
int sqlite3Utf8To8(unsigned char*);
#endif
-#ifdef SQLITE_MEMDEBUG
- void sqlite3MallocDisallow(void);
- void sqlite3MallocAllow(void);
- int sqlite3TestMallocFail(void);
-#else
- #define sqlite3TestMallocFail() 0
- #define sqlite3MallocDisallow()
- #define sqlite3MallocAllow()
-#endif
-
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- void *sqlite3ThreadSafeMalloc(int);
- void sqlite3ThreadSafeFree(void *);
-#else
- #define sqlite3ThreadSafeMalloc sqlite3MallocX
- #define sqlite3ThreadSafeFree sqlite3FreeX
-#endif
-
#ifdef SQLITE_OMIT_VIRTUALTABLE
# define sqlite3VtabClear(X)
-# define sqlite3VtabSync(X,Y) (Y)
+# define sqlite3VtabSync(X,Y) SQLITE_OK
# define sqlite3VtabRollback(X)
# define sqlite3VtabCommit(X)
#else
void sqlite3VtabClear(Table*);
- int sqlite3VtabSync(sqlite3 *db, int rc);
+ int sqlite3VtabSync(sqlite3 *db, char **);
int sqlite3VtabRollback(sqlite3 *db);
int sqlite3VtabCommit(sqlite3 *db);
#endif
+void sqlite3VtabMakeWritable(Parse*,Table*);
void sqlite3VtabLock(sqlite3_vtab*);
void sqlite3VtabUnlock(sqlite3*, sqlite3_vtab*);
void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
@@ -1911,17 +2282,51 @@ int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **);
int sqlite3VtabCallConnect(Parse*, Table*);
int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *);
-FuncDef *sqlite3VtabOverloadFunction(FuncDef*, int nArg, Expr*);
+FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
int sqlite3Reprepare(Vdbe*);
-void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
+void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
+
+/*
+** Available fault injectors. Should be numbered beginning with 0.
+*/
+#define SQLITE_FAULTINJECTOR_MALLOC 0
+#define SQLITE_FAULTINJECTOR_COUNT 1
+
+/*
+** The interface to the code in fault.c used for identifying "benign"
+** malloc failures. This is only present if SQLITE_OMIT_BUILTIN_TEST
+** is not defined.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ void sqlite3BeginBenignMalloc(void);
+ void sqlite3EndBenignMalloc(void);
+#else
+ #define sqlite3BeginBenignMalloc()
+ #define sqlite3EndBenignMalloc()
+#endif
+
+#define IN_INDEX_ROWID 1
+#define IN_INDEX_EPH 2
+#define IN_INDEX_INDEX 3
+int sqlite3FindInIndex(Parse *, Expr *, int*);
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
+ int sqlite3JournalSize(sqlite3_vfs *);
+ int sqlite3JournalCreate(sqlite3_file *);
+#else
+ #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
+#endif
+
#if SQLITE_MAX_EXPR_DEPTH>0
- void sqlite3ExprSetHeight(Expr *);
+ void sqlite3ExprSetHeight(Parse *pParse, Expr *p);
int sqlite3SelectExprHeight(Select *);
#else
- #define sqlite3ExprSetHeight(x)
+ #define sqlite3ExprSetHeight(x,y)
+ #define sqlite3SelectExprHeight(x) 0
#endif
u32 sqlite3Get4byte(const u8*);
@@ -1937,16 +2342,16 @@ void sqlite3Put4byte(u8*, u32);
/*
** If the SQLITE_ENABLE IOTRACE exists then the global variable
-** sqlite3_io_trace is a pointer to a printf-like routine used to
+** sqlite3IoTrace is a pointer to a printf-like routine used to
** print I/O tracing messages.
*/
#ifdef SQLITE_ENABLE_IOTRACE
-# define IOTRACE(A) if( sqlite3_io_trace ){ sqlite3_io_trace A; }
+# define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; }
void sqlite3VdbeIOTraceSql(Vdbe*);
+SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
#else
# define IOTRACE(A)
# define sqlite3VdbeIOTraceSql(X)
#endif
-SQLITE_EXTERN void (*sqlite3_io_trace)(const char*,...);
#endif
diff --git a/third_party/sqlite/sqliteLimit.h b/third_party/sqlite/src/sqliteLimit.h
index 6b7ad98..a1307de 100644..100755
--- a/third_party/sqlite/sqliteLimit.h
+++ b/third_party/sqlite/src/sqliteLimit.h
@@ -12,7 +12,7 @@
**
** This file defines various limits of what SQLite can process.
**
-** @(#) $Id: sqliteLimit.h,v 1.1 2007/06/19 15:23:48 drh Exp $
+** @(#) $Id: sqliteLimit.h,v 1.8 2008/03/26 15:56:22 drh Exp $
*/
/*
@@ -49,18 +49,24 @@
/*
** The maximum length of a single SQL statement in bytes.
-** The hard limit here is the same as SQLITE_MAX_LENGTH.
+**
+** It used to be the case that setting this value to zero would
+** turn the limit off. That is no longer true. It is not possible
+** to turn this limit off.
*/
#ifndef SQLITE_MAX_SQL_LENGTH
-# define SQLITE_MAX_SQL_LENGTH 1000000
+# define SQLITE_MAX_SQL_LENGTH 1000000000
#endif
/*
** The maximum depth of an expression tree. This is limited to
** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
** want to place more severe limits on the complexity of an
-** expression. A value of 0 (the default) means do not enforce
-** any limitation on expression tree depth.
+** expression.
+**
+** A value of 0 used to mean that the limit was not enforced.
+** But that is no longer true. The limit is now strictly enforced
+** at all times.
*/
#ifndef SQLITE_MAX_EXPR_DEPTH
# define SQLITE_MAX_EXPR_DEPTH 1000
@@ -105,11 +111,9 @@
#endif
/*
-** The maximum number of attached databases. This must be at least 2
-** in order to support the main database file (0) and the file used to
-** hold temporary tables (1). And it must be less than 32 because
-** we use a bitmask of databases with a u32 in places (for example
-** the Parse.cookieMask field).
+** The maximum number of attached databases. This must be between 0
+** and 30. The upper bound on 30 is because a 32-bit integer bitmap
+** is used internally to track attached databases.
*/
#ifndef SQLITE_MAX_ATTACHED
# define SQLITE_MAX_ATTACHED 10
@@ -123,20 +127,41 @@
# define SQLITE_MAX_VARIABLE_NUMBER 999
#endif
+/* Maximum page size. The upper bound on this value is 32768. This a limit
+** imposed by the necessity of storing the value in a 2-byte unsigned integer
+** and the fact that the page size must be a power of 2.
+*/
+#ifndef SQLITE_MAX_PAGE_SIZE
+# define SQLITE_MAX_PAGE_SIZE 32768
+#endif
+
+
/*
** The default size of a database page.
*/
#ifndef SQLITE_DEFAULT_PAGE_SIZE
# define SQLITE_DEFAULT_PAGE_SIZE 1024
#endif
+#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
-/* Maximum page size. The upper bound on this value is 32768. This a limit
-** imposed by the necessity of storing the value in a 2-byte unsigned integer
-** and the fact that the page size must be a power of 2.
+/*
+** Ordinarily, if no value is explicitly provided, SQLite creates databases
+** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
+** device characteristics (sector-size and atomic write() support),
+** SQLite may choose a larger value. This constant is the maximum value
+** SQLite will choose on its own.
*/
-#ifndef SQLITE_MAX_PAGE_SIZE
-# define SQLITE_MAX_PAGE_SIZE 32768
+#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
#endif
+#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
+
/*
** Maximum number of pages in one database file.
diff --git a/third_party/sqlite/src/status.c b/third_party/sqlite/src/status.c
new file mode 100755
index 0000000..a60a853
--- /dev/null
+++ b/third_party/sqlite/src/status.c
@@ -0,0 +1,111 @@
+/*
+** 2008 June 18
+**
+** 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 module implements the sqlite3_status() interface and related
+** functionality.
+**
+** $Id: status.c,v 1.7 2008/08/05 17:53:23 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** Variables in which to record status information.
+*/
+static struct {
+ int nowValue[9]; /* Current value */
+ int mxValue[9]; /* Maximum value */
+} sqlite3Stat;
+
+
+/*
+** Reset the status records. This routine is called by
+** sqlite3_initialize().
+*/
+void sqlite3StatusReset(void){
+ memset(&sqlite3Stat, 0, sizeof(sqlite3Stat));
+}
+
+/*
+** Return the current value of a status parameter.
+*/
+int sqlite3StatusValue(int op){
+ assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
+ return sqlite3Stat.nowValue[op];
+}
+
+/*
+** Add N to the value of a status record. It is assumed that the
+** caller holds appropriate locks.
+*/
+void sqlite3StatusAdd(int op, int N){
+ assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
+ sqlite3Stat.nowValue[op] += N;
+ if( sqlite3Stat.nowValue[op]>sqlite3Stat.mxValue[op] ){
+ sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
+ }
+}
+
+/*
+** Set the value of a status to X.
+*/
+void sqlite3StatusSet(int op, int X){
+ assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
+ sqlite3Stat.nowValue[op] = X;
+ if( sqlite3Stat.nowValue[op]>sqlite3Stat.mxValue[op] ){
+ sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
+ }
+}
+
+/*
+** Query status information.
+**
+** This implementation assumes that reading or writing an aligned
+** 32-bit integer is an atomic operation. If that assumption is not true,
+** then this routine is not threadsafe.
+*/
+int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
+ if( op<0 || op>=ArraySize(sqlite3Stat.nowValue) ){
+ return SQLITE_MISUSE;
+ }
+ *pCurrent = sqlite3Stat.nowValue[op];
+ *pHighwater = sqlite3Stat.mxValue[op];
+ if( resetFlag ){
+ sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Query status information for a single database connection
+*/
+int sqlite3_db_status(
+ sqlite3 *db, /* The database connection whose status is desired */
+ int op, /* Status verb */
+ int *pCurrent, /* Write current value here */
+ int *pHighwater, /* Write high-water mark here */
+ int resetFlag /* Reset high-water mark if true */
+){
+ switch( op ){
+ case SQLITE_DBSTATUS_LOOKASIDE_USED: {
+ *pCurrent = db->lookaside.nOut;
+ *pHighwater = db->lookaside.mxOut;
+ if( resetFlag ){
+ db->lookaside.mxOut = db->lookaside.nOut;
+ }
+ break;
+ }
+ default: {
+ return SQLITE_ERROR;
+ }
+ }
+ return SQLITE_OK;
+}
diff --git a/third_party/sqlite/table.c b/third_party/sqlite/src/table.c
index d58fb3c..1bd1bd1 100644..100755
--- a/third_party/sqlite/table.c
+++ b/third_party/sqlite/src/table.c
@@ -15,6 +15,8 @@
**
** These routines are in a separate files so that they will not be linked
** if they are not used.
+**
+** $Id: table.c,v 1.36 2008/07/08 22:28:49 shane Exp $
*/
#include "sqliteInt.h"
#include <stdlib.h>
@@ -70,17 +72,15 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
if( p->nRow==0 ){
p->nColumn = nCol;
for(i=0; i<nCol; i++){
- if( colv[i]==0 ){
- z = sqlite3_mprintf("");
- }else{
- z = sqlite3_mprintf("%s", colv[i]);
- }
+ z = sqlite3_mprintf("%s", colv[i]);
+ if( z==0 ) goto malloc_failed;
p->azResult[p->nData++] = z;
}
}else if( p->nColumn!=nCol ){
- sqlite3SetString(&p->zErrMsg,
- "sqlite3_get_table() called with two or more incompatible queries",
- (char*)0);
+ sqlite3_free(p->zErrMsg);
+ p->zErrMsg = sqlite3_mprintf(
+ "sqlite3_get_table() called with two or more incompatible queries"
+ );
p->rc = SQLITE_ERROR;
return 1;
}
@@ -128,7 +128,7 @@ int sqlite3_get_table(
){
int rc;
TabResult res;
- if( pazResult==0 ){ return SQLITE_ERROR; }
+
*pazResult = 0;
if( pnColumn ) *pnColumn = 0;
if( pnRow ) *pnRow = 0;
@@ -139,14 +139,15 @@ int sqlite3_get_table(
res.nData = 1;
res.nAlloc = 20;
res.rc = SQLITE_OK;
- res.azResult = sqlite3_malloc( sizeof(char*)*res.nAlloc );
- if( res.azResult==0 ) return SQLITE_NOMEM;
+ res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc );
+ if( res.azResult==0 ){
+ db->errCode = SQLITE_NOMEM;
+ return SQLITE_NOMEM;
+ }
res.azResult[0] = 0;
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
- if( res.azResult ){
- assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
- res.azResult[0] = (char*)res.nData;
- }
+ assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
+ res.azResult[0] = SQLITE_INT_TO_PTR(res.nData);
if( (rc&0xff)==SQLITE_ABORT ){
sqlite3_free_table(&res.azResult[1]);
if( res.zErrMsg ){
@@ -154,21 +155,22 @@ int sqlite3_get_table(
sqlite3_free(*pzErrMsg);
*pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
}
- sqliteFree(res.zErrMsg);
+ sqlite3_free(res.zErrMsg);
}
- db->errCode = res.rc;
- return res.rc & db->errMask;
+ db->errCode = res.rc; /* Assume 32-bit assignment is atomic */
+ return res.rc;
}
- sqliteFree(res.zErrMsg);
+ sqlite3_free(res.zErrMsg);
if( rc!=SQLITE_OK ){
sqlite3_free_table(&res.azResult[1]);
- return rc & db->errMask;
+ return rc;
}
if( res.nAlloc>res.nData ){
char **azNew;
azNew = sqlite3_realloc( res.azResult, sizeof(char*)*(res.nData+1) );
if( azNew==0 ){
sqlite3_free_table(&res.azResult[1]);
+ db->errCode = SQLITE_NOMEM;
return SQLITE_NOMEM;
}
res.nAlloc = res.nData+1;
@@ -177,7 +179,7 @@ int sqlite3_get_table(
*pazResult = &res.azResult[1];
if( pnColumn ) *pnColumn = res.nColumn;
if( pnRow ) *pnRow = res.nRow;
- return rc & db->errMask;
+ return rc;
}
/*
@@ -189,8 +191,8 @@ void sqlite3_free_table(
if( azResult ){
int i, n;
azResult--;
- if( azResult==0 ) return;
- n = (int)azResult[0];
+ assert( azResult!=0 );
+ n = SQLITE_PTR_TO_INT(azResult[0]);
for(i=1; i<n; i++){ if( azResult[i] ) sqlite3_free(azResult[i]); }
sqlite3_free(azResult);
}
diff --git a/third_party/sqlite/src/tclsqlite.c b/third_party/sqlite/src/tclsqlite.c
new file mode 100755
index 0000000..35a69b8
--- /dev/null
+++ b/third_party/sqlite/src/tclsqlite.c
@@ -0,0 +1,2599 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+** A TCL Interface to SQLite. Append this file to sqlite3.c and
+** compile the whole thing to build a TCL-enabled version of SQLite.
+**
+** $Id: tclsqlite.c,v 1.219 2008/07/10 17:52:49 danielk1977 Exp $
+*/
+#include "tcl.h"
+#include <errno.h>
+
+/*
+** Some additional include files are needed if this file is not
+** appended to the amalgamation.
+*/
+#ifndef SQLITE_AMALGAMATION
+# include "sqliteInt.h"
+# include <stdlib.h>
+# include <string.h>
+# include <assert.h>
+# include <ctype.h>
+#endif
+
+/*
+ * Windows needs to know which symbols to export. Unix does not.
+ * BUILD_sqlite should be undefined for Unix.
+ */
+#ifdef BUILD_sqlite
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+#endif /* BUILD_sqlite */
+
+#define NUM_PREPARED_STMTS 10
+#define MAX_PREPARED_STMTS 100
+
+/*
+** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we
+** have to do a translation when going between the two. Set the
+** UTF_TRANSLATION_NEEDED macro to indicate that we need to do
+** this translation.
+*/
+#if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8)
+# define UTF_TRANSLATION_NEEDED 1
+#endif
+
+/*
+** New SQL functions can be created as TCL scripts. Each such function
+** is described by an instance of the following structure.
+*/
+typedef struct SqlFunc SqlFunc;
+struct SqlFunc {
+ Tcl_Interp *interp; /* The TCL interpret to execute the function */
+ Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */
+ int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */
+ char *zName; /* Name of this function */
+ SqlFunc *pNext; /* Next function on the list of them all */
+};
+
+/*
+** New collation sequences function can be created as TCL scripts. Each such
+** function is described by an instance of the following structure.
+*/
+typedef struct SqlCollate SqlCollate;
+struct SqlCollate {
+ Tcl_Interp *interp; /* The TCL interpret to execute the function */
+ char *zScript; /* The script to be run */
+ SqlCollate *pNext; /* Next function on the list of them all */
+};
+
+/*
+** Prepared statements are cached for faster execution. Each prepared
+** statement is described by an instance of the following structure.
+*/
+typedef struct SqlPreparedStmt SqlPreparedStmt;
+struct SqlPreparedStmt {
+ SqlPreparedStmt *pNext; /* Next in linked list */
+ SqlPreparedStmt *pPrev; /* Previous on the list */
+ sqlite3_stmt *pStmt; /* The prepared statement */
+ int nSql; /* chars in zSql[] */
+ const char *zSql; /* Text of the SQL statement */
+};
+
+typedef struct IncrblobChannel IncrblobChannel;
+
+/*
+** There is one instance of this structure for each SQLite database
+** that has been opened by the SQLite TCL interface.
+*/
+typedef struct SqliteDb SqliteDb;
+struct SqliteDb {
+ sqlite3 *db; /* The "real" database structure. MUST BE FIRST */
+ Tcl_Interp *interp; /* The interpreter used for this database */
+ char *zBusy; /* The busy callback routine */
+ char *zCommit; /* The commit hook callback routine */
+ char *zTrace; /* The trace callback routine */
+ char *zProfile; /* The profile callback routine */
+ char *zProgress; /* The progress callback routine */
+ char *zAuth; /* The authorization callback routine */
+ char *zNull; /* Text to substitute for an SQL NULL value */
+ SqlFunc *pFunc; /* List of SQL functions */
+ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
+ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */
+ SqlCollate *pCollate; /* List of SQL collation functions */
+ int rc; /* Return code of most recent sqlite3_exec() */
+ Tcl_Obj *pCollateNeeded; /* Collation needed script */
+ SqlPreparedStmt *stmtList; /* List of prepared statements*/
+ SqlPreparedStmt *stmtLast; /* Last statement in the list */
+ int maxStmt; /* The next maximum number of stmtList */
+ int nStmt; /* Number of statements in stmtList */
+ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
+};
+
+struct IncrblobChannel {
+ sqlite3_blob *pBlob; /* sqlite3 blob handle */
+ SqliteDb *pDb; /* Associated database connection */
+ int iSeek; /* Current seek offset */
+ Tcl_Channel channel; /* Channel identifier */
+ IncrblobChannel *pNext; /* Linked list of all open incrblob channels */
+ IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */
+};
+
+#ifndef SQLITE_OMIT_INCRBLOB
+/*
+** Close all incrblob channels opened using database connection pDb.
+** This is called when shutting down the database connection.
+*/
+static void closeIncrblobChannels(SqliteDb *pDb){
+ IncrblobChannel *p;
+ IncrblobChannel *pNext;
+
+ for(p=pDb->pIncrblob; p; p=pNext){
+ pNext = p->pNext;
+
+ /* Note: Calling unregister here call Tcl_Close on the incrblob channel,
+ ** which deletes the IncrblobChannel structure at *p. So do not
+ ** call Tcl_Free() here.
+ */
+ Tcl_UnregisterChannel(pDb->interp, p->channel);
+ }
+}
+
+/*
+** Close an incremental blob channel.
+*/
+static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
+ IncrblobChannel *p = (IncrblobChannel *)instanceData;
+ int rc = sqlite3_blob_close(p->pBlob);
+ sqlite3 *db = p->pDb->db;
+
+ /* Remove the channel from the SqliteDb.pIncrblob list. */
+ if( p->pNext ){
+ p->pNext->pPrev = p->pPrev;
+ }
+ if( p->pPrev ){
+ p->pPrev->pNext = p->pNext;
+ }
+ if( p->pDb->pIncrblob==p ){
+ p->pDb->pIncrblob = p->pNext;
+ }
+
+ /* Free the IncrblobChannel structure */
+ Tcl_Free((char *)p);
+
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Read data from an incremental blob channel.
+*/
+static int incrblobInput(
+ ClientData instanceData,
+ char *buf,
+ int bufSize,
+ int *errorCodePtr
+){
+ IncrblobChannel *p = (IncrblobChannel *)instanceData;
+ int nRead = bufSize; /* Number of bytes to read */
+ int nBlob; /* Total size of the blob */
+ int rc; /* sqlite error code */
+
+ nBlob = sqlite3_blob_bytes(p->pBlob);
+ if( (p->iSeek+nRead)>nBlob ){
+ nRead = nBlob-p->iSeek;
+ }
+ if( nRead<=0 ){
+ return 0;
+ }
+
+ rc = sqlite3_blob_read(p->pBlob, (void *)buf, nRead, p->iSeek);
+ if( rc!=SQLITE_OK ){
+ *errorCodePtr = rc;
+ return -1;
+ }
+
+ p->iSeek += nRead;
+ return nRead;
+}
+
+/*
+** Write data to an incremental blob channel.
+*/
+static int incrblobOutput(
+ ClientData instanceData,
+ CONST char *buf,
+ int toWrite,
+ int *errorCodePtr
+){
+ IncrblobChannel *p = (IncrblobChannel *)instanceData;
+ int nWrite = toWrite; /* Number of bytes to write */
+ int nBlob; /* Total size of the blob */
+ int rc; /* sqlite error code */
+
+ nBlob = sqlite3_blob_bytes(p->pBlob);
+ if( (p->iSeek+nWrite)>nBlob ){
+ *errorCodePtr = EINVAL;
+ return -1;
+ }
+ if( nWrite<=0 ){
+ return 0;
+ }
+
+ rc = sqlite3_blob_write(p->pBlob, (void *)buf, nWrite, p->iSeek);
+ if( rc!=SQLITE_OK ){
+ *errorCodePtr = EIO;
+ return -1;
+ }
+
+ p->iSeek += nWrite;
+ return nWrite;
+}
+
+/*
+** Seek an incremental blob channel.
+*/
+static int incrblobSeek(
+ ClientData instanceData,
+ long offset,
+ int seekMode,
+ int *errorCodePtr
+){
+ IncrblobChannel *p = (IncrblobChannel *)instanceData;
+
+ switch( seekMode ){
+ case SEEK_SET:
+ p->iSeek = offset;
+ break;
+ case SEEK_CUR:
+ p->iSeek += offset;
+ break;
+ case SEEK_END:
+ p->iSeek = sqlite3_blob_bytes(p->pBlob) + offset;
+ break;
+
+ default: assert(!"Bad seekMode");
+ }
+
+ return p->iSeek;
+}
+
+
+static void incrblobWatch(ClientData instanceData, int mode){
+ /* NO-OP */
+}
+static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){
+ return TCL_ERROR;
+}
+
+static Tcl_ChannelType IncrblobChannelType = {
+ "incrblob", /* typeName */
+ TCL_CHANNEL_VERSION_2, /* version */
+ incrblobClose, /* closeProc */
+ incrblobInput, /* inputProc */
+ incrblobOutput, /* outputProc */
+ incrblobSeek, /* seekProc */
+ 0, /* setOptionProc */
+ 0, /* getOptionProc */
+ incrblobWatch, /* watchProc (this is a no-op) */
+ incrblobHandle, /* getHandleProc (always returns error) */
+ 0, /* close2Proc */
+ 0, /* blockModeProc */
+ 0, /* flushProc */
+ 0, /* handlerProc */
+ 0, /* wideSeekProc */
+};
+
+/*
+** Create a new incrblob channel.
+*/
+static int createIncrblobChannel(
+ Tcl_Interp *interp,
+ SqliteDb *pDb,
+ const char *zDb,
+ const char *zTable,
+ const char *zColumn,
+ sqlite_int64 iRow,
+ int isReadonly
+){
+ IncrblobChannel *p;
+ sqlite3 *db = pDb->db;
+ sqlite3_blob *pBlob;
+ int rc;
+ int flags = TCL_READABLE|(isReadonly ? 0 : TCL_WRITABLE);
+
+ /* This variable is used to name the channels: "incrblob_[incr count]" */
+ static int count = 0;
+ char zChannel[64];
+
+ rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob);
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+
+ p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel));
+ p->iSeek = 0;
+ p->pBlob = pBlob;
+
+ sqlite3_snprintf(sizeof(zChannel), zChannel, "incrblob_%d", ++count);
+ p->channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags);
+ Tcl_RegisterChannel(interp, p->channel);
+
+ /* Link the new channel into the SqliteDb.pIncrblob list. */
+ p->pNext = pDb->pIncrblob;
+ p->pPrev = 0;
+ if( p->pNext ){
+ p->pNext->pPrev = p;
+ }
+ pDb->pIncrblob = p;
+ p->pDb = pDb;
+
+ Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE);
+ return TCL_OK;
+}
+#else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */
+ #define closeIncrblobChannels(pDb)
+#endif
+
+/*
+** Look at the script prefix in pCmd. We will be executing this script
+** after first appending one or more arguments. This routine analyzes
+** the script to see if it is safe to use Tcl_EvalObjv() on the script
+** rather than the more general Tcl_EvalEx(). Tcl_EvalObjv() is much
+** faster.
+**
+** Scripts that are safe to use with Tcl_EvalObjv() consists of a
+** command name followed by zero or more arguments with no [...] or $
+** or {...} or ; to be seen anywhere. Most callback scripts consist
+** of just a single procedure name and they meet this requirement.
+*/
+static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){
+ /* We could try to do something with Tcl_Parse(). But we will instead
+ ** just do a search for forbidden characters. If any of the forbidden
+ ** characters appear in pCmd, we will report the string as unsafe.
+ */
+ const char *z;
+ int n;
+ z = Tcl_GetStringFromObj(pCmd, &n);
+ while( n-- > 0 ){
+ int c = *(z++);
+ if( c=='$' || c=='[' || c==';' ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Find an SqlFunc structure with the given name. Or create a new
+** one if an existing one cannot be found. Return a pointer to the
+** structure.
+*/
+static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
+ SqlFunc *p, *pNew;
+ int i;
+ pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen(zName) + 1 );
+ pNew->zName = (char*)&pNew[1];
+ for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); }
+ pNew->zName[i] = 0;
+ for(p=pDb->pFunc; p; p=p->pNext){
+ if( strcmp(p->zName, pNew->zName)==0 ){
+ Tcl_Free((char*)pNew);
+ return p;
+ }
+ }
+ pNew->interp = pDb->interp;
+ pNew->pScript = 0;
+ pNew->pNext = pDb->pFunc;
+ pDb->pFunc = pNew;
+ return pNew;
+}
+
+/*
+** Finalize and free a list of prepared statements
+*/
+static void flushStmtCache( SqliteDb *pDb ){
+ SqlPreparedStmt *pPreStmt;
+
+ while( pDb->stmtList ){
+ sqlite3_finalize( pDb->stmtList->pStmt );
+ pPreStmt = pDb->stmtList;
+ pDb->stmtList = pDb->stmtList->pNext;
+ Tcl_Free( (char*)pPreStmt );
+ }
+ pDb->nStmt = 0;
+ pDb->stmtLast = 0;
+}
+
+/*
+** TCL calls this procedure when an sqlite3 database command is
+** deleted.
+*/
+static void DbDeleteCmd(void *db){
+ SqliteDb *pDb = (SqliteDb*)db;
+ flushStmtCache(pDb);
+ closeIncrblobChannels(pDb);
+ sqlite3_close(pDb->db);
+ while( pDb->pFunc ){
+ SqlFunc *pFunc = pDb->pFunc;
+ pDb->pFunc = pFunc->pNext;
+ Tcl_DecrRefCount(pFunc->pScript);
+ Tcl_Free((char*)pFunc);
+ }
+ while( pDb->pCollate ){
+ SqlCollate *pCollate = pDb->pCollate;
+ pDb->pCollate = pCollate->pNext;
+ Tcl_Free((char*)pCollate);
+ }
+ if( pDb->zBusy ){
+ Tcl_Free(pDb->zBusy);
+ }
+ if( pDb->zTrace ){
+ Tcl_Free(pDb->zTrace);
+ }
+ if( pDb->zProfile ){
+ Tcl_Free(pDb->zProfile);
+ }
+ if( pDb->zAuth ){
+ Tcl_Free(pDb->zAuth);
+ }
+ if( pDb->zNull ){
+ Tcl_Free(pDb->zNull);
+ }
+ if( pDb->pUpdateHook ){
+ Tcl_DecrRefCount(pDb->pUpdateHook);
+ }
+ if( pDb->pRollbackHook ){
+ Tcl_DecrRefCount(pDb->pRollbackHook);
+ }
+ if( pDb->pCollateNeeded ){
+ Tcl_DecrRefCount(pDb->pCollateNeeded);
+ }
+ Tcl_Free((char*)pDb);
+}
+
+/*
+** This routine is called when a database file is locked while trying
+** to execute SQL.
+*/
+static int DbBusyHandler(void *cd, int nTries){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ int rc;
+ char zVal[30];
+
+ sqlite3_snprintf(sizeof(zVal), zVal, "%d", nTries);
+ rc = Tcl_VarEval(pDb->interp, pDb->zBusy, " ", zVal, (char*)0);
+ if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
+ return 0;
+ }
+ return 1;
+}
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+/*
+** This routine is invoked as the 'progress callback' for the database.
+*/
+static int DbProgressHandler(void *cd){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ int rc;
+
+ assert( pDb->zProgress );
+ rc = Tcl_Eval(pDb->interp, pDb->zProgress);
+ if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#ifndef SQLITE_OMIT_TRACE
+/*
+** This routine is called by the SQLite trace handler whenever a new
+** block of SQL is executed. The TCL script in pDb->zTrace is executed.
+*/
+static void DbTraceHandler(void *cd, const char *zSql){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ Tcl_DString str;
+
+ Tcl_DStringInit(&str);
+ Tcl_DStringAppend(&str, pDb->zTrace, -1);
+ Tcl_DStringAppendElement(&str, zSql);
+ Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
+ Tcl_DStringFree(&str);
+ Tcl_ResetResult(pDb->interp);
+}
+#endif
+
+#ifndef SQLITE_OMIT_TRACE
+/*
+** This routine is called by the SQLite profile handler after a statement
+** SQL has executed. The TCL script in pDb->zProfile is evaluated.
+*/
+static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ Tcl_DString str;
+ char zTm[100];
+
+ sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm);
+ Tcl_DStringInit(&str);
+ Tcl_DStringAppend(&str, pDb->zProfile, -1);
+ Tcl_DStringAppendElement(&str, zSql);
+ Tcl_DStringAppendElement(&str, zTm);
+ Tcl_Eval(pDb->interp, Tcl_DStringValue(&str));
+ Tcl_DStringFree(&str);
+ Tcl_ResetResult(pDb->interp);
+}
+#endif
+
+/*
+** This routine is called when a transaction is committed. The
+** TCL script in pDb->zCommit is executed. If it returns non-zero or
+** if it throws an exception, the transaction is rolled back instead
+** of being committed.
+*/
+static int DbCommitHandler(void *cd){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ int rc;
+
+ rc = Tcl_Eval(pDb->interp, pDb->zCommit);
+ if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
+ return 1;
+ }
+ return 0;
+}
+
+static void DbRollbackHandler(void *clientData){
+ SqliteDb *pDb = (SqliteDb*)clientData;
+ assert(pDb->pRollbackHook);
+ if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){
+ Tcl_BackgroundError(pDb->interp);
+ }
+}
+
+static void DbUpdateHandler(
+ void *p,
+ int op,
+ const char *zDb,
+ const char *zTbl,
+ sqlite_int64 rowid
+){
+ SqliteDb *pDb = (SqliteDb *)p;
+ Tcl_Obj *pCmd;
+
+ assert( pDb->pUpdateHook );
+ assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
+
+ pCmd = Tcl_DuplicateObj(pDb->pUpdateHook);
+ Tcl_IncrRefCount(pCmd);
+ Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(
+ ( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1));
+ Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1));
+ Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1));
+ Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid));
+ Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
+}
+
+static void tclCollateNeeded(
+ void *pCtx,
+ sqlite3 *db,
+ int enc,
+ const char *zName
+){
+ SqliteDb *pDb = (SqliteDb *)pCtx;
+ Tcl_Obj *pScript = Tcl_DuplicateObj(pDb->pCollateNeeded);
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zName, -1));
+ Tcl_EvalObjEx(pDb->interp, pScript, 0);
+ Tcl_DecrRefCount(pScript);
+}
+
+/*
+** This routine is called to evaluate an SQL collation function implemented
+** using TCL script.
+*/
+static int tclSqlCollate(
+ void *pCtx,
+ int nA,
+ const void *zA,
+ int nB,
+ const void *zB
+){
+ SqlCollate *p = (SqlCollate *)pCtx;
+ Tcl_Obj *pCmd;
+
+ pCmd = Tcl_NewStringObj(p->zScript, -1);
+ Tcl_IncrRefCount(pCmd);
+ Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA));
+ Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB));
+ Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
+ Tcl_DecrRefCount(pCmd);
+ return (atoi(Tcl_GetStringResult(p->interp)));
+}
+
+/*
+** This routine is called to evaluate an SQL function implemented
+** using TCL script.
+*/
+static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
+ SqlFunc *p = sqlite3_user_data(context);
+ Tcl_Obj *pCmd;
+ int i;
+ int rc;
+
+ if( argc==0 ){
+ /* If there are no arguments to the function, call Tcl_EvalObjEx on the
+ ** script object directly. This allows the TCL compiler to generate
+ ** bytecode for the command on the first invocation and thus make
+ ** subsequent invocations much faster. */
+ pCmd = p->pScript;
+ Tcl_IncrRefCount(pCmd);
+ rc = Tcl_EvalObjEx(p->interp, pCmd, 0);
+ Tcl_DecrRefCount(pCmd);
+ }else{
+ /* If there are arguments to the function, make a shallow copy of the
+ ** script object, lappend the arguments, then evaluate the copy.
+ **
+ ** By "shallow" copy, we mean a only the outer list Tcl_Obj is duplicated.
+ ** The new Tcl_Obj contains pointers to the original list elements.
+ ** That way, when Tcl_EvalObjv() is run and shimmers the first element
+ ** of the list to tclCmdNameType, that alternate representation will
+ ** be preserved and reused on the next invocation.
+ */
+ Tcl_Obj **aArg;
+ int nArg;
+ if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){
+ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
+ return;
+ }
+ pCmd = Tcl_NewListObj(nArg, aArg);
+ Tcl_IncrRefCount(pCmd);
+ for(i=0; i<argc; i++){
+ sqlite3_value *pIn = argv[i];
+ Tcl_Obj *pVal;
+
+ /* Set pVal to contain the i'th column of this row. */
+ switch( sqlite3_value_type(pIn) ){
+ case SQLITE_BLOB: {
+ int bytes = sqlite3_value_bytes(pIn);
+ pVal = Tcl_NewByteArrayObj(sqlite3_value_blob(pIn), bytes);
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite_int64 v = sqlite3_value_int64(pIn);
+ if( v>=-2147483647 && v<=2147483647 ){
+ pVal = Tcl_NewIntObj(v);
+ }else{
+ pVal = Tcl_NewWideIntObj(v);
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double r = sqlite3_value_double(pIn);
+ pVal = Tcl_NewDoubleObj(r);
+ break;
+ }
+ case SQLITE_NULL: {
+ pVal = Tcl_NewStringObj("", 0);
+ break;
+ }
+ default: {
+ int bytes = sqlite3_value_bytes(pIn);
+ pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes);
+ break;
+ }
+ }
+ rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal);
+ if( rc ){
+ Tcl_DecrRefCount(pCmd);
+ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
+ return;
+ }
+ }
+ if( !p->useEvalObjv ){
+ /* Tcl_EvalObjEx() will automatically call Tcl_EvalObjv() if pCmd
+ ** is a list without a string representation. To prevent this from
+ ** happening, make sure pCmd has a valid string representation */
+ Tcl_GetString(pCmd);
+ }
+ rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
+ Tcl_DecrRefCount(pCmd);
+ }
+
+ if( rc && rc!=TCL_RETURN ){
+ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
+ }else{
+ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
+ int n;
+ u8 *data;
+ char *zType = pVar->typePtr ? pVar->typePtr->name : "";
+ char c = zType[0];
+ if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
+ /* Only return a BLOB type if the Tcl variable is a bytearray and
+ ** has no string representation. */
+ data = Tcl_GetByteArrayFromObj(pVar, &n);
+ sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT);
+ }else if( c=='b' && strcmp(zType,"boolean")==0 ){
+ Tcl_GetIntFromObj(0, pVar, &n);
+ sqlite3_result_int(context, n);
+ }else if( c=='d' && strcmp(zType,"double")==0 ){
+ double r;
+ Tcl_GetDoubleFromObj(0, pVar, &r);
+ sqlite3_result_double(context, r);
+ }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
+ (c=='i' && strcmp(zType,"int")==0) ){
+ Tcl_WideInt v;
+ Tcl_GetWideIntFromObj(0, pVar, &v);
+ sqlite3_result_int64(context, v);
+ }else{
+ data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
+ sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT);
+ }
+ }
+}
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This is the authentication function. It appends the authentication
+** type code and the two arguments to zCmd[] then invokes the result
+** on the interpreter. The reply is examined to determine if the
+** authentication fails or succeeds.
+*/
+static int auth_callback(
+ void *pArg,
+ int code,
+ const char *zArg1,
+ const char *zArg2,
+ const char *zArg3,
+ const char *zArg4
+){
+ char *zCode;
+ Tcl_DString str;
+ int rc;
+ const char *zReply;
+ SqliteDb *pDb = (SqliteDb*)pArg;
+
+ switch( code ){
+ case SQLITE_COPY : zCode="SQLITE_COPY"; break;
+ case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break;
+ case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break;
+ case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break;
+ case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break;
+ case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break;
+ case SQLITE_CREATE_TEMP_VIEW : zCode="SQLITE_CREATE_TEMP_VIEW"; break;
+ case SQLITE_CREATE_TRIGGER : zCode="SQLITE_CREATE_TRIGGER"; break;
+ case SQLITE_CREATE_VIEW : zCode="SQLITE_CREATE_VIEW"; break;
+ case SQLITE_DELETE : zCode="SQLITE_DELETE"; break;
+ case SQLITE_DROP_INDEX : zCode="SQLITE_DROP_INDEX"; break;
+ case SQLITE_DROP_TABLE : zCode="SQLITE_DROP_TABLE"; break;
+ case SQLITE_DROP_TEMP_INDEX : zCode="SQLITE_DROP_TEMP_INDEX"; break;
+ case SQLITE_DROP_TEMP_TABLE : zCode="SQLITE_DROP_TEMP_TABLE"; break;
+ case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break;
+ case SQLITE_DROP_TEMP_VIEW : zCode="SQLITE_DROP_TEMP_VIEW"; break;
+ case SQLITE_DROP_TRIGGER : zCode="SQLITE_DROP_TRIGGER"; break;
+ case SQLITE_DROP_VIEW : zCode="SQLITE_DROP_VIEW"; break;
+ case SQLITE_INSERT : zCode="SQLITE_INSERT"; break;
+ case SQLITE_PRAGMA : zCode="SQLITE_PRAGMA"; break;
+ case SQLITE_READ : zCode="SQLITE_READ"; break;
+ case SQLITE_SELECT : zCode="SQLITE_SELECT"; break;
+ case SQLITE_TRANSACTION : zCode="SQLITE_TRANSACTION"; break;
+ case SQLITE_UPDATE : zCode="SQLITE_UPDATE"; break;
+ case SQLITE_ATTACH : zCode="SQLITE_ATTACH"; break;
+ case SQLITE_DETACH : zCode="SQLITE_DETACH"; break;
+ case SQLITE_ALTER_TABLE : zCode="SQLITE_ALTER_TABLE"; break;
+ case SQLITE_REINDEX : zCode="SQLITE_REINDEX"; break;
+ case SQLITE_ANALYZE : zCode="SQLITE_ANALYZE"; break;
+ case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break;
+ case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break;
+ case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break;
+ default : zCode="????"; break;
+ }
+ Tcl_DStringInit(&str);
+ Tcl_DStringAppend(&str, pDb->zAuth, -1);
+ Tcl_DStringAppendElement(&str, zCode);
+ Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : "");
+ Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : "");
+ Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : "");
+ Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : "");
+ rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
+ Tcl_DStringFree(&str);
+ zReply = Tcl_GetStringResult(pDb->interp);
+ if( strcmp(zReply,"SQLITE_OK")==0 ){
+ rc = SQLITE_OK;
+ }else if( strcmp(zReply,"SQLITE_DENY")==0 ){
+ rc = SQLITE_DENY;
+ }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){
+ rc = SQLITE_IGNORE;
+ }else{
+ rc = 999;
+ }
+ return rc;
+}
+#endif /* SQLITE_OMIT_AUTHORIZATION */
+
+/*
+** zText is a pointer to text obtained via an sqlite3_result_text()
+** or similar interface. This routine returns a Tcl string object,
+** reference count set to 0, containing the text. If a translation
+** between iso8859 and UTF-8 is required, it is preformed.
+*/
+static Tcl_Obj *dbTextToObj(char const *zText){
+ Tcl_Obj *pVal;
+#ifdef UTF_TRANSLATION_NEEDED
+ Tcl_DString dCol;
+ Tcl_DStringInit(&dCol);
+ Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol);
+ pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1);
+ Tcl_DStringFree(&dCol);
+#else
+ pVal = Tcl_NewStringObj(zText, -1);
+#endif
+ return pVal;
+}
+
+/*
+** This routine reads a line of text from FILE in, stores
+** the text in memory obtained from malloc() and returns a pointer
+** to the text. NULL is returned at end of file, or if malloc()
+** fails.
+**
+** The interface is like "readline" but no command-line editing
+** is done.
+**
+** copied from shell.c from '.import' command
+*/
+static char *local_getline(char *zPrompt, FILE *in){
+ char *zLine;
+ int nLine;
+ int n;
+ int eol;
+
+ nLine = 100;
+ zLine = malloc( nLine );
+ if( zLine==0 ) return 0;
+ n = 0;
+ eol = 0;
+ while( !eol ){
+ if( n+100>nLine ){
+ nLine = nLine*2 + 100;
+ zLine = realloc(zLine, nLine);
+ if( zLine==0 ) return 0;
+ }
+ if( fgets(&zLine[n], nLine - n, in)==0 ){
+ if( n==0 ){
+ free(zLine);
+ return 0;
+ }
+ zLine[n] = 0;
+ eol = 1;
+ break;
+ }
+ while( zLine[n] ){ n++; }
+ if( n>0 && zLine[n-1]=='\n' ){
+ n--;
+ zLine[n] = 0;
+ eol = 1;
+ }
+ }
+ zLine = realloc( zLine, n+1 );
+ return zLine;
+}
+
+
+/*
+** Figure out the column names for the data returned by the statement
+** passed as the second argument.
+**
+** If parameter papColName is not NULL, then *papColName is set to point
+** at an array allocated using Tcl_Alloc(). It is the callers responsibility
+** to free this array using Tcl_Free(), and to decrement the reference
+** count of each Tcl_Obj* member of the array.
+**
+** The return value of this function is the number of columns of data
+** returned by pStmt (and hence the size of the *papColName array).
+**
+** If pArray is not NULL, then it contains the name of a Tcl array
+** variable. The "*" member of this array is set to a list containing
+** the names of the columns returned by the statement, in order from
+** left to right. e.g. if the names of the returned columns are a, b and
+** c, it does the equivalent of the tcl command:
+**
+** set ${pArray}(*) {a b c}
+*/
+static int
+computeColumnNames(
+ Tcl_Interp *interp,
+ sqlite3_stmt *pStmt, /* SQL statement */
+ Tcl_Obj ***papColName, /* OUT: Array of column names */
+ Tcl_Obj *pArray /* Name of array variable (may be null) */
+){
+ int nCol;
+
+ /* Compute column names */
+ nCol = sqlite3_column_count(pStmt);
+ if( papColName ){
+ int i;
+ Tcl_Obj **apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
+ for(i=0; i<nCol; i++){
+ apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
+ Tcl_IncrRefCount(apColName[i]);
+ }
+
+ /* If results are being stored in an array variable, then create
+ ** the array(*) entry for that array
+ */
+ if( pArray ){
+ Tcl_Obj *pColList = Tcl_NewObj();
+ Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
+ Tcl_IncrRefCount(pColList);
+ for(i=0; i<nCol; i++){
+ Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
+ }
+ Tcl_IncrRefCount(pStar);
+ Tcl_ObjSetVar2(interp, pArray, pStar, pColList,0);
+ Tcl_DecrRefCount(pColList);
+ Tcl_DecrRefCount(pStar);
+ }
+ *papColName = apColName;
+ }
+
+ return nCol;
+}
+
+/*
+** The "sqlite" command below creates a new Tcl command for each
+** connection it opens to an SQLite database. This routine is invoked
+** whenever one of those connection-specific commands is executed
+** in Tcl. For example, if you run Tcl code like this:
+**
+** sqlite3 db1 "my_database"
+** db1 close
+**
+** The first command opens a connection to the "my_database" database
+** and calls that connection "db1". The second command causes this
+** subroutine to be invoked.
+*/
+static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
+ SqliteDb *pDb = (SqliteDb*)cd;
+ int choice;
+ int rc = TCL_OK;
+ static const char *DB_strs[] = {
+ "authorizer", "busy", "cache",
+ "changes", "close", "collate",
+ "collation_needed", "commit_hook", "complete",
+ "copy", "enable_load_extension","errorcode",
+ "eval", "exists", "function",
+ "incrblob", "interrupt", "last_insert_rowid",
+ "nullvalue", "onecolumn", "profile",
+ "progress", "rekey", "rollback_hook",
+ "timeout", "total_changes", "trace",
+ "transaction", "update_hook", "version",
+ 0
+ };
+ enum DB_enum {
+ DB_AUTHORIZER, DB_BUSY, DB_CACHE,
+ DB_CHANGES, DB_CLOSE, DB_COLLATE,
+ DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE,
+ DB_COPY, DB_ENABLE_LOAD_EXTENSION,DB_ERRORCODE,
+ DB_EVAL, DB_EXISTS, DB_FUNCTION,
+ DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID,
+ DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE,
+ DB_PROGRESS, DB_REKEY, DB_ROLLBACK_HOOK,
+ DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
+ DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION
+ };
+ /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
+ return TCL_ERROR;
+ }
+
+ switch( (enum DB_enum)choice ){
+
+ /* $db authorizer ?CALLBACK?
+ **
+ ** Invoke the given callback to authorize each SQL operation as it is
+ ** compiled. 5 arguments are appended to the callback before it is
+ ** invoked:
+ **
+ ** (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...)
+ ** (2) First descriptive name (depends on authorization type)
+ ** (3) Second descriptive name
+ ** (4) Name of the database (ex: "main", "temp")
+ ** (5) Name of trigger that is doing the access
+ **
+ ** The callback should return on of the following strings: SQLITE_OK,
+ ** SQLITE_IGNORE, or SQLITE_DENY. Any other return value is an error.
+ **
+ ** If this method is invoked with no arguments, the current authorization
+ ** callback string is returned.
+ */
+ case DB_AUTHORIZER: {
+#ifdef SQLITE_OMIT_AUTHORIZATION
+ Tcl_AppendResult(interp, "authorization not available in this build", 0);
+ return TCL_ERROR;
+#else
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+ return TCL_ERROR;
+ }else if( objc==2 ){
+ if( pDb->zAuth ){
+ Tcl_AppendResult(interp, pDb->zAuth, 0);
+ }
+ }else{
+ char *zAuth;
+ int len;
+ if( pDb->zAuth ){
+ Tcl_Free(pDb->zAuth);
+ }
+ zAuth = Tcl_GetStringFromObj(objv[2], &len);
+ if( zAuth && len>0 ){
+ pDb->zAuth = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zAuth, zAuth, len+1);
+ }else{
+ pDb->zAuth = 0;
+ }
+ if( pDb->zAuth ){
+ pDb->interp = interp;
+ sqlite3_set_authorizer(pDb->db, auth_callback, pDb);
+ }else{
+ sqlite3_set_authorizer(pDb->db, 0, 0);
+ }
+ }
+#endif
+ break;
+ }
+
+ /* $db busy ?CALLBACK?
+ **
+ ** Invoke the given callback if an SQL statement attempts to open
+ ** a locked database file.
+ */
+ case DB_BUSY: {
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "CALLBACK");
+ return TCL_ERROR;
+ }else if( objc==2 ){
+ if( pDb->zBusy ){
+ Tcl_AppendResult(interp, pDb->zBusy, 0);
+ }
+ }else{
+ char *zBusy;
+ int len;
+ if( pDb->zBusy ){
+ Tcl_Free(pDb->zBusy);
+ }
+ zBusy = Tcl_GetStringFromObj(objv[2], &len);
+ if( zBusy && len>0 ){
+ pDb->zBusy = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zBusy, zBusy, len+1);
+ }else{
+ pDb->zBusy = 0;
+ }
+ if( pDb->zBusy ){
+ pDb->interp = interp;
+ sqlite3_busy_handler(pDb->db, DbBusyHandler, pDb);
+ }else{
+ sqlite3_busy_handler(pDb->db, 0, 0);
+ }
+ }
+ break;
+ }
+
+ /* $db cache flush
+ ** $db cache size n
+ **
+ ** Flush the prepared statement cache, or set the maximum number of
+ ** cached statements.
+ */
+ case DB_CACHE: {
+ char *subCmd;
+ int n;
+
+ if( objc<=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "cache option ?arg?");
+ return TCL_ERROR;
+ }
+ subCmd = Tcl_GetStringFromObj( objv[2], 0 );
+ if( *subCmd=='f' && strcmp(subCmd,"flush")==0 ){
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "flush");
+ return TCL_ERROR;
+ }else{
+ flushStmtCache( pDb );
+ }
+ }else if( *subCmd=='s' && strcmp(subCmd,"size")==0 ){
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "size n");
+ return TCL_ERROR;
+ }else{
+ if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
+ Tcl_AppendResult( interp, "cannot convert \"",
+ Tcl_GetStringFromObj(objv[3],0), "\" to integer", 0);
+ return TCL_ERROR;
+ }else{
+ if( n<0 ){
+ flushStmtCache( pDb );
+ n = 0;
+ }else if( n>MAX_PREPARED_STMTS ){
+ n = MAX_PREPARED_STMTS;
+ }
+ pDb->maxStmt = n;
+ }
+ }
+ }else{
+ Tcl_AppendResult( interp, "bad option \"",
+ Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", 0);
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ /* $db changes
+ **
+ ** Return the number of rows that were modified, inserted, or deleted by
+ ** the most recent INSERT, UPDATE or DELETE statement, not including
+ ** any changes made by trigger programs.
+ */
+ case DB_CHANGES: {
+ Tcl_Obj *pResult;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+ pResult = Tcl_GetObjResult(interp);
+ Tcl_SetIntObj(pResult, sqlite3_changes(pDb->db));
+ break;
+ }
+
+ /* $db close
+ **
+ ** Shutdown the database
+ */
+ case DB_CLOSE: {
+ Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));
+ break;
+ }
+
+ /*
+ ** $db collate NAME SCRIPT
+ **
+ ** Create a new SQL collation function called NAME. Whenever
+ ** that function is called, invoke SCRIPT to evaluate the function.
+ */
+ case DB_COLLATE: {
+ SqlCollate *pCollate;
+ char *zName;
+ char *zScript;
+ int nScript;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
+ return TCL_ERROR;
+ }
+ zName = Tcl_GetStringFromObj(objv[2], 0);
+ zScript = Tcl_GetStringFromObj(objv[3], &nScript);
+ pCollate = (SqlCollate*)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 );
+ if( pCollate==0 ) return TCL_ERROR;
+ pCollate->interp = interp;
+ pCollate->pNext = pDb->pCollate;
+ pCollate->zScript = (char*)&pCollate[1];
+ pDb->pCollate = pCollate;
+ memcpy(pCollate->zScript, zScript, nScript+1);
+ if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8,
+ pCollate, tclSqlCollate) ){
+ Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ /*
+ ** $db collation_needed SCRIPT
+ **
+ ** Create a new SQL collation function called NAME. Whenever
+ ** that function is called, invoke SCRIPT to evaluate the function.
+ */
+ case DB_COLLATION_NEEDED: {
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SCRIPT");
+ return TCL_ERROR;
+ }
+ if( pDb->pCollateNeeded ){
+ Tcl_DecrRefCount(pDb->pCollateNeeded);
+ }
+ pDb->pCollateNeeded = Tcl_DuplicateObj(objv[2]);
+ Tcl_IncrRefCount(pDb->pCollateNeeded);
+ sqlite3_collation_needed(pDb->db, pDb, tclCollateNeeded);
+ break;
+ }
+
+ /* $db commit_hook ?CALLBACK?
+ **
+ ** Invoke the given callback just before committing every SQL transaction.
+ ** If the callback throws an exception or returns non-zero, then the
+ ** transaction is aborted. If CALLBACK is an empty string, the callback
+ ** is disabled.
+ */
+ case DB_COMMIT_HOOK: {
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+ return TCL_ERROR;
+ }else if( objc==2 ){
+ if( pDb->zCommit ){
+ Tcl_AppendResult(interp, pDb->zCommit, 0);
+ }
+ }else{
+ char *zCommit;
+ int len;
+ if( pDb->zCommit ){
+ Tcl_Free(pDb->zCommit);
+ }
+ zCommit = Tcl_GetStringFromObj(objv[2], &len);
+ if( zCommit && len>0 ){
+ pDb->zCommit = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zCommit, zCommit, len+1);
+ }else{
+ pDb->zCommit = 0;
+ }
+ if( pDb->zCommit ){
+ pDb->interp = interp;
+ sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb);
+ }else{
+ sqlite3_commit_hook(pDb->db, 0, 0);
+ }
+ }
+ break;
+ }
+
+ /* $db complete SQL
+ **
+ ** Return TRUE if SQL is a complete SQL statement. Return FALSE if
+ ** additional lines of input are needed. This is similar to the
+ ** built-in "info complete" command of Tcl.
+ */
+ case DB_COMPLETE: {
+#ifndef SQLITE_OMIT_COMPLETE
+ Tcl_Obj *pResult;
+ int isComplete;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SQL");
+ return TCL_ERROR;
+ }
+ isComplete = sqlite3_complete( Tcl_GetStringFromObj(objv[2], 0) );
+ pResult = Tcl_GetObjResult(interp);
+ Tcl_SetBooleanObj(pResult, isComplete);
+#endif
+ break;
+ }
+
+ /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR?
+ **
+ ** Copy data into table from filename, optionally using SEPARATOR
+ ** as column separators. If a column contains a null string, or the
+ ** value of NULLINDICATOR, a NULL is inserted for the column.
+ ** conflict-algorithm is one of the sqlite conflict algorithms:
+ ** rollback, abort, fail, ignore, replace
+ ** On success, return the number of lines processed, not necessarily same
+ ** as 'db changes' due to conflict-algorithm selected.
+ **
+ ** This code is basically an implementation/enhancement of
+ ** the sqlite3 shell.c ".import" command.
+ **
+ ** This command usage is equivalent to the sqlite2.x COPY statement,
+ ** which imports file data into a table using the PostgreSQL COPY file format:
+ ** $db copy $conflit_algo $table_name $filename \t \\N
+ */
+ case DB_COPY: {
+ char *zTable; /* Insert data into this table */
+ char *zFile; /* The file from which to extract data */
+ char *zConflict; /* The conflict algorithm to use */
+ sqlite3_stmt *pStmt; /* A statement */
+ int nCol; /* Number of columns in the table */
+ int nByte; /* Number of bytes in an SQL string */
+ int i, j; /* Loop counters */
+ int nSep; /* Number of bytes in zSep[] */
+ int nNull; /* Number of bytes in zNull[] */
+ char *zSql; /* An SQL statement */
+ char *zLine; /* A single line of input from the file */
+ char **azCol; /* zLine[] broken up into columns */
+ char *zCommit; /* How to commit changes */
+ FILE *in; /* The input file */
+ int lineno = 0; /* Line number of input file */
+ char zLineNum[80]; /* Line number print buffer */
+ Tcl_Obj *pResult; /* interp result */
+
+ char *zSep;
+ char *zNull;
+ if( objc<5 || objc>7 ){
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
+ return TCL_ERROR;
+ }
+ if( objc>=6 ){
+ zSep = Tcl_GetStringFromObj(objv[5], 0);
+ }else{
+ zSep = "\t";
+ }
+ if( objc>=7 ){
+ zNull = Tcl_GetStringFromObj(objv[6], 0);
+ }else{
+ zNull = "";
+ }
+ zConflict = Tcl_GetStringFromObj(objv[2], 0);
+ zTable = Tcl_GetStringFromObj(objv[3], 0);
+ zFile = Tcl_GetStringFromObj(objv[4], 0);
+ nSep = strlen(zSep);
+ nNull = strlen(zNull);
+ if( nSep==0 ){
+ Tcl_AppendResult(interp,"Error: non-null separator required for copy",0);
+ return TCL_ERROR;
+ }
+ if(strcasecmp(zConflict, "rollback") != 0 &&
+ strcasecmp(zConflict, "abort" ) != 0 &&
+ strcasecmp(zConflict, "fail" ) != 0 &&
+ strcasecmp(zConflict, "ignore" ) != 0 &&
+ strcasecmp(zConflict, "replace" ) != 0 ) {
+ Tcl_AppendResult(interp, "Error: \"", zConflict,
+ "\", conflict-algorithm must be one of: rollback, "
+ "abort, fail, ignore, or replace", 0);
+ return TCL_ERROR;
+ }
+ zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
+ if( zSql==0 ){
+ Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
+ return TCL_ERROR;
+ }
+ nByte = strlen(zSql);
+ rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc ){
+ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+ nCol = 0;
+ }else{
+ nCol = sqlite3_column_count(pStmt);
+ }
+ sqlite3_finalize(pStmt);
+ if( nCol==0 ) {
+ return TCL_ERROR;
+ }
+ zSql = malloc( nByte + 50 + nCol*2 );
+ if( zSql==0 ) {
+ Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
+ zConflict, zTable);
+ j = strlen(zSql);
+ for(i=1; i<nCol; i++){
+ zSql[j++] = ',';
+ zSql[j++] = '?';
+ }
+ zSql[j++] = ')';
+ zSql[j] = 0;
+ rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
+ free(zSql);
+ if( rc ){
+ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+ sqlite3_finalize(pStmt);
+ return TCL_ERROR;
+ }
+ in = fopen(zFile, "rb");
+ if( in==0 ){
+ Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL);
+ sqlite3_finalize(pStmt);
+ return TCL_ERROR;
+ }
+ azCol = malloc( sizeof(azCol[0])*(nCol+1) );
+ if( azCol==0 ) {
+ Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+ fclose(in);
+ return TCL_ERROR;
+ }
+ (void)sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0);
+ zCommit = "COMMIT";
+ while( (zLine = local_getline(0, in))!=0 ){
+ char *z;
+ i = 0;
+ lineno++;
+ azCol[0] = zLine;
+ for(i=0, z=zLine; *z; z++){
+ if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){
+ *z = 0;
+ i++;
+ if( i<nCol ){
+ azCol[i] = &z[nSep];
+ z += nSep-1;
+ }
+ }
+ }
+ if( i+1!=nCol ){
+ char *zErr;
+ int nErr = strlen(zFile) + 200;
+ zErr = malloc(nErr);
+ if( zErr ){
+ sqlite3_snprintf(nErr, zErr,
+ "Error: %s line %d: expected %d columns of data but found %d",
+ zFile, lineno, nCol, i+1);
+ Tcl_AppendResult(interp, zErr, 0);
+ free(zErr);
+ }
+ zCommit = "ROLLBACK";
+ break;
+ }
+ for(i=0; i<nCol; i++){
+ /* check for null data, if so, bind as null */
+ if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) {
+ sqlite3_bind_null(pStmt, i+1);
+ }else{
+ sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
+ }
+ }
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ free(zLine);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0);
+ zCommit = "ROLLBACK";
+ break;
+ }
+ }
+ free(azCol);
+ fclose(in);
+ sqlite3_finalize(pStmt);
+ (void)sqlite3_exec(pDb->db, zCommit, 0, 0, 0);
+
+ if( zCommit[0] == 'C' ){
+ /* success, set result as number of lines processed */
+ pResult = Tcl_GetObjResult(interp);
+ Tcl_SetIntObj(pResult, lineno);
+ rc = TCL_OK;
+ }else{
+ /* failure, append lineno where failed */
+ sqlite3_snprintf(sizeof(zLineNum), zLineNum,"%d",lineno);
+ Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0);
+ rc = TCL_ERROR;
+ }
+ break;
+ }
+
+ /*
+ ** $db enable_load_extension BOOLEAN
+ **
+ ** Turn the extension loading feature on or off. It if off by
+ ** default.
+ */
+ case DB_ENABLE_LOAD_EXTENSION: {
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ int onoff;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){
+ return TCL_ERROR;
+ }
+ sqlite3_enable_load_extension(pDb->db, onoff);
+ break;
+#else
+ Tcl_AppendResult(interp, "extension loading is turned off at compile-time",
+ 0);
+ return TCL_ERROR;
+#endif
+ }
+
+ /*
+ ** $db errorcode
+ **
+ ** Return the numeric error code that was returned by the most recent
+ ** call to sqlite3_exec().
+ */
+ case DB_ERRORCODE: {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db)));
+ break;
+ }
+
+ /*
+ ** $db eval $sql ?array? ?{ ...code... }?
+ ** $db onecolumn $sql
+ **
+ ** The SQL statement in $sql is evaluated. For each row, the values are
+ ** placed in elements of the array named "array" and ...code... is executed.
+ ** If "array" and "code" are omitted, then no callback is every invoked.
+ ** If "array" is an empty string, then the values are placed in variables
+ ** that have the same name as the fields extracted by the query.
+ **
+ ** The onecolumn method is the equivalent of:
+ ** lindex [$db eval $sql] 0
+ */
+ case DB_ONECOLUMN:
+ case DB_EVAL:
+ case DB_EXISTS: {
+ char const *zSql; /* Next SQL statement to execute */
+ char const *zLeft; /* What is left after first stmt in zSql */
+ sqlite3_stmt *pStmt; /* Compiled SQL statment */
+ Tcl_Obj *pArray; /* Name of array into which results are written */
+ Tcl_Obj *pScript; /* Script to run for each result set */
+ Tcl_Obj **apParm; /* Parameters that need a Tcl_DecrRefCount() */
+ int nParm; /* Number of entries used in apParm[] */
+ Tcl_Obj *aParm[10]; /* Static space for apParm[] in the common case */
+ Tcl_Obj *pRet; /* Value to be returned */
+ SqlPreparedStmt *pPreStmt; /* Pointer to a prepared statement */
+ int rc2;
+
+ if( choice==DB_EVAL ){
+ if( objc<3 || objc>5 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?");
+ return TCL_ERROR;
+ }
+ pRet = Tcl_NewObj();
+ Tcl_IncrRefCount(pRet);
+ }else{
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SQL");
+ return TCL_ERROR;
+ }
+ if( choice==DB_EXISTS ){
+ pRet = Tcl_NewBooleanObj(0);
+ Tcl_IncrRefCount(pRet);
+ }else{
+ pRet = 0;
+ }
+ }
+ if( objc==3 ){
+ pArray = pScript = 0;
+ }else if( objc==4 ){
+ pArray = 0;
+ pScript = objv[3];
+ }else{
+ pArray = objv[3];
+ if( Tcl_GetString(pArray)[0]==0 ) pArray = 0;
+ pScript = objv[4];
+ }
+
+ Tcl_IncrRefCount(objv[2]);
+ zSql = Tcl_GetStringFromObj(objv[2], 0);
+ while( rc==TCL_OK && zSql[0] ){
+ int i; /* Loop counter */
+ int nVar; /* Number of bind parameters in the pStmt */
+ int nCol = -1; /* Number of columns in the result set */
+ Tcl_Obj **apColName = 0; /* Array of column names */
+ int len; /* String length of zSql */
+
+ /* Try to find a SQL statement that has already been compiled and
+ ** which matches the next sequence of SQL.
+ */
+ pStmt = 0;
+ len = strlen(zSql);
+ for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
+ int n = pPreStmt->nSql;
+ if( len>=n
+ && memcmp(pPreStmt->zSql, zSql, n)==0
+ && (zSql[n]==0 || zSql[n-1]==';')
+ ){
+ pStmt = pPreStmt->pStmt;
+ zLeft = &zSql[pPreStmt->nSql];
+
+ /* When a prepared statement is found, unlink it from the
+ ** cache list. It will later be added back to the beginning
+ ** of the cache list in order to implement LRU replacement.
+ */
+ if( pPreStmt->pPrev ){
+ pPreStmt->pPrev->pNext = pPreStmt->pNext;
+ }else{
+ pDb->stmtList = pPreStmt->pNext;
+ }
+ if( pPreStmt->pNext ){
+ pPreStmt->pNext->pPrev = pPreStmt->pPrev;
+ }else{
+ pDb->stmtLast = pPreStmt->pPrev;
+ }
+ pDb->nStmt--;
+ break;
+ }
+ }
+
+ /* If no prepared statement was found. Compile the SQL text
+ */
+ if( pStmt==0 ){
+ if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, &zLeft) ){
+ Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ rc = TCL_ERROR;
+ break;
+ }
+ if( pStmt==0 ){
+ if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){
+ /* A compile-time error in the statement
+ */
+ Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ rc = TCL_ERROR;
+ break;
+ }else{
+ /* The statement was a no-op. Continue to the next statement
+ ** in the SQL string.
+ */
+ zSql = zLeft;
+ continue;
+ }
+ }
+ assert( pPreStmt==0 );
+ }
+
+ /* Bind values to parameters that begin with $ or :
+ */
+ nVar = sqlite3_bind_parameter_count(pStmt);
+ nParm = 0;
+ if( nVar>sizeof(aParm)/sizeof(aParm[0]) ){
+ apParm = (Tcl_Obj**)Tcl_Alloc(nVar*sizeof(apParm[0]));
+ }else{
+ apParm = aParm;
+ }
+ for(i=1; i<=nVar; i++){
+ const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
+ if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){
+ Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0);
+ if( pVar ){
+ int n;
+ u8 *data;
+ char *zType = pVar->typePtr ? pVar->typePtr->name : "";
+ char c = zType[0];
+ if( zVar[0]=='@' ||
+ (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){
+ /* Load a BLOB type if the Tcl variable is a bytearray and
+ ** it has no string representation or the host
+ ** parameter name begins with "@". */
+ data = Tcl_GetByteArrayFromObj(pVar, &n);
+ sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC);
+ Tcl_IncrRefCount(pVar);
+ apParm[nParm++] = pVar;
+ }else if( c=='b' && strcmp(zType,"boolean")==0 ){
+ Tcl_GetIntFromObj(interp, pVar, &n);
+ sqlite3_bind_int(pStmt, i, n);
+ }else if( c=='d' && strcmp(zType,"double")==0 ){
+ double r;
+ Tcl_GetDoubleFromObj(interp, pVar, &r);
+ sqlite3_bind_double(pStmt, i, r);
+ }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
+ (c=='i' && strcmp(zType,"int")==0) ){
+ Tcl_WideInt v;
+ Tcl_GetWideIntFromObj(interp, pVar, &v);
+ sqlite3_bind_int64(pStmt, i, v);
+ }else{
+ data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
+ sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC);
+ Tcl_IncrRefCount(pVar);
+ apParm[nParm++] = pVar;
+ }
+ }else{
+ sqlite3_bind_null( pStmt, i );
+ }
+ }
+ }
+
+ /* Execute the SQL
+ */
+ while( rc==TCL_OK && pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
+
+ /* Compute column names. This must be done after the first successful
+ ** call to sqlite3_step(), in case the query is recompiled and the
+ ** number or names of the returned columns changes.
+ */
+ assert(!pArray||pScript);
+ if (nCol < 0) {
+ Tcl_Obj ***ap = (pScript?&apColName:0);
+ nCol = computeColumnNames(interp, pStmt, ap, pArray);
+ }
+
+ for(i=0; i<nCol; i++){
+ Tcl_Obj *pVal;
+
+ /* Set pVal to contain the i'th column of this row. */
+ switch( sqlite3_column_type(pStmt, i) ){
+ case SQLITE_BLOB: {
+ int bytes = sqlite3_column_bytes(pStmt, i);
+ const char *zBlob = sqlite3_column_blob(pStmt, i);
+ if( !zBlob ) bytes = 0;
+ pVal = Tcl_NewByteArrayObj((u8*)zBlob, bytes);
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite_int64 v = sqlite3_column_int64(pStmt, i);
+ if( v>=-2147483647 && v<=2147483647 ){
+ pVal = Tcl_NewIntObj(v);
+ }else{
+ pVal = Tcl_NewWideIntObj(v);
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double r = sqlite3_column_double(pStmt, i);
+ pVal = Tcl_NewDoubleObj(r);
+ break;
+ }
+ case SQLITE_NULL: {
+ pVal = dbTextToObj(pDb->zNull);
+ break;
+ }
+ default: {
+ pVal = dbTextToObj((char *)sqlite3_column_text(pStmt, i));
+ break;
+ }
+ }
+
+ if( pScript ){
+ if( pArray==0 ){
+ Tcl_ObjSetVar2(interp, apColName[i], 0, pVal, 0);
+ }else{
+ Tcl_ObjSetVar2(interp, pArray, apColName[i], pVal, 0);
+ }
+ }else if( choice==DB_ONECOLUMN ){
+ assert( pRet==0 );
+ if( pRet==0 ){
+ pRet = pVal;
+ Tcl_IncrRefCount(pRet);
+ }
+ rc = TCL_BREAK;
+ i = nCol;
+ }else if( choice==DB_EXISTS ){
+ Tcl_DecrRefCount(pRet);
+ pRet = Tcl_NewBooleanObj(1);
+ Tcl_IncrRefCount(pRet);
+ rc = TCL_BREAK;
+ i = nCol;
+ }else{
+ Tcl_ListObjAppendElement(interp, pRet, pVal);
+ }
+ }
+
+ if( pScript ){
+ rc = Tcl_EvalObjEx(interp, pScript, 0);
+ if( rc==TCL_CONTINUE ){
+ rc = TCL_OK;
+ }
+ }
+ }
+ if( rc==TCL_BREAK ){
+ rc = TCL_OK;
+ }
+
+ /* Free the column name objects */
+ if( pScript ){
+ /* If the query returned no rows, but an array variable was
+ ** specified, call computeColumnNames() now to populate the
+ ** arrayname(*) variable.
+ */
+ if (pArray && nCol < 0) {
+ Tcl_Obj ***ap = (pScript?&apColName:0);
+ nCol = computeColumnNames(interp, pStmt, ap, pArray);
+ }
+ for(i=0; i<nCol; i++){
+ Tcl_DecrRefCount(apColName[i]);
+ }
+ Tcl_Free((char*)apColName);
+ }
+
+ /* Free the bound string and blob parameters */
+ for(i=0; i<nParm; i++){
+ Tcl_DecrRefCount(apParm[i]);
+ }
+ if( apParm!=aParm ){
+ Tcl_Free((char*)apParm);
+ }
+
+ /* Reset the statement. If the result code is SQLITE_SCHEMA, then
+ ** flush the statement cache and try the statement again.
+ */
+ rc2 = sqlite3_reset(pStmt);
+ if( SQLITE_OK!=rc2 ){
+ /* If a run-time error occurs, report the error and stop reading
+ ** the SQL
+ */
+ Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ sqlite3_finalize(pStmt);
+ rc = TCL_ERROR;
+ if( pPreStmt ) Tcl_Free((char*)pPreStmt);
+ break;
+ }else if( pDb->maxStmt<=0 ){
+ /* If the cache is turned off, deallocated the statement */
+ if( pPreStmt ) Tcl_Free((char*)pPreStmt);
+ sqlite3_finalize(pStmt);
+ }else{
+ /* Everything worked and the cache is operational.
+ ** Create a new SqlPreparedStmt structure if we need one.
+ ** (If we already have one we can just reuse it.)
+ */
+ if( pPreStmt==0 ){
+ len = zLeft - zSql;
+ pPreStmt = (SqlPreparedStmt*)Tcl_Alloc( sizeof(*pPreStmt) );
+ if( pPreStmt==0 ) return TCL_ERROR;
+ pPreStmt->pStmt = pStmt;
+ pPreStmt->nSql = len;
+ pPreStmt->zSql = sqlite3_sql(pStmt);
+ assert( strlen(pPreStmt->zSql)==len );
+ assert( 0==memcmp(pPreStmt->zSql, zSql, len) );
+ }
+
+ /* Add the prepared statement to the beginning of the cache list
+ */
+ pPreStmt->pNext = pDb->stmtList;
+ pPreStmt->pPrev = 0;
+ if( pDb->stmtList ){
+ pDb->stmtList->pPrev = pPreStmt;
+ }
+ pDb->stmtList = pPreStmt;
+ if( pDb->stmtLast==0 ){
+ assert( pDb->nStmt==0 );
+ pDb->stmtLast = pPreStmt;
+ }else{
+ assert( pDb->nStmt>0 );
+ }
+ pDb->nStmt++;
+
+ /* If we have too many statement in cache, remove the surplus from the
+ ** end of the cache list.
+ */
+ while( pDb->nStmt>pDb->maxStmt ){
+ sqlite3_finalize(pDb->stmtLast->pStmt);
+ pDb->stmtLast = pDb->stmtLast->pPrev;
+ Tcl_Free((char*)pDb->stmtLast->pNext);
+ pDb->stmtLast->pNext = 0;
+ pDb->nStmt--;
+ }
+ }
+
+ /* Proceed to the next statement */
+ zSql = zLeft;
+ }
+ Tcl_DecrRefCount(objv[2]);
+
+ if( pRet ){
+ if( rc==TCL_OK ){
+ Tcl_SetObjResult(interp, pRet);
+ }
+ Tcl_DecrRefCount(pRet);
+ }else if( rc==TCL_OK ){
+ Tcl_ResetResult(interp);
+ }
+ break;
+ }
+
+ /*
+ ** $db function NAME SCRIPT
+ **
+ ** Create a new SQL function called NAME. Whenever that function is
+ ** called, invoke SCRIPT to evaluate the function.
+ */
+ case DB_FUNCTION: {
+ SqlFunc *pFunc;
+ Tcl_Obj *pScript;
+ char *zName;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
+ return TCL_ERROR;
+ }
+ zName = Tcl_GetStringFromObj(objv[2], 0);
+ pScript = objv[3];
+ pFunc = findSqlFunc(pDb, zName);
+ if( pFunc==0 ) return TCL_ERROR;
+ if( pFunc->pScript ){
+ Tcl_DecrRefCount(pFunc->pScript);
+ }
+ pFunc->pScript = pScript;
+ Tcl_IncrRefCount(pScript);
+ pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript);
+ rc = sqlite3_create_function(pDb->db, zName, -1, SQLITE_UTF8,
+ pFunc, tclSqlFunc, 0, 0);
+ if( rc!=SQLITE_OK ){
+ rc = TCL_ERROR;
+ Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
+ }
+ break;
+ }
+
+ /*
+ ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID
+ */
+ case DB_INCRBLOB: {
+#ifdef SQLITE_OMIT_INCRBLOB
+ Tcl_AppendResult(interp, "incrblob not available in this build", 0);
+ return TCL_ERROR;
+#else
+ int isReadonly = 0;
+ const char *zDb = "main";
+ const char *zTable;
+ const char *zColumn;
+ sqlite_int64 iRow;
+
+ /* Check for the -readonly option */
+ if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){
+ isReadonly = 1;
+ }
+
+ if( objc!=(5+isReadonly) && objc!=(6+isReadonly) ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID");
+ return TCL_ERROR;
+ }
+
+ if( objc==(6+isReadonly) ){
+ zDb = Tcl_GetString(objv[2]);
+ }
+ zTable = Tcl_GetString(objv[objc-3]);
+ zColumn = Tcl_GetString(objv[objc-2]);
+ rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow);
+
+ if( rc==TCL_OK ){
+ rc = createIncrblobChannel(
+ interp, pDb, zDb, zTable, zColumn, iRow, isReadonly
+ );
+ }
+#endif
+ break;
+ }
+
+ /*
+ ** $db interrupt
+ **
+ ** Interrupt the execution of the inner-most SQL interpreter. This
+ ** causes the SQL statement to return an error of SQLITE_INTERRUPT.
+ */
+ case DB_INTERRUPT: {
+ sqlite3_interrupt(pDb->db);
+ break;
+ }
+
+ /*
+ ** $db nullvalue ?STRING?
+ **
+ ** Change text used when a NULL comes back from the database. If ?STRING?
+ ** is not present, then the current string used for NULL is returned.
+ ** If STRING is present, then STRING is returned.
+ **
+ */
+ case DB_NULLVALUE: {
+ if( objc!=2 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE");
+ return TCL_ERROR;
+ }
+ if( objc==3 ){
+ int len;
+ char *zNull = Tcl_GetStringFromObj(objv[2], &len);
+ if( pDb->zNull ){
+ Tcl_Free(pDb->zNull);
+ }
+ if( zNull && len>0 ){
+ pDb->zNull = Tcl_Alloc( len + 1 );
+ strncpy(pDb->zNull, zNull, len);
+ pDb->zNull[len] = '\0';
+ }else{
+ pDb->zNull = 0;
+ }
+ }
+ Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull));
+ break;
+ }
+
+ /*
+ ** $db last_insert_rowid
+ **
+ ** Return an integer which is the ROWID for the most recent insert.
+ */
+ case DB_LAST_INSERT_ROWID: {
+ Tcl_Obj *pResult;
+ Tcl_WideInt rowid;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+ rowid = sqlite3_last_insert_rowid(pDb->db);
+ pResult = Tcl_GetObjResult(interp);
+ Tcl_SetWideIntObj(pResult, rowid);
+ break;
+ }
+
+ /*
+ ** The DB_ONECOLUMN method is implemented together with DB_EVAL.
+ */
+
+ /* $db progress ?N CALLBACK?
+ **
+ ** Invoke the given callback every N virtual machine opcodes while executing
+ ** queries.
+ */
+ case DB_PROGRESS: {
+ if( objc==2 ){
+ if( pDb->zProgress ){
+ Tcl_AppendResult(interp, pDb->zProgress, 0);
+ }
+ }else if( objc==4 ){
+ char *zProgress;
+ int len;
+ int N;
+ if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &N) ){
+ return TCL_ERROR;
+ };
+ if( pDb->zProgress ){
+ Tcl_Free(pDb->zProgress);
+ }
+ zProgress = Tcl_GetStringFromObj(objv[3], &len);
+ if( zProgress && len>0 ){
+ pDb->zProgress = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zProgress, zProgress, len+1);
+ }else{
+ pDb->zProgress = 0;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( pDb->zProgress ){
+ pDb->interp = interp;
+ sqlite3_progress_handler(pDb->db, N, DbProgressHandler, pDb);
+ }else{
+ sqlite3_progress_handler(pDb->db, 0, 0, 0);
+ }
+#endif
+ }else{
+ Tcl_WrongNumArgs(interp, 2, objv, "N CALLBACK");
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ /* $db profile ?CALLBACK?
+ **
+ ** Make arrangements to invoke the CALLBACK routine after each SQL statement
+ ** that has run. The text of the SQL and the amount of elapse time are
+ ** appended to CALLBACK before the script is run.
+ */
+ case DB_PROFILE: {
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+ return TCL_ERROR;
+ }else if( objc==2 ){
+ if( pDb->zProfile ){
+ Tcl_AppendResult(interp, pDb->zProfile, 0);
+ }
+ }else{
+ char *zProfile;
+ int len;
+ if( pDb->zProfile ){
+ Tcl_Free(pDb->zProfile);
+ }
+ zProfile = Tcl_GetStringFromObj(objv[2], &len);
+ if( zProfile && len>0 ){
+ pDb->zProfile = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zProfile, zProfile, len+1);
+ }else{
+ pDb->zProfile = 0;
+ }
+#ifndef SQLITE_OMIT_TRACE
+ if( pDb->zProfile ){
+ pDb->interp = interp;
+ sqlite3_profile(pDb->db, DbProfileHandler, pDb);
+ }else{
+ sqlite3_profile(pDb->db, 0, 0);
+ }
+#endif
+ }
+ break;
+ }
+
+ /*
+ ** $db rekey KEY
+ **
+ ** Change the encryption key on the currently open database.
+ */
+ case DB_REKEY: {
+ int nKey;
+ void *pKey;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "KEY");
+ return TCL_ERROR;
+ }
+ pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
+#ifdef SQLITE_HAS_CODEC
+ rc = sqlite3_rekey(pDb->db, pKey, nKey);
+ if( rc ){
+ Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
+ rc = TCL_ERROR;
+ }
+#endif
+ break;
+ }
+
+ /*
+ ** $db timeout MILLESECONDS
+ **
+ ** Delay for the number of milliseconds specified when a file is locked.
+ */
+ case DB_TIMEOUT: {
+ int ms;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR;
+ sqlite3_busy_timeout(pDb->db, ms);
+ break;
+ }
+
+ /*
+ ** $db total_changes
+ **
+ ** Return the number of rows that were modified, inserted, or deleted
+ ** since the database handle was created.
+ */
+ case DB_TOTAL_CHANGES: {
+ Tcl_Obj *pResult;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+ pResult = Tcl_GetObjResult(interp);
+ Tcl_SetIntObj(pResult, sqlite3_total_changes(pDb->db));
+ break;
+ }
+
+ /* $db trace ?CALLBACK?
+ **
+ ** Make arrangements to invoke the CALLBACK routine for each SQL statement
+ ** that is executed. The text of the SQL is appended to CALLBACK before
+ ** it is executed.
+ */
+ case DB_TRACE: {
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+ return TCL_ERROR;
+ }else if( objc==2 ){
+ if( pDb->zTrace ){
+ Tcl_AppendResult(interp, pDb->zTrace, 0);
+ }
+ }else{
+ char *zTrace;
+ int len;
+ if( pDb->zTrace ){
+ Tcl_Free(pDb->zTrace);
+ }
+ zTrace = Tcl_GetStringFromObj(objv[2], &len);
+ if( zTrace && len>0 ){
+ pDb->zTrace = Tcl_Alloc( len + 1 );
+ memcpy(pDb->zTrace, zTrace, len+1);
+ }else{
+ pDb->zTrace = 0;
+ }
+#ifndef SQLITE_OMIT_TRACE
+ if( pDb->zTrace ){
+ pDb->interp = interp;
+ sqlite3_trace(pDb->db, DbTraceHandler, pDb);
+ }else{
+ sqlite3_trace(pDb->db, 0, 0);
+ }
+#endif
+ }
+ break;
+ }
+
+ /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT
+ **
+ ** Start a new transaction (if we are not already in the midst of a
+ ** transaction) and execute the TCL script SCRIPT. After SCRIPT
+ ** completes, either commit the transaction or roll it back if SCRIPT
+ ** throws an exception. Or if no new transation was started, do nothing.
+ ** pass the exception on up the stack.
+ **
+ ** This command was inspired by Dave Thomas's talk on Ruby at the
+ ** 2005 O'Reilly Open Source Convention (OSCON).
+ */
+ case DB_TRANSACTION: {
+ int inTrans;
+ Tcl_Obj *pScript;
+ const char *zBegin = "BEGIN";
+ if( objc!=3 && objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT");
+ return TCL_ERROR;
+ }
+ if( objc==3 ){
+ pScript = objv[2];
+ } else {
+ static const char *TTYPE_strs[] = {
+ "deferred", "exclusive", "immediate", 0
+ };
+ enum TTYPE_enum {
+ TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE
+ };
+ int ttype;
+ if( Tcl_GetIndexFromObj(interp, objv[2], TTYPE_strs, "transaction type",
+ 0, &ttype) ){
+ return TCL_ERROR;
+ }
+ switch( (enum TTYPE_enum)ttype ){
+ case TTYPE_DEFERRED: /* no-op */; break;
+ case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break;
+ case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break;
+ }
+ pScript = objv[3];
+ }
+ inTrans = !sqlite3_get_autocommit(pDb->db);
+ if( !inTrans ){
+ (void)sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
+ }
+ rc = Tcl_EvalObjEx(interp, pScript, 0);
+ if( !inTrans ){
+ const char *zEnd;
+ if( rc==TCL_ERROR ){
+ zEnd = "ROLLBACK";
+ } else {
+ zEnd = "COMMIT";
+ }
+ if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
+ sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
+ }
+ }
+ break;
+ }
+
+ /*
+ ** $db update_hook ?script?
+ ** $db rollback_hook ?script?
+ */
+ case DB_UPDATE_HOOK:
+ case DB_ROLLBACK_HOOK: {
+
+ /* set ppHook to point at pUpdateHook or pRollbackHook, depending on
+ ** whether [$db update_hook] or [$db rollback_hook] was invoked.
+ */
+ Tcl_Obj **ppHook;
+ if( choice==DB_UPDATE_HOOK ){
+ ppHook = &pDb->pUpdateHook;
+ }else{
+ ppHook = &pDb->pRollbackHook;
+ }
+
+ if( objc!=2 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
+ return TCL_ERROR;
+ }
+ if( *ppHook ){
+ Tcl_SetObjResult(interp, *ppHook);
+ if( objc==3 ){
+ Tcl_DecrRefCount(*ppHook);
+ *ppHook = 0;
+ }
+ }
+ if( objc==3 ){
+ assert( !(*ppHook) );
+ if( Tcl_GetCharLength(objv[2])>0 ){
+ *ppHook = objv[2];
+ Tcl_IncrRefCount(*ppHook);
+ }
+ }
+
+ sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
+ sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
+
+ break;
+ }
+
+ /* $db version
+ **
+ ** Return the version string for this database.
+ */
+ case DB_VERSION: {
+ Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC);
+ break;
+ }
+
+
+ } /* End of the SWITCH statement */
+ return rc;
+}
+
+/*
+** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN?
+** ?-create BOOLEAN? ?-nomutex BOOLEAN?
+**
+** This is the main Tcl command. When the "sqlite" Tcl command is
+** invoked, this routine runs to process that command.
+**
+** The first argument, DBNAME, is an arbitrary name for a new
+** database connection. This command creates a new command named
+** DBNAME that is used to control that connection. The database
+** connection is deleted when the DBNAME command is deleted.
+**
+** The second argument is the name of the database file.
+**
+*/
+static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
+ SqliteDb *p;
+ void *pKey = 0;
+ int nKey = 0;
+ const char *zArg;
+ char *zErrMsg;
+ int i;
+ const char *zFile;
+ const char *zVfs = 0;
+ int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+ Tcl_DString translatedFilename;
+ if( objc==2 ){
+ zArg = Tcl_GetStringFromObj(objv[1], 0);
+ if( strcmp(zArg,"-version")==0 ){
+ Tcl_AppendResult(interp,sqlite3_version,0);
+ return TCL_OK;
+ }
+ if( strcmp(zArg,"-has-codec")==0 ){
+#ifdef SQLITE_HAS_CODEC
+ Tcl_AppendResult(interp,"1",0);
+#else
+ Tcl_AppendResult(interp,"0",0);
+#endif
+ return TCL_OK;
+ }
+ }
+ for(i=3; i+1<objc; i+=2){
+ zArg = Tcl_GetString(objv[i]);
+ if( strcmp(zArg,"-key")==0 ){
+ pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey);
+ }else if( strcmp(zArg, "-vfs")==0 ){
+ i++;
+ zVfs = Tcl_GetString(objv[i]);
+ }else if( strcmp(zArg, "-readonly")==0 ){
+ int b;
+ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
+ if( b ){
+ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
+ flags |= SQLITE_OPEN_READONLY;
+ }else{
+ flags &= ~SQLITE_OPEN_READONLY;
+ flags |= SQLITE_OPEN_READWRITE;
+ }
+ }else if( strcmp(zArg, "-create")==0 ){
+ int b;
+ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
+ if( b && (flags & SQLITE_OPEN_READONLY)==0 ){
+ flags |= SQLITE_OPEN_CREATE;
+ }else{
+ flags &= ~SQLITE_OPEN_CREATE;
+ }
+ }else if( strcmp(zArg, "-nomutex")==0 ){
+ int b;
+ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
+ if( b ){
+ flags |= SQLITE_OPEN_NOMUTEX;
+ }else{
+ flags &= ~SQLITE_OPEN_NOMUTEX;
+ }
+ }else{
+ Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
+ return TCL_ERROR;
+ }
+ }
+ if( objc<3 || (objc&1)!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
+ " ?-nomutex BOOLEAN?"
+#ifdef SQLITE_HAS_CODEC
+ " ?-key CODECKEY?"
+#endif
+ );
+ return TCL_ERROR;
+ }
+ zErrMsg = 0;
+ p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
+ if( p==0 ){
+ Tcl_SetResult(interp, "malloc failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ memset(p, 0, sizeof(*p));
+ zFile = Tcl_GetStringFromObj(objv[2], 0);
+ zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename);
+ sqlite3_open_v2(zFile, &p->db, flags, zVfs);
+ Tcl_DStringFree(&translatedFilename);
+ if( SQLITE_OK!=sqlite3_errcode(p->db) ){
+ zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
+ sqlite3_close(p->db);
+ p->db = 0;
+ }
+#ifdef SQLITE_HAS_CODEC
+ if( p->db ){
+ sqlite3_key(p->db, pKey, nKey);
+ }
+#endif
+ if( p->db==0 ){
+ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
+ Tcl_Free((char*)p);
+ sqlite3_free(zErrMsg);
+ return TCL_ERROR;
+ }
+ p->maxStmt = NUM_PREPARED_STMTS;
+ p->interp = interp;
+ zArg = Tcl_GetStringFromObj(objv[1], 0);
+ Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd);
+ return TCL_OK;
+}
+
+/*
+** Provide a dummy Tcl_InitStubs if we are using this as a static
+** library.
+*/
+#ifndef USE_TCL_STUBS
+# undef Tcl_InitStubs
+# define Tcl_InitStubs(a,b,c)
+#endif
+
+/*
+** Make sure we have a PACKAGE_VERSION macro defined. This will be
+** defined automatically by the TEA makefile. But other makefiles
+** do not define it.
+*/
+#ifndef PACKAGE_VERSION
+# define PACKAGE_VERSION SQLITE_VERSION
+#endif
+
+/*
+** Initialize this module.
+**
+** This Tcl module contains only a single new Tcl command named "sqlite".
+** (Hence there is no namespace. There is no point in using a namespace
+** if the extension only supplies one new name!) The "sqlite" command is
+** used to open a new SQLite database. See the DbMain() routine above
+** for additional information.
+*/
+EXTERN int Sqlite3_Init(Tcl_Interp *interp){
+ Tcl_InitStubs(interp, "8.4", 0);
+ Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
+ Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
+ Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
+ Tcl_PkgProvide(interp, "sqlite", PACKAGE_VERSION);
+ return TCL_OK;
+}
+EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
+EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
+EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
+
+#ifndef SQLITE_3_SUFFIX_ONLY
+EXTERN int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
+EXTERN int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
+EXTERN int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
+EXTERN int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
+#endif
+
+#ifdef TCLSH
+/*****************************************************************************
+** The code that follows is used to build standalone TCL interpreters
+** that are statically linked with SQLite.
+*/
+
+/*
+** If the macro TCLSH is one, then put in code this for the
+** "main" routine that will initialize Tcl and take input from
+** standard input, or if a file is named on the command line
+** the TCL interpreter reads and evaluates that file.
+*/
+#if TCLSH==1
+static char zMainloop[] =
+ "set line {}\n"
+ "while {![eof stdin]} {\n"
+ "if {$line!=\"\"} {\n"
+ "puts -nonewline \"> \"\n"
+ "} else {\n"
+ "puts -nonewline \"% \"\n"
+ "}\n"
+ "flush stdout\n"
+ "append line [gets stdin]\n"
+ "if {[info complete $line]} {\n"
+ "if {[catch {uplevel #0 $line} result]} {\n"
+ "puts stderr \"Error: $result\"\n"
+ "} elseif {$result!=\"\"} {\n"
+ "puts $result\n"
+ "}\n"
+ "set line {}\n"
+ "} else {\n"
+ "append line \\n\n"
+ "}\n"
+ "}\n"
+;
+#endif
+
+/*
+** If the macro TCLSH is two, then get the main loop code out of
+** the separate file "spaceanal_tcl.h".
+*/
+#if TCLSH==2
+static char zMainloop[] =
+#include "spaceanal_tcl.h"
+;
+#endif
+
+#define TCLSH_MAIN main /* Needed to fake out mktclapp */
+int TCLSH_MAIN(int argc, char **argv){
+ Tcl_Interp *interp;
+ Tcl_FindExecutable(argv[0]);
+ interp = Tcl_CreateInterp();
+ Sqlite3_Init(interp);
+#ifdef SQLITE_TEST
+ {
+ extern int Md5_Init(Tcl_Interp*);
+ extern int Sqliteconfig_Init(Tcl_Interp*);
+ extern int Sqlitetest1_Init(Tcl_Interp*);
+ extern int Sqlitetest2_Init(Tcl_Interp*);
+ extern int Sqlitetest3_Init(Tcl_Interp*);
+ extern int Sqlitetest4_Init(Tcl_Interp*);
+ extern int Sqlitetest5_Init(Tcl_Interp*);
+ extern int Sqlitetest6_Init(Tcl_Interp*);
+ extern int Sqlitetest7_Init(Tcl_Interp*);
+ extern int Sqlitetest8_Init(Tcl_Interp*);
+ extern int Sqlitetest9_Init(Tcl_Interp*);
+ extern int Sqlitetestasync_Init(Tcl_Interp*);
+ extern int Sqlitetest_autoext_Init(Tcl_Interp*);
+ extern int Sqlitetest_func_Init(Tcl_Interp*);
+ extern int Sqlitetest_hexio_Init(Tcl_Interp*);
+ extern int Sqlitetest_malloc_Init(Tcl_Interp*);
+ extern int Sqlitetest_mutex_Init(Tcl_Interp*);
+ extern int Sqlitetestschema_Init(Tcl_Interp*);
+ extern int Sqlitetestsse_Init(Tcl_Interp*);
+ extern int Sqlitetesttclvar_Init(Tcl_Interp*);
+ extern int SqlitetestThread_Init(Tcl_Interp*);
+ extern int SqlitetestOnefile_Init();
+ extern int SqlitetestOsinst_Init(Tcl_Interp*);
+
+ Md5_Init(interp);
+ Sqliteconfig_Init(interp);
+ Sqlitetest1_Init(interp);
+ Sqlitetest2_Init(interp);
+ Sqlitetest3_Init(interp);
+ Sqlitetest4_Init(interp);
+ Sqlitetest5_Init(interp);
+ Sqlitetest6_Init(interp);
+ Sqlitetest7_Init(interp);
+ Sqlitetest8_Init(interp);
+ Sqlitetest9_Init(interp);
+ Sqlitetestasync_Init(interp);
+ Sqlitetest_autoext_Init(interp);
+ Sqlitetest_func_Init(interp);
+ Sqlitetest_hexio_Init(interp);
+ Sqlitetest_malloc_Init(interp);
+ Sqlitetest_mutex_Init(interp);
+ Sqlitetestschema_Init(interp);
+ Sqlitetesttclvar_Init(interp);
+ SqlitetestThread_Init(interp);
+ SqlitetestOnefile_Init(interp);
+ SqlitetestOsinst_Init(interp);
+
+#ifdef SQLITE_SSE
+ Sqlitetestsse_Init(interp);
+#endif
+ }
+#endif
+ if( argc>=2 || TCLSH==2 ){
+ int i;
+ char zArgc[32];
+ sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH));
+ Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
+ for(i=3-TCLSH; i<argc; i++){
+ Tcl_SetVar(interp, "argv", argv[i],
+ TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
+ }
+ if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){
+ const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+ if( zInfo==0 ) zInfo = interp->result;
+ fprintf(stderr,"%s: %s\n", *argv, zInfo);
+ return 1;
+ }
+ }
+ if( argc<=1 || TCLSH==2 ){
+ Tcl_GlobalEval(interp, zMainloop);
+ }
+ return 0;
+}
+#endif /* TCLSH */
diff --git a/third_party/sqlite/src/test1.c b/third_party/sqlite/src/test1.c
new file mode 100755
index 0000000..f83d38f
--- /dev/null
+++ b/third_party/sqlite/src/test1.c
@@ -0,0 +1,4784 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+** Code for testing all sorts of SQLite interfaces. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test1.c,v 1.317 2008/07/31 02:05:04 shane Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** This is a copy of the first part of the SqliteDb structure in
+** tclsqlite.c. We need it here so that the get_sqlite_pointer routine
+** can extract the sqlite3* pointer from an existing Tcl SQLite
+** connection.
+*/
+struct SqliteDb {
+ sqlite3 *db;
+};
+
+/*
+** Convert text generated by the "%p" conversion format back into
+** a pointer.
+*/
+static int testHexToInt(int h){
+ if( h>='0' && h<='9' ){
+ return h - '0';
+ }else if( h>='a' && h<='f' ){
+ return h - 'a' + 10;
+ }else{
+ assert( h>='A' && h<='F' );
+ return h - 'A' + 10;
+ }
+}
+void *sqlite3TestTextToPtr(const char *z){
+ void *p;
+ u64 v;
+ u32 v2;
+ if( z[0]=='0' && z[1]=='x' ){
+ z += 2;
+ }
+ v = 0;
+ while( *z ){
+ v = (v<<4) + testHexToInt(*z);
+ z++;
+ }
+ if( sizeof(p)==sizeof(v) ){
+ memcpy(&p, &v, sizeof(p));
+ }else{
+ assert( sizeof(p)==sizeof(v2) );
+ v2 = (u32)v;
+ memcpy(&p, &v2, sizeof(p));
+ }
+ return p;
+}
+
+
+/*
+** A TCL command that returns the address of the sqlite* pointer
+** for an sqlite connection instance. Bad things happen if the
+** input is not an sqlite connection.
+*/
+static int get_sqlite_pointer(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ struct SqliteDb *p;
+ Tcl_CmdInfo cmdInfo;
+ char zBuf[100];
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SQLITE-CONNECTION");
+ return TCL_ERROR;
+ }
+ if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
+ Tcl_AppendResult(interp, "command not found: ",
+ Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ p = (struct SqliteDb*)cmdInfo.objClientData;
+ sprintf(zBuf, "%p", p->db);
+ if( strncmp(zBuf,"0x",2) ){
+ sprintf(zBuf, "0x%p", p->db);
+ }
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
+ struct SqliteDb *p;
+ Tcl_CmdInfo cmdInfo;
+ if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){
+ p = (struct SqliteDb*)cmdInfo.objClientData;
+ *ppDb = p->db;
+ }else{
+ *ppDb = (sqlite3*)sqlite3TestTextToPtr(zA);
+ }
+ return TCL_OK;
+}
+
+
+const char *sqlite3TestErrorName(int rc){
+ const char *zName = 0;
+ switch( rc & 0xff ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
+ case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
+ case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
+ case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
+ case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
+ case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
+ case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
+ case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
+ case SQLITE_ROW: zName = "SQLITE_ROW"; break;
+ case SQLITE_DONE: zName = "SQLITE_DONE"; break;
+ case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break;
+ case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
+ default: zName = "SQLITE_Unknown"; break;
+ }
+ return zName;
+}
+#define t1ErrorName sqlite3TestErrorName
+
+/*
+** Convert an sqlite3_stmt* into an sqlite3*. This depends on the
+** fact that the sqlite3* is the first field in the Vdbe structure.
+*/
+#define StmtToDb(X) sqlite3_db_handle(X)
+
+/*
+** Check a return value to make sure it agrees with the results
+** from sqlite3_errcode.
+*/
+int sqlite3TestErrCode(Tcl_Interp *interp, sqlite3 *db, int rc){
+ if( rc!=SQLITE_MISUSE && rc!=SQLITE_OK && sqlite3_errcode(db)!=rc ){
+ char zBuf[200];
+ int r2 = sqlite3_errcode(db);
+ sprintf(zBuf, "error code %s (%d) does not match sqlite3_errcode %s (%d)",
+ t1ErrorName(rc), rc, t1ErrorName(r2), r2);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Decode a pointer to an sqlite3_stmt object.
+*/
+static int getStmtPointer(
+ Tcl_Interp *interp,
+ const char *zArg,
+ sqlite3_stmt **ppStmt
+){
+ *ppStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(zArg);
+ return TCL_OK;
+}
+
+/*
+** Generate a text representation of a pointer that can be understood
+** by the getDbPointer and getVmPointer routines above.
+**
+** The problem is, on some machines (Solaris) if you do a printf with
+** "%p" you cannot turn around and do a scanf with the same "%p" and
+** get your pointer back. You have to prepend a "0x" before it will
+** work. Or at least that is what is reported to me (drh). But this
+** behavior varies from machine to machine. The solution used her is
+** to test the string right after it is generated to see if it can be
+** understood by scanf, and if not, try prepending an "0x" to see if
+** that helps. If nothing works, a fatal error is generated.
+*/
+int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p){
+ sqlite3_snprintf(100, zPtr, "%p", p);
+ return TCL_OK;
+}
+
+/*
+** The callback routine for sqlite3_exec_printf().
+*/
+static int exec_printf_cb(void *pArg, int argc, char **argv, char **name){
+ Tcl_DString *str = (Tcl_DString*)pArg;
+ int i;
+
+ if( Tcl_DStringLength(str)==0 ){
+ for(i=0; i<argc; i++){
+ Tcl_DStringAppendElement(str, name[i] ? name[i] : "NULL");
+ }
+ }
+ for(i=0; i<argc; i++){
+ Tcl_DStringAppendElement(str, argv[i] ? argv[i] : "NULL");
+ }
+ return 0;
+}
+
+/*
+** The I/O tracing callback.
+*/
+#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
+static FILE *iotrace_file = 0;
+static void io_trace_callback(const char *zFormat, ...){
+ va_list ap;
+ va_start(ap, zFormat);
+ vfprintf(iotrace_file, zFormat, ap);
+ va_end(ap);
+ fflush(iotrace_file);
+}
+#endif
+
+/*
+** Usage: io_trace FILENAME
+**
+** Turn I/O tracing on or off. If FILENAME is not an empty string,
+** I/O tracing begins going into FILENAME. If FILENAME is an empty
+** string, I/O tracing is turned off.
+*/
+static int test_io_trace(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ if( iotrace_file ){
+ if( iotrace_file!=stdout && iotrace_file!=stderr ){
+ fclose(iotrace_file);
+ }
+ iotrace_file = 0;
+ sqlite3IoTrace = 0;
+ }
+ if( argv[1][0] ){
+ if( strcmp(argv[1],"stdout")==0 ){
+ iotrace_file = stdout;
+ }else if( strcmp(argv[1],"stderr")==0 ){
+ iotrace_file = stderr;
+ }else{
+ iotrace_file = fopen(argv[1], "w");
+ }
+ sqlite3IoTrace = io_trace_callback;
+ }
+#endif
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_exec_printf DB FORMAT STRING
+**
+** Invoke the sqlite3_exec_printf() interface using the open database
+** DB. The SQL is the string FORMAT. The format string should contain
+** one %s or %q. STRING is the value inserted into %s or %q.
+*/
+static int test_exec_printf(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ Tcl_DString str;
+ int rc;
+ char *zErr = 0;
+ char *zSql;
+ char zBuf[30];
+ if( argc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB FORMAT STRING", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ Tcl_DStringInit(&str);
+ zSql = sqlite3_mprintf(argv[2], argv[3]);
+ rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr);
+ sqlite3_free(zSql);
+ sprintf(zBuf, "%d", rc);
+ Tcl_AppendElement(interp, zBuf);
+ Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr);
+ Tcl_DStringFree(&str);
+ if( zErr ) sqlite3_free(zErr);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Usage: db_enter DB
+** db_leave DB
+**
+** Enter or leave the mutex on a database connection.
+*/
+static int db_enter(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ sqlite3_mutex_enter(db->mutex);
+ return TCL_OK;
+}
+static int db_leave(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ sqlite3_mutex_leave(db->mutex);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_exec DB SQL
+**
+** Invoke the sqlite3_exec interface using the open database DB
+*/
+static int test_exec(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ Tcl_DString str;
+ int rc;
+ char *zErr = 0;
+ char *zSql;
+ int i, j;
+ char zBuf[30];
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB SQL", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ Tcl_DStringInit(&str);
+ zSql = sqlite3_mprintf("%s", argv[2]);
+ for(i=j=0; zSql[i];){
+ if( zSql[i]=='%' ){
+ zSql[j++] = (testHexToInt(zSql[i+1])<<4) + testHexToInt(zSql[i+2]);
+ i += 3;
+ }else{
+ zSql[j++] = zSql[i++];
+ }
+ }
+ zSql[j] = 0;
+ rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr);
+ sqlite3_free(zSql);
+ sprintf(zBuf, "%d", rc);
+ Tcl_AppendElement(interp, zBuf);
+ Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr);
+ Tcl_DStringFree(&str);
+ if( zErr ) sqlite3_free(zErr);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_exec_nr DB SQL
+**
+** Invoke the sqlite3_exec interface using the open database DB. Discard
+** all results
+*/
+static int test_exec_nr(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ int rc;
+ char *zErr = 0;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB SQL", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_z_test SEPARATOR ARG0 ARG1 ...
+**
+** Test the %z format of sqliteMPrintf(). Use multiple mprintf() calls to
+** concatenate arg0 through argn using separator as the separator.
+** Return the result.
+*/
+static int test_mprintf_z(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ char *zResult = 0;
+ int i;
+
+ for(i=2; i<argc && (i==2 || zResult); i++){
+ zResult = sqlite3MPrintf(0, "%z%s%s", zResult, argv[1], argv[i]);
+ }
+ Tcl_AppendResult(interp, zResult, 0);
+ sqlite3_free(zResult);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_n_test STRING
+**
+** Test the %n format of sqliteMPrintf(). Return the length of the
+** input string.
+*/
+static int test_mprintf_n(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ char *zStr;
+ int n = 0;
+ zStr = sqlite3MPrintf(0, "%s%n", argv[1], &n);
+ sqlite3_free(zStr);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_snprintf_int SIZE FORMAT INT
+**
+** Test the of sqlite3_snprintf() routine. SIZE is the size of the
+** output buffer in bytes. The maximum size is 100. FORMAT is the
+** format string. INT is a single integer argument. The FORMAT
+** string must require no more than this one integer argument. If
+** You pass in a format string that requires more than one argument,
+** bad things will happen.
+*/
+static int test_snprintf_int(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ char zStr[100];
+ int n = atoi(argv[1]);
+ const char *zFormat = argv[2];
+ int a1 = atoi(argv[3]);
+ if( n>sizeof(zStr) ) n = sizeof(zStr);
+ sqlite3_snprintf(sizeof(zStr), zStr, "abcdefghijklmnopqrstuvwxyz");
+ sqlite3_snprintf(n, zStr, zFormat, a1);
+ Tcl_AppendResult(interp, zStr, 0);
+ return TCL_OK;
+}
+
+#ifndef SQLITE_OMIT_GET_TABLE
+
+/*
+** Usage: sqlite3_get_table_printf DB FORMAT STRING ?--no-counts?
+**
+** Invoke the sqlite3_get_table_printf() interface using the open database
+** DB. The SQL is the string FORMAT. The format string should contain
+** one %s or %q. STRING is the value inserted into %s or %q.
+*/
+static int test_get_table_printf(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ Tcl_DString str;
+ int rc;
+ char *zErr = 0;
+ int nRow, nCol;
+ char **aResult;
+ int i;
+ char zBuf[30];
+ char *zSql;
+ int resCount = -1;
+ if( argc==5 ){
+ if( Tcl_GetInt(interp, argv[4], &resCount) ) return TCL_ERROR;
+ }
+ if( argc!=4 && argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB FORMAT STRING ?COUNT?", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ Tcl_DStringInit(&str);
+ zSql = sqlite3_mprintf(argv[2],argv[3]);
+ if( argc==5 ){
+ rc = sqlite3_get_table(db, zSql, &aResult, 0, 0, &zErr);
+ }else{
+ rc = sqlite3_get_table(db, zSql, &aResult, &nRow, &nCol, &zErr);
+ resCount = (nRow+1)*nCol;
+ }
+ sqlite3_free(zSql);
+ sprintf(zBuf, "%d", rc);
+ Tcl_AppendElement(interp, zBuf);
+ if( rc==SQLITE_OK ){
+ if( argc==4 ){
+ sprintf(zBuf, "%d", nRow);
+ Tcl_AppendElement(interp, zBuf);
+ sprintf(zBuf, "%d", nCol);
+ Tcl_AppendElement(interp, zBuf);
+ }
+ for(i=0; i<resCount; i++){
+ Tcl_AppendElement(interp, aResult[i] ? aResult[i] : "NULL");
+ }
+ }else{
+ Tcl_AppendElement(interp, zErr);
+ }
+ sqlite3_free_table(aResult);
+ if( zErr ) sqlite3_free(zErr);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+#endif /* SQLITE_OMIT_GET_TABLE */
+
+
+/*
+** Usage: sqlite3_last_insert_rowid DB
+**
+** Returns the integer ROWID of the most recent insert.
+*/
+static int test_last_rowid(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ char zBuf[30];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ sprintf(zBuf, "%lld", sqlite3_last_insert_rowid(db));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: sqlite3_key DB KEY
+**
+** Set the codec key.
+*/
+static int test_key(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ const char *zKey;
+ int nKey;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ zKey = argv[2];
+ nKey = strlen(zKey);
+#ifdef SQLITE_HAS_CODEC
+ sqlite3_key(db, zKey, nKey);
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_rekey DB KEY
+**
+** Change the codec key.
+*/
+static int test_rekey(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ const char *zKey;
+ int nKey;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ zKey = argv[2];
+ nKey = strlen(zKey);
+#ifdef SQLITE_HAS_CODEC
+ sqlite3_rekey(db, zKey, nKey);
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_close DB
+**
+** Closes the database opened by sqlite3_open.
+*/
+static int sqlite_test_close(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_close(db);
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+ return TCL_OK;
+}
+
+/*
+** Implementation of the x_coalesce() function.
+** Return the first argument non-NULL argument.
+*/
+static void t1_ifnullFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ for(i=0; i<argc; i++){
+ if( SQLITE_NULL!=sqlite3_value_type(argv[i]) ){
+ int n = sqlite3_value_bytes(argv[i]);
+ sqlite3_result_text(context, (char*)sqlite3_value_text(argv[i]),
+ n, SQLITE_TRANSIENT);
+ break;
+ }
+ }
+}
+
+/*
+** These are test functions. hex8() interprets its argument as
+** UTF8 and returns a hex encoding. hex16le() interprets its argument
+** as UTF16le and returns a hex encoding.
+*/
+static void hex8Func(sqlite3_context *p, int argc, sqlite3_value **argv){
+ const unsigned char *z;
+ int i;
+ char zBuf[200];
+ z = sqlite3_value_text(argv[0]);
+ for(i=0; i<sizeof(zBuf)/2 - 2 && z[i]; i++){
+ sprintf(&zBuf[i*2], "%02x", z[i]&0xff);
+ }
+ zBuf[i*2] = 0;
+ sqlite3_result_text(p, (char*)zBuf, -1, SQLITE_TRANSIENT);
+}
+#ifndef SQLITE_OMIT_UTF16
+static void hex16Func(sqlite3_context *p, int argc, sqlite3_value **argv){
+ const unsigned short int *z;
+ int i;
+ char zBuf[400];
+ z = sqlite3_value_text16(argv[0]);
+ for(i=0; i<sizeof(zBuf)/4 - 4 && z[i]; i++){
+ sprintf(&zBuf[i*4], "%04x", z[i]&0xff);
+ }
+ zBuf[i*4] = 0;
+ sqlite3_result_text(p, (char*)zBuf, -1, SQLITE_TRANSIENT);
+}
+#endif
+
+/*
+** A structure into which to accumulate text.
+*/
+struct dstr {
+ int nAlloc; /* Space allocated */
+ int nUsed; /* Space used */
+ char *z; /* The space */
+};
+
+/*
+** Append text to a dstr
+*/
+static void dstrAppend(struct dstr *p, const char *z, int divider){
+ int n = strlen(z);
+ if( p->nUsed + n + 2 > p->nAlloc ){
+ char *zNew;
+ p->nAlloc = p->nAlloc*2 + n + 200;
+ zNew = sqlite3_realloc(p->z, p->nAlloc);
+ if( zNew==0 ){
+ sqlite3_free(p->z);
+ memset(p, 0, sizeof(*p));
+ return;
+ }
+ p->z = zNew;
+ }
+ if( divider && p->nUsed>0 ){
+ p->z[p->nUsed++] = divider;
+ }
+ memcpy(&p->z[p->nUsed], z, n+1);
+ p->nUsed += n;
+}
+
+/*
+** Invoked for each callback from sqlite3ExecFunc
+*/
+static int execFuncCallback(void *pData, int argc, char **argv, char **NotUsed){
+ struct dstr *p = (struct dstr*)pData;
+ int i;
+ for(i=0; i<argc; i++){
+ if( argv[i]==0 ){
+ dstrAppend(p, "NULL", ' ');
+ }else{
+ dstrAppend(p, argv[i], ' ');
+ }
+ }
+ return 0;
+}
+
+/*
+** Implementation of the x_sqlite_exec() function. This function takes
+** a single argument and attempts to execute that argument as SQL code.
+** This is illegal and should set the SQLITE_MISUSE flag on the database.
+**
+** 2004-Jan-07: We have changed this to make it legal to call sqlite3_exec()
+** from within a function call.
+**
+** This routine simulates the effect of having two threads attempt to
+** use the same database at the same time.
+*/
+static void sqlite3ExecFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ struct dstr x;
+ memset(&x, 0, sizeof(x));
+ (void)sqlite3_exec((sqlite3*)sqlite3_user_data(context),
+ (char*)sqlite3_value_text(argv[0]),
+ execFuncCallback, &x, 0);
+ sqlite3_result_text(context, x.z, x.nUsed, SQLITE_TRANSIENT);
+ sqlite3_free(x.z);
+}
+
+/*
+** Implementation of tkt2213func(), a scalar function that takes exactly
+** one argument. It has two interesting features:
+**
+** * It calls sqlite3_value_text() 3 times on the argument sqlite3_value*.
+** If the three pointers returned are not the same an SQL error is raised.
+**
+** * Otherwise it returns a copy of the text representation of its
+** argument in such a way as the VDBE representation is a Mem* cell
+** with the MEM_Term flag clear.
+**
+** Ticket #2213 can therefore be tested by evaluating the following
+** SQL expression:
+**
+** tkt2213func(tkt2213func('a string'));
+*/
+static void tkt2213Function(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int nText;
+ unsigned char const *zText1;
+ unsigned char const *zText2;
+ unsigned char const *zText3;
+
+ nText = sqlite3_value_bytes(argv[0]);
+ zText1 = sqlite3_value_text(argv[0]);
+ zText2 = sqlite3_value_text(argv[0]);
+ zText3 = sqlite3_value_text(argv[0]);
+
+ if( zText1!=zText2 || zText2!=zText3 ){
+ sqlite3_result_error(context, "tkt2213 is not fixed", -1);
+ }else{
+ char *zCopy = (char *)sqlite3_malloc(nText);
+ memcpy(zCopy, zText1, nText);
+ sqlite3_result_text(context, zCopy, nText, sqlite3_free);
+ }
+}
+
+/*
+** The following SQL function takes 4 arguments. The 2nd and
+** 4th argument must be one of these strings: 'text', 'text16',
+** or 'blob' corresponding to API functions
+**
+** sqlite3_value_text()
+** sqlite3_value_text16()
+** sqlite3_value_blob()
+**
+** The third argument is a string, either 'bytes' or 'bytes16' or 'noop',
+** corresponding to APIs:
+**
+** sqlite3_value_bytes()
+** sqlite3_value_bytes16()
+** noop
+**
+** The APIs designated by the 2nd through 4th arguments are applied
+** to the first argument in order. If the pointers returned by the
+** second and fourth are different, this routine returns 1. Otherwise,
+** this routine returns 0.
+**
+** This function is used to test to see when returned pointers from
+** the _text(), _text16() and _blob() APIs become invalidated.
+*/
+static void ptrChngFunction(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const void *p1, *p2;
+ const char *zCmd;
+ if( argc!=4 ) return;
+ zCmd = (const char*)sqlite3_value_text(argv[1]);
+ if( zCmd==0 ) return;
+ if( strcmp(zCmd,"text")==0 ){
+ p1 = (const void*)sqlite3_value_text(argv[0]);
+#ifndef SQLITE_OMIT_UTF16
+ }else if( strcmp(zCmd, "text16")==0 ){
+ p1 = (const void*)sqlite3_value_text16(argv[0]);
+#endif
+ }else if( strcmp(zCmd, "blob")==0 ){
+ p1 = (const void*)sqlite3_value_blob(argv[0]);
+ }else{
+ return;
+ }
+ zCmd = (const char*)sqlite3_value_text(argv[2]);
+ if( zCmd==0 ) return;
+ if( strcmp(zCmd,"bytes")==0 ){
+ sqlite3_value_bytes(argv[0]);
+#ifndef SQLITE_OMIT_UTF16
+ }else if( strcmp(zCmd, "bytes16")==0 ){
+ sqlite3_value_bytes16(argv[0]);
+#endif
+ }else if( strcmp(zCmd, "noop")==0 ){
+ /* do nothing */
+ }else{
+ return;
+ }
+ zCmd = (const char*)sqlite3_value_text(argv[3]);
+ if( zCmd==0 ) return;
+ if( strcmp(zCmd,"text")==0 ){
+ p2 = (const void*)sqlite3_value_text(argv[0]);
+#ifndef SQLITE_OMIT_UTF16
+ }else if( strcmp(zCmd, "text16")==0 ){
+ p2 = (const void*)sqlite3_value_text16(argv[0]);
+#endif
+ }else if( strcmp(zCmd, "blob")==0 ){
+ p2 = (const void*)sqlite3_value_blob(argv[0]);
+ }else{
+ return;
+ }
+ sqlite3_result_int(context, p1!=p2);
+}
+
+
+/*
+** Usage: sqlite_test_create_function DB
+**
+** Call the sqlite3_create_function API on the given database in order
+** to create a function named "x_coalesce". This function does the same thing
+** as the "coalesce" function. This function also registers an SQL function
+** named "x_sqlite_exec" that invokes sqlite3_exec(). Invoking sqlite3_exec()
+** in this way is illegal recursion and should raise an SQLITE_MISUSE error.
+** The effect is similar to trying to use the same database connection from
+** two threads at the same time.
+**
+** The original motivation for this routine was to be able to call the
+** sqlite3_create_function function while a query is in progress in order
+** to test the SQLITE_MISUSE detection logic.
+*/
+static int test_create_function(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int rc;
+ sqlite3 *db;
+ extern void Md5_Register(sqlite3*);
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_create_function(db, "x_coalesce", -1, SQLITE_ANY, 0,
+ t1_ifnullFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "hex8", 1, SQLITE_ANY, 0,
+ hex8Func, 0, 0);
+ }
+#ifndef SQLITE_OMIT_UTF16
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "hex16", 1, SQLITE_ANY, 0,
+ hex16Func, 0, 0);
+ }
+#endif
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "tkt2213func", 1, SQLITE_ANY, 0,
+ tkt2213Function, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "pointer_change", 4, SQLITE_ANY, 0,
+ ptrChngFunction, 0, 0);
+ }
+
+#ifndef SQLITE_OMIT_UTF16
+ /* Use the sqlite3_create_function16() API here. Mainly for fun, but also
+ ** because it is not tested anywhere else. */
+ if( rc==SQLITE_OK ){
+ const void *zUtf16;
+ sqlite3_value *pVal;
+ sqlite3_mutex_enter(db->mutex);
+ pVal = sqlite3ValueNew(db);
+ sqlite3ValueSetStr(pVal, -1, "x_sqlite_exec", SQLITE_UTF8, SQLITE_STATIC);
+ zUtf16 = sqlite3ValueText(pVal, SQLITE_UTF16NATIVE);
+ if( db->mallocFailed ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_create_function16(db, zUtf16,
+ 1, SQLITE_UTF16, db, sqlite3ExecFunc, 0, 0);
+ }
+ sqlite3ValueFree(pVal);
+ sqlite3_mutex_leave(db->mutex);
+ }
+#endif
+
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
+ return TCL_OK;
+}
+
+/*
+** Routines to implement the x_count() aggregate function.
+**
+** x_count() counts the number of non-null arguments. But there are
+** some twists for testing purposes.
+**
+** If the argument to x_count() is 40 then a UTF-8 error is reported
+** on the step function. If x_count(41) is seen, then a UTF-16 error
+** is reported on the step function. If the total count is 42, then
+** a UTF-8 error is reported on the finalize function.
+*/
+typedef struct t1CountCtx t1CountCtx;
+struct t1CountCtx {
+ int n;
+};
+static void t1CountStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ t1CountCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0]) ) && p ){
+ p->n++;
+ }
+ if( argc>0 ){
+ int v = sqlite3_value_int(argv[0]);
+ if( v==40 ){
+ sqlite3_result_error(context, "value of 40 handed to x_count", -1);
+#ifndef SQLITE_OMIT_UTF16
+ }else if( v==41 ){
+ const char zUtf16ErrMsg[] = { 0, 0x61, 0, 0x62, 0, 0x63, 0, 0, 0};
+ sqlite3_result_error16(context, &zUtf16ErrMsg[1-SQLITE_BIGENDIAN], -1);
+#endif
+ }
+ }
+}
+static void t1CountFinalize(sqlite3_context *context){
+ t1CountCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p ){
+ if( p->n==42 ){
+ sqlite3_result_error(context, "x_count totals to 42", -1);
+ }else{
+ sqlite3_result_int(context, p ? p->n : 0);
+ }
+ }
+}
+
+static void legacyCountStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ /* no-op */
+}
+static void legacyCountFinalize(sqlite3_context *context){
+ sqlite3_result_int(context, sqlite3_aggregate_count(context));
+}
+
+/*
+** Usage: sqlite3_create_aggregate DB
+**
+** Call the sqlite3_create_function API on the given database in order
+** to create a function named "x_count". This function is similar
+** to the built-in count() function, with a few special quirks
+** for testing the sqlite3_result_error() APIs.
+**
+** The original motivation for this routine was to be able to call the
+** sqlite3_create_aggregate function while a query is in progress in order
+** to test the SQLITE_MISUSE detection logic. See misuse.test.
+**
+** This routine was later extended to test the use of sqlite3_result_error()
+** within aggregate functions.
+**
+** Later: It is now also extended to register the aggregate function
+** "legacy_count()" with the supplied database handle. This is used
+** to test the deprecated sqlite3_aggregate_count() API.
+*/
+static int test_create_aggregate(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_create_function(db, "x_count", 0, SQLITE_UTF8, 0, 0,
+ t1CountStep,t1CountFinalize);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "x_count", 1, SQLITE_UTF8, 0, 0,
+ t1CountStep,t1CountFinalize);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "legacy_count", 0, SQLITE_ANY, 0, 0,
+ legacyCountStep, legacyCountFinalize
+ );
+ }
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
+ return TCL_OK;
+}
+
+
+/*
+** Usage: printf TEXT
+**
+** Send output to printf. Use this rather than puts to merge the output
+** in the correct sequence with debugging printfs inserted into C code.
+** Puts uses a separate buffer and debugging statements will be out of
+** sequence if it is used.
+*/
+static int test_printf(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " TEXT\"", 0);
+ return TCL_ERROR;
+ }
+ printf("%s\n", argv[1]);
+ return TCL_OK;
+}
+
+
+
+/*
+** Usage: sqlite3_mprintf_int FORMAT INTEGER INTEGER INTEGER
+**
+** Call mprintf with three integer arguments
+*/
+static int sqlite3_mprintf_int(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int a[3], i;
+ char *z;
+ if( argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT INT INT INT\"", 0);
+ return TCL_ERROR;
+ }
+ for(i=2; i<5; i++){
+ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR;
+ }
+ z = sqlite3_mprintf(argv[1], a[0], a[1], a[2]);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** If zNum represents an integer that will fit in 64-bits, then set
+** *pValue to that integer and return true. Otherwise return false.
+*/
+static int sqlite3GetInt64(const char *zNum, i64 *pValue){
+ if( sqlite3FitsIn64Bits(zNum, 0) ){
+ sqlite3Atoi64(zNum, pValue);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Usage: sqlite3_mprintf_int64 FORMAT INTEGER INTEGER INTEGER
+**
+** Call mprintf with three 64-bit integer arguments
+*/
+static int sqlite3_mprintf_int64(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int i;
+ sqlite_int64 a[3];
+ char *z;
+ if( argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT INT INT INT\"", 0);
+ return TCL_ERROR;
+ }
+ for(i=2; i<5; i++){
+ if( !sqlite3GetInt64(argv[i], &a[i-2]) ){
+ Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0);
+ return TCL_ERROR;
+ }
+ }
+ z = sqlite3_mprintf(argv[1], a[0], a[1], a[2]);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_str FORMAT INTEGER INTEGER STRING
+**
+** Call mprintf with two integer arguments and one string argument
+*/
+static int sqlite3_mprintf_str(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int a[3], i;
+ char *z;
+ if( argc<4 || argc>5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT INT INT ?STRING?\"", 0);
+ return TCL_ERROR;
+ }
+ for(i=2; i<4; i++){
+ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR;
+ }
+ z = sqlite3_mprintf(argv[1], a[0], a[1], argc>4 ? argv[4] : NULL);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_snprintf_str INTEGER FORMAT INTEGER INTEGER STRING
+**
+** Call mprintf with two integer arguments and one string argument
+*/
+static int sqlite3_snprintf_str(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int a[3], i;
+ int n;
+ char *z;
+ if( argc<5 || argc>6 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " INT FORMAT INT INT ?STRING?\"", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
+ if( n<0 ){
+ Tcl_AppendResult(interp, "N must be non-negative", 0);
+ return TCL_ERROR;
+ }
+ for(i=3; i<5; i++){
+ if( Tcl_GetInt(interp, argv[i], &a[i-3]) ) return TCL_ERROR;
+ }
+ z = sqlite3_malloc( n+1 );
+ sqlite3_snprintf(n, z, argv[2], a[0], a[1], argc>4 ? argv[5] : NULL);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_double FORMAT INTEGER INTEGER DOUBLE
+**
+** Call mprintf with two integer arguments and one double argument
+*/
+static int sqlite3_mprintf_double(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int a[3], i;
+ double r;
+ char *z;
+ if( argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT INT INT DOUBLE\"", 0);
+ return TCL_ERROR;
+ }
+ for(i=2; i<4; i++){
+ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR;
+ }
+ if( Tcl_GetDouble(interp, argv[4], &r) ) return TCL_ERROR;
+ z = sqlite3_mprintf(argv[1], a[0], a[1], r);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_scaled FORMAT DOUBLE DOUBLE
+**
+** Call mprintf with a single double argument which is the product of the
+** two arguments given above. This is used to generate overflow and underflow
+** doubles to test that they are converted properly.
+*/
+static int sqlite3_mprintf_scaled(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int i;
+ double r[2];
+ char *z;
+ if( argc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT DOUBLE DOUBLE\"", 0);
+ return TCL_ERROR;
+ }
+ for(i=2; i<4; i++){
+ if( Tcl_GetDouble(interp, argv[i], &r[i-2]) ) return TCL_ERROR;
+ }
+ z = sqlite3_mprintf(argv[1], r[0]*r[1]);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_stronly FORMAT STRING
+**
+** Call mprintf with a single double argument which is the product of the
+** two arguments given above. This is used to generate overflow and underflow
+** doubles to test that they are converted properly.
+*/
+static int sqlite3_mprintf_stronly(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ char *z;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT STRING\"", 0);
+ return TCL_ERROR;
+ }
+ z = sqlite3_mprintf(argv[1], argv[2]);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_mprintf_hexdouble FORMAT HEX
+**
+** Call mprintf with a single double argument which is derived from the
+** hexadecimal encoding of an IEEE double.
+*/
+static int sqlite3_mprintf_hexdouble(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ char *z;
+ double r;
+ unsigned int x1, x2;
+ sqlite_uint64 d;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FORMAT STRING\"", 0);
+ return TCL_ERROR;
+ }
+ if( sscanf(argv[2], "%08x%08x", &x2, &x1)!=2 ){
+ Tcl_AppendResult(interp, "2nd argument should be 16-characters of hex", 0);
+ return TCL_ERROR;
+ }
+ d = x2;
+ d = (d<<32) + x1;
+ memcpy(&r, &d, sizeof(r));
+ z = sqlite3_mprintf(argv[1], r);
+ Tcl_AppendResult(interp, z, 0);
+ sqlite3_free(z);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_enable_shared_cache BOOLEAN
+**
+*/
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
+static int test_enable_shared(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int rc;
+ int enable;
+ int ret = 0;
+ extern int sqlite3SharedCacheEnabled;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ){
+ return TCL_ERROR;
+ }
+ ret = sqlite3SharedCacheEnabled;
+ rc = sqlite3_enable_shared_cache(enable);
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3ErrStr(rc), TCL_STATIC);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ret));
+ return TCL_OK;
+}
+#endif
+
+
+
+/*
+** Usage: sqlite3_extended_result_codes DB BOOLEAN
+**
+*/
+static int test_extended_result_codes(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int enable;
+ sqlite3 *db;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ if( Tcl_GetBooleanFromObj(interp, objv[2], &enable) ) return TCL_ERROR;
+ sqlite3_extended_result_codes(db, enable);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_libversion_number
+**
+*/
+static int test_libversion_number(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_libversion_number()));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_table_column_metadata DB dbname tblname colname
+**
+*/
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+static int test_table_column_metadata(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ const char *zDb;
+ const char *zTbl;
+ const char *zCol;
+ int rc;
+ Tcl_Obj *pRet;
+
+ const char *zDatatype;
+ const char *zCollseq;
+ int notnull;
+ int primarykey;
+ int autoincrement;
+
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB dbname tblname colname");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zDb = Tcl_GetString(objv[2]);
+ zTbl = Tcl_GetString(objv[3]);
+ zCol = Tcl_GetString(objv[4]);
+
+ if( strlen(zDb)==0 ) zDb = 0;
+
+ rc = sqlite3_table_column_metadata(db, zDb, zTbl, zCol,
+ &zDatatype, &zCollseq, &notnull, &primarykey, &autoincrement);
+
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, sqlite3_errmsg(db), 0);
+ return TCL_ERROR;
+ }
+
+ pRet = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zDatatype, -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zCollseq, -1));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(notnull));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(primarykey));
+ Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(autoincrement));
+ Tcl_SetObjResult(interp, pRet);
+
+ return TCL_OK;
+}
+#endif
+
+#ifndef SQLITE_OMIT_INCRBLOB
+
+/*
+** sqlite3_blob_read CHANNEL OFFSET N
+**
+** This command is used to test the sqlite3_blob_read() in ways that
+** the Tcl channel interface does not. The first argument should
+** be the name of a valid channel created by the [incrblob] method
+** of a database handle. This function calls sqlite3_blob_read()
+** to read N bytes from offset OFFSET from the underlying SQLite
+** blob handle.
+**
+** On success, a byte-array object containing the read data is
+** returned. On failure, the interpreter result is set to the
+** text representation of the returned error code (i.e. "SQLITE_NOMEM")
+** and a Tcl exception is thrown.
+*/
+static int test_blob_read(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_Channel channel;
+ ClientData instanceData;
+ sqlite3_blob *pBlob;
+ int notUsed;
+ int nByte;
+ int iOffset;
+ unsigned char *zBuf;
+ int rc;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
+ return TCL_ERROR;
+ }
+
+ channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
+ if( !channel
+ || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+ || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
+ || nByte<0 || iOffset<0
+ ){
+ return TCL_ERROR;
+ }
+
+ instanceData = Tcl_GetChannelInstanceData(channel);
+ pBlob = *((sqlite3_blob **)instanceData);
+
+ zBuf = (unsigned char *)Tcl_Alloc(nByte);
+ rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
+ if( rc==SQLITE_OK ){
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
+ }else{
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ }
+ Tcl_Free((char *)zBuf);
+
+ return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+
+/*
+** sqlite3_blob_write CHANNEL OFFSET DATA
+**
+** This command is used to test the sqlite3_blob_write() in ways that
+** the Tcl channel interface does not. The first argument should
+** be the name of a valid channel created by the [incrblob] method
+** of a database handle. This function calls sqlite3_blob_write()
+** to write the DATA byte-array to the underlying SQLite blob handle.
+** at offset OFFSET.
+**
+** On success, an empty string is returned. On failure, the interpreter
+** result is set to the text representation of the returned error code
+** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
+*/
+static int test_blob_write(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_Channel channel;
+ ClientData instanceData;
+ sqlite3_blob *pBlob;
+ int notUsed;
+ int iOffset;
+ int rc;
+
+ unsigned char *zBuf;
+ int nBuf;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA");
+ return TCL_ERROR;
+ }
+
+ channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
+ if( !channel
+ || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+ || iOffset<0
+ ){
+ return TCL_ERROR;
+ }
+
+ instanceData = Tcl_GetChannelInstanceData(channel);
+ pBlob = *((sqlite3_blob **)instanceData);
+
+ zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
+ rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ }
+
+ return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
+}
+#endif
+
+/*
+** Usage: sqlite3_create_collation_v2 DB-HANDLE NAME CMP-PROC DEL-PROC
+**
+** This Tcl proc is used for testing the experimental
+** sqlite3_create_collation_v2() interface.
+*/
+struct TestCollationX {
+ Tcl_Interp *interp;
+ Tcl_Obj *pCmp;
+ Tcl_Obj *pDel;
+};
+typedef struct TestCollationX TestCollationX;
+static void testCreateCollationDel(void *pCtx){
+ TestCollationX *p = (TestCollationX *)pCtx;
+
+ int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ){
+ Tcl_BackgroundError(p->interp);
+ }
+
+ Tcl_DecrRefCount(p->pCmp);
+ Tcl_DecrRefCount(p->pDel);
+ sqlite3_free((void *)p);
+}
+static int testCreateCollationCmp(
+ void *pCtx,
+ int nLeft,
+ const void *zLeft,
+ int nRight,
+ const void *zRight
+){
+ TestCollationX *p = (TestCollationX *)pCtx;
+ Tcl_Obj *pScript = Tcl_DuplicateObj(p->pCmp);
+ int iRes = 0;
+
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zLeft, nLeft));
+ Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zRight,nRight));
+
+ if( TCL_OK!=Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL)
+ || TCL_OK!=Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iRes)
+ ){
+ Tcl_BackgroundError(p->interp);
+ }
+ Tcl_DecrRefCount(pScript);
+
+ return iRes;
+}
+static int test_create_collation_v2(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ TestCollationX *p;
+ sqlite3 *db;
+ int rc;
+
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE NAME CMP-PROC DEL-PROC");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ p = (TestCollationX *)sqlite3_malloc(sizeof(TestCollationX));
+ p->pCmp = objv[3];
+ p->pDel = objv[4];
+ p->interp = interp;
+ Tcl_IncrRefCount(p->pCmp);
+ Tcl_IncrRefCount(p->pDel);
+
+ rc = sqlite3_create_collation_v2(db, Tcl_GetString(objv[2]), 16,
+ (void *)p, testCreateCollationCmp, testCreateCollationDel
+ );
+ if( rc!=SQLITE_MISUSE ){
+ Tcl_AppendResult(interp, "sqlite3_create_collate_v2() failed to detect "
+ "an invalid encoding", (char*)0);
+ return TCL_ERROR;
+ }
+ rc = sqlite3_create_collation_v2(db, Tcl_GetString(objv[2]), SQLITE_UTF8,
+ (void *)p, testCreateCollationCmp, testCreateCollationDel
+ );
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
+*/
+static int test_load_extension(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_CmdInfo cmdInfo;
+ sqlite3 *db;
+ int rc;
+ char *zDb;
+ char *zFile;
+ char *zProc = 0;
+ char *zErr = 0;
+
+ if( objc!=4 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE FILE ?PROC?");
+ return TCL_ERROR;
+ }
+ zDb = Tcl_GetString(objv[1]);
+ zFile = Tcl_GetString(objv[2]);
+ if( objc==4 ){
+ zProc = Tcl_GetString(objv[3]);
+ }
+
+ /* Extract the C database handle from the Tcl command name */
+ if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
+ Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0);
+ return TCL_ERROR;
+ }
+ db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
+ assert(db);
+
+ /* Call the underlying C function. If an error occurs, set rc to
+ ** TCL_ERROR and load any error string into the interpreter. If no
+ ** error occurs, set rc to TCL_OK.
+ */
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ rc = SQLITE_ERROR;
+ zErr = sqlite3_mprintf("this build omits sqlite3_load_extension()");
+#else
+ rc = sqlite3_load_extension(db, zFile, zProc, &zErr);
+#endif
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, zErr ? zErr : "", TCL_VOLATILE);
+ rc = TCL_ERROR;
+ }else{
+ rc = TCL_OK;
+ }
+ sqlite3_free(zErr);
+
+ return rc;
+}
+
+/*
+** Usage: sqlite3_enable_load_extension DB-HANDLE ONOFF
+*/
+static int test_enable_load(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_CmdInfo cmdInfo;
+ sqlite3 *db;
+ char *zDb;
+ int onoff;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE ONOFF");
+ return TCL_ERROR;
+ }
+ zDb = Tcl_GetString(objv[1]);
+
+ /* Extract the C database handle from the Tcl command name */
+ if( !Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
+ Tcl_AppendResult(interp, "command not found: ", zDb, (char*)0);
+ return TCL_ERROR;
+ }
+ db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
+ assert(db);
+
+ /* Get the onoff parameter */
+ if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){
+ return TCL_ERROR;
+ }
+
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ Tcl_AppendResult(interp, "this build omits sqlite3_load_extension()");
+ return TCL_ERROR;
+#else
+ sqlite3_enable_load_extension(db, onoff);
+ return TCL_OK;
+#endif
+}
+
+/*
+** Usage: sqlite_abort
+**
+** Shutdown the process immediately. This is not a clean shutdown.
+** This command is used to test the recoverability of a database in
+** the event of a program crash.
+*/
+static int sqlite_abort(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ assert( interp==0 ); /* This will always fail */
+ return TCL_OK;
+}
+
+/*
+** The following routine is a user-defined SQL function whose purpose
+** is to test the sqlite_set_result() API.
+*/
+static void testFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ while( argc>=2 ){
+ const char *zArg0 = (char*)sqlite3_value_text(argv[0]);
+ if( zArg0 ){
+ if( 0==sqlite3StrICmp(zArg0, "int") ){
+ sqlite3_result_int(context, sqlite3_value_int(argv[1]));
+ }else if( sqlite3StrICmp(zArg0,"int64")==0 ){
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[1]));
+ }else if( sqlite3StrICmp(zArg0,"string")==0 ){
+ sqlite3_result_text(context, (char*)sqlite3_value_text(argv[1]), -1,
+ SQLITE_TRANSIENT);
+ }else if( sqlite3StrICmp(zArg0,"double")==0 ){
+ sqlite3_result_double(context, sqlite3_value_double(argv[1]));
+ }else if( sqlite3StrICmp(zArg0,"null")==0 ){
+ sqlite3_result_null(context);
+ }else if( sqlite3StrICmp(zArg0,"value")==0 ){
+ sqlite3_result_value(context, argv[sqlite3_value_int(argv[1])]);
+ }else{
+ goto error_out;
+ }
+ }else{
+ goto error_out;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+ return;
+
+error_out:
+ sqlite3_result_error(context,"first argument should be one of: "
+ "int int64 string double null value", -1);
+}
+
+/*
+** Usage: sqlite_register_test_function DB NAME
+**
+** Register the test SQL function on the database DB under the name NAME.
+*/
+static int test_register_func(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3 *db;
+ int rc;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB FUNCTION-NAME", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_create_function(db, argv[2], -1, SQLITE_UTF8, 0,
+ testFunc, 0, 0);
+ if( rc!=0 ){
+ Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
+ return TCL_ERROR;
+ }
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_finalize STMT
+**
+** Finalize a statement handle.
+*/
+static int test_finalize(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+ sqlite3 *db = 0;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+
+ if( pStmt ){
+ db = StmtToDb(pStmt);
+ }
+ rc = sqlite3_finalize(pStmt);
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+ if( db && sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_next_stmt DB STMT
+**
+** Return the next statment in sequence after STMT.
+*/
+static int test_next_stmt(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ sqlite3 *db = 0;
+ char zBuf[50];
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB STMT", 0);
+ return TCL_ERROR;
+ }
+
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ if( getStmtPointer(interp, Tcl_GetString(objv[2]), &pStmt) ) return TCL_ERROR;
+ pStmt = sqlite3_next_stmt(db, pStmt);
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+ }
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_reset STMT
+**
+** Reset a statement handle.
+*/
+static int test_reset(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+
+ rc = sqlite3_reset(pStmt);
+ if( pStmt && sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ){
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+/*
+ if( rc ){
+ return TCL_ERROR;
+ }
+*/
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_expired STMT
+**
+** Return TRUE if a recompilation of the statement is recommended.
+*/
+static int test_expired(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0);
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(sqlite3_expired(pStmt)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_transfer_bindings FROMSTMT TOSTMT
+**
+** Transfer all bindings from FROMSTMT over to TOSTMT
+*/
+static int test_transfer_bind(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt1, *pStmt2;
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " FROM-STMT TO-STMT", 0);
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt1)) return TCL_ERROR;
+ if( getStmtPointer(interp, Tcl_GetString(objv[2]), &pStmt2)) return TCL_ERROR;
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(sqlite3_transfer_bindings(pStmt1,pStmt2)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_changes DB
+**
+** Return the number of changes made to the database by the last SQL
+** execution.
+*/
+static int test_changes(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_changes(db)));
+ return TCL_OK;
+}
+
+/*
+** This is the "static_bind_value" that variables are bound to when
+** the FLAG option of sqlite3_bind is "static"
+*/
+static char *sqlite_static_bind_value = 0;
+static int sqlite_static_bind_nbyte = 0;
+
+/*
+** Usage: sqlite3_bind VM IDX VALUE FLAGS
+**
+** Sets the value of the IDX-th occurance of "?" in the original SQL
+** string. VALUE is the new value. If FLAGS=="null" then VALUE is
+** ignored and the value is set to NULL. If FLAGS=="static" then
+** the value is set to the value of a static variable named
+** "sqlite_static_bind_value". If FLAGS=="normal" then a copy
+** of the VALUE is made. If FLAGS=="blob10" then a VALUE is ignored
+** an a 10-byte blob "abc\000xyz\000pq" is inserted.
+*/
+static int test_bind(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+ int idx;
+ if( argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " VM IDX VALUE (null|static|normal)\"", 0);
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, argv[1], &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[2], &idx) ) return TCL_ERROR;
+ if( strcmp(argv[4],"null")==0 ){
+ rc = sqlite3_bind_null(pStmt, idx);
+ }else if( strcmp(argv[4],"static")==0 ){
+ rc = sqlite3_bind_text(pStmt, idx, sqlite_static_bind_value, -1, 0);
+ }else if( strcmp(argv[4],"static-nbytes")==0 ){
+ rc = sqlite3_bind_text(pStmt, idx, sqlite_static_bind_value,
+ sqlite_static_bind_nbyte, 0);
+ }else if( strcmp(argv[4],"normal")==0 ){
+ rc = sqlite3_bind_text(pStmt, idx, argv[3], -1, SQLITE_TRANSIENT);
+ }else if( strcmp(argv[4],"blob10")==0 ){
+ rc = sqlite3_bind_text(pStmt, idx, "abc\000xyz\000pq", 10, SQLITE_STATIC);
+ }else{
+ Tcl_AppendResult(interp, "4th argument should be "
+ "\"null\" or \"static\" or \"normal\"", 0);
+ return TCL_ERROR;
+ }
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc ){
+ char zBuf[50];
+ sprintf(zBuf, "(%d) ", rc);
+ Tcl_AppendResult(interp, zBuf, sqlite3ErrStr(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#ifndef SQLITE_OMIT_UTF16
+/*
+** Usage: add_test_collate <db ptr> <utf8> <utf16le> <utf16be>
+**
+** This function is used to test that SQLite selects the correct collation
+** sequence callback when multiple versions (for different text encodings)
+** are available.
+**
+** Calling this routine registers the collation sequence "test_collate"
+** with database handle <db>. The second argument must be a list of three
+** boolean values. If the first is true, then a version of test_collate is
+** registered for UTF-8, if the second is true, a version is registered for
+** UTF-16le, if the third is true, a UTF-16be version is available.
+** Previous versions of test_collate are deleted.
+**
+** The collation sequence test_collate is implemented by calling the
+** following TCL script:
+**
+** "test_collate <enc> <lhs> <rhs>"
+**
+** The <lhs> and <rhs> are the two values being compared, encoded in UTF-8.
+** The <enc> parameter is the encoding of the collation function that
+** SQLite selected to call. The TCL test script implements the
+** "test_collate" proc.
+**
+** Note that this will only work with one intepreter at a time, as the
+** interp pointer to use when evaluating the TCL script is stored in
+** pTestCollateInterp.
+*/
+static Tcl_Interp* pTestCollateInterp;
+static int test_collate_func(
+ void *pCtx,
+ int nA, const void *zA,
+ int nB, const void *zB
+){
+ Tcl_Interp *i = pTestCollateInterp;
+ int encin = (int)pCtx;
+ int res;
+ int n;
+
+ sqlite3_value *pVal;
+ Tcl_Obj *pX;
+
+ pX = Tcl_NewStringObj("test_collate", -1);
+ Tcl_IncrRefCount(pX);
+
+ switch( encin ){
+ case SQLITE_UTF8:
+ Tcl_ListObjAppendElement(i,pX,Tcl_NewStringObj("UTF-8",-1));
+ break;
+ case SQLITE_UTF16LE:
+ Tcl_ListObjAppendElement(i,pX,Tcl_NewStringObj("UTF-16LE",-1));
+ break;
+ case SQLITE_UTF16BE:
+ Tcl_ListObjAppendElement(i,pX,Tcl_NewStringObj("UTF-16BE",-1));
+ break;
+ default:
+ assert(0);
+ }
+
+ pVal = sqlite3ValueNew(0);
+ sqlite3ValueSetStr(pVal, nA, zA, encin, SQLITE_STATIC);
+ n = sqlite3_value_bytes(pVal);
+ Tcl_ListObjAppendElement(i,pX,
+ Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n));
+ sqlite3ValueSetStr(pVal, nB, zB, encin, SQLITE_STATIC);
+ n = sqlite3_value_bytes(pVal);
+ Tcl_ListObjAppendElement(i,pX,
+ Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n));
+ sqlite3ValueFree(pVal);
+
+ Tcl_EvalObjEx(i, pX, 0);
+ Tcl_DecrRefCount(pX);
+ Tcl_GetIntFromObj(i, Tcl_GetObjResult(i), &res);
+ return res;
+}
+static int test_collate(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int val;
+ sqlite3_value *pVal;
+ int rc;
+
+ if( objc!=5 ) goto bad_args;
+ pTestCollateInterp = interp;
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR;
+ rc = sqlite3_create_collation(db, "test_collate", SQLITE_UTF8,
+ (void *)SQLITE_UTF8, val?test_collate_func:0);
+ if( rc==SQLITE_OK ){
+ const void *zUtf16;
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &val) ) return TCL_ERROR;
+ rc = sqlite3_create_collation(db, "test_collate", SQLITE_UTF16LE,
+ (void *)SQLITE_UTF16LE, val?test_collate_func:0);
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[4], &val) ) return TCL_ERROR;
+
+#if 0
+ if( sqlite3_iMallocFail>0 ){
+ sqlite3_iMallocFail++;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ pVal = sqlite3ValueNew(db);
+ sqlite3ValueSetStr(pVal, -1, "test_collate", SQLITE_UTF8, SQLITE_STATIC);
+ zUtf16 = sqlite3ValueText(pVal, SQLITE_UTF16NATIVE);
+ if( db->mallocFailed ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_create_collation16(db, zUtf16, SQLITE_UTF16BE,
+ (void *)SQLITE_UTF16BE, val?test_collate_func:0);
+ }
+ sqlite3ValueFree(pVal);
+ sqlite3_mutex_leave(db->mutex);
+ }
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+
+bad_args:
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <DB> <utf8> <utf16le> <utf16be>", 0);
+ return TCL_ERROR;
+}
+
+/*
+** When the collation needed callback is invoked, record the name of
+** the requested collating function here. The recorded name is linked
+** to a TCL variable and used to make sure that the requested collation
+** name is correct.
+*/
+static char zNeededCollation[200];
+static char *pzNeededCollation = zNeededCollation;
+
+
+/*
+** Called when a collating sequence is needed. Registered using
+** sqlite3_collation_needed16().
+*/
+static void test_collate_needed_cb(
+ void *pCtx,
+ sqlite3 *db,
+ int eTextRep,
+ const void *pName
+){
+ int enc = ENC(db);
+ int i;
+ char *z;
+ for(z = (char*)pName, i=0; *z || z[1]; z++){
+ if( *z ) zNeededCollation[i++] = *z;
+ }
+ zNeededCollation[i] = 0;
+ sqlite3_create_collation(
+ db, "test_collate", ENC(db), (void *)enc, test_collate_func);
+}
+
+/*
+** Usage: add_test_collate_needed DB
+*/
+static int test_collate_needed(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int rc;
+
+ if( objc!=2 ) goto bad_args;
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_collation_needed16(db, 0, test_collate_needed_cb);
+ zNeededCollation[0] = 0;
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ return TCL_OK;
+
+bad_args:
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+}
+
+/*
+** tclcmd: add_alignment_test_collations DB
+**
+** Add two new collating sequences to the database DB
+**
+** utf16_aligned
+** utf16_unaligned
+**
+** Both collating sequences use the same sort order as BINARY.
+** The only difference is that the utf16_aligned collating
+** sequence is declared with the SQLITE_UTF16_ALIGNED flag.
+** Both collating functions increment the unaligned utf16 counter
+** whenever they see a string that begins on an odd byte boundary.
+*/
+static int unaligned_string_counter = 0;
+static int alignmentCollFunc(
+ void *NotUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ int rc, n;
+ n = nKey1<nKey2 ? nKey1 : nKey2;
+ if( nKey1>0 && 1==(1&(int)pKey1) ) unaligned_string_counter++;
+ if( nKey2>0 && 1==(1&(int)pKey2) ) unaligned_string_counter++;
+ rc = memcmp(pKey1, pKey2, n);
+ if( rc==0 ){
+ rc = nKey1 - nKey2;
+ }
+ return rc;
+}
+static int add_alignment_test_collations(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ if( objc>=2 ){
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ sqlite3_create_collation(db, "utf16_unaligned",
+ SQLITE_UTF16,
+ 0, alignmentCollFunc);
+ sqlite3_create_collation(db, "utf16_aligned",
+ SQLITE_UTF16 | SQLITE_UTF16_ALIGNED,
+ 0, alignmentCollFunc);
+ }
+ return SQLITE_OK;
+}
+#endif /* !defined(SQLITE_OMIT_UTF16) */
+
+/*
+** Usage: add_test_function <db ptr> <utf8> <utf16le> <utf16be>
+**
+** This function is used to test that SQLite selects the correct user
+** function callback when multiple versions (for different text encodings)
+** are available.
+**
+** Calling this routine registers up to three versions of the user function
+** "test_function" with database handle <db>. If the second argument is
+** true, then a version of test_function is registered for UTF-8, if the
+** third is true, a version is registered for UTF-16le, if the fourth is
+** true, a UTF-16be version is available. Previous versions of
+** test_function are deleted.
+**
+** The user function is implemented by calling the following TCL script:
+**
+** "test_function <enc> <arg>"
+**
+** Where <enc> is one of UTF-8, UTF-16LE or UTF16BE, and <arg> is the
+** single argument passed to the SQL function. The value returned by
+** the TCL script is used as the return value of the SQL function. It
+** is passed to SQLite using UTF-16BE for a UTF-8 test_function(), UTF-8
+** for a UTF-16LE test_function(), and UTF-16LE for an implementation that
+** prefers UTF-16BE.
+*/
+#ifndef SQLITE_OMIT_UTF16
+static void test_function_utf8(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ Tcl_Interp *interp;
+ Tcl_Obj *pX;
+ sqlite3_value *pVal;
+ interp = (Tcl_Interp *)sqlite3_user_data(pCtx);
+ pX = Tcl_NewStringObj("test_function", -1);
+ Tcl_IncrRefCount(pX);
+ Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-8", -1));
+ Tcl_ListObjAppendElement(interp, pX,
+ Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1));
+ Tcl_EvalObjEx(interp, pX, 0);
+ Tcl_DecrRefCount(pX);
+ sqlite3_result_text(pCtx, Tcl_GetStringResult(interp), -1, SQLITE_TRANSIENT);
+ pVal = sqlite3ValueNew(0);
+ sqlite3ValueSetStr(pVal, -1, Tcl_GetStringResult(interp),
+ SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3_result_text16be(pCtx, sqlite3_value_text16be(pVal),
+ -1, SQLITE_TRANSIENT);
+ sqlite3ValueFree(pVal);
+}
+static void test_function_utf16le(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ Tcl_Interp *interp;
+ Tcl_Obj *pX;
+ sqlite3_value *pVal;
+ interp = (Tcl_Interp *)sqlite3_user_data(pCtx);
+ pX = Tcl_NewStringObj("test_function", -1);
+ Tcl_IncrRefCount(pX);
+ Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16LE", -1));
+ Tcl_ListObjAppendElement(interp, pX,
+ Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1));
+ Tcl_EvalObjEx(interp, pX, 0);
+ Tcl_DecrRefCount(pX);
+ pVal = sqlite3ValueNew(0);
+ sqlite3ValueSetStr(pVal, -1, Tcl_GetStringResult(interp),
+ SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3_result_text(pCtx,(char*)sqlite3_value_text(pVal),-1,SQLITE_TRANSIENT);
+ sqlite3ValueFree(pVal);
+}
+static void test_function_utf16be(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ Tcl_Interp *interp;
+ Tcl_Obj *pX;
+ sqlite3_value *pVal;
+ interp = (Tcl_Interp *)sqlite3_user_data(pCtx);
+ pX = Tcl_NewStringObj("test_function", -1);
+ Tcl_IncrRefCount(pX);
+ Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16BE", -1));
+ Tcl_ListObjAppendElement(interp, pX,
+ Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1));
+ Tcl_EvalObjEx(interp, pX, 0);
+ Tcl_DecrRefCount(pX);
+ pVal = sqlite3ValueNew(0);
+ sqlite3ValueSetStr(pVal, -1, Tcl_GetStringResult(interp),
+ SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3_result_text16(pCtx, sqlite3_value_text16le(pVal),
+ -1, SQLITE_TRANSIENT);
+ sqlite3_result_text16be(pCtx, sqlite3_value_text16le(pVal),
+ -1, SQLITE_TRANSIENT);
+ sqlite3_result_text16le(pCtx, sqlite3_value_text16le(pVal),
+ -1, SQLITE_TRANSIENT);
+ sqlite3ValueFree(pVal);
+}
+#endif /* SQLITE_OMIT_UTF16 */
+static int test_function(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3 *db;
+ int val;
+
+ if( objc!=5 ) goto bad_args;
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR;
+ if( val ){
+ sqlite3_create_function(db, "test_function", 1, SQLITE_UTF8,
+ interp, test_function_utf8, 0, 0);
+ }
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &val) ) return TCL_ERROR;
+ if( val ){
+ sqlite3_create_function(db, "test_function", 1, SQLITE_UTF16LE,
+ interp, test_function_utf16le, 0, 0);
+ }
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[4], &val) ) return TCL_ERROR;
+ if( val ){
+ sqlite3_create_function(db, "test_function", 1, SQLITE_UTF16BE,
+ interp, test_function_utf16be, 0, 0);
+ }
+
+ return TCL_OK;
+bad_args:
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <DB> <utf8> <utf16le> <utf16be>", 0);
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_ERROR;
+}
+
+/*
+** Usage: test_errstr <err code>
+**
+** Test that the english language string equivalents for sqlite error codes
+** are sane. The parameter is an integer representing an sqlite error code.
+** The result is a list of two elements, the string representation of the
+** error code and the english language explanation.
+*/
+static int test_errstr(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ char *zCode;
+ int i;
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "<error code>");
+ }
+
+ zCode = Tcl_GetString(objv[1]);
+ for(i=0; i<200; i++){
+ if( 0==strcmp(t1ErrorName(i), zCode) ) break;
+ }
+ Tcl_SetResult(interp, (char *)sqlite3ErrStr(i), 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: breakpoint
+**
+** This routine exists for one purpose - to provide a place to put a
+** breakpoint with GDB that can be triggered using TCL code. The use
+** for this is when a particular test fails on (say) the 1485th iteration.
+** In the TCL test script, we can add code like this:
+**
+** if {$i==1485} breakpoint
+**
+** Then run testfixture in the debugger and wait for the breakpoint to
+** fire. Then additional breakpoints can be set to trace down the bug.
+*/
+static int test_breakpoint(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ return TCL_OK; /* Do nothing */
+}
+
+/*
+** Usage: sqlite3_bind_zeroblob STMT IDX N
+**
+** Test the sqlite3_bind_zeroblob interface. STMT is a prepared statement.
+** IDX is the index of a wildcard in the prepared statement. This command
+** binds a N-byte zero-filled BLOB to the wildcard.
+*/
+static int test_bind_zeroblob(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ int n;
+ int rc;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT IDX N");
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &n) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_zeroblob(pStmt, idx, n);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_int STMT N VALUE
+**
+** Test the sqlite3_bind_int interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a 32-bit integer VALUE to that wildcard.
+*/
+static int test_bind_int(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ int value;
+ int rc;
+
+ if( objc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &value) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_int(pStmt, idx, value);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_bind_int64 STMT N VALUE
+**
+** Test the sqlite3_bind_int64 interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a 64-bit integer VALUE to that wildcard.
+*/
+static int test_bind_int64(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ i64 value;
+ int rc;
+
+ if( objc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+ if( Tcl_GetWideIntFromObj(interp, objv[3], &value) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_int64(pStmt, idx, value);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_bind_double STMT N VALUE
+**
+** Test the sqlite3_bind_double interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a 64-bit integer VALUE to that wildcard.
+*/
+static int test_bind_double(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ double value;
+ int rc;
+ const char *zVal;
+ int i;
+ static const struct {
+ const char *zName; /* Name of the special floating point value */
+ unsigned int iUpper; /* Upper 32 bits */
+ unsigned int iLower; /* Lower 32 bits */
+ } aSpecialFp[] = {
+ { "NaN", 0x7fffffff, 0xffffffff },
+ { "SNaN", 0x7ff7ffff, 0xffffffff },
+ { "-NaN", 0xffffffff, 0xffffffff },
+ { "-SNaN", 0xfff7ffff, 0xffffffff },
+ { "+Inf", 0x7ff00000, 0x00000000 },
+ { "-Inf", 0xfff00000, 0x00000000 },
+ { "Epsilon", 0x00000000, 0x00000001 },
+ { "-Epsilon", 0x80000000, 0x00000001 },
+ { "NaN0", 0x7ff80000, 0x00000000 },
+ { "-NaN0", 0xfff80000, 0x00000000 },
+ };
+
+ if( objc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+
+ /* Intercept the string "NaN" and generate a NaN value for it.
+ ** All other strings are passed through to Tcl_GetDoubleFromObj().
+ ** Tcl_GetDoubleFromObj() should understand "NaN" but some versions
+ ** contain a bug.
+ */
+ zVal = Tcl_GetString(objv[3]);
+ for(i=0; i<sizeof(aSpecialFp)/sizeof(aSpecialFp[0]); i++){
+ if( strcmp(aSpecialFp[i].zName, zVal)==0 ){
+ sqlite3_uint64 x;
+ x = aSpecialFp[i].iUpper;
+ x <<= 32;
+ x |= aSpecialFp[i].iLower;
+ assert( sizeof(value)==8 );
+ assert( sizeof(x)==8 );
+ memcpy(&value, &x, 8);
+ break;
+ }
+ }
+ if( i>=sizeof(aSpecialFp)/sizeof(aSpecialFp[0]) &&
+ Tcl_GetDoubleFromObj(interp, objv[3], &value) ){
+ return TCL_ERROR;
+ }
+ rc = sqlite3_bind_double(pStmt, idx, value);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_null STMT N
+**
+** Test the sqlite3_bind_null interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a NULL to the wildcard.
+*/
+static int test_bind_null(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ int rc;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_null(pStmt, idx);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_text STMT N STRING BYTES
+**
+** Test the sqlite3_bind_text interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a UTF-8 string STRING to the wildcard. The string is BYTES bytes
+** long.
+*/
+static int test_bind_text(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ int bytes;
+ char *value;
+ int rc;
+
+ if( objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE BYTES", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+ value = (char*)Tcl_GetByteArrayFromObj(objv[3], &bytes);
+ if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_text(pStmt, idx, value, bytes, SQLITE_TRANSIENT);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_text16 ?-static? STMT N STRING BYTES
+**
+** Test the sqlite3_bind_text16 interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a UTF-16 string STRING to the wildcard. The string is BYTES bytes
+** long.
+*/
+static int test_bind_text16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3_stmt *pStmt;
+ int idx;
+ int bytes;
+ char *value;
+ int rc;
+
+ void (*xDel)() = (objc==6?SQLITE_STATIC:SQLITE_TRANSIENT);
+ Tcl_Obj *oStmt = objv[objc-4];
+ Tcl_Obj *oN = objv[objc-3];
+ Tcl_Obj *oString = objv[objc-2];
+ Tcl_Obj *oBytes = objv[objc-1];
+
+ if( objc!=5 && objc!=6){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE BYTES", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(oStmt), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, oN, &idx) ) return TCL_ERROR;
+ value = (char*)Tcl_GetByteArrayFromObj(oString, 0);
+ if( Tcl_GetIntFromObj(interp, oBytes, &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_text16(pStmt, idx, (void *)value, bytes, xDel);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
+ return TCL_ERROR;
+ }
+
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_blob ?-static? STMT N DATA BYTES
+**
+** Test the sqlite3_bind_blob interface. STMT is a prepared statement.
+** N is the index of a wildcard in the prepared statement. This command
+** binds a BLOB to the wildcard. The BLOB is BYTES bytes in size.
+*/
+static int test_bind_blob(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int idx;
+ int bytes;
+ char *value;
+ int rc;
+ sqlite3_destructor_type xDestructor = SQLITE_TRANSIENT;
+
+ if( objc!=5 && objc!=6 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT N DATA BYTES", 0);
+ return TCL_ERROR;
+ }
+
+ if( objc==6 ){
+ xDestructor = SQLITE_STATIC;
+ objv++;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
+ value = Tcl_GetString(objv[3]);
+ if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_bind_blob(pStmt, idx, value, bytes, xDestructor);
+ if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_parameter_count STMT
+**
+** Return the number of wildcards in the given statement.
+*/
+static int test_bind_parameter_count(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_bind_parameter_count(pStmt)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_parameter_name STMT N
+**
+** Return the name of the Nth wildcard. The first wildcard is 1.
+** An empty string is returned if N is out of range or if the wildcard
+** is nameless.
+*/
+static int test_bind_parameter_name(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int i;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT N");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &i) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(sqlite3_bind_parameter_name(pStmt,i),-1)
+ );
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_bind_parameter_index STMT NAME
+**
+** Return the index of the wildcard called NAME. Return 0 if there is
+** no such wildcard.
+*/
+static int test_bind_parameter_index(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT NAME");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(
+ sqlite3_bind_parameter_index(pStmt,Tcl_GetString(objv[2]))
+ )
+ );
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_clear_bindings STMT
+**
+*/
+static int test_clear_bindings(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "STMT");
+ return TCL_ERROR;
+ }
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_clear_bindings(pStmt)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_sleep MILLISECONDS
+*/
+static int test_sleep(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int ms;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "MILLISECONDS");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &ms) ){
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_sleep(ms)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_errcode DB
+**
+** Return the string representation of the most recent sqlite3_* API
+** error code. e.g. "SQLITE_ERROR".
+*/
+static int test_errcode(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int rc;
+ char zBuf[30];
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_errcode(db);
+ if( (rc&0xff)==rc ){
+ zBuf[0] = 0;
+ }else{
+ sprintf(zBuf,"+%d", rc>>8);
+ }
+ Tcl_AppendResult(interp, (char *)t1ErrorName(rc), zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: test_errmsg DB
+**
+** Returns the UTF-8 representation of the error message string for the
+** most recent sqlite3_* API call.
+*/
+static int test_errmsg(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ const char *zErr;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ zErr = sqlite3_errmsg(db);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
+ return TCL_OK;
+}
+
+/*
+** Usage: test_errmsg16 DB
+**
+** Returns the UTF-16 representation of the error message string for the
+** most recent sqlite3_* API call. This is a byte array object at the TCL
+** level, and it includes the 0x00 0x00 terminator bytes at the end of the
+** UTF-16 string.
+*/
+static int test_errmsg16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3 *db;
+ const void *zErr;
+ int bytes = 0;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ zErr = sqlite3_errmsg16(db);
+ if( zErr ){
+ bytes = sqlite3Utf16ByteLen(zErr, -1);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zErr, bytes));
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ const char *zSql;
+ int bytes;
+ const char *zTail = 0;
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[50];
+ int rc;
+
+ if( objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zSql = Tcl_GetString(objv[2]);
+ if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_prepare(db, zSql, bytes, &pStmt, &zTail);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ if( zTail ){
+ if( bytes>=0 ){
+ bytes = bytes - (zTail-zSql);
+ }
+ if( strlen(zTail)<bytes ){
+ bytes = strlen(zTail);
+ }
+ Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
+ }
+ if( rc!=SQLITE_OK ){
+ assert( pStmt==0 );
+ sprintf(zBuf, "(%d) ", rc);
+ Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
+ return TCL_ERROR;
+ }
+
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare_v2 DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare_v2(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ const char *zSql;
+ int bytes;
+ const char *zTail = 0;
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[50];
+ int rc;
+
+ if( objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zSql = Tcl_GetString(objv[2]);
+ if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
+ assert(rc==SQLITE_OK || pStmt==0);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ if( zTail ){
+ if( bytes>=0 ){
+ bytes = bytes - (zTail-zSql);
+ }
+ Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
+ }
+ if( rc!=SQLITE_OK ){
+ assert( pStmt==0 );
+ sprintf(zBuf, "(%d) ", rc);
+ Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
+ return TCL_ERROR;
+ }
+
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare_tkt3134 DB
+**
+** Generate a prepared statement for a zero-byte string as a test
+** for ticket #3134. The string should be preceeded by a zero byte.
+*/
+static int test_prepare_tkt3134(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ static const char zSql[] = "\000SELECT 1";
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[50];
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_prepare_v2(db, &zSql[1], 0, &pStmt, 0);
+ assert(rc==SQLITE_OK || pStmt==0);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ if( rc!=SQLITE_OK ){
+ assert( pStmt==0 );
+ sprintf(zBuf, "(%d) ", rc);
+ Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
+ return TCL_ERROR;
+ }
+
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare16 DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3 *db;
+ const void *zSql;
+ const void *zTail = 0;
+ Tcl_Obj *pTail = 0;
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[50];
+ int rc;
+ int bytes; /* The integer specified as arg 3 */
+ int objlen; /* The byte-array length of arg 2 */
+
+ if( objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
+ if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_prepare16(db, zSql, bytes, &pStmt, &zTail);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ if( rc ){
+ return TCL_ERROR;
+ }
+
+ if( zTail ){
+ objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
+ }else{
+ objlen = 0;
+ }
+ pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
+ Tcl_IncrRefCount(pTail);
+ Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
+ Tcl_DecrRefCount(pTail);
+
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, zBuf, 0);
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare16_v2 DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare16_v2(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3 *db;
+ const void *zSql;
+ const void *zTail = 0;
+ Tcl_Obj *pTail = 0;
+ sqlite3_stmt *pStmt = 0;
+ char zBuf[50];
+ int rc;
+ int bytes; /* The integer specified as arg 3 */
+ int objlen; /* The byte-array length of arg 2 */
+
+ if( objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
+ if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+ rc = sqlite3_prepare16_v2(db, zSql, bytes, &pStmt, &zTail);
+ if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+ if( rc ){
+ return TCL_ERROR;
+ }
+
+ if( zTail ){
+ objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
+ }else{
+ objlen = 0;
+ }
+ pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
+ Tcl_IncrRefCount(pTail);
+ Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
+ Tcl_DecrRefCount(pTail);
+
+ if( pStmt ){
+ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, zBuf, 0);
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_open filename ?options-list?
+*/
+static int test_open(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zFilename;
+ sqlite3 *db;
+ int rc;
+ char zBuf[100];
+
+ if( objc!=3 && objc!=2 && objc!=1 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " filename options-list", 0);
+ return TCL_ERROR;
+ }
+
+ zFilename = objc>1 ? Tcl_GetString(objv[1]) : 0;
+ rc = sqlite3_open(zFilename, &db);
+
+ if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_open16 filename options
+*/
+static int test_open16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ const void *zFilename;
+ sqlite3 *db;
+ int rc;
+ char zBuf[100];
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " filename options-list", 0);
+ return TCL_ERROR;
+ }
+
+ zFilename = Tcl_GetByteArrayFromObj(objv[1], 0);
+ rc = sqlite3_open16(zFilename, &db);
+
+ if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+#endif /* SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_complete16 <UTF-16 string>
+**
+** Return 1 if the supplied argument is a complete SQL statement, or zero
+** otherwise.
+*/
+static int test_complete16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#if !defined(SQLITE_OMIT_COMPLETE) && !defined(SQLITE_OMIT_UTF16)
+ char *zBuf;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "<utf-16 sql>");
+ return TCL_ERROR;
+ }
+
+ zBuf = (char*)Tcl_GetByteArrayFromObj(objv[1], 0);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_complete16(zBuf)));
+#endif /* SQLITE_OMIT_COMPLETE && SQLITE_OMIT_UTF16 */
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_step STMT
+**
+** Advance the statement to the next row.
+*/
+static int test_step(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ rc = sqlite3_step(pStmt);
+
+ /* if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ) return TCL_ERROR; */
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_count STMT
+**
+** Return the number of columns returned by the sql statement STMT.
+*/
+static int test_column_count(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_column_count(pStmt)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_type STMT column
+**
+** Return the type of the data in column 'column' of the current row.
+*/
+static int test_column_type(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+ int tp;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ tp = sqlite3_column_type(pStmt, col);
+ switch( tp ){
+ case SQLITE_INTEGER:
+ Tcl_SetResult(interp, "INTEGER", TCL_STATIC);
+ break;
+ case SQLITE_NULL:
+ Tcl_SetResult(interp, "NULL", TCL_STATIC);
+ break;
+ case SQLITE_FLOAT:
+ Tcl_SetResult(interp, "FLOAT", TCL_STATIC);
+ break;
+ case SQLITE_TEXT:
+ Tcl_SetResult(interp, "TEXT", TCL_STATIC);
+ break;
+ case SQLITE_BLOB:
+ Tcl_SetResult(interp, "BLOB", TCL_STATIC);
+ break;
+ default:
+ assert(0);
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_int64 STMT column
+**
+** Return the data in column 'column' of the current row cast as an
+** wide (64-bit) integer.
+*/
+static int test_column_int64(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+ i64 iVal;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ iVal = sqlite3_column_int64(pStmt, col);
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iVal));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_blob STMT column
+*/
+static int test_column_blob(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+
+ int len;
+ const void *pBlob;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ len = sqlite3_column_bytes(pStmt, col);
+ pBlob = sqlite3_column_blob(pStmt, col);
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pBlob, len));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_double STMT column
+**
+** Return the data in column 'column' of the current row cast as a double.
+*/
+static int test_column_double(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+ double rVal;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ rVal = sqlite3_column_double(pStmt, col);
+ Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rVal));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_data_count STMT
+**
+** Return the number of columns returned by the sql statement STMT.
+*/
+static int test_data_count(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_data_count(pStmt)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_text STMT column
+**
+** Usage: sqlite3_column_decltype STMT column
+**
+** Usage: sqlite3_column_name STMT column
+*/
+static int test_stmt_utf8(
+ void * clientData, /* Pointer to SQLite API function to be invoke */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+ const char *(*xFunc)(sqlite3_stmt*, int) = clientData;
+ const char *zRet;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+ zRet = xFunc(pStmt, col);
+ if( zRet ){
+ Tcl_SetResult(interp, (char *)zRet, 0);
+ }
+ return TCL_OK;
+}
+
+static int test_global_recover(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_GLOBALRECOVER
+ int rc;
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+ rc = sqlite3_global_recover();
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_text STMT column
+**
+** Usage: sqlite3_column_decltype STMT column
+**
+** Usage: sqlite3_column_name STMT column
+*/
+static int test_stmt_utf16(
+ void * clientData, /* Pointer to SQLite API function to be invoked */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3_stmt *pStmt;
+ int col;
+ Tcl_Obj *pRet;
+ const void *zName16;
+ const void *(*xFunc)(sqlite3_stmt*, int) = clientData;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ zName16 = xFunc(pStmt, col);
+ if( zName16 ){
+ pRet = Tcl_NewByteArrayObj(zName16, sqlite3Utf16ByteLen(zName16, -1)+2);
+ Tcl_SetObjResult(interp, pRet);
+ }
+#endif /* SQLITE_OMIT_UTF16 */
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_column_int STMT column
+**
+** Usage: sqlite3_column_bytes STMT column
+**
+** Usage: sqlite3_column_bytes16 STMT column
+**
+*/
+static int test_stmt_int(
+ void * clientData, /* Pointer to SQLite API function to be invoked */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int col;
+ int (*xFunc)(sqlite3_stmt*, int) = clientData;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " STMT column", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR;
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(xFunc(pStmt, col)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite_set_magic DB MAGIC-NUMBER
+**
+** Set the db->magic value. This is used to test error recovery logic.
+*/
+static int sqlite_set_magic(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ sqlite3 *db;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB MAGIC", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ if( strcmp(argv[2], "SQLITE_MAGIC_OPEN")==0 ){
+ db->magic = SQLITE_MAGIC_OPEN;
+ }else if( strcmp(argv[2], "SQLITE_MAGIC_CLOSED")==0 ){
+ db->magic = SQLITE_MAGIC_CLOSED;
+ }else if( strcmp(argv[2], "SQLITE_MAGIC_BUSY")==0 ){
+ db->magic = SQLITE_MAGIC_BUSY;
+ }else if( strcmp(argv[2], "SQLITE_MAGIC_ERROR")==0 ){
+ db->magic = SQLITE_MAGIC_ERROR;
+ }else if( Tcl_GetInt(interp, argv[2], &db->magic) ){
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_interrupt DB
+**
+** Trigger an interrupt on DB
+*/
+static int test_interrupt(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ sqlite3 *db;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ sqlite3_interrupt(db);
+ return TCL_OK;
+}
+
+static u8 *sqlite3_stack_baseline = 0;
+
+/*
+** Fill the stack with a known bitpattern.
+*/
+static void prepStack(void){
+ int i;
+ u32 bigBuf[65536];
+ for(i=0; i<sizeof(bigBuf); i++) bigBuf[i] = 0xdeadbeef;
+ sqlite3_stack_baseline = (u8*)&bigBuf[65536];
+}
+
+/*
+** Get the current stack depth. Used for debugging only.
+*/
+u64 sqlite3StackDepth(void){
+ u8 x;
+ return (u64)(sqlite3_stack_baseline - &x);
+}
+
+/*
+** Usage: sqlite3_stack_used DB SQL
+**
+** Try to measure the amount of stack space used by a call to sqlite3_exec
+*/
+static int test_stack_used(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ sqlite3 *db;
+ int i;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB SQL", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ prepStack();
+ (void)sqlite3_exec(db, argv[2], 0, 0, 0);
+ for(i=65535; i>=0 && ((u32*)sqlite3_stack_baseline)[-i]==0xdeadbeef; i--){}
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(i*4));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite_delete_function DB function-name
+**
+** Delete the user function 'function-name' from database handle DB. It
+** is assumed that the user function was created as UTF8, any number of
+** arguments (the way the TCL interface does it).
+*/
+static int delete_function(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ int rc;
+ sqlite3 *db;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB function-name", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_create_function(db, argv[2], -1, SQLITE_UTF8, 0, 0, 0, 0);
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite_delete_collation DB collation-name
+**
+** Delete the collation sequence 'collation-name' from database handle
+** DB. It is assumed that the collation sequence was created as UTF8 (the
+** way the TCL interface does it).
+*/
+static int delete_collation(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ int rc;
+ sqlite3 *db;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB function-name", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ rc = sqlite3_create_collation(db, argv[2], SQLITE_UTF8, 0, 0);
+ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_get_autocommit DB
+**
+** Return true if the database DB is currently in auto-commit mode.
+** Return false if not.
+*/
+static int get_autocommit(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ char zBuf[30];
+ sqlite3 *db;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ sprintf(zBuf, "%d", sqlite3_get_autocommit(db));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_busy_timeout DB MS
+**
+** Set the busy timeout. This is more easily done using the timeout
+** method of the TCL interface. But we need a way to test the case
+** where it returns SQLITE_MISUSE.
+*/
+static int test_busy_timeout(
+ void * clientData,
+ Tcl_Interp *interp,
+ int argc,
+ char **argv
+){
+ int rc, ms;
+ sqlite3 *db;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[2], &ms) ) return TCL_ERROR;
+ rc = sqlite3_busy_timeout(db, ms);
+ Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: tcl_variable_type VARIABLENAME
+**
+** Return the name of the internal representation for the
+** value of the given variable.
+*/
+static int tcl_variable_type(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_Obj *pVar;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "VARIABLE");
+ return TCL_ERROR;
+ }
+ pVar = Tcl_GetVar2Ex(interp, Tcl_GetString(objv[1]), 0, TCL_LEAVE_ERR_MSG);
+ if( pVar==0 ) return TCL_ERROR;
+ if( pVar->typePtr ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(pVar->typePtr->name, -1));
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_release_memory ?N?
+**
+** Attempt to release memory currently held but not actually required.
+** The integer N is the number of bytes we are trying to release. The
+** return value is the amount of memory actually released.
+*/
+static int test_release_memory(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
+ int N;
+ int amt;
+ if( objc!=1 && objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?N?");
+ return TCL_ERROR;
+ }
+ if( objc==2 ){
+ if( Tcl_GetIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
+ }else{
+ N = -1;
+ }
+ amt = sqlite3_release_memory(N);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(amt));
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_soft_heap_limit ?N?
+**
+** Query or set the soft heap limit for the current thread. The
+** limit is only changed if the N is present. The previous limit
+** is returned.
+*/
+static int test_soft_heap_limit(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static int softHeapLimit = 0;
+ int amt;
+ if( objc!=1 && objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?N?");
+ return TCL_ERROR;
+ }
+ amt = softHeapLimit;
+ if( objc==2 ){
+ int N;
+ if( Tcl_GetIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
+ sqlite3_soft_heap_limit(N);
+ softHeapLimit = N;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(amt));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_thread_cleanup
+**
+** Call the sqlite3_thread_cleanup API.
+*/
+static int test_thread_cleanup(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_thread_cleanup();
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_pager_refcounts DB
+**
+** Return a list of numbers which are the PagerRefcount for all
+** pagers on each database connection.
+*/
+static int test_pager_refcounts(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int i;
+ int v, *a;
+ Tcl_Obj *pResult;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ pResult = Tcl_NewObj();
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt==0 ){
+ v = -1;
+ }else{
+ sqlite3_mutex_enter(db->mutex);
+ a = sqlite3PagerStats(sqlite3BtreePager(db->aDb[i].pBt));
+ v = a[0];
+ sqlite3_mutex_leave(db->mutex);
+ }
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(v));
+ }
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+
+/*
+** tclcmd: working_64bit_int
+**
+** Some TCL builds (ex: cygwin) do not support 64-bit integers. This
+** leads to a number of test failures. The present command checks the
+** TCL build to see whether or not it supports 64-bit integers. It
+** returns TRUE if it does and FALSE if not.
+**
+** This command is used to warn users that their TCL build is defective
+** and that the errors they are seeing in the test scripts might be
+** a result of their defective TCL rather than problems in SQLite.
+*/
+static int working_64bit_int(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ Tcl_Obj *pTestObj;
+ int working = 0;
+
+ pTestObj = Tcl_NewWideIntObj(1000000*(i64)1234567890);
+ working = strcmp(Tcl_GetString(pTestObj), "1234567890000000")==0;
+ Tcl_DecrRefCount(pTestObj);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(working));
+ return TCL_OK;
+}
+
+
+/*
+** tclcmd: vfs_unlink_test
+**
+** This TCL command unregisters the primary VFS and then registers
+** it back again. This is used to test the ability to register a
+** VFS when none are previously registered, and the ability to
+** unregister the only available VFS. Ticket #2738
+*/
+static int vfs_unlink_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int i;
+ sqlite3_vfs *pMain;
+ sqlite3_vfs *apVfs[20];
+ sqlite3_vfs one, two;
+
+ sqlite3_vfs_unregister(0); /* Unregister of NULL is harmless */
+ one.zName = "__one";
+ two.zName = "__two";
+
+ /* Calling sqlite3_vfs_register with 2nd argument of 0 does not
+ ** change the default VFS
+ */
+ pMain = sqlite3_vfs_find(0);
+ sqlite3_vfs_register(&one, 0);
+ assert( pMain==0 || pMain==sqlite3_vfs_find(0) );
+ sqlite3_vfs_register(&two, 0);
+ assert( pMain==0 || pMain==sqlite3_vfs_find(0) );
+
+ /* We can find a VFS by its name */
+ assert( sqlite3_vfs_find("__one")==&one );
+ assert( sqlite3_vfs_find("__two")==&two );
+
+ /* Calling sqlite_vfs_register with non-zero second parameter changes the
+ ** default VFS, even if the 1st parameter is an existig VFS that is
+ ** previously registered as the non-default.
+ */
+ sqlite3_vfs_register(&one, 1);
+ assert( sqlite3_vfs_find("__one")==&one );
+ assert( sqlite3_vfs_find("__two")==&two );
+ assert( sqlite3_vfs_find(0)==&one );
+ sqlite3_vfs_register(&two, 1);
+ assert( sqlite3_vfs_find("__one")==&one );
+ assert( sqlite3_vfs_find("__two")==&two );
+ assert( sqlite3_vfs_find(0)==&two );
+ if( pMain ){
+ sqlite3_vfs_register(pMain, 1);
+ assert( sqlite3_vfs_find("__one")==&one );
+ assert( sqlite3_vfs_find("__two")==&two );
+ assert( sqlite3_vfs_find(0)==pMain );
+ }
+
+ /* Unlink the default VFS. Repeat until there are no more VFSes
+ ** registered.
+ */
+ for(i=0; i<sizeof(apVfs)/sizeof(apVfs[0]); i++){
+ apVfs[i] = sqlite3_vfs_find(0);
+ if( apVfs[i] ){
+ assert( apVfs[i]==sqlite3_vfs_find(apVfs[i]->zName) );
+ sqlite3_vfs_unregister(apVfs[i]);
+ assert( 0==sqlite3_vfs_find(apVfs[i]->zName) );
+ }
+ }
+ assert( 0==sqlite3_vfs_find(0) );
+
+ /* Register the main VFS as non-default (will be made default, since
+ ** it'll be the only one in existence).
+ */
+ sqlite3_vfs_register(pMain, 0);
+ assert( sqlite3_vfs_find(0)==pMain );
+
+ /* Un-register the main VFS again to restore an empty VFS list */
+ sqlite3_vfs_unregister(pMain);
+ assert( 0==sqlite3_vfs_find(0) );
+
+ /* Relink all VFSes in reverse order. */
+ for(i=sizeof(apVfs)/sizeof(apVfs[0])-1; i>=0; i--){
+ if( apVfs[i] ){
+ sqlite3_vfs_register(apVfs[i], 1);
+ assert( apVfs[i]==sqlite3_vfs_find(0) );
+ assert( apVfs[i]==sqlite3_vfs_find(apVfs[i]->zName) );
+ }
+ }
+
+ /* Unregister out sample VFSes. */
+ sqlite3_vfs_unregister(&one);
+ sqlite3_vfs_unregister(&two);
+
+ /* Unregistering a VFS that is not currently registered is harmless */
+ sqlite3_vfs_unregister(&one);
+ sqlite3_vfs_unregister(&two);
+ assert( sqlite3_vfs_find("__one")==0 );
+ assert( sqlite3_vfs_find("__two")==0 );
+
+ /* We should be left with the original default VFS back as the
+ ** original */
+ assert( sqlite3_vfs_find(0)==pMain );
+
+ return TCL_OK;
+}
+
+/*
+** tclcmd: vfs_initfail_test
+**
+** This TCL command attempts to vfs_find and vfs_register when the
+** sqlite3_initialize() interface is failing. All calls should fail.
+*/
+static int vfs_initfail_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_vfs one;
+ one.zName = "__one";
+
+ if( sqlite3_vfs_find(0) ) return TCL_ERROR;
+ sqlite3_vfs_register(&one, 0);
+ if( sqlite3_vfs_find(0) ) return TCL_ERROR;
+ sqlite3_vfs_register(&one, 1);
+ if( sqlite3_vfs_find(0) ) return TCL_ERROR;
+ return TCL_OK;
+}
+
+/*
+** Saved VFSes
+*/
+static sqlite3_vfs *apVfs[20];
+static int nVfs = 0;
+
+/*
+** tclcmd: vfs_unregister_all
+**
+** Unregister all VFSes.
+*/
+static int vfs_unregister_all(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int i;
+ for(i=0; i<ArraySize(apVfs); i++){
+ apVfs[i] = sqlite3_vfs_find(0);
+ if( apVfs[i]==0 ) break;
+ sqlite3_vfs_unregister(apVfs[i]);
+ }
+ nVfs = i;
+ return TCL_OK;
+}
+/*
+** tclcmd: vfs_reregister_all
+**
+** Restore all VFSes that were removed using vfs_unregister_all
+*/
+static int vfs_reregister_all(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int i;
+ for(i=0; i<nVfs; i++){
+ sqlite3_vfs_register(apVfs[i], i==0);
+ }
+ return TCL_OK;
+}
+
+
+/*
+** tclcmd: file_control_test DB
+**
+** This TCL command runs the sqlite3_file_control interface and
+** verifies correct operation of the same.
+*/
+static int file_control_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int iArg = 0;
+ sqlite3 *db;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_file_control(db, 0, 0, &iArg);
+ assert( rc==SQLITE_ERROR );
+ rc = sqlite3_file_control(db, "notadatabase", SQLITE_FCNTL_LOCKSTATE, &iArg);
+ assert( rc==SQLITE_ERROR );
+ rc = sqlite3_file_control(db, "main", -1, &iArg);
+ assert( rc==SQLITE_ERROR );
+ rc = sqlite3_file_control(db, "temp", -1, &iArg);
+ assert( rc==SQLITE_ERROR );
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite3_vfs_list
+**
+** Return a tcl list containing the names of all registered vfs's.
+*/
+static int vfs_list(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_vfs *pVfs;
+ Tcl_Obj *pRet = Tcl_NewObj();
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+ for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){
+ Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(pVfs->zName, -1));
+ }
+ Tcl_SetObjResult(interp, pRet);
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite3_limit DB ID VALUE
+**
+** This TCL command runs the sqlite3_limit interface and
+** verifies correct operation of the same.
+*/
+static int test_limit(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ int rc;
+ static const struct {
+ char *zName;
+ int id;
+ } aId[] = {
+ { "SQLITE_LIMIT_LENGTH", SQLITE_LIMIT_LENGTH },
+ { "SQLITE_LIMIT_SQL_LENGTH", SQLITE_LIMIT_SQL_LENGTH },
+ { "SQLITE_LIMIT_COLUMN", SQLITE_LIMIT_COLUMN },
+ { "SQLITE_LIMIT_EXPR_DEPTH", SQLITE_LIMIT_EXPR_DEPTH },
+ { "SQLITE_LIMIT_COMPOUND_SELECT", SQLITE_LIMIT_COMPOUND_SELECT },
+ { "SQLITE_LIMIT_VDBE_OP", SQLITE_LIMIT_VDBE_OP },
+ { "SQLITE_LIMIT_FUNCTION_ARG", SQLITE_LIMIT_FUNCTION_ARG },
+ { "SQLITE_LIMIT_ATTACHED", SQLITE_LIMIT_ATTACHED },
+ { "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", SQLITE_LIMIT_LIKE_PATTERN_LENGTH },
+ { "SQLITE_LIMIT_VARIABLE_NUMBER", SQLITE_LIMIT_VARIABLE_NUMBER },
+
+ /* Out of range test cases */
+ { "SQLITE_LIMIT_TOOSMALL", -1, },
+ { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_VARIABLE_NUMBER+1 },
+ };
+ int i, id;
+ int val;
+ const char *zId;
+
+ if( objc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB ID VALUE", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zId = Tcl_GetString(objv[2]);
+ for(i=0; i<sizeof(aId)/sizeof(aId[0]); i++){
+ if( strcmp(zId, aId[i].zName)==0 ){
+ id = aId[i].id;
+ break;
+ }
+ }
+ if( i>=sizeof(aId)/sizeof(aId[0]) ){
+ Tcl_AppendResult(interp, "unknown limit type: ", zId, (char*)0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR;
+ rc = sqlite3_limit(db, id, val);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** tclcmd: save_prng_state
+**
+** Save the state of the pseudo-random number generator.
+** At the same time, verify that sqlite3_test_control works even when
+** called with an out-of-range opcode.
+*/
+static int save_prng_state(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int rc = sqlite3_test_control(9999);
+ assert( rc==0 );
+ rc = sqlite3_test_control(-1);
+ assert( rc==0 );
+ sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SAVE);
+ return TCL_OK;
+}
+/*
+** tclcmd: restore_prng_state
+*/
+static int restore_prng_state(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_test_control(SQLITE_TESTCTRL_PRNG_RESTORE);
+ return TCL_OK;
+}
+/*
+** tclcmd: reset_prng_state
+*/
+static int reset_prng_state(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3_test_control(SQLITE_TESTCTRL_PRNG_RESET);
+ return TCL_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest1_Init(Tcl_Interp *interp){
+ extern int sqlite3_search_count;
+ extern int sqlite3_interrupt_count;
+ extern int sqlite3_open_file_count;
+ extern int sqlite3_sort_count;
+ extern int sqlite3_current_time;
+ extern int sqlite3_max_blobsize;
+ extern int sqlite3BtreeSharedCacheReport(void*,
+ Tcl_Interp*,int,Tcl_Obj*CONST*);
+ static struct {
+ char *zName;
+ Tcl_CmdProc *xProc;
+ } aCmd[] = {
+ { "db_enter", (Tcl_CmdProc*)db_enter },
+ { "db_leave", (Tcl_CmdProc*)db_leave },
+ { "sqlite3_mprintf_int", (Tcl_CmdProc*)sqlite3_mprintf_int },
+ { "sqlite3_mprintf_int64", (Tcl_CmdProc*)sqlite3_mprintf_int64 },
+ { "sqlite3_mprintf_str", (Tcl_CmdProc*)sqlite3_mprintf_str },
+ { "sqlite3_snprintf_str", (Tcl_CmdProc*)sqlite3_snprintf_str },
+ { "sqlite3_mprintf_stronly", (Tcl_CmdProc*)sqlite3_mprintf_stronly},
+ { "sqlite3_mprintf_double", (Tcl_CmdProc*)sqlite3_mprintf_double },
+ { "sqlite3_mprintf_scaled", (Tcl_CmdProc*)sqlite3_mprintf_scaled },
+ { "sqlite3_mprintf_hexdouble", (Tcl_CmdProc*)sqlite3_mprintf_hexdouble},
+ { "sqlite3_mprintf_z_test", (Tcl_CmdProc*)test_mprintf_z },
+ { "sqlite3_mprintf_n_test", (Tcl_CmdProc*)test_mprintf_n },
+ { "sqlite3_snprintf_int", (Tcl_CmdProc*)test_snprintf_int },
+ { "sqlite3_last_insert_rowid", (Tcl_CmdProc*)test_last_rowid },
+ { "sqlite3_exec_printf", (Tcl_CmdProc*)test_exec_printf },
+ { "sqlite3_exec", (Tcl_CmdProc*)test_exec },
+ { "sqlite3_exec_nr", (Tcl_CmdProc*)test_exec_nr },
+#ifndef SQLITE_OMIT_GET_TABLE
+ { "sqlite3_get_table_printf", (Tcl_CmdProc*)test_get_table_printf },
+#endif
+ { "sqlite3_close", (Tcl_CmdProc*)sqlite_test_close },
+ { "sqlite3_create_function", (Tcl_CmdProc*)test_create_function },
+ { "sqlite3_create_aggregate", (Tcl_CmdProc*)test_create_aggregate },
+ { "sqlite_register_test_function", (Tcl_CmdProc*)test_register_func },
+ { "sqlite_abort", (Tcl_CmdProc*)sqlite_abort },
+ { "sqlite_bind", (Tcl_CmdProc*)test_bind },
+ { "breakpoint", (Tcl_CmdProc*)test_breakpoint },
+ { "sqlite3_key", (Tcl_CmdProc*)test_key },
+ { "sqlite3_rekey", (Tcl_CmdProc*)test_rekey },
+ { "sqlite_set_magic", (Tcl_CmdProc*)sqlite_set_magic },
+ { "sqlite3_interrupt", (Tcl_CmdProc*)test_interrupt },
+ { "sqlite_delete_function", (Tcl_CmdProc*)delete_function },
+ { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation },
+ { "sqlite3_get_autocommit", (Tcl_CmdProc*)get_autocommit },
+ { "sqlite3_stack_used", (Tcl_CmdProc*)test_stack_used },
+ { "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout },
+ { "printf", (Tcl_CmdProc*)test_printf },
+ { "sqlite3IoTrace", (Tcl_CmdProc*)test_io_trace },
+ };
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+ { "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 },
+ { "sqlite3_bind_null", test_bind_null ,0 },
+ { "sqlite3_bind_text", test_bind_text ,0 },
+ { "sqlite3_bind_text16", test_bind_text16 ,0 },
+ { "sqlite3_bind_blob", test_bind_blob ,0 },
+ { "sqlite3_bind_parameter_count", test_bind_parameter_count, 0},
+ { "sqlite3_bind_parameter_name", test_bind_parameter_name, 0},
+ { "sqlite3_bind_parameter_index", test_bind_parameter_index, 0},
+ { "sqlite3_clear_bindings", test_clear_bindings, 0},
+ { "sqlite3_sleep", test_sleep, 0},
+ { "sqlite3_errcode", test_errcode ,0 },
+ { "sqlite3_errmsg", test_errmsg ,0 },
+ { "sqlite3_errmsg16", test_errmsg16 ,0 },
+ { "sqlite3_open", test_open ,0 },
+ { "sqlite3_open16", test_open16 ,0 },
+ { "sqlite3_complete16", test_complete16 ,0 },
+
+ { "sqlite3_prepare", test_prepare ,0 },
+ { "sqlite3_prepare16", test_prepare16 ,0 },
+ { "sqlite3_prepare_v2", test_prepare_v2 ,0 },
+ { "sqlite3_prepare_tkt3134", test_prepare_tkt3134, 0},
+ { "sqlite3_prepare16_v2", test_prepare16_v2 ,0 },
+ { "sqlite3_finalize", test_finalize ,0 },
+ { "sqlite3_reset", test_reset ,0 },
+ { "sqlite3_expired", test_expired ,0 },
+ { "sqlite3_transfer_bindings", test_transfer_bind ,0 },
+ { "sqlite3_changes", test_changes ,0 },
+ { "sqlite3_step", test_step ,0 },
+ { "sqlite3_next_stmt", test_next_stmt ,0 },
+
+ { "sqlite3_release_memory", test_release_memory, 0},
+ { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
+ { "sqlite3_thread_cleanup", test_thread_cleanup, 0},
+ { "sqlite3_pager_refcounts", test_pager_refcounts, 0},
+
+ { "sqlite3_load_extension", test_load_extension, 0},
+ { "sqlite3_enable_load_extension", test_enable_load, 0},
+ { "sqlite3_extended_result_codes", test_extended_result_codes, 0},
+ { "sqlite3_limit", test_limit, 0},
+
+ { "save_prng_state", save_prng_state, 0 },
+ { "restore_prng_state", restore_prng_state, 0 },
+ { "reset_prng_state", reset_prng_state, 0 },
+
+ /* sqlite3_column_*() API */
+ { "sqlite3_column_count", test_column_count ,0 },
+ { "sqlite3_data_count", test_data_count ,0 },
+ { "sqlite3_column_type", test_column_type ,0 },
+ { "sqlite3_column_blob", test_column_blob ,0 },
+ { "sqlite3_column_double", test_column_double ,0 },
+ { "sqlite3_column_int64", test_column_int64 ,0 },
+ { "sqlite3_column_text", test_stmt_utf8, sqlite3_column_text },
+ { "sqlite3_column_name", test_stmt_utf8, sqlite3_column_name },
+ { "sqlite3_column_int", test_stmt_int, sqlite3_column_int },
+ { "sqlite3_column_bytes", test_stmt_int, sqlite3_column_bytes },
+#ifndef SQLITE_OMIT_DECLTYPE
+ { "sqlite3_column_decltype", test_stmt_utf8, sqlite3_column_decltype },
+#endif
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+{ "sqlite3_column_database_name", test_stmt_utf8, sqlite3_column_database_name},
+{ "sqlite3_column_table_name", test_stmt_utf8, sqlite3_column_table_name},
+{ "sqlite3_column_origin_name", test_stmt_utf8, sqlite3_column_origin_name},
+#endif
+
+#ifndef SQLITE_OMIT_UTF16
+ { "sqlite3_column_bytes16", test_stmt_int, sqlite3_column_bytes16 },
+ { "sqlite3_column_text16", test_stmt_utf16, sqlite3_column_text16 },
+ { "sqlite3_column_name16", test_stmt_utf16, sqlite3_column_name16 },
+ { "add_alignment_test_collations", add_alignment_test_collations, 0 },
+#ifndef SQLITE_OMIT_DECLTYPE
+ { "sqlite3_column_decltype16", test_stmt_utf16, sqlite3_column_decltype16},
+#endif
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+{"sqlite3_column_database_name16",
+ test_stmt_utf16, sqlite3_column_database_name16},
+{"sqlite3_column_table_name16", test_stmt_utf16, sqlite3_column_table_name16},
+{"sqlite3_column_origin_name16", test_stmt_utf16, sqlite3_column_origin_name16},
+#endif
+#endif
+ { "sqlite3_create_collation_v2", test_create_collation_v2, 0 },
+ { "sqlite3_global_recover", test_global_recover, 0 },
+ { "working_64bit_int", working_64bit_int, 0 },
+ { "vfs_unlink_test", vfs_unlink_test, 0 },
+ { "vfs_initfail_test", vfs_initfail_test, 0 },
+ { "vfs_unregister_all", vfs_unregister_all, 0 },
+ { "vfs_reregister_all", vfs_reregister_all, 0 },
+ { "file_control_test", file_control_test, 0 },
+ { "sqlite3_vfs_list", vfs_list, 0 },
+
+ /* Functions from os.h */
+#ifndef SQLITE_OMIT_UTF16
+ { "add_test_collate", test_collate, 0 },
+ { "add_test_collate_needed", test_collate_needed, 0 },
+ { "add_test_function", test_function, 0 },
+#endif
+ { "sqlite3_test_errstr", test_errstr, 0 },
+ { "tcl_variable_type", tcl_variable_type, 0 },
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ { "sqlite3_enable_shared_cache", test_enable_shared, 0 },
+ { "sqlite3_shared_cache_report", sqlite3BtreeSharedCacheReport, 0},
+#endif
+ { "sqlite3_libversion_number", test_libversion_number, 0 },
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ { "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
+#endif
+#ifndef SQLITE_OMIT_INCRBLOB
+ { "sqlite3_blob_read", test_blob_read, 0 },
+ { "sqlite3_blob_write", test_blob_write, 0 },
+#endif
+ };
+ static int bitmask_size = sizeof(Bitmask)*8;
+ int i;
+ extern int sqlite3_sync_count, sqlite3_fullsync_count;
+ extern int sqlite3_opentemp_count;
+ extern int sqlite3_like_count;
+ extern int sqlite3_xferopt_count;
+ extern int sqlite3_pager_readdb_count;
+ extern int sqlite3_pager_writedb_count;
+ extern int sqlite3_pager_writej_count;
+ extern int sqlite3_pager_pgfree_count;
+#if SQLITE_OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
+ extern int threadsOverrideEachOthersLocks;
+#endif
+#if SQLITE_OS_WIN
+ extern int sqlite3_os_type;
+#endif
+#ifdef SQLITE_DEBUG
+ extern int sqlite3WhereTrace;
+ extern int sqlite3OSTrace;
+ extern int sqlite3VdbeAddopTrace;
+#endif
+#ifdef SQLITE_TEST
+ extern int sqlite3_enable_in_opt;
+ extern char sqlite3_query_plan[];
+ static char *query_plan = sqlite3_query_plan;
+#endif
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+ Tcl_LinkVar(interp, "sqlite_search_count",
+ (char*)&sqlite3_search_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_sort_count",
+ (char*)&sqlite3_sort_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_max_blobsize",
+ (char*)&sqlite3_max_blobsize, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_like_count",
+ (char*)&sqlite3_like_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_interrupt_count",
+ (char*)&sqlite3_interrupt_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_open_file_count",
+ (char*)&sqlite3_open_file_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_current_time",
+ (char*)&sqlite3_current_time, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_xferopt_count",
+ (char*)&sqlite3_xferopt_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_pager_readdb_count",
+ (char*)&sqlite3_pager_readdb_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_pager_writedb_count",
+ (char*)&sqlite3_pager_writedb_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_pager_writej_count",
+ (char*)&sqlite3_pager_writej_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite3_pager_pgfree_count",
+ (char*)&sqlite3_pager_pgfree_count, TCL_LINK_INT);
+#ifndef SQLITE_OMIT_UTF16
+ Tcl_LinkVar(interp, "unaligned_string_counter",
+ (char*)&unaligned_string_counter, TCL_LINK_INT);
+#endif
+#if SQLITE_OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
+ Tcl_LinkVar(interp, "threadsOverrideEachOthersLocks",
+ (char*)&threadsOverrideEachOthersLocks, TCL_LINK_INT);
+#endif
+#ifndef SQLITE_OMIT_UTF16
+ Tcl_LinkVar(interp, "sqlite_last_needed_collation",
+ (char*)&pzNeededCollation, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
+#endif
+#if SQLITE_OS_WIN
+ Tcl_LinkVar(interp, "sqlite_os_type",
+ (char*)&sqlite3_os_type, TCL_LINK_INT);
+#endif
+#ifdef SQLITE_TEST
+ Tcl_LinkVar(interp, "sqlite_query_plan",
+ (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
+#endif
+#ifdef SQLITE_DEBUG
+ Tcl_LinkVar(interp, "sqlite_addop_trace",
+ (char*)&sqlite3VdbeAddopTrace, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_where_trace",
+ (char*)&sqlite3WhereTrace, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_os_trace",
+ (char*)&sqlite3OSTrace, TCL_LINK_INT);
+#endif
+#ifndef SQLITE_OMIT_DISKIO
+ Tcl_LinkVar(interp, "sqlite_opentemp_count",
+ (char*)&sqlite3_opentemp_count, TCL_LINK_INT);
+#endif
+ Tcl_LinkVar(interp, "sqlite_static_bind_value",
+ (char*)&sqlite_static_bind_value, TCL_LINK_STRING);
+ Tcl_LinkVar(interp, "sqlite_static_bind_nbyte",
+ (char*)&sqlite_static_bind_nbyte, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_temp_directory",
+ (char*)&sqlite3_temp_directory, TCL_LINK_STRING);
+ Tcl_LinkVar(interp, "bitmask_size",
+ (char*)&bitmask_size, TCL_LINK_INT|TCL_LINK_READ_ONLY);
+ Tcl_LinkVar(interp, "sqlite_sync_count",
+ (char*)&sqlite3_sync_count, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_fullsync_count",
+ (char*)&sqlite3_fullsync_count, TCL_LINK_INT);
+#ifdef SQLITE_TEST
+ Tcl_LinkVar(interp, "sqlite_enable_in_opt",
+ (char*)&sqlite3_enable_in_opt, TCL_LINK_INT);
+#endif
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test2.c b/third_party/sqlite/src/test2.c
new file mode 100755
index 0000000..c27a9fd
--- /dev/null
+++ b/third_party/sqlite/src/test2.c
@@ -0,0 +1,659 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the pager.c module in SQLite. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test2.c,v 1.59 2008/07/12 14:52:20 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/*
+** Interpret an SQLite error number
+*/
+static char *errorName(int rc){
+ char *zName;
+ switch( rc ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
+ case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
+ case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
+ case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
+ case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
+ default: zName = "SQLITE_Unknown"; break;
+ }
+ return zName;
+}
+
+/*
+** Page size and reserved size used for testing.
+*/
+static int test_pagesize = 1024;
+
+/*
+** Usage: pager_open FILENAME N-PAGE
+**
+** Open a new pager
+*/
+static int pager_open(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ u16 pageSize;
+ Pager *pPager;
+ int nPage;
+ int rc;
+ char zBuf[100];
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME N-PAGE\"", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
+ rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3PagerSetCachesize(pPager, nPage);
+ pageSize = test_pagesize;
+ sqlite3PagerSetPagesize(pPager, &pageSize);
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPager);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_close ID
+**
+** Close the given pager.
+*/
+static int pager_close(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerClose(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_rollback ID
+**
+** Rollback changes
+*/
+static int pager_rollback(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerRollback(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_commit ID
+**
+** Commit all changes
+*/
+static int pager_commit(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0, 0);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ rc = sqlite3PagerCommitPhaseTwo(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_stmt_begin ID
+**
+** Start a new checkpoint.
+*/
+static int pager_stmt_begin(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerStmtBegin(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_stmt_rollback ID
+**
+** Rollback changes to a checkpoint
+*/
+static int pager_stmt_rollback(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerStmtRollback(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_stmt_commit ID
+**
+** Commit changes to a checkpoint
+*/
+static int pager_stmt_commit(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerStmtCommit(pPager);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_stats ID
+**
+** Return pager statistics.
+*/
+static int pager_stats(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int i, *a;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ a = sqlite3PagerStats(pPager);
+ for(i=0; i<9; i++){
+ static char *zName[] = {
+ "ref", "page", "max", "size", "state", "err",
+ "hit", "miss", "ovfl",
+ };
+ char zBuf[100];
+ Tcl_AppendElement(interp, zName[i]);
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",a[i]);
+ Tcl_AppendElement(interp, zBuf);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_pagecount ID
+**
+** Return the size of the database file.
+*/
+static int pager_pagecount(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ char zBuf[100];
+ int nPage;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ sqlite3PagerPagecount(pPager, &nPage);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nPage);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: page_get ID PGNO
+**
+** Return a pointer to a page from the database.
+*/
+static int page_get(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ char zBuf[100];
+ DbPage *pPage;
+ int pgno;
+ int rc;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID PGNO\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
+ rc = sqlite3PagerGet(pPager, pgno, &pPage);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: page_lookup ID PGNO
+**
+** Return a pointer to a page if the page is already in cache.
+** If not in cache, return an empty string.
+*/
+static int page_lookup(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ char zBuf[100];
+ DbPage *pPage;
+ int pgno;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID PGNO\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
+ pPage = sqlite3PagerLookup(pPager, pgno);
+ if( pPage ){
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
+ Tcl_AppendResult(interp, zBuf, 0);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: pager_truncate ID PGNO
+*/
+static int pager_truncate(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Pager *pPager;
+ int rc;
+ int pgno;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID PGNO\"", 0);
+ return TCL_ERROR;
+ }
+ pPager = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
+ rc = sqlite3PagerTruncate(pPager, pgno);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+** Usage: page_unref PAGE
+**
+** Drop a pointer to a page.
+*/
+static int page_unref(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ DbPage *pPage;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " PAGE\"", 0);
+ return TCL_ERROR;
+ }
+ pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerUnref(pPage);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: page_read PAGE
+**
+** Return the content of a page
+*/
+static int page_read(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ char zBuf[100];
+ DbPage *pPage;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " PAGE\"", 0);
+ return TCL_ERROR;
+ }
+ pPage = sqlite3TestTextToPtr(argv[1]);
+ memcpy(zBuf, sqlite3PagerGetData(pPage), sizeof(zBuf));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: page_number PAGE
+**
+** Return the page number for a page.
+*/
+static int page_number(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ char zBuf[100];
+ DbPage *pPage;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " PAGE\"", 0);
+ return TCL_ERROR;
+ }
+ pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3PagerPagenumber(pPage));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: page_write PAGE DATA
+**
+** Write something into a page.
+*/
+static int page_write(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ DbPage *pPage;
+ char *pData;
+ int rc;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " PAGE DATA\"", 0);
+ return TCL_ERROR;
+ }
+ pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3PagerWrite(pPage);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ pData = sqlite3PagerGetData(pPage);
+ strncpy(pData, argv[2], test_pagesize-1);
+ pData[test_pagesize-1] = 0;
+ return TCL_OK;
+}
+
+#ifndef SQLITE_OMIT_DISKIO
+/*
+** Usage: fake_big_file N FILENAME
+**
+** Write a few bytes at the N megabyte point of FILENAME. This will
+** create a large file. If the file was a valid SQLite database, then
+** the next time the database is opened, SQLite will begin allocating
+** new pages after N. If N is 2096 or bigger, this will test the
+** ability of SQLite to write to large files.
+*/
+static int fake_big_file(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ sqlite3_vfs *pVfs;
+ sqlite3_file *fd = 0;
+ int rc;
+ int n;
+ i64 offset;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " N-MEGABYTES FILE\"", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
+
+ pVfs = sqlite3_vfs_find(0);
+ rc = sqlite3OsOpenMalloc(pVfs, argv[2], &fd,
+ (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
+ );
+ if( rc ){
+ Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ offset = n;
+ offset *= 1024*1024;
+ rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset);
+ sqlite3OsCloseFree(fd);
+ if( rc ){
+ Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+#endif
+
+
+/*
+** sqlite3BitvecBuiltinTest SIZE PROGRAM
+**
+** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control.
+** See comments on sqlite3BitvecBuiltinTest() for additional information.
+*/
+static int testBitvecBuiltinTest(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int sz, rc;
+ int nProg = 0;
+ int aProg[100];
+ const char *z;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " SIZE PROGRAM\"", (void*)0);
+ }
+ if( Tcl_GetInt(interp, argv[1], &sz) ) return TCL_ERROR;
+ z = argv[2];
+ while( nProg<99 && *z ){
+ while( *z && !isdigit(*z) ){ z++; }
+ if( *z==0 ) break;
+ aProg[nProg++] = atoi(z);
+ while( isdigit(*z) ){ z++; }
+ }
+ aProg[nProg] = 0;
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_BITVEC_TEST, sz, aProg);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest2_Init(Tcl_Interp *interp){
+ extern int sqlite3_io_error_persist;
+ extern int sqlite3_io_error_pending;
+ extern int sqlite3_io_error_hit;
+ extern int sqlite3_io_error_hardhit;
+ extern int sqlite3_diskfull_pending;
+ extern int sqlite3_diskfull;
+ extern int sqlite3_pager_n_sort_bucket;
+ static struct {
+ char *zName;
+ Tcl_CmdProc *xProc;
+ } aCmd[] = {
+ { "pager_open", (Tcl_CmdProc*)pager_open },
+ { "pager_close", (Tcl_CmdProc*)pager_close },
+ { "pager_commit", (Tcl_CmdProc*)pager_commit },
+ { "pager_rollback", (Tcl_CmdProc*)pager_rollback },
+ { "pager_stmt_begin", (Tcl_CmdProc*)pager_stmt_begin },
+ { "pager_stmt_commit", (Tcl_CmdProc*)pager_stmt_commit },
+ { "pager_stmt_rollback", (Tcl_CmdProc*)pager_stmt_rollback },
+ { "pager_stats", (Tcl_CmdProc*)pager_stats },
+ { "pager_pagecount", (Tcl_CmdProc*)pager_pagecount },
+ { "page_get", (Tcl_CmdProc*)page_get },
+ { "page_lookup", (Tcl_CmdProc*)page_lookup },
+ { "page_unref", (Tcl_CmdProc*)page_unref },
+ { "page_read", (Tcl_CmdProc*)page_read },
+ { "page_write", (Tcl_CmdProc*)page_write },
+ { "page_number", (Tcl_CmdProc*)page_number },
+ { "pager_truncate", (Tcl_CmdProc*)pager_truncate },
+#ifndef SQLITE_OMIT_DISKIO
+ { "fake_big_file", (Tcl_CmdProc*)fake_big_file },
+#endif
+ { "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest},
+ };
+ int i;
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ Tcl_LinkVar(interp, "sqlite_io_error_pending",
+ (char*)&sqlite3_io_error_pending, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_io_error_persist",
+ (char*)&sqlite3_io_error_persist, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_io_error_hit",
+ (char*)&sqlite3_io_error_hit, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_io_error_hardhit",
+ (char*)&sqlite3_io_error_hardhit, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_diskfull_pending",
+ (char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_diskfull",
+ (char*)&sqlite3_diskfull, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_pending_byte",
+ (char*)&sqlite3_pending_byte, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_pager_n_sort_bucket",
+ (char*)&sqlite3_pager_n_sort_bucket, TCL_LINK_INT);
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test3.c b/third_party/sqlite/src/test3.c
new file mode 100755
index 0000000..6b06dfc
--- /dev/null
+++ b/third_party/sqlite/src/test3.c
@@ -0,0 +1,1605 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the btree.c module in SQLite. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test3.c,v 1.100 2008/07/12 14:52:20 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "btreeInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Interpret an SQLite error number
+*/
+static char *errorName(int rc){
+ char *zName;
+ switch( rc ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
+ default: zName = "SQLITE_Unknown"; break;
+ }
+ return zName;
+}
+
+/*
+** A bogus sqlite3 connection structure for use in the btree
+** tests.
+*/
+static sqlite3 sDb;
+static int nRefSqlite3 = 0;
+
+/*
+** Usage: btree_open FILENAME NCACHE FLAGS
+**
+** Open a new database
+*/
+static int btree_open(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc, nCache, flags;
+ char zBuf[100];
+ if( argc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " FILENAME NCACHE FLAGS\"", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR;
+ nRefSqlite3++;
+ if( nRefSqlite3==1 ){
+ sDb.pVfs = sqlite3_vfs_find(0);
+ sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+ sqlite3_mutex_enter(sDb.mutex);
+ }
+ rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3BtreeSetCacheSize(pBt, nCache);
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_close ID
+**
+** Close the given database.
+*/
+static int btree_close(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ rc = sqlite3BtreeClose(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ nRefSqlite3--;
+ if( nRefSqlite3==0 ){
+ sqlite3_mutex_leave(sDb.mutex);
+ sqlite3_mutex_free(sDb.mutex);
+ sDb.mutex = 0;
+ sDb.pVfs = 0;
+ }
+ return TCL_OK;
+}
+
+
+/*
+** Usage: btree_begin_transaction ID
+**
+** Start a new transaction
+*/
+static int btree_begin_transaction(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeBeginTrans(pBt, 1);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_rollback ID
+**
+** Rollback changes
+*/
+static int btree_rollback(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeRollback(pBt);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_commit ID
+**
+** Commit all changes
+*/
+static int btree_commit(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeCommit(pBt);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_begin_statement ID
+**
+** Start a new statement transaction
+*/
+static int btree_begin_statement(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeBeginStmt(pBt);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_rollback_statement ID
+**
+** Rollback changes
+*/
+static int btree_rollback_statement(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeRollbackStmt(pBt);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_commit_statement ID
+**
+** Commit all changes
+*/
+static int btree_commit_statement(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeCommitStmt(pBt);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_create_table ID FLAGS
+**
+** Create a new table in the database
+*/
+static int btree_create_table(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc, iTable, flags;
+ char zBuf[30];
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID FLAGS\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &flags) ) return TCL_ERROR;
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeCreateTable(pBt, &iTable, flags);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iTable);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_drop_table ID TABLENUM
+**
+** Delete an entire table from the database
+*/
+static int btree_drop_table(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int iTable;
+ int rc;
+ int notUsed1;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID TABLENUM\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeDropTable(pBt, iTable, &notUsed1);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_clear_table ID TABLENUM
+**
+** Remove all entries from the given table but keep the table around.
+*/
+static int btree_clear_table(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int iTable;
+ int rc;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID TABLENUM\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeClearTable(pBt, iTable);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_get_meta ID
+**
+** Return meta data
+*/
+static int btree_get_meta(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ for(i=0; i<SQLITE_N_BTREE_META; i++){
+ char zBuf[30];
+ u32 v;
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeGetMeta(pBt, i, &v);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",v);
+ Tcl_AppendElement(interp, zBuf);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_update_meta ID METADATA...
+**
+** Return meta data
+*/
+static int btree_update_meta(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int rc;
+ int i;
+ int aMeta[SQLITE_N_BTREE_META];
+
+ if( argc!=2+SQLITE_N_BTREE_META ){
+ char zBuf[30];
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",SQLITE_N_BTREE_META);
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID METADATA...\" (METADATA is ", zBuf, " integers)", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ for(i=1; i<SQLITE_N_BTREE_META; i++){
+ if( Tcl_GetInt(interp, argv[i+2], &aMeta[i]) ) return TCL_ERROR;
+ }
+ for(i=1; i<SQLITE_N_BTREE_META; i++){
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeUpdateMeta(pBt, i, aMeta[i]);
+ sqlite3BtreeLeave(pBt);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_pager_stats ID
+**
+** Returns pager statistics
+*/
+static int btree_pager_stats(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int i;
+ int *a;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+
+ /* Normally in this file, with a b-tree handle opened using the
+ ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
+ ** But this function is sometimes called with a btree handle obtained
+ ** from an open SQLite connection (using [btree_from_db]). In this case
+ ** we need to obtain the mutex for the controlling SQLite handle before
+ ** it is safe to call sqlite3BtreeEnter().
+ */
+ sqlite3_mutex_enter(pBt->db->mutex);
+
+ sqlite3BtreeEnter(pBt);
+ a = sqlite3PagerStats(sqlite3BtreePager(pBt));
+ for(i=0; i<11; i++){
+ static char *zName[] = {
+ "ref", "page", "max", "size", "state", "err",
+ "hit", "miss", "ovfl", "read", "write"
+ };
+ char zBuf[100];
+ Tcl_AppendElement(interp, zName[i]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);
+ Tcl_AppendElement(interp, zBuf);
+ }
+ sqlite3BtreeLeave(pBt);
+
+ /* Release the mutex on the SQLite handle that controls this b-tree */
+ sqlite3_mutex_leave(pBt->db->mutex);
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_integrity_check ID ROOT ...
+**
+** Look through every page of the given BTree file to verify correct
+** formatting and linkage. Return a line of text for each problem found.
+** Return an empty string if everything worked.
+*/
+static int btree_integrity_check(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int nRoot;
+ int *aRoot;
+ int i;
+ int nErr;
+ char *zResult;
+
+ if( argc<3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID ROOT ...\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ nRoot = argc-2;
+ aRoot = (int*)sqlite3_malloc( sizeof(int)*(argc-2) );
+ for(i=0; i<argc-2; i++){
+ if( Tcl_GetInt(interp, argv[i+2], &aRoot[i]) ) return TCL_ERROR;
+ }
+#ifndef SQLITE_OMIT_INTEGRITY_CHECK
+ sqlite3BtreeEnter(pBt);
+ zResult = sqlite3BtreeIntegrityCheck(pBt, aRoot, nRoot, 10000, &nErr);
+ sqlite3BtreeLeave(pBt);
+#else
+ zResult = 0;
+#endif
+ sqlite3_free((void*)aRoot);
+ if( zResult ){
+ Tcl_AppendResult(interp, zResult, 0);
+ sqlite3_free(zResult);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_cursor_list ID
+**
+** Print information about all cursors to standard output for debugging.
+*/
+static int btree_cursor_list(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pBt);
+ sqlite3BtreeCursorList(pBt);
+ sqlite3BtreeLeave(pBt);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_cursor ID TABLENUM WRITEABLE
+**
+** Create a new cursor. Return the ID for the cursor.
+*/
+static int btree_cursor(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int iTable;
+ BtCursor *pCur;
+ int rc;
+ int wrFlag;
+ char zBuf[30];
+
+ if( argc!=4 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID TABLENUM WRITEABLE\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+ if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
+ pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
+ memset(pCur, 0, sqlite3BtreeCursorSize());
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
+ sqlite3BtreeLeave(pBt);
+ if( rc ){
+ ckfree((char *)pCur);
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_close_cursor ID
+**
+** Close a cursor opened using btree_cursor.
+*/
+static int btree_close_cursor(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ Btree *pBt;
+ int rc;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ pBt = pCur->pBtree;
+ sqlite3BtreeEnter(pBt);
+ rc = sqlite3BtreeCloseCursor(pCur);
+ sqlite3BtreeLeave(pBt);
+ ckfree((char *)pCur);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_move_to ID KEY
+**
+** Move the cursor to the entry with the given key.
+*/
+static int btree_move_to(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int res;
+ char zBuf[20];
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID KEY\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+ int iKey;
+ if( Tcl_GetInt(interp, argv[2], &iKey) ){
+ sqlite3BtreeLeave(pCur->pBtree);
+ return TCL_ERROR;
+ }
+ rc = sqlite3BtreeMoveto(pCur, 0, 0, iKey, 0, &res);
+ }else{
+ rc = sqlite3BtreeMoveto(pCur, argv[2], 0, strlen(argv[2]), 0, &res);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ if( res<0 ) res = -1;
+ if( res>0 ) res = 1;
+ sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",res);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_delete ID
+**
+** Delete the entry that the cursor is pointing to
+*/
+static int btree_delete(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeDelete(pCur);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_insert ID KEY DATA ?NZERO?
+**
+** Create a new entry with the given key and data. If an entry already
+** exists with the same key the old entry is overwritten.
+*/
+static int btree_insert(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ BtCursor *pCur;
+ int rc;
+ int nZero;
+
+ if( objc!=4 && objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?");
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
+ if( objc==5 ){
+ if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR;
+ }else{
+ nZero = 0;
+ }
+ sqlite3BtreeEnter(pCur->pBtree);
+ if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+ i64 iKey;
+ int len;
+ unsigned char *pBuf;
+ if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ){
+ sqlite3BtreeLeave(pCur->pBtree);
+ return TCL_ERROR;
+ }
+ pBuf = Tcl_GetByteArrayFromObj(objv[3], &len);
+ rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0);
+ }else{
+ int keylen;
+ int dlen;
+ unsigned char *pKBuf;
+ unsigned char *pDBuf;
+ pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen);
+ pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen);
+ rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_next ID
+**
+** Move the cursor to the next entry in the table. Return 0 on success
+** or 1 if the cursor was already on the last entry in the table or if
+** the table is empty.
+*/
+static int btree_next(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int res = 0;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeNext(pCur, &res);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_prev ID
+**
+** Move the cursor to the previous entry in the table. Return 0 on
+** success and 1 if the cursor was already on the first entry in
+** the table or if the table was empty.
+*/
+static int btree_prev(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int res = 0;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreePrevious(pCur, &res);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_first ID
+**
+** Move the cursor to the first entry in the table. Return 0 if the
+** cursor was left point to something and 1 if the table is empty.
+*/
+static int btree_first(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int res = 0;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeFirst(pCur, &res);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_last ID
+**
+** Move the cursor to the last entry in the table. Return 0 if the
+** cursor was left point to something and 1 if the table is empty.
+*/
+static int btree_last(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int res = 0;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeLast(pCur, &res);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_eof ID
+**
+** Return TRUE if the given cursor is not pointing at a valid entry.
+** Return FALSE if the cursor does point to a valid entry.
+*/
+static int btree_eof(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ char zBuf[50];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeEof(pCur);
+ sqlite3BtreeLeave(pCur->pBtree);
+ sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_keysize ID
+**
+** Return the number of bytes of key. For an INTKEY table, this
+** returns the key itself.
+*/
+static int btree_keysize(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ u64 n;
+ char zBuf[50];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ sqlite3BtreeKeySize(pCur, (i64*)&n);
+ sqlite3BtreeLeave(pCur->pBtree);
+ sqlite3_snprintf(sizeof(zBuf),zBuf, "%llu", n);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_key ID
+**
+** Return the key for the entry at which the cursor is pointing.
+*/
+static int btree_key(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ u64 n;
+ char *zBuf;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ sqlite3BtreeKeySize(pCur, (i64*)&n);
+ if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+ char zBuf2[60];
+ sqlite3_snprintf(sizeof(zBuf2),zBuf2, "%llu", n);
+ Tcl_AppendResult(interp, zBuf2, 0);
+ }else{
+ zBuf = sqlite3_malloc( n+1 );
+ rc = sqlite3BtreeKey(pCur, 0, n, zBuf);
+ if( rc ){
+ sqlite3BtreeLeave(pCur->pBtree);
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ return TCL_ERROR;
+ }
+ zBuf[n] = 0;
+ Tcl_AppendResult(interp, zBuf, 0);
+ sqlite3_free(zBuf);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_data ID ?N?
+**
+** Return the data for the entry at which the cursor is pointing.
+*/
+static int btree_data(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ u32 n;
+ char *zBuf;
+
+ if( argc!=2 && argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ if( argc==2 ){
+ sqlite3BtreeDataSize(pCur, &n);
+ }else{
+ n = atoi(argv[2]);
+ }
+ zBuf = sqlite3_malloc( n+1 );
+ rc = sqlite3BtreeData(pCur, 0, n, zBuf);
+ sqlite3BtreeLeave(pCur->pBtree);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ sqlite3_free(zBuf);
+ return TCL_ERROR;
+ }
+ zBuf[n] = 0;
+ Tcl_AppendResult(interp, zBuf, 0);
+ sqlite3_free(zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_fetch_key ID AMT
+**
+** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key.
+** If sqlite3BtreeKeyFetch() fails, return an empty string.
+*/
+static int btree_fetch_key(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int n;
+ int amt;
+ u64 nKey;
+ const char *zBuf;
+ char zStatic[1000];
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID AMT\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ sqlite3BtreeEnter(pCur->pBtree);
+ sqlite3BtreeKeySize(pCur, (i64*)&nKey);
+ zBuf = sqlite3BtreeKeyFetch(pCur, &amt);
+ if( zBuf && amt>=n ){
+ assert( nKey<sizeof(zStatic) );
+ if( n>0 ) nKey = n;
+ memcpy(zStatic, zBuf, (int)nKey);
+ zStatic[nKey] = 0;
+ Tcl_AppendResult(interp, zStatic, 0);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_fetch_data ID AMT
+**
+** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key.
+** If sqlite3BtreeDataFetch() fails, return an empty string.
+*/
+static int btree_fetch_data(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int n;
+ int amt;
+ u32 nData;
+ const char *zBuf;
+ char zStatic[1000];
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID AMT\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ sqlite3BtreeEnter(pCur->pBtree);
+ sqlite3BtreeDataSize(pCur, &nData);
+ zBuf = sqlite3BtreeDataFetch(pCur, &amt);
+ if( zBuf && amt>=n ){
+ assert( nData<sizeof(zStatic) );
+ if( n>0 ) nData = n;
+ memcpy(zStatic, zBuf, (int)nData);
+ zStatic[nData] = 0;
+ Tcl_AppendResult(interp, zStatic, 0);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_payload_size ID
+**
+** Return the number of bytes of payload
+*/
+static int btree_payload_size(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int n2;
+ u64 n1;
+ char zBuf[50];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ sqlite3BtreeEnter(pCur->pBtree);
+ if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+ n1 = 0;
+ }else{
+ sqlite3BtreeKeySize(pCur, (i64*)&n1);
+ }
+ sqlite3BtreeDataSize(pCur, (u32*)&n2);
+ sqlite3BtreeLeave(pCur->pBtree);
+ sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Usage: btree_cursor_info ID ?UP-CNT?
+**
+** Return integers containing information about the entry the
+** cursor is pointing to:
+**
+** aResult[0] = The page number
+** aResult[1] = The entry number
+** aResult[2] = Total number of entries on this page
+** aResult[3] = Cell size (local payload + header)
+** aResult[4] = Number of free bytes on this page
+** aResult[5] = Number of free blocks on the page
+** aResult[6] = Total payload size (local + overflow)
+** aResult[7] = Header size in bytes
+** aResult[8] = Local payload size
+** aResult[9] = Parent page number
+** aResult[10]= Page number of the first overflow page
+*/
+static int btree_cursor_info(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ BtCursor *pCur;
+ int rc;
+ int i, j;
+ int up;
+ int aResult[11];
+ char zBuf[400];
+
+ if( argc!=2 && argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID ?UP-CNT?\"", 0);
+ return TCL_ERROR;
+ }
+ pCur = sqlite3TestTextToPtr(argv[1]);
+ if( argc==3 ){
+ if( Tcl_GetInt(interp, argv[2], &up) ) return TCL_ERROR;
+ }else{
+ up = 0;
+ }
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = sqlite3BtreeCursorInfo(pCur, aResult, up);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ sqlite3BtreeLeave(pCur->pBtree);
+ return TCL_ERROR;
+ }
+ j = 0;
+ for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){
+ sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]);
+ j += strlen(&zBuf[j]);
+ }
+ sqlite3BtreeLeave(pCur->pBtree);
+ Tcl_AppendResult(interp, &zBuf[1], 0);
+ return SQLITE_OK;
+}
+
+/*
+** Copied from btree.c:
+*/
+static u32 t4Get4byte(unsigned char *p){
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+** btree_ovfl_info BTREE CURSOR
+**
+** Given a cursor, return the sequence of pages number that form the
+** overflow pages for the data of the entry that the cursor is point
+** to.
+*/
+static int btree_ovfl_info(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ BtCursor *pCur;
+ Pager *pPager;
+ int rc;
+ int n;
+ int dataSize;
+ u32 pgno;
+ void *pPage;
+ int aResult[11];
+ char zElem[100];
+ Tcl_DString str;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " BTREE CURSOR", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ pCur = sqlite3TestTextToPtr(argv[2]);
+ if( (*(void**)pCur) != (void*)pBt ){
+ Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ",
+ argv[1], 0);
+ return TCL_ERROR;
+ }
+ sqlite3BtreeEnter(pBt);
+ pPager = sqlite3BtreePager(pBt);
+ rc = sqlite3BtreeCursorInfo(pCur, aResult, 0);
+ if( rc ){
+ Tcl_AppendResult(interp, errorName(rc), 0);
+ sqlite3BtreeLeave(pBt);
+ return TCL_ERROR;
+ }
+ dataSize = pBt->pBt->usableSize;
+ Tcl_DStringInit(&str);
+ n = aResult[6] - aResult[8];
+ n = (n + dataSize - 1)/dataSize;
+ pgno = (u32)aResult[10];
+ while( pgno && n-- ){
+ DbPage *pDbPage;
+ sprintf(zElem, "%d", pgno);
+ Tcl_DStringAppendElement(&str, zElem);
+ if( sqlite3PagerGet(pPager, pgno, &pDbPage)!=SQLITE_OK ){
+ Tcl_DStringFree(&str);
+ Tcl_AppendResult(interp, "unable to get page ", zElem, 0);
+ sqlite3BtreeLeave(pBt);
+ return TCL_ERROR;
+ }
+ pPage = sqlite3PagerGetData(pDbPage);
+ pgno = t4Get4byte((unsigned char*)pPage);
+ sqlite3PagerUnref(pDbPage);
+ }
+ sqlite3BtreeLeave(pBt);
+ Tcl_DStringResult(interp, &str);
+ return SQLITE_OK;
+}
+
+/*
+** The command is provided for the purpose of setting breakpoints.
+** in regression test scripts.
+**
+** By setting a GDB breakpoint on this procedure and executing the
+** btree_breakpoint command in a test script, we can stop GDB at
+** the point in the script where the btree_breakpoint command is
+** inserted. This is useful for debugging.
+*/
+static int btree_breakpoint(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ return TCL_OK;
+}
+
+/*
+** usage: varint_test START MULTIPLIER COUNT INCREMENT
+**
+** This command tests the putVarint() and getVarint()
+** routines, both for accuracy and for speed.
+**
+** An integer is written using putVarint() and read back with
+** getVarint() and varified to be unchanged. This repeats COUNT
+** times. The first integer is START*MULTIPLIER. Each iteration
+** increases the integer by INCREMENT.
+**
+** This command returns nothing if it works. It returns an error message
+** if something goes wrong.
+*/
+static int btree_varint_test(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ u32 start, mult, count, incr;
+ u64 in, out;
+ int n1, n2, i, j;
+ unsigned char zBuf[100];
+ if( argc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " START MULTIPLIER COUNT INCREMENT\"", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
+ if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
+ in = start;
+ in *= mult;
+ for(i=0; i<count; i++){
+ char zErr[200];
+ n1 = putVarint(zBuf, in);
+ if( n1>9 || n1<1 ){
+ sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ n2 = getVarint(zBuf, &out);
+ if( n1!=n2 ){
+ sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ if( in!=out ){
+ sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ if( (in & 0xffffffff)==in ){
+ u32 out32;
+ n2 = getVarint32(zBuf, out32);
+ out = out32;
+ if( n1!=n2 ){
+ sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d",
+ n1, n2);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ if( in!=out ){
+ sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
+ in, out);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ }
+
+ /* In order to get realistic timings, run getVarint 19 more times.
+ ** This is because getVarint is called about 20 times more often
+ ** than putVarint.
+ */
+ for(j=0; j<19; j++){
+ getVarint(zBuf, &out);
+ }
+ in += incr;
+ }
+ return TCL_OK;
+}
+
+/*
+** usage: btree_from_db DB-HANDLE
+**
+** This command returns the btree handle for the main database associated
+** with the database-handle passed as the argument. Example usage:
+**
+** sqlite3 db test.db
+** set bt [btree_from_db db]
+*/
+static int btree_from_db(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ char zBuf[100];
+ Tcl_CmdInfo info;
+ sqlite3 *db;
+ Btree *pBt;
+ int iDb = 0;
+
+ if( argc!=2 && argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " DB-HANDLE ?N?\"", 0);
+ return TCL_ERROR;
+ }
+
+ if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
+ Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
+ return TCL_ERROR;
+ }
+ if( argc==3 ){
+ iDb = atoi(argv[2]);
+ }
+
+ db = *((sqlite3 **)info.objClientData);
+ assert( db );
+
+ pBt = db->aDb[iDb].pBt;
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
+ Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+
+/*
+** usage: btree_set_cache_size ID NCACHE
+**
+** Set the size of the cache used by btree $ID.
+*/
+static int btree_set_cache_size(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int nCache;
+ Btree *pBt;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " BT NCACHE\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
+
+ sqlite3_mutex_enter(pBt->db->mutex);
+ sqlite3BtreeEnter(pBt);
+ sqlite3BtreeSetCacheSize(pBt, nCache);
+ sqlite3BtreeLeave(pBt);
+ sqlite3_mutex_leave(pBt->db->mutex);
+
+ return TCL_OK;
+}
+
+/*
+** Usage: btree_ismemdb ID
+**
+** Return true if the B-Tree is in-memory.
+*/
+static int btree_ismemdb(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Btree *pBt;
+ int res;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID\"", 0);
+ return TCL_ERROR;
+ }
+ pBt = sqlite3TestTextToPtr(argv[1]);
+ sqlite3_mutex_enter(pBt->db->mutex);
+ sqlite3BtreeEnter(pBt);
+ res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
+ sqlite3BtreeLeave(pBt);
+ sqlite3_mutex_leave(pBt->db->mutex);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
+ return SQLITE_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest3_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_CmdProc *xProc;
+ } aCmd[] = {
+ { "btree_open", (Tcl_CmdProc*)btree_open },
+ { "btree_close", (Tcl_CmdProc*)btree_close },
+ { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction },
+ { "btree_commit", (Tcl_CmdProc*)btree_commit },
+ { "btree_rollback", (Tcl_CmdProc*)btree_rollback },
+ { "btree_create_table", (Tcl_CmdProc*)btree_create_table },
+ { "btree_drop_table", (Tcl_CmdProc*)btree_drop_table },
+ { "btree_clear_table", (Tcl_CmdProc*)btree_clear_table },
+ { "btree_get_meta", (Tcl_CmdProc*)btree_get_meta },
+ { "btree_update_meta", (Tcl_CmdProc*)btree_update_meta },
+ { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats },
+ { "btree_cursor", (Tcl_CmdProc*)btree_cursor },
+ { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor },
+ { "btree_move_to", (Tcl_CmdProc*)btree_move_to },
+ { "btree_delete", (Tcl_CmdProc*)btree_delete },
+ { "btree_next", (Tcl_CmdProc*)btree_next },
+ { "btree_prev", (Tcl_CmdProc*)btree_prev },
+ { "btree_eof", (Tcl_CmdProc*)btree_eof },
+ { "btree_keysize", (Tcl_CmdProc*)btree_keysize },
+ { "btree_key", (Tcl_CmdProc*)btree_key },
+ { "btree_data", (Tcl_CmdProc*)btree_data },
+ { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key },
+ { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data },
+ { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size },
+ { "btree_first", (Tcl_CmdProc*)btree_first },
+ { "btree_last", (Tcl_CmdProc*)btree_last },
+ { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check },
+ { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint },
+ { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test },
+ { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement },
+ { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement },
+ { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement },
+ { "btree_from_db", (Tcl_CmdProc*)btree_from_db },
+ { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size },
+ { "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info },
+ { "btree_ovfl_info", (Tcl_CmdProc*)btree_ovfl_info },
+ { "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list },
+ { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+
+ /* The btree_insert command is implemented using the tcl 'object'
+ ** interface, not the string interface like the other commands in this
+ ** file. This is so binary data can be inserted into btree tables.
+ */
+ Tcl_CreateObjCommand(interp, "btree_insert", btree_insert, 0, 0);
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test4.c b/third_party/sqlite/src/test4.c
new file mode 100755
index 0000000..de5d3a2
--- /dev/null
+++ b/third_party/sqlite/src/test4.c
@@ -0,0 +1,716 @@
+/*
+** 2003 December 18
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the the SQLite library in a multithreaded environment.
+**
+** $Id: test4.c,v 1.23 2008/07/28 19:34:54 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#if defined(SQLITE_OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sched.h>
+#include <ctype.h>
+
+/*
+** Each thread is controlled by an instance of the following
+** structure.
+*/
+typedef struct Thread Thread;
+struct Thread {
+ /* The first group of fields are writable by the master and read-only
+ ** to the thread. */
+ char *zFilename; /* Name of database file */
+ void (*xOp)(Thread*); /* next operation to do */
+ char *zArg; /* argument usable by xOp */
+ int opnum; /* Operation number */
+ int busy; /* True if this thread is in use */
+
+ /* The next group of fields are writable by the thread but read-only to the
+ ** master. */
+ int completed; /* Number of operations completed */
+ sqlite3 *db; /* Open database */
+ sqlite3_stmt *pStmt; /* Pending operation */
+ char *zErr; /* operation error */
+ char *zStaticErr; /* Static error message */
+ int rc; /* operation return code */
+ int argc; /* number of columns in result */
+ const char *argv[100]; /* result columns */
+ const char *colv[100]; /* result column names */
+};
+
+/*
+** There can be as many as 26 threads running at once. Each is named
+** by a capital letter: A, B, C, ..., Y, Z.
+*/
+#define N_THREAD 26
+static Thread threadset[N_THREAD];
+
+
+/*
+** The main loop for a thread. Threads use busy waiting.
+*/
+static void *thread_main(void *pArg){
+ Thread *p = (Thread*)pArg;
+ if( p->db ){
+ sqlite3_close(p->db);
+ }
+ sqlite3_open(p->zFilename, &p->db);
+ if( SQLITE_OK!=sqlite3_errcode(p->db) ){
+ p->zErr = strdup(sqlite3_errmsg(p->db));
+ sqlite3_close(p->db);
+ p->db = 0;
+ }
+ p->pStmt = 0;
+ p->completed = 1;
+ while( p->opnum<=p->completed ) sched_yield();
+ while( p->xOp ){
+ if( p->zErr && p->zErr!=p->zStaticErr ){
+ sqlite3_free(p->zErr);
+ p->zErr = 0;
+ }
+ (*p->xOp)(p);
+ p->completed++;
+ while( p->opnum<=p->completed ) sched_yield();
+ }
+ if( p->pStmt ){
+ sqlite3_finalize(p->pStmt);
+ p->pStmt = 0;
+ }
+ if( p->db ){
+ sqlite3_close(p->db);
+ p->db = 0;
+ }
+ if( p->zErr && p->zErr!=p->zStaticErr ){
+ sqlite3_free(p->zErr);
+ p->zErr = 0;
+ }
+ p->completed++;
+ sqlite3_thread_cleanup();
+ return 0;
+}
+
+/*
+** Get a thread ID which is an upper case letter. Return the index.
+** If the argument is not a valid thread ID put an error message in
+** the interpreter and return -1.
+*/
+static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
+ if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
+ Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
+ return -1;
+ }
+ return zArg[0] - 'A';
+}
+
+/*
+** Usage: thread_create NAME FILENAME
+**
+** NAME should be an upper case letter. Start the thread running with
+** an open connection to the given database.
+*/
+static int tcl_thread_create(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ pthread_t x;
+ int rc;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID FILENAME", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( threadset[i].busy ){
+ Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
+ return TCL_ERROR;
+ }
+ threadset[i].busy = 1;
+ sqlite3_free(threadset[i].zFilename);
+ threadset[i].zFilename = sqlite3DbStrDup(0, argv[2]);
+ threadset[i].opnum = 1;
+ threadset[i].completed = 0;
+ rc = pthread_create(&x, 0, thread_main, &threadset[i]);
+ if( rc ){
+ Tcl_AppendResult(interp, "failed to create the thread", 0);
+ sqlite3_free(threadset[i].zFilename);
+ threadset[i].busy = 0;
+ return TCL_ERROR;
+ }
+ pthread_detach(x);
+ return TCL_OK;
+}
+
+/*
+** Wait for a thread to reach its idle state.
+*/
+static void thread_wait(Thread *p){
+ while( p->opnum>p->completed ) sched_yield();
+}
+
+/*
+** Usage: thread_wait ID
+**
+** Wait on thread ID to reach its idle state.
+*/
+static int tcl_thread_wait(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ return TCL_OK;
+}
+
+/*
+** Stop a thread.
+*/
+static void stop_thread(Thread *p){
+ thread_wait(p);
+ p->xOp = 0;
+ p->opnum++;
+ thread_wait(p);
+ sqlite3_free(p->zArg);
+ p->zArg = 0;
+ sqlite3_free(p->zFilename);
+ p->zFilename = 0;
+ p->busy = 0;
+}
+
+/*
+** Usage: thread_halt ID
+**
+** Cause a thread to shut itself down. Wait for the shutdown to be
+** completed. If ID is "*" then stop all threads.
+*/
+static int tcl_thread_halt(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ if( argv[1][0]=='*' && argv[1][1]==0 ){
+ for(i=0; i<N_THREAD; i++){
+ if( threadset[i].busy ) stop_thread(&threadset[i]);
+ }
+ }else{
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ stop_thread(&threadset[i]);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_argc ID
+**
+** Wait on the most recent thread_step to complete, then return the
+** number of columns in the result set.
+*/
+static int tcl_thread_argc(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ sprintf(zBuf, "%d", threadset[i].argc);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_argv ID N
+**
+** Wait on the most recent thread_step to complete, then return the
+** value of the N-th columns in the result set.
+*/
+static int tcl_thread_argv(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ int n;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID N", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ thread_wait(&threadset[i]);
+ if( n<0 || n>=threadset[i].argc ){
+ Tcl_AppendResult(interp, "column number out of range", 0);
+ return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, threadset[i].argv[n], 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_colname ID N
+**
+** Wait on the most recent thread_step to complete, then return the
+** name of the N-th columns in the result set.
+*/
+static int tcl_thread_colname(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ int n;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID N", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ thread_wait(&threadset[i]);
+ if( n<0 || n>=threadset[i].argc ){
+ Tcl_AppendResult(interp, "column number out of range", 0);
+ return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, threadset[i].colv[n], 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_result ID
+**
+** Wait on the most recent operation to complete, then return the
+** result code from that operation.
+*/
+static int tcl_thread_result(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ const char *zName;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ switch( threadset[i].rc ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
+ case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
+ case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
+ case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
+ case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
+ case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
+ case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
+ case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
+ case SQLITE_ROW: zName = "SQLITE_ROW"; break;
+ case SQLITE_DONE: zName = "SQLITE_DONE"; break;
+ default: zName = "SQLITE_Unknown"; break;
+ }
+ Tcl_AppendResult(interp, zName, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_error ID
+**
+** Wait on the most recent operation to complete, then return the
+** error string.
+*/
+static int tcl_thread_error(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ Tcl_AppendResult(interp, threadset[i].zErr, 0);
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to compile an SQL statement.
+*/
+static void do_compile(Thread *p){
+ if( p->db==0 ){
+ p->zErr = p->zStaticErr = "no database is open";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ if( p->pStmt ){
+ sqlite3_finalize(p->pStmt);
+ p->pStmt = 0;
+ }
+ p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
+}
+
+/*
+** Usage: thread_compile ID SQL
+**
+** Compile a new virtual machine.
+*/
+static int tcl_thread_compile(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID SQL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ threadset[i].xOp = do_compile;
+ sqlite3_free(threadset[i].zArg);
+ threadset[i].zArg = sqlite3DbStrDup(0, argv[2]);
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to step the virtual machine.
+*/
+static void do_step(Thread *p){
+ int i;
+ if( p->pStmt==0 ){
+ p->zErr = p->zStaticErr = "no virtual machine available";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ p->rc = sqlite3_step(p->pStmt);
+ if( p->rc==SQLITE_ROW ){
+ p->argc = sqlite3_column_count(p->pStmt);
+ for(i=0; i<sqlite3_data_count(p->pStmt); i++){
+ p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
+ }
+ for(i=0; i<p->argc; i++){
+ p->colv[i] = sqlite3_column_name(p->pStmt, i);
+ }
+ }
+}
+
+/*
+** Usage: thread_step ID
+**
+** Advance the virtual machine by one step
+*/
+static int tcl_thread_step(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " IDL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ threadset[i].xOp = do_step;
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to finalize a virtual machine.
+*/
+static void do_finalize(Thread *p){
+ if( p->pStmt==0 ){
+ p->zErr = p->zStaticErr = "no virtual machine available";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ p->rc = sqlite3_finalize(p->pStmt);
+ p->pStmt = 0;
+}
+
+/*
+** Usage: thread_finalize ID
+**
+** Finalize the virtual machine.
+*/
+static int tcl_thread_finalize(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " IDL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ threadset[i].xOp = do_finalize;
+ sqlite3_free(threadset[i].zArg);
+ threadset[i].zArg = 0;
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_swap ID ID
+**
+** Interchange the sqlite* pointer between two threads.
+*/
+static int tcl_thread_swap(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i, j;
+ sqlite3 *temp;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID1 ID2", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ j = parse_thread_id(interp, argv[2]);
+ if( j<0 ) return TCL_ERROR;
+ if( !threadset[j].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[j]);
+ temp = threadset[i].db;
+ threadset[i].db = threadset[j].db;
+ threadset[j].db = temp;
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_db_get ID
+**
+** Return the database connection pointer for the given thread. Then
+** remove the pointer from the thread itself. Afterwards, the thread
+** can be stopped and the connection can be used by the main thread.
+*/
+static int tcl_thread_db_get(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ char zBuf[100];
+ extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
+ threadset[i].db = 0;
+ Tcl_AppendResult(interp, zBuf, (char*)0);
+ return TCL_OK;
+}
+
+/*
+** Usage: thread_stmt_get ID
+**
+** Return the database stmt pointer for the given thread. Then
+** remove the pointer from the thread itself.
+*/
+static int tcl_thread_stmt_get(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ char zBuf[100];
+ extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_thread_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ thread_wait(&threadset[i]);
+ sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
+ threadset[i].pStmt = 0;
+ Tcl_AppendResult(interp, zBuf, (char*)0);
+ return TCL_OK;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest4_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_CmdProc *xProc;
+ } aCmd[] = {
+ { "thread_create", (Tcl_CmdProc*)tcl_thread_create },
+ { "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
+ { "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
+ { "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
+ { "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
+ { "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
+ { "thread_result", (Tcl_CmdProc*)tcl_thread_result },
+ { "thread_error", (Tcl_CmdProc*)tcl_thread_error },
+ { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
+ { "thread_step", (Tcl_CmdProc*)tcl_thread_step },
+ { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
+ { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
+ { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get },
+ { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ return TCL_OK;
+}
+#else
+int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
+#endif /* SQLITE_OS_UNIX */
diff --git a/third_party/sqlite/src/test5.c b/third_party/sqlite/src/test5.c
new file mode 100755
index 0000000..cbb8840
--- /dev/null
+++ b/third_party/sqlite/src/test5.c
@@ -0,0 +1,217 @@
+/*
+** 2001 September 15
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the utf.c module in SQLite. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library. Specifically, the code in this file
+** is used for testing the SQLite routines for converting between
+** the various supported unicode encodings.
+**
+** $Id: test5.c,v 1.21 2008/07/28 19:34:54 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** The first argument is a TCL UTF-8 string. Return the byte array
+** object with the encoded representation of the string, including
+** the NULL terminator.
+*/
+static int binarize(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int len;
+ char *bytes;
+ Tcl_Obj *pRet;
+ assert(objc==2);
+
+ bytes = Tcl_GetStringFromObj(objv[1], &len);
+ pRet = Tcl_NewByteArrayObj((u8*)bytes, len+1);
+ Tcl_SetObjResult(interp, pRet);
+ return TCL_OK;
+}
+
+/*
+** Usage: test_value_overhead <repeat-count> <do-calls>.
+**
+** This routine is used to test the overhead of calls to
+** sqlite3_value_text(), on a value that contains a UTF-8 string. The idea
+** is to figure out whether or not it is a problem to use sqlite3_value
+** structures with collation sequence functions.
+**
+** If <do-calls> is 0, then the calls to sqlite3_value_text() are not
+** actually made.
+*/
+static int test_value_overhead(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int do_calls;
+ int repeat_count;
+ int i;
+ Mem val;
+ const char *zVal;
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " <repeat-count> <do-calls>", 0);
+ return TCL_ERROR;
+ }
+
+ if( Tcl_GetIntFromObj(interp, objv[1], &repeat_count) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &do_calls) ) return TCL_ERROR;
+
+ val.flags = MEM_Str|MEM_Term|MEM_Static;
+ val.z = "hello world";
+ val.type = SQLITE_TEXT;
+ val.enc = SQLITE_UTF8;
+
+ for(i=0; i<repeat_count; i++){
+ if( do_calls ){
+ zVal = (char*)sqlite3_value_text(&val);
+ }
+ }
+
+ return TCL_OK;
+}
+
+static u8 name_to_enc(Tcl_Interp *interp, Tcl_Obj *pObj){
+ struct EncName {
+ char *zName;
+ u8 enc;
+ } encnames[] = {
+ { "UTF8", SQLITE_UTF8 },
+ { "UTF16LE", SQLITE_UTF16LE },
+ { "UTF16BE", SQLITE_UTF16BE },
+ { "UTF16", SQLITE_UTF16NATIVE },
+ { 0, 0 }
+ };
+ struct EncName *pEnc;
+ char *z = Tcl_GetString(pObj);
+ for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
+ if( 0==sqlite3StrICmp(z, pEnc->zName) ){
+ break;
+ }
+ }
+ if( !pEnc->enc ){
+ Tcl_AppendResult(interp, "No such encoding: ", z, 0);
+ }
+ return pEnc->enc;
+}
+
+/*
+** Usage: test_translate <string/blob> <from enc> <to enc> ?<transient>?
+**
+*/
+static int test_translate(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ u8 enc_from;
+ u8 enc_to;
+ sqlite3_value *pVal;
+
+ char *z;
+ int len;
+ void (*xDel)(void *p) = SQLITE_STATIC;
+
+ if( objc!=4 && objc!=5 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0),
+ " <string/blob> <from enc> <to enc>", 0
+ );
+ return TCL_ERROR;
+ }
+ if( objc==5 ){
+ xDel = sqlite3_free;
+ }
+
+ enc_from = name_to_enc(interp, objv[2]);
+ if( !enc_from ) return TCL_ERROR;
+ enc_to = name_to_enc(interp, objv[3]);
+ if( !enc_to ) return TCL_ERROR;
+
+ pVal = sqlite3ValueNew(0);
+
+ if( enc_from==SQLITE_UTF8 ){
+ z = Tcl_GetString(objv[1]);
+ if( objc==5 ){
+ z = sqlite3DbStrDup(0, z);
+ }
+ sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
+ }else{
+ z = (char*)Tcl_GetByteArrayFromObj(objv[1], &len);
+ if( objc==5 ){
+ char *zTmp = z;
+ z = sqlite3_malloc(len);
+ memcpy(z, zTmp, len);
+ }
+ sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
+ }
+
+ z = (char *)sqlite3ValueText(pVal, enc_to);
+ len = sqlite3ValueBytes(pVal, enc_to) + (enc_to==SQLITE_UTF8?1:2);
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((u8*)z, len));
+
+ sqlite3ValueFree(pVal);
+
+ return TCL_OK;
+}
+
+/*
+** Usage: translate_selftest
+**
+** Call sqlite3UtfSelfTest() to run the internal tests for unicode
+** translation. If there is a problem an assert() will fail.
+**/
+void sqlite3UtfSelfTest();
+static int test_translate_selftest(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3UtfSelfTest();
+#endif
+ return SQLITE_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest5_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aCmd[] = {
+ { "binarize", (Tcl_ObjCmdProc*)binarize },
+ { "test_value_overhead", (Tcl_ObjCmdProc*)test_value_overhead },
+ { "test_translate", (Tcl_ObjCmdProc*)test_translate },
+ { "translate_selftest", (Tcl_ObjCmdProc*)test_translate_selftest},
+ };
+ int i;
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ return SQLITE_OK;
+}
diff --git a/third_party/sqlite/src/test6.c b/third_party/sqlite/src/test6.c
new file mode 100755
index 0000000..8805cb8
--- /dev/null
+++ b/third_party/sqlite/src/test6.c
@@ -0,0 +1,881 @@
+/*
+** 2004 May 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 contains code that modified the OS layer in order to simulate
+** the effect on the database file of an OS crash or power failure. This
+** is used to test the ability of SQLite to recover from those situations.
+**
+** $Id: test6.c,v 1.39 2008/06/06 11:11:26 danielk1977 Exp $
+*/
+#if SQLITE_TEST /* This file is used for testing only */
+#include "sqliteInt.h"
+#include "tcl.h"
+
+#ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */
+
+/* #define TRACE_CRASHTEST */
+
+typedef struct CrashFile CrashFile;
+typedef struct CrashGlobal CrashGlobal;
+typedef struct WriteBuffer WriteBuffer;
+
+/*
+** Method:
+**
+** This layer is implemented as a wrapper around the "real"
+** sqlite3_file object for the host system. Each time data is
+** written to the file object, instead of being written to the
+** underlying file, the write operation is stored in an in-memory
+** structure (type WriteBuffer). This structure is placed at the
+** end of a global ordered list (the write-list).
+**
+** When data is read from a file object, the requested region is
+** first retrieved from the real file. The write-list is then
+** traversed and data copied from any overlapping WriteBuffer
+** structures to the output buffer. i.e. a read() operation following
+** one or more write() operations works as expected, even if no
+** data has actually been written out to the real file.
+**
+** When a fsync() operation is performed, an operating system crash
+** may be simulated, in which case exit(-1) is called (the call to
+** xSync() never returns). Whether or not a crash is simulated,
+** the data associated with a subset of the WriteBuffer structures
+** stored in the write-list is written to the real underlying files
+** and the entries removed from the write-list. If a crash is simulated,
+** a subset of the buffers may be corrupted before the data is written.
+**
+** The exact subset of the write-list written and/or corrupted is
+** determined by the simulated device characteristics and sector-size.
+**
+** "Normal" mode:
+**
+** Normal mode is used when the simulated device has none of the
+** SQLITE_IOCAP_XXX flags set.
+**
+** In normal mode, if the fsync() is not a simulated crash, the
+** write-list is traversed from beginning to end. Each WriteBuffer
+** structure associated with the file handle used to call xSync()
+** is written to the real file and removed from the write-list.
+**
+** If a crash is simulated, one of the following takes place for
+** each WriteBuffer in the write-list, regardless of which
+** file-handle it is associated with:
+**
+** 1. The buffer is correctly written to the file, just as if
+** a crash were not being simulated.
+**
+** 2. Nothing is done.
+**
+** 3. Garbage data is written to all sectors of the file that
+** overlap the region specified by the WriteBuffer. Or garbage
+** data is written to some contiguous section within the
+** overlapped sectors.
+**
+** Device Characteristic flag handling:
+**
+** If the IOCAP_ATOMIC flag is set, then option (3) above is
+** never selected.
+**
+** If the IOCAP_ATOMIC512 flag is set, and the WriteBuffer represents
+** an aligned write() of an integer number of 512 byte regions, then
+** option (3) above is never selected. Instead, each 512 byte region
+** is either correctly written or left completely untouched. Similar
+** logic governs the behaviour if any of the other ATOMICXXX flags
+** is set.
+**
+** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set
+** and a crash is being simulated, then an entry of the write-list is
+** selected at random. Everything in the list after the selected entry
+** is discarded before processing begins.
+**
+** If IOCAP_SEQUENTIAL is set and a crash is being simulated, option
+** (1) is selected for all write-list entries except the last. If a
+** crash is not being simulated, then all entries in the write-list
+** that occur before at least one write() on the file-handle specified
+** as part of the xSync() are written to their associated real files.
+**
+** If IOCAP_SAFEAPPEND is set and the first byte written by the write()
+** operation is one byte past the current end of the file, then option
+** (1) is always selected.
+*/
+
+/*
+** Each write operation in the write-list is represented by an instance
+** of the following structure.
+**
+** If zBuf is 0, then this structure represents a call to xTruncate(),
+** not xWrite(). In that case, iOffset is the size that the file is
+** truncated to.
+*/
+struct WriteBuffer {
+ i64 iOffset; /* Byte offset of the start of this write() */
+ int nBuf; /* Number of bytes written */
+ u8 *zBuf; /* Pointer to copy of written data */
+ CrashFile *pFile; /* File this write() applies to */
+
+ WriteBuffer *pNext; /* Next in CrashGlobal.pWriteList */
+};
+
+struct CrashFile {
+ const sqlite3_io_methods *pMethod; /* Must be first */
+ sqlite3_file *pRealFile; /* Underlying "real" file handle */
+ char *zName;
+
+ /* Cache of the entire file. This is used to speed up OsRead() and
+ ** OsFileSize() calls. Although both could be done by traversing the
+ ** write-list, in practice this is impractically slow.
+ */
+ int iSize; /* Size of file in bytes */
+ int nData; /* Size of buffer allocated at zData */
+ u8 *zData; /* Buffer containing file contents */
+};
+
+struct CrashGlobal {
+ WriteBuffer *pWriteList; /* Head of write-list */
+ WriteBuffer *pWriteListEnd; /* End of write-list */
+
+ int iSectorSize; /* Value of simulated sector size */
+ int iDeviceCharacteristics; /* Value of simulated device characteristics */
+
+ int iCrash; /* Crash on the iCrash'th call to xSync() */
+ char zCrashFile[500]; /* Crash during an xSync() on this file */
+};
+
+static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0};
+
+/*
+** Set this global variable to 1 to enable crash testing.
+*/
+static int sqlite3CrashTestEnable = 0;
+
+static void *crash_malloc(int nByte){
+ return (void *)Tcl_Alloc((size_t)nByte);
+}
+static void crash_free(void *p){
+ Tcl_Free(p);
+}
+static void *crash_realloc(void *p, int n){
+ return (void *)Tcl_Realloc(p, (size_t)n);
+}
+
+/*
+** Flush the write-list as if xSync() had been called on file handle
+** pFile. If isCrash is true, simulate a crash.
+*/
+static int writeListSync(CrashFile *pFile, int isCrash){
+ int rc = SQLITE_OK;
+ int iDc = g.iDeviceCharacteristics;
+
+ WriteBuffer *pWrite;
+ WriteBuffer **ppPtr;
+
+ /* If this is not a crash simulation, set pFinal to point to the
+ ** last element of the write-list that is associated with file handle
+ ** pFile.
+ **
+ ** If this is a crash simulation, set pFinal to an arbitrarily selected
+ ** element of the write-list.
+ */
+ WriteBuffer *pFinal = 0;
+ if( !isCrash ){
+ for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
+ if( pWrite->pFile==pFile ){
+ pFinal = pWrite;
+ }
+ }
+ }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
+ int nWrite = 0;
+ int iFinal;
+ for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
+ sqlite3_randomness(sizeof(int), &iFinal);
+ iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
+ for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
+ pFinal = pWrite;
+ }
+
+#ifdef TRACE_CRASHTEST
+ printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
+#endif
+
+ ppPtr = &g.pWriteList;
+ for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
+ sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
+
+ /* (eAction==1) -> write block out normally,
+ ** (eAction==2) -> do nothing,
+ ** (eAction==3) -> trash sectors.
+ */
+ int eAction = 0;
+ if( !isCrash ){
+ eAction = 2;
+ if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
+ eAction = 1;
+ }
+ }else{
+ char random;
+ sqlite3_randomness(1, &random);
+
+ /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag
+ ** is set or this is an OsTruncate(), not an Oswrite().
+ */
+ if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
+ random &= 0x01;
+ }
+
+ /* If IOCAP_SEQUENTIAL is set and this is not the final entry
+ ** in the truncated write-list, always select option 1 (write
+ ** out correctly).
+ */
+ if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
+ random = 0;
+ }
+
+ /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
+ ** an append (first byte of the written region is 1 byte past the
+ ** current EOF), always select option 1 (write out correctly).
+ */
+ if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
+ i64 iSize;
+ sqlite3OsFileSize(pRealFile, &iSize);
+ if( iSize==pWrite->iOffset ){
+ random = 0;
+ }
+ }
+
+ if( (random&0x06)==0x06 ){
+ eAction = 3;
+ }else{
+ eAction = ((random&0x01)?2:1);
+ }
+ }
+
+ switch( eAction ){
+ case 1: { /* Write out correctly */
+ if( pWrite->zBuf ){
+ rc = sqlite3OsWrite(
+ pRealFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset
+ );
+ }else{
+ rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
+ }
+ *ppPtr = pWrite->pNext;
+#ifdef TRACE_CRASHTEST
+ if( isCrash ){
+ printf("Writing %d bytes @ %d (%s)\n",
+ pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
+ );
+ }
+#endif
+ crash_free(pWrite);
+ break;
+ }
+ case 2: { /* Do nothing */
+ ppPtr = &pWrite->pNext;
+#ifdef TRACE_CRASHTEST
+ if( isCrash ){
+ printf("Omiting %d bytes @ %d (%s)\n",
+ pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
+ );
+ }
+#endif
+ break;
+ }
+ case 3: { /* Trash sectors */
+ u8 *zGarbage;
+ int iFirst = (pWrite->iOffset/g.iSectorSize);
+ int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;
+
+ assert(pWrite->zBuf);
+
+#ifdef TRACE_CRASHTEST
+ printf("Trashing %d sectors @ sector %d (%s)\n",
+ 1+iLast-iFirst, iFirst, pWrite->pFile->zName
+ );
+#endif
+
+ zGarbage = crash_malloc(g.iSectorSize);
+ if( zGarbage ){
+ sqlite3_int64 i;
+ for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
+ sqlite3_randomness(g.iSectorSize, zGarbage);
+ rc = sqlite3OsWrite(
+ pRealFile, zGarbage, g.iSectorSize, i*g.iSectorSize
+ );
+ }
+ crash_free(zGarbage);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ ppPtr = &pWrite->pNext;
+ break;
+ }
+
+ default:
+ assert(!"Cannot happen");
+ }
+
+ if( pWrite==pFinal ) break;
+ }
+
+ if( rc==SQLITE_OK && isCrash ){
+ exit(-1);
+ }
+
+ for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext);
+ g.pWriteListEnd = pWrite;
+
+ return rc;
+}
+
+/*
+** Add an entry to the end of the write-list.
+*/
+static int writeListAppend(
+ sqlite3_file *pFile,
+ sqlite3_int64 iOffset,
+ const u8 *zBuf,
+ int nBuf
+){
+ WriteBuffer *pNew;
+
+ assert((zBuf && nBuf) || (!nBuf && !zBuf));
+
+ pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf);
+ if( pNew==0 ){
+ fprintf(stderr, "out of memory in the crash simulator\n");
+ }
+ memset(pNew, 0, sizeof(WriteBuffer)+nBuf);
+ pNew->iOffset = iOffset;
+ pNew->nBuf = nBuf;
+ pNew->pFile = (CrashFile *)pFile;
+ if( zBuf ){
+ pNew->zBuf = (u8 *)&pNew[1];
+ memcpy(pNew->zBuf, zBuf, nBuf);
+ }
+
+ if( g.pWriteList ){
+ assert(g.pWriteListEnd);
+ g.pWriteListEnd->pNext = pNew;
+ }else{
+ g.pWriteList = pNew;
+ }
+ g.pWriteListEnd = pNew;
+
+ return SQLITE_OK;
+}
+
+/*
+** Close a crash-file.
+*/
+static int cfClose(sqlite3_file *pFile){
+ CrashFile *pCrash = (CrashFile *)pFile;
+ writeListSync(pCrash, 0);
+ sqlite3OsClose(pCrash->pRealFile);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a crash-file.
+*/
+static int cfRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ CrashFile *pCrash = (CrashFile *)pFile;
+
+ /* Check the file-size to see if this is a short-read */
+ if( pCrash->iSize<(iOfst+iAmt) ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+
+ memcpy(zBuf, &pCrash->zData[iOfst], iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write data to a crash-file.
+*/
+static int cfWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ CrashFile *pCrash = (CrashFile *)pFile;
+ if( iAmt+iOfst>pCrash->iSize ){
+ pCrash->iSize = iAmt+iOfst;
+ }
+ while( pCrash->iSize>pCrash->nData ){
+ u8 *zNew;
+ int nNew = (pCrash->nData*2) + 4096;
+ zNew = crash_realloc(pCrash->zData, nNew);
+ if( !zNew ){
+ return SQLITE_NOMEM;
+ }
+ memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData);
+ pCrash->nData = nNew;
+ pCrash->zData = zNew;
+ }
+ memcpy(&pCrash->zData[iOfst], zBuf, iAmt);
+ return writeListAppend(pFile, iOfst, zBuf, iAmt);
+}
+
+/*
+** Truncate a crash-file.
+*/
+static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ CrashFile *pCrash = (CrashFile *)pFile;
+ assert(size>=0);
+ if( pCrash->iSize>size ){
+ pCrash->iSize = size;
+ }
+ return writeListAppend(pFile, size, 0, 0);
+}
+
+/*
+** Sync a crash-file.
+*/
+static int cfSync(sqlite3_file *pFile, int flags){
+ CrashFile *pCrash = (CrashFile *)pFile;
+ int isCrash = 0;
+
+ const char *zName = pCrash->zName;
+ const char *zCrashFile = g.zCrashFile;
+ int nName = strlen(zName);
+ int nCrashFile = strlen(zCrashFile);
+
+ if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
+ nCrashFile--;
+ if( nName>nCrashFile ) nName = nCrashFile;
+ }
+
+ if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){
+ if( (--g.iCrash)==0 ) isCrash = 1;
+ }
+
+ return writeListSync(pCrash, isCrash);
+}
+
+/*
+** Return the current file-size of the crash-file.
+*/
+static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ CrashFile *pCrash = (CrashFile *)pFile;
+ *pSize = (i64)pCrash->iSize;
+ return SQLITE_OK;
+}
+
+/*
+** Calls related to file-locks are passed on to the real file handle.
+*/
+static int cfLock(sqlite3_file *pFile, int eLock){
+ return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock);
+}
+static int cfUnlock(sqlite3_file *pFile, int eLock){
+ return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock);
+}
+static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);
+}
+static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
+ return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);
+}
+
+/*
+** The xSectorSize() and xDeviceCharacteristics() functions return
+** the global values configured by the [sqlite_crashparams] tcl
+* interface.
+*/
+static int cfSectorSize(sqlite3_file *pFile){
+ return g.iSectorSize;
+}
+static int cfDeviceCharacteristics(sqlite3_file *pFile){
+ return g.iDeviceCharacteristics;
+}
+
+static const sqlite3_io_methods CrashFileVtab = {
+ 1, /* iVersion */
+ cfClose, /* xClose */
+ cfRead, /* xRead */
+ cfWrite, /* xWrite */
+ cfTruncate, /* xTruncate */
+ cfSync, /* xSync */
+ cfFileSize, /* xFileSize */
+ cfLock, /* xLock */
+ cfUnlock, /* xUnlock */
+ cfCheckReservedLock, /* xCheckReservedLock */
+ cfFileControl, /* xFileControl */
+ cfSectorSize, /* xSectorSize */
+ cfDeviceCharacteristics /* xDeviceCharacteristics */
+};
+
+/*
+** Application data for the crash VFS
+*/
+struct crashAppData {
+ sqlite3_vfs *pOrig; /* Wrapped vfs structure */
+};
+
+/*
+** Open a crash-file file handle.
+**
+** The caller will have allocated pVfs->szOsFile bytes of space
+** at pFile. This file uses this space for the CrashFile structure
+** and allocates space for the "real" file structure using
+** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
+** equal or greater than sizeof(CrashFile).
+*/
+static int cfOpen(
+ sqlite3_vfs *pCfVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ int rc;
+ CrashFile *pWrapper = (CrashFile *)pFile;
+ sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
+
+ memset(pWrapper, 0, sizeof(CrashFile));
+ rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);
+
+ if( rc==SQLITE_OK ){
+ i64 iSize;
+ pWrapper->pMethod = &CrashFileVtab;
+ pWrapper->zName = (char *)zName;
+ pWrapper->pRealFile = pReal;
+ rc = sqlite3OsFileSize(pReal, &iSize);
+ pWrapper->iSize = (int)iSize;
+ }
+ if( rc==SQLITE_OK ){
+ pWrapper->nData = (4096 + pWrapper->iSize);
+ pWrapper->zData = crash_malloc(pWrapper->nData);
+ if( pWrapper->zData ){
+ memset(pWrapper->zData, 0, pWrapper->nData);
+ rc = sqlite3OsRead(pReal, pWrapper->zData, pWrapper->iSize, 0);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc!=SQLITE_OK && pWrapper->pMethod ){
+ sqlite3OsClose(pFile);
+ }
+ return rc;
+}
+
+static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xDelete(pVfs, zPath, dirSync);
+}
+static int cfAccess(
+ sqlite3_vfs *pCfVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xAccess(pVfs, zPath, flags, pResOut);
+}
+static int cfFullPathname(
+ sqlite3_vfs *pCfVfs,
+ const char *zPath,
+ int nPathOut,
+ char *zPathOut
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
+}
+static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xDlOpen(pVfs, zPath);
+}
+static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ pVfs->xDlError(pVfs, nByte, zErrMsg);
+}
+static void *cfDlSym(sqlite3_vfs *pCfVfs, void *pHandle, const char *zSymbol){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xDlSym(pVfs, pHandle, zSymbol);
+}
+static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ pVfs->xDlClose(pVfs, pHandle);
+}
+static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+}
+static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xSleep(pVfs, nMicro);
+}
+static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+ return pVfs->xCurrentTime(pVfs, pTimeOut);
+}
+
+static int processDevSymArgs(
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[],
+ int *piDeviceChar,
+ int *piSectorSize
+){
+ struct DeviceFlag {
+ char *zName;
+ int iValue;
+ } aFlag[] = {
+ { "atomic", SQLITE_IOCAP_ATOMIC },
+ { "atomic512", SQLITE_IOCAP_ATOMIC512 },
+ { "atomic1k", SQLITE_IOCAP_ATOMIC1K },
+ { "atomic2k", SQLITE_IOCAP_ATOMIC2K },
+ { "atomic4k", SQLITE_IOCAP_ATOMIC4K },
+ { "atomic8k", SQLITE_IOCAP_ATOMIC8K },
+ { "atomic16k", SQLITE_IOCAP_ATOMIC16K },
+ { "atomic32k", SQLITE_IOCAP_ATOMIC32K },
+ { "atomic64k", SQLITE_IOCAP_ATOMIC64K },
+ { "sequential", SQLITE_IOCAP_SEQUENTIAL },
+ { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
+ { 0, 0 }
+ };
+
+ int i;
+ int iDc = 0;
+ int iSectorSize = 0;
+ int setSectorsize = 0;
+ int setDeviceChar = 0;
+
+ for(i=0; i<objc; i+=2){
+ int nOpt;
+ char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt);
+
+ if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt))
+ && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt))
+ ){
+ Tcl_AppendResult(interp,
+ "Bad option: \"", zOpt,
+ "\" - must be \"-characteristics\" or \"-sectorsize\"", 0
+ );
+ return TCL_ERROR;
+ }
+ if( i==objc-1 ){
+ Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0);
+ return TCL_ERROR;
+ }
+
+ if( zOpt[1]=='s' ){
+ if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){
+ return TCL_ERROR;
+ }
+ setSectorsize = 1;
+ }else{
+ int j;
+ Tcl_Obj **apObj;
+ int nObj;
+ if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){
+ return TCL_ERROR;
+ }
+ for(j=0; j<nObj; j++){
+ int rc;
+ int iChoice;
+ Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]);
+ Tcl_IncrRefCount(pFlag);
+ Tcl_UtfToLower(Tcl_GetString(pFlag));
+
+ rc = Tcl_GetIndexFromObjStruct(
+ interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice
+ );
+ Tcl_DecrRefCount(pFlag);
+ if( rc ){
+ return TCL_ERROR;
+ }
+
+ iDc |= aFlag[iChoice].iValue;
+ }
+ setDeviceChar = 1;
+ }
+ }
+
+ if( setDeviceChar ){
+ *piDeviceChar = iDc;
+ }
+ if( setSectorsize ){
+ *piSectorSize = iSectorSize;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite_crash_enable ENABLE
+**
+** Parameter ENABLE must be a boolean value. If true, then the "crash"
+** vfs is added to the system. If false, it is removed.
+*/
+static int crashEnableCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int isEnable;
+ static sqlite3_vfs crashVfs = {
+ 1, /* iVersion */
+ 0, /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ "crash", /* zName */
+ 0, /* pAppData */
+
+ cfOpen, /* xOpen */
+ cfDelete, /* xDelete */
+ cfAccess, /* xAccess */
+ cfFullPathname, /* xFullPathname */
+ cfDlOpen, /* xDlOpen */
+ cfDlError, /* xDlError */
+ cfDlSym, /* xDlSym */
+ cfDlClose, /* xDlClose */
+ cfRandomness, /* xRandomness */
+ cfSleep, /* xSleep */
+ cfCurrentTime /* xCurrentTime */
+ };
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "ENABLE");
+ return TCL_ERROR;
+ }
+
+ if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){
+ return TCL_ERROR;
+ }
+
+ if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){
+ return TCL_OK;
+ }
+
+ if( crashVfs.pAppData==0 ){
+ sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0);
+ crashVfs.mxPathname = pOriginalVfs->mxPathname;
+ crashVfs.pAppData = (void *)pOriginalVfs;
+ crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile;
+ sqlite3_vfs_register(&crashVfs, 0);
+ }else{
+ crashVfs.pAppData = 0;
+ sqlite3_vfs_unregister(&crashVfs);
+ }
+
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite_crashparams ?OPTIONS? DELAY CRASHFILE
+**
+** This procedure implements a TCL command that enables crash testing
+** in testfixture. Once enabled, crash testing cannot be disabled.
+**
+** Available options are "-characteristics" and "-sectorsize". Both require
+** an argument. For -sectorsize, this is the simulated sector size in
+** bytes. For -characteristics, the argument must be a list of io-capability
+** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K",
+** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K",
+** "atomic64K", "sequential" and "safe_append".
+**
+** Example:
+**
+** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1
+**
+*/
+static int crashParamsObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int iDelay;
+ const char *zCrashFile;
+ int nCrashFile, iDc, iSectorSize;
+
+ iDc = -1;
+ iSectorSize = -1;
+
+ if( objc<3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE");
+ goto error;
+ }
+
+ zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile);
+ if( nCrashFile>=sizeof(g.zCrashFile) ){
+ Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0);
+ goto error;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){
+ goto error;
+ }
+
+ if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){
+ return TCL_ERROR;
+ }
+
+ if( iDc>=0 ){
+ g.iDeviceCharacteristics = iDc;
+ }
+ if( iSectorSize>=0 ){
+ g.iSectorSize = iSectorSize;
+ }
+
+ g.iCrash = iDelay;
+ memcpy(g.zCrashFile, zCrashFile, nCrashFile+1);
+ sqlite3CrashTestEnable = 1;
+ return TCL_OK;
+
+error:
+ return TCL_ERROR;
+}
+
+static int devSymObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ void devsym_register(int iDeviceChar, int iSectorSize);
+
+ int iDc = -1;
+ int iSectorSize = -1;
+
+ if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){
+ return TCL_ERROR;
+ }
+ devsym_register(iDc, iSectorSize);
+
+ return TCL_OK;
+}
+
+#endif /* SQLITE_OMIT_DISKIO */
+
+/*
+** This procedure registers the TCL procedures defined in this file.
+*/
+int Sqlitetest6_Init(Tcl_Interp *interp){
+#ifndef SQLITE_OMIT_DISKIO
+ Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
+#endif
+ return TCL_OK;
+}
+
+#endif /* SQLITE_TEST */
diff --git a/third_party/sqlite/src/test7.c b/third_party/sqlite/src/test7.c
new file mode 100755
index 0000000..78754bf
--- /dev/null
+++ b/third_party/sqlite/src/test7.c
@@ -0,0 +1,723 @@
+/*
+** 2006 January 09
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the client/server version of the SQLite library.
+** Derived from test4.c.
+**
+** $Id: test7.c,v 1.12 2008/07/28 19:34:54 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+
+/*
+** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
+** the SQLITE_SERVER option.
+*/
+#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
+ defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sched.h>
+#include <ctype.h>
+
+/*
+** Interfaces defined in server.c
+*/
+int sqlite3_client_open(const char*, sqlite3**);
+int sqlite3_client_prepare(sqlite3*,const char*,int,
+ sqlite3_stmt**,const char**);
+int sqlite3_client_step(sqlite3_stmt*);
+int sqlite3_client_reset(sqlite3_stmt*);
+int sqlite3_client_finalize(sqlite3_stmt*);
+int sqlite3_client_close(sqlite3*);
+int sqlite3_server_start(void);
+int sqlite3_server_stop(void);
+
+/*
+** Each thread is controlled by an instance of the following
+** structure.
+*/
+typedef struct Thread Thread;
+struct Thread {
+ /* The first group of fields are writable by the supervisor thread
+ ** and read-only to the client threads
+ */
+ char *zFilename; /* Name of database file */
+ void (*xOp)(Thread*); /* next operation to do */
+ char *zArg; /* argument usable by xOp */
+ volatile int opnum; /* Operation number */
+ volatile int busy; /* True if this thread is in use */
+
+ /* The next group of fields are writable by the client threads
+ ** but read-only to the superviser thread.
+ */
+ volatile int completed; /* Number of operations completed */
+ sqlite3 *db; /* Open database */
+ sqlite3_stmt *pStmt; /* Pending operation */
+ char *zErr; /* operation error */
+ char *zStaticErr; /* Static error message */
+ int rc; /* operation return code */
+ int argc; /* number of columns in result */
+ const char *argv[100]; /* result columns */
+ const char *colv[100]; /* result column names */
+};
+
+/*
+** There can be as many as 26 threads running at once. Each is named
+** by a capital letter: A, B, C, ..., Y, Z.
+*/
+#define N_THREAD 26
+static Thread threadset[N_THREAD];
+
+/*
+** The main loop for a thread. Threads use busy waiting.
+*/
+static void *client_main(void *pArg){
+ Thread *p = (Thread*)pArg;
+ if( p->db ){
+ sqlite3_client_close(p->db);
+ }
+ sqlite3_client_open(p->zFilename, &p->db);
+ if( SQLITE_OK!=sqlite3_errcode(p->db) ){
+ p->zErr = strdup(sqlite3_errmsg(p->db));
+ sqlite3_client_close(p->db);
+ p->db = 0;
+ }
+ p->pStmt = 0;
+ p->completed = 1;
+ while( p->opnum<=p->completed ) sched_yield();
+ while( p->xOp ){
+ if( p->zErr && p->zErr!=p->zStaticErr ){
+ sqlite3_free(p->zErr);
+ p->zErr = 0;
+ }
+ (*p->xOp)(p);
+ p->completed++;
+ while( p->opnum<=p->completed ) sched_yield();
+ }
+ if( p->pStmt ){
+ sqlite3_client_finalize(p->pStmt);
+ p->pStmt = 0;
+ }
+ if( p->db ){
+ sqlite3_client_close(p->db);
+ p->db = 0;
+ }
+ if( p->zErr && p->zErr!=p->zStaticErr ){
+ sqlite3_free(p->zErr);
+ p->zErr = 0;
+ }
+ p->completed++;
+ sqlite3_thread_cleanup();
+ return 0;
+}
+
+/*
+** Get a thread ID which is an upper case letter. Return the index.
+** If the argument is not a valid thread ID put an error message in
+** the interpreter and return -1.
+*/
+static int parse_client_id(Tcl_Interp *interp, const char *zArg){
+ if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
+ Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
+ return -1;
+ }
+ return zArg[0] - 'A';
+}
+
+/*
+** Usage: client_create NAME FILENAME
+**
+** NAME should be an upper case letter. Start the thread running with
+** an open connection to the given database.
+*/
+static int tcl_client_create(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ pthread_t x;
+ int rc;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID FILENAME", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( threadset[i].busy ){
+ Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
+ return TCL_ERROR;
+ }
+ threadset[i].busy = 1;
+ sqlite3_free(threadset[i].zFilename);
+ threadset[i].zFilename = sqlite3DbStrDup(0, argv[2]);
+ threadset[i].opnum = 1;
+ threadset[i].completed = 0;
+ rc = pthread_create(&x, 0, client_main, &threadset[i]);
+ if( rc ){
+ Tcl_AppendResult(interp, "failed to create the thread", 0);
+ sqlite3_free(threadset[i].zFilename);
+ threadset[i].busy = 0;
+ return TCL_ERROR;
+ }
+ pthread_detach(x);
+ sqlite3_server_start();
+ return TCL_OK;
+}
+
+/*
+** Wait for a thread to reach its idle state.
+*/
+static void client_wait(Thread *p){
+ while( p->opnum>p->completed ) sched_yield();
+}
+
+/*
+** Usage: client_wait ID
+**
+** Wait on thread ID to reach its idle state.
+*/
+static int tcl_client_wait(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ return TCL_OK;
+}
+
+/*
+** Stop a thread.
+*/
+static void stop_thread(Thread *p){
+ client_wait(p);
+ p->xOp = 0;
+ p->opnum++;
+ client_wait(p);
+ sqlite3_free(p->zArg);
+ p->zArg = 0;
+ sqlite3_free(p->zFilename);
+ p->zFilename = 0;
+ p->busy = 0;
+}
+
+/*
+** Usage: client_halt ID
+**
+** Cause a client thread to shut itself down. Wait for the shutdown to be
+** completed. If ID is "*" then stop all client threads.
+*/
+static int tcl_client_halt(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ if( argv[1][0]=='*' && argv[1][1]==0 ){
+ for(i=0; i<N_THREAD; i++){
+ if( threadset[i].busy ){
+ stop_thread(&threadset[i]);
+ }
+ }
+ }else{
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ stop_thread(&threadset[i]);
+ }
+
+ /* If no client threads are still running, also stop the server */
+ for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
+ if( i>=N_THREAD ){
+ sqlite3_server_stop();
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: client_argc ID
+**
+** Wait on the most recent client_step to complete, then return the
+** number of columns in the result set.
+*/
+static int tcl_client_argc(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ char zBuf[100];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ sprintf(zBuf, "%d", threadset[i].argc);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: client_argv ID N
+**
+** Wait on the most recent client_step to complete, then return the
+** value of the N-th columns in the result set.
+*/
+static int tcl_client_argv(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ int n;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID N", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ client_wait(&threadset[i]);
+ if( n<0 || n>=threadset[i].argc ){
+ Tcl_AppendResult(interp, "column number out of range", 0);
+ return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, threadset[i].argv[n], 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: client_colname ID N
+**
+** Wait on the most recent client_step to complete, then return the
+** name of the N-th columns in the result set.
+*/
+static int tcl_client_colname(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ int n;
+
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID N", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+ client_wait(&threadset[i]);
+ if( n<0 || n>=threadset[i].argc ){
+ Tcl_AppendResult(interp, "column number out of range", 0);
+ return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, threadset[i].colv[n], 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: client_result ID
+**
+** Wait on the most recent operation to complete, then return the
+** result code from that operation.
+*/
+static int tcl_client_result(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ const char *zName;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ switch( threadset[i].rc ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
+ case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
+ case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
+ case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
+ case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
+ case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
+ case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
+ case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
+ case SQLITE_ROW: zName = "SQLITE_ROW"; break;
+ case SQLITE_DONE: zName = "SQLITE_DONE"; break;
+ default: zName = "SQLITE_Unknown"; break;
+ }
+ Tcl_AppendResult(interp, zName, 0);
+ return TCL_OK;
+}
+
+/*
+** Usage: client_error ID
+**
+** Wait on the most recent operation to complete, then return the
+** error string.
+*/
+static int tcl_client_error(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ Tcl_AppendResult(interp, threadset[i].zErr, 0);
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to compile an SQL statement.
+*/
+static void do_compile(Thread *p){
+ if( p->db==0 ){
+ p->zErr = p->zStaticErr = "no database is open";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ if( p->pStmt ){
+ sqlite3_client_finalize(p->pStmt);
+ p->pStmt = 0;
+ }
+ p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
+}
+
+/*
+** Usage: client_compile ID SQL
+**
+** Compile a new virtual machine.
+*/
+static int tcl_client_compile(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID SQL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ threadset[i].xOp = do_compile;
+ sqlite3_free(threadset[i].zArg);
+ threadset[i].zArg = sqlite3DbStrDup(0, argv[2]);
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to step the virtual machine.
+*/
+static void do_step(Thread *p){
+ int i;
+ if( p->pStmt==0 ){
+ p->zErr = p->zStaticErr = "no virtual machine available";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ p->rc = sqlite3_client_step(p->pStmt);
+ if( p->rc==SQLITE_ROW ){
+ p->argc = sqlite3_column_count(p->pStmt);
+ for(i=0; i<sqlite3_data_count(p->pStmt); i++){
+ p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
+ }
+ for(i=0; i<p->argc; i++){
+ p->colv[i] = sqlite3_column_name(p->pStmt, i);
+ }
+ }
+}
+
+/*
+** Usage: client_step ID
+**
+** Advance the virtual machine by one step
+*/
+static int tcl_client_step(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " IDL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ threadset[i].xOp = do_step;
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to finalize a virtual machine.
+*/
+static void do_finalize(Thread *p){
+ if( p->pStmt==0 ){
+ p->zErr = p->zStaticErr = "no virtual machine available";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ p->rc = sqlite3_client_finalize(p->pStmt);
+ p->pStmt = 0;
+}
+
+/*
+** Usage: client_finalize ID
+**
+** Finalize the virtual machine.
+*/
+static int tcl_client_finalize(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " IDL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ threadset[i].xOp = do_finalize;
+ sqlite3_free(threadset[i].zArg);
+ threadset[i].zArg = 0;
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** This procedure runs in the thread to reset a virtual machine.
+*/
+static void do_reset(Thread *p){
+ if( p->pStmt==0 ){
+ p->zErr = p->zStaticErr = "no virtual machine available";
+ p->rc = SQLITE_ERROR;
+ return;
+ }
+ p->rc = sqlite3_client_reset(p->pStmt);
+ p->pStmt = 0;
+}
+
+/*
+** Usage: client_reset ID
+**
+** Finalize the virtual machine.
+*/
+static int tcl_client_reset(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " IDL", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ threadset[i].xOp = do_reset;
+ sqlite3_free(threadset[i].zArg);
+ threadset[i].zArg = 0;
+ threadset[i].opnum++;
+ return TCL_OK;
+}
+
+/*
+** Usage: client_swap ID ID
+**
+** Interchange the sqlite* pointer between two threads.
+*/
+static int tcl_client_swap(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int i, j;
+ sqlite3 *temp;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ID1 ID2", 0);
+ return TCL_ERROR;
+ }
+ i = parse_client_id(interp, argv[1]);
+ if( i<0 ) return TCL_ERROR;
+ if( !threadset[i].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[i]);
+ j = parse_client_id(interp, argv[2]);
+ if( j<0 ) return TCL_ERROR;
+ if( !threadset[j].busy ){
+ Tcl_AppendResult(interp, "no such thread", 0);
+ return TCL_ERROR;
+ }
+ client_wait(&threadset[j]);
+ temp = threadset[i].db;
+ threadset[i].db = threadset[j].db;
+ threadset[j].db = temp;
+ return TCL_OK;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest7_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_CmdProc *xProc;
+ } aCmd[] = {
+ { "client_create", (Tcl_CmdProc*)tcl_client_create },
+ { "client_wait", (Tcl_CmdProc*)tcl_client_wait },
+ { "client_halt", (Tcl_CmdProc*)tcl_client_halt },
+ { "client_argc", (Tcl_CmdProc*)tcl_client_argc },
+ { "client_argv", (Tcl_CmdProc*)tcl_client_argv },
+ { "client_colname", (Tcl_CmdProc*)tcl_client_colname },
+ { "client_result", (Tcl_CmdProc*)tcl_client_result },
+ { "client_error", (Tcl_CmdProc*)tcl_client_error },
+ { "client_compile", (Tcl_CmdProc*)tcl_client_compile },
+ { "client_step", (Tcl_CmdProc*)tcl_client_step },
+ { "client_reset", (Tcl_CmdProc*)tcl_client_reset },
+ { "client_finalize", (Tcl_CmdProc*)tcl_client_finalize },
+ { "client_swap", (Tcl_CmdProc*)tcl_client_swap },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ return TCL_OK;
+}
+#else
+int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
+#endif /* SQLITE_OS_UNIX */
diff --git a/third_party/sqlite/src/test8.c b/third_party/sqlite/src/test8.c
new file mode 100755
index 0000000..c9b87b9
--- /dev/null
+++ b/third_party/sqlite/src/test8.c
@@ -0,0 +1,1344 @@
+/*
+** 2006 June 10
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the virtual table interfaces. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test8.c,v 1.72 2008/08/05 21:36:07 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+typedef struct echo_vtab echo_vtab;
+typedef struct echo_cursor echo_cursor;
+
+/*
+** The test module defined in this file uses four global Tcl variables to
+** commicate with test-scripts:
+**
+** $::echo_module
+** $::echo_module_sync_fail
+** $::echo_module_begin_fail
+** $::echo_module_cost
+**
+** The variable ::echo_module is a list. Each time one of the following
+** methods is called, one or more elements are appended to the list.
+** This is used for automated testing of virtual table modules.
+**
+** The ::echo_module_sync_fail variable is set by test scripts and read
+** by code in this file. If it is set to the name of a real table in the
+** the database, then all xSync operations on echo virtual tables that
+** use the named table as a backing store will fail.
+*/
+
+/*
+** Errors can be provoked within the following echo virtual table methods:
+**
+** xBestIndex xOpen xFilter xNext
+** xColumn xRowid xUpdate xSync
+** xBegin xRename
+**
+** This is done by setting the global tcl variable:
+**
+** echo_module_fail($method,$tbl)
+**
+** where $method is set to the name of the virtual table method to fail
+** (i.e. "xBestIndex") and $tbl is the name of the table being echoed (not
+** the name of the virtual table, the name of the underlying real table).
+*/
+
+/*
+** An echo virtual-table object.
+**
+** echo.vtab.aIndex is an array of booleans. The nth entry is true if
+** the nth column of the real table is the left-most column of an index
+** (implicit or otherwise). In other words, if SQLite can optimize
+** a query like "SELECT * FROM real_table WHERE col = ?".
+**
+** Member variable aCol[] contains copies of the column names of the real
+** table.
+*/
+struct echo_vtab {
+ sqlite3_vtab base;
+ Tcl_Interp *interp; /* Tcl interpreter containing debug variables */
+ sqlite3 *db; /* Database connection */
+
+ int isPattern;
+ int inTransaction; /* True if within a transaction */
+ char *zThis; /* Name of the echo table */
+ char *zTableName; /* Name of the real table */
+ char *zLogName; /* Name of the log table */
+ int nCol; /* Number of columns in the real table */
+ int *aIndex; /* Array of size nCol. True if column has an index */
+ char **aCol; /* Array of size nCol. Column names */
+};
+
+/* An echo cursor object */
+struct echo_cursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pStmt;
+};
+
+static int simulateVtabError(echo_vtab *p, const char *zMethod){
+ const char *zErr;
+ char zVarname[128];
+ zVarname[127] = '\0';
+ sqlite3_snprintf(127, zVarname,
+ "echo_module_fail(%s,%s)", zMethod, p->zTableName);
+ zErr = Tcl_GetVar(p->interp, zVarname, TCL_GLOBAL_ONLY);
+ if( zErr ){
+ p->base.zErrMsg = sqlite3_mprintf("echo-vtab-error: %s", zErr);
+ }
+ return (zErr!=0);
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+** "abc" becomes abc
+** 'xyz' becomes xyz
+** [pqr] becomes pqr
+** `mno` becomes mno
+*/
+static void dequoteString(char *z){
+ int quote;
+ int i, j;
+ if( z==0 ) return;
+ quote = z[0];
+ switch( quote ){
+ case '\'': break;
+ case '"': break;
+ case '`': break; /* For MySQL compatibility */
+ case '[': quote = ']'; break; /* For MS SqlServer compatibility */
+ default: return;
+ }
+ for(i=1, j=0; z[i]; i++){
+ if( z[i]==quote ){
+ if( z[i+1]==quote ){
+ z[j++] = quote;
+ i++;
+ }else{
+ z[j++] = 0;
+ break;
+ }
+ }else{
+ z[j++] = z[i];
+ }
+ }
+}
+
+/*
+** Retrieve the column names for the table named zTab via database
+** connection db. SQLITE_OK is returned on success, or an sqlite error
+** code otherwise.
+**
+** If successful, the number of columns is written to *pnCol. *paCol is
+** set to point at sqlite3_malloc()'d space containing the array of
+** nCol column names. The caller is responsible for calling sqlite3_free
+** on *paCol.
+*/
+static int getColumnNames(
+ sqlite3 *db,
+ const char *zTab,
+ char ***paCol,
+ int *pnCol
+){
+ char **aCol = 0;
+ char *zSql;
+ sqlite3_stmt *pStmt = 0;
+ int rc = SQLITE_OK;
+ int nCol = 0;
+
+ /* Prepare the statement "SELECT * FROM <tbl>". The column names
+ ** of the result set of the compiled SELECT will be the same as
+ ** the column names of table <tbl>.
+ */
+ zSql = sqlite3_mprintf("SELECT * FROM %Q", zTab);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto out;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+
+ if( rc==SQLITE_OK ){
+ int ii;
+ int nBytes;
+ char *zSpace;
+ nCol = sqlite3_column_count(pStmt);
+
+ /* Figure out how much space to allocate for the array of column names
+ ** (including space for the strings themselves). Then allocate it.
+ */
+ nBytes = sizeof(char *) * nCol;
+ for(ii=0; ii<nCol; ii++){
+ const char *zName = sqlite3_column_name(pStmt, ii);
+ if( !zName ){
+ rc = SQLITE_NOMEM;
+ goto out;
+ }
+ nBytes += strlen(zName)+1;
+ }
+ aCol = (char **)sqlite3MallocZero(nBytes);
+ if( !aCol ){
+ rc = SQLITE_NOMEM;
+ goto out;
+ }
+
+ /* Copy the column names into the allocated space and set up the
+ ** pointers in the aCol[] array.
+ */
+ zSpace = (char *)(&aCol[nCol]);
+ for(ii=0; ii<nCol; ii++){
+ aCol[ii] = zSpace;
+ zSpace += sprintf(zSpace, "%s", sqlite3_column_name(pStmt, ii));
+ zSpace++;
+ }
+ assert( (zSpace-nBytes)==(char *)aCol );
+ }
+
+ *paCol = aCol;
+ *pnCol = nCol;
+
+out:
+ sqlite3_finalize(pStmt);
+ return rc;
+}
+
+/*
+** Parameter zTab is the name of a table in database db with nCol
+** columns. This function allocates an array of integers nCol in
+** size and populates it according to any implicit or explicit
+** indices on table zTab.
+**
+** If successful, SQLITE_OK is returned and *paIndex set to point
+** at the allocated array. Otherwise, an error code is returned.
+**
+** See comments associated with the member variable aIndex above
+** "struct echo_vtab" for details of the contents of the array.
+*/
+static int getIndexArray(
+ sqlite3 *db, /* Database connection */
+ const char *zTab, /* Name of table in database db */
+ int nCol,
+ int **paIndex
+){
+ sqlite3_stmt *pStmt = 0;
+ int *aIndex = 0;
+ int rc;
+ char *zSql;
+
+ /* Allocate space for the index array */
+ aIndex = (int *)sqlite3MallocZero(sizeof(int) * nCol);
+ if( !aIndex ){
+ rc = SQLITE_NOMEM;
+ goto get_index_array_out;
+ }
+
+ /* Compile an sqlite pragma to loop through all indices on table zTab */
+ zSql = sqlite3MPrintf(0, "PRAGMA index_list(%s)", zTab);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto get_index_array_out;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+
+ /* For each index, figure out the left-most column and set the
+ ** corresponding entry in aIndex[] to 1.
+ */
+ while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
+ const char *zIdx = (const char *)sqlite3_column_text(pStmt, 1);
+ sqlite3_stmt *pStmt2 = 0;
+ zSql = sqlite3MPrintf(0, "PRAGMA index_info(%s)", zIdx);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto get_index_array_out;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt2, 0);
+ sqlite3_free(zSql);
+ if( pStmt2 && sqlite3_step(pStmt2)==SQLITE_ROW ){
+ int cid = sqlite3_column_int(pStmt2, 1);
+ assert( cid>=0 && cid<nCol );
+ aIndex[cid] = 1;
+ }
+ if( pStmt2 ){
+ rc = sqlite3_finalize(pStmt2);
+ }
+ if( rc!=SQLITE_OK ){
+ goto get_index_array_out;
+ }
+ }
+
+
+get_index_array_out:
+ if( pStmt ){
+ int rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(aIndex);
+ aIndex = 0;
+ }
+ *paIndex = aIndex;
+ return rc;
+}
+
+/*
+** Global Tcl variable $echo_module is a list. This routine appends
+** the string element zArg to that list in interpreter interp.
+*/
+static void appendToEchoModule(Tcl_Interp *interp, const char *zArg){
+ int flags = (TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "echo_module", (zArg?zArg:""), flags);
+}
+
+/*
+** This function is called from within the echo-modules xCreate and
+** xConnect methods. The argc and argv arguments are copies of those
+** passed to the calling method. This function is responsible for
+** calling sqlite3_declare_vtab() to declare the schema of the virtual
+** table being created or connected.
+**
+** If the constructor was passed just one argument, i.e.:
+**
+** CREATE TABLE t1 AS echo(t2);
+**
+** Then t2 is assumed to be the name of a *real* database table. The
+** schema of the virtual table is declared by passing a copy of the
+** CREATE TABLE statement for the real table to sqlite3_declare_vtab().
+** Hence, the virtual table should have exactly the same column names and
+** types as the real table.
+*/
+static int echoDeclareVtab(
+ echo_vtab *pVtab,
+ sqlite3 *db
+){
+ int rc = SQLITE_OK;
+
+ if( pVtab->zTableName ){
+ sqlite3_stmt *pStmt = 0;
+ rc = sqlite3_prepare(db,
+ "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
+ -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_text(pStmt, 1, pVtab->zTableName, -1, 0);
+ if( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int rc2;
+ const char *zCreateTable = (const char *)sqlite3_column_text(pStmt, 0);
+ rc = sqlite3_declare_vtab(db, zCreateTable);
+ rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ } else {
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_ERROR;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = getColumnNames(db, pVtab->zTableName, &pVtab->aCol, &pVtab->nCol);
+ }
+ if( rc==SQLITE_OK ){
+ rc = getIndexArray(db, pVtab->zTableName, pVtab->nCol, &pVtab->aIndex);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** This function frees all runtime structures associated with the virtual
+** table pVtab.
+*/
+static int echoDestructor(sqlite3_vtab *pVtab){
+ echo_vtab *p = (echo_vtab*)pVtab;
+ sqlite3_free(p->aIndex);
+ sqlite3_free(p->aCol);
+ sqlite3_free(p->zThis);
+ sqlite3_free(p->zTableName);
+ sqlite3_free(p->zLogName);
+ sqlite3_free(p);
+ return 0;
+}
+
+typedef struct EchoModule EchoModule;
+struct EchoModule {
+ Tcl_Interp *interp;
+};
+
+/*
+** This function is called to do the work of the xConnect() method -
+** to allocate the required in-memory structures for a newly connected
+** virtual table.
+*/
+static int echoConstructor(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ int rc;
+ int i;
+ echo_vtab *pVtab;
+
+ /* Allocate the sqlite3_vtab/echo_vtab structure itself */
+ pVtab = sqlite3MallocZero( sizeof(*pVtab) );
+ if( !pVtab ){
+ return SQLITE_NOMEM;
+ }
+ pVtab->interp = ((EchoModule *)pAux)->interp;
+ pVtab->db = db;
+
+ /* Allocate echo_vtab.zThis */
+ pVtab->zThis = sqlite3MPrintf(0, "%s", argv[2]);
+ if( !pVtab->zThis ){
+ echoDestructor((sqlite3_vtab *)pVtab);
+ return SQLITE_NOMEM;
+ }
+
+ /* Allocate echo_vtab.zTableName */
+ if( argc>3 ){
+ pVtab->zTableName = sqlite3MPrintf(0, "%s", argv[3]);
+ dequoteString(pVtab->zTableName);
+ if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){
+ char *z = sqlite3MPrintf(0, "%s%s", argv[2], &(pVtab->zTableName[1]));
+ sqlite3_free(pVtab->zTableName);
+ pVtab->zTableName = z;
+ pVtab->isPattern = 1;
+ }
+ if( !pVtab->zTableName ){
+ echoDestructor((sqlite3_vtab *)pVtab);
+ return SQLITE_NOMEM;
+ }
+ }
+
+ /* Log the arguments to this function to Tcl var ::echo_module */
+ for(i=0; i<argc; i++){
+ appendToEchoModule(pVtab->interp, argv[i]);
+ }
+
+ /* Invoke sqlite3_declare_vtab and set up other members of the echo_vtab
+ ** structure. If an error occurs, delete the sqlite3_vtab structure and
+ ** return an error code.
+ */
+ rc = echoDeclareVtab(pVtab, db);
+ if( rc!=SQLITE_OK ){
+ echoDestructor((sqlite3_vtab *)pVtab);
+ return rc;
+ }
+
+ /* Success. Set *ppVtab and return */
+ *ppVtab = &pVtab->base;
+ return SQLITE_OK;
+}
+
+/*
+** Echo virtual table module xCreate method.
+*/
+static int echoCreate(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ int rc = SQLITE_OK;
+ appendToEchoModule(((EchoModule *)pAux)->interp, "xCreate");
+ rc = echoConstructor(db, pAux, argc, argv, ppVtab, pzErr);
+
+ /* If there were two arguments passed to the module at the SQL level
+ ** (i.e. "CREATE VIRTUAL TABLE tbl USING echo(arg1, arg2)"), then
+ ** the second argument is used as a table name. Attempt to create
+ ** such a table with a single column, "logmsg". This table will
+ ** be used to log calls to the xUpdate method. It will be deleted
+ ** when the virtual table is DROPed.
+ **
+ ** Note: The main point of this is to test that we can drop tables
+ ** from within an xDestroy method call.
+ */
+ if( rc==SQLITE_OK && argc==5 ){
+ char *zSql;
+ echo_vtab *pVtab = *(echo_vtab **)ppVtab;
+ pVtab->zLogName = sqlite3MPrintf(0, "%s", argv[4]);
+ zSql = sqlite3MPrintf(0, "CREATE TABLE %Q(logmsg)", pVtab->zLogName);
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ){
+ *pzErr = sqlite3DbStrDup(0, sqlite3_errmsg(db));
+ }
+ }
+
+ if( *ppVtab && rc!=SQLITE_OK ){
+ echoDestructor(*ppVtab);
+ *ppVtab = 0;
+ }
+
+ if( rc==SQLITE_OK ){
+ (*(echo_vtab**)ppVtab)->inTransaction = 1;
+ }
+
+ return rc;
+}
+
+/*
+** Echo virtual table module xConnect method.
+*/
+static int echoConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ appendToEchoModule(((EchoModule *)pAux)->interp, "xConnect");
+ return echoConstructor(db, pAux, argc, argv, ppVtab, pzErr);
+}
+
+/*
+** Echo virtual table module xDisconnect method.
+*/
+static int echoDisconnect(sqlite3_vtab *pVtab){
+ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect");
+ return echoDestructor(pVtab);
+}
+
+/*
+** Echo virtual table module xDestroy method.
+*/
+static int echoDestroy(sqlite3_vtab *pVtab){
+ int rc = SQLITE_OK;
+ echo_vtab *p = (echo_vtab *)pVtab;
+ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy");
+
+ /* Drop the "log" table, if one exists (see echoCreate() for details) */
+ if( p && p->zLogName ){
+ char *zSql;
+ zSql = sqlite3MPrintf(0, "DROP TABLE %Q", p->zLogName);
+ rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = echoDestructor(pVtab);
+ }
+ return rc;
+}
+
+/*
+** Echo virtual table module xOpen method.
+*/
+static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ echo_cursor *pCur;
+ if( simulateVtabError((echo_vtab *)pVTab, "xOpen") ){
+ return SQLITE_ERROR;
+ }
+ pCur = sqlite3MallocZero(sizeof(echo_cursor));
+ *ppCursor = (sqlite3_vtab_cursor *)pCur;
+ return (pCur ? SQLITE_OK : SQLITE_NOMEM);
+}
+
+/*
+** Echo virtual table module xClose method.
+*/
+static int echoClose(sqlite3_vtab_cursor *cur){
+ int rc;
+ echo_cursor *pCur = (echo_cursor *)cur;
+ sqlite3_stmt *pStmt = pCur->pStmt;
+ pCur->pStmt = 0;
+ sqlite3_free(pCur);
+ rc = sqlite3_finalize(pStmt);
+ return rc;
+}
+
+/*
+** Return non-zero if the cursor does not currently point to a valid record
+** (i.e if the scan has finished), or zero otherwise.
+*/
+static int echoEof(sqlite3_vtab_cursor *cur){
+ return (((echo_cursor *)cur)->pStmt ? 0 : 1);
+}
+
+/*
+** Echo virtual table module xNext method.
+*/
+static int echoNext(sqlite3_vtab_cursor *cur){
+ int rc = SQLITE_OK;
+ echo_cursor *pCur = (echo_cursor *)cur;
+
+ if( simulateVtabError((echo_vtab *)(cur->pVtab), "xNext") ){
+ return SQLITE_ERROR;
+ }
+
+ if( pCur->pStmt ){
+ rc = sqlite3_step(pCur->pStmt);
+ if( rc==SQLITE_ROW ){
+ rc = SQLITE_OK;
+ }else{
+ rc = sqlite3_finalize(pCur->pStmt);
+ pCur->pStmt = 0;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Echo virtual table module xColumn method.
+*/
+static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ int iCol = i + 1;
+ sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
+
+ if( simulateVtabError((echo_vtab *)(cur->pVtab), "xColumn") ){
+ return SQLITE_ERROR;
+ }
+
+ if( !pStmt ){
+ sqlite3_result_null(ctx);
+ }else{
+ assert( sqlite3_data_count(pStmt)>iCol );
+ sqlite3_result_value(ctx, sqlite3_column_value(pStmt, iCol));
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Echo virtual table module xRowid method.
+*/
+static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
+
+ if( simulateVtabError((echo_vtab *)(cur->pVtab), "xRowid") ){
+ return SQLITE_ERROR;
+ }
+
+ *pRowid = sqlite3_column_int64(pStmt, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Compute a simple hash of the null terminated string zString.
+**
+** This module uses only sqlite3_index_info.idxStr, not
+** sqlite3_index_info.idxNum. So to test idxNum, when idxStr is set
+** in echoBestIndex(), idxNum is set to the corresponding hash value.
+** In echoFilter(), code assert()s that the supplied idxNum value is
+** indeed the hash of the supplied idxStr.
+*/
+static int hashString(const char *zString){
+ int val = 0;
+ int ii;
+ for(ii=0; zString[ii]; ii++){
+ val = (val << 3) + (int)zString[ii];
+ }
+ return val;
+}
+
+/*
+** Echo virtual table module xFilter method.
+*/
+static int echoFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ int rc;
+ int i;
+
+ echo_cursor *pCur = (echo_cursor *)pVtabCursor;
+ echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab;
+ sqlite3 *db = pVtab->db;
+
+ if( simulateVtabError(pVtab, "xFilter") ){
+ return SQLITE_ERROR;
+ }
+
+ /* Check that idxNum matches idxStr */
+ assert( idxNum==hashString(idxStr) );
+
+ /* Log arguments to the ::echo_module Tcl variable */
+ appendToEchoModule(pVtab->interp, "xFilter");
+ appendToEchoModule(pVtab->interp, idxStr);
+ for(i=0; i<argc; i++){
+ appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i]));
+ }
+
+ sqlite3_finalize(pCur->pStmt);
+ pCur->pStmt = 0;
+
+ /* Prepare the SQL statement created by echoBestIndex and bind the
+ ** runtime parameters passed to this function to it.
+ */
+ rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0);
+ assert( pCur->pStmt || rc!=SQLITE_OK );
+ for(i=0; rc==SQLITE_OK && i<argc; i++){
+ sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
+ }
+
+ /* If everything was successful, advance to the first row of the scan */
+ if( rc==SQLITE_OK ){
+ rc = echoNext(pVtabCursor);
+ }
+
+ return rc;
+}
+
+
+/*
+** A helper function used by echoUpdate() and echoBestIndex() for
+** manipulating strings in concert with the sqlite3_mprintf() function.
+**
+** Parameter pzStr points to a pointer to a string allocated with
+** sqlite3_mprintf. The second parameter, zAppend, points to another
+** string. The two strings are concatenated together and *pzStr
+** set to point at the result. The initial buffer pointed to by *pzStr
+** is deallocated via sqlite3_free().
+**
+** If the third argument, doFree, is true, then sqlite3_free() is
+** also called to free the buffer pointed to by zAppend.
+*/
+static void string_concat(char **pzStr, char *zAppend, int doFree, int *pRc){
+ char *zIn = *pzStr;
+ if( !zAppend && doFree && *pRc==SQLITE_OK ){
+ *pRc = SQLITE_NOMEM;
+ }
+ if( *pRc!=SQLITE_OK ){
+ sqlite3_free(zIn);
+ zIn = 0;
+ }else{
+ if( zIn ){
+ char *zTemp = zIn;
+ zIn = sqlite3_mprintf("%s%s", zIn, zAppend);
+ sqlite3_free(zTemp);
+ }else{
+ zIn = sqlite3_mprintf("%s", zAppend);
+ }
+ if( !zIn ){
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ *pzStr = zIn;
+ if( doFree ){
+ sqlite3_free(zAppend);
+ }
+}
+
+/*
+** The echo module implements the subset of query constraints and sort
+** orders that may take advantage of SQLite indices on the underlying
+** real table. For example, if the real table is declared as:
+**
+** CREATE TABLE real(a, b, c);
+** CREATE INDEX real_index ON real(b);
+**
+** then the echo module handles WHERE or ORDER BY clauses that refer
+** to the column "b", but not "a" or "c". If a multi-column index is
+** present, only its left most column is considered.
+**
+** This xBestIndex method encodes the proposed search strategy as
+** an SQL query on the real table underlying the virtual echo module
+** table and stores the query in sqlite3_index_info.idxStr. The SQL
+** statement is of the form:
+**
+** SELECT rowid, * FROM <real-table> ?<where-clause>? ?<order-by-clause>?
+**
+** where the <where-clause> and <order-by-clause> are determined
+** by the contents of the structure pointed to by the pIdxInfo argument.
+*/
+static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int ii;
+ char *zQuery = 0;
+ char *zNew;
+ int nArg = 0;
+ const char *zSep = "WHERE";
+ echo_vtab *pVtab = (echo_vtab *)tab;
+ sqlite3_stmt *pStmt = 0;
+ Tcl_Interp *interp = pVtab->interp;
+
+ int nRow;
+ int useIdx = 0;
+ int rc = SQLITE_OK;
+ int useCost = 0;
+ double cost;
+ int isIgnoreUsable = 0;
+ if( Tcl_GetVar(interp, "echo_module_ignore_usable", TCL_GLOBAL_ONLY) ){
+ isIgnoreUsable = 1;
+ }
+
+ if( simulateVtabError(pVtab, "xBestIndex") ){
+ return SQLITE_ERROR;
+ }
+
+ /* Determine the number of rows in the table and store this value in local
+ ** variable nRow. The 'estimated-cost' of the scan will be the number of
+ ** rows in the table for a linear scan, or the log (base 2) of the
+ ** number of rows if the proposed scan uses an index.
+ */
+ if( Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY) ){
+ cost = atof(Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY));
+ useCost = 1;
+ } else {
+ zQuery = sqlite3_mprintf("SELECT count(*) FROM %Q", pVtab->zTableName);
+ if( !zQuery ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_prepare(pVtab->db, zQuery, -1, &pStmt, 0);
+ sqlite3_free(zQuery);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ sqlite3_step(pStmt);
+ nRow = sqlite3_column_int(pStmt, 0);
+ rc = sqlite3_finalize(pStmt);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+
+ zQuery = sqlite3_mprintf("SELECT rowid, * FROM %Q", pVtab->zTableName);
+ if( !zQuery ){
+ return SQLITE_NOMEM;
+ }
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ const struct sqlite3_index_constraint *pConstraint;
+ struct sqlite3_index_constraint_usage *pUsage;
+ int iCol;
+
+ pConstraint = &pIdxInfo->aConstraint[ii];
+ pUsage = &pIdxInfo->aConstraintUsage[ii];
+
+ if( !isIgnoreUsable && !pConstraint->usable ) continue;
+
+ iCol = pConstraint->iColumn;
+ if( pVtab->aIndex[iCol] || iCol<0 ){
+ char *zCol = pVtab->aCol[iCol];
+ char *zOp = 0;
+ useIdx = 1;
+ if( iCol<0 ){
+ zCol = "rowid";
+ }
+ switch( pConstraint->op ){
+ case SQLITE_INDEX_CONSTRAINT_EQ:
+ zOp = "="; break;
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ zOp = "<"; break;
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ zOp = ">"; break;
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ zOp = "<="; break;
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ zOp = ">="; break;
+ case SQLITE_INDEX_CONSTRAINT_MATCH:
+ zOp = "LIKE"; break;
+ }
+ if( zOp[0]=='L' ){
+ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')",
+ zSep, zCol);
+ } else {
+ zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp);
+ }
+ string_concat(&zQuery, zNew, 1, &rc);
+
+ zSep = "AND";
+ pUsage->argvIndex = ++nArg;
+ pUsage->omit = 1;
+ }
+ }
+
+ /* If there is only one term in the ORDER BY clause, and it is
+ ** on a column that this virtual table has an index for, then consume
+ ** the ORDER BY clause.
+ */
+ if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){
+ int iCol = pIdxInfo->aOrderBy->iColumn;
+ char *zCol = pVtab->aCol[iCol];
+ char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
+ if( iCol<0 ){
+ zCol = "rowid";
+ }
+ zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
+ string_concat(&zQuery, zNew, 1, &rc);
+ pIdxInfo->orderByConsumed = 1;
+ }
+
+ appendToEchoModule(pVtab->interp, "xBestIndex");;
+ appendToEchoModule(pVtab->interp, zQuery);
+
+ if( !zQuery ){
+ return rc;
+ }
+ pIdxInfo->idxNum = hashString(zQuery);
+ pIdxInfo->idxStr = zQuery;
+ pIdxInfo->needToFreeIdxStr = 1;
+ if (useCost) {
+ pIdxInfo->estimatedCost = cost;
+ } else if( useIdx ){
+ /* Approximation of log2(nRow). */
+ for( ii=0; ii<(sizeof(int)*8); ii++ ){
+ if( nRow & (1<<ii) ){
+ pIdxInfo->estimatedCost = (double)ii;
+ }
+ }
+ } else {
+ pIdxInfo->estimatedCost = (double)nRow;
+ }
+ return rc;
+}
+
+/*
+** The xUpdate method for echo module virtual tables.
+**
+** apData[0] apData[1] apData[2..]
+**
+** INTEGER DELETE
+**
+** INTEGER NULL (nCol args) UPDATE (do not set rowid)
+** INTEGER INTEGER (nCol args) UPDATE (with SET rowid = <arg1>)
+**
+** NULL NULL (nCol args) INSERT INTO (automatic rowid value)
+** NULL INTEGER (nCol args) INSERT (incl. rowid value)
+**
+*/
+int echoUpdate(
+ sqlite3_vtab *tab,
+ int nData,
+ sqlite3_value **apData,
+ sqlite_int64 *pRowid
+){
+ echo_vtab *pVtab = (echo_vtab *)tab;
+ sqlite3 *db = pVtab->db;
+ int rc = SQLITE_OK;
+
+ sqlite3_stmt *pStmt;
+ char *z = 0; /* SQL statement to execute */
+ int bindArgZero = 0; /* True to bind apData[0] to sql var no. nData */
+ int bindArgOne = 0; /* True to bind apData[1] to sql var no. 1 */
+ int i; /* Counter variable used by for loops */
+
+ assert( nData==pVtab->nCol+2 || nData==1 );
+
+ /* Ticket #3083 - make sure we always start a transaction prior to
+ ** making any changes to a virtual table */
+ assert( pVtab->inTransaction );
+
+ if( simulateVtabError(pVtab, "xUpdate") ){
+ return SQLITE_ERROR;
+ }
+
+ /* If apData[0] is an integer and nData>1 then do an UPDATE */
+ if( nData>1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
+ char *zSep = " SET";
+ z = sqlite3_mprintf("UPDATE %Q", pVtab->zTableName);
+ if( !z ){
+ rc = SQLITE_NOMEM;
+ }
+
+ bindArgOne = (apData[1] && sqlite3_value_type(apData[1])==SQLITE_INTEGER);
+ bindArgZero = 1;
+
+ if( bindArgOne ){
+ string_concat(&z, " SET rowid=?1 ", 0, &rc);
+ zSep = ",";
+ }
+ for(i=2; i<nData; i++){
+ if( apData[i]==0 ) continue;
+ string_concat(&z, sqlite3_mprintf(
+ "%s %Q=?%d", zSep, pVtab->aCol[i-2], i), 1, &rc);
+ zSep = ",";
+ }
+ string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 1, &rc);
+ }
+
+ /* If apData[0] is an integer and nData==1 then do a DELETE */
+ else if( nData==1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
+ z = sqlite3_mprintf("DELETE FROM %Q WHERE rowid = ?1", pVtab->zTableName);
+ if( !z ){
+ rc = SQLITE_NOMEM;
+ }
+ bindArgZero = 1;
+ }
+
+ /* If the first argument is NULL and there are more than two args, INSERT */
+ else if( nData>2 && sqlite3_value_type(apData[0])==SQLITE_NULL ){
+ int ii;
+ char *zInsert = 0;
+ char *zValues = 0;
+
+ zInsert = sqlite3_mprintf("INSERT INTO %Q (", pVtab->zTableName);
+ if( !zInsert ){
+ rc = SQLITE_NOMEM;
+ }
+ if( sqlite3_value_type(apData[1])==SQLITE_INTEGER ){
+ bindArgOne = 1;
+ zValues = sqlite3_mprintf("?");
+ string_concat(&zInsert, "rowid", 0, &rc);
+ }
+
+ assert((pVtab->nCol+2)==nData);
+ for(ii=2; ii<nData; ii++){
+ string_concat(&zInsert,
+ sqlite3_mprintf("%s%Q", zValues?", ":"", pVtab->aCol[ii-2]), 1, &rc);
+ string_concat(&zValues,
+ sqlite3_mprintf("%s?%d", zValues?", ":"", ii), 1, &rc);
+ }
+
+ string_concat(&z, zInsert, 1, &rc);
+ string_concat(&z, ") VALUES(", 0, &rc);
+ string_concat(&z, zValues, 1, &rc);
+ string_concat(&z, ")", 0, &rc);
+ }
+
+ /* Anything else is an error */
+ else{
+ assert(0);
+ return SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare(db, z, -1, &pStmt, 0);
+ }
+ assert( rc!=SQLITE_OK || pStmt );
+ sqlite3_free(z);
+ if( rc==SQLITE_OK ) {
+ if( bindArgZero ){
+ sqlite3_bind_value(pStmt, nData, apData[0]);
+ }
+ if( bindArgOne ){
+ sqlite3_bind_value(pStmt, 1, apData[1]);
+ }
+ for(i=2; i<nData && rc==SQLITE_OK; i++){
+ if( apData[i] ) rc = sqlite3_bind_value(pStmt, i, apData[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_finalize(pStmt);
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+ }
+
+ if( pRowid && rc==SQLITE_OK ){
+ *pRowid = sqlite3_last_insert_rowid(db);
+ }
+ if( rc!=SQLITE_OK ){
+ tab->zErrMsg = sqlite3_mprintf("echo-vtab-error: %s", sqlite3_errmsg(db));
+ }
+
+ return rc;
+}
+
+/*
+** xBegin, xSync, xCommit and xRollback callbacks for echo module
+** virtual tables. Do nothing other than add the name of the callback
+** to the $::echo_module Tcl variable.
+*/
+static int echoTransactionCall(sqlite3_vtab *tab, const char *zCall){
+ char *z;
+ echo_vtab *pVtab = (echo_vtab *)tab;
+ z = sqlite3_mprintf("echo(%s)", pVtab->zTableName);
+ if( z==0 ) return SQLITE_NOMEM;
+ appendToEchoModule(pVtab->interp, zCall);
+ appendToEchoModule(pVtab->interp, z);
+ sqlite3_free(z);
+ return SQLITE_OK;
+}
+static int echoBegin(sqlite3_vtab *tab){
+ int rc;
+ echo_vtab *pVtab = (echo_vtab *)tab;
+ Tcl_Interp *interp = pVtab->interp;
+ const char *zVal;
+
+ /* Ticket #3083 - do not start a transaction if we are already in
+ ** a transaction */
+ assert( !pVtab->inTransaction );
+
+ if( simulateVtabError(pVtab, "xBegin") ){
+ return SQLITE_ERROR;
+ }
+
+ rc = echoTransactionCall(tab, "xBegin");
+
+ if( rc==SQLITE_OK ){
+ /* Check if the $::echo_module_begin_fail variable is defined. If it is,
+ ** and it is set to the name of the real table underlying this virtual
+ ** echo module table, then cause this xSync operation to fail.
+ */
+ zVal = Tcl_GetVar(interp, "echo_module_begin_fail", TCL_GLOBAL_ONLY);
+ if( zVal && 0==strcmp(zVal, pVtab->zTableName) ){
+ rc = SQLITE_ERROR;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pVtab->inTransaction = 1;
+ }
+ return rc;
+}
+static int echoSync(sqlite3_vtab *tab){
+ int rc;
+ echo_vtab *pVtab = (echo_vtab *)tab;
+ Tcl_Interp *interp = pVtab->interp;
+ const char *zVal;
+
+ /* Ticket #3083 - Only call xSync if we have previously started a
+ ** transaction */
+ assert( pVtab->inTransaction );
+
+ if( simulateVtabError(pVtab, "xSync") ){
+ return SQLITE_ERROR;
+ }
+
+ rc = echoTransactionCall(tab, "xSync");
+
+ if( rc==SQLITE_OK ){
+ /* Check if the $::echo_module_sync_fail variable is defined. If it is,
+ ** and it is set to the name of the real table underlying this virtual
+ ** echo module table, then cause this xSync operation to fail.
+ */
+ zVal = Tcl_GetVar(interp, "echo_module_sync_fail", TCL_GLOBAL_ONLY);
+ if( zVal && 0==strcmp(zVal, pVtab->zTableName) ){
+ rc = -1;
+ }
+ }
+ return rc;
+}
+static int echoCommit(sqlite3_vtab *tab){
+ echo_vtab *pVtab = (echo_vtab*)tab;
+ int rc;
+
+ /* Ticket #3083 - Only call xCommit if we have previously started
+ ** a transaction */
+ assert( pVtab->inTransaction );
+
+ if( simulateVtabError(pVtab, "xCommit") ){
+ return SQLITE_ERROR;
+ }
+
+ sqlite3BeginBenignMalloc();
+ rc = echoTransactionCall(tab, "xCommit");
+ sqlite3EndBenignMalloc();
+ pVtab->inTransaction = 0;
+ return rc;
+}
+static int echoRollback(sqlite3_vtab *tab){
+ int rc;
+ echo_vtab *pVtab = (echo_vtab*)tab;
+
+ /* Ticket #3083 - Only call xRollback if we have previously started
+ ** a transaction */
+ assert( pVtab->inTransaction );
+
+ rc = echoTransactionCall(tab, "xRollback");
+ pVtab->inTransaction = 0;
+ return rc;
+}
+
+/*
+** Implementation of "GLOB" function on the echo module. Pass
+** all arguments to the ::echo_glob_overload procedure of TCL
+** and return the result of that procedure as a string.
+*/
+static void overloadedGlobFunction(
+ sqlite3_context *pContext,
+ int nArg,
+ sqlite3_value **apArg
+){
+ Tcl_Interp *interp = sqlite3_user_data(pContext);
+ Tcl_DString str;
+ int i;
+ int rc;
+ Tcl_DStringInit(&str);
+ Tcl_DStringAppendElement(&str, "::echo_glob_overload");
+ for(i=0; i<nArg; i++){
+ Tcl_DStringAppendElement(&str, (char*)sqlite3_value_text(apArg[i]));
+ }
+ rc = Tcl_Eval(interp, Tcl_DStringValue(&str));
+ Tcl_DStringFree(&str);
+ if( rc ){
+ sqlite3_result_error(pContext, Tcl_GetStringResult(interp), -1);
+ }else{
+ sqlite3_result_text(pContext, Tcl_GetStringResult(interp),
+ -1, SQLITE_TRANSIENT);
+ }
+ Tcl_ResetResult(interp);
+}
+
+/*
+** This is the xFindFunction implementation for the echo module.
+** SQLite calls this routine when the first argument of a function
+** is a column of an echo virtual table. This routine can optionally
+** override the implementation of that function. It will choose to
+** do so if the function is named "glob", and a TCL command named
+** ::echo_glob_overload exists.
+*/
+static int echoFindFunction(
+ sqlite3_vtab *vtab,
+ int nArg,
+ const char *zFuncName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ echo_vtab *pVtab = (echo_vtab *)vtab;
+ Tcl_Interp *interp = pVtab->interp;
+ Tcl_CmdInfo info;
+ if( strcmp(zFuncName,"glob")!=0 ){
+ return 0;
+ }
+ if( Tcl_GetCommandInfo(interp, "::echo_glob_overload", &info)==0 ){
+ return 0;
+ }
+ *pxFunc = overloadedGlobFunction;
+ *ppArg = interp;
+ return 1;
+}
+
+static int echoRename(sqlite3_vtab *vtab, const char *zNewName){
+ int rc = SQLITE_OK;
+ echo_vtab *p = (echo_vtab *)vtab;
+
+ if( simulateVtabError(p, "xRename") ){
+ return SQLITE_ERROR;
+ }
+
+ if( p->isPattern ){
+ int nThis = strlen(p->zThis);
+ char *zSql = sqlite3MPrintf(0, "ALTER TABLE %s RENAME TO %s%s",
+ p->zTableName, zNewName, &p->zTableName[nThis]
+ );
+ rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
+/*
+** A virtual table module that merely "echos" the contents of another
+** table (like an SQL VIEW).
+*/
+static sqlite3_module echoModule = {
+ 0, /* iVersion */
+ echoCreate,
+ echoConnect,
+ echoBestIndex,
+ echoDisconnect,
+ echoDestroy,
+ echoOpen, /* xOpen - open a cursor */
+ echoClose, /* xClose - close a cursor */
+ echoFilter, /* xFilter - configure scan constraints */
+ echoNext, /* xNext - advance a cursor */
+ echoEof, /* xEof */
+ echoColumn, /* xColumn - read data */
+ echoRowid, /* xRowid - read data */
+ echoUpdate, /* xUpdate - write data */
+ echoBegin, /* xBegin - begin transaction */
+ echoSync, /* xSync - sync transaction */
+ echoCommit, /* xCommit - commit transaction */
+ echoRollback, /* xRollback - rollback transaction */
+ echoFindFunction, /* xFindFunction - function overloading */
+ echoRename, /* xRename - rename the table */
+};
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+
+static void moduleDestroy(void *p){
+ sqlite3_free(p);
+}
+
+/*
+** Register the echo virtual table module.
+*/
+static int register_echo_module(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ EchoModule *pMod;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ pMod = sqlite3_malloc(sizeof(EchoModule));
+ pMod->interp = interp;
+ sqlite3_create_module_v2(db, "echo", &echoModule, (void*)pMod, moduleDestroy);
+ return TCL_OK;
+}
+
+/*
+** Tcl interface to sqlite3_declare_vtab, invoked as follows from Tcl:
+**
+** sqlite3_declare_vtab DB SQL
+*/
+static int declare_vtab(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ int rc;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ rc = sqlite3_declare_vtab(db, Tcl_GetString(objv[2]));
+ if( rc!=SQLITE_OK ){
+ Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest8_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ { "register_echo_module", register_echo_module, 0 },
+ { "sqlite3_declare_vtab", declare_vtab, 0 },
+#endif
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test9.c b/third_party/sqlite/src/test9.c
new file mode 100755
index 0000000..2043da2
--- /dev/null
+++ b/third_party/sqlite/src/test9.c
@@ -0,0 +1,193 @@
+/*
+** 2007 March 29
+**
+** 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 contains obscure tests of the C-interface required
+** for completeness. Test code is written in C for these cases
+** as there is not much point in binding to Tcl.
+**
+** $Id: test9.c,v 1.6 2008/07/11 13:53:55 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** c_collation_test
+*/
+static int c_collation_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ const char *zErrFunction = "N/A";
+ sqlite3 *db;
+
+ int rc;
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ /* Open a database. */
+ rc = sqlite3_open(":memory:", &db);
+ if( rc!=SQLITE_OK ){
+ zErrFunction = "sqlite3_open";
+ goto error_out;
+ }
+
+ rc = sqlite3_create_collation(db, "collate", 456, 0, 0);
+ if( rc!=SQLITE_MISUSE ){
+ sqlite3_close(db);
+ zErrFunction = "sqlite3_create_collation";
+ goto error_out;
+ }
+
+ sqlite3_close(db);
+ return TCL_OK;
+
+error_out:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
+ return TCL_ERROR;
+}
+
+/*
+** c_realloc_test
+*/
+static int c_realloc_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ void *p;
+ const char *zErrFunction = "N/A";
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ p = sqlite3_malloc(5);
+ if( !p ){
+ zErrFunction = "sqlite3_malloc";
+ goto error_out;
+ }
+
+ /* Test that realloc()ing a block of memory to a negative size is
+ ** the same as free()ing that memory.
+ */
+ p = sqlite3_realloc(p, -1);
+ if( p ){
+ zErrFunction = "sqlite3_realloc";
+ goto error_out;
+ }
+
+ return TCL_OK;
+
+error_out:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
+ return TCL_ERROR;
+}
+
+
+/*
+** c_misuse_test
+*/
+static int c_misuse_test(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ const char *zErrFunction = "N/A";
+ sqlite3 *db = 0;
+ int rc;
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ /* Open a database. Then close it again. We need to do this so that
+ ** we have a "closed database handle" to pass to various API functions.
+ */
+ rc = sqlite3_open(":memory:", &db);
+ if( rc!=SQLITE_OK ){
+ zErrFunction = "sqlite3_open";
+ goto error_out;
+ }
+ sqlite3_close(db);
+
+
+ rc = sqlite3_errcode(db);
+ if( rc!=SQLITE_MISUSE ){
+ zErrFunction = "sqlite3_errcode";
+ goto error_out;
+ }
+
+ rc = sqlite3_prepare(db, 0, 0, 0, 0);
+ if( rc!=SQLITE_MISUSE ){
+ zErrFunction = "sqlite3_prepare";
+ goto error_out;
+ }
+
+ rc = sqlite3_prepare_v2(db, 0, 0, 0, 0);
+ if( rc!=SQLITE_MISUSE ){
+ zErrFunction = "sqlite3_prepare_v2";
+ goto error_out;
+ }
+
+#ifndef SQLITE_OMIT_UTF16
+ rc = sqlite3_prepare16(db, 0, 0, 0, 0);
+ if( rc!=SQLITE_MISUSE ){
+ zErrFunction = "sqlite3_prepare16";
+ goto error_out;
+ }
+ rc = sqlite3_prepare16_v2(db, 0, 0, 0, 0);
+ if( rc!=SQLITE_MISUSE ){
+ zErrFunction = "sqlite3_prepare16_v2";
+ goto error_out;
+ }
+#endif
+
+ return TCL_OK;
+
+error_out:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
+ return TCL_ERROR;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest9_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+ { "c_misuse_test", c_misuse_test, 0 },
+ { "c_realloc_test", c_realloc_test, 0 },
+ { "c_collation_test", c_collation_test, 0 },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_async.c b/third_party/sqlite/src/test_async.c
new file mode 100755
index 0000000..7b48afe
--- /dev/null
+++ b/third_party/sqlite/src/test_async.c
@@ -0,0 +1,1717 @@
+/*
+** 2005 December 14
+**
+** 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.
+**
+*************************************************************************
+**
+** $Id: test_async.c,v 1.45 2008/06/26 10:41:19 danielk1977 Exp $
+**
+** This file contains an example implementation of an asynchronous IO
+** backend for SQLite.
+**
+** WHAT IS ASYNCHRONOUS I/O?
+**
+** With asynchronous I/O, write requests are handled by a separate thread
+** running in the background. This means that the thread that initiates
+** a database write does not have to wait for (sometimes slow) disk I/O
+** to occur. The write seems to happen very quickly, though in reality
+** it is happening at its usual slow pace in the background.
+**
+** Asynchronous I/O appears to give better responsiveness, but at a price.
+** You lose the Durable property. With the default I/O backend of SQLite,
+** once a write completes, you know that the information you wrote is
+** safely on disk. With the asynchronous I/O, this is not the case. If
+** your program crashes or if a power lose occurs after the database
+** write but before the asynchronous write thread has completed, then the
+** database change might never make it to disk and the next user of the
+** database might not see your change.
+**
+** You lose Durability with asynchronous I/O, but you still retain the
+** other parts of ACID: Atomic, Consistent, and Isolated. Many
+** appliations get along fine without the Durablity.
+**
+** HOW IT WORKS
+**
+** Asynchronous I/O works by creating a special SQLite "vfs" structure
+** and registering it with sqlite3_vfs_register(). When files opened via
+** this vfs are written to (using sqlite3OsWrite()), the data is not
+** written directly to disk, but is placed in the "write-queue" to be
+** handled by the background thread.
+**
+** When files opened with the asynchronous vfs are read from
+** (using sqlite3OsRead()), the data is read from the file on
+** disk and the write-queue, so that from the point of view of
+** the vfs reader the OsWrite() appears to have already completed.
+**
+** The special vfs is registered (and unregistered) by calls to
+** function asyncEnable() (see below).
+**
+** LIMITATIONS
+**
+** This demonstration code is deliberately kept simple in order to keep
+** the main ideas clear and easy to understand. Real applications that
+** want to do asynchronous I/O might want to add additional capabilities.
+** For example, in this demonstration if writes are happening at a steady
+** stream that exceeds the I/O capability of the background writer thread,
+** the queue of pending write operations will grow without bound until we
+** run out of memory. Users of this technique may want to keep track of
+** the quantity of pending writes and stop accepting new write requests
+** when the buffer gets to be too big.
+**
+** LOCKING + CONCURRENCY
+**
+** Multiple connections from within a single process that use this
+** implementation of asynchronous IO may access a single database
+** file concurrently. From the point of view of the user, if all
+** connections are from within a single process, there is no difference
+** between the concurrency offered by "normal" SQLite and SQLite
+** using the asynchronous backend.
+**
+** If connections from within multiple database files may access the
+** database file, the ENABLE_FILE_LOCKING symbol (see below) must be
+** defined. If it is not defined, then no locks are established on
+** the database file. In this case, if multiple processes access
+** the database file, corruption will quickly result.
+**
+** If ENABLE_FILE_LOCKING is defined (the default), then connections
+** from within multiple processes may access a single database file
+** without risking corruption. However concurrency is reduced as
+** follows:
+**
+** * When a connection using asynchronous IO begins a database
+** transaction, the database is locked immediately. However the
+** lock is not released until after all relevant operations
+** in the write-queue have been flushed to disk. This means
+** (for example) that the database may remain locked for some
+** time after a "COMMIT" or "ROLLBACK" is issued.
+**
+** * If an application using asynchronous IO executes transactions
+** in quick succession, other database users may be effectively
+** locked out of the database. This is because when a BEGIN
+** is executed, a database lock is established immediately. But
+** when the corresponding COMMIT or ROLLBACK occurs, the lock
+** is not released until the relevant part of the write-queue
+** has been flushed through. As a result, if a COMMIT is followed
+** by a BEGIN before the write-queue is flushed through, the database
+** is never unlocked,preventing other processes from accessing
+** the database.
+**
+** Defining ENABLE_FILE_LOCKING when using an NFS or other remote
+** file-system may slow things down, as synchronous round-trips to the
+** server may be required to establish database file locks.
+*/
+#define ENABLE_FILE_LOCKING
+
+#ifndef SQLITE_AMALGAMATION
+# include "sqliteInt.h"
+#endif
+#include <tcl.h>
+
+/*
+** This test uses pthreads and hence only works on unix and with
+** a threadsafe build of SQLite.
+*/
+#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
+
+/*
+** This demo uses pthreads. If you do not have a pthreads implementation
+** for your operating system, you will need to recode the threading
+** logic.
+*/
+#include <pthread.h>
+#include <sched.h>
+
+/* Useful macros used in several places */
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+/* Forward references */
+typedef struct AsyncWrite AsyncWrite;
+typedef struct AsyncFile AsyncFile;
+typedef struct AsyncFileData AsyncFileData;
+typedef struct AsyncFileLock AsyncFileLock;
+typedef struct AsyncLock AsyncLock;
+
+/* Enable for debugging */
+static int sqlite3async_trace = 0;
+# define ASYNC_TRACE(X) if( sqlite3async_trace ) asyncTrace X
+static void asyncTrace(const char *zFormat, ...){
+ char *z;
+ va_list ap;
+ va_start(ap, zFormat);
+ z = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ fprintf(stderr, "[%d] %s", (int)pthread_self(), z);
+ sqlite3_free(z);
+}
+
+/*
+** THREAD SAFETY NOTES
+**
+** Basic rules:
+**
+** * Both read and write access to the global write-op queue must be
+** protected by the async.queueMutex. As are the async.ioError and
+** async.nFile variables.
+**
+** * The async.aLock hash-table and all AsyncLock and AsyncFileLock
+** structures must be protected by the async.lockMutex mutex.
+**
+** * The file handles from the underlying system are assumed not to
+** be thread safe.
+**
+** * See the last two paragraphs under "The Writer Thread" for
+** an assumption to do with file-handle synchronization by the Os.
+**
+** Deadlock prevention:
+**
+** There are three mutex used by the system: the "writer" mutex,
+** the "queue" mutex and the "lock" mutex. Rules are:
+**
+** * It is illegal to block on the writer mutex when any other mutex
+** are held, and
+**
+** * It is illegal to block on the queue mutex when the lock mutex
+** is held.
+**
+** i.e. mutex's must be grabbed in the order "writer", "queue", "lock".
+**
+** File system operations (invoked by SQLite thread):
+**
+** xOpen
+** xDelete
+** xFileExists
+**
+** File handle operations (invoked by SQLite thread):
+**
+** asyncWrite, asyncClose, asyncTruncate, asyncSync
+**
+** The operations above add an entry to the global write-op list. They
+** prepare the entry, acquire the async.queueMutex momentarily while
+** list pointers are manipulated to insert the new entry, then release
+** the mutex and signal the writer thread to wake up in case it happens
+** to be asleep.
+**
+**
+** asyncRead, asyncFileSize.
+**
+** Read operations. Both of these read from both the underlying file
+** first then adjust their result based on pending writes in the
+** write-op queue. So async.queueMutex is held for the duration
+** of these operations to prevent other threads from changing the
+** queue in mid operation.
+**
+**
+** asyncLock, asyncUnlock, asyncCheckReservedLock
+**
+** These primitives implement in-process locking using a hash table
+** on the file name. Files are locked correctly for connections coming
+** from the same process. But other processes cannot see these locks
+** and will therefore not honor them.
+**
+**
+** The writer thread:
+**
+** The async.writerMutex is used to make sure only there is only
+** a single writer thread running at a time.
+**
+** Inside the writer thread is a loop that works like this:
+**
+** WHILE (write-op list is not empty)
+** Do IO operation at head of write-op list
+** Remove entry from head of write-op list
+** END WHILE
+**
+** The async.queueMutex is always held during the <write-op list is
+** not empty> test, and when the entry is removed from the head
+** of the write-op list. Sometimes it is held for the interim
+** period (while the IO is performed), and sometimes it is
+** relinquished. It is relinquished if (a) the IO op is an
+** ASYNC_CLOSE or (b) when the file handle was opened, two of
+** the underlying systems handles were opened on the same
+** file-system entry.
+**
+** If condition (b) above is true, then one file-handle
+** (AsyncFile.pBaseRead) is used exclusively by sqlite threads to read the
+** file, the other (AsyncFile.pBaseWrite) by sqlite3_async_flush()
+** threads to perform write() operations. This means that read
+** operations are not blocked by asynchronous writes (although
+** asynchronous writes may still be blocked by reads).
+**
+** This assumes that the OS keeps two handles open on the same file
+** properly in sync. That is, any read operation that starts after a
+** write operation on the same file system entry has completed returns
+** data consistent with the write. We also assume that if one thread
+** reads a file while another is writing it all bytes other than the
+** ones actually being written contain valid data.
+**
+** If the above assumptions are not true, set the preprocessor symbol
+** SQLITE_ASYNC_TWO_FILEHANDLES to 0.
+*/
+
+#ifndef SQLITE_ASYNC_TWO_FILEHANDLES
+/* #define SQLITE_ASYNC_TWO_FILEHANDLES 0 */
+#define SQLITE_ASYNC_TWO_FILEHANDLES 1
+#endif
+
+/*
+** State information is held in the static variable "async" defined
+** as the following structure.
+**
+** Both async.ioError and async.nFile are protected by async.queueMutex.
+*/
+static struct TestAsyncStaticData {
+ pthread_mutex_t lockMutex; /* For access to aLock hash table */
+ pthread_mutex_t queueMutex; /* Mutex for access to write operation queue */
+ pthread_mutex_t writerMutex; /* Prevents multiple writer threads */
+ pthread_cond_t queueSignal; /* For waking up sleeping writer thread */
+ pthread_cond_t emptySignal; /* Notify when the write queue is empty */
+ AsyncWrite *pQueueFirst; /* Next write operation to be processed */
+ AsyncWrite *pQueueLast; /* Last write operation on the list */
+ Hash aLock; /* Files locked */
+ volatile int ioDelay; /* Extra delay between write operations */
+ volatile int writerHaltWhenIdle; /* Writer thread halts when queue empty */
+ volatile int writerHaltNow; /* Writer thread halts after next op */
+ int ioError; /* True if an IO error has occured */
+ int nFile; /* Number of open files (from sqlite pov) */
+} async = {
+ PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_COND_INITIALIZER,
+ PTHREAD_COND_INITIALIZER,
+};
+
+/* Possible values of AsyncWrite.op */
+#define ASYNC_NOOP 0
+#define ASYNC_WRITE 1
+#define ASYNC_SYNC 2
+#define ASYNC_TRUNCATE 3
+#define ASYNC_CLOSE 4
+#define ASYNC_DELETE 5
+#define ASYNC_OPENEXCLUSIVE 6
+#define ASYNC_UNLOCK 7
+
+/* Names of opcodes. Used for debugging only.
+** Make sure these stay in sync with the macros above!
+*/
+static const char *azOpcodeName[] = {
+ "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX", "UNLOCK"
+};
+
+/*
+** Entries on the write-op queue are instances of the AsyncWrite
+** structure, defined here.
+**
+** The interpretation of the iOffset and nByte variables varies depending
+** on the value of AsyncWrite.op:
+**
+** ASYNC_NOOP:
+** No values used.
+**
+** ASYNC_WRITE:
+** iOffset -> Offset in file to write to.
+** nByte -> Number of bytes of data to write (pointed to by zBuf).
+**
+** ASYNC_SYNC:
+** nByte -> flags to pass to sqlite3OsSync().
+**
+** ASYNC_TRUNCATE:
+** iOffset -> Size to truncate file to.
+** nByte -> Unused.
+**
+** ASYNC_CLOSE:
+** iOffset -> Unused.
+** nByte -> Unused.
+**
+** ASYNC_DELETE:
+** iOffset -> Contains the "syncDir" flag.
+** nByte -> Number of bytes of zBuf points to (file name).
+**
+** ASYNC_OPENEXCLUSIVE:
+** iOffset -> Value of "delflag".
+** nByte -> Number of bytes of zBuf points to (file name).
+**
+** ASYNC_UNLOCK:
+** nByte -> Argument to sqlite3OsUnlock().
+**
+**
+** For an ASYNC_WRITE operation, zBuf points to the data to write to the file.
+** This space is sqlite3_malloc()d along with the AsyncWrite structure in a
+** single blob, so is deleted when sqlite3_free() is called on the parent
+** structure.
+*/
+struct AsyncWrite {
+ AsyncFileData *pFileData; /* File to write data to or sync */
+ int op; /* One of ASYNC_xxx etc. */
+ i64 iOffset; /* See above */
+ int nByte; /* See above */
+ char *zBuf; /* Data to write to file (or NULL if op!=ASYNC_WRITE) */
+ AsyncWrite *pNext; /* Next write operation (to any file) */
+};
+
+/*
+** An instance of this structure is created for each distinct open file
+** (i.e. if two handles are opened on the one file, only one of these
+** structures is allocated) and stored in the async.aLock hash table. The
+** keys for async.aLock are the full pathnames of the opened files.
+**
+** AsyncLock.pList points to the head of a linked list of AsyncFileLock
+** structures, one for each handle currently open on the file.
+**
+** If the opened file is not a main-database (the SQLITE_OPEN_MAIN_DB is
+** not passed to the sqlite3OsOpen() call), or if ENABLE_FILE_LOCKING is
+** not defined at compile time, variables AsyncLock.pFile and
+** AsyncLock.eLock are never used. Otherwise, pFile is a file handle
+** opened on the file in question and used to obtain the file-system
+** locks required by database connections within this process.
+**
+** See comments above the asyncLock() function for more details on
+** the implementation of database locking used by this backend.
+*/
+struct AsyncLock {
+ sqlite3_file *pFile;
+ int eLock;
+ AsyncFileLock *pList;
+};
+
+/*
+** An instance of the following structure is allocated along with each
+** AsyncFileData structure (see AsyncFileData.lock), but is only used if the
+** file was opened with the SQLITE_OPEN_MAIN_DB.
+*/
+struct AsyncFileLock {
+ int eLock; /* Internally visible lock state (sqlite pov) */
+ int eAsyncLock; /* Lock-state with write-queue unlock */
+ AsyncFileLock *pNext;
+};
+
+/*
+** The AsyncFile structure is a subclass of sqlite3_file used for
+** asynchronous IO.
+**
+** All of the actual data for the structure is stored in the structure
+** pointed to by AsyncFile.pData, which is allocated as part of the
+** sqlite3OsOpen() using sqlite3_malloc(). The reason for this is that the
+** lifetime of the AsyncFile structure is ended by the caller after OsClose()
+** is called, but the data in AsyncFileData may be required by the
+** writer thread after that point.
+*/
+struct AsyncFile {
+ sqlite3_io_methods *pMethod;
+ AsyncFileData *pData;
+};
+struct AsyncFileData {
+ char *zName; /* Underlying OS filename - used for debugging */
+ int nName; /* Number of characters in zName */
+ sqlite3_file *pBaseRead; /* Read handle to the underlying Os file */
+ sqlite3_file *pBaseWrite; /* Write handle to the underlying Os file */
+ AsyncFileLock lock;
+ AsyncWrite close;
+};
+
+/*
+** The following async_XXX functions are debugging wrappers around the
+** corresponding pthread_XXX functions:
+**
+** pthread_mutex_lock();
+** pthread_mutex_unlock();
+** pthread_mutex_trylock();
+** pthread_cond_wait();
+**
+** It is illegal to pass any mutex other than those stored in the
+** following global variables of these functions.
+**
+** async.queueMutex
+** async.writerMutex
+** async.lockMutex
+**
+** If NDEBUG is defined, these wrappers do nothing except call the
+** corresponding pthreads function. If NDEBUG is not defined, then the
+** following variables are used to store the thread-id (as returned
+** by pthread_self()) currently holding the mutex, or 0 otherwise:
+**
+** asyncdebug.queueMutexHolder
+** asyncdebug.writerMutexHolder
+** asyncdebug.lockMutexHolder
+**
+** These variables are used by some assert() statements that verify
+** the statements made in the "Deadlock Prevention" notes earlier
+** in this file.
+*/
+#ifndef NDEBUG
+
+static struct TestAsyncDebugData {
+ pthread_t lockMutexHolder;
+ pthread_t queueMutexHolder;
+ pthread_t writerMutexHolder;
+} asyncdebug = {0, 0, 0};
+
+/*
+** Wrapper around pthread_mutex_lock(). Checks that we have not violated
+** the anti-deadlock rules (see "Deadlock prevention" above).
+*/
+static int async_mutex_lock(pthread_mutex_t *pMutex){
+ int iIdx;
+ int rc;
+ pthread_mutex_t *aMutex = (pthread_mutex_t *)(&async);
+ pthread_t *aHolder = (pthread_t *)(&asyncdebug);
+
+ /* The code in this 'ifndef NDEBUG' block depends on a certain alignment
+ * of the variables in TestAsyncStaticData and TestAsyncDebugData. The
+ * following assert() statements check that this has not been changed.
+ *
+ * Really, these only need to be run once at startup time.
+ */
+ assert(&(aMutex[0])==&async.lockMutex);
+ assert(&(aMutex[1])==&async.queueMutex);
+ assert(&(aMutex[2])==&async.writerMutex);
+ assert(&(aHolder[0])==&asyncdebug.lockMutexHolder);
+ assert(&(aHolder[1])==&asyncdebug.queueMutexHolder);
+ assert(&(aHolder[2])==&asyncdebug.writerMutexHolder);
+
+ assert( pthread_self()!=0 );
+
+ for(iIdx=0; iIdx<3; iIdx++){
+ if( pMutex==&aMutex[iIdx] ) break;
+
+ /* This is the key assert(). Here we are checking that if the caller
+ * is trying to block on async.writerMutex, neither of the other two
+ * mutex are held. If the caller is trying to block on async.queueMutex,
+ * lockMutex is not held.
+ */
+ assert(!pthread_equal(aHolder[iIdx], pthread_self()));
+ }
+ assert(iIdx<3);
+
+ rc = pthread_mutex_lock(pMutex);
+ if( rc==0 ){
+ assert(aHolder[iIdx]==0);
+ aHolder[iIdx] = pthread_self();
+ }
+ return rc;
+}
+
+/*
+** Wrapper around pthread_mutex_unlock().
+*/
+static int async_mutex_unlock(pthread_mutex_t *pMutex){
+ int iIdx;
+ int rc;
+ pthread_mutex_t *aMutex = (pthread_mutex_t *)(&async);
+ pthread_t *aHolder = (pthread_t *)(&asyncdebug);
+
+ for(iIdx=0; iIdx<3; iIdx++){
+ if( pMutex==&aMutex[iIdx] ) break;
+ }
+ assert(iIdx<3);
+
+ assert(pthread_equal(aHolder[iIdx], pthread_self()));
+ aHolder[iIdx] = 0;
+ rc = pthread_mutex_unlock(pMutex);
+ assert(rc==0);
+
+ return 0;
+}
+
+/*
+** Wrapper around pthread_mutex_trylock().
+*/
+static int async_mutex_trylock(pthread_mutex_t *pMutex){
+ int iIdx;
+ int rc;
+ pthread_mutex_t *aMutex = (pthread_mutex_t *)(&async);
+ pthread_t *aHolder = (pthread_t *)(&asyncdebug);
+
+ for(iIdx=0; iIdx<3; iIdx++){
+ if( pMutex==&aMutex[iIdx] ) break;
+ }
+ assert(iIdx<3);
+
+ rc = pthread_mutex_trylock(pMutex);
+ if( rc==0 ){
+ assert(aHolder[iIdx]==0);
+ aHolder[iIdx] = pthread_self();
+ }
+ return rc;
+}
+
+/*
+** Wrapper around pthread_cond_wait().
+*/
+static int async_cond_wait(pthread_cond_t *pCond, pthread_mutex_t *pMutex){
+ int iIdx;
+ int rc;
+ pthread_mutex_t *aMutex = (pthread_mutex_t *)(&async);
+ pthread_t *aHolder = (pthread_t *)(&asyncdebug);
+
+ for(iIdx=0; iIdx<3; iIdx++){
+ if( pMutex==&aMutex[iIdx] ) break;
+ }
+ assert(iIdx<3);
+
+ assert(pthread_equal(aHolder[iIdx],pthread_self()));
+ aHolder[iIdx] = 0;
+ rc = pthread_cond_wait(pCond, pMutex);
+ if( rc==0 ){
+ aHolder[iIdx] = pthread_self();
+ }
+ return rc;
+}
+
+/* Call our async_XX wrappers instead of selected pthread_XX functions */
+#define pthread_mutex_lock async_mutex_lock
+#define pthread_mutex_unlock async_mutex_unlock
+#define pthread_mutex_trylock async_mutex_trylock
+#define pthread_cond_wait async_cond_wait
+
+#endif /* !defined(NDEBUG) */
+
+/*
+** Add an entry to the end of the global write-op list. pWrite should point
+** to an AsyncWrite structure allocated using sqlite3_malloc(). The writer
+** thread will call sqlite3_free() to free the structure after the specified
+** operation has been completed.
+**
+** Once an AsyncWrite structure has been added to the list, it becomes the
+** property of the writer thread and must not be read or modified by the
+** caller.
+*/
+static void addAsyncWrite(AsyncWrite *pWrite){
+ /* We must hold the queue mutex in order to modify the queue pointers */
+ pthread_mutex_lock(&async.queueMutex);
+
+ /* Add the record to the end of the write-op queue */
+ assert( !pWrite->pNext );
+ if( async.pQueueLast ){
+ assert( async.pQueueFirst );
+ async.pQueueLast->pNext = pWrite;
+ }else{
+ async.pQueueFirst = pWrite;
+ }
+ async.pQueueLast = pWrite;
+ ASYNC_TRACE(("PUSH %p (%s %s %d)\n", pWrite, azOpcodeName[pWrite->op],
+ pWrite->pFileData ? pWrite->pFileData->zName : "-", pWrite->iOffset));
+
+ if( pWrite->op==ASYNC_CLOSE ){
+ async.nFile--;
+ }
+
+ /* Drop the queue mutex */
+ pthread_mutex_unlock(&async.queueMutex);
+
+ /* The writer thread might have been idle because there was nothing
+ ** on the write-op queue for it to do. So wake it up. */
+ pthread_cond_signal(&async.queueSignal);
+}
+
+/*
+** Increment async.nFile in a thread-safe manner.
+*/
+static void incrOpenFileCount(){
+ /* We must hold the queue mutex in order to modify async.nFile */
+ pthread_mutex_lock(&async.queueMutex);
+ if( async.nFile==0 ){
+ async.ioError = SQLITE_OK;
+ }
+ async.nFile++;
+ pthread_mutex_unlock(&async.queueMutex);
+}
+
+/*
+** This is a utility function to allocate and populate a new AsyncWrite
+** structure and insert it (via addAsyncWrite() ) into the global list.
+*/
+static int addNewAsyncWrite(
+ AsyncFileData *pFileData,
+ int op,
+ i64 iOffset,
+ int nByte,
+ const char *zByte
+){
+ AsyncWrite *p;
+ if( op!=ASYNC_CLOSE && async.ioError ){
+ return async.ioError;
+ }
+ p = sqlite3_malloc(sizeof(AsyncWrite) + (zByte?nByte:0));
+ if( !p ){
+ /* The upper layer does not expect operations like OsWrite() to
+ ** return SQLITE_NOMEM. This is partly because under normal conditions
+ ** SQLite is required to do rollback without calling malloc(). So
+ ** if malloc() fails here, treat it as an I/O error. The above
+ ** layer knows how to handle that.
+ */
+ return SQLITE_IOERR;
+ }
+ p->op = op;
+ p->iOffset = iOffset;
+ p->nByte = nByte;
+ p->pFileData = pFileData;
+ p->pNext = 0;
+ if( zByte ){
+ p->zBuf = (char *)&p[1];
+ memcpy(p->zBuf, zByte, nByte);
+ }else{
+ p->zBuf = 0;
+ }
+ addAsyncWrite(p);
+ return SQLITE_OK;
+}
+
+/*
+** Close the file. This just adds an entry to the write-op list, the file is
+** not actually closed.
+*/
+static int asyncClose(sqlite3_file *pFile){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+
+ /* Unlock the file, if it is locked */
+ pthread_mutex_lock(&async.lockMutex);
+ p->lock.eLock = 0;
+ pthread_mutex_unlock(&async.lockMutex);
+
+ addAsyncWrite(&p->close);
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of sqlite3OsWrite() for asynchronous files. Instead of
+** writing to the underlying file, this function adds an entry to the end of
+** the global AsyncWrite list. Either SQLITE_OK or SQLITE_NOMEM may be
+** returned.
+*/
+static int asyncWrite(sqlite3_file *pFile, const void *pBuf, int amt, i64 iOff){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ return addNewAsyncWrite(p, ASYNC_WRITE, iOff, amt, pBuf);
+}
+
+/*
+** Read data from the file. First we read from the filesystem, then adjust
+** the contents of the buffer based on ASYNC_WRITE operations in the
+** write-op queue.
+**
+** This method holds the mutex from start to finish.
+*/
+static int asyncRead(sqlite3_file *pFile, void *zOut, int iAmt, i64 iOffset){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ int rc = SQLITE_OK;
+ i64 filesize;
+ int nRead;
+ sqlite3_file *pBase = p->pBaseRead;
+
+ /* Grab the write queue mutex for the duration of the call */
+ pthread_mutex_lock(&async.queueMutex);
+
+ /* If an I/O error has previously occurred in this virtual file
+ ** system, then all subsequent operations fail.
+ */
+ if( async.ioError!=SQLITE_OK ){
+ rc = async.ioError;
+ goto asyncread_out;
+ }
+
+ if( pBase->pMethods ){
+ rc = sqlite3OsFileSize(pBase, &filesize);
+ if( rc!=SQLITE_OK ){
+ goto asyncread_out;
+ }
+ nRead = MIN(filesize - iOffset, iAmt);
+ if( nRead>0 ){
+ rc = sqlite3OsRead(pBase, zOut, nRead, iOffset);
+ ASYNC_TRACE(("READ %s %d bytes at %d\n", p->zName, nRead, iOffset));
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ AsyncWrite *pWrite;
+ char *zName = p->zName;
+
+ for(pWrite=async.pQueueFirst; pWrite; pWrite = pWrite->pNext){
+ if( pWrite->op==ASYNC_WRITE && pWrite->pFileData->zName==zName ){
+ int iBeginOut = (pWrite->iOffset-iOffset);
+ int iBeginIn = -iBeginOut;
+ int nCopy;
+
+ if( iBeginIn<0 ) iBeginIn = 0;
+ if( iBeginOut<0 ) iBeginOut = 0;
+ nCopy = MIN(pWrite->nByte-iBeginIn, iAmt-iBeginOut);
+
+ if( nCopy>0 ){
+ memcpy(&((char *)zOut)[iBeginOut], &pWrite->zBuf[iBeginIn], nCopy);
+ ASYNC_TRACE(("OVERREAD %d bytes at %d\n", nCopy, iBeginOut+iOffset));
+ }
+ }
+ }
+ }
+
+asyncread_out:
+ pthread_mutex_unlock(&async.queueMutex);
+ return rc;
+}
+
+/*
+** Truncate the file to nByte bytes in length. This just adds an entry to
+** the write-op list, no IO actually takes place.
+*/
+static int asyncTruncate(sqlite3_file *pFile, i64 nByte){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ return addNewAsyncWrite(p, ASYNC_TRUNCATE, nByte, 0, 0);
+}
+
+/*
+** Sync the file. This just adds an entry to the write-op list, the
+** sync() is done later by sqlite3_async_flush().
+*/
+static int asyncSync(sqlite3_file *pFile, int flags){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ return addNewAsyncWrite(p, ASYNC_SYNC, 0, flags, 0);
+}
+
+/*
+** Read the size of the file. First we read the size of the file system
+** entry, then adjust for any ASYNC_WRITE or ASYNC_TRUNCATE operations
+** currently in the write-op list.
+**
+** This method holds the mutex from start to finish.
+*/
+int asyncFileSize(sqlite3_file *pFile, i64 *piSize){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ int rc = SQLITE_OK;
+ i64 s = 0;
+ sqlite3_file *pBase;
+
+ pthread_mutex_lock(&async.queueMutex);
+
+ /* Read the filesystem size from the base file. If pBaseRead is NULL, this
+ ** means the file hasn't been opened yet. In this case all relevant data
+ ** must be in the write-op queue anyway, so we can omit reading from the
+ ** file-system.
+ */
+ pBase = p->pBaseRead;
+ if( pBase->pMethods ){
+ rc = sqlite3OsFileSize(pBase, &s);
+ }
+
+ if( rc==SQLITE_OK ){
+ AsyncWrite *pWrite;
+ for(pWrite=async.pQueueFirst; pWrite; pWrite = pWrite->pNext){
+ if( pWrite->op==ASYNC_DELETE && strcmp(p->zName, pWrite->zBuf)==0 ){
+ s = 0;
+ }else if( pWrite->pFileData && pWrite->pFileData->zName==p->zName){
+ switch( pWrite->op ){
+ case ASYNC_WRITE:
+ s = MAX(pWrite->iOffset + (i64)(pWrite->nByte), s);
+ break;
+ case ASYNC_TRUNCATE:
+ s = MIN(s, pWrite->iOffset);
+ break;
+ }
+ }
+ }
+ *piSize = s;
+ }
+ pthread_mutex_unlock(&async.queueMutex);
+ return rc;
+}
+
+/*
+** Lock or unlock the actual file-system entry.
+*/
+static int getFileLock(AsyncLock *pLock){
+ int rc = SQLITE_OK;
+ AsyncFileLock *pIter;
+ int eRequired = 0;
+
+ if( pLock->pFile ){
+ for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
+ assert(pIter->eAsyncLock>=pIter->eLock);
+ if( pIter->eAsyncLock>eRequired ){
+ eRequired = pIter->eAsyncLock;
+ assert(eRequired>=0 && eRequired<=SQLITE_LOCK_EXCLUSIVE);
+ }
+ }
+
+ if( eRequired>pLock->eLock ){
+ rc = sqlite3OsLock(pLock->pFile, eRequired);
+ if( rc==SQLITE_OK ){
+ pLock->eLock = eRequired;
+ }
+ }
+ else if( eRequired<pLock->eLock && eRequired<=SQLITE_LOCK_SHARED ){
+ rc = sqlite3OsUnlock(pLock->pFile, eRequired);
+ if( rc==SQLITE_OK ){
+ pLock->eLock = eRequired;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** The following two methods - asyncLock() and asyncUnlock() - are used
+** to obtain and release locks on database files opened with the
+** asynchronous backend.
+*/
+static int asyncLock(sqlite3_file *pFile, int eLock){
+ int rc = SQLITE_OK;
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+
+ pthread_mutex_lock(&async.lockMutex);
+ if( p->lock.eLock<eLock ){
+ AsyncLock *pLock;
+ AsyncFileLock *pIter;
+ pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
+ assert(pLock && pLock->pList);
+ for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
+ if( pIter!=&p->lock && (
+ (eLock==SQLITE_LOCK_EXCLUSIVE && pIter->eLock>=SQLITE_LOCK_SHARED) ||
+ (eLock==SQLITE_LOCK_PENDING && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
+ (eLock==SQLITE_LOCK_RESERVED && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
+ (eLock==SQLITE_LOCK_SHARED && pIter->eLock>=SQLITE_LOCK_PENDING)
+ )){
+ rc = SQLITE_BUSY;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ p->lock.eLock = eLock;
+ p->lock.eAsyncLock = MAX(p->lock.eAsyncLock, eLock);
+ }
+ assert(p->lock.eAsyncLock>=p->lock.eLock);
+ if( rc==SQLITE_OK ){
+ rc = getFileLock(pLock);
+ }
+ }
+ pthread_mutex_unlock(&async.lockMutex);
+
+ ASYNC_TRACE(("LOCK %d (%s) rc=%d\n", eLock, p->zName, rc));
+ return rc;
+}
+static int asyncUnlock(sqlite3_file *pFile, int eLock){
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+ AsyncFileLock *pLock = &p->lock;
+ pthread_mutex_lock(&async.lockMutex);
+ pLock->eLock = MIN(pLock->eLock, eLock);
+ pthread_mutex_unlock(&async.lockMutex);
+ return addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
+}
+
+/*
+** This function is called when the pager layer first opens a database file
+** and is checking for a hot-journal.
+*/
+static int asyncCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ int ret = 0;
+ AsyncFileLock *pIter;
+ AsyncLock *pLock;
+ AsyncFileData *p = ((AsyncFile *)pFile)->pData;
+
+ pthread_mutex_lock(&async.lockMutex);
+ pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
+ for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
+ if( pIter->eLock>=SQLITE_LOCK_RESERVED ){
+ ret = 1;
+ }
+ }
+ pthread_mutex_unlock(&async.lockMutex);
+
+ ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", ret, p->zName));
+ *pResOut = ret;
+ return SQLITE_OK;
+}
+
+/*
+** This is a no-op, as the asynchronous backend does not support locking.
+*/
+static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
+ switch( op ){
+ case SQLITE_FCNTL_LOCKSTATE: {
+ pthread_mutex_lock(&async.lockMutex);
+ *(int*)pArg = ((AsyncFile*)id)->pData->lock.eLock;
+ pthread_mutex_unlock(&async.lockMutex);
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_ERROR;
+}
+
+/*
+** Return the device characteristics and sector-size of the device. It
+** is not tricky to implement these correctly, as this backend might
+** not have an open file handle at this point.
+*/
+static int asyncSectorSize(sqlite3_file *pFile){
+ return 512;
+}
+static int asyncDeviceCharacteristics(sqlite3_file *pFile){
+ return 0;
+}
+
+static int unlinkAsyncFile(AsyncFileData *pData){
+ AsyncLock *pLock;
+ AsyncFileLock **ppIter;
+ int rc = SQLITE_OK;
+
+ pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
+ for(ppIter=&pLock->pList; *ppIter; ppIter=&((*ppIter)->pNext)){
+ if( (*ppIter)==&pData->lock ){
+ *ppIter = pData->lock.pNext;
+ break;
+ }
+ }
+ if( !pLock->pList ){
+ if( pLock->pFile ){
+ sqlite3OsClose(pLock->pFile);
+ }
+ sqlite3_free(pLock);
+ sqlite3HashInsert(&async.aLock, pData->zName, pData->nName, 0);
+ if( !sqliteHashFirst(&async.aLock) ){
+ sqlite3HashClear(&async.aLock);
+ }
+ }else{
+ rc = getFileLock(pLock);
+ }
+
+ return rc;
+}
+
+/*
+** Open a file.
+*/
+static int asyncOpen(
+ sqlite3_vfs *pAsyncVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ static sqlite3_io_methods async_methods = {
+ 1, /* iVersion */
+ asyncClose, /* xClose */
+ asyncRead, /* xRead */
+ asyncWrite, /* xWrite */
+ asyncTruncate, /* xTruncate */
+ asyncSync, /* xSync */
+ asyncFileSize, /* xFileSize */
+ asyncLock, /* xLock */
+ asyncUnlock, /* xUnlock */
+ asyncCheckReservedLock, /* xCheckReservedLock */
+ asyncFileControl, /* xFileControl */
+ asyncSectorSize, /* xSectorSize */
+ asyncDeviceCharacteristics /* xDeviceCharacteristics */
+ };
+
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ AsyncFile *p = (AsyncFile *)pFile;
+ int nName = strlen(zName)+1;
+ int rc = SQLITE_OK;
+ int nByte;
+ AsyncFileData *pData;
+ AsyncLock *pLock = 0;
+ char *z;
+ int isExclusive = (flags&SQLITE_OPEN_EXCLUSIVE);
+
+ nByte = (
+ sizeof(AsyncFileData) + /* AsyncFileData structure */
+ 2 * pVfs->szOsFile + /* AsyncFileData.pBaseRead and pBaseWrite */
+ nName /* AsyncFileData.zName */
+ );
+ z = sqlite3_malloc(nByte);
+ if( !z ){
+ return SQLITE_NOMEM;
+ }
+ memset(z, 0, nByte);
+ pData = (AsyncFileData*)z;
+ z += sizeof(pData[0]);
+ pData->pBaseRead = (sqlite3_file*)z;
+ z += pVfs->szOsFile;
+ pData->pBaseWrite = (sqlite3_file*)z;
+ z += pVfs->szOsFile;
+ pData->zName = z;
+ pData->nName = nName;
+ pData->close.pFileData = pData;
+ pData->close.op = ASYNC_CLOSE;
+ memcpy(pData->zName, zName, nName);
+
+ if( !isExclusive ){
+ rc = sqlite3OsOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
+ if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
+ rc = sqlite3OsOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
+ }
+ }
+
+ pthread_mutex_lock(&async.lockMutex);
+
+ if( rc==SQLITE_OK ){
+ pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
+ if( !pLock ){
+ pLock = sqlite3MallocZero(pVfs->szOsFile + sizeof(AsyncLock));
+ if( pLock ){
+ AsyncLock *pDelete;
+#ifdef ENABLE_FILE_LOCKING
+ if( flags&SQLITE_OPEN_MAIN_DB ){
+ pLock->pFile = (sqlite3_file *)&pLock[1];
+ rc = sqlite3OsOpen(pVfs, zName, pLock->pFile, flags, 0);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pLock);
+ pLock = 0;
+ }
+ }
+#endif
+ pDelete = sqlite3HashInsert(
+ &async.aLock, pData->zName, pData->nName, (void *)pLock
+ );
+ if( pDelete ){
+ rc = SQLITE_NOMEM;
+ sqlite3_free(pLock);
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ HashElem *pElem;
+ p->pMethod = &async_methods;
+ p->pData = pData;
+
+ /* Link AsyncFileData.lock into the linked list of
+ ** AsyncFileLock structures for this file.
+ */
+ pData->lock.pNext = pLock->pList;
+ pLock->pList = &pData->lock;
+
+ pElem = sqlite3HashFindElem(&async.aLock, pData->zName, pData->nName);
+ pData->zName = (char *)sqliteHashKey(pElem);
+ }else{
+ sqlite3OsClose(pData->pBaseRead);
+ sqlite3OsClose(pData->pBaseWrite);
+ sqlite3_free(pData);
+ }
+
+ pthread_mutex_unlock(&async.lockMutex);
+
+ if( rc==SQLITE_OK ){
+ incrOpenFileCount();
+ }
+
+ if( rc==SQLITE_OK && isExclusive ){
+ rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (i64)flags, 0, 0);
+ if( rc==SQLITE_OK ){
+ if( pOutFlags ) *pOutFlags = flags;
+ }else{
+ pthread_mutex_lock(&async.lockMutex);
+ unlinkAsyncFile(pData);
+ pthread_mutex_unlock(&async.lockMutex);
+ sqlite3_free(pData);
+ }
+ }
+ return rc;
+}
+
+/*
+** Implementation of sqlite3OsDelete. Add an entry to the end of the
+** write-op queue to perform the delete.
+*/
+static int asyncDelete(sqlite3_vfs *pAsyncVfs, const char *z, int syncDir){
+ return addNewAsyncWrite(0, ASYNC_DELETE, syncDir, strlen(z)+1, z);
+}
+
+/*
+** Implementation of sqlite3OsAccess. This method holds the mutex from
+** start to finish.
+*/
+static int asyncAccess(
+ sqlite3_vfs *pAsyncVfs,
+ const char *zName,
+ int flags,
+ int *pResOut
+){
+ int rc;
+ int ret;
+ AsyncWrite *p;
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+
+ assert(flags==SQLITE_ACCESS_READWRITE
+ || flags==SQLITE_ACCESS_READ
+ || flags==SQLITE_ACCESS_EXISTS
+ );
+
+ pthread_mutex_lock(&async.queueMutex);
+ rc = sqlite3OsAccess(pVfs, zName, flags, &ret);
+ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
+ for(p=async.pQueueFirst; p; p = p->pNext){
+ if( p->op==ASYNC_DELETE && 0==strcmp(p->zBuf, zName) ){
+ ret = 0;
+ }else if( p->op==ASYNC_OPENEXCLUSIVE
+ && 0==strcmp(p->pFileData->zName, zName)
+ ){
+ ret = 1;
+ }
+ }
+ }
+ ASYNC_TRACE(("ACCESS(%s): %s = %d\n",
+ flags==SQLITE_ACCESS_READWRITE?"read-write":
+ flags==SQLITE_ACCESS_READ?"read":"exists"
+ , zName, ret)
+ );
+ pthread_mutex_unlock(&async.queueMutex);
+ *pResOut = ret;
+ return rc;
+}
+
+/*
+** Fill in zPathOut with the full path to the file identified by zPath.
+*/
+static int asyncFullPathname(
+ sqlite3_vfs *pAsyncVfs,
+ const char *zPath,
+ int nPathOut,
+ char *zPathOut
+){
+ int rc;
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ rc = sqlite3OsFullPathname(pVfs, zPath, nPathOut, zPathOut);
+
+ /* Because of the way intra-process file locking works, this backend
+ ** needs to return a canonical path. The following block assumes the
+ ** file-system uses unix style paths.
+ */
+ if( rc==SQLITE_OK ){
+ int iIn;
+ int iOut = 0;
+ int nPathOut = strlen(zPathOut);
+
+ for(iIn=0; iIn<nPathOut; iIn++){
+
+ /* Replace any occurences of "//" with "/" */
+ if( iIn<=(nPathOut-2) && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='/'
+ ){
+ continue;
+ }
+
+ /* Replace any occurences of "/./" with "/" */
+ if( iIn<=(nPathOut-3)
+ && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' && zPathOut[iIn+2]=='/'
+ ){
+ iIn++;
+ continue;
+ }
+
+ /* Replace any occurences of "<path-component>/../" with "" */
+ if( iOut>0 && iIn<=(nPathOut-4)
+ && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.'
+ && zPathOut[iIn+2]=='.' && zPathOut[iIn+3]=='/'
+ ){
+ iIn += 3;
+ iOut--;
+ for( ; iOut>0 && zPathOut[iOut-1]!='/'; iOut--);
+ continue;
+ }
+
+ zPathOut[iOut++] = zPathOut[iIn];
+ }
+ zPathOut[iOut] = '\0';
+ }
+
+ return rc;
+}
+static void *asyncDlOpen(sqlite3_vfs *pAsyncVfs, const char *zPath){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ return pVfs->xDlOpen(pVfs, zPath);
+}
+static void asyncDlError(sqlite3_vfs *pAsyncVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ pVfs->xDlError(pVfs, nByte, zErrMsg);
+}
+static void *asyncDlSym(
+ sqlite3_vfs *pAsyncVfs,
+ void *pHandle,
+ const char *zSymbol
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ return pVfs->xDlSym(pVfs, pHandle, zSymbol);
+}
+static void asyncDlClose(sqlite3_vfs *pAsyncVfs, void *pHandle){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ pVfs->xDlClose(pVfs, pHandle);
+}
+static int asyncRandomness(sqlite3_vfs *pAsyncVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+}
+static int asyncSleep(sqlite3_vfs *pAsyncVfs, int nMicro){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ return pVfs->xSleep(pVfs, nMicro);
+}
+static int asyncCurrentTime(sqlite3_vfs *pAsyncVfs, double *pTimeOut){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
+ return pVfs->xCurrentTime(pVfs, pTimeOut);
+}
+
+static sqlite3_vfs async_vfs = {
+ 1, /* iVersion */
+ sizeof(AsyncFile), /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ "async", /* zName */
+ 0, /* pAppData */
+ asyncOpen, /* xOpen */
+ asyncDelete, /* xDelete */
+ asyncAccess, /* xAccess */
+ asyncFullPathname, /* xFullPathname */
+ asyncDlOpen, /* xDlOpen */
+ asyncDlError, /* xDlError */
+ asyncDlSym, /* xDlSym */
+ asyncDlClose, /* xDlClose */
+ asyncRandomness, /* xDlError */
+ asyncSleep, /* xDlSym */
+ asyncCurrentTime /* xDlClose */
+};
+
+/*
+** Call this routine to enable or disable the
+** asynchronous IO features implemented in this file.
+**
+** This routine is not even remotely threadsafe. Do not call
+** this routine while any SQLite database connections are open.
+*/
+static void asyncEnable(int enable){
+ if( enable ){
+ if( !async_vfs.pAppData ){
+ static int hashTableInit = 0;
+ async_vfs.pAppData = (void *)sqlite3_vfs_find(0);
+ async_vfs.mxPathname = ((sqlite3_vfs *)async_vfs.pAppData)->mxPathname;
+ sqlite3_vfs_register(&async_vfs, 1);
+ if( !hashTableInit ){
+ sqlite3HashInit(&async.aLock, SQLITE_HASH_BINARY, 1);
+ hashTableInit = 1;
+ }
+ }
+ }else{
+ if( async_vfs.pAppData ){
+ sqlite3_vfs_unregister(&async_vfs);
+ async_vfs.pAppData = 0;
+ }
+ }
+}
+
+/*
+** This procedure runs in a separate thread, reading messages off of the
+** write queue and processing them one by one.
+**
+** If async.writerHaltNow is true, then this procedure exits
+** after processing a single message.
+**
+** If async.writerHaltWhenIdle is true, then this procedure exits when
+** the write queue is empty.
+**
+** If both of the above variables are false, this procedure runs
+** indefinately, waiting for operations to be added to the write queue
+** and processing them in the order in which they arrive.
+**
+** An artifical delay of async.ioDelay milliseconds is inserted before
+** each write operation in order to simulate the effect of a slow disk.
+**
+** Only one instance of this procedure may be running at a time.
+*/
+static void *asyncWriterThread(void *pIsStarted){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)(async_vfs.pAppData);
+ AsyncWrite *p = 0;
+ int rc = SQLITE_OK;
+ int holdingMutex = 0;
+
+ if( pthread_mutex_trylock(&async.writerMutex) ){
+ return 0;
+ }
+ (*(int *)pIsStarted) = 1;
+ while( async.writerHaltNow==0 ){
+ int doNotFree = 0;
+ sqlite3_file *pBase = 0;
+
+ if( !holdingMutex ){
+ pthread_mutex_lock(&async.queueMutex);
+ }
+ while( (p = async.pQueueFirst)==0 ){
+ pthread_cond_broadcast(&async.emptySignal);
+ if( async.writerHaltWhenIdle ){
+ pthread_mutex_unlock(&async.queueMutex);
+ break;
+ }else{
+ ASYNC_TRACE(("IDLE\n"));
+ pthread_cond_wait(&async.queueSignal, &async.queueMutex);
+ ASYNC_TRACE(("WAKEUP\n"));
+ }
+ }
+ if( p==0 ) break;
+ holdingMutex = 1;
+
+ /* Right now this thread is holding the mutex on the write-op queue.
+ ** Variable 'p' points to the first entry in the write-op queue. In
+ ** the general case, we hold on to the mutex for the entire body of
+ ** the loop.
+ **
+ ** However in the cases enumerated below, we relinquish the mutex,
+ ** perform the IO, and then re-request the mutex before removing 'p' from
+ ** the head of the write-op queue. The idea is to increase concurrency with
+ ** sqlite threads.
+ **
+ ** * An ASYNC_CLOSE operation.
+ ** * An ASYNC_OPENEXCLUSIVE operation. For this one, we relinquish
+ ** the mutex, call the underlying xOpenExclusive() function, then
+ ** re-aquire the mutex before seting the AsyncFile.pBaseRead
+ ** variable.
+ ** * ASYNC_SYNC and ASYNC_WRITE operations, if
+ ** SQLITE_ASYNC_TWO_FILEHANDLES was set at compile time and two
+ ** file-handles are open for the particular file being "synced".
+ */
+ if( async.ioError!=SQLITE_OK && p->op!=ASYNC_CLOSE ){
+ p->op = ASYNC_NOOP;
+ }
+ if( p->pFileData ){
+ pBase = p->pFileData->pBaseWrite;
+ if(
+ p->op==ASYNC_CLOSE ||
+ p->op==ASYNC_OPENEXCLUSIVE ||
+ (pBase->pMethods && (p->op==ASYNC_SYNC || p->op==ASYNC_WRITE) )
+ ){
+ pthread_mutex_unlock(&async.queueMutex);
+ holdingMutex = 0;
+ }
+ if( !pBase->pMethods ){
+ pBase = p->pFileData->pBaseRead;
+ }
+ }
+
+ switch( p->op ){
+ case ASYNC_NOOP:
+ break;
+
+ case ASYNC_WRITE:
+ assert( pBase );
+ ASYNC_TRACE(("WRITE %s %d bytes at %d\n",
+ p->pFileData->zName, p->nByte, p->iOffset));
+ rc = sqlite3OsWrite(pBase, (void *)(p->zBuf), p->nByte, p->iOffset);
+ break;
+
+ case ASYNC_SYNC:
+ assert( pBase );
+ ASYNC_TRACE(("SYNC %s\n", p->pFileData->zName));
+ rc = sqlite3OsSync(pBase, p->nByte);
+ break;
+
+ case ASYNC_TRUNCATE:
+ assert( pBase );
+ ASYNC_TRACE(("TRUNCATE %s to %d bytes\n",
+ p->pFileData->zName, p->iOffset));
+ rc = sqlite3OsTruncate(pBase, p->iOffset);
+ break;
+
+ case ASYNC_CLOSE: {
+ AsyncFileData *pData = p->pFileData;
+ ASYNC_TRACE(("CLOSE %s\n", p->pFileData->zName));
+ sqlite3OsClose(pData->pBaseWrite);
+ sqlite3OsClose(pData->pBaseRead);
+
+ /* Unlink AsyncFileData.lock from the linked list of AsyncFileLock
+ ** structures for this file. Obtain the async.lockMutex mutex
+ ** before doing so.
+ */
+ pthread_mutex_lock(&async.lockMutex);
+ rc = unlinkAsyncFile(pData);
+ pthread_mutex_unlock(&async.lockMutex);
+
+ async.pQueueFirst = p->pNext;
+ sqlite3_free(pData);
+ doNotFree = 1;
+ break;
+ }
+
+ case ASYNC_UNLOCK: {
+ AsyncLock *pLock;
+ AsyncFileData *pData = p->pFileData;
+ int eLock = p->nByte;
+ pthread_mutex_lock(&async.lockMutex);
+ pData->lock.eAsyncLock = MIN(
+ pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
+ );
+ assert(pData->lock.eAsyncLock>=pData->lock.eLock);
+ pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
+ rc = getFileLock(pLock);
+ pthread_mutex_unlock(&async.lockMutex);
+ break;
+ }
+
+ case ASYNC_DELETE:
+ ASYNC_TRACE(("DELETE %s\n", p->zBuf));
+ rc = sqlite3OsDelete(pVfs, p->zBuf, (int)p->iOffset);
+ break;
+
+ case ASYNC_OPENEXCLUSIVE: {
+ int flags = (int)p->iOffset;
+ AsyncFileData *pData = p->pFileData;
+ ASYNC_TRACE(("OPEN %s flags=%d\n", p->zBuf, (int)p->iOffset));
+ assert(pData->pBaseRead->pMethods==0 && pData->pBaseWrite->pMethods==0);
+ rc = sqlite3OsOpen(pVfs, pData->zName, pData->pBaseRead, flags, 0);
+ assert( holdingMutex==0 );
+ pthread_mutex_lock(&async.queueMutex);
+ holdingMutex = 1;
+ break;
+ }
+
+ default: assert(!"Illegal value for AsyncWrite.op");
+ }
+
+ /* If we didn't hang on to the mutex during the IO op, obtain it now
+ ** so that the AsyncWrite structure can be safely removed from the
+ ** global write-op queue.
+ */
+ if( !holdingMutex ){
+ pthread_mutex_lock(&async.queueMutex);
+ holdingMutex = 1;
+ }
+ /* ASYNC_TRACE(("UNLINK %p\n", p)); */
+ if( p==async.pQueueLast ){
+ async.pQueueLast = 0;
+ }
+ if( !doNotFree ){
+ async.pQueueFirst = p->pNext;
+ sqlite3_free(p);
+ }
+ assert( holdingMutex );
+
+ /* An IO error has occured. We cannot report the error back to the
+ ** connection that requested the I/O since the error happened
+ ** asynchronously. The connection has already moved on. There
+ ** really is nobody to report the error to.
+ **
+ ** The file for which the error occured may have been a database or
+ ** journal file. Regardless, none of the currently queued operations
+ ** associated with the same database should now be performed. Nor should
+ ** any subsequently requested IO on either a database or journal file
+ ** handle for the same database be accepted until the main database
+ ** file handle has been closed and reopened.
+ **
+ ** Furthermore, no further IO should be queued or performed on any file
+ ** handle associated with a database that may have been part of a
+ ** multi-file transaction that included the database associated with
+ ** the IO error (i.e. a database ATTACHed to the same handle at some
+ ** point in time).
+ */
+ if( rc!=SQLITE_OK ){
+ async.ioError = rc;
+ }
+
+ if( async.ioError && !async.pQueueFirst ){
+ pthread_mutex_lock(&async.lockMutex);
+ if( 0==sqliteHashFirst(&async.aLock) ){
+ async.ioError = SQLITE_OK;
+ }
+ pthread_mutex_unlock(&async.lockMutex);
+ }
+
+ /* Drop the queue mutex before continuing to the next write operation
+ ** in order to give other threads a chance to work with the write queue.
+ */
+ if( !async.pQueueFirst || !async.ioError ){
+ pthread_mutex_unlock(&async.queueMutex);
+ holdingMutex = 0;
+ if( async.ioDelay>0 ){
+ sqlite3OsSleep(pVfs, async.ioDelay);
+ }else{
+ sched_yield();
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&async.writerMutex);
+ return 0;
+}
+
+/**************************************************************************
+** The remaining code defines a Tcl interface for testing the asynchronous
+** IO implementation in this file.
+**
+** To adapt the code to a non-TCL environment, delete or comment out
+** the code that follows.
+*/
+
+/*
+** sqlite3async_enable ?YES/NO?
+**
+** Enable or disable the asynchronous I/O backend. This command is
+** not thread-safe. Do not call it while any database connections
+** are open.
+*/
+static int testAsyncEnable(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=1 && objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?YES/NO?");
+ return TCL_ERROR;
+ }
+ if( objc==1 ){
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(async_vfs.pAppData!=0));
+ }else{
+ int en;
+ if( Tcl_GetBooleanFromObj(interp, objv[1], &en) ) return TCL_ERROR;
+ asyncEnable(en);
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite3async_halt "now"|"idle"|"never"
+**
+** Set the conditions at which the writer thread will halt.
+*/
+static int testAsyncHalt(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zCond;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "\"now\"|\"idle\"|\"never\"");
+ return TCL_ERROR;
+ }
+ zCond = Tcl_GetString(objv[1]);
+ if( strcmp(zCond, "now")==0 ){
+ async.writerHaltNow = 1;
+ pthread_cond_broadcast(&async.queueSignal);
+ }else if( strcmp(zCond, "idle")==0 ){
+ async.writerHaltWhenIdle = 1;
+ async.writerHaltNow = 0;
+ pthread_cond_broadcast(&async.queueSignal);
+ }else if( strcmp(zCond, "never")==0 ){
+ async.writerHaltWhenIdle = 0;
+ async.writerHaltNow = 0;
+ }else{
+ Tcl_AppendResult(interp,
+ "should be one of: \"now\", \"idle\", or \"never\"", (char*)0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite3async_delay ?MS?
+**
+** Query or set the number of milliseconds of delay in the writer
+** thread after each write operation. The default is 0. By increasing
+** the memory delay we can simulate the effect of slow disk I/O.
+*/
+static int testAsyncDelay(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=1 && objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?MS?");
+ return TCL_ERROR;
+ }
+ if( objc==1 ){
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(async.ioDelay));
+ }else{
+ int ioDelay;
+ if( Tcl_GetIntFromObj(interp, objv[1], &ioDelay) ) return TCL_ERROR;
+ async.ioDelay = ioDelay;
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite3async_start
+**
+** Start a new writer thread.
+*/
+static int testAsyncStart(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ pthread_t x;
+ int rc;
+ volatile int isStarted = 0;
+ rc = pthread_create(&x, 0, asyncWriterThread, (void *)&isStarted);
+ if( rc ){
+ Tcl_AppendResult(interp, "failed to create the thread", 0);
+ return TCL_ERROR;
+ }
+ pthread_detach(x);
+ while( isStarted==0 ){
+ sched_yield();
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite3async_wait
+**
+** Wait for the current writer thread to terminate.
+**
+** If the current writer thread is set to run forever then this
+** command would block forever. To prevent that, an error is returned.
+*/
+static int testAsyncWait(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int cnt = 10;
+ if( async.writerHaltNow==0 && async.writerHaltWhenIdle==0 ){
+ Tcl_AppendResult(interp, "would block forever", (char*)0);
+ return TCL_ERROR;
+ }
+
+ while( cnt-- && !pthread_mutex_trylock(&async.writerMutex) ){
+ pthread_mutex_unlock(&async.writerMutex);
+ sched_yield();
+ }
+ if( cnt>=0 ){
+ ASYNC_TRACE(("WAIT\n"));
+ pthread_mutex_lock(&async.queueMutex);
+ pthread_cond_broadcast(&async.queueSignal);
+ pthread_mutex_unlock(&async.queueMutex);
+ pthread_mutex_lock(&async.writerMutex);
+ pthread_mutex_unlock(&async.writerMutex);
+ }else{
+ ASYNC_TRACE(("NO-WAIT\n"));
+ }
+ return TCL_OK;
+}
+
+
+#endif /* SQLITE_OS_UNIX and SQLITE_THREADSAFE */
+
+/*
+** This routine registers the custom TCL commands defined in this
+** module. This should be the only procedure visible from outside
+** of this module.
+*/
+int Sqlitetestasync_Init(Tcl_Interp *interp){
+#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
+ Tcl_CreateObjCommand(interp,"sqlite3async_enable",testAsyncEnable,0,0);
+ Tcl_CreateObjCommand(interp,"sqlite3async_halt",testAsyncHalt,0,0);
+ Tcl_CreateObjCommand(interp,"sqlite3async_delay",testAsyncDelay,0,0);
+ Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0);
+ Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
+ Tcl_LinkVar(interp, "sqlite3async_trace",
+ (char*)&sqlite3async_trace, TCL_LINK_INT);
+#endif /* SQLITE_OS_UNIX and SQLITE_THREADSAFE */
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_autoext.c b/third_party/sqlite/src/test_autoext.c
new file mode 100755
index 0000000..11f5413
--- /dev/null
+++ b/third_party/sqlite/src/test_autoext.c
@@ -0,0 +1,169 @@
+/*
+** 2006 August 23
+**
+** 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.
+**
+*************************************************************************
+** Test extension for testing the sqlite3_auto_extension() function.
+**
+** $Id: test_autoext.c,v 1.5 2008/07/08 02:12:37 drh Exp $
+*/
+#include "tcl.h"
+#include "sqlite3ext.h"
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+static SQLITE_EXTENSION_INIT1
+
+/*
+** The sqr() SQL function returns the square of its input value.
+*/
+static void sqrFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ double r = sqlite3_value_double(argv[0]);
+ sqlite3_result_double(context, r*r);
+}
+
+/*
+** This is the entry point to register the extension for the sqr() function.
+*/
+static int sqr_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ sqlite3_create_function(db, "sqr", 1, SQLITE_ANY, 0, sqrFunc, 0, 0);
+ return 0;
+}
+
+/*
+** The cube() SQL function returns the cube of its input value.
+*/
+static void cubeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ double r = sqlite3_value_double(argv[0]);
+ sqlite3_result_double(context, r*r*r);
+}
+
+/*
+** This is the entry point to register the extension for the cube() function.
+*/
+static int cube_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ sqlite3_create_function(db, "cube", 1, SQLITE_ANY, 0, cubeFunc, 0, 0);
+ return 0;
+}
+
+/*
+** This is a broken extension entry point
+*/
+static int broken_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ char *zErr;
+ SQLITE_EXTENSION_INIT2(pApi);
+ zErr = sqlite3_mprintf("broken autoext!");
+ *pzErrMsg = zErr;
+ return 1;
+}
+
+/*
+** tclcmd: sqlite3_auto_extension_sqr
+**
+** Register the "sqr" extension to be loaded automatically.
+*/
+static int autoExtSqrObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_auto_extension((void*)sqr_init);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
+
+/*
+** tclcmd: sqlite3_auto_extension_cube
+**
+** Register the "cube" extension to be loaded automatically.
+*/
+static int autoExtCubeObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_auto_extension((void*)cube_init);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
+
+/*
+** tclcmd: sqlite3_auto_extension_broken
+**
+** Register the broken extension to be loaded automatically.
+*/
+static int autoExtBrokenObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_auto_extension((void*)broken_init);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
+
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+
+/*
+** tclcmd: sqlite3_reset_auto_extension
+**
+** Reset all auto-extensions
+*/
+static int resetAutoExtObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_reset_auto_extension();
+ return SQLITE_OK;
+}
+
+
+/*
+** This procedure registers the TCL procs defined in this file.
+*/
+int Sqlitetest_autoext_Init(Tcl_Interp *interp){
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_sqr",
+ autoExtSqrObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_cube",
+ autoExtCubeObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_broken",
+ autoExtBrokenObjCmd, 0, 0);
+#endif
+ Tcl_CreateObjCommand(interp, "sqlite3_reset_auto_extension",
+ resetAutoExtObjCmd, 0, 0);
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_btree.c b/third_party/sqlite/src/test_btree.c
new file mode 100755
index 0000000..006df81
--- /dev/null
+++ b/third_party/sqlite/src/test_btree.c
@@ -0,0 +1,140 @@
+/*
+** 2007 May 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.
+**
+*************************************************************************
+** Code for testing the btree.c module in SQLite. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test_btree.c,v 1.6 2008/07/15 00:27:35 drh Exp $
+*/
+#include "btreeInt.h"
+#include <tcl.h>
+
+/*
+** Usage: sqlite3_shared_cache_report
+**
+** Return a list of file that are shared and the number of
+** references to each file.
+*/
+int sqlite3BtreeSharedCacheReport(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ extern BtShared *sqlite3SharedCacheList;
+ BtShared *pBt;
+ Tcl_Obj *pRet = Tcl_NewObj();
+ for(pBt=sqlite3SharedCacheList; pBt; pBt=pBt->pNext){
+ const char *zFile = sqlite3PagerFilename(pBt->pPager);
+ Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1));
+ Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef));
+ }
+ Tcl_SetObjResult(interp, pRet);
+#endif
+ return TCL_OK;
+}
+
+/*
+** Print debugging information about all cursors to standard output.
+*/
+void sqlite3BtreeCursorList(Btree *p){
+#ifdef SQLITE_DEBUG
+ BtCursor *pCur;
+ BtShared *pBt = p->pBt;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ MemPage *pPage = pCur->pPage;
+ char *zMode = pCur->wrFlag ? "rw" : "ro";
+ sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
+ pCur, pCur->pgnoRoot, zMode,
+ pPage ? pPage->pgno : 0, pCur->idx,
+ (pCur->eState==CURSOR_VALID) ? "" : " eof"
+ );
+ }
+#endif
+}
+
+
+/*
+** Fill aResult[] with information about the entry and page that the
+** cursor is pointing to.
+**
+** aResult[0] = The page number
+** aResult[1] = The entry number
+** aResult[2] = Total number of entries on this page
+** aResult[3] = Cell size (local payload + header)
+** aResult[4] = Number of free bytes on this page
+** aResult[5] = Number of free blocks on the page
+** aResult[6] = Total payload size (local + overflow)
+** aResult[7] = Header size in bytes
+** aResult[8] = Local payload size
+** aResult[9] = Parent page number
+** aResult[10]= Page number of the first overflow page
+**
+** This routine is used for testing and debugging only.
+*/
+int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
+ int cnt, idx;
+ MemPage *pPage = pCur->pPage;
+ BtCursor tmpCur;
+ int rc;
+
+ if( pCur->eState==CURSOR_REQUIRESEEK ){
+ rc = sqlite3BtreeRestoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+
+ assert( pPage->isInit );
+ sqlite3BtreeGetTempCursor(pCur, &tmpCur);
+ while( upCnt-- ){
+ sqlite3BtreeMoveToParent(&tmpCur);
+ }
+ pPage = tmpCur.pPage;
+ aResult[0] = sqlite3PagerPagenumber(pPage->pDbPage);
+ assert( aResult[0]==pPage->pgno );
+ aResult[1] = tmpCur.idx;
+ aResult[2] = pPage->nCell;
+ if( tmpCur.idx>=0 && tmpCur.idx<pPage->nCell ){
+ sqlite3BtreeParseCell(tmpCur.pPage, tmpCur.idx, &tmpCur.info);
+ aResult[3] = tmpCur.info.nSize;
+ aResult[6] = tmpCur.info.nData;
+ aResult[7] = tmpCur.info.nHeader;
+ aResult[8] = tmpCur.info.nLocal;
+ }else{
+ aResult[3] = 0;
+ aResult[6] = 0;
+ aResult[7] = 0;
+ aResult[8] = 0;
+ }
+ aResult[4] = pPage->nFree;
+ cnt = 0;
+ idx = get2byte(&pPage->aData[pPage->hdrOffset+1]);
+ while( idx>0 && idx<pPage->pBt->usableSize ){
+ cnt++;
+ idx = get2byte(&pPage->aData[idx]);
+ }
+ aResult[5] = cnt;
+ if( pPage->pParent==0 || sqlite3BtreeIsRootPage(pPage) ){
+ aResult[9] = 0;
+ }else{
+ aResult[9] = pPage->pParent->pgno;
+ }
+ if( tmpCur.info.iOverflow ){
+ aResult[10] = get4byte(&tmpCur.info.pCell[tmpCur.info.iOverflow]);
+ }else{
+ aResult[10] = 0;
+ }
+ sqlite3BtreeReleaseTempCursor(&tmpCur);
+ return SQLITE_OK;
+}
diff --git a/third_party/sqlite/src/test_config.c b/third_party/sqlite/src/test_config.c
new file mode 100755
index 0000000..fe96b29
--- /dev/null
+++ b/third_party/sqlite/src/test_config.c
@@ -0,0 +1,488 @@
+/*
+** 2007 May 7
+**
+** 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 contains code used for testing the SQLite system.
+** None of the code in this file goes into a deliverable build.
+**
+** The focus of this file is providing the TCL testing layer
+** access to compile-time constants.
+**
+** $Id: test_config.c,v 1.33 2008/07/31 02:05:05 shane Exp $
+*/
+
+#include "sqliteLimit.h"
+
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** This routine sets entries in the global ::sqlite_options() array variable
+** according to the compile-time configuration of the database. Test
+** procedures use this to determine when tests should be omitted.
+*/
+static void set_options(Tcl_Interp *interp){
+ int rc = 0;
+
+#ifdef SQLITE_32BIT_ROWID
+ Tcl_SetVar2(interp, "sqlite_options", "rowid32", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "rowid32", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_CASE_SENSITIVE_LIKE
+ Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","1",TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","0",TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_DEBUG
+ Tcl_SetVar2(interp, "sqlite_options", "debug", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "debug", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_DISABLE_DIRSYNC
+ Tcl_SetVar2(interp, "sqlite_options", "dirsync", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "dirsync", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_DISABLE_LFS
+ Tcl_SetVar2(interp, "sqlite_options", "lfs", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "lfs", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#if 1 /* def SQLITE_MEMDEBUG */
+ Tcl_SetVar2(interp, "sqlite_options", "memdebug", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "memdebug", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_MEMSYS3
+ Tcl_SetVar2(interp, "sqlite_options", "mem3", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "mem3", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_MEMSYS5
+ Tcl_SetVar2(interp, "sqlite_options", "mem5", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_ALTERTABLE
+ Tcl_SetVar2(interp, "sqlite_options", "altertable", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "altertable", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_ANALYZE
+ Tcl_SetVar2(interp, "sqlite_options", "analyze", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "analyze", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_ATTACH
+ Tcl_SetVar2(interp, "sqlite_options", "attach", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "attach", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_AUTHORIZATION
+ Tcl_SetVar2(interp, "sqlite_options", "auth", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "auth", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_AUTOINCREMENT
+ Tcl_SetVar2(interp, "sqlite_options", "autoinc", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "autoinc", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_AUTOVACUUM
+ Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "1", TCL_GLOBAL_ONLY);
+#endif /* SQLITE_OMIT_AUTOVACUUM */
+#if !defined(SQLITE_DEFAULT_AUTOVACUUM) || SQLITE_DEFAULT_AUTOVACUUM==0
+ Tcl_SetVar2(interp,"sqlite_options","default_autovacuum","0",TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp,"sqlite_options","default_autovacuum","1",TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION
+ Tcl_SetVar2(interp, "sqlite_options", "between_opt", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "between_opt", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+ Tcl_SetVar2(interp, "sqlite_options", "builtin_test", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "builtin_test", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_BLOB_LITERAL
+ Tcl_SetVar2(interp, "sqlite_options", "bloblit", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "bloblit", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_CAST
+ Tcl_SetVar2(interp, "sqlite_options", "cast", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "cast", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_CHECK
+ Tcl_SetVar2(interp, "sqlite_options", "check", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_COMPLETE
+ Tcl_SetVar2(interp, "sqlite_options", "complete", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "complete", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_COMPOUND_SELECT
+ Tcl_SetVar2(interp, "sqlite_options", "compound", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "compound", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_CONFLICT_CLAUSE
+ Tcl_SetVar2(interp, "sqlite_options", "conflict", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "conflict", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#if SQLITE_OS_UNIX
+ Tcl_SetVar2(interp, "sqlite_options", "crashtest", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "crashtest", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_DATETIME_FUNCS
+ Tcl_SetVar2(interp, "sqlite_options", "datetime", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "datetime", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_DECLTYPE
+ Tcl_SetVar2(interp, "sqlite_options", "decltype", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "decltype", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_DISKIO
+ Tcl_SetVar2(interp, "sqlite_options", "diskio", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "diskio", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_EXPLAIN
+ Tcl_SetVar2(interp, "sqlite_options", "explain", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "explain", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_FLOATING_POINT
+ Tcl_SetVar2(interp, "sqlite_options", "floatingpoint", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "floatingpoint", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_FOREIGN_KEY
+ Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_FTS1
+ Tcl_SetVar2(interp, "sqlite_options", "fts1", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "fts1", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_FTS2
+ Tcl_SetVar2(interp, "sqlite_options", "fts2", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "fts2", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_FTS3
+ Tcl_SetVar2(interp, "sqlite_options", "fts3", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_GET_TABLE
+ Tcl_SetVar2(interp, "sqlite_options", "gettable", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "gettable", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_GLOBALRECOVER
+ Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_ICU
+ Tcl_SetVar2(interp, "sqlite_options", "icu", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "icu", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_INCRBLOB
+ Tcl_SetVar2(interp, "sqlite_options", "incrblob", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "incrblob", "1", TCL_GLOBAL_ONLY);
+#endif /* SQLITE_OMIT_AUTOVACUUM */
+
+#ifdef SQLITE_OMIT_INTEGRITY_CHECK
+ Tcl_SetVar2(interp, "sqlite_options", "integrityck", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "integrityck", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#if defined(SQLITE_DEFAULT_FILE_FORMAT) && SQLITE_DEFAULT_FILE_FORMAT==1
+ Tcl_SetVar2(interp, "sqlite_options", "legacyformat", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "legacyformat", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION
+ Tcl_SetVar2(interp, "sqlite_options", "like_opt", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "like_opt", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ Tcl_SetVar2(interp, "sqlite_options", "load_ext", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "load_ext", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_LOCALTIME
+ Tcl_SetVar2(interp, "sqlite_options", "localtime", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "localtime", "1", TCL_GLOBAL_ONLY);
+#endif
+
+Tcl_SetVar2(interp, "sqlite_options", "long_double",
+ sizeof(LONGDOUBLE_TYPE)>sizeof(double) ? "1" : "0",
+ TCL_GLOBAL_ONLY);
+
+#ifdef SQLITE_OMIT_MEMORYDB
+ Tcl_SetVar2(interp, "sqlite_options", "memorydb", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "memorydb", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ Tcl_SetVar2(interp, "sqlite_options", "memorymanage", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "memorymanage", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_OR_OPTIMIZATION
+ Tcl_SetVar2(interp, "sqlite_options", "or_opt", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "or_opt", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_PAGER_PRAGMAS
+ Tcl_SetVar2(interp, "sqlite_options", "pager_pragmas", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "pager_pragmas", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_PARSER
+ Tcl_SetVar2(interp, "sqlite_options", "parser", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "parser", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#if defined(SQLITE_OMIT_PRAGMA) || defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ Tcl_SetVar2(interp, "sqlite_options", "pragma", "0", TCL_GLOBAL_ONLY);
+ Tcl_SetVar2(interp, "sqlite_options", "integrityck", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "pragma", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_PROGRESS_CALLBACK
+ Tcl_SetVar2(interp, "sqlite_options", "progress", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "progress", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_REINDEX
+ Tcl_SetVar2(interp, "sqlite_options", "reindex", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "reindex", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_ENABLE_RTREE
+ Tcl_SetVar2(interp, "sqlite_options", "rtree", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "rtree", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS
+ Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
+ Tcl_SetVar2(interp, "sqlite_options", "schema_version", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_SHARED_CACHE
+ Tcl_SetVar2(interp, "sqlite_options", "shared_cache", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "shared_cache", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_SUBQUERY
+ Tcl_SetVar2(interp, "sqlite_options", "subquery", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "subquery", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_TCL_VARIABLE
+ Tcl_SetVar2(interp, "sqlite_options", "tclvar", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "tclvar", "1", TCL_GLOBAL_ONLY);
+#endif
+
+ rc = sqlite3_threadsafe();
+#if SQLITE_THREADSAFE
+ Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "1", TCL_GLOBAL_ONLY);
+ assert( rc );
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "0", TCL_GLOBAL_ONLY);
+ assert( !rc );
+#endif
+
+#ifdef SQLITE_OMIT_TRACE
+ Tcl_SetVar2(interp, "sqlite_options", "trace", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "trace", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_TRIGGER
+ Tcl_SetVar2(interp, "sqlite_options", "trigger", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "trigger", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_TEMPDB
+ Tcl_SetVar2(interp, "sqlite_options", "tempdb", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "tempdb", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_UTF16
+ Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#if defined(SQLITE_OMIT_VACUUM) || defined(SQLITE_OMIT_ATTACH)
+ Tcl_SetVar2(interp, "sqlite_options", "vacuum", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "vacuum", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_VIEW
+ Tcl_SetVar2(interp, "sqlite_options", "view", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "view", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+ Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef SQLITE_SECURE_DELETE
+ Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#ifdef YYTRACKMAXSTACKDEPTH
+ Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY);
+#endif
+
+#define LINKVAR(x) { \
+ static const int cv_ ## x = SQLITE_ ## x; \
+ Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
+ TCL_LINK_INT | TCL_LINK_READ_ONLY); }
+
+ LINKVAR( MAX_LENGTH );
+ LINKVAR( MAX_COLUMN );
+ LINKVAR( MAX_SQL_LENGTH );
+ LINKVAR( MAX_EXPR_DEPTH );
+ LINKVAR( MAX_COMPOUND_SELECT );
+ LINKVAR( MAX_VDBE_OP );
+ LINKVAR( MAX_FUNCTION_ARG );
+ LINKVAR( MAX_VARIABLE_NUMBER );
+ LINKVAR( MAX_PAGE_SIZE );
+ LINKVAR( MAX_PAGE_COUNT );
+ LINKVAR( MAX_LIKE_PATTERN_LENGTH );
+ LINKVAR( DEFAULT_TEMP_CACHE_SIZE );
+ LINKVAR( DEFAULT_CACHE_SIZE );
+ LINKVAR( DEFAULT_PAGE_SIZE );
+ LINKVAR( DEFAULT_FILE_FORMAT );
+ LINKVAR( MAX_ATTACHED );
+
+ {
+ static const int cv_TEMP_STORE = SQLITE_TEMP_STORE;
+ Tcl_LinkVar(interp, "TEMP_STORE", (char *)&(cv_TEMP_STORE),
+ TCL_LINK_INT | TCL_LINK_READ_ONLY);
+ }
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqliteconfig_Init(Tcl_Interp *interp){
+ set_options(interp);
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_devsym.c b/third_party/sqlite/src/test_devsym.c
new file mode 100755
index 0000000..0314804
--- /dev/null
+++ b/third_party/sqlite/src/test_devsym.c
@@ -0,0 +1,353 @@
+/*
+** 2008 Jan 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 contains code that modified the OS layer in order to simulate
+** different device types (by overriding the return values of the
+** xDeviceCharacteristics() and xSectorSize() methods).
+**
+** $Id: test_devsym.c,v 1.7 2008/06/06 11:11:26 danielk1977 Exp $
+*/
+#if SQLITE_TEST /* This file is used for testing only */
+
+#include "sqlite3.h"
+#include "sqliteInt.h"
+
+/*
+** Maximum pathname length supported by the devsym backend.
+*/
+#define DEVSYM_MAX_PATHNAME 512
+
+/*
+** Name used to identify this VFS.
+*/
+#define DEVSYM_VFS_NAME "devsym"
+
+typedef struct devsym_file devsym_file;
+struct devsym_file {
+ sqlite3_file base;
+ sqlite3_file *pReal;
+};
+
+/*
+** Method declarations for devsym_file.
+*/
+static int devsymClose(sqlite3_file*);
+static int devsymRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int devsymWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int devsymTruncate(sqlite3_file*, sqlite3_int64 size);
+static int devsymSync(sqlite3_file*, int flags);
+static int devsymFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int devsymLock(sqlite3_file*, int);
+static int devsymUnlock(sqlite3_file*, int);
+static int devsymCheckReservedLock(sqlite3_file*, int *);
+static int devsymFileControl(sqlite3_file*, int op, void *pArg);
+static int devsymSectorSize(sqlite3_file*);
+static int devsymDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for devsym_vfs.
+*/
+static int devsymOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int devsymDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int devsymAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int devsymFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+static void *devsymDlOpen(sqlite3_vfs*, const char *zFilename);
+static void devsymDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void *devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol);
+static void devsymDlClose(sqlite3_vfs*, void*);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int devsymSleep(sqlite3_vfs*, int microseconds);
+static int devsymCurrentTime(sqlite3_vfs*, double*);
+
+static sqlite3_vfs devsym_vfs = {
+ 1, /* iVersion */
+ sizeof(devsym_file), /* szOsFile */
+ DEVSYM_MAX_PATHNAME, /* mxPathname */
+ 0, /* pNext */
+ DEVSYM_VFS_NAME, /* zName */
+ 0, /* pAppData */
+ devsymOpen, /* xOpen */
+ devsymDelete, /* xDelete */
+ devsymAccess, /* xAccess */
+ devsymFullPathname, /* xFullPathname */
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ devsymDlOpen, /* xDlOpen */
+ devsymDlError, /* xDlError */
+ devsymDlSym, /* xDlSym */
+ devsymDlClose, /* xDlClose */
+#else
+ 0, /* xDlOpen */
+ 0, /* xDlError */
+ 0, /* xDlSym */
+ 0, /* xDlClose */
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+ devsymRandomness, /* xRandomness */
+ devsymSleep, /* xSleep */
+ devsymCurrentTime /* xCurrentTime */
+};
+
+static sqlite3_io_methods devsym_io_methods = {
+ 1, /* iVersion */
+ devsymClose, /* xClose */
+ devsymRead, /* xRead */
+ devsymWrite, /* xWrite */
+ devsymTruncate, /* xTruncate */
+ devsymSync, /* xSync */
+ devsymFileSize, /* xFileSize */
+ devsymLock, /* xLock */
+ devsymUnlock, /* xUnlock */
+ devsymCheckReservedLock, /* xCheckReservedLock */
+ devsymFileControl, /* xFileControl */
+ devsymSectorSize, /* xSectorSize */
+ devsymDeviceCharacteristics /* xDeviceCharacteristics */
+};
+
+struct DevsymGlobal {
+ sqlite3_vfs *pVfs;
+ int iDeviceChar;
+ int iSectorSize;
+};
+struct DevsymGlobal g = {0, 0, 512};
+
+/*
+** Close an devsym-file.
+*/
+static int devsymClose(sqlite3_file *pFile){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsClose(p->pReal);
+}
+
+/*
+** Read data from an devsym-file.
+*/
+static int devsymRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+}
+
+/*
+** Write data to an devsym-file.
+*/
+static int devsymWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+}
+
+/*
+** Truncate an devsym-file.
+*/
+static int devsymTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsTruncate(p->pReal, size);
+}
+
+/*
+** Sync an devsym-file.
+*/
+static int devsymSync(sqlite3_file *pFile, int flags){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsSync(p->pReal, flags);
+}
+
+/*
+** Return the current file-size of an devsym-file.
+*/
+static int devsymFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsFileSize(p->pReal, pSize);
+}
+
+/*
+** Lock an devsym-file.
+*/
+static int devsymLock(sqlite3_file *pFile, int eLock){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsLock(p->pReal, eLock);
+}
+
+/*
+** Unlock an devsym-file.
+*/
+static int devsymUnlock(sqlite3_file *pFile, int eLock){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsUnlock(p->pReal, eLock);
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an devsym-file.
+*/
+static int devsymCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsCheckReservedLock(p->pReal, pResOut);
+}
+
+/*
+** File control method. For custom operations on an devsym-file.
+*/
+static int devsymFileControl(sqlite3_file *pFile, int op, void *pArg){
+ devsym_file *p = (devsym_file *)pFile;
+ return sqlite3OsFileControl(p->pReal, op, pArg);
+}
+
+/*
+** Return the sector-size in bytes for an devsym-file.
+*/
+static int devsymSectorSize(sqlite3_file *pFile){
+ return g.iSectorSize;
+}
+
+/*
+** Return the device characteristic flags supported by an devsym-file.
+*/
+static int devsymDeviceCharacteristics(sqlite3_file *pFile){
+ return g.iDeviceChar;
+}
+
+/*
+** Open an devsym file handle.
+*/
+static int devsymOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ devsym_file *p = (devsym_file *)pFile;
+ pFile->pMethods = &devsym_io_methods;
+ p->pReal = (sqlite3_file *)&p[1];
+ return sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int devsymDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ return sqlite3OsDelete(g.pVfs, zPath, dirSync);
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int devsymAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
+*/
+static int devsymFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
+}
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *devsymDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return sqlite3OsDlOpen(g.pVfs, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void devsymDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ sqlite3OsDlError(g.pVfs, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void *devsymDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+ return sqlite3OsDlSym(g.pVfs, pHandle, zSymbol);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void devsymDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ sqlite3OsDlClose(g.pVfs, pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int devsymRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int devsymSleep(sqlite3_vfs *pVfs, int nMicro){
+ return sqlite3OsSleep(g.pVfs, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
+}
+
+/*
+** This procedure registers the devsym vfs with SQLite. If the argument is
+** true, the devsym vfs becomes the new default vfs. It is the only publicly
+** available function in this file.
+*/
+void devsym_register(int iDeviceChar, int iSectorSize){
+ if( g.pVfs==0 ){
+ g.pVfs = sqlite3_vfs_find(0);
+ devsym_vfs.szOsFile += g.pVfs->szOsFile;
+ sqlite3_vfs_register(&devsym_vfs, 0);
+ }
+ if( iDeviceChar>=0 ){
+ g.iDeviceChar = iDeviceChar;
+ }
+ if( iSectorSize>=0 ){
+ g.iSectorSize = iSectorSize;
+ }
+}
+
+#endif
diff --git a/third_party/sqlite/src/test_func.c b/third_party/sqlite/src/test_func.c
new file mode 100755
index 0000000..eaafc23
--- /dev/null
+++ b/third_party/sqlite/src/test_func.c
@@ -0,0 +1,431 @@
+/*
+** 2008 March 19
+**
+** 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.
+**
+*************************************************************************
+** Code for testing all sorts of SQLite interfaces. This code
+** implements new SQL functions used by the test scripts.
+**
+** $Id: test_func.c,v 1.10 2008/08/02 03:50:39 drh Exp $
+*/
+#include "sqlite3.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+
+/*
+** Allocate nByte bytes of space using sqlite3_malloc(). If the
+** allocation fails, call sqlite3_result_error_nomem() to notify
+** the database handle that malloc() has failed.
+*/
+static void *testContextMalloc(sqlite3_context *context, int nByte){
+ char *z = sqlite3_malloc(nByte);
+ if( !z && nByte>0 ){
+ sqlite3_result_error_nomem(context);
+ }
+ return z;
+}
+
+/*
+** This function generates a string of random characters. Used for
+** generating test data.
+*/
+static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){
+ static const unsigned char zSrc[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ ".-!,:*^+=_|?/<> ";
+ int iMin, iMax, n, r, i;
+ unsigned char zBuf[1000];
+
+ /* It used to be possible to call randstr() with any number of arguments,
+ ** but now it is registered with SQLite as requiring exactly 2.
+ */
+ assert(argc==2);
+
+ iMin = sqlite3_value_int(argv[0]);
+ if( iMin<0 ) iMin = 0;
+ if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1;
+ iMax = sqlite3_value_int(argv[1]);
+ if( iMax<iMin ) iMax = iMin;
+ if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf)-1;
+ n = iMin;
+ if( iMax>iMin ){
+ sqlite3_randomness(sizeof(r), &r);
+ r &= 0x7fffffff;
+ n += r%(iMax + 1 - iMin);
+ }
+ assert( n<sizeof(zBuf) );
+ sqlite3_randomness(n, zBuf);
+ for(i=0; i<n; i++){
+ zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)];
+ }
+ zBuf[n] = 0;
+ sqlite3_result_text(context, (char*)zBuf, n, SQLITE_TRANSIENT);
+}
+
+/*
+** The following two SQL functions are used to test returning a text
+** result with a destructor. Function 'test_destructor' takes one argument
+** and returns the same argument interpreted as TEXT. A destructor is
+** passed with the sqlite3_result_text() call.
+**
+** SQL function 'test_destructor_count' returns the number of outstanding
+** allocations made by 'test_destructor';
+**
+** WARNING: Not threadsafe.
+*/
+static int test_destructor_count_var = 0;
+static void destructor(void *p){
+ char *zVal = (char *)p;
+ assert(zVal);
+ zVal--;
+ sqlite3_free(zVal);
+ test_destructor_count_var--;
+}
+static void test_destructor(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ char *zVal;
+ int len;
+
+ test_destructor_count_var++;
+ assert( nArg==1 );
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ len = sqlite3_value_bytes(argv[0]);
+ zVal = testContextMalloc(pCtx, len+3);
+ if( !zVal ){
+ return;
+ }
+ zVal[len+1] = 0;
+ zVal[len+2] = 0;
+ zVal++;
+ memcpy(zVal, sqlite3_value_text(argv[0]), len);
+ sqlite3_result_text(pCtx, zVal, -1, destructor);
+}
+#ifndef SQLITE_OMIT_UTF16
+static void test_destructor16(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ char *zVal;
+ int len;
+
+ test_destructor_count_var++;
+ assert( nArg==1 );
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ len = sqlite3_value_bytes16(argv[0]);
+ zVal = testContextMalloc(pCtx, len+3);
+ if( !zVal ){
+ return;
+ }
+ zVal[len+1] = 0;
+ zVal[len+2] = 0;
+ zVal++;
+ memcpy(zVal, sqlite3_value_text16(argv[0]), len);
+ sqlite3_result_text16(pCtx, zVal, -1, destructor);
+}
+#endif
+static void test_destructor_count(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ sqlite3_result_int(pCtx, test_destructor_count_var);
+}
+
+/*
+** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata()
+** interface.
+**
+** The test_auxdata() SQL function attempts to register each of its arguments
+** as auxiliary data. If there are no prior registrations of aux data for
+** that argument (meaning the argument is not a constant or this is its first
+** call) then the result for that argument is 0. If there is a prior
+** registration, the result for that argument is 1. The overall result
+** is the individual argument results separated by spaces.
+*/
+static void free_test_auxdata(void *p) {sqlite3_free(p);}
+static void test_auxdata(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ int i;
+ char *zRet = testContextMalloc(pCtx, nArg*2);
+ if( !zRet ) return;
+ memset(zRet, 0, nArg*2);
+ for(i=0; i<nArg; i++){
+ char const *z = (char*)sqlite3_value_text(argv[i]);
+ if( z ){
+ int n;
+ char *zAux = sqlite3_get_auxdata(pCtx, i);
+ if( zAux ){
+ zRet[i*2] = '1';
+ assert( strcmp(zAux,z)==0 );
+ }else {
+ zRet[i*2] = '0';
+ }
+ n = strlen(z) + 1;
+ zAux = testContextMalloc(pCtx, n);
+ if( zAux ){
+ memcpy(zAux, z, n);
+ sqlite3_set_auxdata(pCtx, i, zAux, free_test_auxdata);
+ }
+ zRet[i*2+1] = ' ';
+ }
+ }
+ sqlite3_result_text(pCtx, zRet, 2*nArg-1, free_test_auxdata);
+}
+
+/*
+** A function to test error reporting from user functions. This function
+** returns a copy of its first argument as the error message. If the
+** second argument exists, it becomes the error code.
+*/
+static void test_error(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ sqlite3_result_error(pCtx, (char*)sqlite3_value_text(argv[0]), -1);
+ if( nArg==2 ){
+ sqlite3_result_error_code(pCtx, sqlite3_value_int(argv[1]));
+ }
+}
+
+/*
+** This function takes two arguments. It performance UTF-8/16 type
+** conversions on the first argument then returns a copy of the second
+** argument.
+**
+** This function is used in cases such as the following:
+**
+** SELECT test_isolation(x,x) FROM t1;
+**
+** We want to verify that the type conversions that occur on the
+** first argument do not invalidate the second argument.
+*/
+static void test_isolation(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+#ifndef SQLITE_OMIT_UTF16
+ sqlite3_value_text16(argv[0]);
+ sqlite3_value_text(argv[0]);
+ sqlite3_value_text16(argv[0]);
+ sqlite3_value_text(argv[0]);
+#endif
+ sqlite3_result_value(pCtx, argv[1]);
+}
+
+/*
+** Invoke an SQL statement recursively. The function result is the
+** first column of the first row of the result set.
+*/
+static void test_eval(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+ const char *zSql;
+
+ zSql = (char*)sqlite3_value_text(argv[0]);
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0));
+ }
+ rc = sqlite3_finalize(pStmt);
+ }
+ if( rc ){
+ char *zErr;
+ assert( pStmt==0 );
+ zErr = sqlite3_mprintf("sqlite3_prepare_v2() error: %s",sqlite3_errmsg(db));
+ sqlite3_result_text(pCtx, zErr, -1, sqlite3_free);
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+
+
+static int registerTestFunctions(sqlite3 *db){
+ static const struct {
+ char *zName;
+ signed char nArg;
+ unsigned char eTextRep; /* 1: UTF-16. 0: UTF-8 */
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
+ } aFuncs[] = {
+ { "randstr", 2, SQLITE_UTF8, randStr },
+ { "test_destructor", 1, SQLITE_UTF8, test_destructor},
+#ifndef SQLITE_OMIT_UTF16
+ { "test_destructor16", 1, SQLITE_UTF8, test_destructor16},
+#endif
+ { "test_destructor_count", 0, SQLITE_UTF8, test_destructor_count},
+ { "test_auxdata", -1, SQLITE_UTF8, test_auxdata},
+ { "test_error", 1, SQLITE_UTF8, test_error},
+ { "test_error", 2, SQLITE_UTF8, test_error},
+ { "test_eval", 1, SQLITE_UTF8, test_eval},
+ { "test_isolation", 2, SQLITE_UTF8, test_isolation},
+ };
+ int i;
+ extern int Md5_Register(sqlite3*);
+
+ for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
+ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg,
+ aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0);
+ }
+ Md5_Register(db);
+ return SQLITE_OK;
+}
+
+/*
+** TCLCMD: autoinstall_test_functions
+**
+** Invoke this TCL command to use sqlite3_auto_extension() to cause
+** the standard set of test functions to be loaded into each new
+** database connection.
+*/
+static int autoinstall_test_funcs(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_auto_extension((void*)registerTestFunctions);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** A bogus step function and finalizer function.
+*/
+static void tStep(sqlite3_context *a, int b, sqlite3_value **c){}
+static void tFinal(sqlite3_context *a){}
+
+
+/*
+** tclcmd: abuse_create_function
+**
+** Make various calls to sqlite3_create_function that do not have valid
+** parameters. Verify that the error condition is detected and reported.
+*/
+static int abuse_create_function(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
+ sqlite3 *db;
+ int rc;
+ int mxArg;
+
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+ rc = sqlite3_create_function(db, "tx", 1, SQLITE_UTF8, 0, tStep,tStep,tFinal);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", 1, SQLITE_UTF8, 0, tStep, tStep, 0);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", 1, SQLITE_UTF8, 0, tStep, 0, tFinal);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", 1, SQLITE_UTF8, 0, 0, 0, tFinal);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", 1, SQLITE_UTF8, 0, 0, tStep, 0);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", -2, SQLITE_UTF8, 0, tStep, 0, 0);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "tx", 128, SQLITE_UTF8, 0, tStep, 0, 0);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ rc = sqlite3_create_function(db, "funcxx"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789",
+ 1, SQLITE_UTF8, 0, tStep, 0, 0);
+ if( rc!=SQLITE_ERROR ) goto abuse_err;
+ if( sqlite3_errcode(db)!=SQLITE_ERROR ) goto abuse_err;
+ if( strcmp(sqlite3_errmsg(db), "bad parameters")!=0 ) goto abuse_err;
+
+ /* This last function registration should actually work. Generate
+ ** a no-op function (that always returns NULL) and which has the
+ ** maximum-length function name and the maximum number of parameters.
+ */
+ sqlite3_limit(db, SQLITE_LIMIT_FUNCTION_ARG, 10000);
+ mxArg = sqlite3_limit(db, SQLITE_LIMIT_FUNCTION_ARG, -1);
+ rc = sqlite3_create_function(db, "nullx"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789"
+ "_123456789_123456789_123456789_123456789_123456789",
+ mxArg, SQLITE_UTF8, 0, tStep, 0, 0);
+ if( rc!=SQLITE_OK ) goto abuse_err;
+
+ return TCL_OK;
+
+abuse_err:
+ Tcl_AppendResult(interp, "sqlite3_create_function abused test failed",
+ (char*)0);
+ return TCL_ERROR;
+}
+
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest_func_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aObjCmd[] = {
+ { "autoinstall_test_functions", autoinstall_test_funcs },
+ { "abuse_create_function", abuse_create_function },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
+ }
+ sqlite3_initialize();
+ sqlite3_auto_extension((void*)registerTestFunctions);
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_hexio.c b/third_party/sqlite/src/test_hexio.c
new file mode 100755
index 0000000..9e1e8de
--- /dev/null
+++ b/third_party/sqlite/src/test_hexio.c
@@ -0,0 +1,342 @@
+/*
+** 2007 April 6
+**
+** 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.
+**
+*************************************************************************
+** Code for testing all sorts of SQLite interfaces. This code
+** implements TCL commands for reading and writing the binary
+** database files and displaying the content of those files as
+** hexadecimal. We could, in theory, use the built-in "binary"
+** command of TCL to do a lot of this, but there are some issues
+** with historical versions of the "binary" command. So it seems
+** easier and safer to build our own mechanism.
+**
+** $Id: test_hexio.c,v 1.7 2008/05/12 16:17:42 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+
+/*
+** Convert binary to hex. The input zBuf[] contains N bytes of
+** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[]
+** with a hexadecimal representation of its original binary input.
+*/
+void sqlite3TestBinToHex(unsigned char *zBuf, int N){
+ const unsigned char zHex[] = "0123456789ABCDEF";
+ int i, j;
+ unsigned char c;
+ i = N*2;
+ zBuf[i--] = 0;
+ for(j=N-1; j>=0; j--){
+ c = zBuf[j];
+ zBuf[i--] = zHex[c&0xf];
+ zBuf[i--] = zHex[c>>4];
+ }
+ assert( i==-1 );
+}
+
+/*
+** Convert hex to binary. The input zIn[] contains N bytes of
+** hexadecimal. Convert this into binary and write aOut[] with
+** the binary data. Spaces in the original input are ignored.
+** Return the number of bytes of binary rendered.
+*/
+int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){
+ const unsigned char aMap[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0,
+ 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ int i, j;
+ int hi=1;
+ unsigned char c;
+
+ for(i=j=0; i<N; i++){
+ c = aMap[zIn[i]];
+ if( c==0 ) continue;
+ if( hi ){
+ aOut[j] = (c-1)<<4;
+ hi = 0;
+ }else{
+ aOut[j++] |= c-1;
+ hi = 1;
+ }
+ }
+ return j;
+}
+
+
+/*
+** Usage: hexio_read FILENAME OFFSET AMT
+**
+** Read AMT bytes from file FILENAME beginning at OFFSET from the
+** beginning of the file. Convert that information to hexadecimal
+** and return the resulting HEX string.
+*/
+static int hexio_read(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int offset;
+ int amt, got;
+ const char *zFile;
+ unsigned char *zBuf;
+ FILE *in;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR;
+ zFile = Tcl_GetString(objv[1]);
+ zBuf = sqlite3_malloc( amt*2+1 );
+ if( zBuf==0 ){
+ return TCL_ERROR;
+ }
+ in = fopen(zFile, "rb");
+ if( in==0 ){
+ in = fopen(zFile, "r");
+ }
+ if( in==0 ){
+ Tcl_AppendResult(interp, "cannot open input file ", zFile, 0);
+ return TCL_ERROR;
+ }
+ fseek(in, offset, SEEK_SET);
+ got = fread(zBuf, 1, amt, in);
+ fclose(in);
+ if( got<0 ){
+ got = 0;
+ }
+ sqlite3TestBinToHex(zBuf, got);
+ Tcl_AppendResult(interp, zBuf, 0);
+ sqlite3_free(zBuf);
+ return TCL_OK;
+}
+
+
+/*
+** Usage: hexio_write FILENAME OFFSET DATA
+**
+** Write DATA into file FILENAME beginning at OFFSET from the
+** beginning of the file. DATA is expressed in hexadecimal.
+*/
+static int hexio_write(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int offset;
+ int nIn, nOut, written;
+ const char *zFile;
+ const unsigned char *zIn;
+ unsigned char *aOut;
+ FILE *out;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
+ zFile = Tcl_GetString(objv[1]);
+ zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn);
+ aOut = sqlite3_malloc( nIn/2 );
+ if( aOut==0 ){
+ return TCL_ERROR;
+ }
+ nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
+ out = fopen(zFile, "r+b");
+ if( out==0 ){
+ out = fopen(zFile, "r+");
+ }
+ if( out==0 ){
+ Tcl_AppendResult(interp, "cannot open output file ", zFile, 0);
+ return TCL_ERROR;
+ }
+ fseek(out, offset, SEEK_SET);
+ written = fwrite(aOut, 1, nOut, out);
+ sqlite3_free(aOut);
+ fclose(out);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(written));
+ return TCL_OK;
+}
+
+/*
+** USAGE: hexio_get_int HEXDATA
+**
+** Interpret the HEXDATA argument as a big-endian integer. Return
+** the value of that integer. HEXDATA can contain between 2 and 8
+** hexadecimal digits.
+*/
+static int hexio_get_int(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int val;
+ int nIn, nOut;
+ const unsigned char *zIn;
+ unsigned char *aOut;
+ unsigned char aNum[4];
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA");
+ return TCL_ERROR;
+ }
+ zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn);
+ aOut = sqlite3_malloc( nIn/2 );
+ if( aOut==0 ){
+ return TCL_ERROR;
+ }
+ nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
+ if( nOut>=4 ){
+ memcpy(aNum, aOut, 4);
+ }else{
+ memset(aNum, 0, sizeof(aNum));
+ memcpy(&aNum[4-nOut], aOut, nOut);
+ }
+ sqlite3_free(aOut);
+ val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3];
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(val));
+ return TCL_OK;
+}
+
+
+/*
+** USAGE: hexio_render_int16 INTEGER
+**
+** Render INTEGER has a 16-bit big-endian integer in hexadecimal.
+*/
+static int hexio_render_int16(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int val;
+ unsigned char aNum[10];
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
+ aNum[0] = val>>8;
+ aNum[1] = val;
+ sqlite3TestBinToHex(aNum, 2);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4));
+ return TCL_OK;
+}
+
+
+/*
+** USAGE: hexio_render_int32 INTEGER
+**
+** Render INTEGER has a 32-bit big-endian integer in hexadecimal.
+*/
+static int hexio_render_int32(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int val;
+ unsigned char aNum[10];
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
+ aNum[0] = val>>24;
+ aNum[1] = val>>16;
+ aNum[2] = val>>8;
+ aNum[3] = val;
+ sqlite3TestBinToHex(aNum, 4);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8));
+ return TCL_OK;
+}
+
+/*
+** USAGE: utf8_to_utf8 HEX
+**
+** The argument is a UTF8 string represented in hexadecimal.
+** The UTF8 might not be well-formed. Run this string through
+** sqlite3Utf8to8() convert it back to hex and return the result.
+*/
+static int utf8_to_utf8(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifdef SQLITE_DEBUG
+ int n;
+ int nOut;
+ const unsigned char *zOrig;
+ unsigned char *z;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HEX");
+ return TCL_ERROR;
+ }
+ zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n);
+ z = sqlite3_malloc( n+3 );
+ n = sqlite3TestHexToBin(zOrig, n, z);
+ z[n] = 0;
+ nOut = sqlite3Utf8To8(z);
+ sqlite3TestBinToHex(z,nOut);
+ Tcl_AppendResult(interp, (char*)z, 0);
+ sqlite3_free(z);
+#endif
+ return TCL_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest_hexio_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aObjCmd[] = {
+ { "hexio_read", hexio_read },
+ { "hexio_write", hexio_write },
+ { "hexio_get_int", hexio_get_int },
+ { "hexio_render_int16", hexio_render_int16 },
+ { "hexio_render_int32", hexio_render_int32 },
+ { "utf8_to_utf8", utf8_to_utf8 },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
+ }
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_loadext.c b/third_party/sqlite/src/test_loadext.c
new file mode 100755
index 0000000..e973566
--- /dev/null
+++ b/third_party/sqlite/src/test_loadext.c
@@ -0,0 +1,124 @@
+/*
+** 2006 June 14
+**
+** 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.
+**
+*************************************************************************
+** Test extension for testing the sqlite3_load_extension() function.
+**
+** $Id: test_loadext.c,v 1.3 2008/08/02 03:50:39 drh Exp $
+*/
+#include <string.h>
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+
+/*
+** The half() SQL function returns half of its input value.
+*/
+static void halfFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3_result_double(context, 0.5*sqlite3_value_double(argv[0]));
+}
+
+/*
+** SQL functions to call the sqlite3_status function and return results.
+*/
+static void statusFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int op, mx, cur, resetFlag, rc;
+ if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
+ op = sqlite3_value_int(argv[0]);
+ }else if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
+ int i;
+ const char *zName;
+ static const struct {
+ const char *zName;
+ int op;
+ } aOp[] = {
+ { "MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
+ { "PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
+ { "PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
+ { "SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
+ { "SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
+ { "MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
+ };
+ int nOp = sizeof(aOp)/sizeof(aOp[0]);
+ zName = (const char*)sqlite3_value_text(argv[0]);
+ for(i=0; i<nOp; i++){
+ if( strcmp(aOp[i].zName, zName)==0 ){
+ op = aOp[i].op;
+ break;
+ }
+ }
+ if( i>=nOp ){
+ char *zMsg = sqlite3_mprintf("unknown status property: %s", zName);
+ sqlite3_result_error(context, zMsg, -1);
+ sqlite3_free(zMsg);
+ return;
+ }
+ }else{
+ sqlite3_result_error(context, "unknown status type", -1);
+ return;
+ }
+ if( argc==2 ){
+ resetFlag = sqlite3_value_int(argv[1]);
+ }else{
+ resetFlag = 0;
+ }
+ rc = sqlite3_status(op, &cur, &mx, resetFlag);
+ if( rc!=SQLITE_OK ){
+ char *zMsg = sqlite3_mprintf("sqlite3_status(%d,...) returns %d", op, rc);
+ sqlite3_result_error(context, zMsg, -1);
+ sqlite3_free(zMsg);
+ return;
+ }
+ if( argc==2 ){
+ sqlite3_result_int(context, mx);
+ }else{
+ sqlite3_result_int(context, cur);
+ }
+}
+
+/*
+** Extension load function.
+*/
+int testloadext_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int nErr = 0;
+ SQLITE_EXTENSION_INIT2(pApi);
+ nErr |= sqlite3_create_function(db, "half", 1, SQLITE_ANY, 0, halfFunc, 0, 0);
+ nErr |= sqlite3_create_function(db, "sqlite3_status", 1, SQLITE_ANY, 0,
+ statusFunc, 0, 0);
+ nErr |= sqlite3_create_function(db, "sqlite3_status", 2, SQLITE_ANY, 0,
+ statusFunc, 0, 0);
+ return nErr ? SQLITE_ERROR : SQLITE_OK;
+}
+
+/*
+** Another extension entry point. This one always fails.
+*/
+int testbrokenext_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ char *zErr;
+ SQLITE_EXTENSION_INIT2(pApi);
+ zErr = sqlite3_mprintf("broken!");
+ *pzErrMsg = zErr;
+ return 1;
+}
diff --git a/third_party/sqlite/src/test_malloc.c b/third_party/sqlite/src/test_malloc.c
new file mode 100755
index 0000000..24d2389
--- /dev/null
+++ b/third_party/sqlite/src/test_malloc.c
@@ -0,0 +1,1339 @@
+/*
+** 2007 August 15
+**
+** 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 contains code used to implement test interfaces to the
+** memory allocation subsystem.
+**
+** $Id: test_malloc.c,v 1.47 2008/08/05 17:53:24 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/*
+** This structure is used to encapsulate the global state variables used
+** by malloc() fault simulation.
+*/
+static struct MemFault {
+ int iCountdown; /* Number of pending successes before a failure */
+ int nRepeat; /* Number of times to repeat the failure */
+ int nBenign; /* Number of benign failures seen since last config */
+ int nFail; /* Number of failures seen since last config */
+ u8 enable; /* True if enabled */
+ int isInstalled; /* True if the fault simulation layer is installed */
+ int isBenignMode; /* True if malloc failures are considered benign */
+ sqlite3_mem_methods m; /* 'Real' malloc implementation */
+} memfault;
+
+/*
+** This routine exists as a place to set a breakpoint that will
+** fire on any simulated malloc() failure.
+*/
+static void sqlite3Fault(void){
+ static int cnt = 0;
+ cnt++;
+}
+
+/*
+** Check to see if a fault should be simulated. Return true to simulate
+** the fault. Return false if the fault should not be simulated.
+*/
+static int faultsimStep(){
+ if( likely(!memfault.enable) ){
+ return 0;
+ }
+ if( memfault.iCountdown>0 ){
+ memfault.iCountdown--;
+ return 0;
+ }
+ sqlite3Fault();
+ memfault.nFail++;
+ if( memfault.isBenignMode>0 ){
+ memfault.nBenign++;
+ }
+ memfault.nRepeat--;
+ if( memfault.nRepeat<=0 ){
+ memfault.enable = 0;
+ }
+ return 1;
+}
+
+/*
+** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation
+** logic.
+*/
+static void *faultsimMalloc(int n){
+ void *p = 0;
+ if( !faultsimStep() ){
+ p = memfault.m.xMalloc(n);
+ }
+ return p;
+}
+
+
+/*
+** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation
+** logic.
+*/
+static void *faultsimRealloc(void *pOld, int n){
+ void *p = 0;
+ if( !faultsimStep() ){
+ p = memfault.m.xRealloc(pOld, n);
+ }
+ return p;
+}
+
+/*
+** The following method calls are passed directly through to the underlying
+** malloc system:
+**
+** xFree
+** xSize
+** xRoundup
+** xInit
+** xShutdown
+*/
+static void faultsimFree(void *p){
+ memfault.m.xFree(p);
+}
+static int faultsimSize(void *p){
+ return memfault.m.xSize(p);
+}
+static int faultsimRoundup(int n){
+ return memfault.m.xRoundup(n);
+}
+static int faultsimInit(void *p){
+ return memfault.m.xInit(memfault.m.pAppData);
+}
+static void faultsimShutdown(void *p){
+ memfault.m.xShutdown(memfault.m.pAppData);
+}
+
+/*
+** This routine configures the malloc failure simulation. After
+** calling this routine, the next nDelay mallocs will succeed, followed
+** by a block of nRepeat failures, after which malloc() calls will begin
+** to succeed again.
+*/
+static void faultsimConfig(int nDelay, int nRepeat){
+ memfault.iCountdown = nDelay;
+ memfault.nRepeat = nRepeat;
+ memfault.nBenign = 0;
+ memfault.nFail = 0;
+ memfault.enable = nDelay>=0;
+}
+
+/*
+** Return the number of faults (both hard and benign faults) that have
+** occurred since the injector was last configured.
+*/
+static int faultsimFailures(void){
+ return memfault.nFail;
+}
+
+/*
+** Return the number of benign faults that have occurred since the
+** injector was last configured.
+*/
+static int faultsimBenignFailures(void){
+ return memfault.nBenign;
+}
+
+/*
+** Return the number of successes that will occur before the next failure.
+** If no failures are scheduled, return -1.
+*/
+static int faultsimPending(void){
+ if( memfault.enable ){
+ return memfault.iCountdown;
+ }else{
+ return -1;
+ }
+}
+
+
+static void faultsimBeginBenign(void){
+ memfault.isBenignMode++;
+}
+static void faultsimEndBenign(void){
+ memfault.isBenignMode--;
+}
+
+/*
+** Add or remove the fault-simulation layer using sqlite3_config(). If
+** the argument is non-zero, the
+*/
+static int faultsimInstall(int install){
+ static struct sqlite3_mem_methods m = {
+ faultsimMalloc, /* xMalloc */
+ faultsimFree, /* xFree */
+ faultsimRealloc, /* xRealloc */
+ faultsimSize, /* xSize */
+ faultsimRoundup, /* xRoundup */
+ faultsimInit, /* xInit */
+ faultsimShutdown, /* xShutdown */
+ 0 /* pAppData */
+ };
+ int rc;
+
+ install = (install ? 1 : 0);
+ assert(memfault.isInstalled==1 || memfault.isInstalled==0);
+
+ if( install==memfault.isInstalled ){
+ return SQLITE_ERROR;
+ }
+
+ if( install ){
+ rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m);
+ assert(memfault.m.xMalloc);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
+ }
+ sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,
+ faultsimBeginBenign, faultsimEndBenign
+ );
+ }else{
+ assert(memfault.m.xMalloc);
+ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m);
+ sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0);
+ }
+
+ if( rc==SQLITE_OK ){
+ memfault.isInstalled = 1;
+ }
+ return rc;
+}
+
+#ifdef SQLITE_TEST
+
+/*
+** This function is implemented in test1.c. Returns a pointer to a static
+** buffer containing the symbolic SQLite error code that corresponds to
+** the least-significant 8-bits of the integer passed as an argument.
+** For example:
+**
+** sqlite3TestErrorName(1) -> "SQLITE_ERROR"
+*/
+const char *sqlite3TestErrorName(int);
+
+/*
+** Transform pointers to text and back again
+*/
+static void pointerToText(void *p, char *z){
+ static const char zHex[] = "0123456789abcdef";
+ int i, k;
+ unsigned int u;
+ sqlite3_uint64 n;
+ if( p==0 ){
+ strcpy(z, "0");
+ return;
+ }
+ if( sizeof(n)==sizeof(p) ){
+ memcpy(&n, &p, sizeof(p));
+ }else if( sizeof(u)==sizeof(p) ){
+ memcpy(&u, &p, sizeof(u));
+ n = u;
+ }else{
+ assert( 0 );
+ }
+ for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
+ z[k] = zHex[n&0xf];
+ n >>= 4;
+ }
+ z[sizeof(p)*2] = 0;
+}
+static int hexToInt(int h){
+ if( h>='0' && h<='9' ){
+ return h - '0';
+ }else if( h>='a' && h<='f' ){
+ return h - 'a' + 10;
+ }else{
+ return -1;
+ }
+}
+static int textToPointer(const char *z, void **pp){
+ sqlite3_uint64 n = 0;
+ int i;
+ unsigned int u;
+ for(i=0; i<sizeof(void*)*2 && z[0]; i++){
+ int v;
+ v = hexToInt(*z++);
+ if( v<0 ) return TCL_ERROR;
+ n = n*16 + v;
+ }
+ if( *z!=0 ) return TCL_ERROR;
+ if( sizeof(n)==sizeof(*pp) ){
+ memcpy(pp, &n, sizeof(n));
+ }else if( sizeof(u)==sizeof(*pp) ){
+ u = (unsigned int)n;
+ memcpy(pp, &u, sizeof(u));
+ }else{
+ assert( 0 );
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_malloc NBYTES
+**
+** Raw test interface for sqlite3_malloc().
+*/
+static int test_malloc(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nByte;
+ void *p;
+ char zOut[100];
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
+ p = sqlite3_malloc((unsigned)nByte);
+ pointerToText(p, zOut);
+ Tcl_AppendResult(interp, zOut, NULL);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_realloc PRIOR NBYTES
+**
+** Raw test interface for sqlite3_realloc().
+*/
+static int test_realloc(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nByte;
+ void *pPrior, *p;
+ char zOut[100];
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
+ if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
+ Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ p = sqlite3_realloc(pPrior, (unsigned)nByte);
+ pointerToText(p, zOut);
+ Tcl_AppendResult(interp, zOut, NULL);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_free PRIOR
+**
+** Raw test interface for sqlite3_free().
+*/
+static int test_free(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ void *pPrior;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
+ return TCL_ERROR;
+ }
+ if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
+ Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ sqlite3_free(pPrior);
+ return TCL_OK;
+}
+
+/*
+** These routines are in test_hexio.c
+*/
+int sqlite3TestHexToBin(const char *, int, char *);
+int sqlite3TestBinToHex(char*,int);
+
+/*
+** Usage: memset ADDRESS SIZE HEX
+**
+** Set a chunk of memory (obtained from malloc, probably) to a
+** specified hex pattern.
+*/
+static int test_memset(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ void *p;
+ int size, n, i;
+ char *zHex;
+ char *zOut;
+ char zBin[100];
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
+ return TCL_ERROR;
+ }
+ if( textToPointer(Tcl_GetString(objv[1]), &p) ){
+ Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
+ return TCL_ERROR;
+ }
+ if( size<=0 ){
+ Tcl_AppendResult(interp, "size must be positive", (char*)0);
+ return TCL_ERROR;
+ }
+ zHex = Tcl_GetStringFromObj(objv[3], &n);
+ if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
+ n = sqlite3TestHexToBin(zHex, n, zBin);
+ if( n==0 ){
+ Tcl_AppendResult(interp, "no data", (char*)0);
+ return TCL_ERROR;
+ }
+ zOut = p;
+ for(i=0; i<size; i++){
+ zOut[i] = zBin[i%n];
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: memget ADDRESS SIZE
+**
+** Return memory as hexadecimal text.
+*/
+static int test_memget(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ void *p;
+ int size, n;
+ char *zBin;
+ char zHex[100];
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
+ return TCL_ERROR;
+ }
+ if( textToPointer(Tcl_GetString(objv[1]), &p) ){
+ Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
+ return TCL_ERROR;
+ }
+ if( size<=0 ){
+ Tcl_AppendResult(interp, "size must be positive", (char*)0);
+ return TCL_ERROR;
+ }
+ zBin = p;
+ while( size>0 ){
+ if( size>(sizeof(zHex)-1)/2 ){
+ n = (sizeof(zHex)-1)/2;
+ }else{
+ n = size;
+ }
+ memcpy(zHex, zBin, n);
+ zBin += n;
+ size -= n;
+ sqlite3TestBinToHex(zHex, n);
+ Tcl_AppendResult(interp, zHex, (char*)0);
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memory_used
+**
+** Raw test interface for sqlite3_memory_used().
+*/
+static int test_memory_used(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memory_highwater ?RESETFLAG?
+**
+** Raw test interface for sqlite3_memory_highwater().
+*/
+static int test_memory_highwater(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int resetFlag = 0;
+ if( objc!=1 && objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
+ return TCL_ERROR;
+ }
+ if( objc==2 ){
+ if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp,
+ Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memdebug_backtrace DEPTH
+**
+** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined
+** then this routine is a no-op.
+*/
+static int test_memdebug_backtrace(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int depth;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
+#ifdef SQLITE_MEMDEBUG
+ {
+ extern void sqlite3MemdebugBacktrace(int);
+ sqlite3MemdebugBacktrace(depth);
+ }
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memdebug_dump FILENAME
+**
+** Write a summary of unfreed memory to FILENAME.
+*/
+static int test_memdebug_dump(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
+ return TCL_ERROR;
+ }
+#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
+ || defined(SQLITE_POW2_MEMORY_SIZE)
+ {
+ extern void sqlite3MemdebugDump(const char*);
+ sqlite3MemdebugDump(Tcl_GetString(objv[1]));
+ }
+#endif
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memdebug_malloc_count
+**
+** Return the total number of times malloc() has been called.
+*/
+static int test_memdebug_malloc_count(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nMalloc = -1;
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+#if defined(SQLITE_MEMDEBUG)
+ {
+ extern int sqlite3MemdebugMallocCount();
+ nMalloc = sqlite3MemdebugMallocCount();
+ }
+#endif
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS?
+**
+** where options are:
+**
+** -repeat <count>
+** -benigncnt <varname>
+**
+** Arrange for a simulated malloc() failure after COUNTER successes.
+** If a repeat count is specified, the fault is repeated that many
+** times.
+**
+** Each call to this routine overrides the prior counter value.
+** This routine returns the number of simulated failures that have
+** happened since the previous call to this routine.
+**
+** To disable simulated failures, use a COUNTER of -1.
+*/
+static int test_memdebug_fail(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int ii;
+ int iFail;
+ int nRepeat = 1;
+ Tcl_Obj *pBenignCnt = 0;
+ int nBenign;
+ int nFail = 0;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
+
+ for(ii=2; ii<objc; ii+=2){
+ int nOption;
+ char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
+ char *zErr = 0;
+
+ if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
+ if( ii==(objc-1) ){
+ zErr = "option requires an argument: ";
+ }else{
+ if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
+ return TCL_ERROR;
+ }
+ }
+ }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
+ if( ii==(objc-1) ){
+ zErr = "option requires an argument: ";
+ }else{
+ pBenignCnt = objv[ii+1];
+ }
+ }else{
+ zErr = "unknown option: ";
+ }
+
+ if( zErr ){
+ Tcl_AppendResult(interp, zErr, zOption, 0);
+ return TCL_ERROR;
+ }
+ }
+
+ nBenign = faultsimBenignFailures();
+ nFail = faultsimFailures();
+ faultsimConfig(iFail, nRepeat);
+
+ if( pBenignCnt ){
+ Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_memdebug_pending
+**
+** Return the number of malloc() calls that will succeed before a
+** simulated failure occurs. A negative return value indicates that
+** no malloc() failure is scheduled.
+*/
+static int test_memdebug_pending(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nPending;
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+ nPending = faultsimPending();
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_memdebug_settitle TITLE
+**
+** Set a title string stored with each allocation. The TITLE is
+** typically the name of the test that was running when the
+** allocation occurred. The TITLE is stored with the allocation
+** and can be used to figure out which tests are leaking memory.
+**
+** Each title overwrite the previous.
+*/
+static int test_memdebug_settitle(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zTitle;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
+ return TCL_ERROR;
+ }
+ zTitle = Tcl_GetString(objv[1]);
+#ifdef SQLITE_MEMDEBUG
+ {
+ extern int sqlite3MemdebugSettitle(const char*);
+ sqlite3MemdebugSettitle(zTitle);
+ }
+#endif
+ return TCL_OK;
+}
+
+#define MALLOC_LOG_FRAMES 10
+static Tcl_HashTable aMallocLog;
+static int mallocLogEnabled = 0;
+
+typedef struct MallocLog MallocLog;
+struct MallocLog {
+ int nCall;
+ int nByte;
+};
+
+#ifdef SQLITE_MEMDEBUG
+static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
+ if( mallocLogEnabled ){
+ MallocLog *pLog;
+ Tcl_HashEntry *pEntry;
+ int isNew;
+
+ int aKey[MALLOC_LOG_FRAMES];
+ int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
+
+ memset(aKey, 0, nKey);
+ if( (sizeof(void*)*nFrame)<nKey ){
+ nKey = nFrame*sizeof(void*);
+ }
+ memcpy(aKey, aFrame, nKey);
+
+ pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
+ if( isNew ){
+ pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
+ memset(pLog, 0, sizeof(MallocLog));
+ Tcl_SetHashValue(pEntry, (ClientData)pLog);
+ }else{
+ pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
+ }
+
+ pLog->nCall++;
+ pLog->nByte += nByte;
+ }
+}
+#endif /* SQLITE_MEMDEBUG */
+
+static void test_memdebug_log_clear(){
+ Tcl_HashSearch search;
+ Tcl_HashEntry *pEntry;
+ for(
+ pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
+ pEntry;
+ pEntry=Tcl_NextHashEntry(&search)
+ ){
+ MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
+ Tcl_Free((char *)pLog);
+ }
+ Tcl_DeleteHashTable(&aMallocLog);
+ Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
+}
+
+static int test_memdebug_log(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static int isInit = 0;
+ int iSub;
+
+ static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
+ enum MB_enum {
+ MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
+ };
+
+ if( !isInit ){
+#ifdef SQLITE_MEMDEBUG
+ extern void sqlite3MemdebugBacktraceCallback(
+ void (*xBacktrace)(int, int, void **));
+ sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
+#endif
+ Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
+ isInit = 1;
+ }
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
+ }
+ if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
+ return TCL_ERROR;
+ }
+
+ switch( (enum MB_enum)iSub ){
+ case MB_LOG_START:
+ mallocLogEnabled = 1;
+ break;
+ case MB_LOG_STOP:
+ mallocLogEnabled = 0;
+ break;
+ case MB_LOG_DUMP: {
+ Tcl_HashSearch search;
+ Tcl_HashEntry *pEntry;
+ Tcl_Obj *pRet = Tcl_NewObj();
+
+ assert(sizeof(int)==sizeof(void*));
+
+ for(
+ pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
+ pEntry;
+ pEntry=Tcl_NextHashEntry(&search)
+ ){
+ Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
+ MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
+ int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
+ int ii;
+
+ apElem[0] = Tcl_NewIntObj(pLog->nCall);
+ apElem[1] = Tcl_NewIntObj(pLog->nByte);
+ for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
+ apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
+ }
+
+ Tcl_ListObjAppendElement(interp, pRet,
+ Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
+ );
+ }
+
+ Tcl_SetObjResult(interp, pRet);
+ break;
+ }
+ case MB_LOG_CLEAR: {
+ test_memdebug_log_clear();
+ break;
+ }
+
+ case MB_LOG_SYNC: {
+#ifdef SQLITE_MEMDEBUG
+ extern void sqlite3MemdebugSync();
+ test_memdebug_log_clear();
+ mallocLogEnabled = 1;
+ sqlite3MemdebugSync();
+#endif
+ break;
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_scratch SIZE N
+**
+** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
+** The buffer is static and is of limited size. N might be
+** adjusted downward as needed to accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_scratch(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int sz, N, rc;
+ Tcl_Obj *pResult;
+ static char *buf = 0;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
+ free(buf);
+ if( sz<0 ){
+ buf = 0;
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
+ }else{
+ buf = malloc( sz*N + 1 );
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
+ }
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_pagecache SIZE N
+**
+** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
+** The buffer is static and is of limited size. N might be
+** adjusted downward as needed to accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_pagecache(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int sz, N, rc;
+ Tcl_Obj *pResult;
+ static char *buf = 0;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
+ free(buf);
+ if( sz<0 ){
+ buf = 0;
+ rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
+ }else{
+ buf = malloc( sz*N );
+ rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
+ }
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_memstatus BOOLEAN
+**
+** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
+*/
+static int test_config_memstatus(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int enable, rc;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR;
+ rc = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, enable);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_chunkalloc
+**
+*/
+static int test_config_chunkalloc(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+ int nThreshold;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "THRESHOLD");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &nThreshold) ) return TCL_ERROR;
+ rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC, nThreshold);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_lookaside SIZE COUNT
+**
+*/
+static int test_config_lookaside(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+ int sz, cnt;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SIZE COUNT");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &cnt) ) return TCL_ERROR;
+ rc = sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+
+/*
+** Usage: sqlite3_db_config_lookaside CONNECTION BUFID SIZE COUNT
+**
+** There are two static buffers with BUFID 1 and 2. Each static buffer
+** is 10KB in size. A BUFID of 0 indicates that the buffer should be NULL
+** which will cause sqlite3_db_config() to allocate space on its own.
+*/
+static int test_db_config_lookaside(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+ int sz, cnt;
+ sqlite3 *db;
+ int bufid;
+ static char azBuf[2][10000];
+ int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "BUFID SIZE COUNT");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[2], &bufid) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[4], &cnt) ) return TCL_ERROR;
+ if( bufid==0 ){
+ rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, 0, sz, cnt);
+ }else if( bufid>=1 && bufid<=2 && sz*cnt<=sizeof(azBuf[0]) ){
+ rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, azBuf[bufid], sz,cnt);
+ }else{
+ Tcl_AppendResult(interp, "illegal arguments - see documentation", (char*)0);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return TCL_OK;
+}
+
+/*
+** Usage:
+**
+** sqlite3_config_heap NBYTE NMINALLOC
+*/
+static int test_config_heap(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static char *zBuf; /* Use this memory */
+ static int szBuf; /* Bytes allocated for zBuf */
+ int nByte; /* Size of buffer to pass to sqlite3_config() */
+ int nMinAlloc; /* Size of minimum allocation */
+ int rc; /* Return code of sqlite3_config() */
+
+ Tcl_Obj * CONST *aArg = &objv[1];
+ int nArg = objc-1;
+
+ if( nArg!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NBYTE NMINALLOC");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, aArg[1], &nMinAlloc) ) return TCL_ERROR;
+
+ if( nByte==0 ){
+ free( zBuf );
+ zBuf = 0;
+ szBuf = 0;
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
+ }else{
+ zBuf = realloc(zBuf, nByte);
+ szBuf = nByte;
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc);
+ }
+
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite3_config_error [DB]
+**
+** Invoke sqlite3_config() or sqlite3_db_config() with invalid
+** opcodes and verify that they return errors.
+*/
+static int test_config_error(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db;
+ int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
+
+ if( objc!=2 && objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "[DB]");
+ return TCL_ERROR;
+ }
+ if( objc==2 ){
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ if( sqlite3_db_config(db, 99999)!=SQLITE_ERROR ){
+ Tcl_AppendResult(interp,
+ "sqlite3_db_config(db, 99999) does not return SQLITE_ERROR",
+ (char*)0);
+ return TCL_ERROR;
+ }
+ }else{
+ if( sqlite3_config(99999)!=SQLITE_ERROR ){
+ Tcl_AppendResult(interp,
+ "sqlite3_config(99999) does not return SQLITE_ERROR",
+ (char*)0);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage:
+**
+** sqlite3_dump_memsys3 FILENAME
+** sqlite3_dump_memsys5 FILENAME
+**
+** Write a summary of unfreed memsys3 allocations to FILENAME.
+*/
+static int test_dump_memsys3(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
+ return TCL_ERROR;
+ }
+
+ switch( (int)clientData ){
+ case 3: {
+#ifdef SQLITE_ENABLE_MEMSYS3
+ extern void sqlite3Memsys3Dump(const char*);
+ sqlite3Memsys3Dump(Tcl_GetString(objv[1]));
+ break;
+#endif
+ }
+ case 5: {
+#ifdef SQLITE_ENABLE_MEMSYS5
+ extern void sqlite3Memsys5Dump(const char*);
+ sqlite3Memsys5Dump(Tcl_GetString(objv[1]));
+ break;
+#endif
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_status OPCODE RESETFLAG
+**
+** Return a list of three elements which are the sqlite3_status() return
+** code, the current value, and the high-water mark value.
+*/
+static int test_status(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc, iValue, mxValue;
+ int i, op, resetFlag;
+ const char *zOpName;
+ static const struct {
+ const char *zName;
+ int op;
+ } aOp[] = {
+ { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
+ { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
+ { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
+ { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
+ { "SQLITE_STATUS_PAGECACHE_SIZE", SQLITE_STATUS_PAGECACHE_SIZE },
+ { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
+ { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
+ { "SQLITE_STATUS_SCRATCH_SIZE", SQLITE_STATUS_SCRATCH_SIZE },
+ { "SQLITE_STATUS_PARSER_STACK", SQLITE_STATUS_PARSER_STACK },
+ };
+ Tcl_Obj *pResult;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
+ return TCL_ERROR;
+ }
+ zOpName = Tcl_GetString(objv[1]);
+ for(i=0; i<ArraySize(aOp); i++){
+ if( strcmp(aOp[i].zName, zOpName)==0 ){
+ op = aOp[i].op;
+ break;
+ }
+ }
+ if( i>=ArraySize(aOp) ){
+ if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR;
+ }
+ if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR;
+ iValue = 0;
+ mxValue = 0;
+ rc = sqlite3_status(op, &iValue, &mxValue, resetFlag);
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_db_status DATABASE OPCODE RESETFLAG
+**
+** Return a list of three elements which are the sqlite3_db_status() return
+** code, the current value, and the high-water mark value.
+*/
+static int test_db_status(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc, iValue, mxValue;
+ int i, op, resetFlag;
+ const char *zOpName;
+ sqlite3 *db;
+ int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
+ static const struct {
+ const char *zName;
+ int op;
+ } aOp[] = {
+ { "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED },
+ };
+ Tcl_Obj *pResult;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zOpName = Tcl_GetString(objv[2]);
+ for(i=0; i<ArraySize(aOp); i++){
+ if( strcmp(aOp[i].zName, zOpName)==0 ){
+ op = aOp[i].op;
+ break;
+ }
+ }
+ if( i>=ArraySize(aOp) ){
+ if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR;
+ }
+ if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR;
+ iValue = 0;
+ mxValue = 0;
+ rc = sqlite3_db_status(db, op, &iValue, &mxValue, resetFlag);
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** install_malloc_faultsim BOOLEAN
+*/
+static int test_install_malloc_faultsim(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+ int isInstall;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
+ return TCL_ERROR;
+ }
+ rc = faultsimInstall(isInstall);
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest_malloc_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ int clientData;
+ } aObjCmd[] = {
+ { "sqlite3_malloc", test_malloc ,0 },
+ { "sqlite3_realloc", test_realloc ,0 },
+ { "sqlite3_free", test_free ,0 },
+ { "memset", test_memset ,0 },
+ { "memget", test_memget ,0 },
+ { "sqlite3_memory_used", test_memory_used ,0 },
+ { "sqlite3_memory_highwater", test_memory_highwater ,0 },
+ { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 },
+ { "sqlite3_memdebug_dump", test_memdebug_dump ,0 },
+ { "sqlite3_memdebug_fail", test_memdebug_fail ,0 },
+ { "sqlite3_memdebug_pending", test_memdebug_pending ,0 },
+ { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 },
+ { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 },
+ { "sqlite3_memdebug_log", test_memdebug_log ,0 },
+ { "sqlite3_config_scratch", test_config_scratch ,0 },
+ { "sqlite3_config_pagecache", test_config_pagecache ,0 },
+ { "sqlite3_status", test_status ,0 },
+ { "sqlite3_db_status", test_db_status ,0 },
+ { "install_malloc_faultsim", test_install_malloc_faultsim ,0 },
+ { "sqlite3_config_heap", test_config_heap ,0 },
+ { "sqlite3_config_memstatus", test_config_memstatus ,0 },
+ { "sqlite3_config_chunkalloc", test_config_chunkalloc ,0 },
+ { "sqlite3_config_lookaside", test_config_lookaside ,0 },
+ { "sqlite3_config_error", test_config_error ,0 },
+ { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 },
+ { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 },
+ { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ ClientData c = (ClientData)aObjCmd[i].clientData;
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
+ }
+ return TCL_OK;
+}
+#endif
diff --git a/third_party/sqlite/src/test_md5.c b/third_party/sqlite/src/test_md5.c
new file mode 100755
index 0000000..0470d9f
--- /dev/null
+++ b/third_party/sqlite/src/test_md5.c
@@ -0,0 +1,392 @@
+/*
+** SQLite uses this code for testing only. It is not a part of
+** the SQLite library. This file implements two new TCL commands
+** "md5" and "md5file" that compute md5 checksums on arbitrary text
+** and on complete files. These commands are used by the "testfixture"
+** program to help verify the correct operation of the SQLite library.
+**
+** The original use of these TCL commands was to test the ROLLBACK
+** feature of SQLite. First compute the MD5-checksum of the database.
+** Then make some changes but rollback the changes rather than commit
+** them. Compute a second MD5-checksum of the file and verify that the
+** two checksums are the same. Such is the original use of this code.
+** New uses may have been added since this comment was written.
+**
+** $Id: test_md5.c,v 1.8 2008/05/16 04:51:55 danielk1977 Exp $
+*/
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <tcl.h>
+#include <string.h>
+#include "sqlite3.h"
+
+/*
+ * If compiled on a machine that doesn't have a 32-bit integer,
+ * you just set "uint32" to the appropriate datatype for an
+ * unsigned 32-bit integer. For example:
+ *
+ * cc -Duint32='unsigned long' md5.c
+ *
+ */
+#ifndef uint32
+# define uint32 unsigned int
+#endif
+
+struct Context {
+ int isInit;
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+typedef struct Context MD5Context;
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse (unsigned char *buf, unsigned longs){
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], const uint32 in[16]){
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void MD5Init(MD5Context *ctx){
+ ctx->isInit = 1;
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static
+void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
+ struct Context *ctx = (struct Context *)pCtx;
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
+ struct Context *ctx = (struct Context *)pCtx;
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */
+}
+
+/*
+** Convert a digest into base-16. digest should be declared as
+** "unsigned char digest[16]" in the calling function. The MD5
+** digest is stored in the first 16 bytes. zBuf should
+** be "char zBuf[33]".
+*/
+static void DigestToBase16(unsigned char *digest, char *zBuf){
+ static char const zEncode[] = "0123456789abcdef";
+ int i, j;
+
+ for(j=i=0; i<16; i++){
+ int a = digest[i];
+ zBuf[j++] = zEncode[(a>>4)&0xf];
+ zBuf[j++] = zEncode[a & 0xf];
+ }
+ zBuf[j] = 0;
+}
+
+/*
+** A TCL command for md5. The argument is the text to be hashed. The
+** Result is the hash in base64.
+*/
+static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
+ MD5Context ctx;
+ unsigned char digest[16];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " TEXT\"", 0);
+ return TCL_ERROR;
+ }
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
+ MD5Final(digest, &ctx);
+ DigestToBase16(digest, interp->result);
+ return TCL_OK;
+}
+
+/*
+** A TCL command to take the md5 hash of a file. The argument is the
+** name of the file.
+*/
+static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
+ FILE *in;
+ MD5Context ctx;
+ unsigned char digest[16];
+ char zBuf[10240];
+
+ if( argc!=2 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " FILENAME\"", 0);
+ return TCL_ERROR;
+ }
+ in = fopen(argv[1],"rb");
+ if( in==0 ){
+ Tcl_AppendResult(interp,"unable to open file \"", argv[1],
+ "\" for reading", 0);
+ return TCL_ERROR;
+ }
+ MD5Init(&ctx);
+ for(;;){
+ int n;
+ n = fread(zBuf, 1, sizeof(zBuf), in);
+ if( n<=0 ) break;
+ MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
+ }
+ fclose(in);
+ MD5Final(digest, &ctx);
+ DigestToBase16(digest, interp->result);
+ return TCL_OK;
+}
+
+/*
+** Register the two TCL commands above with the TCL interpreter.
+*/
+int Md5_Init(Tcl_Interp *interp){
+ Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
+ Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
+ return TCL_OK;
+}
+
+/*
+** During testing, the special md5sum() aggregate function is available.
+** inside SQLite. The following routines implement that function.
+*/
+static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
+ MD5Context *p;
+ int i;
+ if( argc<1 ) return;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p==0 ) return;
+ if( !p->isInit ){
+ MD5Init(p);
+ }
+ for(i=0; i<argc; i++){
+ const char *zData = (char*)sqlite3_value_text(argv[i]);
+ if( zData ){
+ MD5Update(p, (unsigned char*)zData, strlen(zData));
+ }
+ }
+}
+static void md5finalize(sqlite3_context *context){
+ MD5Context *p;
+ unsigned char digest[16];
+ char zBuf[33];
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ MD5Final(digest,p);
+ DigestToBase16(digest, zBuf);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+}
+int Md5_Register(sqlite3 *db){
+ int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
+ md5step, md5finalize);
+ sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */
+ return rc;
+}
diff --git a/third_party/sqlite/src/test_mutex.c b/third_party/sqlite/src/test_mutex.c
new file mode 100755
index 0000000..571f81a
--- /dev/null
+++ b/third_party/sqlite/src/test_mutex.c
@@ -0,0 +1,387 @@
+/*
+** 2008 June 18
+**
+** 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.
+**
+*************************************************************************
+**
+** $Id: test_mutex.c,v 1.11 2008/07/19 13:43:24 danielk1977 Exp $
+*/
+
+#include "tcl.h"
+#include "sqlite3.h"
+#include "sqliteInt.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+/* defined in test1.c */
+const char *sqlite3TestErrorName(int);
+
+/* A countable mutex */
+struct sqlite3_mutex {
+ sqlite3_mutex *pReal;
+ int eType;
+};
+
+/* State variables */
+static struct test_mutex_globals {
+ int isInstalled; /* True if installed */
+ int disableInit; /* True to cause sqlite3_initalize() to fail */
+ int disableTry; /* True to force sqlite3_mutex_try() to fail */
+ int isInit; /* True if initialized */
+ sqlite3_mutex_methods m; /* Interface to "real" mutex system */
+ int aCounter[8]; /* Number of grabs of each type of mutex */
+ sqlite3_mutex aStatic[6]; /* The six static mutexes */
+} g;
+
+/* Return true if the countable mutex is currently held */
+static int counterMutexHeld(sqlite3_mutex *p){
+ return g.m.xMutexHeld(p->pReal);
+}
+
+/* Return true if the countable mutex is not currently held */
+static int counterMutexNotheld(sqlite3_mutex *p){
+ return g.m.xMutexNotheld(p->pReal);
+}
+
+/* Initialize the countable mutex interface
+** Or, if g.disableInit is non-zero, then do not initialize but instead
+** return the value of g.disableInit as the result code. This can be used
+** to simulate an initialization failure.
+*/
+static int counterMutexInit(void){
+ int rc;
+ if( g.disableInit ) return g.disableInit;
+ rc = g.m.xMutexInit();
+ g.isInit = 1;
+ return rc;
+}
+
+/*
+** Uninitialize the mutex subsystem
+*/
+static int counterMutexEnd(void){
+ g.isInit = 0;
+ return g.m.xMutexEnd();
+}
+
+/*
+** Allocate a countable mutex
+*/
+static sqlite3_mutex *counterMutexAlloc(int eType){
+ sqlite3_mutex *pReal;
+ sqlite3_mutex *pRet = 0;
+
+ assert( g.isInit );
+ assert(eType<8 && eType>=0);
+
+ pReal = g.m.xMutexAlloc(eType);
+ if( !pReal ) return 0;
+
+ if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){
+ pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex));
+ }else{
+ pRet = &g.aStatic[eType-2];
+ }
+
+ pRet->eType = eType;
+ pRet->pReal = pReal;
+ return pRet;
+}
+
+/*
+** Free a countable mutex
+*/
+static void counterMutexFree(sqlite3_mutex *p){
+ assert( g.isInit );
+ g.m.xMutexFree(p->pReal);
+ if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){
+ free(p);
+ }
+}
+
+/*
+** Enter a countable mutex. Block until entry is safe.
+*/
+static void counterMutexEnter(sqlite3_mutex *p){
+ assert( g.isInit );
+ g.aCounter[p->eType]++;
+ g.m.xMutexEnter(p->pReal);
+}
+
+/*
+** Try to enter a mutex. Return true on success.
+*/
+static int counterMutexTry(sqlite3_mutex *p){
+ assert( g.isInit );
+ g.aCounter[p->eType]++;
+ if( g.disableTry ) return SQLITE_BUSY;
+ return g.m.xMutexTry(p->pReal);
+}
+
+/* Leave a mutex
+*/
+static void counterMutexLeave(sqlite3_mutex *p){
+ assert( g.isInit );
+ g.m.xMutexLeave(p->pReal);
+}
+
+/*
+** sqlite3_shutdown
+*/
+static int test_shutdown(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ rc = sqlite3_shutdown();
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+** sqlite3_initialize
+*/
+static int test_initialize(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ rc = sqlite3_initialize();
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+** install_mutex_counters BOOLEAN
+*/
+static int test_install_mutex_counters(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = SQLITE_OK;
+ int isInstall;
+
+ sqlite3_mutex_methods counter_methods = {
+ counterMutexInit,
+ counterMutexEnd,
+ counterMutexAlloc,
+ counterMutexFree,
+ counterMutexEnter,
+ counterMutexTry,
+ counterMutexLeave,
+ counterMutexHeld,
+ counterMutexNotheld
+ };
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
+ return TCL_ERROR;
+ }
+
+ assert(isInstall==0 || isInstall==1);
+ assert(g.isInstalled==0 || g.isInstalled==1);
+ if( isInstall==g.isInstalled ){
+ Tcl_AppendResult(interp, "mutex counters are ", 0);
+ Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0);
+ return TCL_ERROR;
+ }
+
+ if( isInstall ){
+ assert( g.m.xMutexAlloc==0 );
+ rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m);
+ if( rc==SQLITE_OK ){
+ sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods);
+ }
+ g.disableTry = 0;
+ }else{
+ assert( g.m.xMutexAlloc );
+ rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m);
+ memset(&g.m, 0, sizeof(sqlite3_mutex_methods));
+ }
+
+ if( rc==SQLITE_OK ){
+ g.isInstalled = isInstall;
+ }
+
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+** read_mutex_counters
+*/
+static int test_read_mutex_counters(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_Obj *pRet;
+ int ii;
+ char *aName[8] = {
+ "fast", "recursive", "static_master", "static_mem",
+ "static_mem2", "static_prng", "static_lru", "static_lru2"
+ };
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ pRet = Tcl_NewObj();
+ Tcl_IncrRefCount(pRet);
+ for(ii=0; ii<8; ii++){
+ Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1));
+ Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii]));
+ }
+ Tcl_SetObjResult(interp, pRet);
+ Tcl_DecrRefCount(pRet);
+
+ return TCL_OK;
+}
+
+/*
+** clear_mutex_counters
+*/
+static int test_clear_mutex_counters(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int ii;
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ for(ii=0; ii<8; ii++){
+ g.aCounter[ii] = 0;
+ }
+ return TCL_OK;
+}
+
+/*
+** Create and free a mutex. Return the mutex pointer. The pointer
+** will be invalid since the mutex has already been freed. The
+** return pointer just checks to see if the mutex really was allocated.
+*/
+static int test_alloc_mutex(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ char zBuf[100];
+ sqlite3_mutex_free(p);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p);
+ Tcl_AppendResult(interp, zBuf, (char*)0);
+#endif
+ return TCL_OK;
+}
+
+/*
+** sqlite3_config OPTION
+**
+** OPTION can be either one of the keywords:
+**
+** SQLITE_CONFIG_SINGLETHREAD
+** SQLITE_CONFIG_MULTITHREAD
+** SQLITE_CONFIG_SERIALIZED
+**
+** Or OPTION can be an raw integer.
+*/
+static int test_config(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ struct ConfigOption {
+ const char *zName;
+ int iValue;
+ } aOpt[] = {
+ {"singlethread", SQLITE_CONFIG_SINGLETHREAD},
+ {"multithread", SQLITE_CONFIG_MULTITHREAD},
+ {"serialized", SQLITE_CONFIG_SERIALIZED},
+ {0, 0}
+ };
+ int s = sizeof(struct ConfigOption);
+ int i;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){
+ if( Tcl_GetIntFromObj(interp, objv[1], &i) ){
+ return TCL_ERROR;
+ }
+ }else{
+ i = aOpt[i].iValue;
+ }
+
+ rc = sqlite3_config(i);
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+int Sqlitetest_mutex_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aCmd[] = {
+ { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown },
+ { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize },
+ { "sqlite3_config", (Tcl_ObjCmdProc*)test_config },
+
+ { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex },
+ { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters },
+ { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters },
+ { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters },
+ };
+ int i;
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+ memset(&g, 0, sizeof(g));
+
+ Tcl_LinkVar(interp, "disable_mutex_init",
+ (char*)&g.disableInit, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "disable_mutex_try",
+ (char*)&g.disableTry, TCL_LINK_INT);
+ return SQLITE_OK;
+}
diff --git a/third_party/sqlite/src/test_onefile.c b/third_party/sqlite/src/test_onefile.c
new file mode 100755
index 0000000..c8cc43c
--- /dev/null
+++ b/third_party/sqlite/src/test_onefile.c
@@ -0,0 +1,822 @@
+/*
+** 2007 September 14
+**
+** 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.
+**
+*************************************************************************
+**
+** $Id: test_onefile.c,v 1.9 2008/06/26 10:54:12 danielk1977 Exp $
+**
+** OVERVIEW:
+**
+** This file contains some example code demonstrating how the SQLite
+** vfs feature can be used to have SQLite operate directly on an
+** embedded media, without using an intermediate file system.
+**
+** Because this is only a demo designed to run on a workstation, the
+** underlying media is simulated using a regular file-system file. The
+** size of the file is fixed when it is first created (default size 10 MB).
+** From SQLite's point of view, this space is used to store a single
+** database file and the journal file.
+**
+** Any statement journal created is stored in volatile memory obtained
+** from sqlite3_malloc(). Any attempt to create a temporary database file
+** will fail (SQLITE_IOERR). To prevent SQLite from attempting this,
+** it should be configured to store all temporary database files in
+** main memory (see pragma "temp_store" or the SQLITE_TEMP_STORE compile
+** time option).
+**
+** ASSUMPTIONS:
+**
+** After it has been created, the blob file is accessed using the
+** following three functions only:
+**
+** mediaRead(); - Read a 512 byte block from the file.
+** mediaWrite(); - Write a 512 byte block to the file.
+** mediaSync(); - Tell the media hardware to sync.
+**
+** It is assumed that these can be easily implemented by any "real"
+** media vfs driver adapting this code.
+**
+** FILE FORMAT:
+**
+** The basic principle is that the "database file" is stored at the
+** beginning of the 10 MB blob and grows in a forward direction. The
+** "journal file" is stored at the end of the 10MB blob and grows
+** in the reverse direction. If, during a transaction, insufficient
+** space is available to expand either the journal or database file,
+** an SQLITE_FULL error is returned. The database file is never allowed
+** to consume more than 90% of the blob space. If SQLite tries to
+** create a file larger than this, SQLITE_FULL is returned.
+**
+** No allowance is made for "wear-leveling", as is required by.
+** embedded devices in the absence of equivalent hardware features.
+**
+** The first 512 block byte of the file is reserved for storing the
+** size of the "database file". It is updated as part of the sync()
+** operation. On startup, it can only be trusted if no journal file
+** exists. If a journal-file does exist, then it stores the real size
+** of the database region. The second and subsequent blocks store the
+** actual database content.
+**
+** The size of the "journal file" is not stored persistently in the
+** file. When the system is running, the size of the journal file is
+** stored in volatile memory. When recovering from a crash, this vfs
+** reports a very large size for the journal file. The normal journal
+** header and checksum mechanisms serve to prevent SQLite from
+** processing any data that lies past the logical end of the journal.
+**
+** When SQLite calls OsDelete() to delete the journal file, the final
+** 512 bytes of the blob (the area containing the first journal header)
+** are zeroed.
+**
+** LOCKING:
+**
+** File locking is a no-op. Only one connection may be open at any one
+** time using this demo vfs.
+*/
+
+#include "sqlite3.h"
+#include <assert.h>
+#include <string.h>
+
+/*
+** Maximum pathname length supported by the fs backend.
+*/
+#define BLOCKSIZE 512
+#define BLOBSIZE 10485760
+
+/*
+** Name used to identify this VFS.
+*/
+#define FS_VFS_NAME "fs"
+
+typedef struct fs_real_file fs_real_file;
+struct fs_real_file {
+ sqlite3_file *pFile;
+ const char *zName;
+ int nDatabase; /* Current size of database region */
+ int nJournal; /* Current size of journal region */
+ int nBlob; /* Total size of allocated blob */
+ int nRef; /* Number of pointers to this structure */
+ fs_real_file *pNext;
+ fs_real_file **ppThis;
+};
+
+typedef struct fs_file fs_file;
+struct fs_file {
+ sqlite3_file base;
+ int eType;
+ fs_real_file *pReal;
+};
+
+typedef struct tmp_file tmp_file;
+struct tmp_file {
+ sqlite3_file base;
+ int nSize;
+ int nAlloc;
+ char *zAlloc;
+};
+
+/* Values for fs_file.eType. */
+#define DATABASE_FILE 1
+#define JOURNAL_FILE 2
+
+/*
+** Method declarations for fs_file.
+*/
+static int fsClose(sqlite3_file*);
+static int fsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int fsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+static int fsTruncate(sqlite3_file*, sqlite3_int64 size);
+static int fsSync(sqlite3_file*, int flags);
+static int fsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int fsLock(sqlite3_file*, int);
+static int fsUnlock(sqlite3_file*, int);
+static int fsCheckReservedLock(sqlite3_file*, int *pResOut);
+static int fsFileControl(sqlite3_file*, int op, void *pArg);
+static int fsSectorSize(sqlite3_file*);
+static int fsDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for tmp_file.
+*/
+static int tmpClose(sqlite3_file*);
+static int tmpRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int tmpWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+static int tmpTruncate(sqlite3_file*, sqlite3_int64 size);
+static int tmpSync(sqlite3_file*, int flags);
+static int tmpFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int tmpLock(sqlite3_file*, int);
+static int tmpUnlock(sqlite3_file*, int);
+static int tmpCheckReservedLock(sqlite3_file*, int *pResOut);
+static int tmpFileControl(sqlite3_file*, int op, void *pArg);
+static int tmpSectorSize(sqlite3_file*);
+static int tmpDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for fs_vfs.
+*/
+static int fsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int fsDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int fsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int fsFullPathname(sqlite3_vfs*, const char *zName, int nOut,char *zOut);
+static void *fsDlOpen(sqlite3_vfs*, const char *zFilename);
+static void fsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void *fsDlSym(sqlite3_vfs*,void*, const char *zSymbol);
+static void fsDlClose(sqlite3_vfs*, void*);
+static int fsRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int fsSleep(sqlite3_vfs*, int microseconds);
+static int fsCurrentTime(sqlite3_vfs*, double*);
+
+
+typedef struct fs_vfs_t fs_vfs_t;
+struct fs_vfs_t {
+ sqlite3_vfs base;
+ fs_real_file *pFileList;
+ sqlite3_vfs *pParent;
+};
+
+static fs_vfs_t fs_vfs = {
+ {
+ 1, /* iVersion */
+ 0, /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ FS_VFS_NAME, /* zName */
+ 0, /* pAppData */
+ fsOpen, /* xOpen */
+ fsDelete, /* xDelete */
+ fsAccess, /* xAccess */
+ fsFullPathname, /* xFullPathname */
+ fsDlOpen, /* xDlOpen */
+ fsDlError, /* xDlError */
+ fsDlSym, /* xDlSym */
+ fsDlClose, /* xDlClose */
+ fsRandomness, /* xRandomness */
+ fsSleep, /* xSleep */
+ fsCurrentTime /* xCurrentTime */
+ },
+ 0, /* pFileList */
+ 0 /* pParent */
+};
+
+static sqlite3_io_methods fs_io_methods = {
+ 1, /* iVersion */
+ fsClose, /* xClose */
+ fsRead, /* xRead */
+ fsWrite, /* xWrite */
+ fsTruncate, /* xTruncate */
+ fsSync, /* xSync */
+ fsFileSize, /* xFileSize */
+ fsLock, /* xLock */
+ fsUnlock, /* xUnlock */
+ fsCheckReservedLock, /* xCheckReservedLock */
+ fsFileControl, /* xFileControl */
+ fsSectorSize, /* xSectorSize */
+ fsDeviceCharacteristics /* xDeviceCharacteristics */
+};
+
+
+static sqlite3_io_methods tmp_io_methods = {
+ 1, /* iVersion */
+ tmpClose, /* xClose */
+ tmpRead, /* xRead */
+ tmpWrite, /* xWrite */
+ tmpTruncate, /* xTruncate */
+ tmpSync, /* xSync */
+ tmpFileSize, /* xFileSize */
+ tmpLock, /* xLock */
+ tmpUnlock, /* xUnlock */
+ tmpCheckReservedLock, /* xCheckReservedLock */
+ tmpFileControl, /* xFileControl */
+ tmpSectorSize, /* xSectorSize */
+ tmpDeviceCharacteristics /* xDeviceCharacteristics */
+};
+
+/* Useful macros used in several places */
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+
+/*
+** Close a tmp-file.
+*/
+static int tmpClose(sqlite3_file *pFile){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ sqlite3_free(pTmp->zAlloc);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a tmp-file.
+*/
+static int tmpRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ if( (iAmt+iOfst)>pTmp->nSize ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ memcpy(zBuf, &pTmp->zAlloc[iOfst], iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write data to a tmp-file.
+*/
+static int tmpWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ if( (iAmt+iOfst)>pTmp->nAlloc ){
+ int nNew = 2*(iAmt+iOfst+pTmp->nAlloc);
+ char *zNew = sqlite3_realloc(pTmp->zAlloc, nNew);
+ if( !zNew ){
+ return SQLITE_NOMEM;
+ }
+ pTmp->zAlloc = zNew;
+ pTmp->nAlloc = nNew;
+ }
+ memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt);
+ pTmp->nSize = MAX(pTmp->nSize, iOfst+iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Truncate a tmp-file.
+*/
+static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ pTmp->nSize = MIN(pTmp->nSize, size);
+ return SQLITE_OK;
+}
+
+/*
+** Sync a tmp-file.
+*/
+static int tmpSync(sqlite3_file *pFile, int flags){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current file-size of a tmp-file.
+*/
+static int tmpFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ tmp_file *pTmp = (tmp_file *)pFile;
+ *pSize = pTmp->nSize;
+ return SQLITE_OK;
+}
+
+/*
+** Lock a tmp-file.
+*/
+static int tmpLock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Unlock a tmp-file.
+*/
+static int tmpUnlock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on a tmp-file.
+*/
+static int tmpCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on a tmp-file.
+*/
+static int tmpFileControl(sqlite3_file *pFile, int op, void *pArg){
+ return SQLITE_OK;
+}
+
+/*
+** Return the sector-size in bytes for a tmp-file.
+*/
+static int tmpSectorSize(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Return the device characteristic flags supported by a tmp-file.
+*/
+static int tmpDeviceCharacteristics(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Close an fs-file.
+*/
+static int fsClose(sqlite3_file *pFile){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+
+ /* Decrement the real_file ref-count. */
+ pReal->nRef--;
+ assert(pReal->nRef>=0);
+
+ /* When the ref-count reaches 0, destroy the structure */
+ if( pReal->nRef==0 ){
+ *pReal->ppThis = pReal->pNext;
+ if( pReal->pNext ){
+ pReal->pNext->ppThis = pReal->ppThis;
+ }
+ rc = pReal->pFile->pMethods->xClose(pReal->pFile);
+ sqlite3_free(pReal);
+ }
+
+ return rc;
+}
+
+/*
+** Read data from an fs-file.
+*/
+static int fsRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pF = pReal->pFile;
+
+ if( (p->eType==DATABASE_FILE && (iAmt+iOfst)>pReal->nDatabase)
+ || (p->eType==JOURNAL_FILE && (iAmt+iOfst)>pReal->nJournal)
+ ){
+ rc = SQLITE_IOERR_SHORT_READ;
+ }else if( p->eType==DATABASE_FILE ){
+ rc = pF->pMethods->xRead(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
+ }else{
+ /* Journal file. */
+ int iRem = iAmt;
+ int iBuf = 0;
+ int ii = iOfst;
+ while( iRem>0 && rc==SQLITE_OK ){
+ int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
+ int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
+
+ rc = pF->pMethods->xRead(pF, &((char *)zBuf)[iBuf], iRealAmt, iRealOff);
+ ii += iRealAmt;
+ iBuf += iRealAmt;
+ iRem -= iRealAmt;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Write data to an fs-file.
+*/
+static int fsWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc = SQLITE_OK;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pF = pReal->pFile;
+
+ if( p->eType==DATABASE_FILE ){
+ if( (iAmt+iOfst+BLOCKSIZE)>(pReal->nBlob-pReal->nJournal) ){
+ rc = SQLITE_FULL;
+ }else{
+ rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
+ if( rc==SQLITE_OK ){
+ pReal->nDatabase = MAX(pReal->nDatabase, iAmt+iOfst);
+ }
+ }
+ }else{
+ /* Journal file. */
+ int iRem = iAmt;
+ int iBuf = 0;
+ int ii = iOfst;
+ while( iRem>0 && rc==SQLITE_OK ){
+ int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
+ int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
+
+ if( iRealOff<(pReal->nDatabase+BLOCKSIZE) ){
+ rc = SQLITE_FULL;
+ }else{
+ rc = pF->pMethods->xWrite(pF, &((char *)zBuf)[iBuf], iRealAmt,iRealOff);
+ ii += iRealAmt;
+ iBuf += iRealAmt;
+ iRem -= iRealAmt;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pReal->nJournal = MAX(pReal->nJournal, iAmt+iOfst);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Truncate an fs-file.
+*/
+static int fsTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ if( p->eType==DATABASE_FILE ){
+ pReal->nDatabase = MIN(pReal->nDatabase, size);
+ }else{
+ pReal->nJournal = MIN(pReal->nJournal, size);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Sync an fs-file.
+*/
+static int fsSync(sqlite3_file *pFile, int flags){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ sqlite3_file *pRealFile = pReal->pFile;
+ int rc = SQLITE_OK;
+
+ if( p->eType==DATABASE_FILE ){
+ unsigned char zSize[4];
+ zSize[0] = (pReal->nDatabase&0xFF000000)>>24;
+ zSize[1] = (pReal->nDatabase&0x00FF0000)>>16;
+ zSize[2] = (pReal->nDatabase&0x0000FF00)>>8;
+ zSize[3] = (pReal->nDatabase&0x000000FF);
+ rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pRealFile->pMethods->xSync(pRealFile, flags&(~SQLITE_SYNC_DATAONLY));
+ }
+
+ return rc;
+}
+
+/*
+** Return the current file-size of an fs-file.
+*/
+static int fsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = p->pReal;
+ if( p->eType==DATABASE_FILE ){
+ *pSize = pReal->nDatabase;
+ }else{
+ *pSize = pReal->nJournal;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Lock an fs-file.
+*/
+static int fsLock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Unlock an fs-file.
+*/
+static int fsUnlock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an fs-file.
+*/
+static int fsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on an fs-file.
+*/
+static int fsFileControl(sqlite3_file *pFile, int op, void *pArg){
+ return SQLITE_OK;
+}
+
+/*
+** Return the sector-size in bytes for an fs-file.
+*/
+static int fsSectorSize(sqlite3_file *pFile){
+ return BLOCKSIZE;
+}
+
+/*
+** Return the device characteristic flags supported by an fs-file.
+*/
+static int fsDeviceCharacteristics(sqlite3_file *pFile){
+ return 0;
+}
+
+/*
+** Open an fs file handle.
+*/
+static int fsOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_file *p = (fs_file *)pFile;
+ fs_real_file *pReal = 0;
+ int eType;
+ int nName;
+ int rc = SQLITE_OK;
+
+ if( 0==(flags&(SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL)) ){
+ tmp_file *p = (tmp_file *)pFile;
+ memset(p, 0, sizeof(*p));
+ p->base.pMethods = &tmp_io_methods;
+ return SQLITE_OK;
+ }
+
+ eType = ((flags&(SQLITE_OPEN_MAIN_DB))?DATABASE_FILE:JOURNAL_FILE);
+ p->base.pMethods = &fs_io_methods;
+ p->eType = eType;
+
+ assert(strlen("-journal")==8);
+ nName = strlen(zName)-((eType==JOURNAL_FILE)?8:0);
+ pReal=pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext);
+
+ if( !pReal ){
+ sqlite3_int64 size;
+ sqlite3_file *pRealFile;
+ sqlite3_vfs *pParent = pFsVfs->pParent;
+ assert(eType==DATABASE_FILE);
+
+ pReal = (fs_real_file *)sqlite3_malloc(sizeof(*pReal)+pParent->szOsFile);
+ if( !pReal ){
+ rc = SQLITE_NOMEM;
+ goto open_out;
+ }
+ memset(pReal, 0, sizeof(*pReal)+pParent->szOsFile);
+ pReal->zName = zName;
+ pReal->pFile = (sqlite3_file *)(&pReal[1]);
+
+ rc = pParent->xOpen(pParent, zName, pReal->pFile, flags, pOutFlags);
+ if( rc!=SQLITE_OK ){
+ goto open_out;
+ }
+ pRealFile = pReal->pFile;
+
+ rc = pRealFile->pMethods->xFileSize(pRealFile, &size);
+ if( rc!=SQLITE_OK ){
+ goto open_out;
+ }
+ if( size==0 ){
+ rc = pRealFile->pMethods->xWrite(pRealFile, "\0", 1, BLOBSIZE-1);
+ pReal->nBlob = BLOBSIZE;
+ }else{
+ unsigned char zS[4];
+ pReal->nBlob = size;
+ rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0);
+ pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3];
+ if( rc==SQLITE_OK ){
+ rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, pReal->nBlob-4);
+ if( zS[0] || zS[1] || zS[2] || zS[3] ){
+ pReal->nJournal = pReal->nBlob;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pReal->pNext = pFsVfs->pFileList;
+ if( pReal->pNext ){
+ pReal->pNext->ppThis = &pReal->pNext;
+ }
+ pReal->ppThis = &pFsVfs->pFileList;
+ pFsVfs->pFileList = pReal;
+ }
+ }
+
+open_out:
+ if( pReal ){
+ if( rc==SQLITE_OK ){
+ p->pReal = pReal;
+ pReal->nRef++;
+ }else{
+ if( pReal->pFile->pMethods ){
+ pReal->pFile->pMethods->xClose(pReal->pFile);
+ }
+ sqlite3_free(pReal);
+ }
+ }
+ return rc;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int fsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc = SQLITE_OK;
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_real_file *pReal;
+ sqlite3_file *pF;
+ int nName = strlen(zPath) - 8;
+
+ assert(strlen("-journal")==8);
+ assert(strcmp("-journal", &zPath[nName])==0);
+
+ pReal = pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
+ if( pReal ){
+ pF = pReal->pFile;
+ rc = pF->pMethods->xWrite(pF, "\0\0\0\0", 4, pReal->nBlob-BLOCKSIZE);
+ if( rc==SQLITE_OK ){
+ pReal->nJournal = 0;
+ }
+ }
+ return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int fsAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
+ fs_real_file *pReal;
+ int isJournal = 0;
+ int nName = strlen(zPath);
+
+ if( flags!=SQLITE_ACCESS_EXISTS ){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xAccess(pParent, zPath, flags, pResOut);
+ }
+
+ assert(strlen("-journal")==8);
+ if( nName>8 && strcmp("-journal", &zPath[nName-8])==0 ){
+ nName -= 8;
+ isJournal = 1;
+ }
+
+ pReal = pFsVfs->pFileList;
+ for(; pReal && strncmp(pReal->zName, zPath, nName); pReal=pReal->pNext);
+
+ *pResOut = (pReal && (!isJournal || pReal->nJournal>0));
+ return SQLITE_OK;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (FS_MAX_PATHNAME+1) bytes.
+*/
+static int fsFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zPath, /* Possibly relative input path */
+ int nOut, /* Size of output buffer in bytes */
+ char *zOut /* Output buffer */
+){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xFullPathname(pParent, zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *fsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xDlOpen(pParent, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void fsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ pParent->xDlError(pParent, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void *fsDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xDlSym(pParent, pHandle, zSymbol);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void fsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ pParent->xDlClose(pParent, pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int fsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xRandomness(pParent, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int fsSleep(sqlite3_vfs *pVfs, int nMicro){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xSleep(pParent, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int fsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
+ return pParent->xCurrentTime(pParent, pTimeOut);
+}
+
+/*
+** This procedure registers the fs vfs with SQLite. If the argument is
+** true, the fs vfs becomes the new default vfs. It is the only publicly
+** available function in this file.
+*/
+int fs_register(){
+ if( fs_vfs.pParent ) return SQLITE_OK;
+ fs_vfs.pParent = sqlite3_vfs_find(0);
+ fs_vfs.base.mxPathname = fs_vfs.pParent->mxPathname;
+ fs_vfs.base.szOsFile = MAX(sizeof(tmp_file), sizeof(fs_file));
+ return sqlite3_vfs_register(&fs_vfs.base, 0);
+}
+
+#ifdef SQLITE_TEST
+ int SqlitetestOnefile_Init() {return fs_register();}
+#endif
diff --git a/third_party/sqlite/src/test_osinst.c b/third_party/sqlite/src/test_osinst.c
new file mode 100755
index 0000000..a05ef7a
--- /dev/null
+++ b/third_party/sqlite/src/test_osinst.c
@@ -0,0 +1,1069 @@
+/*
+** 2008 April 10
+**
+** 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 contains the implementation of an SQLite vfs wrapper that
+** adds instrumentation to all vfs and file methods. C and Tcl interfaces
+** are provided to control the instrumentation.
+**
+** $Id: test_osinst.c,v 1.18 2008/07/25 13:32:45 drh Exp $
+*/
+
+#ifdef SQLITE_ENABLE_INSTVFS
+/*
+** C interface:
+**
+** sqlite3_instvfs_create()
+** sqlite3_instvfs_destroy()
+** sqlite3_instvfs_configure()
+**
+** sqlite3_instvfs_reset()
+** sqlite3_instvfs_get()
+**
+** sqlite3_instvfs_binarylog
+** sqlite3_instvfs_binarylog_marker
+**
+** Tcl interface (omitted if SQLITE_TEST is not set):
+**
+** sqlite3_instvfs create NAME ?PARENT?
+**
+** Create and register new vfs called $NAME, which is a wrapper around
+** the existing vfs $PARENT. If the PARENT argument is omitted, the
+** new vfs is a wrapper around the current default vfs.
+**
+** sqlite3_instvfs destroy NAME
+**
+** Deregister and destroy the vfs named $NAME, which must have been
+** created by an earlier invocation of [sqlite3_instvfs create].
+**
+** sqlite3_instvfs configure NAME SCRIPT
+**
+** Configure the callback script for the vfs $NAME, which much have
+** been created by an earlier invocation of [sqlite3_instvfs create].
+** After a callback script has been configured, it is invoked each
+** time a vfs or file method is called by SQLite. Before invoking
+** the callback script, five arguments are appended to it:
+**
+** * The name of the invoked method - i.e. "xRead".
+**
+** * The time consumed by the method call as measured by
+** sqlite3Hwtime() (an integer value)
+**
+** * A string value with a different meaning for different calls.
+** For file methods, the name of the file being operated on. For
+** other methods it is the filename argument, if any.
+**
+** * A 32-bit integer value with a call-specific meaning.
+**
+** * A 64-bit integer value. For xRead() and xWrite() calls this
+** is the file offset being written to or read from. Unused by
+** all other calls.
+**
+** sqlite3_instvfs reset NAME
+**
+** Zero the internal event counters associated with vfs $NAME,
+** which must have been created by an earlier invocation of
+** [sqlite3_instvfs create].
+**
+** sqlite3_instvfs report NAME
+**
+** Return the values of the internal event counters associated
+** with vfs $NAME. The report format is a list with one element
+** for each method call (xWrite, xRead etc.). Each element is
+** itself a list with three elements:
+**
+** * The name of the method call - i.e. "xWrite",
+** * The total number of calls to the method (an integer).
+** * The aggregate time consumed by all calls to the method as
+** measured by sqlite3Hwtime() (an integer).
+*/
+
+#include "sqlite3.h"
+#include <string.h>
+#include <assert.h>
+
+/*
+** Maximum pathname length supported by the inst backend.
+*/
+#define INST_MAX_PATHNAME 512
+
+
+/* File methods */
+/* Vfs methods */
+#define OS_ACCESS 1
+#define OS_CHECKRESERVEDLOCK 2
+#define OS_CLOSE 3
+#define OS_CURRENTTIME 4
+#define OS_DELETE 5
+#define OS_DEVCHAR 6
+#define OS_FILECONTROL 7
+#define OS_FILESIZE 8
+#define OS_FULLPATHNAME 9
+#define OS_LOCK 11
+#define OS_OPEN 12
+#define OS_RANDOMNESS 13
+#define OS_READ 14
+#define OS_SECTORSIZE 15
+#define OS_SLEEP 16
+#define OS_SYNC 17
+#define OS_TRUNCATE 18
+#define OS_UNLOCK 19
+#define OS_WRITE 20
+
+#define OS_NUMEVENTS 21
+
+#define BINARYLOG_STRING 30
+#define BINARYLOG_MARKER 31
+
+#define BINARYLOG_PREPARE_V2 64
+#define BINARYLOG_STEP 65
+#define BINARYLOG_FINALIZE 66
+
+struct InstVfs {
+ sqlite3_vfs base;
+ sqlite3_vfs *pVfs;
+
+ void *pClient;
+ void (*xDel)(void *);
+ void (*xCall)(void *, int, int, sqlite3_int64, int, const char *, int, int, sqlite3_int64);
+
+ /* Counters */
+ sqlite3_int64 aTime[OS_NUMEVENTS];
+ int aCount[OS_NUMEVENTS];
+
+ int iNextFileId;
+};
+typedef struct InstVfs InstVfs;
+
+#define REALVFS(p) (((InstVfs *)(p))->pVfs)
+
+typedef struct inst_file inst_file;
+struct inst_file {
+ sqlite3_file base;
+ sqlite3_file *pReal;
+ InstVfs *pInstVfs;
+ const char *zName;
+ int iFileId; /* File id number */
+ int flags;
+};
+
+/*
+** Method declarations for inst_file.
+*/
+static int instClose(sqlite3_file*);
+static int instRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int instWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int instTruncate(sqlite3_file*, sqlite3_int64 size);
+static int instSync(sqlite3_file*, int flags);
+static int instFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int instLock(sqlite3_file*, int);
+static int instUnlock(sqlite3_file*, int);
+static int instCheckReservedLock(sqlite3_file*, int *pResOut);
+static int instFileControl(sqlite3_file*, int op, void *pArg);
+static int instSectorSize(sqlite3_file*);
+static int instDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for inst_vfs.
+*/
+static int instOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int instDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int instAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int instFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *instDlOpen(sqlite3_vfs*, const char *zFilename);
+static void instDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void *instDlSym(sqlite3_vfs*,void*, const char *zSymbol);
+static void instDlClose(sqlite3_vfs*, void*);
+static int instRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int instSleep(sqlite3_vfs*, int microseconds);
+static int instCurrentTime(sqlite3_vfs*, double*);
+
+static void binarylog_blob(sqlite3_vfs *, const char *, int, int);
+
+static sqlite3_vfs inst_vfs = {
+ 1, /* iVersion */
+ sizeof(inst_file), /* szOsFile */
+ INST_MAX_PATHNAME, /* mxPathname */
+ 0, /* pNext */
+ 0, /* zName */
+ 0, /* pAppData */
+ instOpen, /* xOpen */
+ instDelete, /* xDelete */
+ instAccess, /* xAccess */
+ instFullPathname, /* xFullPathname */
+ instDlOpen, /* xDlOpen */
+ instDlError, /* xDlError */
+ instDlSym, /* xDlSym */
+ instDlClose, /* xDlClose */
+ instRandomness, /* xRandomness */
+ instSleep, /* xSleep */
+ instCurrentTime /* xCurrentTime */
+};
+
+static sqlite3_io_methods inst_io_methods = {
+ 1, /* iVersion */
+ instClose, /* xClose */
+ instRead, /* xRead */
+ instWrite, /* xWrite */
+ instTruncate, /* xTruncate */
+ instSync, /* xSync */
+ instFileSize, /* xFileSize */
+ instLock, /* xLock */
+ instUnlock, /* xUnlock */
+ instCheckReservedLock, /* xCheckReservedLock */
+ instFileControl, /* xFileControl */
+ instSectorSize, /* xSectorSize */
+ instDeviceCharacteristics /* xDeviceCharacteristics */
+};
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+#include "hwtime.h"
+
+#define OS_TIME_IO(eEvent, A, B, Call) { \
+ inst_file *p = (inst_file *)pFile; \
+ InstVfs *pInstVfs = p->pInstVfs; \
+ int rc; \
+ sqlite_uint64 t = sqlite3Hwtime(); \
+ rc = Call; \
+ t = sqlite3Hwtime() - t; \
+ pInstVfs->aTime[eEvent] += t; \
+ pInstVfs->aCount[eEvent] += 1; \
+ if( pInstVfs->xCall ){ \
+ pInstVfs->xCall( \
+ pInstVfs->pClient,eEvent,p->iFileId,t,rc,p->zName,p->flags,A,B \
+ ); \
+ } \
+ return rc; \
+}
+
+#define OS_TIME_VFS(eEvent, Z, flags, A, B, Call) { \
+ InstVfs *pInstVfs = (InstVfs *)pVfs; \
+ int rc; \
+ sqlite_uint64 t = sqlite3Hwtime(); \
+ rc = Call; \
+ t = sqlite3Hwtime() - t; \
+ pInstVfs->aTime[eEvent] += t; \
+ pInstVfs->aCount[eEvent] += 1; \
+ if( pInstVfs->xCall ){ \
+ pInstVfs->xCall(pInstVfs->pClient,eEvent,0, t, rc, Z, flags, A, B); \
+ } \
+ return rc; \
+}
+
+/*
+** Close an inst-file.
+*/
+static int instClose(sqlite3_file *pFile){
+ OS_TIME_IO(OS_CLOSE, 0, 0,
+ (p->pReal->pMethods ? p->pReal->pMethods->xClose(p->pReal) : SQLITE_OK)
+ );
+}
+
+/*
+** Read data from an inst-file.
+*/
+static int instRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs);
+ OS_TIME_IO(OS_READ, iAmt, (binarylog_blob(pVfs, zBuf, iAmt, 1), iOfst),
+ p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst)
+ );
+}
+
+/*
+** Write data to an inst-file.
+*/
+static int instWrite(
+ sqlite3_file *pFile,
+ const void *z,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs);
+ binarylog_blob(pVfs, z, iAmt, 1);
+ OS_TIME_IO(OS_WRITE, iAmt, iOfst,
+ p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst)
+ );
+}
+
+/*
+** Truncate an inst-file.
+*/
+static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ OS_TIME_IO(OS_TRUNCATE, 0, (int)size,
+ p->pReal->pMethods->xTruncate(p->pReal, size)
+ );
+}
+
+/*
+** Sync an inst-file.
+*/
+static int instSync(sqlite3_file *pFile, int flags){
+ OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags));
+}
+
+/*
+** Return the current file-size of an inst-file.
+*/
+static int instFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ OS_TIME_IO(OS_FILESIZE, (int)(*pSize), 0,
+ p->pReal->pMethods->xFileSize(p->pReal, pSize)
+ );
+}
+
+/*
+** Lock an inst-file.
+*/
+static int instLock(sqlite3_file *pFile, int eLock){
+ OS_TIME_IO(OS_LOCK, eLock, 0, p->pReal->pMethods->xLock(p->pReal, eLock));
+}
+
+/*
+** Unlock an inst-file.
+*/
+static int instUnlock(sqlite3_file *pFile, int eLock){
+ OS_TIME_IO(OS_UNLOCK, eLock, 0, p->pReal->pMethods->xUnlock(p->pReal, eLock));
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an inst-file.
+*/
+static int instCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ OS_TIME_IO(OS_CHECKRESERVEDLOCK, 0, 0,
+ p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut)
+ );
+}
+
+/*
+** File control method. For custom operations on an inst-file.
+*/
+static int instFileControl(sqlite3_file *pFile, int op, void *pArg){
+ OS_TIME_IO(OS_FILECONTROL, 0, 0, p->pReal->pMethods->xFileControl(p->pReal, op, pArg));
+}
+
+/*
+** Return the sector-size in bytes for an inst-file.
+*/
+static int instSectorSize(sqlite3_file *pFile){
+ OS_TIME_IO(OS_SECTORSIZE, 0, 0, p->pReal->pMethods->xSectorSize(p->pReal));
+}
+
+/*
+** Return the device characteristic flags supported by an inst-file.
+*/
+static int instDeviceCharacteristics(sqlite3_file *pFile){
+ OS_TIME_IO(OS_DEVCHAR, 0, 0, p->pReal->pMethods->xDeviceCharacteristics(p->pReal));
+}
+
+/*
+** Open an inst file handle.
+*/
+static int instOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ inst_file *p = (inst_file *)pFile;
+ pFile->pMethods = &inst_io_methods;
+ p->pReal = (sqlite3_file *)&p[1];
+ p->pInstVfs = (InstVfs *)pVfs;
+ p->zName = zName;
+ p->flags = flags;
+ p->iFileId = ++p->pInstVfs->iNextFileId;
+
+ binarylog_blob(pVfs, zName, -1, 0);
+ OS_TIME_VFS(OS_OPEN, zName, flags, p->iFileId, 0,
+ REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags)
+ );
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int instDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ binarylog_blob(pVfs, zPath, -1, 0);
+ OS_TIME_VFS(OS_DELETE, zPath, 0, dirSync, 0,
+ REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync)
+ );
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int instAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ binarylog_blob(pVfs, zPath, -1, 0);
+ OS_TIME_VFS(OS_ACCESS, zPath, 0, flags, *pResOut,
+ REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut)
+ );
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (INST_MAX_PATHNAME+1) bytes.
+*/
+static int instFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ OS_TIME_VFS( OS_FULLPATHNAME, zPath, 0, 0, 0,
+ REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
+ );
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *instDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void instDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void *instDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), pHandle, zSymbol);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void instDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int instRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ OS_TIME_VFS( OS_RANDOMNESS, 0, 0, nByte, 0,
+ REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
+ );
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int instSleep(sqlite3_vfs *pVfs, int nMicro){
+ OS_TIME_VFS( OS_SLEEP, 0, 0, nMicro, 0,
+ REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro)
+ );
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int instCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ OS_TIME_VFS( OS_CURRENTTIME, 0, 0, 0, 0,
+ REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut)
+ );
+}
+
+sqlite3_vfs *sqlite3_instvfs_create(const char *zName, const char *zParent){
+ int nByte;
+ InstVfs *p;
+ sqlite3_vfs *pParent;
+
+ pParent = sqlite3_vfs_find(zParent);
+ if( !pParent ){
+ return 0;
+ }
+
+ nByte = strlen(zName) + 1 + sizeof(InstVfs);
+ p = (InstVfs *)sqlite3_malloc(nByte);
+ if( p ){
+ char *zCopy = (char *)&p[1];
+ memset(p, 0, nByte);
+ memcpy(p, &inst_vfs, sizeof(sqlite3_vfs));
+ p->pVfs = pParent;
+ memcpy(zCopy, zName, strlen(zName));
+ p->base.zName = (const char *)zCopy;
+ p->base.szOsFile += pParent->szOsFile;
+ sqlite3_vfs_register((sqlite3_vfs *)p, 0);
+ }
+
+ return (sqlite3_vfs *)p;
+}
+
+void sqlite3_instvfs_configure(
+ sqlite3_vfs *pVfs,
+ void (*xCall)(
+ void*,
+ int, /* File id */
+ int, /* Event code */
+ sqlite3_int64,
+ int, /* Return code */
+ const char*, /* File name */
+ int,
+ int,
+ sqlite3_int64
+ ),
+ void *pClient,
+ void (*xDel)(void *)
+){
+ InstVfs *p = (InstVfs *)pVfs;
+ assert( pVfs->xOpen==instOpen );
+ if( p->xDel ){
+ p->xDel(p->pClient);
+ }
+ p->xCall = xCall;
+ p->xDel = xDel;
+ p->pClient = pClient;
+}
+
+void sqlite3_instvfs_destroy(sqlite3_vfs *pVfs){
+ if( pVfs ){
+ sqlite3_vfs_unregister(pVfs);
+ sqlite3_instvfs_configure(pVfs, 0, 0, 0);
+ sqlite3_free(pVfs);
+ }
+}
+
+void sqlite3_instvfs_reset(sqlite3_vfs *pVfs){
+ InstVfs *p = (InstVfs *)pVfs;
+ assert( pVfs->xOpen==instOpen );
+ memset(p->aTime, 0, sizeof(sqlite3_int64)*OS_NUMEVENTS);
+ memset(p->aCount, 0, sizeof(int)*OS_NUMEVENTS);
+}
+
+const char *sqlite3_instvfs_name(int eEvent){
+ const char *zEvent = 0;
+
+ switch( eEvent ){
+ case OS_CLOSE: zEvent = "xClose"; break;
+ case OS_READ: zEvent = "xRead"; break;
+ case OS_WRITE: zEvent = "xWrite"; break;
+ case OS_TRUNCATE: zEvent = "xTruncate"; break;
+ case OS_SYNC: zEvent = "xSync"; break;
+ case OS_FILESIZE: zEvent = "xFilesize"; break;
+ case OS_LOCK: zEvent = "xLock"; break;
+ case OS_UNLOCK: zEvent = "xUnlock"; break;
+ case OS_CHECKRESERVEDLOCK: zEvent = "xCheckReservedLock"; break;
+ case OS_FILECONTROL: zEvent = "xFileControl"; break;
+ case OS_SECTORSIZE: zEvent = "xSectorSize"; break;
+ case OS_DEVCHAR: zEvent = "xDeviceCharacteristics"; break;
+ case OS_OPEN: zEvent = "xOpen"; break;
+ case OS_DELETE: zEvent = "xDelete"; break;
+ case OS_ACCESS: zEvent = "xAccess"; break;
+ case OS_FULLPATHNAME: zEvent = "xFullPathname"; break;
+ case OS_RANDOMNESS: zEvent = "xRandomness"; break;
+ case OS_SLEEP: zEvent = "xSleep"; break;
+ case OS_CURRENTTIME: zEvent = "xCurrentTime"; break;
+ }
+
+ return zEvent;
+}
+
+void sqlite3_instvfs_get(
+ sqlite3_vfs *pVfs,
+ int eEvent,
+ const char **pzEvent,
+ sqlite3_int64 *pnClick,
+ int *pnCall
+){
+ InstVfs *p = (InstVfs *)pVfs;
+ assert( pVfs->xOpen==instOpen );
+ if( eEvent<1 || eEvent>=OS_NUMEVENTS ){
+ *pzEvent = 0;
+ *pnClick = 0;
+ *pnCall = 0;
+ return;
+ }
+
+ *pzEvent = sqlite3_instvfs_name(eEvent);
+ *pnClick = p->aTime[eEvent];
+ *pnCall = p->aCount[eEvent];
+}
+
+#define BINARYLOG_BUFFERSIZE 8192
+
+struct InstVfsBinaryLog {
+ int nBuf;
+ char *zBuf;
+ sqlite3_int64 iOffset;
+ int log_data;
+ sqlite3_file *pOut;
+ char *zOut; /* Log file name */
+};
+typedef struct InstVfsBinaryLog InstVfsBinaryLog;
+
+static void put32bits(unsigned char *p, unsigned int v){
+ p[0] = v>>24;
+ p[1] = v>>16;
+ p[2] = v>>8;
+ p[3] = v;
+}
+
+static void binarylog_flush(InstVfsBinaryLog *pLog){
+ sqlite3_file *pFile = pLog->pOut;
+
+#ifdef SQLITE_TEST
+ extern int sqlite3_io_error_pending;
+ extern int sqlite3_io_error_persist;
+ extern int sqlite3_diskfull_pending;
+
+ int pending = sqlite3_io_error_pending;
+ int persist = sqlite3_io_error_persist;
+ int diskfull = sqlite3_diskfull_pending;
+
+ sqlite3_io_error_pending = 0;
+ sqlite3_io_error_persist = 0;
+ sqlite3_diskfull_pending = 0;
+#endif
+
+ pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
+ pLog->iOffset += pLog->nBuf;
+ pLog->nBuf = 0;
+
+#ifdef SQLITE_TEST
+ sqlite3_io_error_pending = pending;
+ sqlite3_io_error_persist = persist;
+ sqlite3_diskfull_pending = diskfull;
+#endif
+}
+
+static void binarylog_xcall(
+ void *p,
+ int eEvent,
+ int iFileId,
+ sqlite3_int64 nClick,
+ int return_code,
+ const char *zName,
+ int flags,
+ int nByte,
+ sqlite3_int64 iOffset
+){
+ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
+ unsigned char *zRec;
+ if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
+ binarylog_flush(pLog);
+ }
+ zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
+ put32bits(&zRec[0], eEvent);
+ put32bits(&zRec[4], (int)iFileId);
+ put32bits(&zRec[8], (int)nClick);
+ put32bits(&zRec[12], return_code);
+ put32bits(&zRec[16], flags);
+ put32bits(&zRec[20], nByte);
+ put32bits(&zRec[24], (int)iOffset);
+ pLog->nBuf += 28;
+}
+
+static void binarylog_xdel(void *p){
+ /* Close the log file and free the memory allocated for the
+ ** InstVfsBinaryLog structure.
+ */
+ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
+ sqlite3_file *pFile = pLog->pOut;
+ if( pLog->nBuf ){
+ binarylog_flush(pLog);
+ }
+ pFile->pMethods->xClose(pFile);
+ sqlite3_free(pLog->pOut);
+ sqlite3_free(pLog->zBuf);
+ sqlite3_free(pLog);
+}
+
+static void binarylog_blob(
+ sqlite3_vfs *pVfs,
+ const char *zBlob,
+ int nBlob,
+ int isBinary
+){
+ InstVfsBinaryLog *pLog;
+ InstVfs *pInstVfs = (InstVfs *)pVfs;
+
+ if( pVfs->xOpen!=instOpen || pInstVfs->xCall!=binarylog_xcall ){
+ return;
+ }
+ pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
+ if( zBlob && (!isBinary || pLog->log_data) ){
+ unsigned char *zRec;
+ int nWrite;
+
+ if( nBlob<0 ){
+ nBlob = strlen(zBlob);
+ }
+ nWrite = nBlob + 28;
+
+ if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
+ binarylog_flush(pLog);
+ }
+
+ zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
+ memset(zRec, 0, nWrite);
+ put32bits(&zRec[0], BINARYLOG_STRING);
+ put32bits(&zRec[4], (int)nBlob);
+ put32bits(&zRec[8], (int)isBinary);
+ memcpy(&zRec[28], zBlob, nBlob);
+ pLog->nBuf += nWrite;
+ }
+}
+
+void sqlite3_instvfs_binarylog_call(
+ sqlite3_vfs *pVfs,
+ int eEvent,
+ sqlite3_int64 nClick,
+ int return_code,
+ const char *zString
+){
+ InstVfs *pInstVfs = (InstVfs *)pVfs;
+ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
+
+ if( zString ){
+ binarylog_blob(pVfs, zString, -1, 0);
+ }
+ binarylog_xcall(pLog, eEvent, 0, nClick, return_code, 0, 0, 0, 0);
+}
+
+void sqlite3_instvfs_binarylog_marker(
+ sqlite3_vfs *pVfs,
+ const char *zMarker
+){
+ InstVfs *pInstVfs = (InstVfs *)pVfs;
+ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
+ binarylog_blob(pVfs, zMarker, -1, 0);
+ binarylog_xcall(pLog, BINARYLOG_MARKER, 0, 0, 0, 0, 0, 0, 0);
+}
+
+sqlite3_vfs *sqlite3_instvfs_binarylog(
+ const char *zVfs,
+ const char *zParentVfs,
+ const char *zLog,
+ int log_data
+){
+ InstVfsBinaryLog *p;
+ sqlite3_vfs *pVfs;
+ sqlite3_vfs *pParent;
+ int nByte;
+ int flags;
+ int rc;
+
+ pParent = sqlite3_vfs_find(zParentVfs);
+ if( !pParent ){
+ return 0;
+ }
+
+ nByte = sizeof(InstVfsBinaryLog) + pParent->mxPathname+1;
+ p = (InstVfsBinaryLog *)sqlite3_malloc(nByte);
+ memset(p, 0, nByte);
+ p->zBuf = sqlite3_malloc(BINARYLOG_BUFFERSIZE);
+ p->zOut = (char *)&p[1];
+ p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile);
+ p->log_data = log_data;
+ pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut);
+ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
+ pParent->xDelete(pParent, p->zOut, 0);
+ rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags);
+ if( rc==SQLITE_OK ){
+ memcpy(p->zBuf, "sqlite_ostrace1.....", 20);
+ p->iOffset = 0;
+ p->nBuf = 20;
+ }
+ if( rc ){
+ binarylog_xdel(p);
+ return 0;
+ }
+
+ pVfs = sqlite3_instvfs_create(zVfs, zParentVfs);
+ if( pVfs ){
+ sqlite3_instvfs_configure(pVfs, binarylog_xcall, p, binarylog_xdel);
+ }
+
+ return pVfs;
+}
+#endif /* SQLITE_ENABLE_INSTVFS */
+
+/**************************************************************************
+***************************************************************************
+** Tcl interface starts here.
+*/
+#if SQLITE_TEST
+
+#include <tcl.h>
+
+#ifdef SQLITE_ENABLE_INSTVFS
+struct InstVfsCall {
+ Tcl_Interp *interp;
+ Tcl_Obj *pScript;
+};
+typedef struct InstVfsCall InstVfsCall;
+
+static void test_instvfs_xcall(
+ void *p,
+ int eEvent,
+ int iFileId,
+ sqlite3_int64 nClick,
+ int return_code,
+ const char *zName,
+ int flags,
+ int nByte,
+ sqlite3_int64 iOffset
+){
+ int rc;
+ InstVfsCall *pCall = (InstVfsCall *)p;
+ Tcl_Obj *pObj = Tcl_DuplicateObj( pCall->pScript);
+ const char *zEvent = sqlite3_instvfs_name(eEvent);
+
+ Tcl_IncrRefCount(pObj);
+ Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zEvent, -1));
+ Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(nClick));
+ Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zName, -1));
+ Tcl_ListObjAppendElement(0, pObj, Tcl_NewIntObj(nByte));
+ Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(iOffset));
+
+ rc = Tcl_EvalObjEx(pCall->interp, pObj, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT);
+ if( rc ){
+ Tcl_BackgroundError(pCall->interp);
+ }
+ Tcl_DecrRefCount(pObj);
+}
+
+static void test_instvfs_xdel(void *p){
+ InstVfsCall *pCall = (InstVfsCall *)p;
+ Tcl_DecrRefCount(pCall->pScript);
+ sqlite3_free(pCall);
+}
+
+static int test_sqlite3_instvfs(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static const char *IV_strs[] =
+ { "create", "destroy", "reset", "report", "configure", "binarylog", "marker", 0 };
+ enum IV_enum { IV_CREATE, IV_DESTROY, IV_RESET, IV_REPORT, IV_CONFIGURE, IV_BINARYLOG, IV_MARKER };
+ int iSub;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
+ }
+ if( Tcl_GetIndexFromObj(interp, objv[1], IV_strs, "sub-command", 0, &iSub) ){
+ return TCL_ERROR;
+ }
+
+ switch( (enum IV_enum)iSub ){
+ case IV_CREATE: {
+ char *zParent = 0;
+ sqlite3_vfs *p;
+ int isDefault = 0;
+ if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){
+ isDefault = 1;
+ }
+ if( (objc-isDefault)!=4 && (objc-isDefault)!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME ?PARENT-VFS?");
+ return TCL_ERROR;
+ }
+ if( objc==(4+isDefault) ){
+ zParent = Tcl_GetString(objv[3+isDefault]);
+ }
+ p = sqlite3_instvfs_create(Tcl_GetString(objv[2+isDefault]), zParent);
+ if( !p ){
+ Tcl_AppendResult(interp, "error creating vfs ", 0);
+ return TCL_ERROR;
+ }
+ if( isDefault ){
+ sqlite3_vfs_register(p, 1);
+ }
+ Tcl_SetObjResult(interp, objv[2]);
+ break;
+ }
+ case IV_BINARYLOG: {
+ char *zName = 0;
+ char *zLog = 0;
+ char *zParent = 0;
+ sqlite3_vfs *p;
+ int isDefault = 0;
+ int isLogdata = 0;
+ int argbase = 2;
+
+ for(argbase=2; argbase<(objc-2); argbase++){
+ if( 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){
+ isDefault = 1;
+ }
+ else if( 0==strcmp("-parent", Tcl_GetString(objv[argbase])) ){
+ argbase++;
+ zParent = Tcl_GetString(objv[argbase]);
+ }
+ else if( 0==strcmp("-logdata", Tcl_GetString(objv[argbase])) ){
+ isLogdata = 1;
+ }else{
+ break;
+ }
+ }
+
+ if( (objc-argbase)!=2 ){
+ Tcl_WrongNumArgs(
+ interp, 2, objv, "?-default? ?-parent VFS? ?-logdata? NAME LOGFILE"
+ );
+ return TCL_ERROR;
+ }
+ zName = Tcl_GetString(objv[argbase]);
+ zLog = Tcl_GetString(objv[argbase+1]);
+ p = sqlite3_instvfs_binarylog(zName, zParent, zLog, isLogdata);
+ if( !p ){
+ Tcl_AppendResult(interp, "error creating vfs ", 0);
+ return TCL_ERROR;
+ }
+ if( isDefault ){
+ sqlite3_vfs_register(p, 1);
+ }
+ Tcl_SetObjResult(interp, objv[2]);
+ break;
+ }
+
+ case IV_MARKER: {
+ sqlite3_vfs *p;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "VFS MARKER");
+ return TCL_ERROR;
+ }
+ p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
+ if( !p || p->xOpen!=instOpen ){
+ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
+ return TCL_ERROR;
+ }
+ sqlite3_instvfs_binarylog_marker(p, Tcl_GetString(objv[3]));
+ Tcl_ResetResult(interp);
+ break;
+ }
+
+ case IV_CONFIGURE: {
+ InstVfsCall *pCall;
+
+ sqlite3_vfs *p;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
+ return TCL_ERROR;
+ }
+ p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
+ if( !p || p->xOpen!=instOpen ){
+ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
+ return TCL_ERROR;
+ }
+
+ if( strlen(Tcl_GetString(objv[3])) ){
+ pCall = (InstVfsCall *)sqlite3_malloc(sizeof(InstVfsCall));
+ pCall->interp = interp;
+ pCall->pScript = Tcl_DuplicateObj(objv[3]);
+ Tcl_IncrRefCount(pCall->pScript);
+ sqlite3_instvfs_configure(p,
+ test_instvfs_xcall, (void *)pCall, test_instvfs_xdel
+ );
+ }else{
+ sqlite3_instvfs_configure(p, 0, 0, 0);
+ }
+ break;
+ }
+
+ case IV_REPORT:
+ case IV_DESTROY:
+ case IV_RESET: {
+ sqlite3_vfs *p;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "NAME");
+ return TCL_ERROR;
+ }
+ p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
+ if( !p || p->xOpen!=instOpen ){
+ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
+ return TCL_ERROR;
+ }
+
+ if( ((enum IV_enum)iSub)==IV_DESTROY ){
+ sqlite3_instvfs_destroy(p);
+ }
+ if( ((enum IV_enum)iSub)==IV_RESET ){
+ sqlite3_instvfs_reset(p);
+ }
+ if( ((enum IV_enum)iSub)==IV_REPORT ){
+ int ii;
+ Tcl_Obj *pRet = Tcl_NewObj();
+
+ const char *zName = (char *)1;
+ sqlite3_int64 nClick;
+ int nCall;
+ for(ii=1; zName; ii++){
+ sqlite3_instvfs_get(p, ii, &zName, &nClick, &nCall);
+ if( zName ){
+ Tcl_Obj *pElem = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zName, -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(nCall));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewWideIntObj(nClick));
+ Tcl_ListObjAppendElement(0, pRet, pElem);
+ }
+ }
+
+ Tcl_SetObjResult(interp, pRet);
+ }
+
+ break;
+ }
+ }
+
+ return TCL_OK;
+}
+#endif /* SQLITE_ENABLE_INSTVFS */
+
+/* Alternative implementation of sqlite3_instvfs when the real
+** implementation is unavailable.
+*/
+#ifndef SQLITE_ENABLE_INSTVFS
+static int test_sqlite3_instvfs(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_AppendResult(interp,
+ "not compiled with -DSQLITE_ENABLE_INSTVFS; sqlite3_instvfs is "
+ "unavailable", (char*)0);
+ return TCL_ERROR;
+}
+#endif /* !defined(SQLITE_ENABLE_INSTVFS) */
+
+int SqlitetestOsinst_Init(Tcl_Interp *interp){
+ Tcl_CreateObjCommand(interp, "sqlite3_instvfs", test_sqlite3_instvfs, 0, 0);
+ return TCL_OK;
+}
+
+#endif /* SQLITE_TEST */
diff --git a/third_party/sqlite/src/test_schema.c b/third_party/sqlite/src/test_schema.c
new file mode 100755
index 0000000..51099f5
--- /dev/null
+++ b/third_party/sqlite/src/test_schema.c
@@ -0,0 +1,361 @@
+/*
+** 2006 June 10
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the virtual table interfaces. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** $Id: test_schema.c,v 1.15 2008/07/07 14:50:14 drh Exp $
+*/
+
+/* The code in this file defines a sqlite3 virtual-table module that
+** provides a read-only view of the current database schema. There is one
+** row in the schema table for each column in the database schema.
+*/
+#define SCHEMA \
+"CREATE TABLE x(" \
+ "database," /* Name of database (i.e. main, temp etc.) */ \
+ "tablename," /* Name of table */ \
+ "cid," /* Column number (from left-to-right, 0 upward) */ \
+ "name," /* Column name */ \
+ "type," /* Specified type (i.e. VARCHAR(32)) */ \
+ "not_null," /* Boolean. True if NOT NULL was specified */ \
+ "dflt_value," /* Default value for this column */ \
+ "pk" /* True if this column is part of the primary key */ \
+")"
+
+/* If SQLITE_TEST is defined this code is preprocessed for use as part
+** of the sqlite test binary "testfixture". Otherwise it is preprocessed
+** to be compiled into an sqlite dynamic extension.
+*/
+#ifdef SQLITE_TEST
+ #include "sqliteInt.h"
+ #include "tcl.h"
+#else
+ #include "sqlite3ext.h"
+ SQLITE_EXTENSION_INIT1
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct schema_vtab schema_vtab;
+typedef struct schema_cursor schema_cursor;
+
+/* A schema table object */
+struct schema_vtab {
+ sqlite3_vtab base;
+ sqlite3 *db;
+};
+
+/* A schema table cursor object */
+struct schema_cursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pDbList;
+ sqlite3_stmt *pTableList;
+ sqlite3_stmt *pColumnList;
+ int rowid;
+};
+
+/*
+** None of this works unless we have virtual tables.
+*/
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/*
+** Table destructor for the schema module.
+*/
+static int schemaDestroy(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return 0;
+}
+
+/*
+** Table constructor for the schema module.
+*/
+static int schemaCreate(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ int rc = SQLITE_NOMEM;
+ schema_vtab *pVtab = sqlite3_malloc(sizeof(schema_vtab));
+ if( pVtab ){
+ memset(pVtab, 0, sizeof(schema_vtab));
+ pVtab->db = db;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ rc = sqlite3_declare_vtab(db, SCHEMA);
+#endif
+ }
+ *ppVtab = (sqlite3_vtab *)pVtab;
+ return rc;
+}
+
+/*
+** Open a new cursor on the schema table.
+*/
+static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ int rc = SQLITE_NOMEM;
+ schema_cursor *pCur;
+ pCur = sqlite3_malloc(sizeof(schema_cursor));
+ if( pCur ){
+ memset(pCur, 0, sizeof(schema_cursor));
+ *ppCursor = (sqlite3_vtab_cursor *)pCur;
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Close a schema table cursor.
+*/
+static int schemaClose(sqlite3_vtab_cursor *cur){
+ schema_cursor *pCur = (schema_cursor *)cur;
+ sqlite3_finalize(pCur->pDbList);
+ sqlite3_finalize(pCur->pTableList);
+ sqlite3_finalize(pCur->pColumnList);
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+/*
+** Retrieve a column of data.
+*/
+static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ schema_cursor *pCur = (schema_cursor *)cur;
+ switch( i ){
+ case 0:
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pDbList, 1));
+ break;
+ case 1:
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pTableList, 0));
+ break;
+ default:
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pColumnList, i-2));
+ break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Retrieve the current rowid.
+*/
+static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ schema_cursor *pCur = (schema_cursor *)cur;
+ *pRowid = pCur->rowid;
+ return SQLITE_OK;
+}
+
+static int finalize(sqlite3_stmt **ppStmt){
+ int rc = sqlite3_finalize(*ppStmt);
+ *ppStmt = 0;
+ return rc;
+}
+
+static int schemaEof(sqlite3_vtab_cursor *cur){
+ schema_cursor *pCur = (schema_cursor *)cur;
+ return (pCur->pDbList ? 0 : 1);
+}
+
+/*
+** Advance the cursor to the next row.
+*/
+static int schemaNext(sqlite3_vtab_cursor *cur){
+ int rc = SQLITE_OK;
+ schema_cursor *pCur = (schema_cursor *)cur;
+ schema_vtab *pVtab = (schema_vtab *)(cur->pVtab);
+ char *zSql = 0;
+
+ while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){
+ if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit;
+
+ while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){
+ if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit;
+
+ assert(pCur->pDbList);
+ while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){
+ rc = finalize(&pCur->pDbList);
+ goto next_exit;
+ }
+
+ /* Set zSql to the SQL to pull the list of tables from the
+ ** sqlite_master (or sqlite_temp_master) table of the database
+ ** identfied by the row pointed to by the SQL statement pCur->pDbList
+ ** (iterating through a "PRAGMA database_list;" statement).
+ */
+ if( sqlite3_column_int(pCur->pDbList, 0)==1 ){
+ zSql = sqlite3_mprintf(
+ "SELECT name FROM sqlite_temp_master WHERE type='table'"
+ );
+ }else{
+ sqlite3_stmt *pDbList = pCur->pDbList;
+ zSql = sqlite3_mprintf(
+ "SELECT name FROM %Q.sqlite_master WHERE type='table'",
+ sqlite3_column_text(pDbList, 1)
+ );
+ }
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto next_exit;
+ }
+
+ rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) goto next_exit;
+ }
+
+ /* Set zSql to the SQL to the table_info pragma for the table currently
+ ** identified by the rows pointed to by statements pCur->pDbList and
+ ** pCur->pTableList.
+ */
+ zSql = sqlite3_mprintf("PRAGMA %Q.table_info(%Q)",
+ sqlite3_column_text(pCur->pDbList, 1),
+ sqlite3_column_text(pCur->pTableList, 0)
+ );
+
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto next_exit;
+ }
+ rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK ) goto next_exit;
+ }
+ pCur->rowid++;
+
+next_exit:
+ /* TODO: Handle rc */
+ return rc;
+}
+
+/*
+** Reset a schema table cursor.
+*/
+static int schemaFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ int rc;
+ schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab);
+ schema_cursor *pCur = (schema_cursor *)pVtabCursor;
+ pCur->rowid = 0;
+ finalize(&pCur->pTableList);
+ finalize(&pCur->pColumnList);
+ finalize(&pCur->pDbList);
+ rc = sqlite3_prepare(pVtab->db,"PRAGMA database_list", -1, &pCur->pDbList, 0);
+ return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc);
+}
+
+/*
+** Analyse the WHERE condition.
+*/
+static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ return SQLITE_OK;
+}
+
+/*
+** A virtual table module that merely echos method calls into TCL
+** variables.
+*/
+static sqlite3_module schemaModule = {
+ 0, /* iVersion */
+ schemaCreate,
+ schemaCreate,
+ schemaBestIndex,
+ schemaDestroy,
+ schemaDestroy,
+ schemaOpen, /* xOpen - open a cursor */
+ schemaClose, /* xClose - close a cursor */
+ schemaFilter, /* xFilter - configure scan constraints */
+ schemaNext, /* xNext - advance a cursor */
+ schemaEof, /* xEof */
+ schemaColumn, /* xColumn - read data */
+ schemaRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+};
+
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
+
+#ifdef SQLITE_TEST
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+
+/*
+** Register the schema virtual table module.
+*/
+static int register_schema_module(
+ ClientData clientData, /* Not used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_create_module(db, "schema", &schemaModule, 0);
+#endif
+ return TCL_OK;
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetestschema_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+ { "register_schema_module", register_schema_module, 0 },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+ return TCL_OK;
+}
+
+#else
+
+/*
+** Extension load function.
+*/
+int sqlite3_extension_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_create_module(db, "schema", &schemaModule, 0);
+#endif
+ return 0;
+}
+
+#endif
diff --git a/third_party/sqlite/src/test_server.c b/third_party/sqlite/src/test_server.c
new file mode 100755
index 0000000..6862d7c
--- /dev/null
+++ b/third_party/sqlite/src/test_server.c
@@ -0,0 +1,493 @@
+/*
+** 2006 January 07
+**
+** 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.
+**
+******************************************************************************
+**
+** $Id: test_server.c,v 1.8 2008/06/26 10:41:19 danielk1977 Exp $
+**
+** This file contains demonstration code. Nothing in this file gets compiled
+** or linked into the SQLite library unless you use a non-standard option:
+**
+** -DSQLITE_SERVER=1
+**
+** The configure script will never generate a Makefile with the option
+** above. You will need to manually modify the Makefile if you want to
+** include any of the code from this file in your project. Or, at your
+** option, you may copy and paste the code from this file and
+** thereby avoiding a recompile of SQLite.
+**
+**
+** This source file demonstrates how to use SQLite to create an SQL database
+** server thread in a multiple-threaded program. One or more client threads
+** send messages to the server thread and the server thread processes those
+** messages in the order received and returns the results to the client.
+**
+** One might ask: "Why bother? Why not just let each thread connect
+** to the database directly?" There are a several of reasons to
+** prefer the client/server approach.
+**
+** (1) Some systems (ex: Redhat9) have broken threading implementations
+** that prevent SQLite database connections from being used in
+** a thread different from the one where they were created. With
+** the client/server approach, all database connections are created
+** and used within the server thread. Client calls to the database
+** can be made from multiple threads (though not at the same time!)
+**
+** (2) Beginning with SQLite version 3.3.0, when two or more
+** connections to the same database occur within the same thread,
+** they can optionally share their database cache. This reduces
+** I/O and memory requirements. Cache shared is controlled using
+** the sqlite3_enable_shared_cache() API.
+**
+** (3) Database connections on a shared cache use table-level locking
+** instead of file-level locking for improved concurrency.
+**
+** (4) Database connections on a shared cache can by optionally
+** set to READ UNCOMMITTED isolation. (The default isolation for
+** SQLite is SERIALIZABLE.) When this occurs, readers will
+** never be blocked by a writer and writers will not be
+** blocked by readers. There can still only be a single writer
+** at a time, but multiple readers can simultaneously exist with
+** that writer. This is a huge increase in concurrency.
+**
+** To summarize the rational for using a client/server approach: prior
+** to SQLite version 3.3.0 it probably was not worth the trouble. But
+** with SQLite version 3.3.0 and beyond you can get significant performance
+** and concurrency improvements and memory usage reductions by going
+** client/server.
+**
+** Note: The extra features of version 3.3.0 described by points (2)
+** through (4) above are only available if you compile without the
+** option -DSQLITE_OMIT_SHARED_CACHE.
+**
+** Here is how the client/server approach works: The database server
+** thread is started on this procedure:
+**
+** void *sqlite3_server(void *NotUsed);
+**
+** The sqlite_server procedure runs as long as the g.serverHalt variable
+** is false. A mutex is used to make sure no more than one server runs
+** at a time. The server waits for messages to arrive on a message
+** queue and processes the messages in order.
+**
+** Two convenience routines are provided for starting and stopping the
+** server thread:
+**
+** void sqlite3_server_start(void);
+** void sqlite3_server_stop(void);
+**
+** Both of the convenience routines return immediately. Neither will
+** ever give an error. If a server is already started or already halted,
+** then the routines are effectively no-ops.
+**
+** Clients use the following interfaces:
+**
+** sqlite3_client_open
+** sqlite3_client_prepare
+** sqlite3_client_step
+** sqlite3_client_reset
+** sqlite3_client_finalize
+** sqlite3_client_close
+**
+** These interfaces work exactly like the standard core SQLite interfaces
+** having the same names without the "_client_" infix. Many other SQLite
+** interfaces can be used directly without having to send messages to the
+** server as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined.
+** The following interfaces fall into this second category:
+**
+** sqlite3_bind_*
+** sqlite3_changes
+** sqlite3_clear_bindings
+** sqlite3_column_*
+** sqlite3_complete
+** sqlite3_create_collation
+** sqlite3_create_function
+** sqlite3_data_count
+** sqlite3_db_handle
+** sqlite3_errcode
+** sqlite3_errmsg
+** sqlite3_last_insert_rowid
+** sqlite3_total_changes
+** sqlite3_transfer_bindings
+**
+** A single SQLite connection (an sqlite3* object) or an SQLite statement
+** (an sqlite3_stmt* object) should only be passed to a single interface
+** function at a time. The connections and statements can be passed from
+** any thread to any of the functions listed in the second group above as
+** long as the same connection is not in use by two threads at once and
+** as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined. Additional
+** information about the SQLITE_ENABLE_MEMORY_MANAGEMENT constraint is
+** below.
+**
+** The busy handler for all database connections should remain turned
+** off. That means that any lock contention will cause the associated
+** sqlite3_client_step() call to return immediately with an SQLITE_BUSY
+** error code. If a busy handler is enabled and lock contention occurs,
+** then the entire server thread will block. This will cause not only
+** the requesting client to block but every other database client as
+** well. It is possible to enhance the code below so that lock
+** contention will cause the message to be placed back on the top of
+** the queue to be tried again later. But such enhanced processing is
+** not included here, in order to keep the example simple.
+**
+** This example code assumes the use of pthreads. Pthreads
+** implementations are available for windows. (See, for example
+** http://sourceware.org/pthreads-win32/announcement.html.) Or, you
+** can translate the locking and thread synchronization code to use
+** windows primitives easily enough. The details are left as an
+** exercise to the reader.
+**
+**** Restrictions Associated With SQLITE_ENABLE_MEMORY_MANAGEMENT ****
+**
+** If you compile with SQLITE_ENABLE_MEMORY_MANAGEMENT defined, then
+** SQLite includes code that tracks how much memory is being used by
+** each thread. These memory counts can become confused if memory
+** is allocated by one thread and then freed by another. For that
+** reason, when SQLITE_ENABLE_MEMORY_MANAGEMENT is used, all operations
+** that might allocate or free memory should be performanced in the same
+** thread that originally created the database connection. In that case,
+** many of the operations that are listed above as safe to be performed
+** in separate threads would need to be sent over to the server to be
+** done there. If SQLITE_ENABLE_MEMORY_MANAGEMENT is defined, then
+** the following functions can be used safely from different threads
+** without messing up the allocation counts:
+**
+** sqlite3_bind_parameter_name
+** sqlite3_bind_parameter_index
+** sqlite3_changes
+** sqlite3_column_blob
+** sqlite3_column_count
+** sqlite3_complete
+** sqlite3_data_count
+** sqlite3_db_handle
+** sqlite3_errcode
+** sqlite3_errmsg
+** sqlite3_last_insert_rowid
+** sqlite3_total_changes
+**
+** The remaining functions are not thread-safe when memory management
+** is enabled. So one would have to define some new interface routines
+** along the following lines:
+**
+** sqlite3_client_bind_*
+** sqlite3_client_clear_bindings
+** sqlite3_client_column_*
+** sqlite3_client_create_collation
+** sqlite3_client_create_function
+** sqlite3_client_transfer_bindings
+**
+** The example code in this file is intended for use with memory
+** management turned off. So the implementation of these additional
+** client interfaces is left as an exercise to the reader.
+**
+** It may seem surprising to the reader that the list of safe functions
+** above does not include things like sqlite3_bind_int() or
+** sqlite3_column_int(). But those routines might, in fact, allocate
+** or deallocate memory. In the case of sqlite3_bind_int(), if the
+** parameter was previously bound to a string that string might need
+** to be deallocated before the new integer value is inserted. In
+** the case of sqlite3_column_int(), the value of the column might be
+** a UTF-16 string which will need to be converted to UTF-8 then into
+** an integer.
+*/
+
+/* Include this to get the definition of SQLITE_THREADSAFE, in the
+** case that default values are used.
+*/
+#include "sqliteInt.h"
+
+/*
+** Only compile the code in this file on UNIX with a SQLITE_THREADSAFE build
+** and only if the SQLITE_SERVER macro is defined.
+*/
+#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
+#if defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
+
+/*
+** We require only pthreads and the public interface of SQLite.
+*/
+#include <pthread.h>
+#include "sqlite3.h"
+
+/*
+** Messages are passed from client to server and back again as
+** instances of the following structure.
+*/
+typedef struct SqlMessage SqlMessage;
+struct SqlMessage {
+ int op; /* Opcode for the message */
+ sqlite3 *pDb; /* The SQLite connection */
+ sqlite3_stmt *pStmt; /* A specific statement */
+ int errCode; /* Error code returned */
+ const char *zIn; /* Input filename or SQL statement */
+ int nByte; /* Size of the zIn parameter for prepare() */
+ const char *zOut; /* Tail of the SQL statement */
+ SqlMessage *pNext; /* Next message in the queue */
+ SqlMessage *pPrev; /* Previous message in the queue */
+ pthread_mutex_t clientMutex; /* Hold this mutex to access the message */
+ pthread_cond_t clientWakeup; /* Signal to wake up the client */
+};
+
+/*
+** Legal values for SqlMessage.op
+*/
+#define MSG_Open 1 /* sqlite3_open(zIn, &pDb) */
+#define MSG_Prepare 2 /* sqlite3_prepare(pDb, zIn, nByte, &pStmt, &zOut) */
+#define MSG_Step 3 /* sqlite3_step(pStmt) */
+#define MSG_Reset 4 /* sqlite3_reset(pStmt) */
+#define MSG_Finalize 5 /* sqlite3_finalize(pStmt) */
+#define MSG_Close 6 /* sqlite3_close(pDb) */
+#define MSG_Done 7 /* Server has finished with this message */
+
+
+/*
+** State information about the server is stored in a static variable
+** named "g" as follows:
+*/
+static struct ServerState {
+ pthread_mutex_t queueMutex; /* Hold this mutex to access the msg queue */
+ pthread_mutex_t serverMutex; /* Held by the server while it is running */
+ pthread_cond_t serverWakeup; /* Signal this condvar to wake up the server */
+ volatile int serverHalt; /* Server halts itself when true */
+ SqlMessage *pQueueHead; /* Head of the message queue */
+ SqlMessage *pQueueTail; /* Tail of the message queue */
+} g = {
+ PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_COND_INITIALIZER,
+};
+
+/*
+** Send a message to the server. Block until we get a reply.
+**
+** The mutex and condition variable in the message are uninitialized
+** when this routine is called. This routine takes care of
+** initializing them and destroying them when it has finished.
+*/
+static void sendToServer(SqlMessage *pMsg){
+ /* Initialize the mutex and condition variable on the message
+ */
+ pthread_mutex_init(&pMsg->clientMutex, 0);
+ pthread_cond_init(&pMsg->clientWakeup, 0);
+
+ /* Add the message to the head of the server's message queue.
+ */
+ pthread_mutex_lock(&g.queueMutex);
+ pMsg->pNext = g.pQueueHead;
+ if( g.pQueueHead==0 ){
+ g.pQueueTail = pMsg;
+ }else{
+ g.pQueueHead->pPrev = pMsg;
+ }
+ pMsg->pPrev = 0;
+ g.pQueueHead = pMsg;
+ pthread_mutex_unlock(&g.queueMutex);
+
+ /* Signal the server that the new message has be queued, then
+ ** block waiting for the server to process the message.
+ */
+ pthread_mutex_lock(&pMsg->clientMutex);
+ pthread_cond_signal(&g.serverWakeup);
+ while( pMsg->op!=MSG_Done ){
+ pthread_cond_wait(&pMsg->clientWakeup, &pMsg->clientMutex);
+ }
+ pthread_mutex_unlock(&pMsg->clientMutex);
+
+ /* Destroy the mutex and condition variable of the message.
+ */
+ pthread_mutex_destroy(&pMsg->clientMutex);
+ pthread_cond_destroy(&pMsg->clientWakeup);
+}
+
+/*
+** The following 6 routines are client-side implementations of the
+** core SQLite interfaces:
+**
+** sqlite3_open
+** sqlite3_prepare
+** sqlite3_step
+** sqlite3_reset
+** sqlite3_finalize
+** sqlite3_close
+**
+** Clients should use the following client-side routines instead of
+** the core routines above.
+**
+** sqlite3_client_open
+** sqlite3_client_prepare
+** sqlite3_client_step
+** sqlite3_client_reset
+** sqlite3_client_finalize
+** sqlite3_client_close
+**
+** Each of these routines creates a message for the desired operation,
+** sends that message to the server, waits for the server to process
+** then message and return a response.
+*/
+int sqlite3_client_open(const char *zDatabaseName, sqlite3 **ppDb){
+ SqlMessage msg;
+ msg.op = MSG_Open;
+ msg.zIn = zDatabaseName;
+ sendToServer(&msg);
+ *ppDb = msg.pDb;
+ return msg.errCode;
+}
+int sqlite3_client_prepare(
+ sqlite3 *pDb,
+ const char *zSql,
+ int nByte,
+ sqlite3_stmt **ppStmt,
+ const char **pzTail
+){
+ SqlMessage msg;
+ msg.op = MSG_Prepare;
+ msg.pDb = pDb;
+ msg.zIn = zSql;
+ msg.nByte = nByte;
+ sendToServer(&msg);
+ *ppStmt = msg.pStmt;
+ if( pzTail ) *pzTail = msg.zOut;
+ return msg.errCode;
+}
+int sqlite3_client_step(sqlite3_stmt *pStmt){
+ SqlMessage msg;
+ msg.op = MSG_Step;
+ msg.pStmt = pStmt;
+ sendToServer(&msg);
+ return msg.errCode;
+}
+int sqlite3_client_reset(sqlite3_stmt *pStmt){
+ SqlMessage msg;
+ msg.op = MSG_Reset;
+ msg.pStmt = pStmt;
+ sendToServer(&msg);
+ return msg.errCode;
+}
+int sqlite3_client_finalize(sqlite3_stmt *pStmt){
+ SqlMessage msg;
+ msg.op = MSG_Finalize;
+ msg.pStmt = pStmt;
+ sendToServer(&msg);
+ return msg.errCode;
+}
+int sqlite3_client_close(sqlite3 *pDb){
+ SqlMessage msg;
+ msg.op = MSG_Close;
+ msg.pDb = pDb;
+ sendToServer(&msg);
+ return msg.errCode;
+}
+
+/*
+** This routine implements the server. To start the server, first
+** make sure g.serverHalt is false, then create a new detached thread
+** on this procedure. See the sqlite3_server_start() routine below
+** for an example. This procedure loops until g.serverHalt becomes
+** true.
+*/
+void *sqlite3_server(void *NotUsed){
+ if( pthread_mutex_trylock(&g.serverMutex) ){
+ return 0; /* Another server is already running */
+ }
+ sqlite3_enable_shared_cache(1);
+ while( !g.serverHalt ){
+ SqlMessage *pMsg;
+
+ /* Remove the last message from the message queue.
+ */
+ pthread_mutex_lock(&g.queueMutex);
+ while( g.pQueueTail==0 && g.serverHalt==0 ){
+ pthread_cond_wait(&g.serverWakeup, &g.queueMutex);
+ }
+ pMsg = g.pQueueTail;
+ if( pMsg ){
+ if( pMsg->pPrev ){
+ pMsg->pPrev->pNext = 0;
+ }else{
+ g.pQueueHead = 0;
+ }
+ g.pQueueTail = pMsg->pPrev;
+ }
+ pthread_mutex_unlock(&g.queueMutex);
+ if( pMsg==0 ) break;
+
+ /* Process the message just removed
+ */
+ pthread_mutex_lock(&pMsg->clientMutex);
+ switch( pMsg->op ){
+ case MSG_Open: {
+ pMsg->errCode = sqlite3_open(pMsg->zIn, &pMsg->pDb);
+ break;
+ }
+ case MSG_Prepare: {
+ pMsg->errCode = sqlite3_prepare(pMsg->pDb, pMsg->zIn, pMsg->nByte,
+ &pMsg->pStmt, &pMsg->zOut);
+ break;
+ }
+ case MSG_Step: {
+ pMsg->errCode = sqlite3_step(pMsg->pStmt);
+ break;
+ }
+ case MSG_Reset: {
+ pMsg->errCode = sqlite3_reset(pMsg->pStmt);
+ break;
+ }
+ case MSG_Finalize: {
+ pMsg->errCode = sqlite3_finalize(pMsg->pStmt);
+ break;
+ }
+ case MSG_Close: {
+ pMsg->errCode = sqlite3_close(pMsg->pDb);
+ break;
+ }
+ }
+
+ /* Signal the client that the message has been processed.
+ */
+ pMsg->op = MSG_Done;
+ pthread_mutex_unlock(&pMsg->clientMutex);
+ pthread_cond_signal(&pMsg->clientWakeup);
+ }
+ sqlite3_thread_cleanup();
+ pthread_mutex_unlock(&g.serverMutex);
+ return 0;
+}
+
+/*
+** Start a server thread if one is not already running. If there
+** is aleady a server thread running, the new thread will quickly
+** die and this routine is effectively a no-op.
+*/
+void sqlite3_server_start(void){
+ pthread_t x;
+ int rc;
+ g.serverHalt = 0;
+ rc = pthread_create(&x, 0, sqlite3_server, 0);
+ if( rc==0 ){
+ pthread_detach(x);
+ }
+}
+
+/*
+** If a server thread is running, then stop it. If no server is
+** running, this routine is effectively a no-op.
+**
+** This routine waits until the server has actually stopped before
+** returning.
+*/
+void sqlite3_server_stop(void){
+ g.serverHalt = 1;
+ pthread_cond_broadcast(&g.serverWakeup);
+ pthread_mutex_lock(&g.serverMutex);
+ pthread_mutex_unlock(&g.serverMutex);
+}
+
+#endif /* defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE */
+#endif /* defined(SQLITE_SERVER) */
diff --git a/third_party/sqlite/src/test_tclvar.c b/third_party/sqlite/src/test_tclvar.c
new file mode 100755
index 0000000..6cbec53
--- /dev/null
+++ b/third_party/sqlite/src/test_tclvar.c
@@ -0,0 +1,325 @@
+/*
+** 2006 June 13
+**
+** 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.
+**
+*************************************************************************
+** Code for testing the virtual table interfaces. This code
+** is not included in the SQLite library. It is used for automated
+** testing of the SQLite library.
+**
+** The emphasis of this file is a virtual table that provides
+** access to TCL variables.
+**
+** $Id: test_tclvar.c,v 1.16 2008/07/07 14:50:14 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+typedef struct tclvar_vtab tclvar_vtab;
+typedef struct tclvar_cursor tclvar_cursor;
+
+/*
+** A tclvar virtual-table object
+*/
+struct tclvar_vtab {
+ sqlite3_vtab base;
+ Tcl_Interp *interp;
+};
+
+/* A tclvar cursor object */
+struct tclvar_cursor {
+ sqlite3_vtab_cursor base;
+
+ Tcl_Obj *pList1; /* Result of [info vars ?pattern?] */
+ Tcl_Obj *pList2; /* Result of [array names [lindex $pList1 $i1]] */
+ int i1; /* Current item in pList1 */
+ int i2; /* Current item (if any) in pList2 */
+};
+
+/* Methods for the tclvar module */
+static int tclvarConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ tclvar_vtab *pVtab;
+ static const char zSchema[] =
+ "CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
+ pVtab = sqlite3MallocZero( sizeof(*pVtab) );
+ if( pVtab==0 ) return SQLITE_NOMEM;
+ *ppVtab = &pVtab->base;
+ pVtab->interp = (Tcl_Interp *)pAux;
+ sqlite3_declare_vtab(db, zSchema);
+ return SQLITE_OK;
+}
+/* Note that for this virtual table, the xCreate and xConnect
+** methods are identical. */
+
+static int tclvarDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+/* The xDisconnect and xDestroy methods are also the same */
+
+/*
+** Open a new tclvar cursor.
+*/
+static int tclvarOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ tclvar_cursor *pCur;
+ pCur = sqlite3MallocZero(sizeof(tclvar_cursor));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tclvar cursor.
+*/
+static int tclvarClose(sqlite3_vtab_cursor *cur){
+ tclvar_cursor *pCur = (tclvar_cursor *)cur;
+ if( pCur->pList1 ){
+ Tcl_DecrRefCount(pCur->pList1);
+ }
+ if( pCur->pList2 ){
+ Tcl_DecrRefCount(pCur->pList2);
+ }
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+/*
+** Returns 1 if data is ready, or 0 if not.
+*/
+static int next2(Tcl_Interp *interp, tclvar_cursor *pCur, Tcl_Obj *pObj){
+ Tcl_Obj *p;
+
+ if( pObj ){
+ if( !pCur->pList2 ){
+ p = Tcl_NewStringObj("array names", -1);
+ Tcl_IncrRefCount(p);
+ Tcl_ListObjAppendElement(0, p, pObj);
+ Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(p);
+ pCur->pList2 = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(pCur->pList2);
+ assert( pCur->i2==0 );
+ }else{
+ int n = 0;
+ pCur->i2++;
+ Tcl_ListObjLength(0, pCur->pList2, &n);
+ if( pCur->i2>=n ){
+ Tcl_DecrRefCount(pCur->pList2);
+ pCur->pList2 = 0;
+ pCur->i2 = 0;
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int tclvarNext(sqlite3_vtab_cursor *cur){
+ Tcl_Obj *pObj;
+ int n = 0;
+ int ok = 0;
+
+ tclvar_cursor *pCur = (tclvar_cursor *)cur;
+ Tcl_Interp *interp = ((tclvar_vtab *)(cur->pVtab))->interp;
+
+ Tcl_ListObjLength(0, pCur->pList1, &n);
+ while( !ok && pCur->i1<n ){
+ Tcl_ListObjIndex(0, pCur->pList1, pCur->i1, &pObj);
+ ok = next2(interp, pCur, pObj);
+ if( !ok ){
+ pCur->i1++;
+ }
+ }
+
+ return 0;
+}
+
+static int tclvarFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor;
+ Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp;
+
+ Tcl_Obj *p = Tcl_NewStringObj("info vars", -1);
+ Tcl_IncrRefCount(p);
+
+ assert( argc==0 || argc==1 );
+ if( argc==1 ){
+ Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1);
+ Tcl_ListObjAppendElement(0, p, pArg);
+ }
+ Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
+ pCur->pList1 = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(pCur->pList1);
+ assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 );
+
+ Tcl_DecrRefCount(p);
+ return tclvarNext(pVtabCursor);
+}
+
+static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ Tcl_Obj *p1;
+ Tcl_Obj *p2;
+ const char *z1;
+ const char *z2 = "";
+ tclvar_cursor *pCur = (tclvar_cursor*)cur;
+ Tcl_Interp *interp = ((tclvar_vtab *)cur->pVtab)->interp;
+
+ Tcl_ListObjIndex(interp, pCur->pList1, pCur->i1, &p1);
+ Tcl_ListObjIndex(interp, pCur->pList2, pCur->i2, &p2);
+ z1 = Tcl_GetString(p1);
+ if( p2 ){
+ z2 = Tcl_GetString(p2);
+ }
+ switch (i) {
+ case 0: {
+ sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
+ break;
+ }
+ case 1: {
+ sqlite3_result_text(ctx, z2, -1, SQLITE_TRANSIENT);
+ break;
+ }
+ case 2: {
+ Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
+ sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ *pRowid = 0;
+ return SQLITE_OK;
+}
+
+static int tclvarEof(sqlite3_vtab_cursor *cur){
+ tclvar_cursor *pCur = (tclvar_cursor*)cur;
+ return (pCur->pList2?0:1);
+}
+
+static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int ii;
+
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
+ if( pCons->iColumn==0 && pCons->usable
+ && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ struct sqlite3_index_constraint_usage *pUsage;
+ pUsage = &pIdxInfo->aConstraintUsage[ii];
+ pUsage->omit = 0;
+ pUsage->argvIndex = 1;
+ return SQLITE_OK;
+ }
+ }
+
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
+ if( pCons->iColumn==0 && pCons->usable
+ && pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ struct sqlite3_index_constraint_usage *pUsage;
+ pUsage = &pIdxInfo->aConstraintUsage[ii];
+ pUsage->omit = 1;
+ pUsage->argvIndex = 1;
+ return SQLITE_OK;
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** A virtual table module that provides read-only access to a
+** Tcl global variable namespace.
+*/
+static sqlite3_module tclvarModule = {
+ 0, /* iVersion */
+ tclvarConnect,
+ tclvarConnect,
+ tclvarBestIndex,
+ tclvarDisconnect,
+ tclvarDisconnect,
+ tclvarOpen, /* xOpen - open a cursor */
+ tclvarClose, /* xClose - close a cursor */
+ tclvarFilter, /* xFilter - configure scan constraints */
+ tclvarNext, /* xNext - advance a cursor */
+ tclvarEof, /* xEof - check for end of scan */
+ tclvarColumn, /* xColumn - read data */
+ tclvarRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+};
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+
+/*
+** Register the echo virtual table module.
+*/
+static int register_tclvar_module(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp);
+#endif
+ return TCL_OK;
+}
+
+#endif
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetesttclvar_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ { "register_tclvar_module", register_tclvar_module, 0 },
+#endif
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+ return TCL_OK;
+}
diff --git a/third_party/sqlite/src/test_thread.c b/third_party/sqlite/src/test_thread.c
new file mode 100755
index 0000000..d74470e
--- /dev/null
+++ b/third_party/sqlite/src/test_thread.c
@@ -0,0 +1,332 @@
+/*
+** 2007 September 9
+**
+** 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 contains the implementation of some Tcl commands used to
+** test that sqlite3 database handles may be concurrently accessed by
+** multiple threads. Right now this only works on unix.
+**
+** $Id: test_thread.c,v 1.6 2007/12/13 21:54:11 drh Exp $
+*/
+
+#include "sqliteInt.h"
+#include <tcl.h>
+
+#if SQLITE_THREADSAFE && defined(TCL_THREADS)
+
+#include <errno.h>
+#include <unistd.h>
+
+/*
+** One of these is allocated for each thread created by [sqlthread spawn].
+*/
+typedef struct SqlThread SqlThread;
+struct SqlThread {
+ Tcl_ThreadId parent; /* Thread id of parent thread */
+ Tcl_Interp *interp; /* Parent interpreter */
+ char *zScript; /* The script to execute. */
+ char *zVarname; /* Varname in parent script */
+};
+
+/*
+** A custom Tcl_Event type used by this module. When the event is
+** handled, script zScript is evaluated in interpreter interp. If
+** the evaluation throws an exception (returns TCL_ERROR), then the
+** error is handled by Tcl_BackgroundError(). If no error occurs,
+** the result is simply discarded.
+*/
+typedef struct EvalEvent EvalEvent;
+struct EvalEvent {
+ Tcl_Event base; /* Base class of type Tcl_Event */
+ char *zScript; /* The script to execute. */
+ Tcl_Interp *interp; /* The interpreter to execute it in. */
+};
+
+static Tcl_ObjCmdProc sqlthread_proc;
+int Sqlitetest1_Init(Tcl_Interp *);
+
+/*
+** Handler for events of type EvalEvent.
+*/
+static int tclScriptEvent(Tcl_Event *evPtr, int flags){
+ int rc;
+ EvalEvent *p = (EvalEvent *)evPtr;
+ rc = Tcl_Eval(p->interp, p->zScript);
+ if( rc!=TCL_OK ){
+ Tcl_BackgroundError(p->interp);
+ }
+ return 1;
+}
+
+/*
+** Register an EvalEvent to evaluate the script pScript in the
+** parent interpreter/thread of SqlThread p.
+*/
+static void postToParent(SqlThread *p, Tcl_Obj *pScript){
+ EvalEvent *pEvent;
+ char *zMsg;
+ int nMsg;
+
+ zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
+ pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
+ pEvent->base.nextPtr = 0;
+ pEvent->base.proc = tclScriptEvent;
+ pEvent->zScript = (char *)&pEvent[1];
+ memcpy(pEvent->zScript, zMsg, nMsg+1);
+ pEvent->interp = p->interp;
+
+ Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
+ Tcl_ThreadAlert(p->parent);
+}
+
+/*
+** The main function for threads created with [sqlthread spawn].
+*/
+static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
+ Tcl_Interp *interp;
+ Tcl_Obj *pRes;
+ Tcl_Obj *pList;
+ int rc;
+
+ SqlThread *p = (SqlThread *)pSqlThread;
+
+ interp = Tcl_CreateInterp();
+ Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
+ Sqlitetest1_Init(interp);
+
+ rc = Tcl_Eval(interp, p->zScript);
+ pRes = Tcl_GetObjResult(interp);
+ pList = Tcl_NewObj();
+ Tcl_IncrRefCount(pList);
+ Tcl_IncrRefCount(pRes);
+
+ if( rc!=TCL_OK ){
+ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
+ Tcl_ListObjAppendElement(interp, pList, pRes);
+ postToParent(p, pList);
+ Tcl_DecrRefCount(pList);
+ pList = Tcl_NewObj();
+ }
+
+ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
+ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
+ Tcl_ListObjAppendElement(interp, pList, pRes);
+ postToParent(p, pList);
+
+ ckfree((void *)p);
+ Tcl_DecrRefCount(pList);
+ Tcl_DecrRefCount(pRes);
+ Tcl_DeleteInterp(interp);
+ return;
+}
+
+/*
+** sqlthread spawn VARNAME SCRIPT
+**
+** Spawn a new thread with its own Tcl interpreter and run the
+** specified SCRIPT(s) in it. The thread terminates after running
+** the script. The result of the script is stored in the variable
+** VARNAME.
+**
+** The caller can wait for the script to terminate using [vwait VARNAME].
+*/
+static int sqlthread_spawn(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_ThreadId x;
+ SqlThread *pNew;
+ int rc;
+
+ int nVarname; char *zVarname;
+ int nScript; char *zScript;
+
+ /* Parameters for thread creation */
+ const int nStack = TCL_THREAD_STACK_DEFAULT;
+ const int flags = TCL_THREAD_NOFLAGS;
+
+ assert(objc==4);
+
+ zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
+ zScript = Tcl_GetStringFromObj(objv[3], &nScript);
+
+ pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
+ pNew->zVarname = (char *)&pNew[1];
+ pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
+ memcpy(pNew->zVarname, zVarname, nVarname+1);
+ memcpy(pNew->zScript, zScript, nScript+1);
+ pNew->parent = Tcl_GetCurrentThread();
+ pNew->interp = interp;
+
+ rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
+ if( rc!=TCL_OK ){
+ Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
+ sqlite3_free(pNew);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+** sqlthread parent SCRIPT
+**
+** This can be called by spawned threads only. It sends the specified
+** script back to the parent thread for execution. The result of
+** evaluating the SCRIPT is returned. The parent thread must enter
+** the event loop for this to work - otherwise the caller will
+** block indefinitely.
+**
+** NOTE: At the moment, this doesn't work. FIXME.
+*/
+static int sqlthread_parent(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ EvalEvent *pEvent;
+ char *zMsg;
+ int nMsg;
+ SqlThread *p = (SqlThread *)clientData;
+
+ assert(objc==3);
+ if( p==0 ){
+ Tcl_AppendResult(interp, "no parent thread", 0);
+ return TCL_ERROR;
+ }
+
+ zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
+ pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
+ pEvent->base.nextPtr = 0;
+ pEvent->base.proc = tclScriptEvent;
+ pEvent->zScript = (char *)&pEvent[1];
+ memcpy(pEvent->zScript, zMsg, nMsg+1);
+ pEvent->interp = p->interp;
+ Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
+ Tcl_ThreadAlert(p->parent);
+
+ return TCL_OK;
+}
+
+static int xBusy(void *pArg, int nBusy){
+ sqlite3_sleep(50);
+ return 1; /* Try again... */
+}
+
+/*
+** sqlthread open
+**
+** Open a database handle and return the string representation of
+** the pointer value.
+*/
+static int sqlthread_open(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
+
+ const char *zFilename;
+ sqlite3 *db;
+ int rc;
+ char zBuf[100];
+ extern void Md5_Register(sqlite3*);
+
+ zFilename = Tcl_GetString(objv[2]);
+ rc = sqlite3_open(zFilename, &db);
+ Md5_Register(db);
+ sqlite3_busy_handler(db, xBusy, 0);
+
+ if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
+ Tcl_AppendResult(interp, zBuf, 0);
+
+ return TCL_OK;
+}
+
+
+/*
+** sqlthread open
+**
+** Return the current thread-id (Tcl_GetCurrentThread()) cast to
+** an integer.
+*/
+static int sqlthread_id(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_ThreadId id = Tcl_GetCurrentThread();
+ Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
+ return TCL_OK;
+}
+
+
+/*
+** Dispatch routine for the sub-commands of [sqlthread].
+*/
+static int sqlthread_proc(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ struct SubCommand {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ int nArg;
+ char *zUsage;
+ } aSub[] = {
+ {"parent", sqlthread_parent, 1, "SCRIPT"},
+ {"spawn", sqlthread_spawn, 2, "VARNAME SCRIPT"},
+ {"open", sqlthread_open, 1, "DBNAME"},
+ {"id", sqlthread_id, 0, ""},
+ {0, 0, 0}
+ };
+ struct SubCommand *pSub;
+ int rc;
+ int iIndex;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
+ return TCL_ERROR;
+ }
+
+ rc = Tcl_GetIndexFromObjStruct(
+ interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
+ );
+ if( rc!=TCL_OK ) return rc;
+ pSub = &aSub[iIndex];
+
+ if( objc!=(pSub->nArg+2) ){
+ Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
+ return TCL_ERROR;
+ }
+
+ return pSub->xProc(clientData, interp, objc, objv);
+}
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int SqlitetestThread_Init(Tcl_Interp *interp){
+ Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
+ return TCL_OK;
+}
+#else
+int SqlitetestThread_Init(Tcl_Interp *interp){
+ return TCL_OK;
+}
+#endif
diff --git a/third_party/sqlite/tokenize.c b/third_party/sqlite/src/tokenize.c
index 7a933ff..bea8d5e 100644..100755
--- a/third_party/sqlite/tokenize.c
+++ b/third_party/sqlite/src/tokenize.c
@@ -15,10 +15,9 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.131 2007/07/23 19:31:17 drh Exp $
+** $Id: tokenize.c,v 1.148 2008/07/28 19:34:54 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
#include <stdlib.h>
@@ -86,7 +85,7 @@ const unsigned char ebcdicToAscii[] = {
** But the feature is undocumented.
*/
#ifdef SQLITE_ASCII
-const char sqlite3IsIdChar[] = {
+const char sqlite3IsAsciiIdChar[] = {
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
@@ -95,10 +94,10 @@ const char sqlite3IsIdChar[] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
};
-#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsIdChar[c-0x20]))
+#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsAsciiIdChar[c-0x20]))
#endif
#ifdef SQLITE_EBCDIC
-const char sqlite3IsIdChar[] = {
+const char sqlite3IsEbcdicIdChar[] = {
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 4x */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, /* 5x */
@@ -113,7 +112,7 @@ const char sqlite3IsIdChar[] = {
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Ex */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* Fx */
};
-#define IdChar(C) (((c=C)>=0x42 && sqlite3IsIdChar[c-0x40]))
+#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
#endif
@@ -121,7 +120,7 @@ const char sqlite3IsIdChar[] = {
** Return the length of the token that begins at z[0].
** Store the token type in *tokenType before returning.
*/
-static int getToken(const unsigned char *z, int *tokenType){
+int sqlite3GetToken(const unsigned char *z, int *tokenType){
int i, c;
switch( *z ){
case ' ': case '\t': case '\n': case '\f': case '\r': {
@@ -293,7 +292,7 @@ static int getToken(const unsigned char *z, int *tokenType){
}
case '[': {
for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
- *tokenType = TK_ID;
+ *tokenType = c==']' ? TK_ID : TK_ILLEGAL;
return i;
}
case '?': {
@@ -345,19 +344,14 @@ static int getToken(const unsigned char *z, int *tokenType){
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case 'x': case 'X': {
- if( (c=z[1])=='\'' || c=='"' ){
- int delim = c;
+ if( z[1]=='\'' ){
*tokenType = TK_BLOB;
- for(i=2; (c=z[i])!=0; i++){
- if( c==delim ){
- if( i%2 ) *tokenType = TK_ILLEGAL;
- break;
- }
+ for(i=2; (c=z[i])!=0 && c!='\''; i++){
if( !isxdigit(c) ){
*tokenType = TK_ILLEGAL;
- return i;
}
}
+ if( i%2 || !c ) *tokenType = TK_ILLEGAL;
if( c ) i++;
return i;
}
@@ -376,16 +370,13 @@ static int getToken(const unsigned char *z, int *tokenType){
*tokenType = TK_ILLEGAL;
return 1;
}
-int sqlite3GetToken(const unsigned char *z, int *tokenType){
- return getToken(z, tokenType);
-}
/*
** Run the parser on the given SQL string. The parser structure is
** passed in. An SQLITE_ status code is returned. If an error occurs
-** and pzErrMsg!=NULL then an error message might be written into
-** memory obtained from malloc() and *pzErrMsg made to point to that
-** error message. Or maybe not.
+** then an and attempt is made to write an error message into
+** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that
+** error message.
*/
int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
int nErr = 0;
@@ -394,14 +385,18 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
int tokenType;
int lastTokenParsed = -1;
sqlite3 *db = pParse->db;
+ int mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
if( db->activeVdbeCnt==0 ){
db->u1.isInterrupted = 0;
}
pParse->rc = SQLITE_OK;
+ pParse->zTail = pParse->zSql = zSql;
i = 0;
- pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3MallocX);
+ assert( pzErrMsg!=0 );
+ pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
if( pEngine==0 ){
+ db->mallocFailed = 1;
return SQLITE_NOMEM;
}
assert( pParse->sLastToken.dyn==0 );
@@ -411,14 +406,13 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
assert( pParse->nVarExpr==0 );
assert( pParse->nVarExprAlloc==0 );
assert( pParse->apVarExpr==0 );
- pParse->zTail = pParse->zSql = zSql;
- while( !sqlite3MallocFailed() && zSql[i]!=0 ){
+ while( !db->mallocFailed && zSql[i]!=0 ){
assert( i>=0 );
pParse->sLastToken.z = (u8*)&zSql[i];
assert( pParse->sLastToken.dyn==0 );
- pParse->sLastToken.n = getToken((unsigned char*)&zSql[i],&tokenType);
+ pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
i += pParse->sLastToken.n;
- if( i>SQLITE_MAX_SQL_LENGTH ){
+ if( i>mxSqlLen ){
pParse->rc = SQLITE_TOOBIG;
break;
}
@@ -427,17 +421,15 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
case TK_COMMENT: {
if( db->u1.isInterrupted ){
pParse->rc = SQLITE_INTERRUPT;
- sqlite3SetString(pzErrMsg, "interrupt", (char*)0);
+ sqlite3SetString(pzErrMsg, db, "interrupt");
goto abort_parse;
}
break;
}
case TK_ILLEGAL: {
- if( pzErrMsg ){
- sqliteFree(*pzErrMsg);
- *pzErrMsg = sqlite3MPrintf("unrecognized token: \"%T\"",
- &pParse->sLastToken);
- }
+ sqlite3DbFree(db, *pzErrMsg);
+ *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"",
+ &pParse->sLastToken);
nErr++;
goto abort_parse;
}
@@ -463,21 +455,26 @@ abort_parse:
}
sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
}
- sqlite3ParserFree(pEngine, sqlite3FreeX);
- if( sqlite3MallocFailed() ){
+#ifdef YYTRACKMAXSTACKDEPTH
+ sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK,
+ sqlite3ParserStackPeak(pEngine)
+ );
+#endif /* YYDEBUG */
+ sqlite3ParserFree(pEngine, sqlite3_free);
+ if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM;
}
if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
- sqlite3SetString(&pParse->zErrMsg, sqlite3ErrStr(pParse->rc), (char*)0);
+ sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc));
}
if( pParse->zErrMsg ){
- if( pzErrMsg && *pzErrMsg==0 ){
+ if( *pzErrMsg==0 ){
*pzErrMsg = pParse->zErrMsg;
}else{
- sqliteFree(pParse->zErrMsg);
+ sqlite3DbFree(db, pParse->zErrMsg);
}
pParse->zErrMsg = 0;
- if( !nErr ) nErr++;
+ nErr++;
}
if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){
sqlite3VdbeDelete(pParse->pVdbe);
@@ -485,11 +482,14 @@ abort_parse:
}
#ifndef SQLITE_OMIT_SHARED_CACHE
if( pParse->nested==0 ){
- sqliteFree(pParse->aTableLock);
+ sqlite3DbFree(db, pParse->aTableLock);
pParse->aTableLock = 0;
pParse->nTableLock = 0;
}
#endif
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3DbFree(db, pParse->apVtabLock);
+#endif
if( !IN_DECLARE_VTAB ){
/* If the pParse->declareVtab flag is set, do not delete any table
@@ -499,8 +499,8 @@ abort_parse:
sqlite3DeleteTable(pParse->pNewTable);
}
- sqlite3DeleteTrigger(pParse->pNewTrigger);
- sqliteFree(pParse->apVarExpr);
+ sqlite3DeleteTrigger(db, pParse->pNewTrigger);
+ sqlite3DbFree(db, pParse->apVarExpr);
if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
pParse->rc = SQLITE_ERROR;
}
diff --git a/third_party/sqlite/trigger.c b/third_party/sqlite/src/trigger.c
index 5d5ec8d..760c708 100644..100755
--- a/third_party/sqlite/trigger.c
+++ b/third_party/sqlite/src/trigger.c
@@ -8,7 +8,9 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-*
+**
+**
+** $Id: trigger.c,v 1.128 2008/07/28 19:34:54 drh Exp $
*/
#include "sqliteInt.h"
@@ -16,18 +18,18 @@
/*
** Delete a linked list of TriggerStep structures.
*/
-void sqlite3DeleteTriggerStep(TriggerStep *pTriggerStep){
+void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
while( pTriggerStep ){
TriggerStep * pTmp = pTriggerStep;
pTriggerStep = pTriggerStep->pNext;
- if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
- sqlite3ExprDelete(pTmp->pWhere);
- sqlite3ExprListDelete(pTmp->pExprList);
- sqlite3SelectDelete(pTmp->pSelect);
- sqlite3IdListDelete(pTmp->pIdList);
+ if( pTmp->target.dyn ) sqlite3DbFree(db, (char*)pTmp->target.z);
+ sqlite3ExprDelete(db, pTmp->pWhere);
+ sqlite3ExprListDelete(db, pTmp->pExprList);
+ sqlite3SelectDelete(db, pTmp->pSelect);
+ sqlite3IdListDelete(db, pTmp->pIdList);
- sqliteFree(pTmp);
+ sqlite3DbFree(db, pTmp);
}
}
@@ -83,7 +85,7 @@ void sqlite3BeginTrigger(
** If sqlite3SrcListLookup() returns 0, indicating the table does not
** exist, the error is caught by the block below.
*/
- if( !pTableName || sqlite3MallocFailed() ){
+ if( !pTableName || db->mallocFailed ){
goto trigger_cleanup;
}
pTab = sqlite3SrcListLookup(pParse, pTableName);
@@ -92,7 +94,7 @@ void sqlite3BeginTrigger(
}
/* Ensure the table name matches database name and that the table exists */
- if( sqlite3MallocFailed() ) goto trigger_cleanup;
+ if( db->mallocFailed ) goto trigger_cleanup;
assert( pTableName->nSrc==1 );
if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
sqlite3FixSrcList(&sFix, pTableName) ){
@@ -110,7 +112,7 @@ void sqlite3BeginTrigger(
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
- zName = sqlite3NameFromToken(pName);
+ zName = sqlite3NameFromToken(db, pName);
if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto trigger_cleanup;
}
@@ -168,28 +170,28 @@ void sqlite3BeginTrigger(
}
/* Build the Trigger object */
- pTrigger = (Trigger*)sqliteMalloc(sizeof(Trigger));
+ pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
if( pTrigger==0 ) goto trigger_cleanup;
pTrigger->name = zName;
zName = 0;
- pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
+ pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
pTrigger->pSchema = db->aDb[iDb].pSchema;
pTrigger->pTabSchema = pTab->pSchema;
pTrigger->op = op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
- pTrigger->pWhen = sqlite3ExprDup(pWhen);
- pTrigger->pColumns = sqlite3IdListDup(pColumns);
- sqlite3TokenCopy(&pTrigger->nameToken,pName);
+ pTrigger->pWhen = sqlite3ExprDup(db, pWhen);
+ pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
+ sqlite3TokenCopy(db, &pTrigger->nameToken,pName);
assert( pParse->pNewTrigger==0 );
pParse->pNewTrigger = pTrigger;
trigger_cleanup:
- sqliteFree(zName);
- sqlite3SrcListDelete(pTableName);
- sqlite3IdListDelete(pColumns);
- sqlite3ExprDelete(pWhen);
+ sqlite3DbFree(db, zName);
+ sqlite3SrcListDelete(db, pTableName);
+ sqlite3IdListDelete(db, pColumns);
+ sqlite3ExprDelete(db, pWhen);
if( !pParse->pNewTrigger ){
- sqlite3DeleteTrigger(pTrigger);
+ sqlite3DeleteTrigger(db, pTrigger);
}else{
assert( pParse->pNewTrigger==pTrigger );
}
@@ -227,34 +229,23 @@ void sqlite3FinishTrigger(
** build the sqlite_master entry
*/
if( !db->init.busy ){
- static const VdbeOpList insertTrig[] = {
- { OP_NewRowid, 0, 0, 0 },
- { OP_String8, 0, 0, "trigger" },
- { OP_String8, 0, 0, 0 }, /* 2: trigger name */
- { OP_String8, 0, 0, 0 }, /* 3: table name */
- { OP_Integer, 0, 0, 0 },
- { OP_String8, 0, 0, "CREATE TRIGGER "},
- { OP_String8, 0, 0, 0 }, /* 6: SQL */
- { OP_Concat, 0, 0, 0 },
- { OP_MakeRecord, 5, 0, "aaada" },
- { OP_Insert, 0, 0, 0 },
- };
- int addr;
Vdbe *v;
+ char *z;
/* Make an entry in the sqlite_master table */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup;
sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3OpenMasterTable(pParse, iDb);
- addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
- sqlite3VdbeChangeP3(v, addr+2, pTrig->name, 0);
- sqlite3VdbeChangeP3(v, addr+3, pTrig->table, 0);
- sqlite3VdbeChangeP3(v, addr+6, (char*)pAll->z, pAll->n);
- sqlite3ChangeCookie(db, v, iDb);
- sqlite3VdbeAddOp(v, OP_Close, 0, 0);
- sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
- sqlite3MPrintf("type='trigger' AND name='%q'", pTrig->name), P3_DYNAMIC);
+ z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
+ sqlite3NestedParse(pParse,
+ "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
+ db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrig->name,
+ pTrig->table, z);
+ sqlite3DbFree(db, z);
+ sqlite3ChangeCookie(pParse, iDb);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(
+ db, "type='trigger' AND name='%q'", pTrig->name), P4_DYNAMIC
+ );
}
if( db->init.busy ){
@@ -264,7 +255,8 @@ void sqlite3FinishTrigger(
pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash,
pTrig->name, strlen(pTrig->name), pTrig);
if( pDel ){
- assert( sqlite3MallocFailed() && pDel==pTrig );
+ assert( pDel==pTrig );
+ db->mallocFailed = 1;
goto triggerfinish_cleanup;
}
n = strlen(pTrig->table) + 1;
@@ -276,44 +268,44 @@ void sqlite3FinishTrigger(
}
triggerfinish_cleanup:
- sqlite3DeleteTrigger(pTrig);
+ sqlite3DeleteTrigger(db, pTrig);
assert( !pParse->pNewTrigger );
- sqlite3DeleteTriggerStep(pStepList);
+ sqlite3DeleteTriggerStep(db, pStepList);
}
/*
** Make a copy of all components of the given trigger step. This has
** the effect of copying all Expr.token.z values into memory obtained
-** from sqliteMalloc(). As initially created, the Expr.token.z values
+** from sqlite3_malloc(). As initially created, the Expr.token.z values
** all point to the input string that was fed to the parser. But that
** string is ephemeral - it will go away as soon as the sqlite3_exec()
** call that started the parser exits. This routine makes a persistent
** copy of all the Expr.token.z strings so that the TriggerStep structure
** will be valid even after the sqlite3_exec() call returns.
*/
-static void sqlitePersistTriggerStep(TriggerStep *p){
+static void sqlitePersistTriggerStep(sqlite3 *db, TriggerStep *p){
if( p->target.z ){
- p->target.z = (u8*)sqliteStrNDup((char*)p->target.z, p->target.n);
+ p->target.z = (u8*)sqlite3DbStrNDup(db, (char*)p->target.z, p->target.n);
p->target.dyn = 1;
}
if( p->pSelect ){
- Select *pNew = sqlite3SelectDup(p->pSelect);
- sqlite3SelectDelete(p->pSelect);
+ Select *pNew = sqlite3SelectDup(db, p->pSelect);
+ sqlite3SelectDelete(db, p->pSelect);
p->pSelect = pNew;
}
if( p->pWhere ){
- Expr *pNew = sqlite3ExprDup(p->pWhere);
- sqlite3ExprDelete(p->pWhere);
+ Expr *pNew = sqlite3ExprDup(db, p->pWhere);
+ sqlite3ExprDelete(db, p->pWhere);
p->pWhere = pNew;
}
if( p->pExprList ){
- ExprList *pNew = sqlite3ExprListDup(p->pExprList);
- sqlite3ExprListDelete(p->pExprList);
+ ExprList *pNew = sqlite3ExprListDup(db, p->pExprList);
+ sqlite3ExprListDelete(db, p->pExprList);
p->pExprList = pNew;
}
if( p->pIdList ){
- IdList *pNew = sqlite3IdListDup(p->pIdList);
- sqlite3IdListDelete(p->pIdList);
+ IdList *pNew = sqlite3IdListDup(db, p->pIdList);
+ sqlite3IdListDelete(db, p->pIdList);
p->pIdList = pNew;
}
}
@@ -325,17 +317,17 @@ static void sqlitePersistTriggerStep(TriggerStep *p){
** The parser calls this routine when it finds a SELECT statement in
** body of a TRIGGER.
*/
-TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){
- TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
+ TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
if( pTriggerStep==0 ) {
- sqlite3SelectDelete(pSelect);
+ sqlite3SelectDelete(db, pSelect);
return 0;
}
pTriggerStep->op = TK_SELECT;
pTriggerStep->pSelect = pSelect;
pTriggerStep->orconf = OE_Default;
- sqlitePersistTriggerStep(pTriggerStep);
+ sqlitePersistTriggerStep(db, pTriggerStep);
return pTriggerStep;
}
@@ -348,17 +340,19 @@ TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){
** body of a trigger.
*/
TriggerStep *sqlite3TriggerInsertStep(
+ sqlite3 *db, /* The database connection */
Token *pTableName, /* Name of the table into which we insert */
IdList *pColumn, /* List of columns in pTableName to insert into */
ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
Select *pSelect, /* A SELECT statement that supplies values */
int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
){
- TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ TriggerStep *pTriggerStep;
assert(pEList == 0 || pSelect == 0);
- assert(pEList != 0 || pSelect != 0);
+ assert(pEList != 0 || pSelect != 0 || db->mallocFailed);
+ pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
if( pTriggerStep ){
pTriggerStep->op = TK_INSERT;
pTriggerStep->pSelect = pSelect;
@@ -366,11 +360,11 @@ TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->pIdList = pColumn;
pTriggerStep->pExprList = pEList;
pTriggerStep->orconf = orconf;
- sqlitePersistTriggerStep(pTriggerStep);
+ sqlitePersistTriggerStep(db, pTriggerStep);
}else{
- sqlite3IdListDelete(pColumn);
- sqlite3ExprListDelete(pEList);
- sqlite3SelectDup(pSelect);
+ sqlite3IdListDelete(db, pColumn);
+ sqlite3ExprListDelete(db, pEList);
+ sqlite3SelectDelete(db, pSelect);
}
return pTriggerStep;
@@ -382,15 +376,16 @@ TriggerStep *sqlite3TriggerInsertStep(
** sees an UPDATE statement inside the body of a CREATE TRIGGER.
*/
TriggerStep *sqlite3TriggerUpdateStep(
+ sqlite3 *db, /* The database connection */
Token *pTableName, /* Name of the table to be updated */
ExprList *pEList, /* The SET clause: list of column and new values */
Expr *pWhere, /* The WHERE clause */
int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
){
- TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
if( pTriggerStep==0 ){
- sqlite3ExprListDelete(pEList);
- sqlite3ExprDelete(pWhere);
+ sqlite3ExprListDelete(db, pEList);
+ sqlite3ExprDelete(db, pWhere);
return 0;
}
@@ -399,7 +394,7 @@ TriggerStep *sqlite3TriggerUpdateStep(
pTriggerStep->pExprList = pEList;
pTriggerStep->pWhere = pWhere;
pTriggerStep->orconf = orconf;
- sqlitePersistTriggerStep(pTriggerStep);
+ sqlitePersistTriggerStep(db, pTriggerStep);
return pTriggerStep;
}
@@ -409,10 +404,14 @@ TriggerStep *sqlite3TriggerUpdateStep(
** a pointer to that trigger step. The parser calls this routine when it
** sees a DELETE statement inside the body of a CREATE TRIGGER.
*/
-TriggerStep *sqlite3TriggerDeleteStep(Token *pTableName, Expr *pWhere){
- TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+TriggerStep *sqlite3TriggerDeleteStep(
+ sqlite3 *db, /* Database connection */
+ Token *pTableName, /* The table from which rows are deleted */
+ Expr *pWhere /* The WHERE clause */
+){
+ TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
if( pTriggerStep==0 ){
- sqlite3ExprDelete(pWhere);
+ sqlite3ExprDelete(db, pWhere);
return 0;
}
@@ -420,7 +419,7 @@ TriggerStep *sqlite3TriggerDeleteStep(Token *pTableName, Expr *pWhere){
pTriggerStep->target = *pTableName;
pTriggerStep->pWhere = pWhere;
pTriggerStep->orconf = OE_Default;
- sqlitePersistTriggerStep(pTriggerStep);
+ sqlitePersistTriggerStep(db, pTriggerStep);
return pTriggerStep;
}
@@ -428,15 +427,15 @@ TriggerStep *sqlite3TriggerDeleteStep(Token *pTableName, Expr *pWhere){
/*
** Recursively delete a Trigger structure
*/
-void sqlite3DeleteTrigger(Trigger *pTrigger){
+void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
if( pTrigger==0 ) return;
- sqlite3DeleteTriggerStep(pTrigger->step_list);
- sqliteFree(pTrigger->name);
- sqliteFree(pTrigger->table);
- sqlite3ExprDelete(pTrigger->pWhen);
- sqlite3IdListDelete(pTrigger->pColumns);
- if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z);
- sqliteFree(pTrigger);
+ sqlite3DeleteTriggerStep(db, pTrigger->step_list);
+ sqlite3DbFree(db, pTrigger->name);
+ sqlite3DbFree(db, pTrigger->table);
+ sqlite3ExprDelete(db, pTrigger->pWhen);
+ sqlite3IdListDelete(db, pTrigger->pColumns);
+ if( pTrigger->nameToken.dyn ) sqlite3DbFree(db, (char*)pTrigger->nameToken.z);
+ sqlite3DbFree(db, pTrigger);
}
/*
@@ -455,7 +454,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
int nName;
sqlite3 *db = pParse->db;
- if( sqlite3MallocFailed() ) goto drop_trigger_cleanup;
+ if( db->mallocFailed ) goto drop_trigger_cleanup;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto drop_trigger_cleanup;
}
@@ -479,7 +478,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
sqlite3DropTriggerPtr(pParse, pTrigger);
drop_trigger_cleanup:
- sqlite3SrcListDelete(pName);
+ sqlite3SrcListDelete(db, pName);
}
/*
@@ -526,12 +525,12 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
int base;
static const VdbeOpList dropTrigger[] = {
{ OP_Rewind, 0, ADDR(9), 0},
- { OP_String8, 0, 0, 0}, /* 1 */
- { OP_Column, 0, 1, 0},
- { OP_Ne, 0, ADDR(8), 0},
- { OP_String8, 0, 0, "trigger"},
- { OP_Column, 0, 0, 0},
- { OP_Ne, 0, ADDR(8), 0},
+ { OP_String8, 0, 1, 0}, /* 1 */
+ { OP_Column, 0, 1, 2},
+ { OP_Ne, 2, ADDR(8), 1},
+ { OP_String8, 0, 1, 0}, /* 4: "trigger" */
+ { OP_Column, 0, 0, 2},
+ { OP_Ne, 2, ADDR(8), 1},
{ OP_Delete, 0, 0, 0},
{ OP_Next, 0, ADDR(1), 0}, /* 8 */
};
@@ -539,10 +538,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(pParse, iDb);
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
- sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0);
- sqlite3ChangeCookie(db, v, iDb);
- sqlite3VdbeAddOp(v, OP_Close, 0, 0);
- sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrigger->name, 0);
+ sqlite3VdbeChangeP4(v, base+1, pTrigger->name, 0);
+ sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
+ sqlite3ChangeCookie(pParse, iDb);
+ sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
+ sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0);
}
}
@@ -570,7 +570,7 @@ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
}
assert(cc);
}
- sqlite3DeleteTrigger(pTrigger);
+ sqlite3DeleteTrigger(db, pTrigger);
db->flags |= SQLITE_InternChanges;
}
}
@@ -644,9 +644,9 @@ static SrcList *targetSrcList(
assert( iDb<pParse->db->nDb );
sDb.z = (u8*)pParse->db->aDb[iDb].zName;
sDb.n = strlen((char*)sDb.z);
- pSrc = sqlite3SrcListAppend(0, &sDb, &pStep->target);
+ pSrc = sqlite3SrcListAppend(pParse->db, 0, &sDb, &pStep->target);
} else {
- pSrc = sqlite3SrcListAppend(0, &pStep->target, 0);
+ pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0);
}
return pSrc;
}
@@ -663,51 +663,56 @@ static int codeTriggerProgram(
TriggerStep * pTriggerStep = pStepList;
int orconf;
Vdbe *v = pParse->pVdbe;
+ sqlite3 *db = pParse->db;
assert( pTriggerStep!=0 );
assert( v!=0 );
- sqlite3VdbeAddOp(v, OP_ContextPush, 0, 0);
- VdbeComment((v, "# begin trigger %s", pStepList->pTrig->name));
+ sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0);
+ VdbeComment((v, "begin trigger %s", pStepList->pTrig->name));
while( pTriggerStep ){
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
pParse->trigStack->orconf = orconf;
switch( pTriggerStep->op ){
case TK_SELECT: {
- Select *ss = sqlite3SelectDup(pTriggerStep->pSelect);
+ Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect);
if( ss ){
+ SelectDest dest;
+
+ sqlite3SelectDestInit(&dest, SRT_Discard, 0);
sqlite3SelectResolve(pParse, ss, 0);
- sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0);
- sqlite3SelectDelete(ss);
+ sqlite3Select(pParse, ss, &dest, 0, 0, 0);
+ sqlite3SelectDelete(db, ss);
}
break;
}
case TK_UPDATE: {
SrcList *pSrc;
pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
sqlite3Update(pParse, pSrc,
- sqlite3ExprListDup(pTriggerStep->pExprList),
- sqlite3ExprDup(pTriggerStep->pWhere), orconf);
- sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ sqlite3ExprListDup(db, pTriggerStep->pExprList),
+ sqlite3ExprDup(db, pTriggerStep->pWhere), orconf);
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break;
}
case TK_INSERT: {
SrcList *pSrc;
pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
sqlite3Insert(pParse, pSrc,
- sqlite3ExprListDup(pTriggerStep->pExprList),
- sqlite3SelectDup(pTriggerStep->pSelect),
- sqlite3IdListDup(pTriggerStep->pIdList), orconf);
- sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ sqlite3ExprListDup(db, pTriggerStep->pExprList),
+ sqlite3SelectDup(db, pTriggerStep->pSelect),
+ sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break;
}
case TK_DELETE: {
SrcList *pSrc;
- sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3DeleteFrom(pParse, pSrc, sqlite3ExprDup(pTriggerStep->pWhere));
- sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ sqlite3DeleteFrom(pParse, pSrc,
+ sqlite3ExprDup(db, pTriggerStep->pWhere));
+ sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break;
}
default:
@@ -715,8 +720,8 @@ static int codeTriggerProgram(
}
pTriggerStep = pTriggerStep->pNext;
}
- sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0);
- VdbeComment((v, "# end trigger %s", pStepList->pTrig->name));
+ sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
+ VdbeComment((v, "end trigger %s", pStepList->pTrig->name));
return 0;
}
@@ -740,6 +745,13 @@ static int codeTriggerProgram(
** a row containing values to be substituted for old.* expressions in the
** trigger program(s).
**
+** If they are not NULL, the piOldColMask and piNewColMask output variables
+** are set to values that describe the columns used by the trigger program
+** in the OLD.* and NEW.* tables respectively. If column N of the
+** pseudo-table is read at least once, the corresponding bit of the output
+** mask is set. If a column with an index greater than 32 is read, the
+** output mask is set to the special value 0xffffffff.
+**
*/
int sqlite3CodeRowTrigger(
Parse *pParse, /* Parse context */
@@ -750,11 +762,17 @@ int sqlite3CodeRowTrigger(
int newIdx, /* The indice of the "new" row to access */
int oldIdx, /* The indice of the "old" row to access */
int orconf, /* ON CONFLICT policy */
- int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */
+ int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */
+ u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */
+ u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */
){
Trigger *p;
+ sqlite3 *db = pParse->db;
TriggerStack trigStackEntry;
+ trigStackEntry.oldColMask = 0;
+ trigStackEntry.newColMask = 0;
+
assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );
@@ -767,7 +785,7 @@ int sqlite3CodeRowTrigger(
if(
p->op==op &&
p->tr_tm==tr_tm &&
- (p->pSchema==p->pTabSchema || p->pSchema==pParse->db->aDb[1].pSchema) &&
+ (p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema) &&
(op!=TK_UPDATE||!p->pColumns||checkColumnOverLap(p->pColumns,pChanges))
){
TriggerStack *pS; /* Pointer to trigger-stack entry */
@@ -790,6 +808,11 @@ int sqlite3CodeRowTrigger(
AuthContext sContext;
NameContext sNC;
+#ifndef SQLITE_OMIT_TRACE
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0,
+ sqlite3MPrintf(db, "-- TRIGGER %s", p->name),
+ P4_DYNAMIC);
+#endif
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
@@ -805,14 +828,14 @@ int sqlite3CodeRowTrigger(
/* code the WHEN clause */
endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
- whenExpr = sqlite3ExprDup(p->pWhen);
- if( sqlite3ExprResolveNames(&sNC, whenExpr) ){
+ whenExpr = sqlite3ExprDup(db, p->pWhen);
+ if( db->mallocFailed || sqlite3ExprResolveNames(&sNC, whenExpr) ){
pParse->trigStack = trigStackEntry.pNext;
- sqlite3ExprDelete(whenExpr);
+ sqlite3ExprDelete(db, whenExpr);
return 1;
}
- sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1);
- sqlite3ExprDelete(whenExpr);
+ sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL);
+ sqlite3ExprDelete(db, whenExpr);
codeTriggerProgram(pParse, p->step_list, orconf);
@@ -823,6 +846,8 @@ int sqlite3CodeRowTrigger(
sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);
}
}
+ if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask;
+ if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask;
return 0;
}
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
diff --git a/third_party/sqlite/update.c b/third_party/sqlite/src/update.c
index 0a4eddc..7647d4a 100644..100755
--- a/third_party/sqlite/update.c
+++ b/third_party/sqlite/src/update.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.138 2007/06/25 16:29:34 danielk1977 Exp $
+** $Id: update.c,v 1.181 2008/07/28 19:34:54 drh Exp $
*/
#include "sqliteInt.h"
@@ -31,7 +31,7 @@ static void updateVirtualTable(
/*
** The most recently coded instruction was an OP_Column to retrieve the
-** i-th column of table pTab. This routine sets the P3 parameter of the
+** i-th column of table pTab. This routine sets the P4 parameter of the
** OP_Column to the default value, if any.
**
** The default value of a column is specified by a DEFAULT clause in the
@@ -39,9 +39,9 @@ static void updateVirtualTable(
** was created, or added later to the table definition by an ALTER TABLE
** command. If the latter, then the row-records in the table btree on disk
** may not contain a value for the column and the default value, taken
-** from the P3 parameter of the OP_Column instruction, is returned instead.
+** from the P4 parameter of the OP_Column instruction, is returned instead.
** If the former, then all row-records are guaranteed to include a value
-** for the column and the P3 value is not required.
+** for the column and the P4 value is not required.
**
** Column definitions created by an ALTER TABLE command may only have
** literal default values specified: a number, null or a string. (If a more
@@ -49,7 +49,7 @@ static void updateVirtualTable(
** when the ALTER TABLE is executed and one of the literal values written
** into the sqlite_master table.)
**
-** Therefore, the P3 parameter is only required if the default value for
+** Therefore, the P4 parameter is only required if the default value for
** the column is a literal number, string or null. The sqlite3ValueFromExpr()
** function is capable of transforming these types of expressions into
** sqlite3_value objects.
@@ -59,12 +59,12 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i){
sqlite3_value *pValue;
u8 enc = ENC(sqlite3VdbeDb(v));
Column *pCol = &pTab->aCol[i];
+ VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
assert( i<pTab->nCol );
- sqlite3ValueFromExpr(pCol->pDflt, enc, pCol->affinity, &pValue);
+ sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc,
+ pCol->affinity, &pValue);
if( pValue ){
- sqlite3VdbeChangeP3(v, -1, (const char *)pValue, P3_MEM);
- }else{
- VdbeComment((v, "# %s.%s", pTab->zName, pCol->zName));
+ sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM);
}
}
}
@@ -90,11 +90,9 @@ void sqlite3Update(
Vdbe *v; /* The virtual database engine */
Index *pIdx; /* For looping over indices */
int nIdx; /* Number of indices that need updating */
- int nIdxTotal; /* Total number of indices */
int iCur; /* VDBE Cursor number of pTab */
sqlite3 *db; /* The database structure */
- Index **apIdx = 0; /* An array of indices that need updating too */
- char *aIdxUsed = 0; /* aIdxUsed[i]==1 if the i-th index is used */
+ int *aRegIdx = 0; /* One register assigned to each index to be updated */
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
@@ -104,21 +102,34 @@ void sqlite3Update(
AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */
int iDb; /* Database containing the table being updated */
- int memCnt = 0; /* Memory cell used for counting rows changed */
+ int j1; /* Addresses of jump instructions */
+ int okOnePass; /* True for one-pass algorithm without the FIFO */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* Trying to update a view */
int triggers_exist = 0; /* True if any row triggers exist */
#endif
+ int iBeginAfterTrigger; /* Address of after trigger program */
+ int iEndAfterTrigger; /* Exit of after trigger program */
+ int iBeginBeforeTrigger; /* Address of before trigger program */
+ int iEndBeforeTrigger; /* Exit of before trigger program */
+ u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
+ u32 new_col_mask = 0; /* Mask of NEW.* columns in use */
int newIdx = -1; /* index of trigger "new" temp table */
int oldIdx = -1; /* index of trigger "old" temp table */
+ /* Register Allocations */
+ int regRowCount = 0; /* A count of rows changed */
+ int regOldRowid; /* The old rowid */
+ int regNewRowid; /* The new rowid */
+ int regData; /* New data for the row */
+
sContext.pParse = 0;
- if( pParse->nErr || sqlite3MallocFailed() ){
+ db = pParse->db;
+ if( pParse->nErr || db->mallocFailed ){
goto update_cleanup;
}
- db = pParse->db;
assert( pTabList->nSrc==1 );
/* Locate the table which we want to update.
@@ -148,7 +159,7 @@ void sqlite3Update(
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto update_cleanup;
}
- aXRef = sqliteMallocRaw( sizeof(int) * pTab->nCol );
+ aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol );
if( aXRef==0 ) goto update_cleanup;
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
@@ -219,42 +230,48 @@ void sqlite3Update(
#endif
}
- /* Allocate memory for the array apIdx[] and fill it with pointers to every
- ** index that needs to be updated. Indices only need updating if their
- ** key includes one of the columns named in pChanges or if the record
- ** number of the original table entry is changing.
+ /* Allocate memory for the array aRegIdx[]. There is one entry in the
+ ** array for each index associated with table being updated. Fill in
+ ** the value with a register number for indices that are to be used
+ ** and with zero for unused indices.
*/
- for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){
- if( chngRowid ){
- i = 0;
- }else {
- for(i=0; i<pIdx->nColumn; i++){
- if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
- }
- }
- if( i<pIdx->nColumn ) nIdx++;
- }
- if( nIdxTotal>0 ){
- apIdx = sqliteMallocRaw( sizeof(Index*) * nIdx + nIdxTotal );
- if( apIdx==0 ) goto update_cleanup;
- aIdxUsed = (char*)&apIdx[nIdx];
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
+ if( nIdx>0 ){
+ aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
+ if( aRegIdx==0 ) goto update_cleanup;
}
- for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ int reg;
if( chngRowid ){
- i = 0;
+ reg = ++pParse->nMem;
}else{
+ reg = 0;
for(i=0; i<pIdx->nColumn; i++){
- if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
+ if( aXRef[pIdx->aiColumn[i]]>=0 ){
+ reg = ++pParse->nMem;
+ break;
+ }
}
}
- if( i<pIdx->nColumn ){
- apIdx[nIdx++] = pIdx;
- aIdxUsed[j] = 1;
- }else{
- aIdxUsed[j] = 0;
- }
+ aRegIdx[j] = reg;
}
+ /* Allocate a block of register used to store the change record
+ ** sent to sqlite3GenerateConstraintChecks(). There are either
+ ** one or two registers for holding the rowid. One rowid register
+ ** is used if chngRowid is false and two are used if chngRowid is
+ ** true. Following these are pTab->nCol register holding column
+ ** data.
+ */
+ regOldRowid = regNewRowid = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol + 1;
+ if( chngRowid ){
+ regNewRowid++;
+ pParse->nMem++;
+ }
+ regData = regNewRowid+1;
+
+
/* Begin generating code.
*/
v = sqlite3GetVdbe(pParse);
@@ -273,38 +290,67 @@ void sqlite3Update(
}
#endif
- /* Resolve the column names in all the expressions in the
- ** WHERE clause.
- */
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
- goto update_cleanup;
- }
-
/* Start the view context
*/
if( isView ){
sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
}
+ /* Generate the code for triggers.
+ */
+ if( triggers_exist ){
+ int iGoto;
+
+ /* Create pseudo-tables for NEW and OLD
+ */
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
+
+ iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ addr = sqlite3VdbeMakeLabel(v);
+ iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
+ if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
+ newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
+ goto update_cleanup;
+ }
+ iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
+ if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab,
+ newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
+ goto update_cleanup;
+ }
+ iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ sqlite3VdbeJumpHere(v, iGoto);
+ }
+
/* If we are trying to update a view, realize that view into
** a ephemeral table.
*/
if( isView ){
- Select *pView;
- pView = sqlite3SelectDup(pTab->pSelect);
- sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
- sqlite3SelectDelete(pView);
+ sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, iCur);
+ }
+
+ /* Resolve the column names in all the expressions in the
+ ** WHERE clause.
+ */
+ if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ goto update_cleanup;
}
/* Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
+ WHERE_ONEPASS_DESIRED);
if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = pWInfo->okOnePass;
/* Remember the rowid of every item to be updated.
*/
- sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0);
+ sqlite3VdbeAddOp2(v, IsVirtual(pTab)?OP_VRowid:OP_Rowid, iCur, regOldRowid);
+ if( !okOnePass ) sqlite3VdbeAddOp2(v, OP_FifoWrite, regOldRowid, 0);
/* End the database scan loop.
*/
@@ -313,202 +359,202 @@ void sqlite3Update(
/* Initialize the count of updated rows
*/
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
- memCnt = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
+ regRowCount = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
- if( triggers_exist ){
- /* Create pseudo-tables for NEW and OLD
+ if( !isView && !IsVirtual(pTab) ){
+ /*
+ ** Open every index that needs updating. Note that if any
+ ** index could potentially invoke a REPLACE conflict resolution
+ ** action, then we need to open all indices because we might need
+ ** to be deleting some records.
*/
- sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
- sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);
+ if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ if( onError==OE_Replace ){
+ openAll = 1;
+ }else{
+ openAll = 0;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->onError==OE_Replace ){
+ openAll = 1;
+ break;
+ }
+ }
+ }
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ if( openAll || aRegIdx[i]>0 ){
+ KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb,
+ (char*)pKey, P4_KEYINFO_HANDOFF);
+ assert( pParse->nTab>iCur+i+1 );
+ }
+ }
+ }
+
+ /* Jump back to this point if a trigger encounters an IGNORE constraint. */
+ if( triggers_exist ){
+ sqlite3VdbeResolveLabel(v, addr);
+ }
- /* The top of the update loop for when there are triggers.
- */
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
+ /* Top of the update loop */
+ if( okOnePass ){
+ int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
+ addr = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, a1);
+ }else{
+ addr = sqlite3VdbeAddOp2(v, OP_FifoRead, regOldRowid, 0);
+ }
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- /* Open a cursor and make it point to the record that is
- ** being updated.
- */
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ if( triggers_exist ){
+ int regRowid;
+ int regRow;
+ int regCols;
+
+ /* Make cursor iCur point to the record that is being updated.
+ */
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
/* Generate the OLD table
*/
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
+ regRowid = sqlite3GetTempReg(pParse);
+ regRow = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
+ if( !old_col_mask ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regRow);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow);
+ }
+ sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid);
/* Generate the NEW table
*/
if( chngRowid ){
- sqlite3ExprCodeAndCache(pParse, pRowidExpr);
+ sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid);
}else{
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
}
+ regCols = sqlite3GetTempRange(pParse, pTab->nCol);
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
continue;
}
j = aXRef[i];
- if( j<0 ){
- sqlite3VdbeAddOp(v, OP_Column, iCur, i);
- sqlite3ColumnDefault(v, pTab, i);
+ if( new_col_mask&((u32)1<<i) || new_col_mask==0xffffffff ){
+ if( j<0 ){
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regCols+i);
+ sqlite3ColumnDefault(v, pTab, i);
+ }else{
+ sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr, regCols+i);
+ }
}else{
- sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
}
}
- sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow);
if( !isView ){
sqlite3TableAffinityStr(v, pTab);
+ sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol);
}
- if( pParse->nErr ) goto update_cleanup;
- sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0);
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
-
- /* Fire the BEFORE and INSTEAD OF triggers
- */
- if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
- newIdx, oldIdx, onError, addr) ){
- goto update_cleanup;
- }
+ sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
+ /* if( pParse->nErr ) goto update_cleanup; */
+ sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ sqlite3ReleaseTempReg(pParse, regRow);
+
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
+ sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
}
if( !isView && !IsVirtual(pTab) ){
- /*
- ** Open every index that needs updating. Note that if any
- ** index could potentially invoke a REPLACE conflict resolution
- ** action, then we need to open all indices because we might need
- ** to be deleting some records.
- */
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
- if( onError==OE_Replace ){
- openAll = 1;
- }else{
- openAll = 0;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->onError==OE_Replace ){
- openAll = 1;
- break;
- }
- }
- }
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- if( openAll || aIdxUsed[i] ){
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
- (char*)pKey, P3_KEYINFO_HANDOFF);
- assert( pParse->nTab>iCur+i+1 );
- }
- }
-
/* Loop over every record that needs updating. We have to load
** the old data for each record to be updated because some columns
** might not change and we will need to copy the old value.
** Also, the old data is needed to delete the old index entries.
** So make the cursor point at the old record.
*/
- if( !triggers_exist ){
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- }
- sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
/* If the record number will change, push the record number as it
** will be after the update. (The old record number is currently
** on top of the stack.)
*/
if( chngRowid ){
- sqlite3ExprCode(pParse, pRowidExpr);
- sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
}
/* Compute new data for this record.
*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i);
continue;
}
j = aXRef[i];
if( j<0 ){
- sqlite3VdbeAddOp(v, OP_Column, iCur, i);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i);
sqlite3ColumnDefault(v, pTab, i);
}else{
- sqlite3ExprCode(pParse, pChanges->a[j].pExpr);
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i);
}
}
/* Do constraint checks
*/
- sqlite3GenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRowid, 1,
- onError, addr);
+ sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
+ aRegIdx, chngRowid, 1,
+ onError, addr);
/* Delete the old indices for the current record.
*/
- sqlite3GenerateRowIndexDelete(v, pTab, iCur, aIdxUsed);
+ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
/* If changing the record number, delete the old record.
*/
if( chngRowid ){
- sqlite3VdbeAddOp(v, OP_Delete, iCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
}
+ sqlite3VdbeJumpHere(v, j1);
/* Create the new index entries and the new record.
*/
- sqlite3CompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRowid, 1, -1, 0);
+ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
+ aRegIdx, chngRowid, 1, -1, 0);
}
/* Increment the row counter
*/
if( db->flags & SQLITE_CountRows && !pParse->trigStack){
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
+ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
/* If there are triggers, close all the cursors after each iteration
** through the loop. The fire the after triggers.
*/
if( triggers_exist ){
- if( !isView ){
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- if( openAll || aIdxUsed[i] )
- sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
- }
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab,
- newIdx, oldIdx, onError, addr) ){
- goto update_cleanup;
- }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
+ sqlite3VdbeJumpHere(v, iEndAfterTrigger);
}
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
- sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
sqlite3VdbeJumpHere(v, addr);
- /* Close all tables if there were no FOR EACH ROW triggers */
- if( !triggers_exist ){
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- if( openAll || aIdxUsed[i] ){
- sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
- }
+ /* Close all tables */
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ if( openAll || aRegIdx[i]>0 ){
+ sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
}
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }else{
- sqlite3VdbeAddOp(v, OP_Close, newIdx, 0);
- sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0);
+ }
+ sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
+ if( triggers_exist ){
+ sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
}
/*
@@ -517,19 +563,18 @@ void sqlite3Update(
** invoke the callback function.
*/
if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){
- sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", P3_STATIC);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", P4_STATIC);
}
update_cleanup:
sqlite3AuthContextPop(&sContext);
- sqliteFree(apIdx);
- sqliteFree(aXRef);
- sqlite3SrcListDelete(pTabList);
- sqlite3ExprListDelete(pChanges);
- sqlite3ExprDelete(pWhere);
+ sqlite3DbFree(db, aRegIdx);
+ sqlite3DbFree(db, aXRef);
+ sqlite3SrcListDelete(db, pTabList);
+ sqlite3ExprListDelete(db, pChanges);
+ sqlite3ExprDelete(db, pWhere);
return;
}
@@ -569,59 +614,65 @@ static void updateVirtualTable(
int ephemTab; /* Table holding the result of the SELECT */
int i; /* Loop counter */
int addr; /* Address of top of loop */
+ int iReg; /* First register in set passed to OP_VUpdate */
+ sqlite3 *db = pParse->db; /* Database connection */
+ const char *pVtab = (const char*)pTab->pVtab;
+ SelectDest dest;
/* Construct the SELECT statement that will find the new values for
** all updated rows.
*/
- pEList = sqlite3ExprListAppend(0, sqlite3CreateIdExpr("_rowid_"), 0);
+ pEList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3CreateIdExpr(pParse, "_rowid_"), 0);
if( pRowid ){
- pEList = sqlite3ExprListAppend(pEList, sqlite3ExprDup(pRowid), 0);
+ pEList = sqlite3ExprListAppend(pParse, pEList,
+ sqlite3ExprDup(db, pRowid), 0);
}
assert( pTab->iPKey<0 );
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){
- pExpr = sqlite3ExprDup(pChanges->a[aXRef[i]].pExpr);
+ pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr);
}else{
- pExpr = sqlite3CreateIdExpr(pTab->aCol[i].zName);
+ pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName);
}
- pEList = sqlite3ExprListAppend(pEList, pExpr, 0);
+ pEList = sqlite3ExprListAppend(pParse, pEList, pExpr, 0);
}
- pSelect = sqlite3SelectNew(pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);
+ pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);
/* Create the ephemeral table into which the update results will
** be stored.
*/
assert( v );
ephemTab = pParse->nTab++;
- sqlite3VdbeAddOp(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
/* fill the ephemeral table
*/
- sqlite3Select(pParse, pSelect, SRT_Table, ephemTab, 0, 0, 0, 0);
+ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab);
+ sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
- /*
- ** Generate code to scan the ephemeral table and call VDelete and
- ** VInsert
- */
- sqlite3VdbeAddOp(v, OP_Rewind, ephemTab, 0);
+ /* Generate code to scan the ephemeral table and call VUpdate. */
+ iReg = ++pParse->nMem;
+ pParse->nMem += pTab->nCol+1;
+ sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
addr = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_Column, ephemTab, 0);
- if( pRowid ){
- sqlite3VdbeAddOp(v, OP_Column, ephemTab, 1);
- }else{
- sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- }
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
for(i=0; i<pTab->nCol; i++){
- sqlite3VdbeAddOp(v, OP_Column, ephemTab, i+1+(pRowid!=0));
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
}
- pParse->pVirtualLock = pTab;
- sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2,
- (const char*)pTab->pVtab, P3_VTAB);
- sqlite3VdbeAddOp(v, OP_Next, ephemTab, addr);
+ sqlite3VtabMakeWritable(pParse, pTab);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB);
+ sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr);
sqlite3VdbeJumpHere(v, addr-1);
- sqlite3VdbeAddOp(v, OP_Close, ephemTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
/* Cleanup */
- sqlite3SelectDelete(pSelect);
+ sqlite3SelectDelete(db, pSelect);
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/* Make sure "isView" gets undefined in case this file becomes part of
+** the amalgamation - so that subsequent files do not see isView as a
+** macro. */
+#undef isView
diff --git a/third_party/sqlite/utf.c b/third_party/sqlite/src/utf.c
index 914d647..8711a4c 100644..100755
--- a/third_party/sqlite/utf.c
+++ b/third_party/sqlite/src/utf.c
@@ -12,7 +12,7 @@
** This file contains routines used to translate between UTF-8,
** UTF-16, UTF-16BE, and UTF-16LE.
**
-** $Id: utf.c,v 1.53 2007/08/07 17:04:59 drh Exp $
+** $Id: utf.c,v 1.63 2008/07/29 11:25:14 danielk1977 Exp $
**
** Notes on UTF-8:
**
@@ -154,27 +154,31 @@ static const unsigned char sqlite3UtfTrans1[] = {
** for unicode values 0x80 and greater. It do not change over-length
** encodings to 0xfffd as some systems recommend.
*/
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = sqlite3UtfTrans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
int sqlite3Utf8Read(
const unsigned char *z, /* First byte of UTF-8 character */
const unsigned char *zTerm, /* Pretend this byte is 0x00 */
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
){
- int c = *(z++);
- if( c>=0xc0 ){
- c = sqlite3UtfTrans1[c-0xc0];
- while( z!=zTerm && (*z & 0xc0)==0x80 ){
- c = (c<<6) + (0x3f & *(z++));
- }
- if( c<0x80
- || (c&0xFFFFF800)==0xD800
- || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
- }
+ int c;
+ READ_UTF8(z, zTerm, c);
*pzNext = z;
return c;
}
+
/*
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
@@ -188,7 +192,6 @@ int sqlite3Utf8Read(
** encoding, or if *pMem does not contain a string value.
*/
int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
- unsigned char zShort[NBFS]; /* Temporary short output buffer */
int len; /* Maximum length of output string in bytes */
unsigned char *zOut; /* Output buffer */
unsigned char *zIn; /* Input iterator */
@@ -196,6 +199,7 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
unsigned char *z; /* Output iterator */
unsigned int c;
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( pMem->flags&MEM_Str );
assert( pMem->enc!=desiredEnc );
assert( pMem->enc!=0 );
@@ -253,17 +257,14 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
/* Set zIn to point at the start of the input buffer and zTerm to point 1
** byte past the end.
**
- ** Variable zOut is set to point at the output buffer. This may be space
- ** obtained from malloc(), or Mem.zShort, if it large enough and not in
- ** use, or the zShort array on the stack (see above).
+ ** Variable zOut is set to point at the output buffer, space obtained
+ ** from sqlite3_malloc().
*/
zIn = (u8*)pMem->z;
zTerm = &zIn[pMem->n];
- if( len>NBFS ){
- zOut = sqliteMallocRaw(len);
- if( !zOut ) return SQLITE_NOMEM;
- }else{
- zOut = zShort;
+ zOut = sqlite3DbMallocRaw(pMem->db, len);
+ if( !zOut ){
+ return SQLITE_NOMEM;
}
z = zOut;
@@ -271,14 +272,16 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
if( desiredEnc==SQLITE_UTF16LE ){
/* UTF-8 -> UTF-16 Little-endian */
while( zIn<zTerm ){
- c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn);
+ /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
+ READ_UTF8(zIn, zTerm, c);
WRITE_UTF16LE(z, c);
}
}else{
assert( desiredEnc==SQLITE_UTF16BE );
/* UTF-8 -> UTF-16 Big-endian */
while( zIn<zTerm ){
- c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn);
+ /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
+ READ_UTF8(zIn, zTerm, c);
WRITE_UTF16BE(z, c);
}
}
@@ -293,7 +296,7 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
WRITE_UTF8(z, c);
}
}else{
- /* UTF-16 Little-endian -> UTF-8 */
+ /* UTF-16 Big-endian -> UTF-8 */
while( zIn<zTerm ){
READ_UTF16BE(zIn, c);
WRITE_UTF8(z, c);
@@ -305,16 +308,11 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
sqlite3VdbeMemRelease(pMem);
- pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short);
+ pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
pMem->enc = desiredEnc;
- if( zOut==zShort ){
- memcpy(pMem->zShort, zOut, len);
- zOut = (u8*)pMem->zShort;
- pMem->flags |= (MEM_Term|MEM_Short);
- }else{
- pMem->flags |= (MEM_Term|MEM_Dyn);
- }
+ pMem->flags |= (MEM_Term|MEM_Dyn);
pMem->z = (char*)zOut;
+ pMem->zMalloc = pMem->z;
translate_out:
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
@@ -352,23 +350,14 @@ int sqlite3VdbeMemHandleBom(Mem *pMem){
}
if( bom ){
- /* This function is called as soon as a string is stored in a Mem*,
- ** from within sqlite3VdbeMemSetStr(). At that point it is not possible
- ** for the string to be stored in Mem.zShort, or for it to be stored
- ** in dynamic memory with no destructor.
- */
- assert( !(pMem->flags&MEM_Short) );
- assert( !(pMem->flags&MEM_Dyn) || pMem->xDel );
- if( pMem->flags & MEM_Dyn ){
- void (*xDel)(void*) = pMem->xDel;
- char *z = pMem->z;
- pMem->z = 0;
- pMem->xDel = 0;
- rc = sqlite3VdbeMemSetStr(pMem, &z[2], pMem->n-2, bom, SQLITE_TRANSIENT);
- xDel(z);
- }else{
- rc = sqlite3VdbeMemSetStr(pMem, &pMem->z[2], pMem->n-2, bom,
- SQLITE_TRANSIENT);
+ rc = sqlite3VdbeMemMakeWriteable(pMem);
+ if( rc==SQLITE_OK ){
+ pMem->n -= 2;
+ memmove(pMem->z, &pMem->z[2], pMem->n);
+ pMem->z[pMem->n] = '\0';
+ pMem->z[pMem->n+1] = '\0';
+ pMem->flags |= MEM_Term;
+ pMem->enc = bom;
}
}
return rc;
@@ -399,22 +388,57 @@ int sqlite3Utf8CharLen(const char *zIn, int nByte){
return r;
}
+/* This test function is not currently used by the automated test-suite.
+** Hence it is only available in debug builds.
+*/
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+/*
+** Translate UTF-8 to UTF-8.
+**
+** This has the effect of making sure that the string is well-formed
+** UTF-8. Miscoded characters are removed.
+**
+** The translation is done in-place (since it is impossible for the
+** correct UTF-8 encoding to be longer than a malformed encoding).
+*/
+int sqlite3Utf8To8(unsigned char *zIn){
+ unsigned char *zOut = zIn;
+ unsigned char *zStart = zIn;
+ unsigned char *zTerm;
+ u32 c;
+
+ while( zIn[0] ){
+ c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn);
+ if( c!=0xfffd ){
+ WRITE_UTF8(zOut, c);
+ }
+ }
+ *zOut = 0;
+ return zOut - zStart;
+}
+#endif
+
#ifndef SQLITE_OMIT_UTF16
/*
** Convert a UTF-16 string in the native encoding into a UTF-8 string.
-** Memory to hold the UTF-8 string is obtained from malloc and must be
-** freed by the calling function.
+** Memory to hold the UTF-8 string is obtained from sqlite3_malloc and must
+** be freed by the calling function.
**
** NULL is returned if there is an allocation error.
*/
-char *sqlite3Utf16to8(const void *z, int nByte){
+char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte){
Mem m;
memset(&m, 0, sizeof(m));
+ m.db = db;
sqlite3VdbeMemSetStr(&m, z, nByte, SQLITE_UTF16NATIVE, SQLITE_STATIC);
sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8);
- assert( (m.flags & MEM_Term)!=0 || sqlite3MallocFailed() );
- assert( (m.flags & MEM_Str)!=0 || sqlite3MallocFailed() );
- return (m.flags & MEM_Dyn)!=0 ? m.z : sqliteStrDup(m.z);
+ if( db->mallocFailed ){
+ sqlite3VdbeMemRelease(&m);
+ m.z = 0;
+ }
+ assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
+ assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
+ return (m.flags & MEM_Dyn)!=0 ? m.z : sqlite3DbStrDup(db, m.z);
}
/*
@@ -453,33 +477,6 @@ int sqlite3Utf16ByteLen(const void *zIn, int nChar){
#if defined(SQLITE_TEST)
/*
-** Translate UTF-8 to UTF-8.
-**
-** This has the effect of making sure that the string is well-formed
-** UTF-8. Miscoded characters are removed.
-**
-** The translation is done in-place (since it is impossible for the
-** correct UTF-8 encoding to be longer than a malformed encoding).
-*/
-int sqlite3Utf8To8(unsigned char *zIn){
- unsigned char *zOut = zIn;
- unsigned char *zStart = zIn;
- unsigned char *zTerm;
- u32 c;
-
- while( zIn[0] ){
- c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn);
- if( c!=0xfffd ){
- WRITE_UTF8(zOut, c);
- }
- }
- *zOut = 0;
- return zOut - zStart;
-}
-#endif
-
-#if defined(SQLITE_TEST)
-/*
** This routine is called from the TCL test function "translate_selftest".
** It checks that the primitives for serializing and deserializing
** characters in each encoding are inverses of each other.
diff --git a/third_party/sqlite/util.c b/third_party/sqlite/src/util.c
index cf222b5..919452c 100644..100755
--- a/third_party/sqlite/util.c
+++ b/third_party/sqlite/src/util.c
@@ -14,15 +14,61 @@
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
-** $Id: util.c,v 1.207 2007/06/26 00:37:28 drh Exp $
+** $Id: util.c,v 1.241 2008/07/28 19:34:54 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <stdarg.h>
#include <ctype.h>
/*
+** Return true if the floating point value is Not a Number (NaN).
+*/
+int sqlite3IsNaN(double x){
+ /* This NaN test sometimes fails if compiled on GCC with -ffast-math.
+ ** On the other hand, the use of -ffast-math comes with the following
+ ** warning:
+ **
+ ** This option [-ffast-math] should never be turned on by any
+ ** -O option since it can result in incorrect output for programs
+ ** which depend on an exact implementation of IEEE or ISO
+ ** rules/specifications for math functions.
+ **
+ ** Under MSVC, this NaN test may fail if compiled with a floating-
+ ** point precision mode other than /fp:precise. From the MSDN
+ ** documentation:
+ **
+ ** The compiler [with /fp:precise] will properly handle comparisons
+ ** involving NaN. For example, x != x evaluates to true if x is NaN
+ ** ...
+ */
+#ifdef __FAST_MATH__
+# error SQLite will not work correctly with the -ffast-math option of GCC.
+#endif
+ volatile double y = x;
+ volatile double z = y;
+ return y!=z;
+}
+
+/*
+** Return the length of a string, except do not allow the string length
+** to exceed the SQLITE_LIMIT_LENGTH setting.
+*/
+int sqlite3Strlen(sqlite3 *db, const char *z){
+ const char *z2 = z;
+ int len;
+ size_t x;
+ while( *z2 ){ z2++; }
+ x = z2 - z;
+ len = 0x7fffffff & x;
+ if( len!=x || len > db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ return db->aLimit[SQLITE_LIMIT_LENGTH];
+ }else{
+ return len;
+ }
+}
+
+/*
** Set the most recent error code and error string for the sqlite
** handle "db". The error code is set to "err_code".
**
@@ -44,15 +90,15 @@
** to NULL.
*/
void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
- if( db && (db->pErr || (db->pErr = sqlite3ValueNew())!=0) ){
+ if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
db->errCode = err_code;
if( zFormat ){
char *z;
va_list ap;
va_start(ap, zFormat);
- z = sqlite3VMPrintf(zFormat, ap);
+ z = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
- sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, sqlite3FreeX);
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
}else{
sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
}
@@ -78,10 +124,11 @@ void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
*/
void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
va_list ap;
+ sqlite3 *db = pParse->db;
pParse->nErr++;
- sqliteFree(pParse->zErrMsg);
+ sqlite3DbFree(db, pParse->zErrMsg);
va_start(ap, zFormat);
- pParse->zErrMsg = sqlite3VMPrintf(zFormat, ap);
+ pParse->zErrMsg = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
if( pParse->rc==SQLITE_OK ){
pParse->rc = SQLITE_ERROR;
@@ -92,7 +139,7 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
** Clear the error message in pParse, if any
*/
void sqlite3ErrorClear(Parse *pParse){
- sqliteFree(pParse->zErrMsg);
+ sqlite3DbFree(pParse->db, pParse->zErrMsg);
pParse->zErrMsg = 0;
pParse->nErr = 0;
}
@@ -134,46 +181,7 @@ void sqlite3Dequote(char *z){
}
}
-/* An array to map all upper-case characters into their corresponding
-** lower-case character.
-*/
-const unsigned char sqlite3UpperToLower[] = {
-#ifdef SQLITE_ASCII
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
- 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
- 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
- 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
- 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
- 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
- 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
- 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
- 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
- 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
- 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
- 252,253,254,255
-#endif
-#ifdef SQLITE_EBCDIC
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */
- 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */
- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */
- 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */
- 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */
- 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */
- 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */
- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */
- 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */
- 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */
- 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */
- 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */
- 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */
- 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */
-#endif
-};
+/* Convenient short-hand */
#define UpperToLower sqlite3UpperToLower
/*
@@ -246,6 +254,7 @@ int sqlite3AtoF(const char *z, double *pResult){
int sign = 1;
const char *zBegin = z;
LONGDOUBLE_TYPE v1 = 0.0;
+ int nSignificant = 0;
while( isspace(*(u8*)z) ) z++;
if( *z=='-' ){
sign = -1;
@@ -253,16 +262,29 @@ int sqlite3AtoF(const char *z, double *pResult){
}else if( *z=='+' ){
z++;
}
+ while( z[0]=='0' ){
+ z++;
+ }
while( isdigit(*(u8*)z) ){
v1 = v1*10.0 + (*z - '0');
z++;
+ nSignificant++;
}
if( *z=='.' ){
LONGDOUBLE_TYPE divisor = 1.0;
z++;
+ if( nSignificant==0 ){
+ while( z[0]=='0' ){
+ divisor *= 10.0;
+ z++;
+ }
+ }
while( isdigit(*(u8*)z) ){
- v1 = v1*10.0 + (*z - '0');
- divisor *= 10.0;
+ if( nSignificant<18 ){
+ v1 = v1*10.0 + (*z - '0');
+ divisor *= 10.0;
+ nSignificant++;
+ }
z++;
}
v1 /= divisor;
@@ -336,6 +358,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum){
i64 v = 0;
int neg;
int i, c;
+ const char *zStart;
while( isspace(*(u8*)zNum) ) zNum++;
if( *zNum=='-' ){
neg = 1;
@@ -346,12 +369,13 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum){
}else{
neg = 0;
}
+ zStart = zNum;
while( zNum[0]=='0' ){ zNum++; } /* Skip over leading zeros. Ticket #2454 */
for(i=0; (c=zNum[i])>='0' && c<='9'; i++){
v = v*10 + c - '0';
}
*pNum = neg ? -v : v;
- if( c!=0 || i==0 || i>19 ){
+ if( c!=0 || (i==0 && zStart==zNum) || i>19 ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranting that it is too large) */
return 0;
@@ -377,7 +401,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum){
** 9223373036854775808 will not fit in 64 bits. So it seems safer to return
** false.
*/
-int sqlite3FitsIn64Bits(const char *zNum){
+int sqlite3FitsIn64Bits(const char *zNum, int negFlag){
int i, c;
int neg = 0;
if( *zNum=='-' ){
@@ -386,6 +410,7 @@ int sqlite3FitsIn64Bits(const char *zNum){
}else if( *zNum=='+' ){
zNum++;
}
+ if( negFlag ) neg = 1-neg;
while( *zNum=='0' ){
zNum++; /* Skip leading zeros. Ticket #2454 */
}
@@ -421,10 +446,16 @@ int sqlite3GetInt32(const char *zNum, int *pValue){
zNum++;
}
while( zNum[0]=='0' ) zNum++;
- for(i=0; i<10 && (c = zNum[i] - '0')>=0 && c<=9; i++){
+ for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
v = v*10 + c;
}
- if( i>9 ){
+
+ /* The longest decimal representation of a 32 bit integer is 10 digits:
+ **
+ ** 1234567890
+ ** 2^31 -> 2147483648
+ */
+ if( i>10 ){
return 0;
}
if( v-neg>2147483647 ){
@@ -438,25 +469,6 @@ int sqlite3GetInt32(const char *zNum, int *pValue){
}
/*
-** Check to make sure we have a valid db pointer. This test is not
-** foolproof but it does provide some measure of protection against
-** misuse of the interface such as passing in db pointers that are
-** NULL or which have been previously closed. If this routine returns
-** TRUE it means that the db pointer is invalid and should not be
-** dereferenced for any reason. The calling function should invoke
-** SQLITE_MISUSE immediately.
-*/
-int sqlite3SafetyCheck(sqlite3 *db){
- int magic;
- if( db==0 ) return 1;
- magic = db->magic;
- if( magic!=SQLITE_MAGIC_CLOSED &&
- magic!=SQLITE_MAGIC_OPEN &&
- magic!=SQLITE_MAGIC_BUSY ) return 1;
- return 0;
-}
-
-/*
** The variable-length integer encoding is as follows:
**
** KEY:
@@ -511,71 +523,271 @@ int sqlite3PutVarint(unsigned char *p, u64 v){
}
/*
+** This routine is a faster version of sqlite3PutVarint() that only
+** works for 32-bit positive integers and which is optimized for
+** the common case of small integers. A MACRO version, putVarint32,
+** is provided which inlines the single-byte case. All code should use
+** the MACRO version as this function assumes the single-byte case has
+** already been handled.
+*/
+int sqlite3PutVarint32(unsigned char *p, u32 v){
+#ifndef putVarint32
+ if( (v & ~0x7f)==0 ){
+ p[0] = v;
+ return 1;
+ }
+#endif
+ if( (v & ~0x3fff)==0 ){
+ p[0] = (v>>7) | 0x80;
+ p[1] = v & 0x7f;
+ return 2;
+ }
+ return sqlite3PutVarint(p, v);
+}
+
+/*
** Read a 64-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read. The value is stored in *v.
*/
int sqlite3GetVarint(const unsigned char *p, u64 *v){
- u32 x;
- u64 x64;
- int n;
- unsigned char c;
- if( ((c = p[0]) & 0x80)==0 ){
- *v = c;
+ u32 a,b,s;
+
+ a = *p;
+ /* a: p0 (unmasked) */
+ if (!(a&0x80))
+ {
+ *v = a;
return 1;
}
- x = c & 0x7f;
- if( ((c = p[1]) & 0x80)==0 ){
- *v = (x<<7) | c;
+
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ a &= 0x7f;
+ a = a<<7;
+ a |= b;
+ *v = a;
return 2;
}
- x = (x<<7) | (c&0x7f);
- if( ((c = p[2]) & 0x80)==0 ){
- *v = (x<<7) | c;
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= (0x7f<<14)|(0x7f);
+ b &= 0x7f;
+ b = b<<7;
+ a |= b;
+ *v = a;
return 3;
}
- x = (x<<7) | (c&0x7f);
- if( ((c = p[3]) & 0x80)==0 ){
- *v = (x<<7) | c;
+
+ /* CSE1 from below */
+ a &= (0x7f<<14)|(0x7f);
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<14 | p3 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= (0x7f<<14)|(0x7f);
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ *v = a;
return 4;
}
- x64 = (x<<7) | (c&0x7f);
- n = 4;
- do{
- c = p[n++];
- if( n==9 ){
- x64 = (x64<<8) | c;
- break;
- }
- x64 = (x64<<7) | (c&0x7f);
- }while( (c & 0x80)!=0 );
- *v = x64;
- return n;
+
+ /* a: p0<<14 | p2 (masked) */
+ /* b: p1<<14 | p3 (unmasked) */
+ /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ b &= (0x7f<<14)|(0x7f);
+ s = a;
+ /* s: p0<<14 | p2 (masked) */
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* we can skip these cause they were (effectively) done above in calc'ing s */
+ /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ /* b &= (0x7f<<14)|(0x7f); */
+ b = b<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 5;
+ }
+
+ /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ s = s<<7;
+ s |= b;
+ /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<28 | p3<<14 | p5 (unmasked) */
+ if (!(b&0x80))
+ {
+ /* we can skip this cause it was (effectively) done above in calc'ing s */
+ /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ a &= (0x7f<<14)|(0x7f);
+ a = a<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 6;
+ }
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p2<<28 | p4<<14 | p6 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
+ b &= (0x7f<<14)|(0x7f);
+ b = b<<7;
+ a |= b;
+ s = s>>11;
+ *v = ((u64)s)<<32 | a;
+ return 7;
+ }
+
+ /* CSE2 from below */
+ a &= (0x7f<<14)|(0x7f);
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p3<<28 | p5<<14 | p7 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
+ /* moved CSE2 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ s = s>>4;
+ *v = ((u64)s)<<32 | a;
+ return 8;
+ }
+
+ p++;
+ a = a<<15;
+ a |= *p;
+ /* a: p4<<29 | p6<<15 | p8 (unmasked) */
+
+ /* moved CSE2 up */
+ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
+ b &= (0x7f<<14)|(0x7f);
+ b = b<<8;
+ a |= b;
+
+ s = s<<4;
+ b = p[-4];
+ b &= 0x7f;
+ b = b>>3;
+ s |= b;
+
+ *v = ((u64)s)<<32 | a;
+
+ return 9;
}
/*
** Read a 32-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read. The value is stored in *v.
+** A MACRO version, getVarint32, is provided which inlines the
+** single-byte case. All code should use the MACRO version as
+** this function assumes the single-byte case has already been handled.
*/
int sqlite3GetVarint32(const unsigned char *p, u32 *v){
- u32 x;
- int n;
- unsigned char c;
- if( ((signed char*)p)[0]>=0 ){
- *v = p[0];
+ u32 a,b;
+
+ a = *p;
+ /* a: p0 (unmasked) */
+#ifndef getVarint32
+ if (!(a&0x80))
+ {
+ *v = a;
return 1;
}
- x = p[0] & 0x7f;
- if( ((signed char*)p)[1]>=0 ){
- *v = (x<<7) | p[1];
+#endif
+
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ a &= 0x7f;
+ a = a<<7;
+ *v = a | b;
return 2;
}
- x = (x<<7) | (p[1] & 0x7f);
- n = 2;
- do{
- x = (x<<7) | ((c = p[n++])&0x7f);
- }while( (c & 0x80)!=0 && n<9 );
- *v = x;
- return n;
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= (0x7f<<14)|(0x7f);
+ b &= 0x7f;
+ b = b<<7;
+ *v = a | b;
+ return 3;
+ }
+
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<14 | p3 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= (0x7f<<14)|(0x7f);
+ a &= (0x7f<<14)|(0x7f);
+ a = a<<7;
+ *v = a | b;
+ return 4;
+ }
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
+ b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
+ b = b<<7;
+ *v = a | b;
+ return 5;
+ }
+
+ /* We can only reach this point when reading a corrupt database
+ ** file. In that case we are not in any hurry. Use the (relatively
+ ** slow) general-purpose sqlite3GetVarint() routine to extract the
+ ** value. */
+ {
+ u64 v64;
+ int n;
+
+ p -= 4;
+ n = sqlite3GetVarint(p, &v64);
+ assert( n>5 && n<=9 );
+ *v = (u32)v64;
+ return n;
+ }
}
/*
@@ -607,22 +819,23 @@ void sqlite3Put4byte(unsigned char *p, u32 v){
-#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) \
- || defined(SQLITE_TEST)
+#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
/*
** Translate a single byte of Hex into an integer.
+** This routinen only works if h really is a valid hexadecimal
+** character: 0..9a..fA..F
*/
static int hexToInt(int h){
- if( h>='0' && h<='9' ){
- return h - '0';
- }else if( h>='a' && h<='f' ){
- return h - 'a' + 10;
- }else{
- assert( h>='A' && h<='F' );
- return h - 'A' + 10;
- }
+ assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
+#ifdef SQLITE_ASCII
+ h += 9*(1&(h>>6));
+#endif
+#ifdef SQLITE_EBCDIC
+ h += 9*(1&~(h>>4));
+#endif
+ return h & 0xf;
}
-#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC || SQLITE_TEST */
+#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
/*
@@ -631,17 +844,17 @@ static int hexToInt(int h){
** binary value has been obtained from malloc and must be freed by
** the calling routine.
*/
-void *sqlite3HexToBlob(const char *z){
+void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
char *zBlob;
int i;
- int n = strlen(z);
- if( n%2 ) return 0;
- zBlob = (char *)sqliteMalloc(n/2);
+ zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1);
+ n--;
if( zBlob ){
for(i=0; i<n; i+=2){
zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]);
}
+ zBlob[i/2] = 0;
}
return zBlob;
}
@@ -673,9 +886,11 @@ void *sqlite3HexToBlob(const char *z){
** call to sqlite3_close(db) and db has been deallocated. And we do
** not want to write into deallocated memory.
*/
+#ifdef SQLITE_DEBUG
int sqlite3SafetyOn(sqlite3 *db){
if( db->magic==SQLITE_MAGIC_OPEN ){
db->magic = SQLITE_MAGIC_BUSY;
+ assert( sqlite3_mutex_held(db->mutex) );
return 0;
}else if( db->magic==SQLITE_MAGIC_BUSY ){
db->magic = SQLITE_MAGIC_ERROR;
@@ -683,50 +898,55 @@ int sqlite3SafetyOn(sqlite3 *db){
}
return 1;
}
+#endif
/*
** Change the magic from SQLITE_MAGIC_BUSY to SQLITE_MAGIC_OPEN.
** Return an error (non-zero) if the magic was not SQLITE_MAGIC_BUSY
** when this routine is called.
*/
+#ifdef SQLITE_DEBUG
int sqlite3SafetyOff(sqlite3 *db){
if( db->magic==SQLITE_MAGIC_BUSY ){
db->magic = SQLITE_MAGIC_OPEN;
+ assert( sqlite3_mutex_held(db->mutex) );
return 0;
- }else {
+ }else{
db->magic = SQLITE_MAGIC_ERROR;
db->u1.isInterrupted = 1;
return 1;
}
}
+#endif
/*
-** Return a pointer to the ThreadData associated with the calling thread.
-*/
-ThreadData *sqlite3ThreadData(){
- ThreadData *p = (ThreadData*)sqlite3OsThreadSpecificData(1);
- if( !p ){
- sqlite3FailedMalloc();
- }
- return p;
-}
-
-/*
-** Return a pointer to the ThreadData associated with the calling thread.
-** If no ThreadData has been allocated to this thread yet, return a pointer
-** to a substitute ThreadData structure that is all zeros.
+** Check to make sure we have a valid db pointer. This test is not
+** foolproof but it does provide some measure of protection against
+** misuse of the interface such as passing in db pointers that are
+** NULL or which have been previously closed. If this routine returns
+** 1 it means that the db pointer is valid and 0 if it should not be
+** dereferenced for any reason. The calling function should invoke
+** SQLITE_MISUSE immediately.
+**
+** sqlite3SafetyCheckOk() requires that the db pointer be valid for
+** use. sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to
+** open properly and is not fit for general use but which can be
+** used as an argument to sqlite3_errmsg() or sqlite3_close().
*/
-const ThreadData *sqlite3ThreadDataReadOnly(){
- static const ThreadData zeroData = {0}; /* Initializer to silence warnings
- ** from broken compilers */
- const ThreadData *pTd = sqlite3OsThreadSpecificData(0);
- return pTd ? pTd : &zeroData;
+int sqlite3SafetyCheckOk(sqlite3 *db){
+ int magic;
+ if( db==0 ) return 0;
+ magic = db->magic;
+ if( magic!=SQLITE_MAGIC_OPEN &&
+ magic!=SQLITE_MAGIC_BUSY ) return 0;
+ return 1;
}
-
-/*
-** Check to see if the ThreadData for this thread is all zero. If it
-** is, then deallocate it.
-*/
-void sqlite3ReleaseThreadData(){
- sqlite3OsThreadSpecificData(-1);
+int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
+ int magic;
+ if( db==0 ) return 0;
+ magic = db->magic;
+ if( magic!=SQLITE_MAGIC_SICK &&
+ magic!=SQLITE_MAGIC_OPEN &&
+ magic!=SQLITE_MAGIC_BUSY ) return 0;
+ return 1;
}
diff --git a/third_party/sqlite/vacuum.c b/third_party/sqlite/src/vacuum.c
index e34a4e9..45bc61e 100644..100755
--- a/third_party/sqlite/vacuum.c
+++ b/third_party/sqlite/src/vacuum.c
@@ -14,11 +14,10 @@
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
-** $Id: vacuum.c,v 1.69 2007/03/27 16:19:52 danielk1977 Exp $
+** $Id: vacuum.c,v 1.81 2008/07/08 19:34:07 drh Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
-#include "os.h"
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/*
@@ -26,6 +25,9 @@
*/
static int execSql(sqlite3 *db, const char *zSql){
sqlite3_stmt *pStmt;
+ if( !zSql ){
+ return SQLITE_NOMEM;
+ }
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
return sqlite3_errcode(db);
}
@@ -68,7 +70,7 @@ static int execExecSql(sqlite3 *db, const char *zSql){
void sqlite3Vacuum(Parse *pParse){
Vdbe *v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
}
return;
}
@@ -82,15 +84,19 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
Btree *pTemp; /* The temporary database we vacuum into */
char *zSql = 0; /* SQL statements */
int saved_flags; /* Saved value of the db->flags */
+ int saved_nChange; /* Saved value of db->nChange */
+ int saved_nTotalChange; /* Saved value of db->nTotalChange */
Db *pDb = 0; /* Database to detach at end of vacuum */
+ int nRes;
/* Save the current value of the write-schema flag before setting it. */
saved_flags = db->flags;
+ saved_nChange = db->nChange;
+ saved_nTotalChange = db->nTotalChange;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
if( !db->autoCommit ){
- sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction",
- (char*)0);
+ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
rc = SQLITE_ERROR;
goto end_of_vacuum;
}
@@ -103,6 +109,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** sqlite3BtreeCopyFile() is called.
**
** An optimisation would be to use a non-journaled pager.
+ ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but
+ ** that actually made the VACUUM run slower. Very little journalling
+ ** actually occurs when doing a vacuum since the vacuum_db is initially
+ ** empty. Only the journal header is written. Apparently it takes more
+ ** time to parse and run the PRAGMA to turn journalling off than it does
+ ** to write the journal header file.
*/
zSql = "ATTACH '' AS vacuum_db;";
rc = execSql(db, zSql);
@@ -110,20 +122,35 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
pDb = &db->aDb[db->nDb-1];
assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 );
pTemp = db->aDb[db->nDb-1].pBt;
- sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain),
- sqlite3BtreeGetReserve(pMain));
- if( sqlite3MallocFailed() ){
+
+ nRes = sqlite3BtreeGetReserve(pMain);
+
+ /* A VACUUM cannot change the pagesize of an encrypted database. */
+#ifdef SQLITE_HAS_CODEC
+ if( db->nextPagesize ){
+ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
+ int nKey;
+ char *zKey;
+ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
+ if( nKey ) db->nextPagesize = 0;
+ }
+#endif
+
+ if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes)
+ || sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes)
+ || db->mallocFailed
+ ){
rc = SQLITE_NOMEM;
goto end_of_vacuum;
}
- assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) );
rc = execSql(db, "PRAGMA vacuum_db.synchronous=OFF");
if( rc!=SQLITE_OK ){
goto end_of_vacuum;
}
#ifndef SQLITE_OMIT_AUTOVACUUM
- sqlite3BtreeSetAutoVacuum(pTemp, sqlite3BtreeGetAutoVacuum(pMain));
+ sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
+ sqlite3BtreeGetAutoVacuum(pMain));
#endif
/* Begin a transaction */
@@ -134,17 +161,17 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** in the temporary database.
*/
rc = execExecSql(db,
- "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) "
+ "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
" AND rootpage>0"
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db,
- "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000)"
+ "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
" FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db,
- "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) "
+ "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
" FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
@@ -234,9 +261,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
rc = sqlite3BtreeCommit(pMain);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes);
+ }
+
end_of_vacuum:
/* Restore the original value of db->flags */
db->flags = saved_flags;
+ db->nChange = saved_nChange;
+ db->nTotalChange = saved_nTotalChange;
/* Currently there is an SQL level transaction open on the vacuum
** database. No locks are held on any other files (since the main file
@@ -248,9 +281,7 @@ end_of_vacuum:
db->autoCommit = 1;
if( pDb ){
- sqlite3MallocDisallow();
sqlite3BtreeClose(pDb->pBt);
- sqlite3MallocAllow();
pDb->pBt = 0;
pDb->pSchema = 0;
}
diff --git a/third_party/sqlite/vdbe.c b/third_party/sqlite/src/vdbe.c
index d692511..b326c79 100644..100755
--- a/third_party/sqlite/vdbe.c
+++ b/third_party/sqlite/src/vdbe.c
@@ -22,14 +22,14 @@
** the VDBE to do the work of the SQL statement. VDBE programs are
** similar in form to assembly language. The program consists of
** a linear sequence of operations. Each operation has an opcode
-** and 3 operands. Operands P1 and P2 are integers. Operand P3
-** is a null-terminated string. The P2 operand must be non-negative.
-** Opcodes will typically ignore one or more operands. Many opcodes
-** ignore all three operands.
-**
-** Computation results are stored on a stack. Each entry on the
-** stack is either an integer, a null-terminated string, a floating point
-** number, or the SQL "NULL" value. An inplicit conversion from one
+** and 5 operands. Operands P1, P2, and P3 are integers. Operand P4
+** is a null-terminated string. Operand P5 is an unsigned character.
+** Few opcodes use all 5 operands.
+**
+** Computation results are stored on a set of registers numbered beginning
+** with 1 and going up to Vdbe.nMem. Each register can store
+** either an integer, a null-terminated string, a floating point
+** number, or the SQL "NULL" value. An implicit conversion from one
** type to the other occurs as necessary.
**
** Most of the code in this file is taken up by the sqlite3VdbeExec()
@@ -43,12 +43,10 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.639 2007/07/26 06:50:06 danielk1977 Exp $
+** $Id: vdbe.c,v 1.772 2008/08/02 15:10:09 danielk1977 Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
-#include <math.h>
#include "vdbeInt.h"
/*
@@ -77,7 +75,7 @@ int sqlite3_interrupt_count = 0;
/*
** The next global variable is incremented each type the OP_Sort opcode
** is executed. The test procedures use this information to make sure that
-** sorting is occurring or not occuring at appropriate times. This variable
+** sorting is occurring or not occurring at appropriate times. This variable
** has no function other than to help verify the correct operation of the
** library.
*/
@@ -87,23 +85,38 @@ int sqlite3_sort_count = 0;
/*
** The next global variable records the size of the largest MEM_Blob
-** or MEM_Str that has appeared on the VDBE stack. The test procedures
+** or MEM_Str that has been used by a VDBE opcode. The test procedures
** use this information to make sure that the zero-blob functionality
** is working correctly. This variable has no function other than to
** help verify the correct operation of the library.
*/
#ifdef SQLITE_TEST
int sqlite3_max_blobsize = 0;
+static void updateMaxBlobsize(Mem *p){
+ if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlite3_max_blobsize ){
+ sqlite3_max_blobsize = p->n;
+ }
+}
+#endif
+
+/*
+** Test a register to see if it exceeds the current maximum blob size.
+** If it does, record the new maximum blob size.
+*/
+#if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_BUILTIN_TEST)
+# define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P)
+#else
+# define UPDATE_MAX_BLOBSIZE(P)
#endif
/*
-** Release the memory associated with the given stack level. This
+** Release the memory associated with a register. This
** leaves the Mem.flags field in an inconsistent state.
*/
#define Release(P) if((P)->flags&MEM_Dyn){ sqlite3VdbeMemRelease(P); }
/*
-** Convert the given stack entity into a string if it isn't one
+** Convert the given register into a string if it isn't one
** already. Return non-zero if a malloc() fails.
*/
#define Stringify(P, enc) \
@@ -111,40 +124,14 @@ int sqlite3_max_blobsize = 0;
{ goto no_mem; }
/*
-** Convert the given stack entity into a string that has been obtained
-** from sqliteMalloc(). This is different from Stringify() above in that
-** Stringify() will use the NBFS bytes of static string space if the string
-** will fit but this routine always mallocs for space.
-** Return non-zero if we run out of memory.
-*/
-#define Dynamicify(P,enc) sqlite3VdbeMemDynamicify(P)
-
-/*
-** The header of a record consists of a sequence variable-length integers.
-** These integers are almost always small and are encoded as a single byte.
-** The following macro takes advantage this fact to provide a fast decode
-** of the integers in a record header. It is faster for the common case
-** where the integer is a single byte. It is a little slower when the
-** integer is two or more bytes. But overall it is faster.
-**
-** The following expressions are equivalent:
-**
-** x = sqlite3GetVarint32( A, &B );
-**
-** x = GetVarint( A, B );
-**
-*/
-#define GetVarint(A,B) ((B = *(A))<=0x7f ? 1 : sqlite3GetVarint32(A, &B))
-
-/*
** An ephemeral string value (signified by the MEM_Ephem flag) contains
** a pointer to a dynamically allocated string where some other entity
-** is responsible for deallocating that string. Because the stack entry
-** does not control the string, it might be deleted without the stack
-** entry knowing it.
+** is responsible for deallocating that string. Because the register
+** does not control the string, it might be deleted without the register
+** knowing it.
**
** This routine converts an ephemeral string into a dynamically allocated
-** string that the stack entry itself controls. In other words, it
+** string that the register itself controls. In other words, it
** converts an MEM_Ephem string into an MEM_Dyn string.
*/
#define Deephemeralize(P) \
@@ -158,10 +145,10 @@ int sqlite3_max_blobsize = 0;
#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
/*
-** Argument pMem points at a memory cell that will be passed to a
+** Argument pMem points at a register that will be passed to a
** user-defined function or returned to the user as the result of a query.
** The second argument, 'db_enc' is the text encoding used by the vdbe for
-** stack variables. This routine sets the pMem->enc and pMem->type
+** register variables. This routine sets the pMem->enc and pMem->type
** variables used by the sqlite3_value_*() routines.
*/
#define storeTypeInfo(A,B) _storeTypeInfo(A)
@@ -184,31 +171,85 @@ static void _storeTypeInfo(Mem *pMem){
}
/*
-** Pop the stack N times.
+** Properties of opcodes. The OPFLG_INITIALIZER macro is
+** created by mkopcodeh.awk during compilation. Data is obtained
+** from the comments following the "case OP_xxxx:" statements in
+** this file.
*/
-static void popStack(Mem **ppTos, int N){
- Mem *pTos = *ppTos;
- while( N>0 ){
- N--;
- Release(pTos);
- pTos--;
- }
- *ppTos = pTos;
+static unsigned char opcodeProperty[] = OPFLG_INITIALIZER;
+
+/*
+** Return true if an opcode has any of the OPFLG_xxx properties
+** specified by mask.
+*/
+int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){
+ assert( opcode>0 && opcode<sizeof(opcodeProperty) );
+ return (opcodeProperty[opcode]&mask)!=0;
}
/*
** Allocate cursor number iCur. Return a pointer to it. Return NULL
** if we run out of memory.
*/
-static Cursor *allocateCursor(Vdbe *p, int iCur, int iDb){
- Cursor *pCx;
+static Cursor *allocateCursor(
+ Vdbe *p,
+ int iCur,
+ Op *pOp,
+ int iDb,
+ int isBtreeCursor
+){
+ /* Find the memory cell that will be used to store the blob of memory
+ ** required for this Cursor structure. It is convenient to use a
+ ** vdbe memory cell to manage the memory allocation required for a
+ ** Cursor structure for the following reasons:
+ **
+ ** * Sometimes cursor numbers are used for a couple of different
+ ** purposes in a vdbe program. The different uses might require
+ ** different sized allocations. Memory cells provide growable
+ ** allocations.
+ **
+ ** * When using ENABLE_MEMORY_MANAGEMENT, memory cell buffers can
+ ** be freed lazily via the sqlite3_release_memory() API. This
+ ** minimizes the number of malloc calls made by the system.
+ **
+ ** Memory cells for cursors are allocated at the top of the address
+ ** space. Memory cell (p->nMem) corresponds to cursor 0. Space for
+ ** cursor 1 is managed by memory cell (p->nMem-1), etc.
+ */
+ Mem *pMem = &p->aMem[p->nMem-iCur];
+
+ int nByte;
+ Cursor *pCx = 0;
+ /* If the opcode of pOp is OP_SetNumColumns, then pOp->p2 contains
+ ** the number of fields in the records contained in the table or
+ ** index being opened. Use this to reserve space for the
+ ** Cursor.aType[] array.
+ */
+ int nField = 0;
+ if( pOp->opcode==OP_SetNumColumns || pOp->opcode==OP_OpenEphemeral ){
+ nField = pOp->p2;
+ }
+ nByte =
+ sizeof(Cursor) +
+ (isBtreeCursor?sqlite3BtreeCursorSize():0) +
+ 2*nField*sizeof(u32);
+
assert( iCur<p->nCursor );
if( p->apCsr[iCur] ){
sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
+ p->apCsr[iCur] = 0;
}
- p->apCsr[iCur] = pCx = sqliteMalloc( sizeof(Cursor) );
- if( pCx ){
+ if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ p->apCsr[iCur] = pCx = (Cursor *)pMem->z;
+ memset(pMem->z, 0, nByte);
pCx->iDb = iDb;
+ pCx->nField = nField;
+ if( nField ){
+ pCx->aType = (u32 *)&pMem->z[sizeof(Cursor)];
+ }
+ if( isBtreeCursor ){
+ pCx->pCursor = (BtCursor *)&pMem->z[sizeof(Cursor)+2*nField*sizeof(u32)];
+ }
}
return pCx;
}
@@ -228,9 +269,8 @@ static void applyNumericAffinity(Mem *pRec){
i64 value;
sqlite3VdbeChangeEncoding(pRec, SQLITE_UTF8);
if( !realnum && sqlite3Atoi64(pRec->z, &value) ){
- sqlite3VdbeMemRelease(pRec);
pRec->u.i = value;
- pRec->flags = MEM_Int;
+ MemSetTypeFlag(pRec, MEM_Int);
}else{
sqlite3VdbeMemRealify(pRec);
}
@@ -256,7 +296,11 @@ static void applyNumericAffinity(Mem *pRec){
** SQLITE_AFF_NONE:
** No-op. pRec is unchanged.
*/
-static void applyAffinity(Mem *pRec, char affinity, u8 enc){
+static void applyAffinity(
+ Mem *pRec, /* The value to apply affinity to */
+ char affinity, /* The affinity to be applied */
+ u8 enc /* Use this text encoding */
+){
if( affinity==SQLITE_AFF_TEXT ){
/* Only attempt the conversion to TEXT if there is an integer or real
** representation (blob and NULL do not get converted) but no string
@@ -295,7 +339,11 @@ int sqlite3_value_numeric_type(sqlite3_value *pVal){
** Exported version of applyAffinity(). This one works on sqlite3_value*,
** not the internal Mem* type.
*/
-void sqlite3ValueApplyAffinity(sqlite3_value *pVal, u8 affinity, u8 enc){
+void sqlite3ValueApplyAffinity(
+ sqlite3_value *pVal,
+ u8 affinity,
+ u8 enc
+){
applyAffinity((Mem *)pVal, affinity, enc);
}
@@ -382,21 +430,48 @@ void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
}
#endif
-
-#ifdef VDBE_PROFILE
+#ifdef SQLITE_DEBUG
/*
-** The following routine only works on pentium-class processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-__inline__ unsigned long long int hwtime(void){
- unsigned long long int x;
- __asm__("rdtsc\n\t"
- "mov %%edx, %%ecx\n\t"
- :"=A" (x));
- return x;
+** Print the value of a register for tracing purposes:
+*/
+static void memTracePrint(FILE *out, Mem *p){
+ if( p->flags & MEM_Null ){
+ fprintf(out, " NULL");
+ }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
+ fprintf(out, " si:%lld", p->u.i);
+ }else if( p->flags & MEM_Int ){
+ fprintf(out, " i:%lld", p->u.i);
+ }else if( p->flags & MEM_Real ){
+ fprintf(out, " r:%g", p->r);
+ }else{
+ char zBuf[200];
+ sqlite3VdbeMemPrettyPrint(p, zBuf);
+ fprintf(out, " ");
+ fprintf(out, "%s", zBuf);
+ }
}
+static void registerTrace(FILE *out, int iReg, Mem *p){
+ fprintf(out, "REG[%d] = ", iReg);
+ memTracePrint(out, p);
+ fprintf(out, "\n");
+}
+#endif
+
+#ifdef SQLITE_DEBUG
+# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M)
+#else
+# define REGISTER_TRACE(R,M)
+#endif
+
+
+#ifdef VDBE_PROFILE
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+#include "hwtime.h"
+
#endif
/*
@@ -412,6 +487,22 @@ __inline__ unsigned long long int hwtime(void){
#define CHECK_FOR_INTERRUPT \
if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+#ifdef SQLITE_DEBUG
+static int fileExists(sqlite3 *db, const char *zFile){
+ int res = 0;
+ int rc = SQLITE_OK;
+#ifdef SQLITE_TEST
+ /* If we are currently testing IO errors, then do not call OsAccess() to
+ ** test for the presence of zFile. This is because any IO error that
+ ** occurs here will not be reported, causing the test to fail.
+ */
+ extern int sqlite3_io_error_pending;
+ if( sqlite3_io_error_pending<=0 )
+#endif
+ rc = sqlite3OsAccess(db->pVfs, zFile, SQLITE_ACCESS_EXISTS, &res);
+ return (res && rc==SQLITE_OK);
+}
+#endif
/*
** Execute as much of a VDBE program as we can then return.
@@ -429,7 +520,7 @@ __inline__ unsigned long long int hwtime(void){
** return SQLITE_BUSY.
**
** If an error occurs, an error message is written to memory obtained
-** from sqliteMalloc() and p->zErrMsg is made to point to that memory.
+** from sqlite3_malloc() and p->zErrMsg is made to point to that memory.
** The error code is stored in p->rc and this routine returns SQLITE_ERROR.
**
** If the callback ever returns non-zero, then the program exits
@@ -452,21 +543,22 @@ int sqlite3VdbeExec(
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
u8 encoding = ENC(db); /* The database encoding */
- Mem *pTos; /* Top entry in the operand stack */
+ Mem *pIn1, *pIn2, *pIn3; /* Input operands */
+ Mem *pOut; /* Output operand */
+ u8 opProperty;
+ int iCompare = 0; /* Result of last OP_Compare operation */
+ int *aPermute = 0; /* Permuation of columns for OP_Compare */
#ifdef VDBE_PROFILE
- unsigned long long start; /* CPU clock count at start of opcode */
+ u64 start; /* CPU clock count at start of opcode */
int origPc; /* Program counter at start of opcode */
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int nProgressOps = 0; /* Opcodes executed since progress callback. */
#endif
-#ifndef NDEBUG
- Mem *pStackLimit;
-#endif
- if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
+ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
assert( db->magic==SQLITE_MAGIC_BUSY );
- pTos = p->pTos;
+ sqlite3BtreeMutexArrayEnter(&p->aMutex);
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
@@ -475,17 +567,14 @@ int sqlite3VdbeExec(
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
p->rc = SQLITE_OK;
assert( p->explain==0 );
- if( p->popStack ){
- popStack(&pTos, p->popStack);
- p->popStack = 0;
- }
- p->resOnStack = 0;
+ p->pResultSet = 0;
db->busyHandler.nBusy = 0;
CHECK_FOR_INTERRUPT;
sqlite3VdbeIOTraceSql(p);
#ifdef SQLITE_DEBUG
- if( (p->db->flags & SQLITE_VdbeListing)!=0
- || sqlite3OsFileExists("vdbe_explain")
+ sqlite3BeginBenignMalloc();
+ if( p->pc==0
+ && ((p->db->flags & SQLITE_VdbeListing) || fileExists(db, "vdbe_explain"))
){
int i;
printf("VDBE Program Listing:\n");
@@ -494,17 +583,17 @@ int sqlite3VdbeExec(
sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
}
}
- if( sqlite3OsFileExists("vdbe_trace") ){
+ if( fileExists(db, "vdbe_trace") ){
p->trace = stdout;
}
+ sqlite3EndBenignMalloc();
#endif
for(pc=p->pc; rc==SQLITE_OK; pc++){
assert( pc>=0 && pc<p->nOp );
- assert( pTos<=&p->aStack[pc] );
- if( sqlite3MallocFailed() ) goto no_mem;
+ if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
origPc = pc;
- start = hwtime();
+ start = sqlite3Hwtime();
#endif
pOp = &p->aOp[pc];
@@ -518,8 +607,12 @@ int sqlite3VdbeExec(
}
sqlite3VdbePrintOp(p->trace, pc, pOp);
}
- if( p->trace==0 && pc==0 && sqlite3OsFileExists("vdbe_sqltrace") ){
- sqlite3VdbePrintSql(p);
+ if( p->trace==0 && pc==0 ){
+ sqlite3BeginBenignMalloc();
+ if( fileExists(db, "vdbe_sqltrace") ){
+ sqlite3VdbePrintSql(p);
+ }
+ sqlite3EndBenignMalloc();
}
#endif
@@ -551,7 +644,7 @@ int sqlite3VdbeExec(
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
if( prc!=0 ){
rc = SQLITE_INTERRUPT;
- goto vdbe_halt;
+ goto vdbe_error_halt;
}
nProgressOps = 0;
}
@@ -559,22 +652,63 @@ int sqlite3VdbeExec(
}
#endif
-#ifndef NDEBUG
- /* This is to check that the return value of static function
- ** opcodeNoPush() (see vdbeaux.c) returns values that match the
- ** implementation of the virtual machine in this file. If
- ** opcodeNoPush() returns non-zero, then the stack is guarenteed
- ** not to grow when the opcode is executed. If it returns zero, then
- ** the stack may grow by at most 1.
+ /* Do common setup processing for any opcode that is marked
+ ** with the "out2-prerelease" tag. Such opcodes have a single
+ ** output which is specified by the P2 parameter. The P2 register
+ ** is initialized to a NULL.
+ */
+ opProperty = opcodeProperty[pOp->opcode];
+ if( (opProperty & OPFLG_OUT2_PRERELEASE)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=p->nMem );
+ pOut = &p->aMem[pOp->p2];
+ sqlite3VdbeMemReleaseExternal(pOut);
+ pOut->flags = MEM_Null;
+ }else
+
+ /* Do common setup for opcodes marked with one of the following
+ ** combinations of properties.
**
- ** The global wrapper function sqlite3VdbeOpcodeUsesStack() is not
- ** available if NDEBUG is defined at build time.
- */
- pStackLimit = pTos;
- if( !sqlite3VdbeOpcodeNoPush(pOp->opcode) ){
- pStackLimit++;
+ ** in1
+ ** in1 in2
+ ** in1 in2 out3
+ ** in1 in3
+ **
+ ** Variables pIn1, pIn2, and pIn3 are made to point to appropriate
+ ** registers for inputs. Variable pOut points to the output register.
+ */
+ if( (opProperty & OPFLG_IN1)!=0 ){
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=p->nMem );
+ pIn1 = &p->aMem[pOp->p1];
+ REGISTER_TRACE(pOp->p1, pIn1);
+ if( (opProperty & OPFLG_IN2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=p->nMem );
+ pIn2 = &p->aMem[pOp->p2];
+ REGISTER_TRACE(pOp->p2, pIn2);
+ if( (opProperty & OPFLG_OUT3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=p->nMem );
+ pOut = &p->aMem[pOp->p3];
+ }
+ }else if( (opProperty & OPFLG_IN3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=p->nMem );
+ pIn3 = &p->aMem[pOp->p3];
+ REGISTER_TRACE(pOp->p3, pIn3);
+ }
+ }else if( (opProperty & OPFLG_IN2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=p->nMem );
+ pIn2 = &p->aMem[pOp->p2];
+ REGISTER_TRACE(pOp->p2, pIn2);
+ }else if( (opProperty & OPFLG_IN3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=p->nMem );
+ pIn3 = &p->aMem[pOp->p3];
+ REGISTER_TRACE(pOp->p3, pIn3);
}
-#endif
switch( pOp->opcode ){
@@ -596,10 +730,10 @@ int sqlite3VdbeExec(
** case statement is followed by a comment of the form "/# same as ... #/"
** that comment is used to determine the particular value of the opcode.
**
-** If a comment on the same line as the "case OP_" construction contains
-** the word "no-push", then the opcode is guarenteed not to grow the
-** vdbe stack when it is executed. See function opcode() in
-** vdbeaux.c for details.
+** Other keywords in the comment that follows each case are used to
+** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[].
+** Keywords include: in1, in2, in3, out2_prerelease, out2, out3. See
+** the mkopcodeh.awk script for additional information.
**
** Documentation about VDBE opcodes is generated by scanning this file
** for lines of that contain "Opcode:". That line and all subsequent
@@ -613,50 +747,66 @@ int sqlite3VdbeExec(
**
*****************************************************************************/
-/* Opcode: Goto * P2 *
+/* Opcode: Goto * P2 * * *
**
** An unconditional jump to address P2.
** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
*/
-case OP_Goto: { /* no-push */
+case OP_Goto: { /* jump */
CHECK_FOR_INTERRUPT;
pc = pOp->p2 - 1;
break;
}
-/* Opcode: Gosub * P2 *
+/* Opcode: Gosub P1 P2 * * *
**
-** Push the current address plus 1 onto the return address stack
+** Write the current address onto register P1
** and then jump to address P2.
-**
-** The return address stack is of limited depth. If too many
-** OP_Gosub operations occur without intervening OP_Returns, then
-** the return address stack will fill up and processing will abort
-** with a fatal error.
*/
-case OP_Gosub: { /* no-push */
- assert( p->returnDepth<sizeof(p->returnStack)/sizeof(p->returnStack[0]) );
- p->returnStack[p->returnDepth++] = pc+1;
+case OP_Gosub: { /* jump */
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=p->nMem );
+ pIn1 = &p->aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Dyn)==0 );
+ pIn1->flags = MEM_Int;
+ pIn1->u.i = pc;
+ REGISTER_TRACE(pOp->p1, pIn1);
pc = pOp->p2 - 1;
break;
}
-/* Opcode: Return * * *
+/* Opcode: Return P1 * * * *
**
-** Jump immediately to the next instruction after the last unreturned
-** OP_Gosub. If an OP_Return has occurred for all OP_Gosubs, then
-** processing aborts with a fatal error.
+** Jump to the next instruction after the address in register P1.
*/
-case OP_Return: { /* no-push */
- assert( p->returnDepth>0 );
- p->returnDepth--;
- pc = p->returnStack[p->returnDepth] - 1;
+case OP_Return: { /* in1 */
+ assert( pIn1->flags & MEM_Int );
+ pc = pIn1->u.i;
break;
}
-/* Opcode: Halt P1 P2 P3
+/* Opcode: Yield P1 * * * *
+**
+** Swap the program counter with the value in register P1.
+*/
+case OP_Yield: {
+ int pcDest;
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=p->nMem );
+ pIn1 = &p->aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Dyn)==0 );
+ pIn1->flags = MEM_Int;
+ pcDest = pIn1->u.i;
+ pIn1->u.i = pc;
+ REGISTER_TRACE(pOp->p1, pIn1);
+ pc = pcDest;
+ break;
+}
+
+
+/* Opcode: Halt P1 P2 * P4 *
**
** Exit immediately. All open cursors, Fifos, etc are closed
** automatically.
@@ -669,194 +819,152 @@ case OP_Return: { /* no-push */
** then back out all changes that have occurred during this execution of the
** VDBE, but do not rollback the transaction.
**
-** If P3 is not null then it is an error message string.
+** If P4 is not null then it is an error message string.
**
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
** every program. So a jump past the last instruction of the program
** is the same as executing Halt.
*/
-case OP_Halt: { /* no-push */
- p->pTos = pTos;
+case OP_Halt: {
p->rc = pOp->p1;
p->pc = pc;
p->errorAction = pOp->p2;
- if( pOp->p3 ){
- sqlite3SetString(&p->zErrMsg, pOp->p3, (char*)0);
+ if( pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK );
if( rc==SQLITE_BUSY ){
- p->rc = SQLITE_BUSY;
- return SQLITE_BUSY;
+ p->rc = rc = SQLITE_BUSY;
+ }else{
+ rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
}
- return p->rc ? SQLITE_ERROR : SQLITE_DONE;
+ goto vdbe_return;
}
-/* Opcode: Integer P1 * *
+/* Opcode: Integer P1 P2 * * *
**
-** The 32-bit integer value P1 is pushed onto the stack.
+** The 32-bit integer value P1 is written into register P2.
*/
-case OP_Integer: {
- pTos++;
- pTos->flags = MEM_Int;
- pTos->u.i = pOp->p1;
+case OP_Integer: { /* out2-prerelease */
+ pOut->flags = MEM_Int;
+ pOut->u.i = pOp->p1;
break;
}
-/* Opcode: Int64 * * P3
+/* Opcode: Int64 * P2 * P4 *
**
-** P3 is a string representation of an integer. Convert that integer
-** to a 64-bit value and push it onto the stack.
+** P4 is a pointer to a 64-bit integer value.
+** Write that value into register P2.
*/
-case OP_Int64: {
- pTos++;
- assert( pOp->p3!=0 );
- pTos->flags = MEM_Str|MEM_Static|MEM_Term;
- pTos->z = pOp->p3;
- pTos->n = strlen(pTos->z);
- pTos->enc = SQLITE_UTF8;
- pTos->u.i = sqlite3VdbeIntValue(pTos);
- pTos->flags |= MEM_Int;
+case OP_Int64: { /* out2-prerelease */
+ assert( pOp->p4.pI64!=0 );
+ pOut->flags = MEM_Int;
+ pOut->u.i = *pOp->p4.pI64;
break;
}
-/* Opcode: Real * * P3
+/* Opcode: Real * P2 * P4 *
**
-** The string value P3 is converted to a real and pushed on to the stack.
+** P4 is a pointer to a 64-bit floating point value.
+** Write that value into register P2.
*/
-case OP_Real: { /* same as TK_FLOAT, */
- pTos++;
- pTos->flags = MEM_Str|MEM_Static|MEM_Term;
- pTos->z = pOp->p3;
- pTos->n = strlen(pTos->z);
- pTos->enc = SQLITE_UTF8;
- pTos->r = sqlite3VdbeRealValue(pTos);
- pTos->flags |= MEM_Real;
- sqlite3VdbeChangeEncoding(pTos, encoding);
+case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
+ pOut->flags = MEM_Real;
+ assert( !sqlite3IsNaN(*pOp->p4.pReal) );
+ pOut->r = *pOp->p4.pReal;
break;
}
-/* Opcode: String8 * * P3
+/* Opcode: String8 * P2 * P4 *
**
-** P3 points to a nul terminated UTF-8 string. This opcode is transformed
+** P4 points to a nul terminated UTF-8 string. This opcode is transformed
** into an OP_String before it is executed for the first time.
*/
-case OP_String8: { /* same as TK_STRING */
- assert( pOp->p3!=0 );
+case OP_String8: { /* same as TK_STRING, out2-prerelease */
+ assert( pOp->p4.z!=0 );
pOp->opcode = OP_String;
- pOp->p1 = strlen(pOp->p3);
- assert( SQLITE_MAX_SQL_LENGTH < SQLITE_MAX_LENGTH );
- assert( pOp->p1 < SQLITE_MAX_LENGTH );
+ pOp->p1 = strlen(pOp->p4.z);
#ifndef SQLITE_OMIT_UTF16
if( encoding!=SQLITE_UTF8 ){
- pTos++;
- sqlite3VdbeMemSetStr(pTos, pOp->p3, -1, SQLITE_UTF8, SQLITE_STATIC);
- if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pTos, encoding) ) goto no_mem;
- if( SQLITE_OK!=sqlite3VdbeMemDynamicify(pTos) ) goto no_mem;
- pTos->flags &= ~(MEM_Dyn);
- pTos->flags |= MEM_Static;
- if( pOp->p3type==P3_DYNAMIC ){
- sqliteFree(pOp->p3);
+ sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
+ if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
+ if( SQLITE_OK!=sqlite3VdbeMemMakeWriteable(pOut) ) goto no_mem;
+ pOut->zMalloc = 0;
+ pOut->flags |= MEM_Static;
+ pOut->flags &= ~MEM_Dyn;
+ if( pOp->p4type==P4_DYNAMIC ){
+ sqlite3DbFree(db, pOp->p4.z);
}
- pOp->p3type = P3_DYNAMIC;
- pOp->p3 = pTos->z;
- pOp->p1 = pTos->n;
- assert( pOp->p1 < SQLITE_MAX_LENGTH ); /* Due to SQLITE_MAX_SQL_LENGTH */
+ pOp->p4type = P4_DYNAMIC;
+ pOp->p4.z = pOut->z;
+ pOp->p1 = pOut->n;
+ if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
+ }
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
#endif
- /* Otherwise fall through to the next case, OP_String */
+ if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
+ }
+ /* Fall through to the next case, OP_String */
}
-/* Opcode: String P1 * P3
-**
-** The string value P3 of length P1 (bytes) is pushed onto the stack.
-*/
-case OP_String: {
- assert( pOp->p1 < SQLITE_MAX_LENGTH ); /* Due to SQLITE_MAX_SQL_LENGTH */
- pTos++;
- assert( pOp->p3!=0 );
- pTos->flags = MEM_Str|MEM_Static|MEM_Term;
- pTos->z = pOp->p3;
- pTos->n = pOp->p1;
- pTos->enc = encoding;
+/* Opcode: String P1 P2 * P4 *
+**
+** The string value P4 of length P1 (bytes) is stored in register P2.
+*/
+case OP_String: { /* out2-prerelease */
+ assert( pOp->p4.z!=0 );
+ pOut->flags = MEM_Str|MEM_Static|MEM_Term;
+ pOut->z = pOp->p4.z;
+ pOut->n = pOp->p1;
+ pOut->enc = encoding;
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Null * * *
+/* Opcode: Null * P2 * * *
**
-** Push a NULL onto the stack.
+** Write a NULL into register P2.
*/
-case OP_Null: {
- pTos++;
- pTos->flags = MEM_Null;
- pTos->n = 0;
+case OP_Null: { /* out2-prerelease */
break;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
-/* Opcode: HexBlob * * P3
-**
-** P3 is an UTF-8 SQL hex encoding of a blob. The blob is pushed onto the
-** vdbe stack.
+/* Opcode: Blob P1 P2 * P4
**
-** The first time this instruction executes, in transforms itself into a
-** 'Blob' opcode with a binary blob as P3.
-*/
-case OP_HexBlob: { /* same as TK_BLOB */
- pOp->opcode = OP_Blob;
- pOp->p1 = strlen(pOp->p3)/2;
- assert( SQLITE_MAX_SQL_LENGTH < SQLITE_MAX_LENGTH );
- assert( pOp->p1 < SQLITE_MAX_LENGTH );
- if( pOp->p1 ){
- char *zBlob = sqlite3HexToBlob(pOp->p3);
- if( !zBlob ) goto no_mem;
- if( pOp->p3type==P3_DYNAMIC ){
- sqliteFree(pOp->p3);
- }
- pOp->p3 = zBlob;
- pOp->p3type = P3_DYNAMIC;
- }else{
- if( pOp->p3type==P3_DYNAMIC ){
- sqliteFree(pOp->p3);
- }
- pOp->p3type = P3_STATIC;
- pOp->p3 = "";
- }
-
- /* Fall through to the next case, OP_Blob. */
-}
-
-/* Opcode: Blob P1 * P3
-**
-** P3 points to a blob of data P1 bytes long. Push this
-** value onto the stack. This instruction is not coded directly
+** P4 points to a blob of data P1 bytes long. Store this
+** blob in register P2. This instruction is not coded directly
** by the compiler. Instead, the compiler layer specifies
** an OP_HexBlob opcode, with the hex string representation of
-** the blob as P3. This opcode is transformed to an OP_Blob
+** the blob as P4. This opcode is transformed to an OP_Blob
** the first time it is executed.
*/
-case OP_Blob: {
- pTos++;
- assert( pOp->p1 < SQLITE_MAX_LENGTH ); /* Due to SQLITE_MAX_SQL_LENGTH */
- sqlite3VdbeMemSetStr(pTos, pOp->p3, pOp->p1, 0, 0);
- pTos->enc = encoding;
+case OP_Blob: { /* out2-prerelease */
+ assert( pOp->p1 <= SQLITE_MAX_LENGTH );
+ sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
+ pOut->enc = encoding;
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
#endif /* SQLITE_OMIT_BLOB_LITERAL */
-/* Opcode: Variable P1 * *
+/* Opcode: Variable P1 P2 * * *
**
-** Push the value of variable P1 onto the stack. A variable is
+** The value of variable P1 is written into register P2. A variable is
** an unknown in the original SQL string as handed to sqlite3_compile().
-** Any occurance of the '?' character in the original SQL is considered
+** Any occurrence of the '?' character in the original SQL is considered
** a variable. Variables in the SQL string are number from left to
** right beginning with 1. The values of variables are set using the
** sqlite3_bind() API.
*/
-case OP_Variable: {
+case OP_Variable: { /* out2-prerelease */
int j = pOp->p1 - 1;
Mem *pVar;
assert( j>=0 && j<p->nVar );
@@ -865,364 +973,287 @@ case OP_Variable: {
if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
}
- pTos++;
- sqlite3VdbeMemShallowCopy(pTos, &p->aVar[j], MEM_Static);
- break;
-}
-
-/* Opcode: Pop P1 * *
-**
-** P1 elements are popped off of the top of stack and discarded.
-*/
-case OP_Pop: { /* no-push */
- assert( pOp->p1>=0 );
- popStack(&pTos, pOp->p1);
- assert( pTos>=&p->aStack[-1] );
+ sqlite3VdbeMemShallowCopy(pOut, &p->aVar[j], MEM_Static);
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Dup P1 P2 *
-**
-** A copy of the P1-th element of the stack
-** is made and pushed onto the top of the stack.
-** The top of the stack is element 0. So the
-** instruction "Dup 0 0 0" will make a copy of the
-** top of the stack.
-**
-** If the content of the P1-th element is a dynamically
-** allocated string, then a new copy of that string
-** is made if P2==0. If P2!=0, then just a pointer
-** to the string is copied.
+/* Opcode: Move P1 P2 P3 * *
**
-** Also see the Pull instruction.
+** Move the values in register P1..P1+P3-1 over into
+** registers P2..P2+P3-1. Registers P1..P1+P1-1 are
+** left holding a NULL. It is an error for register ranges
+** P1..P1+P3-1 and P2..P2+P3-1 to overlap.
*/
-case OP_Dup: {
- Mem *pFrom = &pTos[-pOp->p1];
- assert( pFrom<=pTos && pFrom>=p->aStack );
- pTos++;
- sqlite3VdbeMemShallowCopy(pTos, pFrom, MEM_Ephem);
- if( pOp->p2 ){
- Deephemeralize(pTos);
+case OP_Move: {
+ char *zMalloc;
+ int n = pOp->p3;
+ int p1 = pOp->p1;
+ int p2 = pOp->p2;
+ assert( n>0 );
+ assert( p1>0 );
+ assert( p1+n<p->nMem );
+ pIn1 = &p->aMem[p1];
+ assert( p2>0 );
+ assert( p2+n<p->nMem );
+ pOut = &p->aMem[p2];
+ assert( p1+n<=p2 || p2+n<=p1 );
+ while( n-- ){
+ zMalloc = pOut->zMalloc;
+ pOut->zMalloc = 0;
+ sqlite3VdbeMemMove(pOut, pIn1);
+ pIn1->zMalloc = zMalloc;
+ REGISTER_TRACE(p2++, pOut);
+ pIn1++;
+ pOut++;
}
break;
}
-/* Opcode: Pull P1 * *
+/* Opcode: Copy P1 P2 * * *
**
-** The P1-th element is removed from its current location on
-** the stack and pushed back on top of the stack. The
-** top of the stack is element 0, so "Pull 0 0 0" is
-** a no-op. "Pull 1 0 0" swaps the top two elements of
-** the stack.
+** Make a copy of register P1 into register P2.
**
-** See also the Dup instruction.
+** This instruction makes a deep copy of the value. A duplicate
+** is made of any string or blob constant. See also OP_SCopy.
*/
-case OP_Pull: { /* no-push */
- Mem *pFrom = &pTos[-pOp->p1];
- int i;
- Mem ts;
-
- ts = *pFrom;
- Deephemeralize(pTos);
- for(i=0; i<pOp->p1; i++, pFrom++){
- Deephemeralize(&pFrom[1]);
- assert( (pFrom[1].flags & MEM_Ephem)==0 );
- *pFrom = pFrom[1];
- if( pFrom->flags & MEM_Short ){
- assert( pFrom->flags & (MEM_Str|MEM_Blob) );
- assert( pFrom->z==pFrom[1].zShort );
- pFrom->z = pFrom->zShort;
- }
- }
- *pTos = ts;
- if( pTos->flags & MEM_Short ){
- assert( pTos->flags & (MEM_Str|MEM_Blob) );
- assert( pTos->z==pTos[-pOp->p1].zShort );
- pTos->z = pTos->zShort;
- }
+case OP_Copy: {
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=p->nMem );
+ pIn1 = &p->aMem[pOp->p1];
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=p->nMem );
+ pOut = &p->aMem[pOp->p2];
+ assert( pOut!=pIn1 );
+ sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
+ Deephemeralize(pOut);
+ REGISTER_TRACE(pOp->p2, pOut);
break;
}
-/* Opcode: Push P1 * *
-**
-** Overwrite the value of the P1-th element down on the
-** stack (P1==0 is the top of the stack) with the value
-** of the top of the stack. Then pop the top of the stack.
-*/
-case OP_Push: { /* no-push */
- Mem *pTo = &pTos[-pOp->p1];
-
- assert( pTo>=p->aStack );
- sqlite3VdbeMemMove(pTo, pTos);
- pTos--;
+/* Opcode: SCopy P1 P2 * * *
+**
+** Make a shallow copy of register P1 into register P2.
+**
+** This instruction makes a shallow copy of the value. If the value
+** is a string or blob, then the copy is only a pointer to the
+** original and hence if the original changes so will the copy.
+** Worse, if the original is deallocated, the copy becomes invalid.
+** Thus the program must guarantee that the original will not change
+** during the lifetime of the copy. Use OP_Copy to make a complete
+** copy.
+*/
+case OP_SCopy: {
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=p->nMem );
+ pIn1 = &p->aMem[pOp->p1];
+ REGISTER_TRACE(pOp->p1, pIn1);
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=p->nMem );
+ pOut = &p->aMem[pOp->p2];
+ assert( pOut!=pIn1 );
+ sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
+ REGISTER_TRACE(pOp->p2, pOut);
break;
}
-/* Opcode: Callback P1 * *
+/* Opcode: ResultRow P1 P2 * * *
**
-** The top P1 values on the stack represent a single result row from
-** a query. This opcode causes the sqlite3_step() call to terminate
+** The registers P1 through P1+P2-1 contain a single row of
+** results. This opcode causes the sqlite3_step() call to terminate
** with an SQLITE_ROW return code and it sets up the sqlite3_stmt
** structure to provide access to the top P1 values as the result
-** row. When the sqlite3_step() function is run again, the top P1
-** values will be automatically popped from the stack before the next
-** instruction executes.
+** row.
*/
-case OP_Callback: { /* no-push */
+case OP_ResultRow: {
Mem *pMem;
- Mem *pFirstColumn;
- assert( p->nResColumn==pOp->p1 );
-
- /* Data in the pager might be moved or changed out from under us
- ** in between the return from this sqlite3_step() call and the
- ** next call to sqlite3_step(). So deephermeralize everything on
- ** the stack. Note that ephemeral data is never stored in memory
- ** cells so we do not have to worry about them.
- */
- pFirstColumn = &pTos[0-pOp->p1];
- for(pMem = p->aStack; pMem<pFirstColumn; pMem++){
- Deephemeralize(pMem);
- }
+ int i;
+ assert( p->nResColumn==pOp->p2 );
+ assert( pOp->p1>0 );
+ assert( pOp->p1+pOp->p2<=p->nMem );
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
/* Make sure the results of the current row are \000 terminated
- ** and have an assigned type. The results are deephemeralized as
+ ** and have an assigned type. The results are de-ephemeralized as
** as side effect.
*/
- for(; pMem<=pTos; pMem++ ){
- sqlite3VdbeMemNulTerminate(pMem);
- storeTypeInfo(pMem, encoding);
+ pMem = p->pResultSet = &p->aMem[pOp->p1];
+ for(i=0; i<pOp->p2; i++){
+ sqlite3VdbeMemNulTerminate(&pMem[i]);
+ storeTypeInfo(&pMem[i], encoding);
+ REGISTER_TRACE(pOp->p1+i, &pMem[i]);
}
+ if( db->mallocFailed ) goto no_mem;
- /* Set up the statement structure so that it will pop the current
- ** results from the stack when the statement returns.
+ /* Return SQLITE_ROW
*/
- p->resOnStack = 1;
p->nCallback++;
- p->popStack = pOp->p1;
p->pc = pc + 1;
- p->pTos = pTos;
- return SQLITE_ROW;
+ rc = SQLITE_ROW;
+ goto vdbe_return;
}
-/* Opcode: Concat P1 P2 *
+/* Opcode: Concat P1 P2 P3 * *
+**
+** Add the text in register P1 onto the end of the text in
+** register P2 and store the result in register P3.
+** If either the P1 or P2 text are NULL then store NULL in P3.
**
-** Look at the first P1+2 elements of the stack. Append them all
-** together with the lowest element first. The original P1+2 elements
-** are popped from the stack if P2==0 and retained if P2==1. If
-** any element of the stack is NULL, then the result is NULL.
+** P3 = P2 || P1
**
-** When P1==1, this routine makes a copy of the top stack element
-** into memory obtained from sqliteMalloc().
+** It is illegal for P1 and P3 to be the same register. Sometimes,
+** if P3 is the same register as P2, the implementation is able
+** to avoid a memcpy().
*/
-case OP_Concat: { /* same as TK_CONCAT */
- char *zNew;
+case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
i64 nByte;
- int nField;
- int i, j;
- Mem *pTerm;
-
- /* Loop through the stack elements to see how long the result will be. */
- nField = pOp->p1 + 2;
- pTerm = &pTos[1-nField];
- nByte = 0;
- for(i=0; i<nField; i++, pTerm++){
- assert( pOp->p2==0 || (pTerm->flags&MEM_Str) );
- if( pTerm->flags&MEM_Null ){
- nByte = -1;
- break;
- }
- ExpandBlob(pTerm);
- Stringify(pTerm, encoding);
- nByte += pTerm->n;
- }
-
- if( nByte<0 ){
- /* If nByte is less than zero, then there is a NULL value on the stack.
- ** In this case just pop the values off the stack (if required) and
- ** push on a NULL.
- */
- if( pOp->p2==0 ){
- popStack(&pTos, nField);
- }
- pTos++;
- pTos->flags = MEM_Null;
- }else{
- /* Otherwise malloc() space for the result and concatenate all the
- ** stack values.
- */
- if( nByte+2>SQLITE_MAX_LENGTH ){
- goto too_big;
- }
- zNew = sqliteMallocRaw( nByte+2 );
- if( zNew==0 ) goto no_mem;
- j = 0;
- pTerm = &pTos[1-nField];
- for(i=j=0; i<nField; i++, pTerm++){
- int n = pTerm->n;
- assert( pTerm->flags & (MEM_Str|MEM_Blob) );
- memcpy(&zNew[j], pTerm->z, n);
- j += n;
- }
- zNew[j] = 0;
- zNew[j+1] = 0;
- assert( j==nByte );
- if( pOp->p2==0 ){
- popStack(&pTos, nField);
- }
- pTos++;
- pTos->n = j;
- pTos->flags = MEM_Str|MEM_Dyn|MEM_Term;
- pTos->xDel = 0;
- pTos->enc = encoding;
- pTos->z = zNew;
+ assert( pIn1!=pOut );
+ if( (pIn1->flags | pIn2->flags) & MEM_Null ){
+ sqlite3VdbeMemSetNull(pOut);
+ break;
+ }
+ ExpandBlob(pIn1);
+ Stringify(pIn1, encoding);
+ ExpandBlob(pIn2);
+ Stringify(pIn2, encoding);
+ nByte = pIn1->n + pIn2->n;
+ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}
+ MemSetTypeFlag(pOut, MEM_Str);
+ if( sqlite3VdbeMemGrow(pOut, nByte+2, pOut==pIn2) ){
+ goto no_mem;
+ }
+ if( pOut!=pIn2 ){
+ memcpy(pOut->z, pIn2->z, pIn2->n);
+ }
+ memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n);
+ pOut->z[nByte] = 0;
+ pOut->z[nByte+1] = 0;
+ pOut->flags |= MEM_Term;
+ pOut->n = nByte;
+ pOut->enc = encoding;
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Add * * *
+/* Opcode: Add P1 P2 P3 * *
**
-** Pop the top two elements from the stack, add them together,
-** and push the result back onto the stack. If either element
-** is a string then it is converted to a double using the atof()
-** function before the addition.
-** If either operand is NULL, the result is NULL.
+** Add the value in register P1 to the value in register P2
+** and store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: Multiply * * *
+/* Opcode: Multiply P1 P2 P3 * *
**
-** Pop the top two elements from the stack, multiply them together,
-** and push the result back onto the stack. If either element
-** is a string then it is converted to a double using the atof()
-** function before the multiplication.
-** If either operand is NULL, the result is NULL.
+**
+** Multiply the value in register P1 by the value in register P2
+** and store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: Subtract * * *
+/* Opcode: Subtract P1 P2 P3 * *
**
-** Pop the top two elements from the stack, subtract the
-** first (what was on top of the stack) from the second (the
-** next on stack)
-** and push the result back onto the stack. If either element
-** is a string then it is converted to a double using the atof()
-** function before the subtraction.
-** If either operand is NULL, the result is NULL.
+** Subtract the value in register P1 from the value in register P2
+** and store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: Divide * * *
+/* Opcode: Divide P1 P2 P3 * *
**
-** Pop the top two elements from the stack, divide the
-** first (what was on top of the stack) from the second (the
-** next on stack)
-** and push the result back onto the stack. If either element
-** is a string then it is converted to a double using the atof()
-** function before the division. Division by zero returns NULL.
-** If either operand is NULL, the result is NULL.
+** Divide the value in register P1 by the value in register P2
+** and store the result in register P3. If the value in register P2
+** is zero, then the result is NULL.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: Remainder * * *
+/* Opcode: Remainder P1 P2 P3 * *
**
-** Pop the top two elements from the stack, divide the
-** first (what was on top of the stack) from the second (the
-** next on stack)
-** and push the remainder after division onto the stack. If either element
-** is a string then it is converted to a double using the atof()
-** function before the division. Division by zero returns NULL.
+** Compute the remainder after integer division of the value in
+** register P1 by the value in register P2 and store the result in P3.
+** If the value in register P2 is zero the result is NULL.
** If either operand is NULL, the result is NULL.
*/
-case OP_Add: /* same as TK_PLUS, no-push */
-case OP_Subtract: /* same as TK_MINUS, no-push */
-case OP_Multiply: /* same as TK_STAR, no-push */
-case OP_Divide: /* same as TK_SLASH, no-push */
-case OP_Remainder: { /* same as TK_REM, no-push */
- Mem *pNos = &pTos[-1];
+case OP_Add: /* same as TK_PLUS, in1, in2, out3 */
+case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
+case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
+case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
+case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
int flags;
- assert( pNos>=p->aStack );
- flags = pTos->flags | pNos->flags;
- if( (flags & MEM_Null)!=0 ){
- Release(pTos);
- pTos--;
- Release(pTos);
- pTos->flags = MEM_Null;
- }else if( (pTos->flags & pNos->flags & MEM_Int)==MEM_Int ){
+ applyNumericAffinity(pIn1);
+ applyNumericAffinity(pIn2);
+ flags = pIn1->flags | pIn2->flags;
+ if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
+ if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){
i64 a, b;
- a = pTos->u.i;
- b = pNos->u.i;
+ a = pIn1->u.i;
+ b = pIn2->u.i;
switch( pOp->opcode ){
case OP_Add: b += a; break;
case OP_Subtract: b -= a; break;
case OP_Multiply: b *= a; break;
case OP_Divide: {
- if( a==0 ) goto divide_by_zero;
+ if( a==0 ) goto arithmetic_result_is_null;
/* Dividing the largest possible negative 64-bit integer (1<<63) by
- ** -1 returns an integer to large to store in a 64-bit data-type. On
+ ** -1 returns an integer too large to store in a 64-bit data-type. On
** some architectures, the value overflows to (1<<63). On others,
** a SIGFPE is issued. The following statement normalizes this
- ** behaviour so that all architectures behave as if integer
- ** overflow occured.
+ ** behavior so that all architectures behave as if integer
+ ** overflow occurred.
*/
- if( a==-1 && b==(((i64)1)<<63) ) a = 1;
+ if( a==-1 && b==SMALLEST_INT64 ) a = 1;
b /= a;
break;
}
default: {
- if( a==0 ) goto divide_by_zero;
+ if( a==0 ) goto arithmetic_result_is_null;
if( a==-1 ) a = 1;
b %= a;
break;
}
}
- Release(pTos);
- pTos--;
- Release(pTos);
- pTos->u.i = b;
- pTos->flags = MEM_Int;
+ pOut->u.i = b;
+ MemSetTypeFlag(pOut, MEM_Int);
}else{
double a, b;
- a = sqlite3VdbeRealValue(pTos);
- b = sqlite3VdbeRealValue(pNos);
+ a = sqlite3VdbeRealValue(pIn1);
+ b = sqlite3VdbeRealValue(pIn2);
switch( pOp->opcode ){
case OP_Add: b += a; break;
case OP_Subtract: b -= a; break;
case OP_Multiply: b *= a; break;
case OP_Divide: {
- if( a==0.0 ) goto divide_by_zero;
+ if( a==0.0 ) goto arithmetic_result_is_null;
b /= a;
break;
}
default: {
i64 ia = (i64)a;
i64 ib = (i64)b;
- if( ia==0 ) goto divide_by_zero;
+ if( ia==0 ) goto arithmetic_result_is_null;
if( ia==-1 ) ia = 1;
b = ib % ia;
break;
}
}
- if( sqlite3_isnan(b) ){
- goto divide_by_zero;
+ if( sqlite3IsNaN(b) ){
+ goto arithmetic_result_is_null;
}
- Release(pTos);
- pTos--;
- Release(pTos);
- pTos->r = b;
- pTos->flags = MEM_Real;
+ pOut->r = b;
+ MemSetTypeFlag(pOut, MEM_Real);
if( (flags & MEM_Real)==0 ){
- sqlite3VdbeIntegerAffinity(pTos);
+ sqlite3VdbeIntegerAffinity(pOut);
}
}
break;
-divide_by_zero:
- Release(pTos);
- pTos--;
- Release(pTos);
- pTos->flags = MEM_Null;
+arithmetic_result_is_null:
+ sqlite3VdbeMemSetNull(pOut);
break;
}
-/* Opcode: CollSeq * * P3
+/* Opcode: CollSeq * * P4
**
-** P3 is a pointer to a CollSeq struct. If the next call to a user function
+** P4 is a pointer to a CollSeq struct. If the next call to a user function
** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
** be returned. This is used by the built-in min(), max() and nullif()
** functions.
@@ -1231,16 +1262,17 @@ divide_by_zero:
** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c.
*/
-case OP_CollSeq: { /* no-push */
- assert( pOp->p3type==P3_COLLSEQ );
+case OP_CollSeq: {
+ assert( pOp->p4type==P4_COLLSEQ );
break;
}
-/* Opcode: Function P1 P2 P3
+/* Opcode: Function P1 P2 P3 P4 P5
**
-** Invoke a user function (P3 is a pointer to a Function structure that
-** defines the function) with P2 arguments taken from the stack. Pop all
-** arguments from the stack and push back the result.
+** Invoke a user function (P4 is a pointer to a Function structure that
+** defines the function) with P5 arguments taken from register P2 and
+** successors. The result of the function is stored in register P3.
+** Register P3 must not be one of the function inputs.
**
** P1 is a 32-bit bitmask indicating whether or not each argument to the
** function was determined to be constant at compile time. If the first
@@ -1256,40 +1288,57 @@ case OP_Function: {
Mem *pArg;
sqlite3_context ctx;
sqlite3_value **apVal;
- int n = pOp->p2;
+ int n = pOp->p5;
apVal = p->apArg;
assert( apVal || n==0 );
- pArg = &pTos[1-n];
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem) );
+ assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
+ pArg = &p->aMem[pOp->p2];
for(i=0; i<n; i++, pArg++){
apVal[i] = pArg;
storeTypeInfo(pArg, encoding);
+ REGISTER_TRACE(pOp->p2, pArg);
}
- assert( pOp->p3type==P3_FUNCDEF || pOp->p3type==P3_VDBEFUNC );
- if( pOp->p3type==P3_FUNCDEF ){
- ctx.pFunc = (FuncDef*)pOp->p3;
+ assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC );
+ if( pOp->p4type==P4_FUNCDEF ){
+ ctx.pFunc = pOp->p4.pFunc;
ctx.pVdbeFunc = 0;
}else{
- ctx.pVdbeFunc = (VdbeFunc*)pOp->p3;
+ ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc;
ctx.pFunc = ctx.pVdbeFunc->pFunc;
}
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ pOut = &p->aMem[pOp->p3];
ctx.s.flags = MEM_Null;
- ctx.s.z = 0;
+ ctx.s.db = db;
ctx.s.xDel = 0;
+ ctx.s.zMalloc = 0;
+
+ /* The output cell may already have a buffer allocated. Move
+ ** the pointer to ctx.s so in case the user-function can use
+ ** the already allocated buffer instead of allocating a new one.
+ */
+ sqlite3VdbeMemMove(&ctx.s, pOut);
+ MemSetTypeFlag(&ctx.s, MEM_Null);
+
ctx.isError = 0;
if( ctx.pFunc->needCollSeq ){
assert( pOp>p->aOp );
- assert( pOp[-1].p3type==P3_COLLSEQ );
+ assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
- ctx.pColl = (CollSeq *)pOp[-1].p3;
+ ctx.pColl = pOp[-1].p4.pColl;
}
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
(*ctx.pFunc->xFunc)(&ctx, n, apVal);
- if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
- if( sqlite3MallocFailed() ){
+ if( sqlite3SafetyOn(db) ){
+ sqlite3VdbeMemRelease(&ctx.s);
+ goto abort_due_to_misuse;
+ }
+ if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
** to return a value. The following call releases any resources
@@ -1302,234 +1351,209 @@ case OP_Function: {
sqlite3VdbeMemRelease(&ctx.s);
goto no_mem;
}
- popStack(&pTos, n);
- /* If any auxilary data functions have been called by this user function,
+ /* If any auxiliary data functions have been called by this user function,
** immediately call the destructor for any non-static values.
*/
if( ctx.pVdbeFunc ){
sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1);
- pOp->p3 = (char *)ctx.pVdbeFunc;
- pOp->p3type = P3_VDBEFUNC;
+ pOp->p4.pVdbeFunc = ctx.pVdbeFunc;
+ pOp->p4type = P4_VDBEFUNC;
}
/* If the function returned an error, throw an exception */
if( ctx.isError ){
- sqlite3SetString(&p->zErrMsg, sqlite3_value_text(&ctx.s), (char*)0);
- rc = SQLITE_ERROR;
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
+ rc = ctx.isError;
}
- /* Copy the result of the function to the top of the stack */
+ /* Copy the result of the function into register P3 */
sqlite3VdbeChangeEncoding(&ctx.s, encoding);
- pTos++;
- pTos->flags = 0;
- sqlite3VdbeMemMove(pTos, &ctx.s);
- if( sqlite3VdbeMemTooBig(pTos) ){
+ sqlite3VdbeMemMove(pOut, &ctx.s);
+ if( sqlite3VdbeMemTooBig(pOut) ){
goto too_big;
}
+ REGISTER_TRACE(pOp->p3, pOut);
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: BitAnd * * *
+/* Opcode: BitAnd P1 P2 P3 * *
**
-** Pop the top two elements from the stack. Convert both elements
-** to integers. Push back onto the stack the bit-wise AND of the
-** two elements.
-** If either operand is NULL, the result is NULL.
+** Take the bit-wise AND of the values in register P1 and P2 and
+** store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: BitOr * * *
+/* Opcode: BitOr P1 P2 P3 * *
**
-** Pop the top two elements from the stack. Convert both elements
-** to integers. Push back onto the stack the bit-wise OR of the
-** two elements.
-** If either operand is NULL, the result is NULL.
+** Take the bit-wise OR of the values in register P1 and P2 and
+** store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: ShiftLeft * * *
+/* Opcode: ShiftLeft P1 P2 P3 * *
**
-** Pop the top two elements from the stack. Convert both elements
-** to integers. Push back onto the stack the second element shifted
-** left by N bits where N is the top element on the stack.
-** If either operand is NULL, the result is NULL.
+** Shift the integer value in register P2 to the left by the
+** number of bits specified by the integer in regiser P1.
+** Store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-/* Opcode: ShiftRight * * *
+/* Opcode: ShiftRight P1 P2 P3 * *
**
-** Pop the top two elements from the stack. Convert both elements
-** to integers. Push back onto the stack the second element shifted
-** right by N bits where N is the top element on the stack.
-** If either operand is NULL, the result is NULL.
+** Shift the integer value in register P2 to the right by the
+** number of bits specified by the integer in register P1.
+** Store the result in register P3.
+** If either input is NULL, the result is NULL.
*/
-case OP_BitAnd: /* same as TK_BITAND, no-push */
-case OP_BitOr: /* same as TK_BITOR, no-push */
-case OP_ShiftLeft: /* same as TK_LSHIFT, no-push */
-case OP_ShiftRight: { /* same as TK_RSHIFT, no-push */
- Mem *pNos = &pTos[-1];
+case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */
+case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */
+case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */
+case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
i64 a, b;
- assert( pNos>=p->aStack );
- if( (pTos->flags | pNos->flags) & MEM_Null ){
- popStack(&pTos, 2);
- pTos++;
- pTos->flags = MEM_Null;
+ if( (pIn1->flags | pIn2->flags) & MEM_Null ){
+ sqlite3VdbeMemSetNull(pOut);
break;
}
- a = sqlite3VdbeIntValue(pNos);
- b = sqlite3VdbeIntValue(pTos);
+ a = sqlite3VdbeIntValue(pIn2);
+ b = sqlite3VdbeIntValue(pIn1);
switch( pOp->opcode ){
case OP_BitAnd: a &= b; break;
case OP_BitOr: a |= b; break;
case OP_ShiftLeft: a <<= b; break;
- case OP_ShiftRight: a >>= b; break;
- default: /* CANT HAPPEN */ break;
- }
- Release(pTos);
- pTos--;
- Release(pTos);
- pTos->u.i = a;
- pTos->flags = MEM_Int;
+ default: assert( pOp->opcode==OP_ShiftRight );
+ a >>= b; break;
+ }
+ pOut->u.i = a;
+ MemSetTypeFlag(pOut, MEM_Int);
break;
}
-/* Opcode: AddImm P1 * *
+/* Opcode: AddImm P1 P2 * * *
**
-** Add the value P1 to whatever is on top of the stack. The result
-** is always an integer.
+** Add the constant P2 to the value in register P1.
+** The result is always an integer.
**
-** To force the top of the stack to be an integer, just add 0.
+** To force any register to be an integer, just add 0.
*/
-case OP_AddImm: { /* no-push */
- assert( pTos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
- pTos->u.i += pOp->p1;
+case OP_AddImm: { /* in1 */
+ sqlite3VdbeMemIntegerify(pIn1);
+ pIn1->u.i += pOp->p2;
break;
}
-/* Opcode: ForceInt P1 P2 *
+/* Opcode: ForceInt P1 P2 P3 * *
**
-** Convert the top of the stack into an integer. If the current top of
-** the stack is not numeric (meaning that is is a NULL or a string that
-** does not look like an integer or floating point number) then pop the
-** stack and jump to P2. If the top of the stack is numeric then
+** Convert value in register P1 into an integer. If the value
+** in P1 is not numeric (meaning that is is a NULL or a string that
+** does not look like an integer or floating point number) then
+** jump to P2. If the value in P1 is numeric then
** convert it into the least integer that is greater than or equal to its
-** current value if P1==0, or to the least integer that is strictly
-** greater than its current value if P1==1.
+** current value if P3==0, or to the least integer that is strictly
+** greater than its current value if P3==1.
*/
-case OP_ForceInt: { /* no-push */
+case OP_ForceInt: { /* jump, in1 */
i64 v;
- assert( pTos>=p->aStack );
- applyAffinity(pTos, SQLITE_AFF_NUMERIC, encoding);
- if( (pTos->flags & (MEM_Int|MEM_Real))==0 ){
- Release(pTos);
- pTos--;
+ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
+ if( (pIn1->flags & (MEM_Int|MEM_Real))==0 ){
pc = pOp->p2 - 1;
break;
}
- if( pTos->flags & MEM_Int ){
- v = pTos->u.i + (pOp->p1!=0);
+ if( pIn1->flags & MEM_Int ){
+ v = pIn1->u.i + (pOp->p3!=0);
}else{
- /* FIX ME: should this not be assert( pTos->flags & MEM_Real ) ??? */
- sqlite3VdbeMemRealify(pTos);
- v = (int)pTos->r;
- if( pTos->r>(double)v ) v++;
- if( pOp->p1 && pTos->r==(double)v ) v++;
- }
- Release(pTos);
- pTos->u.i = v;
- pTos->flags = MEM_Int;
+ assert( pIn1->flags & MEM_Real );
+ v = (sqlite3_int64)pIn1->r;
+ if( pIn1->r>(double)v ) v++;
+ if( pOp->p3 && pIn1->r==(double)v ) v++;
+ }
+ pIn1->u.i = v;
+ MemSetTypeFlag(pIn1, MEM_Int);
break;
}
-/* Opcode: MustBeInt P1 P2 *
+/* Opcode: MustBeInt P1 P2 * * *
**
-** Force the top of the stack to be an integer. If the top of the
-** stack is not an integer and cannot be converted into an integer
-** with out data loss, then jump immediately to P2, or if P2==0
+** Force the value in register P1 to be an integer. If the value
+** in P1 is not an integer and cannot be converted into an integer
+** without data loss, then jump immediately to P2, or if P2==0
** raise an SQLITE_MISMATCH exception.
-**
-** If the top of the stack is not an integer and P2 is not zero and
-** P1 is 1, then the stack is popped. In all other cases, the depth
-** of the stack is unchanged.
*/
-case OP_MustBeInt: { /* no-push */
- assert( pTos>=p->aStack );
- applyAffinity(pTos, SQLITE_AFF_NUMERIC, encoding);
- if( (pTos->flags & MEM_Int)==0 ){
+case OP_MustBeInt: { /* jump, in1 */
+ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
+ if( (pIn1->flags & MEM_Int)==0 ){
if( pOp->p2==0 ){
rc = SQLITE_MISMATCH;
goto abort_due_to_error;
}else{
- if( pOp->p1 ) popStack(&pTos, 1);
pc = pOp->p2 - 1;
}
}else{
- Release(pTos);
- pTos->flags = MEM_Int;
+ MemSetTypeFlag(pIn1, MEM_Int);
}
break;
}
-/* Opcode: RealAffinity * * *
+/* Opcode: RealAffinity P1 * * * *
**
-** If the top of the stack is an integer, convert it to a real value.
+** If register P1 holds an integer convert it to a real value.
**
** This opcode is used when extracting information from a column that
** has REAL affinity. Such column values may still be stored as
** integers, for space efficiency, but after extraction we want them
** to have only a real value.
*/
-case OP_RealAffinity: { /* no-push */
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Int ){
- sqlite3VdbeMemRealify(pTos);
+case OP_RealAffinity: { /* in1 */
+ if( pIn1->flags & MEM_Int ){
+ sqlite3VdbeMemRealify(pIn1);
}
break;
}
#ifndef SQLITE_OMIT_CAST
-/* Opcode: ToText * * *
+/* Opcode: ToText P1 * * * *
**
-** Force the value on the top of the stack to be text.
+** Force the value in register P1 to be text.
** If the value is numeric, convert it to a string using the
** equivalent of printf(). Blob values are unchanged and
** are afterwards simply interpreted as text.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToText: { /* same as TK_TO_TEXT, no-push */
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Null ) break;
+case OP_ToText: { /* same as TK_TO_TEXT, in1 */
+ if( pIn1->flags & MEM_Null ) break;
assert( MEM_Str==(MEM_Blob>>3) );
- pTos->flags |= (pTos->flags&MEM_Blob)>>3;
- applyAffinity(pTos, SQLITE_AFF_TEXT, encoding);
- rc = ExpandBlob(pTos);
- assert( pTos->flags & MEM_Str );
- pTos->flags &= ~(MEM_Int|MEM_Real|MEM_Blob);
+ pIn1->flags |= (pIn1->flags&MEM_Blob)>>3;
+ applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
+ rc = ExpandBlob(pIn1);
+ assert( pIn1->flags & MEM_Str || db->mallocFailed );
+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob);
+ UPDATE_MAX_BLOBSIZE(pIn1);
break;
}
-/* Opcode: ToBlob * * *
+/* Opcode: ToBlob P1 * * * *
**
-** Force the value on the top of the stack to be a BLOB.
+** Force the value in register P1 to be a BLOB.
** If the value is numeric, convert it to a string first.
** Strings are simply reinterpreted as blobs with no change
** to the underlying data.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToBlob: { /* same as TK_TO_BLOB, no-push */
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Null ) break;
- if( (pTos->flags & MEM_Blob)==0 ){
- applyAffinity(pTos, SQLITE_AFF_TEXT, encoding);
- assert( pTos->flags & MEM_Str );
- pTos->flags |= MEM_Blob;
+case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
+ if( pIn1->flags & MEM_Null ) break;
+ if( (pIn1->flags & MEM_Blob)==0 ){
+ applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
+ assert( pIn1->flags & MEM_Str || db->mallocFailed );
}
- pTos->flags &= ~(MEM_Int|MEM_Real|MEM_Str);
+ MemSetTypeFlag(pIn1, MEM_Blob);
+ UPDATE_MAX_BLOBSIZE(pIn1);
break;
}
-/* Opcode: ToNumeric * * *
+/* Opcode: ToNumeric P1 * * * *
**
-** Force the value on the top of the stack to be numeric (either an
+** Force the value in register P1 to be numeric (either an
** integer or a floating-point number.)
** If the value is text or blob, try to convert it to an using the
** equivalent of atoi() or atof() and store 0 if no such conversion
@@ -1537,177 +1561,144 @@ case OP_ToBlob: { /* same as TK_TO_BLOB, no-push */
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToNumeric: { /* same as TK_TO_NUMERIC, no-push */
- assert( pTos>=p->aStack );
- if( (pTos->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){
- sqlite3VdbeMemNumerify(pTos);
+case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
+ if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){
+ sqlite3VdbeMemNumerify(pIn1);
}
break;
}
#endif /* SQLITE_OMIT_CAST */
-/* Opcode: ToInt * * *
+/* Opcode: ToInt P1 * * * *
**
-** Force the value on the top of the stack to be an integer. If
+** Force the value in register P1 be an integer. If
** The value is currently a real number, drop its fractional part.
** If the value is text or blob, try to convert it to an integer using the
** equivalent of atoi() and store 0 if no such conversion is possible.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToInt: { /* same as TK_TO_INT, no-push */
- assert( pTos>=p->aStack );
- if( (pTos->flags & MEM_Null)==0 ){
- sqlite3VdbeMemIntegerify(pTos);
+case OP_ToInt: { /* same as TK_TO_INT, in1 */
+ if( (pIn1->flags & MEM_Null)==0 ){
+ sqlite3VdbeMemIntegerify(pIn1);
}
break;
}
#ifndef SQLITE_OMIT_CAST
-/* Opcode: ToReal * * *
+/* Opcode: ToReal P1 * * * *
**
-** Force the value on the top of the stack to be a floating point number.
+** Force the value in register P1 to be a floating point number.
** If The value is currently an integer, convert it.
** If the value is text or blob, try to convert it to an integer using the
-** equivalent of atoi() and store 0 if no such conversion is possible.
+** equivalent of atoi() and store 0.0 if no such conversion is possible.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToReal: { /* same as TK_TO_REAL, no-push */
- assert( pTos>=p->aStack );
- if( (pTos->flags & MEM_Null)==0 ){
- sqlite3VdbeMemRealify(pTos);
+case OP_ToReal: { /* same as TK_TO_REAL, in1 */
+ if( (pIn1->flags & MEM_Null)==0 ){
+ sqlite3VdbeMemRealify(pIn1);
}
break;
}
#endif /* SQLITE_OMIT_CAST */
-/* Opcode: Eq P1 P2 P3
-**
-** Pop the top two elements from the stack. If they are equal, then
-** jump to instruction P2. Otherwise, continue to the next instruction.
+/* Opcode: Lt P1 P2 P3 P4 P5
**
-** If the 0x100 bit of P1 is true and either operand is NULL then take the
-** jump. If the 0x100 bit of P1 is clear then fall thru if either operand
-** is NULL.
+** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
+** jump to address P2.
**
-** If the 0x200 bit of P1 is set and either operand is NULL then
-** both operands are converted to integers prior to comparison.
-** NULL operands are converted to zero and non-NULL operands are
-** converted to 1. Thus, for example, with 0x200 set, NULL==NULL is true
-** whereas it would normally be NULL. Similarly, NULL==123 is false when
-** 0x200 is set but is NULL when the 0x200 bit of P1 is clear.
+** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
+** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
+** bit is clear then fall thru if either operand is NULL.
**
-** The least significant byte of P1 (mask 0xff) must be an affinity character -
+** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
-** to coerce both values
-** according to the affinity before the comparison is made. If the byte is
-** 0x00, then numeric affinity is used.
+** to coerce both inputs according to this affinity before the
+** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
+** affinity is used. Note that the affinity conversions are stored
+** back into the input registers P1 and P3. So this opcode can cause
+** persistent changes to registers P1 and P3.
**
** Once any conversions have taken place, and neither value is NULL,
-** the values are compared. If both values are blobs, or both are text,
-** then memcmp() is used to determine the results of the comparison. If
-** both values are numeric, then a numeric comparison is used. If the
-** two values are of different types, then they are inequal.
-**
-** If P2 is zero, do not jump. Instead, push an integer 1 onto the
-** stack if the jump would have been taken, or a 0 if not. Push a
-** NULL if either operand was NULL.
-**
-** If P3 is not NULL it is a pointer to a collating sequence (a CollSeq
-** structure) that defines how to compare text.
-*/
-/* Opcode: Ne P1 P2 P3
-**
-** This works just like the Eq opcode except that the jump is taken if
-** the operands from the stack are not equal. See the Eq opcode for
+** the values are compared. If both values are blobs then memcmp() is
+** used to determine the results of the comparison. If both values
+** are text, then the appropriate collating function specified in
+** P4 is used to do the comparison. If P4 is not specified then
+** memcmp() is used to compare text string. If both values are
+** numeric, then a numeric comparison is used. If the two values
+** are of different types, then numbers are considered less than
+** strings and strings are considered less than blobs.
+**
+** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
+** store a boolean result (either 0, or 1, or NULL) in register P2.
+*/
+/* Opcode: Ne P1 P2 P3 P4 P5
+**
+** This works just like the Lt opcode except that the jump is taken if
+** the operands in registers P1 and P3 are not equal. See the Lt opcode for
** additional information.
*/
-/* Opcode: Lt P1 P2 P3
+/* Opcode: Eq P1 P2 P3 P4 P5
**
-** This works just like the Eq opcode except that the jump is taken if
-** the 2nd element down on the stack is less than the top of the stack.
-** See the Eq opcode for additional information.
+** This works just like the Lt opcode except that the jump is taken if
+** the operands in registers P1 and P3 are equal.
+** See the Lt opcode for additional information.
*/
-/* Opcode: Le P1 P2 P3
+/* Opcode: Le P1 P2 P3 P4 P5
**
-** This works just like the Eq opcode except that the jump is taken if
-** the 2nd element down on the stack is less than or equal to the
-** top of the stack. See the Eq opcode for additional information.
+** This works just like the Lt opcode except that the jump is taken if
+** the content of register P3 is less than or equal to the content of
+** register P1. See the Lt opcode for additional information.
*/
-/* Opcode: Gt P1 P2 P3
+/* Opcode: Gt P1 P2 P3 P4 P5
**
-** This works just like the Eq opcode except that the jump is taken if
-** the 2nd element down on the stack is greater than the top of the stack.
-** See the Eq opcode for additional information.
+** This works just like the Lt opcode except that the jump is taken if
+** the content of register P3 is greater than the content of
+** register P1. See the Lt opcode for additional information.
*/
-/* Opcode: Ge P1 P2 P3
+/* Opcode: Ge P1 P2 P3 P4 P5
**
-** This works just like the Eq opcode except that the jump is taken if
-** the 2nd element down on the stack is greater than or equal to the
-** top of the stack. See the Eq opcode for additional information.
+** This works just like the Lt opcode except that the jump is taken if
+** the content of register P3 is greater than or equal to the content of
+** register P1. See the Lt opcode for additional information.
*/
-case OP_Eq: /* same as TK_EQ, no-push */
-case OP_Ne: /* same as TK_NE, no-push */
-case OP_Lt: /* same as TK_LT, no-push */
-case OP_Le: /* same as TK_LE, no-push */
-case OP_Gt: /* same as TK_GT, no-push */
-case OP_Ge: { /* same as TK_GE, no-push */
- Mem *pNos;
+case OP_Eq: /* same as TK_EQ, jump, in1, in3 */
+case OP_Ne: /* same as TK_NE, jump, in1, in3 */
+case OP_Lt: /* same as TK_LT, jump, in1, in3 */
+case OP_Le: /* same as TK_LE, jump, in1, in3 */
+case OP_Gt: /* same as TK_GT, jump, in1, in3 */
+case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
int flags;
int res;
char affinity;
- pNos = &pTos[-1];
- flags = pTos->flags|pNos->flags;
+ flags = pIn1->flags|pIn3->flags;
- /* If either value is a NULL P2 is not zero, take the jump if the least
- ** significant byte of P1 is true. If P2 is zero, then push a NULL onto
- ** the stack.
- */
if( flags&MEM_Null ){
- if( (pOp->p1 & 0x200)!=0 ){
- /* The 0x200 bit of P1 means, roughly "do not treat NULL as the
- ** magic SQL value it normally is - treat it as if it were another
- ** integer".
- **
- ** With 0x200 set, if either operand is NULL then both operands
- ** are converted to integers prior to being passed down into the
- ** normal comparison logic below. NULL operands are converted to
- ** zero and non-NULL operands are converted to 1. Thus, for example,
- ** with 0x200 set, NULL==NULL is true whereas it would normally
- ** be NULL. Similarly, NULL!=123 is true.
- */
- sqlite3VdbeMemSetInt64(pTos, (pTos->flags & MEM_Null)==0);
- sqlite3VdbeMemSetInt64(pNos, (pNos->flags & MEM_Null)==0);
- }else{
- /* If the 0x200 bit of P1 is clear and either operand is NULL then
- ** the result is always NULL. The jump is taken if the 0x100 bit
- ** of P1 is set.
- */
- popStack(&pTos, 2);
- if( pOp->p2 ){
- if( pOp->p1 & 0x100 ){
- pc = pOp->p2-1;
- }
- }else{
- pTos++;
- pTos->flags = MEM_Null;
- }
- break;
+ /* If either operand is NULL then the result is always NULL.
+ ** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
+ */
+ if( pOp->p5 & SQLITE_STOREP2 ){
+ pOut = &p->aMem[pOp->p2];
+ MemSetTypeFlag(pOut, MEM_Null);
+ REGISTER_TRACE(pOp->p2, pOut);
+ }else if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ pc = pOp->p2-1;
}
+ break;
}
- affinity = pOp->p1 & 0xFF;
+ affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity ){
- applyAffinity(pNos, affinity, encoding);
- applyAffinity(pTos, affinity, encoding);
+ applyAffinity(pIn1, affinity, encoding);
+ applyAffinity(pIn3, affinity, encoding);
}
- assert( pOp->p3type==P3_COLLSEQ || pOp->p3==0 );
- ExpandBlob(pNos);
- ExpandBlob(pTos);
- res = sqlite3MemCompare(pNos, pTos, (CollSeq*)pOp->p3);
+ assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
+ ExpandBlob(pIn1);
+ ExpandBlob(pIn3);
+ res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
switch( pOp->opcode ){
case OP_Eq: res = res==0; break;
case OP_Ne: res = res!=0; break;
@@ -1717,264 +1708,266 @@ case OP_Ge: { /* same as TK_GE, no-push */
default: res = res>=0; break;
}
- popStack(&pTos, 2);
- if( pOp->p2 ){
- if( res ){
- pc = pOp->p2-1;
+ if( pOp->p5 & SQLITE_STOREP2 ){
+ pOut = &p->aMem[pOp->p2];
+ MemSetTypeFlag(pOut, MEM_Int);
+ pOut->u.i = res;
+ REGISTER_TRACE(pOp->p2, pOut);
+ }else if( res ){
+ pc = pOp->p2-1;
+ }
+ break;
+}
+
+/* Opcode: Permutation * * * P4 *
+**
+** Set the permuation used by the OP_Compare operator to be the array
+** of integers in P4.
+**
+** The permutation is only valid until the next OP_Permutation, OP_Compare,
+** OP_Halt, or OP_ResultRow. Typically the OP_Permutation should occur
+** immediately prior to the OP_Compare.
+*/
+case OP_Permutation: {
+ assert( pOp->p4type==P4_INTARRAY );
+ assert( pOp->p4.ai );
+ aPermute = pOp->p4.ai;
+ break;
+}
+
+/* Opcode: Compare P1 P2 P3 P4 *
+**
+** Compare to vectors of registers in reg(P1)..reg(P1+P3-1) (all this
+** one "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
+** the comparison for use by the next OP_Jump instruct.
+**
+** P4 is a KeyInfo structure that defines collating sequences and sort
+** orders for the comparison. The permutation applies to registers
+** only. The KeyInfo elements are used sequentially.
+**
+** The comparison is a sort comparison, so NULLs compare equal,
+** NULLs are less than numbers, numbers are less than strings,
+** and strings are less than blobs.
+*/
+case OP_Compare: {
+ int n = pOp->p3;
+ int i, p1, p2;
+ const KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
+ assert( n>0 );
+ assert( pKeyInfo!=0 );
+ p1 = pOp->p1;
+ assert( p1>0 && p1+n-1<p->nMem );
+ p2 = pOp->p2;
+ assert( p2>0 && p2+n-1<p->nMem );
+ for(i=0; i<n; i++){
+ int idx = aPermute ? aPermute[i] : i;
+ CollSeq *pColl; /* Collating sequence to use on this term */
+ int bRev; /* True for DESCENDING sort order */
+ REGISTER_TRACE(p1+idx, &p->aMem[p1+idx]);
+ REGISTER_TRACE(p2+idx, &p->aMem[p2+idx]);
+ assert( i<pKeyInfo->nField );
+ pColl = pKeyInfo->aColl[i];
+ bRev = pKeyInfo->aSortOrder[i];
+ iCompare = sqlite3MemCompare(&p->aMem[p1+idx], &p->aMem[p2+idx], pColl);
+ if( iCompare ){
+ if( bRev ) iCompare = -iCompare;
+ break;
}
+ }
+ aPermute = 0;
+ break;
+}
+
+/* Opcode: Jump P1 P2 P3 * *
+**
+** Jump to the instruction at address P1, P2, or P3 depending on whether
+** in the most recent OP_Compare instruction the P1 vector was less than
+** equal to, or greater than the P2 vector, respectively.
+*/
+case OP_Jump: { /* jump */
+ if( iCompare<0 ){
+ pc = pOp->p1 - 1;
+ }else if( iCompare==0 ){
+ pc = pOp->p2 - 1;
}else{
- pTos++;
- pTos->flags = MEM_Int;
- pTos->u.i = res;
+ pc = pOp->p3 - 1;
}
break;
}
-/* Opcode: And * * *
+/* Opcode: And P1 P2 P3 * *
**
-** Pop two values off the stack. Take the logical AND of the
-** two values and push the resulting boolean value back onto the
-** stack.
+** Take the logical AND of the values in registers P1 and P2 and
+** write the result into register P3.
+**
+** If either P1 or P2 is 0 (false) then the result is 0 even if
+** the other input is NULL. A NULL and true or two NULLs give
+** a NULL output.
*/
-/* Opcode: Or * * *
+/* Opcode: Or P1 P2 P3 * *
+**
+** Take the logical OR of the values in register P1 and P2 and
+** store the answer in register P3.
**
-** Pop two values off the stack. Take the logical OR of the
-** two values and push the resulting boolean value back onto the
-** stack.
+** If either P1 or P2 is nonzero (true) then the result is 1 (true)
+** even if the other input is NULL. A NULL and false or two NULLs
+** give a NULL output.
*/
-case OP_And: /* same as TK_AND, no-push */
-case OP_Or: { /* same as TK_OR, no-push */
- Mem *pNos = &pTos[-1];
- int v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
+case OP_And: /* same as TK_AND, in1, in2, out3 */
+case OP_Or: { /* same as TK_OR, in1, in2, out3 */
+ int v1, v2; /* 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
- assert( pNos>=p->aStack );
- if( pTos->flags & MEM_Null ){
+ if( pIn1->flags & MEM_Null ){
v1 = 2;
}else{
- sqlite3VdbeMemIntegerify(pTos);
- v1 = pTos->u.i==0;
+ v1 = sqlite3VdbeIntValue(pIn1)!=0;
}
- if( pNos->flags & MEM_Null ){
+ if( pIn2->flags & MEM_Null ){
v2 = 2;
}else{
- sqlite3VdbeMemIntegerify(pNos);
- v2 = pNos->u.i==0;
+ v2 = sqlite3VdbeIntValue(pIn2)!=0;
}
if( pOp->opcode==OP_And ){
- static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
+ static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
v1 = and_logic[v1*3+v2];
}else{
- static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
+ static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
v1 = or_logic[v1*3+v2];
}
- popStack(&pTos, 2);
- pTos++;
if( v1==2 ){
- pTos->flags = MEM_Null;
+ MemSetTypeFlag(pOut, MEM_Null);
}else{
- pTos->u.i = v1==0;
- pTos->flags = MEM_Int;
+ pOut->u.i = v1;
+ MemSetTypeFlag(pOut, MEM_Int);
}
break;
}
-/* Opcode: Negative * * *
+/* Opcode: Not P1 * * * *
**
-** Treat the top of the stack as a numeric quantity. Replace it
-** with its additive inverse. If the top of the stack is NULL
-** its value is unchanged.
-*/
-/* Opcode: AbsValue * * *
-**
-** Treat the top of the stack as a numeric quantity. Replace it
-** with its absolute value. If the top of the stack is NULL
-** its value is unchanged.
-*/
-case OP_Negative: /* same as TK_UMINUS, no-push */
-case OP_AbsValue: {
- assert( pTos>=p->aStack );
- if( (pTos->flags & (MEM_Real|MEM_Int|MEM_Null))==0 ){
- sqlite3VdbeMemNumerify(pTos);
- }
- if( pTos->flags & MEM_Real ){
- Release(pTos);
- if( pOp->opcode==OP_Negative || pTos->r<0.0 ){
- pTos->r = -pTos->r;
- }
- pTos->flags = MEM_Real;
- }else if( pTos->flags & MEM_Int ){
- Release(pTos);
- if( pOp->opcode==OP_Negative || pTos->u.i<0 ){
- pTos->u.i = -pTos->u.i;
- }
- pTos->flags = MEM_Int;
- }
- break;
-}
-
-/* Opcode: Not * * *
-**
-** Interpret the top of the stack as a boolean value. Replace it
-** with its complement. If the top of the stack is NULL its value
+** Interpret the value in register P1 as a boolean value. Replace it
+** with its complement. If the value in register P1 is NULL its value
** is unchanged.
*/
-case OP_Not: { /* same as TK_NOT, no-push */
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
- sqlite3VdbeMemIntegerify(pTos);
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos->u.i = !pTos->u.i;
- pTos->flags = MEM_Int;
+case OP_Not: { /* same as TK_NOT, in1 */
+ if( pIn1->flags & MEM_Null ) break; /* Do nothing to NULLs */
+ sqlite3VdbeMemIntegerify(pIn1);
+ pIn1->u.i = !pIn1->u.i;
+ assert( pIn1->flags&MEM_Int );
break;
}
-/* Opcode: BitNot * * *
+/* Opcode: BitNot P1 * * * *
**
-** Interpret the top of the stack as an value. Replace it
-** with its ones-complement. If the top of the stack is NULL its
-** value is unchanged.
-*/
-case OP_BitNot: { /* same as TK_BITNOT, no-push */
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
- sqlite3VdbeMemIntegerify(pTos);
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos->u.i = ~pTos->u.i;
- pTos->flags = MEM_Int;
- break;
-}
-
-/* Opcode: Noop * * *
-**
-** Do nothing. This instruction is often useful as a jump
-** destination.
-*/
-/*
-** The magic Explain opcode are only inserted when explain==2 (which
-** is to say when the EXPLAIN QUERY PLAN syntax is used.)
-** This opcode records information from the optimizer. It is the
-** the same as a no-op. This opcodesnever appears in a real VM program.
+** Interpret the content of register P1 as an integer. Replace it
+** with its ones-complement. If the value is originally NULL, leave
+** it unchanged.
*/
-case OP_Explain:
-case OP_Noop: { /* no-push */
+case OP_BitNot: { /* same as TK_BITNOT, in1 */
+ if( pIn1->flags & MEM_Null ) break; /* Do nothing to NULLs */
+ sqlite3VdbeMemIntegerify(pIn1);
+ pIn1->u.i = ~pIn1->u.i;
+ assert( pIn1->flags&MEM_Int );
break;
}
-/* Opcode: If P1 P2 *
-**
-** Pop a single boolean from the stack. If the boolean popped is
-** true, then jump to p2. Otherwise continue to the next instruction.
-** An integer is false if zero and true otherwise. A string is
-** false if it has zero length and true otherwise.
+/* Opcode: If P1 P2 P3 * *
**
-** If the value popped of the stack is NULL, then take the jump if P1
-** is true and fall through if P1 is false.
+** Jump to P2 if the value in register P1 is true. The value is
+** is considered true if it is numeric and non-zero. If the value
+** in P1 is NULL then take the jump if P3 is true.
*/
-/* Opcode: IfNot P1 P2 *
+/* Opcode: IfNot P1 P2 P3 * *
**
-** Pop a single boolean from the stack. If the boolean popped is
-** false, then jump to p2. Otherwise continue to the next instruction.
-** An integer is false if zero and true otherwise. A string is
-** false if it has zero length and true otherwise.
-**
-** If the value popped of the stack is NULL, then take the jump if P1
-** is true and fall through if P1 is false.
+** Jump to P2 if the value in register P1 is False. The value is
+** is considered true if it has a numeric value of zero. If the value
+** in P1 is NULL then take the jump if P3 is true.
*/
-case OP_If: /* no-push */
-case OP_IfNot: { /* no-push */
+case OP_If: /* jump, in1 */
+case OP_IfNot: { /* jump, in1 */
int c;
- assert( pTos>=p->aStack );
- if( pTos->flags & MEM_Null ){
- c = pOp->p1;
+ if( pIn1->flags & MEM_Null ){
+ c = pOp->p3;
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
- c = sqlite3VdbeIntValue(pTos);
+ c = sqlite3VdbeIntValue(pIn1);
#else
- c = sqlite3VdbeRealValue(pTos)!=0.0;
+ c = sqlite3VdbeRealValue(pIn1)!=0.0;
#endif
if( pOp->opcode==OP_IfNot ) c = !c;
}
- Release(pTos);
- pTos--;
- if( c ) pc = pOp->p2-1;
+ if( c ){
+ pc = pOp->p2-1;
+ }
break;
}
-/* Opcode: IsNull P1 P2 *
+/* Opcode: IsNull P1 P2 P3 * *
**
-** Check the top of the stack and jump to P2 if the top of the stack
-** is NULL. If P1 is positive, then pop P1 elements from the stack
-** regardless of whether or not the jump is taken. If P1 is negative,
-** pop -P1 elements from the stack only if the jump is taken and leave
-** the stack unchanged if the jump is not taken.
+** Jump to P2 if the value in register P1 is NULL. If P3 is greater
+** than zero, then check all values reg(P1), reg(P1+1),
+** reg(P1+2), ..., reg(P1+P3-1).
*/
-case OP_IsNull: { /* same as TK_ISNULL, no-push */
- if( pTos->flags & MEM_Null ){
- pc = pOp->p2-1;
- if( pOp->p1<0 ){
- popStack(&pTos, -pOp->p1);
+case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
+ int n = pOp->p3;
+ assert( pOp->p3==0 || pOp->p1>0 );
+ do{
+ if( (pIn1->flags & MEM_Null)!=0 ){
+ pc = pOp->p2 - 1;
+ break;
}
- }
- if( pOp->p1>0 ){
- popStack(&pTos, pOp->p1);
- }
+ pIn1++;
+ }while( --n > 0 );
break;
}
-/* Opcode: NotNull P1 P2 *
-**
-** Jump to P2 if the top abs(P1) values on the stack are all not NULL.
-** Regardless of whether or not the jump is taken, pop the stack
-** P1 times if P1 is greater than zero. But if P1 is negative,
-** leave the stack unchanged.
-*/
-case OP_NotNull: { /* same as TK_NOTNULL, no-push */
- int i, cnt;
- cnt = pOp->p1;
- if( cnt<0 ) cnt = -cnt;
- assert( &pTos[1-cnt] >= p->aStack );
- for(i=0; i<cnt && (pTos[1+i-cnt].flags & MEM_Null)==0; i++){}
- if( i>=cnt ) pc = pOp->p2-1;
- if( pOp->p1>0 ) popStack(&pTos, cnt);
+/* Opcode: NotNull P1 P2 * * *
+**
+** Jump to P2 if the value in register P1 is not NULL.
+*/
+case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
+ if( (pIn1->flags & MEM_Null)==0 ){
+ pc = pOp->p2 - 1;
+ }
break;
}
-/* Opcode: SetNumColumns P1 P2 *
+/* Opcode: SetNumColumns * P2 * * *
+**
+** This opcode sets the number of columns for the cursor opened by the
+** following instruction to P2.
**
-** Before the OP_Column opcode can be executed on a cursor, this
-** opcode must be called to set the number of fields in the table.
+** An OP_SetNumColumns is only useful if it occurs immediately before
+** one of the following opcodes:
**
-** This opcode sets the number of columns for cursor P1 to P2.
+** OpenRead
+** OpenWrite
+** OpenPseudo
**
-** If OP_KeyAsData is to be applied to cursor P1, it must be executed
-** before this op-code.
+** If the OP_Column opcode is to be executed on a cursor, then
+** this opcode must be present immediately before the opcode that
+** opens the cursor.
*/
-case OP_SetNumColumns: { /* no-push */
- Cursor *pC;
- assert( (pOp->p1)<p->nCursor );
- assert( p->apCsr[pOp->p1]!=0 );
- pC = p->apCsr[pOp->p1];
- pC->nField = pOp->p2;
+case OP_SetNumColumns: {
break;
}
-/* Opcode: Column P1 P2 P3
+/* Opcode: Column P1 P2 P3 P4 *
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
-** information about the format of the data.) Push onto the stack the value
-** of the P2-th column contained in the data. If there are less that (P2+1)
-** values in the record, push a NULL onto the stack.
+** information about the format of the data.) Extract the P2-th column
+** from this record. If there are less that (P2+1)
+** values in the record, extract a NULL.
+**
+** The value extracted is stored in register P3.
**
** If the KeyAsData opcode has previously executed on this cursor, then the
** field might be extracted from the key rather than the data.
**
-** If the column contains fewer than P2 fields, then push a NULL. Or
-** if P3 is of type P3_MEM, then push the P3 value. The P3 value will
-** be default value for a column that has been added using the ALTER TABLE
-** ADD COLUMN command. If P3 is an ordinary string, just push a NULL.
-** When P3 is a string it is really just a comment describing the value
-** to be pushed, not a default value.
+** If the column contains fewer than P2 fields, then extract a NULL. Or,
+** if the P4 argument is a P4_MEM use the value of the P4 argument as
+** the result.
*/
case OP_Column: {
u32 payloadSize; /* Number of bytes in the record */
@@ -1989,12 +1982,16 @@ case OP_Column: {
int len; /* The length of the serialized data for the column */
int i; /* Loop counter */
char *zData; /* Part of the record being decoded */
+ Mem *pDest; /* Where to write the extracted value */
Mem sMem; /* For storing the record being decoded */
sMem.flags = 0;
+ sMem.db = 0;
+ sMem.zMalloc = 0;
assert( p1<p->nCursor );
- pTos++;
- pTos->flags = MEM_Null;
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ pDest = &p->aMem[pOp->p3];
+ MemSetTypeFlag(pDest, MEM_Null);
/* This block sets the variable payloadSize to be the total number of
** bytes in the record.
@@ -2006,15 +2003,13 @@ case OP_Column: {
** If the data is unavailable, zRec is set to NULL.
**
** We also compute the number of columns in the record. For cursors,
- ** the number of columns is stored in the Cursor.nField element. For
- ** records on the stack, the next entry down on the stack is an integer
- ** which is the number of records.
+ ** the number of columns is stored in the Cursor.nField element.
*/
pC = p->apCsr[p1];
+ assert( pC!=0 );
#ifndef SQLITE_OMIT_VIRTUALTABLE
assert( pC->pVtabCursor==0 );
#endif
- assert( pC!=0 );
if( pC->pCursor!=0 ){
/* The record is stored in a B-Tree */
rc = sqlite3VdbeCursorMoveto(pC);
@@ -2034,7 +2029,8 @@ case OP_Column: {
sqlite3BtreeDataSize(pCrsr, &payloadSize);
}
nField = pC->nField;
- }else if( pC->pseudoTable ){
+ }else{
+ assert( pC->pseudoTable );
/* The record is the sole entry of a pseudo-table */
payloadSize = pC->nData;
zRec = pC->pData;
@@ -2042,19 +2038,14 @@ case OP_Column: {
assert( payloadSize==0 || zRec!=0 );
nField = pC->nField;
pCrsr = 0;
- }else{
- zRec = 0;
- payloadSize = 0;
- pCrsr = 0;
- nField = 0;
}
- /* If payloadSize is 0, then just push a NULL onto the stack. */
+ /* If payloadSize is 0, then just store a NULL */
if( payloadSize==0 ){
- assert( pTos->flags==MEM_Null );
- break;
+ assert( pDest->flags&MEM_Null );
+ goto op_column_out;
}
- if( payloadSize>SQLITE_MAX_LENGTH ){
+ if( payloadSize>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
@@ -2063,8 +2054,8 @@ case OP_Column: {
/* Read and parse the table header. Store the results of the parse
** into the record header cache fields of the cursor.
*/
- if( pC && pC->cacheStatus==p->cacheCtr ){
- aType = pC->aType;
+ aType = pC->aType;
+ if( pC->cacheStatus==p->cacheCtr ){
aOffset = pC->aOffset;
}else{
u8 *zIdx; /* Index into header */
@@ -2073,13 +2064,7 @@ case OP_Column: {
int szHdrSz; /* Size of the header size field at start of record */
int avail; /* Number of bytes of available data */
- aType = pC->aType;
- if( aType==0 ){
- pC->aType = aType = sqliteMallocRaw( 2*nField*sizeof(aType) );
- }
- if( aType==0 ){
- goto no_mem;
- }
+ assert(aType);
pC->aOffset = aOffset = &aType[nField];
pC->payloadSize = payloadSize;
pC->cacheStatus = p->cacheCtr;
@@ -2108,7 +2093,7 @@ case OP_Column: {
/* The following assert is true in all cases accept when
** the database file has been corrupted externally.
** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
- szHdrSz = GetVarint((u8*)zData, offset);
+ szHdrSz = getVarint32((u8*)zData, offset);
/* The KeyFetch() or DataFetch() above are fast and will get the entire
** record header in most cases. But they will fail to get the complete
@@ -2117,6 +2102,8 @@ case OP_Column: {
** acquire the complete header text.
*/
if( !zRec && avail<offset ){
+ sMem.flags = 0;
+ sMem.db = 0;
rc = sqlite3VdbeMemFromBtree(pCrsr, 0, offset, pC->isIndex, &sMem);
if( rc!=SQLITE_OK ){
goto op_column_out;
@@ -2134,26 +2121,28 @@ case OP_Column: {
for(i=0; i<nField; i++){
if( zIdx<zEndHdr ){
aOffset[i] = offset;
- zIdx += GetVarint(zIdx, aType[i]);
+ zIdx += getVarint32(zIdx, aType[i]);
offset += sqlite3VdbeSerialTypeLen(aType[i]);
}else{
/* If i is less that nField, then there are less fields in this
** record than SetNumColumns indicated there are columns in the
** table. Set the offset for any extra columns not present in
- ** the record to 0. This tells code below to push a NULL onto the
- ** stack instead of deserializing a value from the record.
+ ** the record to 0. This tells code below to store a NULL
+ ** instead of deserializing a value from the record.
*/
aOffset[i] = 0;
}
}
- Release(&sMem);
+ sqlite3VdbeMemRelease(&sMem);
sMem.flags = MEM_Null;
/* If we have read more header data than was contained in the header,
** or if the end of the last field appears to be past the end of the
- ** record, then we must be dealing with a corrupt database.
+ ** record, or if the end of the last field appears to be before the end
+ ** of the record (when all fields present), then we must be dealing
+ ** with a corrupt database.
*/
- if( zIdx>zEndHdr || offset>payloadSize ){
+ if( zIdx>zEndHdr || offset>payloadSize || (zIdx==zEndHdr && offset!=payloadSize) ){
rc = SQLITE_CORRUPT_BKPT;
goto op_column_out;
}
@@ -2162,90 +2151,95 @@ case OP_Column: {
/* Get the column information. If aOffset[p2] is non-zero, then
** deserialize the value from the record. If aOffset[p2] is zero,
** then there are not enough fields in the record to satisfy the
- ** request. In this case, set the value NULL or to P3 if P3 is
+ ** request. In this case, set the value NULL or to P4 if P4 is
** a pointer to a Mem object.
*/
if( aOffset[p2] ){
assert( rc==SQLITE_OK );
if( zRec ){
- zData = &zRec[aOffset[p2]];
+ sqlite3VdbeMemReleaseExternal(pDest);
+ sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
}else{
len = sqlite3VdbeSerialTypeLen(aType[p2]);
- rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,&sMem);
+ sqlite3VdbeMemMove(&sMem, pDest);
+ rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem);
if( rc!=SQLITE_OK ){
goto op_column_out;
}
zData = sMem.z;
+ sqlite3VdbeSerialGet((u8*)zData, aType[p2], pDest);
}
- sqlite3VdbeSerialGet((u8*)zData, aType[p2], pTos);
- pTos->enc = encoding;
+ pDest->enc = encoding;
}else{
- if( pOp->p3type==P3_MEM ){
- sqlite3VdbeMemShallowCopy(pTos, (Mem *)(pOp->p3), MEM_Static);
+ if( pOp->p4type==P4_MEM ){
+ sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
}else{
- pTos->flags = MEM_Null;
+ assert( pDest->flags&MEM_Null );
}
}
/* If we dynamically allocated space to hold the data (in the
** sqlite3VdbeMemFromBtree() call above) then transfer control of that
- ** dynamically allocated space over to the pTos structure.
+ ** dynamically allocated space over to the pDest structure.
** This prevents a memory copy.
*/
- if( (sMem.flags & MEM_Dyn)!=0 ){
- assert( pTos->flags & MEM_Ephem );
- assert( pTos->flags & (MEM_Str|MEM_Blob) );
- assert( pTos->z==sMem.z );
- assert( sMem.flags & MEM_Term );
- pTos->flags &= ~MEM_Ephem;
- pTos->flags |= MEM_Dyn|MEM_Term;
+ if( sMem.zMalloc ){
+ assert( sMem.z==sMem.zMalloc );
+ assert( !(pDest->flags & MEM_Dyn) );
+ assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z );
+ pDest->flags &= ~(MEM_Ephem|MEM_Static);
+ pDest->flags |= MEM_Term;
+ pDest->z = sMem.z;
+ pDest->zMalloc = sMem.zMalloc;
}
- /* pTos->z might be pointing to sMem.zShort[]. Fix that so that we
- ** can abandon sMem */
- rc = sqlite3VdbeMemMakeWriteable(pTos);
+ rc = sqlite3VdbeMemMakeWriteable(pDest);
op_column_out:
+ UPDATE_MAX_BLOBSIZE(pDest);
+ REGISTER_TRACE(pOp->p3, pDest);
break;
}
-/* Opcode: MakeRecord P1 P2 P3
+/* Opcode: Affinity P1 P2 * P4 *
**
-** Convert the top abs(P1) entries of the stack into a single entry
-** suitable for use as a data record in a database table or as a key
-** in an index. The details of the format are irrelavant as long as
-** the OP_Column opcode can decode the record later and as long as the
-** sqlite3VdbeRecordCompare function will correctly compare two encoded
-** records. Refer to source code comments for the details of the record
-** format.
+** Apply affinities to a range of P2 registers starting with P1.
**
-** The original stack entries are popped from the stack if P1>0 but
-** remain on the stack if P1<0.
+** P4 is a string that is P2 characters long. The nth character of the
+** string indicates the column affinity that should be used for the nth
+** memory cell in the range.
+*/
+case OP_Affinity: {
+ char *zAffinity = pOp->p4.z;
+ Mem *pData0 = &p->aMem[pOp->p1];
+ Mem *pLast = &pData0[pOp->p2-1];
+ Mem *pRec;
+
+ for(pRec=pData0; pRec<=pLast; pRec++){
+ ExpandBlob(pRec);
+ applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
+ }
+ break;
+}
+
+/* Opcode: MakeRecord P1 P2 P3 P4 *
**
-** If P2 is not zero and one or more of the entries are NULL, then jump
-** to the address given by P2. This feature can be used to skip a
-** uniqueness test on indices.
+** Convert P2 registers beginning with P1 into a single entry
+** suitable for use as a data record in a database table or as a key
+** in an index. The details of the format are irrelevant as long as
+** the OP_Column opcode can decode the record later.
+** Refer to source code comments for the details of the record
+** format.
**
-** P3 may be a string that is P1 characters long. The nth character of the
+** P4 may be a string that is P2 characters long. The nth character of the
** string indicates the column affinity that should be used for the nth
-** field of the index key (i.e. the first character of P3 corresponds to the
-** lowest element on the stack).
+** field of the index key.
**
** The mapping from character to affinity is given by the SQLITE_AFF_
** macros defined in sqliteInt.h.
**
-** If P3 is NULL then all index fields have the affinity NONE.
-**
-** See also OP_MakeIdxRec
-*/
-/* Opcode: MakeIdxRec P1 P2 P3
-**
-** This opcode works just OP_MakeRecord except that it reads an extra
-** integer from the stack (thus reading a total of abs(P1+1) entries)
-** and appends that extra integer to the end of the record as a varint.
-** This results in an index key.
+** If P4 is NULL then all index fields have the affinity NONE.
*/
-case OP_MakeIdxRec:
case OP_MakeRecord: {
/* Assuming the record contains N fields, the record format looks
** like this:
@@ -2254,8 +2248,8 @@ case OP_MakeRecord: {
** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
** ------------------------------------------------------------------------
**
- ** Data(0) is taken from the lowest element of the stack and data(N-1) is
- ** the top of the stack.
+ ** Data(0) is taken from register P1. Data(1) comes from register P1+1
+ ** and so froth.
**
** Each type field is a varint representing the serial type of the
** corresponding data element (see sqlite3VdbeSerialType()). The
@@ -2264,48 +2258,37 @@ case OP_MakeRecord: {
*/
u8 *zNewRecord; /* A buffer to hold the data for the new record */
Mem *pRec; /* The new record */
- Mem *pRowid = 0; /* Rowid appended to the new record */
u64 nData = 0; /* Number of bytes of data space */
int nHdr = 0; /* Number of bytes of header space */
u64 nByte = 0; /* Data space required for this record */
int nZero = 0; /* Number of zero bytes at the end of the record */
int nVarint; /* Number of bytes in a varint */
u32 serial_type; /* Type field */
- int containsNull = 0; /* True if any of the data fields are NULL */
- Mem *pData0; /* Bottom of the stack */
- int leaveOnStack; /* If true, leave the entries on the stack */
+ Mem *pData0; /* First field to be combined into the record */
+ Mem *pLast; /* Last field of the record */
int nField; /* Number of fields in the record */
- int jumpIfNull; /* Jump here if non-zero and any entries are NULL. */
- int addRowid; /* True to append a rowid column at the end */
char *zAffinity; /* The affinity string for the record */
int file_format; /* File format to use for encoding */
int i; /* Space used in zNewRecord[] */
- char zTemp[NBFS]; /* Space to hold small records */
- leaveOnStack = ((pOp->p1<0)?1:0);
- nField = pOp->p1 * (leaveOnStack?-1:1);
- jumpIfNull = pOp->p2;
- addRowid = pOp->opcode==OP_MakeIdxRec;
- zAffinity = pOp->p3;
-
- pData0 = &pTos[1-nField];
- assert( pData0>=p->aStack );
- containsNull = 0;
+ nField = pOp->p1;
+ zAffinity = pOp->p4.z;
+ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem );
+ pData0 = &p->aMem[nField];
+ nField = pOp->p2;
+ pLast = &pData0[nField-1];
file_format = p->minWriteFileFormat;
/* Loop through the elements that will make up the record to figure
** out how much space is required for the new record.
*/
- for(pRec=pData0; pRec<=pTos; pRec++){
+ for(pRec=pData0; pRec<=pLast; pRec++){
int len;
if( zAffinity ){
applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
}
- if( pRec->flags&MEM_Null ){
- containsNull = 1;
- }
if( pRec->flags&MEM_Zero && pRec->n>0 ){
- ExpandBlob(pRec);
+ sqlite3VdbeMemExpandBlob(pRec);
}
serial_type = sqlite3VdbeSerialType(pRec, file_format);
len = sqlite3VdbeSerialTypeLen(serial_type);
@@ -2320,104 +2303,84 @@ case OP_MakeRecord: {
}
}
- /* If we have to append a varint rowid to this record, set pRowid
- ** to the value of the rowid and increase nByte by the amount of space
- ** required to store it.
- */
- if( addRowid ){
- pRowid = &pTos[0-nField];
- assert( pRowid>=p->aStack );
- sqlite3VdbeMemIntegerify(pRowid);
- serial_type = sqlite3VdbeSerialType(pRowid, 0);
- nData += sqlite3VdbeSerialTypeLen(serial_type);
- nHdr += sqlite3VarintLen(serial_type);
- nZero = 0;
- }
-
/* Add the initial header varint and total the size */
nHdr += nVarint = sqlite3VarintLen(nHdr);
if( nVarint<sqlite3VarintLen(nHdr) ){
nHdr++;
}
nByte = nHdr+nData-nZero;
- if( nByte>SQLITE_MAX_LENGTH ){
+ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- /* Allocate space for the new record. */
- if( nByte>sizeof(zTemp) ){
- zNewRecord = sqliteMallocRaw(nByte);
- if( !zNewRecord ){
- goto no_mem;
- }
- }else{
- zNewRecord = (u8*)zTemp;
+ /* Make sure the output register has a buffer large enough to store
+ ** the new record. The output register (pOp->p3) is not allowed to
+ ** be one of the input registers (because the following call to
+ ** sqlite3VdbeMemGrow() could clobber the value before it is used).
+ */
+ assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
+ pOut = &p->aMem[pOp->p3];
+ if( sqlite3VdbeMemGrow(pOut, nByte, 0) ){
+ goto no_mem;
}
+ zNewRecord = (u8 *)pOut->z;
/* Write the record */
- i = sqlite3PutVarint(zNewRecord, nHdr);
- for(pRec=pData0; pRec<=pTos; pRec++){
+ i = putVarint32(zNewRecord, nHdr);
+ for(pRec=pData0; pRec<=pLast; pRec++){
serial_type = sqlite3VdbeSerialType(pRec, file_format);
- i += sqlite3PutVarint(&zNewRecord[i], serial_type); /* serial type */
- }
- if( addRowid ){
- i += sqlite3PutVarint(&zNewRecord[i], sqlite3VdbeSerialType(pRowid, 0));
+ i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
}
- for(pRec=pData0; pRec<=pTos; pRec++){ /* serial data */
+ for(pRec=pData0; pRec<=pLast; pRec++){ /* serial data */
i += sqlite3VdbeSerialPut(&zNewRecord[i], nByte-i, pRec, file_format);
}
- if( addRowid ){
- i += sqlite3VdbeSerialPut(&zNewRecord[i], nByte-i, pRowid, 0);
- }
assert( i==nByte );
- /* Pop entries off the stack if required. Push the new record on. */
- if( !leaveOnStack ){
- popStack(&pTos, nField+addRowid);
- }
- pTos++;
- pTos->n = nByte;
- if( nByte<=sizeof(zTemp) ){
- assert( zNewRecord==(unsigned char *)zTemp );
- pTos->z = pTos->zShort;
- memcpy(pTos->zShort, zTemp, nByte);
- pTos->flags = MEM_Blob | MEM_Short;
- }else{
- assert( zNewRecord!=(unsigned char *)zTemp );
- pTos->z = (char*)zNewRecord;
- pTos->flags = MEM_Blob | MEM_Dyn;
- pTos->xDel = 0;
- }
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ pOut->n = nByte;
+ pOut->flags = MEM_Blob | MEM_Dyn;
+ pOut->xDel = 0;
if( nZero ){
- pTos->u.i = nZero;
- pTos->flags |= MEM_Zero;
- }
- pTos->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */
-
- /* If a NULL was encountered and jumpIfNull is non-zero, take the jump. */
- if( jumpIfNull && containsNull ){
- pc = jumpIfNull - 1;
+ pOut->u.i = nZero;
+ pOut->flags |= MEM_Zero;
}
+ pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */
+ REGISTER_TRACE(pOp->p3, pOut);
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Statement P1 * *
+/* Opcode: Statement P1 * * * *
**
** Begin an individual statement transaction which is part of a larger
-** BEGIN..COMMIT transaction. This is needed so that the statement
+** transaction. This is needed so that the statement
** can be rolled back after an error without having to roll back the
** entire transaction. The statement transaction will automatically
** commit when the VDBE halts.
**
+** If the database connection is currently in autocommit mode (that
+** is to say, if it is in between BEGIN and COMMIT)
+** and if there are no other active statements on the same database
+** connection, then this operation is a no-op. No statement transaction
+** is needed since any error can use the normal ROLLBACK process to
+** undo changes.
+**
+** If a statement transaction is started, then a statement journal file
+** will be allocated and initialized.
+**
** The statement is begun on the database file with index P1. The main
** database file has an index of 0 and the file used for temporary tables
** has an index of 1.
*/
-case OP_Statement: { /* no-push */
- int i = pOp->p1;
- Btree *pBt;
- if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt)!=0 && !(db->autoCommit) ){
+case OP_Statement: {
+ if( db->autoCommit==0 || db->activeVdbeCnt>1 ){
+ int i = pOp->p1;
+ Btree *pBt;
+ assert( i>=0 && i<db->nDb );
+ assert( db->aDb[i].pBt!=0 );
+ pBt = db->aDb[i].pBt;
assert( sqlite3BtreeIsInTrans(pBt) );
+ assert( (p->btreeMask & (1<<i))!=0 );
if( !sqlite3BtreeIsInStmt(pBt) ){
rc = sqlite3BtreeBeginStmt(pBt);
p->openedStatement = 1;
@@ -2426,7 +2389,7 @@ case OP_Statement: { /* no-push */
break;
}
-/* Opcode: AutoCommit P1 P2 *
+/* Opcode: AutoCommit P1 P2 * * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
@@ -2434,7 +2397,7 @@ case OP_Statement: { /* no-push */
**
** This instruction causes the VM to halt.
*/
-case OP_AutoCommit: { /* no-push */
+case OP_AutoCommit: {
u8 i = pOp->p1;
u8 rollback = pOp->p2;
@@ -2448,8 +2411,9 @@ case OP_AutoCommit: { /* no-push */
** still running, and a transaction is active, return an error indicating
** that the other VMs must complete first.
*/
- sqlite3SetString(&p->zErrMsg, "cannot ", rollback?"rollback":"commit",
- " transaction - SQL statements in progress", (char*)0);
+ sqlite3SetString(&p->zErrMsg, db, "cannot %s transaction - "
+ "SQL statements in progress",
+ rollback ? "rollback" : "commit");
rc = SQLITE_ERROR;
}else if( i!=db->autoCommit ){
if( pOp->p2 ){
@@ -2459,30 +2423,30 @@ case OP_AutoCommit: { /* no-push */
}else{
db->autoCommit = i;
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
- p->pTos = pTos;
p->pc = pc;
db->autoCommit = 1-i;
- p->rc = SQLITE_BUSY;
- return SQLITE_BUSY;
+ p->rc = rc = SQLITE_BUSY;
+ goto vdbe_return;
}
}
if( p->rc==SQLITE_OK ){
- return SQLITE_DONE;
+ rc = SQLITE_DONE;
}else{
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
}
+ goto vdbe_return;
}else{
- sqlite3SetString(&p->zErrMsg,
+ sqlite3SetString(&p->zErrMsg, db,
(!i)?"cannot start a transaction within a transaction":(
(rollback)?"cannot rollback - no transaction is active":
- "cannot commit - no transaction is active"), (char*)0);
+ "cannot commit - no transaction is active"));
rc = SQLITE_ERROR;
}
break;
}
-/* Opcode: Transaction P1 P2 *
+/* Opcode: Transaction P1 P2 * * *
**
** Begin a transaction. The transaction ends when a Commit or Rollback
** opcode is encountered. Depending on the ON CONFLICT setting, the
@@ -2490,7 +2454,8 @@ case OP_AutoCommit: { /* no-push */
**
** P1 is the index of the database file on which the transaction is
** started. Index 0 is the main database file and index 1 is the
-** file used for temporary tables.
+** file used for temporary tables. Indices of 2 or more are used for
+** attached databases.
**
** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is
** obtained on the database file when a write-transaction is started. No
@@ -2502,20 +2467,20 @@ case OP_AutoCommit: { /* no-push */
**
** If P2 is zero, then a read-lock is obtained on the database file.
*/
-case OP_Transaction: { /* no-push */
+case OP_Transaction: {
int i = pOp->p1;
Btree *pBt;
assert( i>=0 && i<db->nDb );
+ assert( (p->btreeMask & (1<<i))!=0 );
pBt = db->aDb[i].pBt;
if( pBt ){
rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
if( rc==SQLITE_BUSY ){
p->pc = pc;
- p->rc = SQLITE_BUSY;
- p->pTos = pTos;
- return SQLITE_BUSY;
+ p->rc = rc = SQLITE_BUSY;
+ goto vdbe_return;
}
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){
goto abort_due_to_error;
@@ -2524,16 +2489,16 @@ case OP_Transaction: { /* no-push */
break;
}
-/* Opcode: ReadCookie P1 P2 *
+/* Opcode: ReadCookie P1 P2 P3 * *
**
-** Read cookie number P2 from database P1 and push it onto the stack.
-** P2==0 is the schema version. P2==1 is the database format.
-** P2==2 is the recommended pager cache size, and so forth. P1==0 is
+** Read cookie number P3 from database P1 and write it into register P2.
+** P3==0 is the schema version. P3==1 is the database format.
+** P3==2 is the recommended pager cache size, and so forth. P1==0 is
** the main database file and P1==1 is the database file used to store
** temporary tables.
**
** If P1 is negative, then this is a request to read the size of a
-** databases free-list. P2 must be set to 1 in this case. The actual
+** databases free-list. P3 must be set to 1 in this case. The actual
** database accessed is ((P1+1)*-1). For example, a P1 parameter of -1
** corresponds to database 0 ("main"), a P1 of -2 is database 1 ("temp").
**
@@ -2541,18 +2506,19 @@ case OP_Transaction: { /* no-push */
** must be started or there must be an open cursor) before
** executing this instruction.
*/
-case OP_ReadCookie: {
+case OP_ReadCookie: { /* out2-prerelease */
int iMeta;
int iDb = pOp->p1;
- int iCookie = pOp->p2;
+ int iCookie = pOp->p3;
- assert( pOp->p2<SQLITE_N_BTREE_META );
+ assert( pOp->p3<SQLITE_N_BTREE_META );
if( iDb<0 ){
iDb = (-1*(iDb+1));
iCookie *= -1;
}
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
+ assert( (p->btreeMask & (1<<iDb))!=0 );
/* The indexing of meta values at the schema layer is off by one from
** the indexing in the btree layer. The btree considers meta[0] to
** be the number of free pages in the database (a read-only value)
@@ -2561,15 +2527,15 @@ case OP_ReadCookie: {
** by one in the following statement.
*/
rc = sqlite3BtreeGetMeta(db->aDb[iDb].pBt, 1 + iCookie, (u32 *)&iMeta);
- pTos++;
- pTos->u.i = iMeta;
- pTos->flags = MEM_Int;
+ pOut->u.i = iMeta;
+ MemSetTypeFlag(pOut, MEM_Int);
break;
}
-/* Opcode: SetCookie P1 P2 *
+/* Opcode: SetCookie P1 P2 P3 * *
**
-** Write the top of the stack into cookie number P2 of database P1.
+** Write the content of register P3 (interpreted as an integer)
+** into cookie number P2 of database P1.
** P2==0 is the schema version. P2==1 is the database format.
** P2==2 is the recommended pager cache size, and so forth. P1==0 is
** the main database file and P1==1 is the database file used to store
@@ -2577,26 +2543,24 @@ case OP_ReadCookie: {
**
** A transaction must be started before executing this opcode.
*/
-case OP_SetCookie: { /* no-push */
+case OP_SetCookie: { /* in3 */
Db *pDb;
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( (p->btreeMask & (1<<pOp->p1))!=0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
- assert( pTos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
+ sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
- rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->u.i);
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pIn3->u.i);
if( pOp->p2==0 ){
/* When the schema cookie changes, record the new cookie internally */
- pDb->pSchema->schema_cookie = pTos->u.i;
+ pDb->pSchema->schema_cookie = pIn3->u.i;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==1 ){
/* Record changes in the file format */
- pDb->pSchema->file_format = pTos->u.i;
+ pDb->pSchema->file_format = pIn3->u.i;
}
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
** schema is changed. Ticket #1644 */
@@ -2621,10 +2585,11 @@ case OP_SetCookie: { /* no-push */
** to be executed (to establish a read lock) before this opcode is
** invoked.
*/
-case OP_VerifyCookie: { /* no-push */
+case OP_VerifyCookie: {
int iMeta;
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( (p->btreeMask & (1<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&iMeta);
@@ -2633,12 +2598,13 @@ case OP_VerifyCookie: { /* no-push */
iMeta = 0;
}
if( rc==SQLITE_OK && iMeta!=pOp->p2 ){
- sqlite3SetString(&p->zErrMsg, "database schema has changed", (char*)0);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
/* If the schema-cookie from the database file matches the cookie
** stored with the in-memory representation of the schema, do
** not reload the schema from the database file.
**
- ** If virtual-tables are in use, this is not just an optimisation.
+ ** If virtual-tables are in use, this is not just an optimization.
** Often, v-tables store their data in other SQLite tables, which
** are queried from within xNext() and other v-table methods using
** prepared queries. If such a query is out-of-date, we do not want to
@@ -2657,17 +2623,18 @@ case OP_VerifyCookie: { /* no-push */
break;
}
-/* Opcode: OpenRead P1 P2 P3
+/* Opcode: OpenRead P1 P2 P3 P4 P5
**
** Open a read-only cursor for the database table whose root page is
-** P2 in a database file. The database file is determined by an
-** integer from the top of the stack. 0 means the main database and
-** 1 means the database used for temporary tables. Give the new
-** cursor an identifier of P1. The P1 values need not be contiguous
-** but all P1 values should be small integers. It is an error for
-** P1 to be negative.
+** P2 in a database file. The database file is determined by P3.
+** P3==0 means the main database, P3==1 means the database used for
+** temporary tables, and P3>1 means used the corresponding attached
+** database. Give the new cursor an identifier of P1. The P1
+** values need not be contiguous but all P1 values should be small integers.
+** It is an error for P1 to be negative.
**
-** If P2==0 then take the root page number from the next of the stack.
+** If P5!=0 then use the content of register P2 as the root page, not
+** the value of P2 itself.
**
** There will be a read lock on the database whenever there is an
** open cursor. If the database was unlocked prior to this instruction
@@ -2678,19 +2645,20 @@ case OP_VerifyCookie: { /* no-push */
** to get a read lock but fails, the script terminates with an
** SQLITE_BUSY error code.
**
-** The P3 value is a pointer to a KeyInfo structure that defines the
-** content and collating sequence of indices. P3 is NULL for cursors
+** The P4 value is a pointer to a KeyInfo structure that defines the
+** content and collating sequence of indices. P4 is NULL for cursors
** that are not pointing to indices.
**
** See also OpenWrite.
*/
-/* Opcode: OpenWrite P1 P2 P3
+/* Opcode: OpenWrite P1 P2 P3 P4 P5
**
** Open a read/write cursor named P1 on the table or index whose root
-** page is P2. If P2==0 then take the root page number from the stack.
+** page is P2. Or if P5!=0 use the content of register P2 to find the
+** root page.
**
-** The P3 value is a pointer to a KeyInfo structure that defines the
-** content and collating sequence of indices. P3 is NULL for cursors
+** The P4 value is a pointer to a KeyInfo structure that defines the
+** content and collating sequence of indices. P4 is NULL for cursors
** that are not pointing to indices.
**
** This instruction works just like OpenRead except that it opens the cursor
@@ -2699,22 +2667,18 @@ case OP_VerifyCookie: { /* no-push */
**
** See also OpenRead.
*/
-case OP_OpenRead: /* no-push */
-case OP_OpenWrite: { /* no-push */
+case OP_OpenRead:
+case OP_OpenWrite: {
int i = pOp->p1;
int p2 = pOp->p2;
+ int iDb = pOp->p3;
int wrFlag;
Btree *pX;
- int iDb;
Cursor *pCur;
Db *pDb;
- assert( pTos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
- iDb = pTos->u.i;
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
assert( iDb>=0 && iDb<db->nDb );
+ assert( (p->btreeMask & (1<<iDb))!=0 );
pDb = &db->aDb[iDb];
pX = pDb->pBt;
assert( pX!=0 );
@@ -2726,26 +2690,21 @@ case OP_OpenWrite: { /* no-push */
}else{
wrFlag = 0;
}
- if( p2<=0 ){
- assert( pTos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
- p2 = pTos->u.i;
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
+ if( pOp->p5 ){
+ assert( p2>0 );
+ assert( p2<=p->nMem );
+ pIn2 = &p->aMem[p2];
+ sqlite3VdbeMemIntegerify(pIn2);
+ p2 = pIn2->u.i;
assert( p2>=2 );
}
assert( i>=0 );
- pCur = allocateCursor(p, i, iDb);
+ pCur = allocateCursor(p, i, &pOp[-1], iDb, 1);
if( pCur==0 ) goto no_mem;
pCur->nullRow = 1;
- if( pX==0 ) break;
- /* We always provide a key comparison function. If the table being
- ** opened is of type INTKEY, the comparision function will be ignored. */
- rc = sqlite3BtreeCursor(pX, p2, wrFlag,
- sqlite3VdbeRecordCompare, pOp->p3,
- &pCur->pCursor);
- if( pOp->p3type==P3_KEYINFO ){
- pCur->pKeyInfo = (KeyInfo*)pOp->p3;
+ rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor);
+ if( pOp->p4type==P4_KEYINFO ){
+ pCur->pKeyInfo = pOp->p4.pKeyInfo;
pCur->pIncrKey = &pCur->pKeyInfo->incrKey;
pCur->pKeyInfo->enc = ENC(p->db);
}else{
@@ -2755,14 +2714,13 @@ case OP_OpenWrite: { /* no-push */
switch( rc ){
case SQLITE_BUSY: {
p->pc = pc;
- p->rc = SQLITE_BUSY;
- p->pTos = &pTos[1 + (pOp->p2<=0)]; /* Operands must remain on stack */
- return SQLITE_BUSY;
+ p->rc = rc = SQLITE_BUSY;
+ goto vdbe_return;
}
case SQLITE_OK: {
int flags = sqlite3BtreeFlags(pCur->pCursor);
/* Sanity checking. Only the lower four bits of the flags byte should
- ** be used. Bit 3 (mask 0x08) is unpreditable. The lower 3 bits
+ ** be used. Bit 3 (mask 0x08) is unpredictable. The lower 3 bits
** (mask 0x07) should be either 5 (intkey+leafdata for tables) or
** 2 (zerodata for indices). If these conditions are not met it can
** only mean that we are dealing with a corrupt database file
@@ -2773,20 +2731,21 @@ case OP_OpenWrite: { /* no-push */
}
pCur->isTable = (flags & BTREE_INTKEY)!=0;
pCur->isIndex = (flags & BTREE_ZERODATA)!=0;
- /* If P3==0 it means we are expected to open a table. If P3!=0 then
+ /* If P4==0 it means we are expected to open a table. If P4!=0 then
** we expect to be opening an index. If this is not what happened,
** then the database is corrupt
*/
- if( (pCur->isTable && pOp->p3type==P3_KEYINFO)
- || (pCur->isIndex && pOp->p3type!=P3_KEYINFO) ){
+ if( (pCur->isTable && pOp->p4type==P4_KEYINFO)
+ || (pCur->isIndex && pOp->p4type!=P4_KEYINFO) ){
rc = SQLITE_CORRUPT_BKPT;
goto abort_due_to_error;
}
break;
}
case SQLITE_EMPTY: {
- pCur->isTable = pOp->p3type!=P3_KEYINFO;
+ pCur->isTable = pOp->p4type!=P4_KEYINFO;
pCur->isIndex = !pCur->isTable;
+ pCur->pCursor = 0;
rc = SQLITE_OK;
break;
}
@@ -2797,7 +2756,7 @@ case OP_OpenWrite: { /* no-push */
break;
}
-/* Opcode: OpenEphemeral P1 P2 P3
+/* Opcode: OpenEphemeral P1 P2 * P4 *
**
** Open a new cursor P1 to a transient table.
** The cursor is always opened read/write even if
@@ -2805,8 +2764,8 @@ case OP_OpenWrite: { /* no-push */
** table is deleted automatically when the cursor is closed.
**
** P2 is the number of columns in the virtual table.
-** The cursor points to a BTree table if P3==0 and to a BTree index
-** if P3 is not 0. If P3 is not NULL, it points to a KeyInfo structure
+** The cursor points to a BTree table if P4==0 and to a BTree index
+** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
** that defines the format of keys in the index.
**
** This opcode was once called OpenTemp. But that created
@@ -2815,14 +2774,22 @@ case OP_OpenWrite: { /* no-push */
** this opcode. Then this opcode was call OpenVirtual. But
** that created confusion with the whole virtual-table idea.
*/
-case OP_OpenEphemeral: { /* no-push */
+case OP_OpenEphemeral: {
int i = pOp->p1;
Cursor *pCx;
+ static const int openFlags =
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE |
+ SQLITE_OPEN_DELETEONCLOSE |
+ SQLITE_OPEN_TRANSIENT_DB;
+
assert( i>=0 );
- pCx = allocateCursor(p, i, -1);
+ pCx = allocateCursor(p, i, pOp, -1, 1);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
- rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, &pCx->pBt);
+ rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
+ &pCx->pBt);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
}
@@ -2832,31 +2799,30 @@ case OP_OpenEphemeral: { /* no-push */
** opening it. If a transient table is required, just use the
** automatically created table with root-page 1 (an INTKEY table).
*/
- if( pOp->p3 ){
+ if( pOp->p4.pKeyInfo ){
int pgno;
- assert( pOp->p3type==P3_KEYINFO );
+ assert( pOp->p4type==P4_KEYINFO );
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA);
if( rc==SQLITE_OK ){
assert( pgno==MASTER_ROOT+1 );
- rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, sqlite3VdbeRecordCompare,
- pOp->p3, &pCx->pCursor);
- pCx->pKeyInfo = (KeyInfo*)pOp->p3;
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
+ (KeyInfo*)pOp->p4.z, pCx->pCursor);
+ pCx->pKeyInfo = pOp->p4.pKeyInfo;
pCx->pKeyInfo->enc = ENC(p->db);
pCx->pIncrKey = &pCx->pKeyInfo->incrKey;
}
pCx->isTable = 0;
}else{
- rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, 0, &pCx->pCursor);
+ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
pCx->isTable = 1;
pCx->pIncrKey = &pCx->bogusIncrKey;
}
}
- pCx->nField = pOp->p2;
pCx->isIndex = !pCx->isTable;
break;
}
-/* Opcode: OpenPseudo P1 * *
+/* Opcode: OpenPseudo P1 P2 * * *
**
** Open a new cursor that points to a fake table that contains a single
** row of data. Any attempt to write a second row of data causes the
@@ -2867,83 +2833,109 @@ case OP_OpenEphemeral: { /* no-push */
** NEW or OLD tables in a trigger. Also used to hold the a single
** row output from the sorter so that the row can be decomposed into
** individual columns using the OP_Column opcode.
+**
+** When OP_Insert is executed to insert a row in to the pseudo table,
+** the pseudo-table cursor may or may not make it's own copy of the
+** original row data. If P2 is 0, then the pseudo-table will copy the
+** original row data. Otherwise, a pointer to the original memory cell
+** is stored. In this case, the vdbe program must ensure that the
+** memory cell containing the row data is not overwritten until the
+** pseudo table is closed (or a new row is inserted into it).
*/
-case OP_OpenPseudo: { /* no-push */
+case OP_OpenPseudo: {
int i = pOp->p1;
Cursor *pCx;
assert( i>=0 );
- pCx = allocateCursor(p, i, -1);
+ pCx = allocateCursor(p, i, &pOp[-1], -1, 0);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
pCx->pseudoTable = 1;
+ pCx->ephemPseudoTable = pOp->p2;
pCx->pIncrKey = &pCx->bogusIncrKey;
pCx->isTable = 1;
pCx->isIndex = 0;
break;
}
-/* Opcode: Close P1 * *
+/* Opcode: Close P1 * * * *
**
** Close a cursor previously opened as P1. If P1 is not
** currently open, this instruction is a no-op.
*/
-case OP_Close: { /* no-push */
+case OP_Close: {
int i = pOp->p1;
- if( i>=0 && i<p->nCursor ){
- sqlite3VdbeFreeCursor(p, p->apCsr[i]);
- p->apCsr[i] = 0;
- }
+ assert( i>=0 && i<p->nCursor );
+ sqlite3VdbeFreeCursor(p, p->apCsr[i]);
+ p->apCsr[i] = 0;
break;
}
-/* Opcode: MoveGe P1 P2 *
+/* Opcode: MoveGe P1 P2 P3 P4 *
+**
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the integer value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Pop the top of the stack and use its value as a key. Reposition
-** cursor P1 so that it points to the smallest entry that is greater
-** than or equal to the key that was popped ffrom the stack.
-** If there are no records greater than or equal to the key and P2
-** is not zero, then jump to P2.
+** Reposition cursor P1 so that it points to the smallest entry that
+** is greater than or equal to the key value. If there are no records
+** greater than or equal to the key and P2 is not zero, then jump to P2.
+**
+** A special feature of this opcode (and different from the
+** related OP_MoveGt, OP_MoveLt, and OP_MoveLe) is that if P2 is
+** zero and P1 is an SQL table (a b-tree with integer keys) then
+** the seek is deferred until it is actually needed. It might be
+** the case that the cursor is never accessed. By deferring the
+** seek, we avoid unnecessary seeks.
**
** See also: Found, NotFound, Distinct, MoveLt, MoveGt, MoveLe
*/
-/* Opcode: MoveGt P1 P2 *
+/* Opcode: MoveGt P1 P2 P3 P4 *
+**
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the integer value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Pop the top of the stack and use its value as a key. Reposition
-** cursor P1 so that it points to the smallest entry that is greater
-** than the key from the stack.
-** If there are no records greater than the key and P2 is not zero,
-** then jump to P2.
+** Reposition cursor P1 so that it points to the smallest entry that
+** is greater than the key value. If there are no records greater than
+** the key and P2 is not zero, then jump to P2.
**
** See also: Found, NotFound, Distinct, MoveLt, MoveGe, MoveLe
*/
-/* Opcode: MoveLt P1 P2 *
+/* Opcode: MoveLt P1 P2 P3 P4 *
+**
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the integer value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Pop the top of the stack and use its value as a key. Reposition
-** cursor P1 so that it points to the largest entry that is less
-** than the key from the stack.
-** If there are no records less than the key and P2 is not zero,
-** then jump to P2.
+** Reposition cursor P1 so that it points to the largest entry that
+** is less than the key value. If there are no records less than
+** the key and P2 is not zero, then jump to P2.
**
** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLe
*/
-/* Opcode: MoveLe P1 P2 *
+/* Opcode: MoveLe P1 P2 P3 P4 *
**
-** Pop the top of the stack and use its value as a key. Reposition
-** cursor P1 so that it points to the largest entry that is less than
-** or equal to the key that was popped from the stack.
-** If there are no records less than or eqal to the key and P2 is not zero,
-** then jump to P2.
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the integer value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
+**
+** Reposition cursor P1 so that it points to the largest entry that
+** is less than or equal to the key value. If there are no records
+** less than or equal to the key and P2 is not zero, then jump to P2.
**
** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt
*/
-case OP_MoveLt: /* no-push */
-case OP_MoveLe: /* no-push */
-case OP_MoveGe: /* no-push */
-case OP_MoveGt: { /* no-push */
+case OP_MoveLt: /* jump, in3 */
+case OP_MoveLe: /* jump, in3 */
+case OP_MoveGe: /* jump, in3 */
+case OP_MoveGt: { /* jump, in3 */
int i = pOp->p1;
Cursor *pC;
- assert( pTos>=p->aStack );
assert( i>=0 && i<p->nCursor );
pC = p->apCsr[i];
assert( pC!=0 );
@@ -2953,26 +2945,31 @@ case OP_MoveGt: { /* no-push */
pC->nullRow = 0;
*pC->pIncrKey = oc==OP_MoveGt || oc==OP_MoveLe;
if( pC->isTable ){
- i64 iKey;
- sqlite3VdbeMemIntegerify(pTos);
- iKey = intToKey(pTos->u.i);
- if( pOp->p2==0 && pOp->opcode==OP_MoveGe ){
+ i64 iKey = sqlite3VdbeIntValue(pIn3);
+ if( pOp->p2==0 ){
+ assert( pOp->opcode==OP_MoveGe );
pC->movetoTarget = iKey;
+ pC->rowidIsValid = 0;
pC->deferredMoveto = 1;
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
break;
}
- rc = sqlite3BtreeMoveto(pC->pCursor, 0, (u64)iKey, 0, &res);
+ rc = sqlite3BtreeMoveto(pC->pCursor, 0, 0, (u64)iKey, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pC->lastRowid = pTos->u.i;
+ pC->lastRowid = iKey;
pC->rowidIsValid = res==0;
}else{
- assert( pTos->flags & MEM_Blob );
- ExpandBlob(pTos);
- rc = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, 0, &res);
+ UnpackedRecord r;
+ int nField = pOp->p4.i;
+ assert( pOp->p4type==P4_INT32 );
+ assert( nField>0 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = nField;
+ r.needFree = 0;
+ r.needDestroy = 0;
+ r.aMem = &p->aMem[pOp->p3];
+ rc = sqlite3BtreeMoveto(pC->pCursor, 0, &r, 0, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
@@ -3005,83 +3002,70 @@ case OP_MoveGt: { /* no-push */
res = sqlite3BtreeEof(pC->pCursor);
}
}
+ assert( pOp->p2>0 );
if( res ){
- if( pOp->p2>0 ){
- pc = pOp->p2 - 1;
- }else{
- pC->nullRow = 1;
- }
+ pc = pOp->p2 - 1;
}
+ }else if( !pC->pseudoTable ){
+ /* This happens when attempting to open the sqlite3_master table
+ ** for read access returns SQLITE_EMPTY. In this case always
+ ** take the jump (since there are no records in the table).
+ */
+ pc = pOp->p2 - 1;
}
- Release(pTos);
- pTos--;
break;
}
-/* Opcode: Distinct P1 P2 *
-**
-** Use the top of the stack as a record created using MakeRecord. P1 is a
-** cursor on a table that declared as an index. If that table contains an
-** entry that matches the top of the stack fall thru. If the top of the stack
-** matches no entry in P1 then jump to P2.
-**
-** The cursor is left pointing at the matching entry if it exists. The
-** record on the top of the stack is not popped.
-**
-** This instruction is similar to NotFound except that this operation
-** does not pop the key from the stack.
-**
-** The instruction is used to implement the DISTINCT operator on SELECT
-** statements. The P1 table is not a true index but rather a record of
-** all results that have produced so far.
+/* Opcode: Found P1 P2 P3 * *
**
-** See also: Found, NotFound, MoveTo, IsUnique, NotExists
-*/
-/* Opcode: Found P1 P2 *
-**
-** Top of the stack holds a blob constructed by MakeRecord. P1 is an index.
-** If an entry that matches the top of the stack exists in P1 then
-** jump to P2. If the top of the stack does not match any entry in P1
+** Register P3 holds a blob constructed by MakeRecord. P1 is an index.
+** If an entry that matches the value in register p3 exists in P1 then
+** jump to P2. If the P3 value does not match any entry in P1
** then fall thru. The P1 cursor is left pointing at the matching entry
-** if it exists. The blob is popped off the top of the stack.
+** if it exists.
**
** This instruction is used to implement the IN operator where the
-** left-hand side is a SELECT statement. P1 is not a true index but
-** is instead a temporary index that holds the results of the SELECT
-** statement. This instruction just checks to see if the left-hand side
-** of the IN operator (stored on the top of the stack) exists in the
-** result of the SELECT statement.
+** left-hand side is a SELECT statement. P1 may be a true index, or it
+** may be a temporary index that holds the results of the SELECT
+** statement. This instruction is also used to implement the
+** DISTINCT keyword in SELECT statements.
**
-** See also: Distinct, NotFound, MoveTo, IsUnique, NotExists
+** This instruction checks if index P1 contains a record for which
+** the first N serialized values exactly match the N serialized values
+** in the record in register P3, where N is the total number of values in
+** the P3 record (the P3 record is a prefix of the P1 record).
+**
+** See also: NotFound, MoveTo, IsUnique, NotExists
*/
-/* Opcode: NotFound P1 P2 *
+/* Opcode: NotFound P1 P2 P3 * *
**
-** The top of the stack holds a blob constructed by MakeRecord. P1 is
+** Register P3 holds a blob constructed by MakeRecord. P1 is
** an index. If no entry exists in P1 that matches the blob then jump
** to P2. If an entry does existing, fall through. The cursor is left
-** pointing to the entry that matches. The blob is popped from the stack.
-**
-** The difference between this operation and Distinct is that
-** Distinct does not pop the key from the stack.
+** pointing to the entry that matches.
**
-** See also: Distinct, Found, MoveTo, NotExists, IsUnique
+** See also: Found, MoveTo, NotExists, IsUnique
*/
-case OP_Distinct: /* no-push */
-case OP_NotFound: /* no-push */
-case OP_Found: { /* no-push */
+case OP_NotFound: /* jump, in3 */
+case OP_Found: { /* jump, in3 */
int i = pOp->p1;
int alreadyExists = 0;
Cursor *pC;
- assert( pTos>=p->aStack );
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
if( (pC = p->apCsr[i])->pCursor!=0 ){
- int res, rx;
+ int res;
assert( pC->isTable==0 );
- assert( pTos->flags & MEM_Blob );
- Stringify(pTos, encoding);
- rx = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, 0, &res);
- alreadyExists = rx==SQLITE_OK && res==0;
+ assert( pIn3->flags & MEM_Blob );
+ if( pOp->opcode==OP_Found ){
+ pC->pKeyInfo->prefixIsEqual = 1;
+ }
+ rc = sqlite3BtreeMoveto(pC->pCursor, pIn3->z, 0, pIn3->n, 0, &res);
+ pC->pKeyInfo->prefixIsEqual = 0;
+ if( rc!=SQLITE_OK ){
+ break;
+ }
+ alreadyExists = (res==0);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
@@ -3090,19 +3074,14 @@ case OP_Found: { /* no-push */
}else{
if( !alreadyExists ) pc = pOp->p2 - 1;
}
- if( pOp->opcode!=OP_Distinct ){
- Release(pTos);
- pTos--;
- }
break;
}
-/* Opcode: IsUnique P1 P2 *
+/* Opcode: IsUnique P1 P2 P3 P4 *
**
-** The top of the stack is an integer record number. Call this
-** record number R. The next on the stack is an index key created
-** using MakeIdxRec. Call it K. This instruction pops R from the
-** stack but it leaves K unchanged.
+** The P3 register contains an integer record number. Call this
+** record number R. The P4 register contains an index key created
+** using MakeIdxRec. Call it K.
**
** P1 is an index. So it has no data and its key consists of a
** record generated by OP_MakeRecord where the last field is the
@@ -3113,25 +3092,25 @@ case OP_Found: { /* no-push */
** If there is no such entry, then there is an immediate
** jump to P2. If any entry does exist where the index string
** matches K but the record number is not R, then the record
-** number for that entry is pushed onto the stack and control
+** number for that entry is written into P3 and control
** falls through to the next instruction.
**
-** See also: Distinct, NotFound, NotExists, Found
+** See also: NotFound, NotExists, Found
*/
-case OP_IsUnique: { /* no-push */
+case OP_IsUnique: { /* jump, in3 */
int i = pOp->p1;
- Mem *pNos = &pTos[-1];
Cursor *pCx;
BtCursor *pCrsr;
+ Mem *pK;
i64 R;
/* Pop the value R off the top of the stack
*/
- assert( pNos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
- R = pTos->u.i;
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
+ assert( pOp->p4type==P4_INT32 );
+ assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
+ pK = &p->aMem[pOp->p4.i];
+ sqlite3VdbeMemIntegerify(pIn3);
+ R = pIn3->u.i;
assert( i>=0 && i<p->nCursor );
pCx = p->apCsr[i];
assert( pCx!=0 );
@@ -3146,12 +3125,18 @@ case OP_IsUnique: { /* no-push */
/* Make sure K is a string and make zKey point to K
*/
- assert( pNos->flags & MEM_Blob );
- Stringify(pNos, encoding);
- zKey = pNos->z;
- nKey = pNos->n;
-
- szRowid = sqlite3VdbeIdxRowidLen((u8*)zKey);
+ assert( pK->flags & MEM_Blob );
+ zKey = pK->z;
+ nKey = pK->n;
+
+ /* sqlite3VdbeIdxRowidLen() only returns other than SQLITE_OK when the
+ ** record passed as an argument corrupt. Since the record in this case
+ ** has just been created by an OP_MakeRecord instruction, and not loaded
+ ** from the database file, it is not possible for it to be corrupt.
+ ** Therefore, assert(rc==SQLITE_OK).
+ */
+ rc = sqlite3VdbeIdxRowidLen((u8*)zKey, nKey, &szRowid);
+ assert(rc==SQLITE_OK);
len = nKey-szRowid;
/* Search for an entry in P1 where all but the last four bytes match K.
@@ -3159,7 +3144,7 @@ case OP_IsUnique: { /* no-push */
*/
assert( pCx->deferredMoveto==0 );
pCx->cacheStatus = CACHE_STALE;
- rc = sqlite3BtreeMoveto(pCrsr, zKey, len, 0, &res);
+ rc = sqlite3BtreeMoveto(pCrsr, zKey, 0, len, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
@@ -3170,7 +3155,7 @@ case OP_IsUnique: { /* no-push */
break;
}
}
- rc = sqlite3VdbeIdxKeyCompare(pCx, len, (u8*)zKey, &res);
+ rc = sqlite3VdbeIdxKeyCompare(pCx, 0, len, (u8*)zKey, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
if( res>0 ){
pc = pOp->p2 - 1;
@@ -3191,46 +3176,44 @@ case OP_IsUnique: { /* no-push */
break;
}
- /* The final varint of the key is different from R. Push it onto
- ** the stack. (The record number of an entry that violates a UNIQUE
- ** constraint.)
+ /* The final varint of the key is different from R. Store it back
+ ** into register R3. (The record number of an entry that violates
+ ** a UNIQUE constraint.)
*/
- pTos++;
- pTos->u.i = v;
- pTos->flags = MEM_Int;
+ pIn3->u.i = v;
+ assert( pIn3->flags&MEM_Int );
}
break;
}
-/* Opcode: NotExists P1 P2 *
+/* Opcode: NotExists P1 P2 P3 * *
**
-** Use the top of the stack as a integer key. If a record with that key
-** does not exist in table of P1, then jump to P2. If the record
-** does exist, then fall thru. The cursor is left pointing to the
-** record if it exists. The integer key is popped from the stack.
+** Use the content of register P3 as a integer key. If a record
+** with that key does not exist in table of P1, then jump to P2.
+** If the record does exist, then fall thru. The cursor is left
+** pointing to the record if it exists.
**
** The difference between this operation and NotFound is that this
** operation assumes the key is an integer and that P1 is a table whereas
** NotFound assumes key is a blob constructed from MakeRecord and
** P1 is an index.
**
-** See also: Distinct, Found, MoveTo, NotFound, IsUnique
+** See also: Found, MoveTo, NotFound, IsUnique
*/
-case OP_NotExists: { /* no-push */
+case OP_NotExists: { /* jump, in3 */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
- assert( pTos>=p->aStack );
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
int res;
u64 iKey;
- assert( pTos->flags & MEM_Int );
+ assert( pIn3->flags & MEM_Int );
assert( p->apCsr[i]->isTable );
- iKey = intToKey(pTos->u.i);
- rc = sqlite3BtreeMoveto(pCrsr, 0, iKey, 0,&res);
- pC->lastRowid = pTos->u.i;
+ iKey = intToKey(pIn3->u.i);
+ rc = sqlite3BtreeMoveto(pCrsr, 0, 0, iKey, 0,&res);
+ pC->lastRowid = pIn3->u.i;
pC->rowidIsValid = res==0;
pC->nullRow = 0;
pC->cacheStatus = CACHE_STALE;
@@ -3241,47 +3224,51 @@ case OP_NotExists: { /* no-push */
** running ioerr.test and similar failure-recovery test scripts.) */
if( res!=0 ){
pc = pOp->p2 - 1;
- pC->rowidIsValid = 0;
+ assert( pC->rowidIsValid==0 );
}
+ }else if( !pC->pseudoTable ){
+ /* This happens when an attempt to open a read cursor on the
+ ** sqlite_master table returns SQLITE_EMPTY.
+ */
+ assert( pC->isTable );
+ pc = pOp->p2 - 1;
+ assert( pC->rowidIsValid==0 );
}
- Release(pTos);
- pTos--;
break;
}
-/* Opcode: Sequence P1 * *
+/* Opcode: Sequence P1 P2 * * *
**
-** Push an integer onto the stack which is the next available
-** sequence number for cursor P1. The sequence number on the
-** cursor is incremented after the push.
+** Find the next available sequence number for cursor P1.
+** Write the sequence number into register P2.
+** The sequence number on the cursor is incremented after this
+** instruction.
*/
-case OP_Sequence: {
+case OP_Sequence: { /* out2-prerelease */
int i = pOp->p1;
- assert( pTos>=p->aStack );
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
- pTos++;
- pTos->u.i = p->apCsr[i]->seqCount++;
- pTos->flags = MEM_Int;
+ pOut->u.i = p->apCsr[i]->seqCount++;
+ MemSetTypeFlag(pOut, MEM_Int);
break;
}
-/* Opcode: NewRowid P1 P2 *
+/* Opcode: NewRowid P1 P2 P3 * *
**
** Get a new integer record number (a.k.a "rowid") used as the key to a table.
** The record number is not previously used as a key in the database
-** table that cursor P1 points to. The new record number is pushed
-** onto the stack.
+** table that cursor P1 points to. The new record number is written
+** written to register P2.
**
-** If P2>0 then P2 is a memory cell that holds the largest previously
+** If P3>0 then P3 is a register that holds the largest previously
** generated record number. No new record numbers are allowed to be less
** than this value. When this value reaches its maximum, a SQLITE_FULL
-** error is generated. The P2 memory cell is updated with the generated
-** record number. This P2 mechanism is used to help implement the
+** error is generated. The P3 register is updated with the generated
+** record number. This P3 mechanism is used to help implement the
** AUTOINCREMENT feature.
*/
-case OP_NewRowid: {
+case OP_NewRowid: { /* out2-prerelease */
int i = pOp->p1;
i64 v = 0;
Cursor *pC;
@@ -3317,7 +3304,7 @@ case OP_NewRowid: {
** random number generator based on the RC4 algorithm.
**
** To promote locality of reference for repetitive inserts, the
- ** first few attempts at chosing a random rowid pick values just a little
+ ** first few attempts at choosing a random rowid pick values just a little
** larger than the previous rowid. This has been shown experimentally
** to double the speed of the COPY operation.
*/
@@ -3364,12 +3351,13 @@ case OP_NewRowid: {
}
#ifndef SQLITE_OMIT_AUTOINCREMENT
- if( pOp->p2 ){
+ if( pOp->p3 ){
Mem *pMem;
- assert( pOp->p2>0 && pOp->p2<p->nMem ); /* P2 is a valid memory cell */
- pMem = &p->aMem[pOp->p2];
+ assert( pOp->p3>0 && pOp->p3<=p->nMem ); /* P3 is a valid memory cell */
+ pMem = &p->aMem[pOp->p3];
+ REGISTER_TRACE(pOp->p3, pMem);
sqlite3VdbeMemIntegerify(pMem);
- assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P2) holds an integer */
+ assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){
rc = SQLITE_FULL;
goto abort_due_to_error;
@@ -3389,23 +3377,21 @@ case OP_NewRowid: {
}
}
if( pC->useRandomRowid ){
- assert( pOp->p2==0 ); /* SQLITE_FULL must have occurred prior to this */
+ assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */
v = db->priorNewRowid;
cnt = 0;
do{
- if( v==0 || cnt>2 ){
- sqlite3Randomness(sizeof(v), &v);
- if( cnt<5 ) v &= 0xffffff;
+ if( cnt==0 && (v&0xffffff)==v ){
+ v++;
}else{
- unsigned char r;
- sqlite3Randomness(1, &r);
- v += r + 1;
+ sqlite3_randomness(sizeof(v), &v);
+ if( cnt<5 ) v &= 0xffffff;
}
if( v==0 ) continue;
x = intToKey(v);
- rx = sqlite3BtreeMoveto(pC->pCursor, 0, (u64)x, 0, &res);
+ rx = sqlite3BtreeMoveto(pC->pCursor, 0, 0, (u64)x, 0, &res);
cnt++;
- }while( cnt<1000 && rx==SQLITE_OK && res==0 );
+ }while( cnt<100 && rx==SQLITE_OK && res==0 );
db->priorNewRowid = v;
if( rx==SQLITE_OK && res==0 ){
rc = SQLITE_FULL;
@@ -3416,104 +3402,115 @@ case OP_NewRowid: {
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
- pTos++;
- pTos->u.i = v;
- pTos->flags = MEM_Int;
+ MemSetTypeFlag(pOut, MEM_Int);
+ pOut->u.i = v;
break;
}
-/* Opcode: Insert P1 P2 P3
+/* Opcode: Insert P1 P2 P3 P4 P5
**
** Write an entry into the table of cursor P1. A new entry is
** created if it doesn't already exist or the data for an existing
-** entry is overwritten. The data is the value on the top of the
-** stack. The key is the next value down on the stack. The key must
-** be an integer. The stack is popped twice by this instruction.
+** entry is overwritten. The data is the value stored register
+** number P2. The key is stored in register P3. The key must
+** be an integer.
**
-** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
-** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P2 is set,
+** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is
+** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set,
** then rowid is stored for subsequent return by the
-** sqlite3_last_insert_rowid() function (otherwise it's unmodified).
+** sqlite3_last_insert_rowid() function (otherwise it is unmodified).
**
-** Parameter P3 may point to a string containing the table-name, or
+** Parameter P4 may point to a string containing the table-name, or
** may be NULL. If it is not NULL, then the update-hook
** (sqlite3.xUpdateCallback) is invoked following a successful insert.
**
+** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically
+** allocated, then ownership of P2 is transferred to the pseudo-cursor
+** and register P2 becomes ephemeral. If the cursor is changed, the
+** value of register P2 will then change. Make sure this does not
+** cause any problems.)
+**
** This instruction only works on tables. The equivalent instruction
** for indices is OP_IdxInsert.
*/
-case OP_Insert: { /* no-push */
- Mem *pNos = &pTos[-1];
+case OP_Insert: {
+ Mem *pData = &p->aMem[pOp->p2];
+ Mem *pKey = &p->aMem[pOp->p3];
+
+ i64 iKey; /* The integer ROWID or key for the record to be inserted */
int i = pOp->p1;
Cursor *pC;
- assert( pNos>=p->aStack );
assert( i>=0 && i<p->nCursor );
- assert( p->apCsr[i]!=0 );
- if( ((pC = p->apCsr[i])->pCursor!=0 || pC->pseudoTable) ){
- i64 iKey; /* The integer ROWID or key for the record to be inserted */
-
- assert( pNos->flags & MEM_Int );
- assert( pC->isTable );
- iKey = intToKey(pNos->u.i);
-
- if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
- if( pOp->p2 & OPFLAG_LASTROWID ) db->lastRowid = pNos->u.i;
- if( pC->nextRowidValid && pNos->u.i>=pC->nextRowid ){
- pC->nextRowidValid = 0;
- }
- if( pTos->flags & MEM_Null ){
- pTos->z = 0;
- pTos->n = 0;
- }else{
- assert( pTos->flags & (MEM_Blob|MEM_Str) );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ assert( pC->pCursor!=0 || pC->pseudoTable );
+ assert( pKey->flags & MEM_Int );
+ assert( pC->isTable );
+ REGISTER_TRACE(pOp->p2, pData);
+ REGISTER_TRACE(pOp->p3, pKey);
+
+ iKey = intToKey(pKey->u.i);
+ if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i;
+ if( pC->nextRowidValid && pKey->u.i>=pC->nextRowid ){
+ pC->nextRowidValid = 0;
+ }
+ if( pData->flags & MEM_Null ){
+ pData->z = 0;
+ pData->n = 0;
+ }else{
+ assert( pData->flags & (MEM_Blob|MEM_Str) );
+ }
+ if( pC->pseudoTable ){
+ if( !pC->ephemPseudoTable ){
+ sqlite3DbFree(db, pC->pData);
}
- if( pC->pseudoTable ){
- sqliteFree(pC->pData);
- pC->iKey = iKey;
- pC->nData = pTos->n;
- if( pTos->flags & MEM_Dyn ){
- pC->pData = pTos->z;
- pTos->flags = MEM_Null;
- }else{
- pC->pData = sqliteMallocRaw( pC->nData+2 );
- if( !pC->pData ) goto no_mem;
- memcpy(pC->pData, pTos->z, pC->nData);
- pC->pData[pC->nData] = 0;
- pC->pData[pC->nData+1] = 0;
+ pC->iKey = iKey;
+ pC->nData = pData->n;
+ if( pData->z==pData->zMalloc || pC->ephemPseudoTable ){
+ pC->pData = pData->z;
+ if( !pC->ephemPseudoTable ){
+ pData->flags &= ~MEM_Dyn;
+ pData->flags |= MEM_Ephem;
+ pData->zMalloc = 0;
}
- pC->nullRow = 0;
}else{
- int nZero;
- if( pTos->flags & MEM_Zero ){
- nZero = pTos->u.i;
- }else{
- nZero = 0;
- }
- rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
- pTos->z, pTos->n, nZero,
- pOp->p2 & OPFLAG_APPEND);
+ pC->pData = sqlite3Malloc( pC->nData+2 );
+ if( !pC->pData ) goto no_mem;
+ memcpy(pC->pData, pData->z, pC->nData);
+ pC->pData[pC->nData] = 0;
+ pC->pData[pC->nData+1] = 0;
}
-
- pC->rowidIsValid = 0;
- pC->deferredMoveto = 0;
- pC->cacheStatus = CACHE_STALE;
-
- /* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p3 ){
- const char *zDb = db->aDb[pC->iDb].zName;
- const char *zTbl = pOp->p3;
- int op = ((pOp->p2 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
- assert( pC->isTable );
- db->xUpdateCallback(db->pUpdateArg, op, zDb, zTbl, iKey);
- assert( pC->iDb>=0 );
+ pC->nullRow = 0;
+ }else{
+ int nZero;
+ if( pData->flags & MEM_Zero ){
+ nZero = pData->u.i;
+ }else{
+ nZero = 0;
}
+ rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
+ pData->z, pData->n, nZero,
+ pOp->p5 & OPFLAG_APPEND);
+ }
+
+ pC->rowidIsValid = 0;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
+
+ /* Invoke the update-hook if required. */
+ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
+ const char *zDb = db->aDb[pC->iDb].zName;
+ const char *zTbl = pOp->p4.z;
+ int op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
+ assert( pC->isTable );
+ db->xUpdateCallback(db->pUpdateArg, op, zDb, zTbl, iKey);
+ assert( pC->iDb>=0 );
}
- popStack(&pTos, 2);
-
break;
}
-/* Opcode: Delete P1 P2 P3
+/* Opcode: Delete P1 P2 * P4 *
**
** Delete the record at which the P1 cursor is currently pointing.
**
@@ -3525,46 +3522,45 @@ case OP_Insert: { /* no-push */
** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
** incremented (otherwise not).
**
-** If P1 is a pseudo-table, then this instruction is a no-op.
+** P1 must not be pseudo-table. It has to be a real table with
+** multiple rows.
+**
+** If P4 is not NULL, then it is the name of the table that P1 is
+** pointing to. The update hook will be invoked, if it exists.
+** If P4 is not NULL then the P1 cursor must have been positioned
+** using OP_NotFound prior to invoking this opcode.
*/
-case OP_Delete: { /* no-push */
+case OP_Delete: {
int i = pOp->p1;
+ i64 iKey;
Cursor *pC;
+
assert( i>=0 && i<p->nCursor );
pC = p->apCsr[i];
assert( pC!=0 );
- if( pC->pCursor!=0 ){
- i64 iKey;
+ assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
- /* If the update-hook will be invoked, set iKey to the rowid of the
- ** row being deleted.
- */
- if( db->xUpdateCallback && pOp->p3 ){
- assert( pC->isTable );
- if( pC->rowidIsValid ){
- iKey = pC->lastRowid;
- }else{
- rc = sqlite3BtreeKeySize(pC->pCursor, &iKey);
- if( rc ){
- goto abort_due_to_error;
- }
- iKey = keyToInt(iKey);
- }
- }
+ /* If the update-hook will be invoked, set iKey to the rowid of the
+ ** row being deleted.
+ */
+ if( db->xUpdateCallback && pOp->p4.z ){
+ assert( pC->isTable );
+ assert( pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */
+ iKey = pC->lastRowid;
+ }
- rc = sqlite3VdbeCursorMoveto(pC);
- if( rc ) goto abort_due_to_error;
- rc = sqlite3BtreeDelete(pC->pCursor);
- pC->nextRowidValid = 0;
- pC->cacheStatus = CACHE_STALE;
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ rc = sqlite3BtreeDelete(pC->pCursor);
+ pC->nextRowidValid = 0;
+ pC->cacheStatus = CACHE_STALE;
- /* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p3 ){
- const char *zDb = db->aDb[pC->iDb].zName;
- const char *zTbl = pOp->p3;
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, iKey);
- assert( pC->iDb>=0 );
- }
+ /* Invoke the update-hook if required. */
+ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
+ const char *zDb = db->aDb[pC->iDb].zName;
+ const char *zTbl = pOp->p4.z;
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, iKey);
+ assert( pC->iDb>=0 );
}
if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
break;
@@ -3577,7 +3573,7 @@ case OP_Delete: { /* no-push */
** change counter (returned by subsequent calls to sqlite3_changes())
** before it is reset. This is used by trigger programs.
*/
-case OP_ResetCount: { /* no-push */
+case OP_ResetCount: {
if( pOp->p1 ){
sqlite3VdbeSetChanges(db, p->nChange);
}
@@ -3585,94 +3581,82 @@ case OP_ResetCount: { /* no-push */
break;
}
-/* Opcode: RowData P1 * *
+/* Opcode: RowData P1 P2 * * *
**
-** Push onto the stack the complete row data for cursor P1.
-** There is no interpretation of the data. It is just copied
-** onto the stack exactly as it is found in the database file.
+** Write into register P2 the complete row data for cursor P1.
+** There is no interpretation of the data.
+** It is just copied onto the P2 register exactly as
+** it is found in the database file.
**
-** If the cursor is not pointing to a valid row, a NULL is pushed
-** onto the stack.
+** If the P1 cursor must be pointing to a valid row (not a NULL row)
+** of a real table, not a pseudo-table.
*/
-/* Opcode: RowKey P1 * *
+/* Opcode: RowKey P1 P2 * * *
**
-** Push onto the stack the complete row key for cursor P1.
-** There is no interpretation of the key. It is just copied
-** onto the stack exactly as it is found in the database file.
+** Write into register P2 the complete row key for cursor P1.
+** There is no interpretation of the data.
+** The key is copied onto the P3 register exactly as
+** it is found in the database file.
**
-** If the cursor is not pointing to a valid row, a NULL is pushed
-** onto the stack.
+** If the P1 cursor must be pointing to a valid row (not a NULL row)
+** of a real table, not a pseudo-table.
*/
case OP_RowKey:
case OP_RowData: {
int i = pOp->p1;
Cursor *pC;
+ BtCursor *pCrsr;
u32 n;
+ pOut = &p->aMem[pOp->p2];
+
/* Note that RowKey and RowData are really exactly the same instruction */
- pTos++;
assert( i>=0 && i<p->nCursor );
pC = p->apCsr[i];
assert( pC->isTable || pOp->opcode==OP_RowKey );
assert( pC->isIndex || pOp->opcode==OP_RowData );
assert( pC!=0 );
- if( pC->nullRow ){
- pTos->flags = MEM_Null;
- }else if( pC->pCursor!=0 ){
- BtCursor *pCrsr = pC->pCursor;
- rc = sqlite3VdbeCursorMoveto(pC);
- if( rc ) goto abort_due_to_error;
- if( pC->nullRow ){
- pTos->flags = MEM_Null;
- break;
- }else if( pC->isIndex ){
- i64 n64;
- assert( !pC->isTable );
- sqlite3BtreeKeySize(pCrsr, &n64);
- if( n64>SQLITE_MAX_LENGTH ){
- goto too_big;
- }
- n = n64;
- }else{
- sqlite3BtreeDataSize(pCrsr, &n);
- }
- if( n>SQLITE_MAX_LENGTH ){
+ assert( pC->nullRow==0 );
+ assert( pC->pseudoTable==0 );
+ assert( pC->pCursor!=0 );
+ pCrsr = pC->pCursor;
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ if( pC->isIndex ){
+ i64 n64;
+ assert( !pC->isTable );
+ sqlite3BtreeKeySize(pCrsr, &n64);
+ if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- pTos->n = n;
- if( n<=NBFS ){
- pTos->flags = MEM_Blob | MEM_Short;
- pTos->z = pTos->zShort;
- }else{
- char *z = sqliteMallocRaw( n );
- if( z==0 ) goto no_mem;
- pTos->flags = MEM_Blob | MEM_Dyn;
- pTos->xDel = 0;
- pTos->z = z;
- }
- if( pC->isIndex ){
- rc = sqlite3BtreeKey(pCrsr, 0, n, pTos->z);
- }else{
- rc = sqlite3BtreeData(pCrsr, 0, n, pTos->z);
+ n = n64;
+ }else{
+ sqlite3BtreeDataSize(pCrsr, &n);
+ if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}
- }else if( pC->pseudoTable ){
- pTos->n = pC->nData;
- assert( pC->nData<=SQLITE_MAX_LENGTH );
- pTos->z = pC->pData;
- pTos->flags = MEM_Blob|MEM_Ephem;
+ }
+ if( sqlite3VdbeMemGrow(pOut, n, 0) ){
+ goto no_mem;
+ }
+ pOut->n = n;
+ MemSetTypeFlag(pOut, MEM_Blob);
+ if( pC->isIndex ){
+ rc = sqlite3BtreeKey(pCrsr, 0, n, pOut->z);
}else{
- pTos->flags = MEM_Null;
+ rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z);
}
- pTos->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
+ pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
+ UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Rowid P1 * *
+/* Opcode: Rowid P1 P2 * * *
**
-** Push onto the stack an integer which is the key of the table entry that
+** Store in register P2 an integer which is the key of the table entry that
** P1 is currently point to.
*/
-case OP_Rowid: {
+case OP_Rowid: { /* out2-prerelease */
int i = pOp->p1;
Cursor *pC;
i64 v;
@@ -3682,31 +3666,30 @@ case OP_Rowid: {
assert( pC!=0 );
rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
- pTos++;
if( pC->rowidIsValid ){
v = pC->lastRowid;
}else if( pC->pseudoTable ){
v = keyToInt(pC->iKey);
- }else if( pC->nullRow || pC->pCursor==0 ){
- pTos->flags = MEM_Null;
+ }else if( pC->nullRow ){
+ /* Leave the rowid set to a NULL */
break;
}else{
assert( pC->pCursor!=0 );
sqlite3BtreeKeySize(pC->pCursor, &v);
v = keyToInt(v);
}
- pTos->u.i = v;
- pTos->flags = MEM_Int;
+ pOut->u.i = v;
+ MemSetTypeFlag(pOut, MEM_Int);
break;
}
-/* Opcode: NullRow P1 * *
+/* Opcode: NullRow P1 * * * *
**
** Move the cursor P1 to a null row. Any OP_Column operations
-** that occur while the cursor is on the null row will always push
-** a NULL onto the stack.
+** that occur while the cursor is on the null row will always
+** write a NULL.
*/
-case OP_NullRow: { /* no-push */
+case OP_NullRow: {
int i = pOp->p1;
Cursor *pC;
@@ -3718,7 +3701,7 @@ case OP_NullRow: { /* no-push */
break;
}
-/* Opcode: Last P1 P2 *
+/* Opcode: Last P1 P2 * * *
**
** The next use of the Rowid or Column or Next instruction for P1
** will refer to the last entry in the database table or index.
@@ -3726,31 +3709,29 @@ case OP_NullRow: { /* no-push */
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
*/
-case OP_Last: { /* no-push */
+case OP_Last: { /* jump */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
+ int res;
assert( i>=0 && i<p->nCursor );
pC = p->apCsr[i];
assert( pC!=0 );
- if( (pCrsr = pC->pCursor)!=0 ){
- int res;
- rc = sqlite3BtreeLast(pCrsr, &res);
- pC->nullRow = res;
- pC->deferredMoveto = 0;
- pC->cacheStatus = CACHE_STALE;
- if( res && pOp->p2>0 ){
- pc = pOp->p2 - 1;
- }
- }else{
- pC->nullRow = 0;
+ pCrsr = pC->pCursor;
+ assert( pCrsr!=0 );
+ rc = sqlite3BtreeLast(pCrsr, &res);
+ pC->nullRow = res;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
+ if( res && pOp->p2>0 ){
+ pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: Sort P1 P2 *
+/* Opcode: Sort P1 P2 * * *
**
** This opcode does exactly the same thing as OP_Rewind except that
** it increments an undocumented global variable used for testing.
@@ -3762,14 +3743,14 @@ case OP_Last: { /* no-push */
** regression tests can determine whether or not the optimizer is
** correctly optimizing out sorts.
*/
-case OP_Sort: { /* no-push */
+case OP_Sort: { /* jump */
#ifdef SQLITE_TEST
sqlite3_sort_count++;
sqlite3_search_count--;
#endif
/* Fall through into OP_Rewind */
}
-/* Opcode: Rewind P1 P2 *
+/* Opcode: Rewind P1 P2 * * *
**
** The next use of the Rowid or Column or Next instruction for P1
** will refer to the first entry in the database table or index.
@@ -3777,7 +3758,7 @@ case OP_Sort: { /* no-push */
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
*/
-case OP_Rewind: { /* no-push */
+case OP_Rewind: { /* jump */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
@@ -3795,32 +3776,38 @@ case OP_Rewind: { /* no-push */
res = 1;
}
pC->nullRow = res;
- if( res && pOp->p2>0 ){
+ assert( pOp->p2>0 && pOp->p2<p->nOp );
+ if( res ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: Next P1 P2 *
+/* Opcode: Next P1 P2 * * *
**
** Advance cursor P1 so that it points to the next key/data pair in its
** table or index. If there are no more key/value pairs then fall through
** to the following instruction. But if the cursor advance was successful,
** jump immediately to P2.
**
+** The P1 cursor must be for a real table, not a pseudo-table.
+**
** See also: Prev
*/
-/* Opcode: Prev P1 P2 *
+/* Opcode: Prev P1 P2 * * *
**
** Back up cursor P1 so that it points to the previous key/data pair in its
** table or index. If there is no previous key/value pairs then fall through
** to the following instruction. But if the cursor backup was successful,
** jump immediately to P2.
+**
+** The P1 cursor must be for a real table, not a pseudo-table.
*/
-case OP_Prev: /* no-push */
-case OP_Next: { /* no-push */
+case OP_Prev: /* jump */
+case OP_Next: { /* jump */
Cursor *pC;
BtCursor *pCrsr;
+ int res;
CHECK_FOR_INTERRUPT;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
@@ -3828,231 +3815,202 @@ case OP_Next: { /* no-push */
if( pC==0 ){
break; /* See ticket #2273 */
}
- if( (pCrsr = pC->pCursor)!=0 ){
- int res;
- if( pC->nullRow ){
- res = 1;
- }else{
- assert( pC->deferredMoveto==0 );
- rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
- sqlite3BtreePrevious(pCrsr, &res);
- pC->nullRow = res;
- pC->cacheStatus = CACHE_STALE;
- }
- if( res==0 ){
- pc = pOp->p2 - 1;
+ pCrsr = pC->pCursor;
+ assert( pCrsr );
+ res = 1;
+ assert( pC->deferredMoveto==0 );
+ rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
+ sqlite3BtreePrevious(pCrsr, &res);
+ pC->nullRow = res;
+ pC->cacheStatus = CACHE_STALE;
+ if( res==0 ){
+ pc = pOp->p2 - 1;
#ifdef SQLITE_TEST
- sqlite3_search_count++;
+ sqlite3_search_count++;
#endif
- }
- }else{
- pC->nullRow = 1;
}
pC->rowidIsValid = 0;
break;
}
-/* Opcode: IdxInsert P1 P2 *
+/* Opcode: IdxInsert P1 P2 P3 * *
**
-** The top of the stack holds a SQL index key made using either the
-** MakeIdxRec or MakeRecord instructions. This opcode writes that key
+** Register P2 holds a SQL index key made using the
+** MakeIdxRec instructions. This opcode writes that key
** into the index P1. Data for the entry is nil.
**
-** P2 is a flag that provides a hint to the b-tree layer that this
+** P3 is a flag that provides a hint to the b-tree layer that this
** insert is likely to be an append.
**
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
-case OP_IdxInsert: { /* no-push */
+case OP_IdxInsert: { /* in2 */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
- assert( pTos>=p->aStack );
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
- assert( pTos->flags & MEM_Blob );
+ assert( pIn2->flags & MEM_Blob );
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
assert( pC->isTable==0 );
- rc = ExpandBlob(pTos);
+ rc = ExpandBlob(pIn2);
if( rc==SQLITE_OK ){
- int nKey = pTos->n;
- const char *zKey = pTos->z;
- rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p2);
+ int nKey = pIn2->n;
+ const char *zKey = pIn2->z;
+ rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3);
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
}
}
- Release(pTos);
- pTos--;
break;
}
-/* Opcode: IdxDelete P1 * *
+/* Opcode: IdxDeleteM P1 P2 P3 * *
**
-** The top of the stack is an index key built using the either the
-** MakeIdxRec or MakeRecord opcodes.
-** This opcode removes that entry from the index.
+** The content of P3 registers starting at register P2 form
+** an unpacked index key. This opcode removes that entry from the
+** index opened by cursor P1.
*/
-case OP_IdxDelete: { /* no-push */
+case OP_IdxDelete: {
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
- assert( pTos>=p->aStack );
- assert( pTos->flags & MEM_Blob );
+ assert( pOp->p3>0 );
+ assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem );
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
int res;
- rc = sqlite3BtreeMoveto(pCrsr, pTos->z, pTos->n, 0, &res);
+ UnpackedRecord r;
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = pOp->p3;
+ r.needFree = 0;
+ r.needDestroy = 0;
+ r.aMem = &p->aMem[pOp->p2];
+ rc = sqlite3BtreeMoveto(pCrsr, 0, &r, 0, 0, &res);
if( rc==SQLITE_OK && res==0 ){
rc = sqlite3BtreeDelete(pCrsr);
}
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
}
- Release(pTos);
- pTos--;
break;
}
-/* Opcode: IdxRowid P1 * *
+/* Opcode: IdxRowid P1 P2 * * *
**
-** Push onto the stack an integer which is the last entry in the record at
+** Write into register P2 an integer which is the last entry in the record at
** the end of the index key pointed to by cursor P1. This integer should be
** the rowid of the table entry to which this index entry points.
**
** See also: Rowid, MakeIdxRec.
*/
-case OP_IdxRowid: {
+case OP_IdxRowid: { /* out2-prerelease */
int i = pOp->p1;
BtCursor *pCrsr;
Cursor *pC;
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
- pTos++;
- pTos->flags = MEM_Null;
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
i64 rowid;
assert( pC->deferredMoveto==0 );
assert( pC->isTable==0 );
- if( pC->nullRow ){
- pTos->flags = MEM_Null;
- }else{
+ if( !pC->nullRow ){
rc = sqlite3VdbeIdxRowid(pCrsr, &rowid);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pTos->flags = MEM_Int;
- pTos->u.i = rowid;
+ MemSetTypeFlag(pOut, MEM_Int);
+ pOut->u.i = rowid;
}
}
break;
}
-/* Opcode: IdxGT P1 P2 *
-**
-** The top of the stack is an index entry that omits the ROWID. Compare
-** the top of stack against the index that P1 is currently pointing to.
-** Ignore the ROWID on the P1 index.
-**
-** The top of the stack might have fewer columns that P1.
-**
-** If the P1 index entry is greater than the top of the stack
-** then jump to P2. Otherwise fall through to the next instruction.
-** In either case, the stack is popped once.
-*/
-/* Opcode: IdxGE P1 P2 P3
+/* Opcode: IdxGE P1 P2 P3 P4 P5
**
-** The top of the stack is an index entry that omits the ROWID. Compare
-** the top of stack against the index that P1 is currently pointing to.
-** Ignore the ROWID on the P1 index.
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the ROWID. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
**
-** If the P1 index entry is greater than or equal to the top of the stack
+** If the P1 index entry is greater than or equal to the key value
** then jump to P2. Otherwise fall through to the next instruction.
-** In either case, the stack is popped once.
**
-** If P3 is the "+" string (or any other non-NULL string) then the
-** index taken from the top of the stack is temporarily increased by
-** an epsilon prior to the comparison. This make the opcode work
-** like IdxGT except that if the key from the stack is a prefix of
-** the key in the cursor, the result is false whereas it would be
-** true with IdxGT.
+** If P5 is non-zero then the key value is increased by an epsilon
+** prior to the comparison. This make the opcode work like IdxGT except
+** that if the key from register P3 is a prefix of the key in the cursor,
+** the result is false whereas it would be true with IdxGT.
*/
-/* Opcode: IdxLT P1 P2 P3
+/* Opcode: IdxLT P1 P2 P3 * P5
**
-** The top of the stack is an index entry that omits the ROWID. Compare
-** the top of stack against the index that P1 is currently pointing to.
-** Ignore the ROWID on the P1 index.
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the ROWID. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
**
-** If the P1 index entry is less than the top of the stack
-** then jump to P2. Otherwise fall through to the next instruction.
-** In either case, the stack is popped once.
+** If the P1 index entry is less than the key value then jump to P2.
+** Otherwise fall through to the next instruction.
**
-** If P3 is the "+" string (or any other non-NULL string) then the
-** index taken from the top of the stack is temporarily increased by
-** an epsilon prior to the comparison. This makes the opcode work
-** like IdxLE.
+** If P5 is non-zero then the key value is increased by an epsilon prior
+** to the comparison. This makes the opcode work like IdxLE.
*/
-case OP_IdxLT: /* no-push */
-case OP_IdxGT: /* no-push */
-case OP_IdxGE: { /* no-push */
+case OP_IdxLT: /* jump, in3 */
+case OP_IdxGE: { /* jump, in3 */
int i= pOp->p1;
Cursor *pC;
assert( i>=0 && i<p->nCursor );
assert( p->apCsr[i]!=0 );
- assert( pTos>=p->aStack );
if( (pC = p->apCsr[i])->pCursor!=0 ){
int res;
-
- assert( pTos->flags & MEM_Blob ); /* Created using OP_MakeRecord */
+ UnpackedRecord r;
assert( pC->deferredMoveto==0 );
- ExpandBlob(pTos);
- *pC->pIncrKey = pOp->p3!=0;
- assert( pOp->p3==0 || pOp->opcode!=OP_IdxGT );
- rc = sqlite3VdbeIdxKeyCompare(pC, pTos->n, (u8*)pTos->z, &res);
+ assert( pOp->p5==0 || pOp->p5==1 );
+ assert( pOp->p4type==P4_INT32 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = pOp->p4.i;
+ r.needFree = 0;
+ r.needDestroy = 0;
+ r.aMem = &p->aMem[pOp->p3];
+ *pC->pIncrKey = pOp->p5;
+ rc = sqlite3VdbeIdxKeyCompare(pC, &r, 0, 0, &res);
*pC->pIncrKey = 0;
- if( rc!=SQLITE_OK ){
- break;
- }
if( pOp->opcode==OP_IdxLT ){
res = -res;
- }else if( pOp->opcode==OP_IdxGE ){
+ }else{
+ assert( pOp->opcode==OP_IdxGE );
res++;
}
if( res>0 ){
pc = pOp->p2 - 1 ;
}
}
- Release(pTos);
- pTos--;
break;
}
-/* Opcode: Destroy P1 P2 *
+/* Opcode: Destroy P1 P2 P3 * *
**
** Delete an entire database table or index whose root page in the database
** file is given by P1.
**
-** The table being destroyed is in the main database file if P2==0. If
-** P2==1 then the table to be clear is in the auxiliary database file
+** The table being destroyed is in the main database file if P3==0. If
+** P3==1 then the table to be clear is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** If AUTOVACUUM is enabled then it is possible that another root page
** might be moved into the newly deleted root page in order to keep all
** root pages contiguous at the beginning of the database. The former
** value of the root page that moved - its value before the move occurred -
-** is pushed onto the stack. If no page movement was required (because
-** the table being dropped was already the last one in the database) then
-** a zero is pushed onto the stack. If AUTOVACUUM is disabled
-** then a zero is pushed onto the stack.
+** is stored in register P2. If no page
+** movement was required (because the table being dropped was already
+** the last one in the database) then a zero is stored in register P2.
+** If AUTOVACUUM is disabled then a zero is stored in register P2.
**
** See also: Clear
*/
-case OP_Destroy: {
+case OP_Destroy: { /* out2-prerelease */
int iMoved;
int iCnt;
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -4068,15 +4026,17 @@ case OP_Destroy: {
#endif
if( iCnt>1 ){
rc = SQLITE_LOCKED;
+ p->errorAction = OE_Abort;
}else{
+ int iDb = pOp->p3;
assert( iCnt==1 );
- rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1, &iMoved);
- pTos++;
- pTos->flags = MEM_Int;
- pTos->u.i = iMoved;
+ assert( (p->btreeMask & (1<<iDb))!=0 );
+ rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
+ MemSetTypeFlag(pOut, MEM_Int);
+ pOut->u.i = iMoved;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( rc==SQLITE_OK && iMoved!=0 ){
- sqlite3RootPageMoved(&db->aDb[pOp->p2], iMoved, pOp->p1);
+ sqlite3RootPageMoved(&db->aDb[iDb], iMoved, pOp->p1);
}
#endif
}
@@ -4095,51 +4055,18 @@ case OP_Destroy: {
**
** See also: Destroy
*/
-case OP_Clear: { /* no-push */
-
- /* For consistency with the way other features of SQLite operate
- ** with a truncate, we will also skip the update callback.
- */
-#if 0
- Btree *pBt = db->aDb[pOp->p2].pBt;
- if( db->xUpdateCallback && pOp->p3 ){
- const char *zDb = db->aDb[pOp->p2].zName;
- const char *zTbl = pOp->p3;
- BtCursor *pCur = 0;
- int fin = 0;
-
- rc = sqlite3BtreeCursor(pBt, pOp->p1, 0, 0, 0, &pCur);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- for(
- rc=sqlite3BtreeFirst(pCur, &fin);
- rc==SQLITE_OK && !fin;
- rc=sqlite3BtreeNext(pCur, &fin)
- ){
- i64 iKey;
- rc = sqlite3BtreeKeySize(pCur, &iKey);
- if( rc ){
- break;
- }
- iKey = keyToInt(iKey);
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, iKey);
- }
- sqlite3BtreeCloseCursor(pCur);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- }
-#endif
+case OP_Clear: {
+ assert( (p->btreeMask & (1<<pOp->p2))!=0 );
rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
break;
}
-/* Opcode: CreateTable P1 * *
+/* Opcode: CreateTable P1 P2 * * *
**
-** Allocate a new table in the main database file if P2==0 or in the
-** auxiliary database file if P2==1. Push the page number
-** for the root page of the new table onto the stack.
+** Allocate a new table in the main database file if P1==0 or in the
+** auxiliary database file if P1==1 or in an attached database if
+** P1>1. Write the root page number of the new table into
+** register P2
**
** The difference between a table and an index is this: A table must
** have a 4-byte integer key and can have arbitrary data. An index
@@ -4147,20 +4074,22 @@ case OP_Clear: { /* no-push */
**
** See also: CreateIndex
*/
-/* Opcode: CreateIndex P1 * *
+/* Opcode: CreateIndex P1 P2 * * *
**
-** Allocate a new index in the main database file if P2==0 or in the
-** auxiliary database file if P2==1. Push the page number of the
-** root page of the new index onto the stack.
+** Allocate a new index in the main database file if P1==0 or in the
+** auxiliary database file if P1==1 or in an attached database if
+** P1>1. Write the root page number of the new table into
+** register P2.
**
** See documentation on OP_CreateTable for additional information.
*/
-case OP_CreateIndex:
-case OP_CreateTable: {
+case OP_CreateIndex: /* out2-prerelease */
+case OP_CreateTable: { /* out2-prerelease */
int pgno;
int flags;
Db *pDb;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( (p->btreeMask & (1<<pOp->p1))!=0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
@@ -4170,29 +4099,26 @@ case OP_CreateTable: {
flags = BTREE_ZERODATA;
}
rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
- pTos++;
if( rc==SQLITE_OK ){
- pTos->u.i = pgno;
- pTos->flags = MEM_Int;
- }else{
- pTos->flags = MEM_Null;
+ pOut->u.i = pgno;
+ MemSetTypeFlag(pOut, MEM_Int);
}
break;
}
-/* Opcode: ParseSchema P1 P2 P3
+/* Opcode: ParseSchema P1 P2 * P4 *
**
** Read and parse all entries from the SQLITE_MASTER table of database P1
-** that match the WHERE clause P3. P2 is the "force" flag. Always do
+** that match the WHERE clause P4. P2 is the "force" flag. Always do
** the parsing if P2 is true. If P2 is false, then this routine is a
** no-op if the schema is not currently loaded. In other words, if P2
** is false, the SQLITE_MASTER table is only parsed if the rest of the
** schema is already loaded into the symbol table.
**
** This opcode invokes the parser to create a new virtual machine,
-** then runs the new virtual machine. It is thus a reentrant opcode.
+** then runs the new virtual machine. It is thus a re-entrant opcode.
*/
-case OP_ParseSchema: { /* no-push */
+case OP_ParseSchema: {
char *zSql;
int iDb = pOp->p1;
const char *zMaster;
@@ -4206,34 +4132,33 @@ case OP_ParseSchema: { /* no-push */
initData.db = db;
initData.iDb = pOp->p1;
initData.pzErrMsg = &p->zErrMsg;
- zSql = sqlite3MPrintf(
+ zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s",
- db->aDb[iDb].zName, zMaster, pOp->p3);
+ db->aDb[iDb].zName, zMaster, pOp->p4.z);
if( zSql==0 ) goto no_mem;
- sqlite3SafetyOff(db);
+ (void)sqlite3SafetyOff(db);
assert( db->init.busy==0 );
db->init.busy = 1;
- assert( !sqlite3MallocFailed() );
+ assert( !db->mallocFailed );
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
if( rc==SQLITE_ABORT ) rc = initData.rc;
- sqliteFree(zSql);
+ sqlite3DbFree(db, zSql);
db->init.busy = 0;
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
if( rc==SQLITE_NOMEM ){
- sqlite3FailedMalloc();
goto no_mem;
}
break;
}
#if !defined(SQLITE_OMIT_ANALYZE) && !defined(SQLITE_OMIT_PARSER)
-/* Opcode: LoadAnalysis P1 * *
+/* Opcode: LoadAnalysis P1 * * * *
**
** Read the sqlite_stat1 table for database P1 and load the content
** of that table into the internal index hash table. This will cause
** the analysis to be used when preparing all subsequent queries.
*/
-case OP_LoadAnalysis: { /* no-push */
+case OP_LoadAnalysis: {
int iDb = pOp->p1;
assert( iDb>=0 && iDb<db->nDb );
rc = sqlite3AnalysisLoad(db, iDb);
@@ -4241,136 +4166,131 @@ case OP_LoadAnalysis: { /* no-push */
}
#endif /* !defined(SQLITE_OMIT_ANALYZE) && !defined(SQLITE_OMIT_PARSER) */
-/* Opcode: DropTable P1 * P3
+/* Opcode: DropTable P1 * * P4 *
**
** Remove the internal (in-memory) data structures that describe
-** the table named P3 in database P1. This is called after a table
+** the table named P4 in database P1. This is called after a table
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropTable: { /* no-push */
- sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p3);
+case OP_DropTable: {
+ sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z);
break;
}
-/* Opcode: DropIndex P1 * P3
+/* Opcode: DropIndex P1 * * P4 *
**
** Remove the internal (in-memory) data structures that describe
-** the index named P3 in database P1. This is called after an index
+** the index named P4 in database P1. This is called after an index
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropIndex: { /* no-push */
- sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p3);
+case OP_DropIndex: {
+ sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z);
break;
}
-/* Opcode: DropTrigger P1 * P3
+/* Opcode: DropTrigger P1 * * P4 *
**
** Remove the internal (in-memory) data structures that describe
-** the trigger named P3 in database P1. This is called after a trigger
+** the trigger named P4 in database P1. This is called after a trigger
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropTrigger: { /* no-push */
- sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p3);
+case OP_DropTrigger: {
+ sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z);
break;
}
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
-/* Opcode: IntegrityCk P1 P2 *
+/* Opcode: IntegrityCk P1 P2 P3 * P5
**
-** Do an analysis of the currently open database. Push onto the
-** stack the text of an error message describing any problems.
-** If no problems are found, push a NULL onto the stack.
+** Do an analysis of the currently open database. Store in
+** register P1 the text of an error message describing any problems.
+** If no problems are found, store a NULL in register P1.
**
-** P1 is the address of a memory cell that contains the maximum
-** number of allowed errors. At most mem[P1] errors will be reported.
-** In other words, the analysis stops as soon as mem[P1] errors are
-** seen. Mem[P1] is updated with the number of errors remaining.
+** The register P3 contains the maximum number of allowed errors.
+** At most reg(P3) errors will be reported.
+** In other words, the analysis stops as soon as reg(P1) errors are
+** seen. Reg(P1) is updated with the number of errors remaining.
**
** The root page numbers of all tables in the database are integer
-** values on the stack. This opcode pulls as many integers as it
-** can off of the stack and uses those numbers as the root pages.
+** stored in reg(P1), reg(P1+1), reg(P1+2), .... There are P2 tables
+** total.
**
-** If P2 is not zero, the check is done on the auxiliary database
+** If P5 is not zero, the check is done on the auxiliary database
** file, not the main database file.
**
** This opcode is used to implement the integrity_check pragma.
*/
case OP_IntegrityCk: {
- int nRoot;
- int *aRoot;
- int j;
- int nErr;
- char *z;
- Mem *pnErr;
-
- for(nRoot=0; &pTos[-nRoot]>=p->aStack; nRoot++){
- if( (pTos[-nRoot].flags & MEM_Int)==0 ) break;
- }
+ int nRoot; /* Number of tables to check. (Number of root pages.) */
+ int *aRoot; /* Array of rootpage numbers for tables to be checked */
+ int j; /* Loop counter */
+ int nErr; /* Number of errors reported */
+ char *z; /* Text of the error report */
+ Mem *pnErr; /* Register keeping track of errors remaining */
+
+ nRoot = pOp->p2;
assert( nRoot>0 );
- aRoot = sqliteMallocRaw( sizeof(int)*(nRoot+1) );
+ aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
if( aRoot==0 ) goto no_mem;
- j = pOp->p1;
- assert( j>=0 && j<p->nMem );
- pnErr = &p->aMem[j];
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ pnErr = &p->aMem[pOp->p3];
assert( (pnErr->flags & MEM_Int)!=0 );
+ assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
+ pIn1 = &p->aMem[pOp->p1];
for(j=0; j<nRoot; j++){
- aRoot[j] = (pTos-j)->u.i;
+ aRoot[j] = sqlite3VdbeIntValue(&pIn1[j]);
}
aRoot[j] = 0;
- popStack(&pTos, nRoot);
- pTos++;
- z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot,
+ assert( pOp->p5<db->nDb );
+ assert( (p->btreeMask & (1<<pOp->p5))!=0 );
+ z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
pnErr->u.i, &nErr);
+ sqlite3DbFree(db, aRoot);
pnErr->u.i -= nErr;
+ sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
assert( z==0 );
- pTos->flags = MEM_Null;
+ }else if( z==0 ){
+ goto no_mem;
}else{
- pTos->z = z;
- pTos->n = strlen(z);
- pTos->flags = MEM_Str | MEM_Dyn | MEM_Term;
- pTos->xDel = 0;
- }
- pTos->enc = SQLITE_UTF8;
- sqlite3VdbeChangeEncoding(pTos, encoding);
- sqliteFree(aRoot);
+ sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free);
+ }
+ UPDATE_MAX_BLOBSIZE(pIn1);
+ sqlite3VdbeChangeEncoding(pIn1, encoding);
break;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
-/* Opcode: FifoWrite * * *
+/* Opcode: FifoWrite P1 * * * *
**
-** Write the integer on the top of the stack
-** into the Fifo.
+** Write the integer from register P1 into the Fifo.
*/
-case OP_FifoWrite: { /* no-push */
- assert( pTos>=p->aStack );
- sqlite3VdbeMemIntegerify(pTos);
- sqlite3VdbeFifoPush(&p->sFifo, pTos->u.i);
- assert( (pTos->flags & MEM_Dyn)==0 );
- pTos--;
+case OP_FifoWrite: { /* in1 */
+ p->sFifo.db = db;
+ if( sqlite3VdbeFifoPush(&p->sFifo, sqlite3VdbeIntValue(pIn1))==SQLITE_NOMEM ){
+ goto no_mem;
+ }
break;
}
-/* Opcode: FifoRead * P2 *
+/* Opcode: FifoRead P1 P2 * * *
**
-** Attempt to read a single integer from the Fifo
-** and push it onto the stack. If the Fifo is empty
-** push nothing but instead jump to P2.
+** Attempt to read a single integer from the Fifo. Store that
+** integer in register P1.
+**
+** If the Fifo is empty jump to P2.
*/
-case OP_FifoRead: {
- i64 v;
+case OP_FifoRead: { /* jump */
CHECK_FOR_INTERRUPT;
- if( sqlite3VdbeFifoPop(&p->sFifo, &v)==SQLITE_DONE ){
+ assert( pOp->p1>0 && pOp->p1<=p->nMem );
+ pOut = &p->aMem[pOp->p1];
+ MemSetTypeFlag(pOut, MEM_Int);
+ if( sqlite3VdbeFifoPop(&p->sFifo, &pOut->u.i)==SQLITE_DONE ){
pc = pOp->p2 - 1;
- }else{
- pTos++;
- pTos->u.i = v;
- pTos->flags = MEM_Int;
}
break;
}
@@ -4382,7 +4302,7 @@ case OP_FifoRead: {
** opcode. The context stores the last insert row id, the last statement change
** count, and the current statement change count.
*/
-case OP_ContextPush: { /* no-push */
+case OP_ContextPush: {
int i = p->contextStackTop++;
Context *pContext;
@@ -4390,7 +4310,7 @@ case OP_ContextPush: { /* no-push */
/* FIX ME: This should be allocated as part of the vdbe at compile-time */
if( i>=p->contextStackDepth ){
p->contextStackDepth = i+1;
- p->contextStack = sqliteReallocOrFree(p->contextStack,
+ p->contextStack = sqlite3DbReallocOrFree(db, p->contextStack,
sizeof(Context)*(i+1));
if( p->contextStack==0 ) goto no_mem;
}
@@ -4398,7 +4318,7 @@ case OP_ContextPush: { /* no-push */
pContext->lastRowid = db->lastRowid;
pContext->nChange = p->nChange;
pContext->sFifo = p->sFifo;
- sqlite3VdbeFifoInit(&p->sFifo);
+ sqlite3VdbeFifoInit(&p->sFifo, db);
break;
}
@@ -4408,7 +4328,7 @@ case OP_ContextPush: { /* no-push */
** executed. The context stores the last insert row id, the last statement
** change count, and the current statement change count.
*/
-case OP_ContextPop: { /* no-push */
+case OP_ContextPop: {
Context *pContext = &p->contextStack[--p->contextStackTop];
assert( p->contextStackTop>=0 );
db->lastRowid = pContext->lastRowid;
@@ -4419,248 +4339,144 @@ case OP_ContextPop: { /* no-push */
}
#endif /* #ifndef SQLITE_OMIT_TRIGGER */
-/* Opcode: MemStore P1 P2 *
-**
-** Write the top of the stack into memory location P1.
-** P1 should be a small integer since space is allocated
-** for all memory locations between 0 and P1 inclusive.
-**
-** After the data is stored in the memory location, the
-** stack is popped once if P2 is 1. If P2 is zero, then
-** the original data remains on the stack.
-*/
-case OP_MemStore: { /* no-push */
- assert( pTos>=p->aStack );
- assert( pOp->p1>=0 && pOp->p1<p->nMem );
- rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], pTos);
- pTos--;
-
- /* If P2 is 0 then fall thru to the next opcode, OP_MemLoad, that will
- ** restore the top of the stack to its original value.
- */
- if( pOp->p2 ){
- break;
- }
-}
-/* Opcode: MemLoad P1 * *
-**
-** Push a copy of the value in memory location P1 onto the stack.
-**
-** If the value is a string, then the value pushed is a pointer to
-** the string that is stored in the memory location. If the memory
-** location is subsequently changed (using OP_MemStore) then the
-** value pushed onto the stack will change too.
-*/
-case OP_MemLoad: {
- int i = pOp->p1;
- assert( i>=0 && i<p->nMem );
- pTos++;
- sqlite3VdbeMemShallowCopy(pTos, &p->aMem[i], MEM_Ephem);
- break;
-}
-
#ifndef SQLITE_OMIT_AUTOINCREMENT
-/* Opcode: MemMax P1 * *
+/* Opcode: MemMax P1 P2 * * *
**
-** Set the value of memory cell P1 to the maximum of its current value
-** and the value on the top of the stack. The stack is unchanged.
+** Set the value of register P1 to the maximum of its current value
+** and the value in register P2.
**
** This instruction throws an error if the memory cell is not initially
** an integer.
*/
-case OP_MemMax: { /* no-push */
- int i = pOp->p1;
- Mem *pMem;
- assert( pTos>=p->aStack );
- assert( i>=0 && i<p->nMem );
- pMem = &p->aMem[i];
- sqlite3VdbeMemIntegerify(pMem);
- sqlite3VdbeMemIntegerify(pTos);
- if( pMem->u.i<pTos->u.i){
- pMem->u.i = pTos->u.i;
+case OP_MemMax: { /* in1, in2 */
+ sqlite3VdbeMemIntegerify(pIn1);
+ sqlite3VdbeMemIntegerify(pIn2);
+ if( pIn1->u.i<pIn2->u.i){
+ pIn1->u.i = pIn2->u.i;
}
break;
}
#endif /* SQLITE_OMIT_AUTOINCREMENT */
-/* Opcode: MemIncr P1 P2 *
-**
-** Increment the integer valued memory cell P2 by the value in P1.
-**
-** It is illegal to use this instruction on a memory cell that does
-** not contain an integer. An assertion fault will result if you try.
-*/
-case OP_MemIncr: { /* no-push */
- int i = pOp->p2;
- Mem *pMem;
- assert( i>=0 && i<p->nMem );
- pMem = &p->aMem[i];
- assert( pMem->flags==MEM_Int );
- pMem->u.i += pOp->p1;
- break;
-}
-
-/* Opcode: IfMemPos P1 P2 *
+/* Opcode: IfPos P1 P2 * * *
**
-** If the value of memory cell P1 is 1 or greater, jump to P2.
+** If the value of register P1 is 1 or greater, jump to P2.
**
-** It is illegal to use this instruction on a memory cell that does
+** It is illegal to use this instruction on a register that does
** not contain an integer. An assertion fault will result if you try.
*/
-case OP_IfMemPos: { /* no-push */
- int i = pOp->p1;
- Mem *pMem;
- assert( i>=0 && i<p->nMem );
- pMem = &p->aMem[i];
- assert( pMem->flags==MEM_Int );
- if( pMem->u.i>0 ){
+case OP_IfPos: { /* jump, in1 */
+ assert( pIn1->flags&MEM_Int );
+ if( pIn1->u.i>0 ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: IfMemNeg P1 P2 *
+/* Opcode: IfNeg P1 P2 * * *
**
-** If the value of memory cell P1 is less than zero, jump to P2.
+** If the value of register P1 is less than zero, jump to P2.
**
-** It is illegal to use this instruction on a memory cell that does
+** It is illegal to use this instruction on a register that does
** not contain an integer. An assertion fault will result if you try.
*/
-case OP_IfMemNeg: { /* no-push */
- int i = pOp->p1;
- Mem *pMem;
- assert( i>=0 && i<p->nMem );
- pMem = &p->aMem[i];
- assert( pMem->flags==MEM_Int );
- if( pMem->u.i<0 ){
+case OP_IfNeg: { /* jump, in1 */
+ assert( pIn1->flags&MEM_Int );
+ if( pIn1->u.i<0 ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: IfMemZero P1 P2 *
+/* Opcode: IfZero P1 P2 * * *
**
-** If the value of memory cell P1 is exactly 0, jump to P2.
+** If the value of register P1 is exactly 0, jump to P2.
**
-** It is illegal to use this instruction on a memory cell that does
+** It is illegal to use this instruction on a register that does
** not contain an integer. An assertion fault will result if you try.
*/
-case OP_IfMemZero: { /* no-push */
- int i = pOp->p1;
- Mem *pMem;
- assert( i>=0 && i<p->nMem );
- pMem = &p->aMem[i];
- assert( pMem->flags==MEM_Int );
- if( pMem->u.i==0 ){
+case OP_IfZero: { /* jump, in1 */
+ assert( pIn1->flags&MEM_Int );
+ if( pIn1->u.i==0 ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: MemNull P1 * *
-**
-** Store a NULL in memory cell P1
-*/
-case OP_MemNull: {
- assert( pOp->p1>=0 && pOp->p1<p->nMem );
- sqlite3VdbeMemSetNull(&p->aMem[pOp->p1]);
- break;
-}
-
-/* Opcode: MemInt P1 P2 *
-**
-** Store the integer value P1 in memory cell P2.
-*/
-case OP_MemInt: {
- assert( pOp->p2>=0 && pOp->p2<p->nMem );
- sqlite3VdbeMemSetInt64(&p->aMem[pOp->p2], pOp->p1);
- break;
-}
-
-/* Opcode: MemMove P1 P2 *
-**
-** Move the content of memory cell P2 over to memory cell P1.
-** Any prior content of P1 is erased. Memory cell P2 is left
-** containing a NULL.
-*/
-case OP_MemMove: {
- assert( pOp->p1>=0 && pOp->p1<p->nMem );
- assert( pOp->p2>=0 && pOp->p2<p->nMem );
- rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], &p->aMem[pOp->p2]);
- break;
-}
-
-/* Opcode: AggStep P1 P2 P3
+/* Opcode: AggStep * P2 P3 P4 P5
**
** Execute the step function for an aggregate. The
-** function has P2 arguments. P3 is a pointer to the FuncDef
-** structure that specifies the function. Use memory location
-** P1 as the accumulator.
+** function has P5 arguments. P4 is a pointer to the FuncDef
+** structure that specifies the function. Use register
+** P3 as the accumulator.
**
-** The P2 arguments are popped from the stack.
+** The P5 arguments are taken from register P2 and its
+** successors.
*/
-case OP_AggStep: { /* no-push */
- int n = pOp->p2;
+case OP_AggStep: {
+ int n = pOp->p5;
int i;
Mem *pMem, *pRec;
sqlite3_context ctx;
sqlite3_value **apVal;
assert( n>=0 );
- pRec = &pTos[1-n];
- assert( pRec>=p->aStack );
+ pRec = &p->aMem[pOp->p2];
apVal = p->apArg;
assert( apVal || n==0 );
for(i=0; i<n; i++, pRec++){
apVal[i] = pRec;
storeTypeInfo(pRec, encoding);
}
- ctx.pFunc = (FuncDef*)pOp->p3;
- assert( pOp->p1>=0 && pOp->p1<p->nMem );
- ctx.pMem = pMem = &p->aMem[pOp->p1];
+ ctx.pFunc = pOp->p4.pFunc;
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ ctx.pMem = pMem = &p->aMem[pOp->p3];
pMem->n++;
ctx.s.flags = MEM_Null;
ctx.s.z = 0;
+ ctx.s.zMalloc = 0;
ctx.s.xDel = 0;
+ ctx.s.db = db;
ctx.isError = 0;
ctx.pColl = 0;
if( ctx.pFunc->needCollSeq ){
assert( pOp>p->aOp );
- assert( pOp[-1].p3type==P3_COLLSEQ );
+ assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
- ctx.pColl = (CollSeq *)pOp[-1].p3;
+ ctx.pColl = pOp[-1].p4.pColl;
}
(ctx.pFunc->xStep)(&ctx, n, apVal);
- popStack(&pTos, n);
if( ctx.isError ){
- sqlite3SetString(&p->zErrMsg, sqlite3_value_text(&ctx.s), (char*)0);
- rc = SQLITE_ERROR;
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
+ rc = ctx.isError;
}
sqlite3VdbeMemRelease(&ctx.s);
break;
}
-/* Opcode: AggFinal P1 P2 P3
+/* Opcode: AggFinal P1 P2 * P4 *
**
** Execute the finalizer function for an aggregate. P1 is
** the memory location that is the accumulator for the aggregate.
**
** P2 is the number of arguments that the step function takes and
-** P3 is a pointer to the FuncDef for this function. The P2
+** P4 is a pointer to the FuncDef for this function. The P2
** argument is not used by this opcode. It is only there to disambiguate
** functions that can take varying numbers of arguments. The
-** P3 argument is only needed for the degenerate case where
+** P4 argument is only needed for the degenerate case where
** the step function was not previously called.
*/
-case OP_AggFinal: { /* no-push */
+case OP_AggFinal: {
Mem *pMem;
- assert( pOp->p1>=0 && pOp->p1<p->nMem );
+ assert( pOp->p1>0 && pOp->p1<=p->nMem );
pMem = &p->aMem[pOp->p1];
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
- rc = sqlite3VdbeMemFinalize(pMem, (FuncDef*)pOp->p3);
+ rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
if( rc==SQLITE_ERROR ){
- sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pMem), (char*)0);
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem));
}
+ sqlite3VdbeChangeEncoding(pMem, encoding);
+ UPDATE_MAX_BLOBSIZE(pMem);
if( sqlite3VdbeMemTooBig(pMem) ){
goto too_big;
}
@@ -4669,13 +4485,13 @@ case OP_AggFinal: { /* no-push */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/* Opcode: Vacuum * * *
+/* Opcode: Vacuum * * * * *
**
** Vacuum the entire database. This opcode will cause other virtual
** machines to be created and run. It may not be called from within
** a transaction.
*/
-case OP_Vacuum: { /* no-push */
+case OP_Vacuum: {
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
rc = sqlite3RunVacuum(&p->zErrMsg, db);
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
@@ -4684,16 +4500,17 @@ case OP_Vacuum: { /* no-push */
#endif
#if !defined(SQLITE_OMIT_AUTOVACUUM)
-/* Opcode: IncrVacuum P1 P2 *
+/* Opcode: IncrVacuum P1 P2 * * *
**
** Perform a single step of the incremental vacuum procedure on
** the P1 database. If the vacuum has finished, jump to instruction
** P2. Otherwise, fall through to the next instruction.
*/
-case OP_IncrVacuum: { /* no-push */
+case OP_IncrVacuum: { /* jump */
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( (p->btreeMask & (1<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(pBt);
if( rc==SQLITE_DONE ){
@@ -4704,7 +4521,7 @@ case OP_IncrVacuum: { /* no-push */
}
#endif
-/* Opcode: Expire P1 * *
+/* Opcode: Expire P1 * * * *
**
** Cause precompiled statements to become expired. An expired statement
** fails with an error code of SQLITE_SCHEMA if it is ever executed
@@ -4713,7 +4530,7 @@ case OP_IncrVacuum: { /* no-push */
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
** then only the currently executing statement is affected.
*/
-case OP_Expire: { /* no-push */
+case OP_Expire: {
if( !pOp->p1 ){
sqlite3ExpirePreparedStatements(db);
}else{
@@ -4723,103 +4540,115 @@ case OP_Expire: { /* no-push */
}
#ifndef SQLITE_OMIT_SHARED_CACHE
-/* Opcode: TableLock P1 P2 P3
+/* Opcode: TableLock P1 P2 P3 P4 *
**
** Obtain a lock on a particular table. This instruction is only used when
** the shared-cache feature is enabled.
**
-** If P1 is not negative, then it is the index of the database
-** in sqlite3.aDb[] and a read-lock is required. If P1 is negative, a
-** write-lock is required. In this case the index of the database is the
-** absolute value of P1 minus one (iDb = abs(P1) - 1;) and a write-lock is
-** required.
+** If P1 is the index of the database in sqlite3.aDb[] of the database
+** on which the lock is acquired. A readlock is obtained if P3==0 or
+** a write lock if P3==1.
**
** P2 contains the root-page of the table to lock.
**
-** P3 contains a pointer to the name of the table being locked. This is only
+** P4 contains a pointer to the name of the table being locked. This is only
** used to generate an error message if the lock cannot be obtained.
*/
-case OP_TableLock: { /* no-push */
+case OP_TableLock: {
int p1 = pOp->p1;
- u8 isWriteLock = (p1<0);
- if( isWriteLock ){
- p1 = (-1*p1)-1;
- }
+ u8 isWriteLock = pOp->p3;
+ assert( p1>=0 && p1<db->nDb );
+ assert( (p->btreeMask & (1<<p1))!=0 );
+ assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
if( rc==SQLITE_LOCKED ){
- const char *z = (const char *)pOp->p3;
- sqlite3SetString(&p->zErrMsg, "database table is locked: ", z, (char*)0);
+ const char *z = pOp->p4.z;
+ sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
}
break;
}
#endif /* SQLITE_OMIT_SHARED_CACHE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VBegin * * P3
+/* Opcode: VBegin * * * P4 *
**
-** P3 a pointer to an sqlite3_vtab structure. Call the xBegin method
-** for that table.
+** P4 may be a pointer to an sqlite3_vtab structure. If so, call the
+** xBegin method for that table.
+**
+** Also, whether or not P4 is set, check that this is not being called from
+** within a callback to a virtual table xSync() method. If it is, set the
+** error code to SQLITE_LOCKED.
*/
-case OP_VBegin: { /* no-push */
- rc = sqlite3VtabBegin(db, (sqlite3_vtab *)pOp->p3);
+case OP_VBegin: {
+ sqlite3_vtab *pVtab = pOp->p4.pVtab;
+ rc = sqlite3VtabBegin(db, pVtab);
+ if( pVtab ){
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
+ }
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VCreate P1 * P3
+/* Opcode: VCreate P1 * * P4 *
**
-** P3 is the name of a virtual table in database P1. Call the xCreate method
+** P4 is the name of a virtual table in database P1. Call the xCreate method
** for that table.
*/
-case OP_VCreate: { /* no-push */
- rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p3, &p->zErrMsg);
+case OP_VCreate: {
+ rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p4.z, &p->zErrMsg);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VDestroy P1 * P3
+/* Opcode: VDestroy P1 * * P4 *
**
-** P3 is the name of a virtual table in database P1. Call the xDestroy method
+** P4 is the name of a virtual table in database P1. Call the xDestroy method
** of that table.
*/
-case OP_VDestroy: { /* no-push */
+case OP_VDestroy: {
p->inVtabMethod = 2;
- rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p3);
+ rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z);
p->inVtabMethod = 0;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VOpen P1 * P3
+/* Opcode: VOpen P1 * * P4 *
**
-** P3 is a pointer to a virtual table object, an sqlite3_vtab structure.
+** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** P1 is a cursor number. This opcode opens a cursor to the virtual
** table and stores that cursor in P1.
*/
-case OP_VOpen: { /* no-push */
+case OP_VOpen: {
Cursor *pCur = 0;
sqlite3_vtab_cursor *pVtabCursor = 0;
- sqlite3_vtab *pVtab = (sqlite3_vtab *)(pOp->p3);
+ sqlite3_vtab *pVtab = pOp->p4.pVtab;
sqlite3_module *pModule = (sqlite3_module *)pVtab->pModule;
assert(pVtab && pModule);
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
rc = pModule->xOpen(pVtab, &pVtabCursor);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
if( SQLITE_OK==rc ){
- /* Initialise sqlite3_vtab_cursor base class */
+ /* Initialize sqlite3_vtab_cursor base class */
pVtabCursor->pVtab = pVtab;
/* Initialise vdbe cursor object */
- pCur = allocateCursor(p, pOp->p1, -1);
+ pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0);
if( pCur ){
pCur->pVtabCursor = pVtabCursor;
pCur->pModule = pVtabCursor->pVtab->pModule;
}else{
+ db->mallocFailed = 1;
pModule->xClose(pVtabCursor);
}
}
@@ -4828,41 +4657,45 @@ case OP_VOpen: { /* no-push */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VFilter P1 P2 P3
+/* Opcode: VFilter P1 P2 P3 P4 *
**
** P1 is a cursor opened using VOpen. P2 is an address to jump to if
** the filtered result set is empty.
**
-** P3 is either NULL or a string that was generated by the xBestIndex
-** method of the module. The interpretation of the P3 string is left
+** P4 is either NULL or a string that was generated by the xBestIndex
+** method of the module. The interpretation of the P4 string is left
** to the module implementation.
**
** This opcode invokes the xFilter method on the virtual table specified
-** by P1. The integer query plan parameter to xFilter is the top of the
-** stack. Next down on the stack is the argc parameter. Beneath the
-** next of stack are argc additional parameters which are passed to
-** xFilter as argv. The topmost parameter (i.e. 3rd element popped from
-** the stack) becomes argv[argc-1] when passed to xFilter.
-**
-** The integer query plan parameter, argc, and all argv stack values
-** are popped from the stack before this instruction completes.
+** by P1. The integer query plan parameter to xFilter is stored in register
+** P3. Register P3+1 stores the argc parameter to be passed to the
+** xFilter method. Registers P3+2..P3+1+argc are the argc
+** additional parameters which are passed to
+** xFilter as argv. Register P3+2 becomes argv[0] when passed to xFilter.
**
-** A jump is made to P2 if the result set after filtering would be
-** empty.
+** A jump is made to P2 if the result set after filtering would be empty.
*/
-case OP_VFilter: { /* no-push */
+case OP_VFilter: { /* jump */
int nArg;
-
+ int iQuery;
const sqlite3_module *pModule;
+ Mem *pQuery = &p->aMem[pOp->p3];
+ Mem *pArgc = &pQuery[1];
+ sqlite3_vtab_cursor *pVtabCursor;
+ sqlite3_vtab *pVtab;
Cursor *pCur = p->apCsr[pOp->p1];
+
+ REGISTER_TRACE(pOp->p3, pQuery);
assert( pCur->pVtabCursor );
- pModule = pCur->pVtabCursor->pVtab->pModule;
+ pVtabCursor = pCur->pVtabCursor;
+ pVtab = pVtabCursor->pVtab;
+ pModule = pVtab->pModule;
- /* Grab the index number and argc parameters off the top of the stack. */
- assert( (&pTos[-1])>=p->aStack );
- assert( (pTos[0].flags&MEM_Int)!=0 && pTos[-1].flags==MEM_Int );
- nArg = pTos[-1].u.i;
+ /* Grab the index number and argc parameters */
+ assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int );
+ nArg = pArgc->u.i;
+ iQuery = pQuery->u.i;
/* Invoke the xFilter method */
{
@@ -4870,16 +4703,21 @@ case OP_VFilter: { /* no-push */
int i;
Mem **apArg = p->apArg;
for(i = 0; i<nArg; i++){
- apArg[i] = &pTos[i+1-2-nArg];
+ apArg[i] = &pArgc[i+1];
storeTypeInfo(apArg[i], 0);
}
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ sqlite3VtabLock(pVtab);
p->inVtabMethod = 1;
- rc = pModule->xFilter(pCur->pVtabCursor, pTos->u.i, pOp->p3, nArg, apArg);
+ rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg);
p->inVtabMethod = 0;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
+ sqlite3VtabUnlock(db, pVtab);
if( rc==SQLITE_OK ){
- res = pModule->xEof(pCur->pVtabCursor);
+ res = pModule->xEof(pVtabCursor);
}
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
@@ -4887,171 +4725,195 @@ case OP_VFilter: { /* no-push */
pc = pOp->p2 - 1;
}
}
+ pCur->nullRow = 0;
- /* Pop the index number, argc value and parameters off the stack */
- popStack(&pTos, 2+nArg);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VRowid P1 * *
+/* Opcode: VRowid P1 P2 * * *
**
-** Push an integer onto the stack which is the rowid of
+** Store into register P2 the rowid of
** the virtual-table that the P1 cursor is pointing to.
*/
-case OP_VRowid: {
+case OP_VRowid: { /* out2-prerelease */
+ sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
-
+ sqlite_int64 iRow;
Cursor *pCur = p->apCsr[pOp->p1];
- assert( pCur->pVtabCursor );
- pModule = pCur->pVtabCursor->pVtab->pModule;
- if( pModule->xRowid==0 ){
- sqlite3SetString(&p->zErrMsg, "Unsupported module operation: xRowid", 0);
- rc = SQLITE_ERROR;
- } else {
- sqlite_int64 iRow;
-
- if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
- rc = pModule->xRowid(pCur->pVtabCursor, &iRow);
- if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
- pTos++;
- pTos->flags = MEM_Int;
- pTos->u.i = iRow;
+ assert( pCur->pVtabCursor );
+ if( pCur->nullRow ){
+ break;
}
-
+ pVtab = pCur->pVtabCursor->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xRowid );
+ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ rc = pModule->xRowid(pCur->pVtabCursor, &iRow);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
+ if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
+ MemSetTypeFlag(pOut, MEM_Int);
+ pOut->u.i = iRow;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VColumn P1 P2 *
+/* Opcode: VColumn P1 P2 P3 * *
**
-** Push onto the stack the value of the P2-th column of
-** the row of the virtual-table that the P1 cursor is pointing to.
+** Store the value of the P2-th column of
+** the row of the virtual-table that the
+** P1 cursor is pointing to into register P3.
*/
case OP_VColumn: {
+ sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
+ Mem *pDest;
+ sqlite3_context sContext;
Cursor *pCur = p->apCsr[pOp->p1];
assert( pCur->pVtabCursor );
- pModule = pCur->pVtabCursor->pVtab->pModule;
- if( pModule->xColumn==0 ){
- sqlite3SetString(&p->zErrMsg, "Unsupported module operation: xColumn", 0);
- rc = SQLITE_ERROR;
- } else {
- sqlite3_context sContext;
- memset(&sContext, 0, sizeof(sContext));
- sContext.s.flags = MEM_Null;
- if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
- rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2);
+ assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ pDest = &p->aMem[pOp->p3];
+ if( pCur->nullRow ){
+ sqlite3VdbeMemSetNull(pDest);
+ break;
+ }
+ pVtab = pCur->pVtabCursor->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xColumn );
+ memset(&sContext, 0, sizeof(sContext));
- /* Copy the result of the function to the top of the stack. We
- ** do this regardless of whether or not an error occured to ensure any
- ** dynamic allocation in sContext.s (a Mem struct) is released.
- */
- sqlite3VdbeChangeEncoding(&sContext.s, encoding);
- pTos++;
- pTos->flags = 0;
- sqlite3VdbeMemMove(pTos, &sContext.s);
+ /* The output cell may already have a buffer allocated. Move
+ ** the current contents to sContext.s so in case the user-function
+ ** can use the already allocated buffer instead of allocating a
+ ** new one.
+ */
+ sqlite3VdbeMemMove(&sContext.s, pDest);
+ MemSetTypeFlag(&sContext.s, MEM_Null);
- if( sqlite3SafetyOn(db) ){
- goto abort_due_to_misuse;
- }
- if( sqlite3VdbeMemTooBig(pTos) ){
- goto too_big;
- }
+ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
+
+ /* Copy the result of the function to the P3 register. We
+ ** do this regardless of whether or not an error occured to ensure any
+ ** dynamic allocation in sContext.s (a Mem struct) is released.
+ */
+ sqlite3VdbeChangeEncoding(&sContext.s, encoding);
+ REGISTER_TRACE(pOp->p3, pDest);
+ sqlite3VdbeMemMove(pDest, &sContext.s);
+ UPDATE_MAX_BLOBSIZE(pDest);
+
+ if( sqlite3SafetyOn(db) ){
+ goto abort_due_to_misuse;
+ }
+ if( sqlite3VdbeMemTooBig(pDest) ){
+ goto too_big;
}
-
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VNext P1 P2 *
+/* Opcode: VNext P1 P2 * * *
**
** Advance virtual table P1 to the next row in its result set and
** jump to instruction P2. Or, if the virtual table has reached
** the end of its result set, then fall through to the next instruction.
*/
-case OP_VNext: { /* no-push */
+case OP_VNext: { /* jump */
+ sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res = 0;
Cursor *pCur = p->apCsr[pOp->p1];
assert( pCur->pVtabCursor );
- pModule = pCur->pVtabCursor->pVtab->pModule;
- if( pModule->xNext==0 ){
- sqlite3SetString(&p->zErrMsg, "Unsupported module operation: xNext", 0);
- rc = SQLITE_ERROR;
- } else {
- /* Invoke the xNext() method of the module. There is no way for the
- ** underlying implementation to return an error if one occurs during
- ** xNext(). Instead, if an error occurs, true is returned (indicating that
- ** data is available) and the error code returned when xColumn or
- ** some other method is next invoked on the save virtual table cursor.
- */
- if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
- p->inVtabMethod = 1;
- rc = pModule->xNext(pCur->pVtabCursor);
- p->inVtabMethod = 0;
- if( rc==SQLITE_OK ){
- res = pModule->xEof(pCur->pVtabCursor);
- }
- if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
+ if( pCur->nullRow ){
+ break;
+ }
+ pVtab = pCur->pVtabCursor->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xNext );
- if( !res ){
- /* If there is data, jump to P2 */
- pc = pOp->p2 - 1;
- }
+ /* Invoke the xNext() method of the module. There is no way for the
+ ** underlying implementation to return an error if one occurs during
+ ** xNext(). Instead, if an error occurs, true is returned (indicating that
+ ** data is available) and the error code returned when xColumn or
+ ** some other method is next invoked on the save virtual table cursor.
+ */
+ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ sqlite3VtabLock(pVtab);
+ p->inVtabMethod = 1;
+ rc = pModule->xNext(pCur->pVtabCursor);
+ p->inVtabMethod = 0;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
+ sqlite3VtabUnlock(db, pVtab);
+ if( rc==SQLITE_OK ){
+ res = pModule->xEof(pCur->pVtabCursor);
}
+ if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
+ if( !res ){
+ /* If there is data, jump to P2 */
+ pc = pOp->p2 - 1;
+ }
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VRename * * P3
+/* Opcode: VRename P1 * * P4 *
**
-** P3 is a pointer to a virtual table object, an sqlite3_vtab structure.
+** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** This opcode invokes the corresponding xRename method. The value
-** on the top of the stack is popped and passed as the zName argument
-** to the xRename method.
+** in register P1 is passed as the zName argument to the xRename method.
*/
-case OP_VRename: { /* no-push */
- sqlite3_vtab *pVtab = (sqlite3_vtab *)(pOp->p3);
+case OP_VRename: {
+ sqlite3_vtab *pVtab = pOp->p4.pVtab;
+ Mem *pName = &p->aMem[pOp->p1];
assert( pVtab->pModule->xRename );
+ REGISTER_TRACE(pOp->p1, pName);
- Stringify(pTos, encoding);
+ Stringify(pName, encoding);
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
sqlite3VtabLock(pVtab);
- rc = pVtab->pModule->xRename(pVtab, pTos->z);
+ rc = pVtab->pModule->xRename(pVtab, pName->z);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
sqlite3VtabUnlock(db, pVtab);
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
- popStack(&pTos, 1);
break;
}
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VUpdate P1 P2 P3
+/* Opcode: VUpdate P1 P2 P3 P4 *
**
-** P3 is a pointer to a virtual table object, an sqlite3_vtab structure.
+** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** This opcode invokes the corresponding xUpdate method. P2 values
-** are taken from the stack to pass to the xUpdate invocation. The
-** value on the top of the stack corresponds to the p2th element
-** of the argv array passed to xUpdate.
+** are contiguous memory cells starting at P3 to pass to the xUpdate
+** invocation. The value in register (P3+P2-1) corresponds to the
+** p2th element of the argv array passed to xUpdate.
**
** The xUpdate method will do a DELETE or an INSERT or both.
-** The argv[0] element (which corresponds to the P2-th element down
-** on the stack) is the rowid of a row to delete. If argv[0] is
-** NULL then no deletion occurs. The argv[1] element is the rowid
-** of the new row. This can be NULL to have the virtual table
-** select the new rowid for itself. The higher elements in the
-** stack are the values of columns in the new row.
+** The argv[0] element (which corresponds to memory cell P3)
+** is the rowid of a row to delete. If argv[0] is NULL then no
+** deletion occurs. The argv[1] element is the rowid of the new
+** row. This can be NULL to have the virtual table select the new
+** rowid for itself. The subsequent elements in the array are
+** the values of columns in the new row.
**
** If P2==1 then no insert is performed. argv[0] is the rowid of
** a row to delete.
@@ -5060,42 +4922,95 @@ case OP_VRename: { /* no-push */
** is successful, then the value returned by sqlite3_last_insert_rowid()
** is set to the value of the rowid for the row just inserted.
*/
-case OP_VUpdate: { /* no-push */
- sqlite3_vtab *pVtab = (sqlite3_vtab *)(pOp->p3);
+case OP_VUpdate: {
+ sqlite3_vtab *pVtab = pOp->p4.pVtab;
sqlite3_module *pModule = (sqlite3_module *)pVtab->pModule;
int nArg = pOp->p2;
- assert( pOp->p3type==P3_VTAB );
+ assert( pOp->p4type==P4_VTAB );
if( pModule->xUpdate==0 ){
- sqlite3SetString(&p->zErrMsg, "read-only table", 0);
+ sqlite3SetString(&p->zErrMsg, db, "read-only table");
rc = SQLITE_ERROR;
}else{
int i;
sqlite_int64 rowid;
Mem **apArg = p->apArg;
- Mem *pX = &pTos[1-nArg];
- for(i = 0; i<nArg; i++, pX++){
+ Mem *pX = &p->aMem[pOp->p3];
+ for(i=0; i<nArg; i++){
storeTypeInfo(pX, 0);
apArg[i] = pX;
+ pX++;
}
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
sqlite3VtabLock(pVtab);
rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
sqlite3VtabUnlock(db, pVtab);
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
if( pOp->p1 && rc==SQLITE_OK ){
assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
db->lastRowid = rowid;
}
+ p->nChange++;
}
- popStack(&pTos, nArg);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-/* An other opcode is illegal...
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+/* Opcode: Pagecount P1 P2 * * *
+**
+** Write the current number of pages in database P1 to memory cell P2.
+*/
+case OP_Pagecount: { /* out2-prerelease */
+ int p1 = pOp->p1;
+ int nPage;
+ Pager *pPager = sqlite3BtreePager(db->aDb[p1].pBt);
+
+ rc = sqlite3PagerPagecount(pPager, &nPage);
+ if( rc==SQLITE_OK ){
+ pOut->flags = MEM_Int;
+ pOut->u.i = nPage;
+ }
+ break;
+}
+#endif
+
+#ifndef SQLITE_OMIT_TRACE
+/* Opcode: Trace * * * P4 *
+**
+** If tracing is enabled (by the sqlite3_trace()) interface, then
+** the UTF-8 string contained in P4 is emitted on the trace callback.
+*/
+case OP_Trace: {
+ if( pOp->p4.z ){
+ if( db->xTrace ){
+ db->xTrace(db->pTraceArg, pOp->p4.z);
+ }
+#ifdef SQLITE_DEBUG
+ if( (db->flags & SQLITE_SqlTrace)!=0 ){
+ sqlite3DebugPrintf("SQL-trace: %s\n", pOp->p4.z);
+ }
+#endif /* SQLITE_DEBUG */
+ }
+ break;
+}
+#endif
+
+
+/* Opcode: Noop * * * * *
+**
+** Do nothing. This instruction is often useful as a jump
+** destination.
+*/
+/*
+** The magic Explain opcode are only inserted when explain==2 (which
+** is to say when the EXPLAIN QUERY PLAN syntax is used.)
+** This opcode records information from the optimizer. It is the
+** the same as a no-op. This opcodesnever appears in a real VM program.
*/
-default: {
- assert( 0 );
+default: { /* This is really OP_Noop and OP_Explain */
break;
}
@@ -5107,103 +5022,72 @@ default: {
*****************************************************************************/
}
- /* Make sure the stack limit was not exceeded */
- assert( pTos<=pStackLimit );
-
#ifdef VDBE_PROFILE
{
- long long elapse = hwtime() - start;
- pOp->cycles += elapse;
+ u64 elapsed = sqlite3Hwtime() - start;
+ pOp->cycles += elapsed;
pOp->cnt++;
#if 0
- fprintf(stdout, "%10lld ", elapse);
+ fprintf(stdout, "%10llu ", elapsed);
sqlite3VdbePrintOp(stdout, origPc, &p->aOp[origPc]);
#endif
}
#endif
-#ifdef SQLITE_TEST
- /* Keep track of the size of the largest BLOB or STR that has appeared
- ** on the top of the VDBE stack.
- */
- if( pTos>=p->aStack && (pTos->flags & (MEM_Blob|MEM_Str))!=0
- && pTos->n>sqlite3_max_blobsize ){
- sqlite3_max_blobsize = pTos->n;
- }
-#endif
-
/* The following code adds nothing to the actual functionality
** of the program. It is only here for testing and debugging.
** On the other hand, it does burn CPU cycles every time through
** the evaluator loop. So we can leave it out when NDEBUG is defined.
*/
#ifndef NDEBUG
- /* Sanity checking on the top element of the stack. If the previous
- ** instruction was VNoChange, then the flags field of the top
- ** of the stack is set to 0. This is technically invalid for a memory
- ** cell, so avoid calling MemSanity() in this case.
- */
- if( pTos>=p->aStack && pTos->flags ){
- sqlite3VdbeMemSanity(pTos);
- assert( !sqlite3VdbeMemTooBig(pTos) );
- }
assert( pc>=-1 && pc<p->nOp );
#ifdef SQLITE_DEBUG
- /* Code for tracing the vdbe stack. */
- if( p->trace && pTos>=p->aStack ){
- int i;
- fprintf(p->trace, "Stack:");
- for(i=0; i>-5 && &pTos[i]>=p->aStack; i--){
- if( pTos[i].flags & MEM_Null ){
- fprintf(p->trace, " NULL");
- }else if( (pTos[i].flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
- fprintf(p->trace, " si:%lld", pTos[i].u.i);
- }else if( pTos[i].flags & MEM_Int ){
- fprintf(p->trace, " i:%lld", pTos[i].u.i);
- }else if( pTos[i].flags & MEM_Real ){
- fprintf(p->trace, " r:%g", pTos[i].r);
- }else{
- char zBuf[200];
- sqlite3VdbeMemPrettyPrint(&pTos[i], zBuf);
- fprintf(p->trace, " ");
- fprintf(p->trace, "%s", zBuf);
- }
+ if( p->trace ){
+ if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc);
+ if( opProperty & OPFLG_OUT2_PRERELEASE ){
+ registerTrace(p->trace, pOp->p2, pOut);
+ }
+ if( opProperty & OPFLG_OUT3 ){
+ registerTrace(p->trace, pOp->p3, pOut);
}
- if( rc!=0 ) fprintf(p->trace," rc=%d",rc);
- fprintf(p->trace,"\n");
}
#endif /* SQLITE_DEBUG */
#endif /* NDEBUG */
} /* The end of the for(;;) loop the loops through opcodes */
- /* If we reach this point, it means that execution is finished.
+ /* If we reach this point, it means that execution is finished with
+ ** an error of some kind.
*/
-vdbe_halt:
- if( rc ){
- p->rc = rc;
- rc = SQLITE_ERROR;
- }else{
- rc = SQLITE_DONE;
- }
+vdbe_error_halt:
+ assert( rc );
+ p->rc = rc;
sqlite3VdbeHalt(p);
- p->pTos = pTos;
+ if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
+ rc = SQLITE_ERROR;
+
+ /* This is the only way out of this procedure. We have to
+ ** release the mutexes on btrees that were acquired at the
+ ** top. */
+vdbe_return:
+ sqlite3BtreeMutexArrayLeave(&p->aMutex);
return rc;
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
** is encountered.
*/
too_big:
- sqlite3SetString(&p->zErrMsg, "string or blob too big", (char*)0);
+ sqlite3SetString(&p->zErrMsg, db, "string or blob too big");
rc = SQLITE_TOOBIG;
- goto vdbe_halt;
+ goto vdbe_error_halt;
/* Jump to here if a malloc() fails.
*/
no_mem:
- sqlite3SetString(&p->zErrMsg, "out of memory", (char*)0);
+ db->mallocFailed = 1;
+ sqlite3SetString(&p->zErrMsg, db, "out of memory");
rc = SQLITE_NOMEM;
- goto vdbe_halt;
+ goto vdbe_error_halt;
/* Jump to here for an SQLITE_MISUSE error.
*/
@@ -5215,23 +5099,20 @@ abort_due_to_misuse:
** should hold the error number.
*/
abort_due_to_error:
- if( p->zErrMsg==0 ){
- if( sqlite3MallocFailed() ) rc = SQLITE_NOMEM;
- sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0);
+ assert( p->zErrMsg==0 );
+ if( db->mallocFailed ) rc = SQLITE_NOMEM;
+ if( rc!=SQLITE_IOERR_NOMEM ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
}
- goto vdbe_halt;
+ goto vdbe_error_halt;
/* Jump to here if the sqlite3_interrupt() API sets the interrupt
** flag.
*/
abort_due_to_interrupt:
assert( db->u1.isInterrupted );
- if( db->magic!=SQLITE_MAGIC_BUSY ){
- rc = SQLITE_MISUSE;
- }else{
- rc = SQLITE_INTERRUPT;
- }
+ rc = SQLITE_INTERRUPT;
p->rc = rc;
- sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0);
- goto vdbe_halt;
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
+ goto vdbe_error_halt;
}
diff --git a/third_party/sqlite/vdbe.h b/third_party/sqlite/src/vdbe.h
index f6b145b..815b615 100644..100755
--- a/third_party/sqlite/vdbe.h
+++ b/third_party/sqlite/src/vdbe.h
@@ -15,7 +15,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
-** $Id: vdbe.h,v 1.110 2007/05/08 21:45:28 drh Exp $
+** $Id: vdbe.h,v 1.135 2008/08/01 20:10:08 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -29,19 +29,46 @@
typedef struct Vdbe Vdbe;
/*
+** The names of the following types declared in vdbeInt.h are required
+** for the VdbeOp definition.
+*/
+typedef struct VdbeFunc VdbeFunc;
+typedef struct Mem Mem;
+typedef struct UnpackedRecord UnpackedRecord;
+
+/*
** A single instruction of the virtual machine has an opcode
** and as many as three operands. The instruction is recorded
** as an instance of the following structure:
*/
struct VdbeOp {
u8 opcode; /* What operation to perform */
+ signed char p4type; /* One of the P4_xxx constants for p4 */
+ u8 opflags; /* Not currently used */
+ u8 p5; /* Fifth parameter is an unsigned character */
int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */
- char *p3; /* Third parameter */
- int p3type; /* One of the P3_xxx constants defined below */
+ int p3; /* The third parameter */
+ union { /* forth parameter */
+ int i; /* Integer value if p4type==P4_INT32 */
+ void *p; /* Generic pointer */
+ char *z; /* Pointer to data for string (char array) types */
+ i64 *pI64; /* Used when p4type is P4_INT64 */
+ double *pReal; /* Used when p4type is P4_REAL */
+ FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
+ VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */
+ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */
+ Mem *pMem; /* Used when p4type is P4_MEM */
+ sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */
+ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
+ int *ai; /* Used when p4type is P4_INTARRAY */
+ } p4;
+#ifdef SQLITE_DEBUG
+ char *zComment; /* Comment to improve readability */
+#endif
#ifdef VDBE_PROFILE
- int cnt; /* Number of times this instruction was executed */
- long long cycles; /* Total time spend executing this instruction */
+ int cnt; /* Number of times this instruction was executed */
+ u64 cycles; /* Total time spent executing this instruction */
#endif
};
typedef struct VdbeOp VdbeOp;
@@ -53,34 +80,39 @@ typedef struct VdbeOp VdbeOp;
struct VdbeOpList {
u8 opcode; /* What operation to perform */
signed char p1; /* First operand */
- short int p2; /* Second parameter (often the jump destination) */
- char *p3; /* Third parameter */
+ signed char p2; /* Second parameter (often the jump destination) */
+ signed char p3; /* Third parameter */
};
typedef struct VdbeOpList VdbeOpList;
/*
** Allowed values of VdbeOp.p3type
*/
-#define P3_NOTUSED 0 /* The P3 parameter is not used */
-#define P3_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */
-#define P3_STATIC (-2) /* Pointer to a static string */
-#define P3_COLLSEQ (-4) /* P3 is a pointer to a CollSeq structure */
-#define P3_FUNCDEF (-5) /* P3 is a pointer to a FuncDef structure */
-#define P3_KEYINFO (-6) /* P3 is a pointer to a KeyInfo structure */
-#define P3_VDBEFUNC (-7) /* P3 is a pointer to a VdbeFunc structure */
-#define P3_MEM (-8) /* P3 is a pointer to a Mem* structure */
-#define P3_TRANSIENT (-9) /* P3 is a pointer to a transient string */
-#define P3_VTAB (-10) /* P3 is a pointer to an sqlite3_vtab structure */
-#define P3_MPRINTF (-11) /* P3 is a string obtained from sqlite3_mprintf() */
+#define P4_NOTUSED 0 /* The P4 parameter is not used */
+#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */
+#define P4_STATIC (-2) /* Pointer to a static string */
+#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */
+#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */
+#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
+#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */
+#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
+#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */
+#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
+#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */
+#define P4_REAL (-12) /* P4 is a 64-bit floating point value */
+#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
+#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */
+#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
-/* When adding a P3 argument using P3_KEYINFO, a copy of the KeyInfo structure
+/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
** is made. That copy is freed when the Vdbe is finalized. But if the
-** argument is P3_KEYINFO_HANDOFF, the passed in pointer is used. It still
+** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still
** gets freed when the Vdbe is finalized so it still should be obtained
** from a single sqliteMalloc(). But no copy is made and the calling
** function should *not* try to free the KeyInfo.
*/
-#define P3_KEYINFO_HANDOFF (-9)
+#define P4_KEYINFO_HANDOFF (-16)
+#define P4_KEYINFO_STATIC (-17)
/*
** The Vdbe.aColName array contains 5n Mem structures, where n is the
@@ -91,7 +123,15 @@ typedef struct VdbeOpList VdbeOpList;
#define COLNAME_DATABASE 2
#define COLNAME_TABLE 3
#define COLNAME_COLUMN 4
-#define COLNAME_N 5 /* Number of COLNAME_xxx symbols */
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */
+#else
+# ifdef SQLITE_OMIT_DECLTYPE
+# define COLNAME_N 1 /* Store only the name */
+# else
+# define COLNAME_N 2 /* Store the name and decltype */
+# endif
+#endif
/*
** The following macro converts a relative address in the p2 field
@@ -112,14 +152,20 @@ typedef struct VdbeOpList VdbeOpList;
** for a description of what each of these routines does.
*/
Vdbe *sqlite3VdbeCreate(sqlite3*);
-int sqlite3VdbeAddOp(Vdbe*,int,int,int);
-int sqlite3VdbeOp3(Vdbe*,int,int,int,const char *zP3,int);
+int sqlite3VdbeAddOp0(Vdbe*,int);
+int sqlite3VdbeAddOp1(Vdbe*,int,int);
+int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
+int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
+int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
+void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
+void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
void sqlite3VdbeJumpHere(Vdbe*, int addr);
void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N);
-void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
+void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
+void sqlite3VdbeUsesBtree(Vdbe*, int);
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*);
void sqlite3VdbeDelete(Vdbe*);
@@ -137,14 +183,24 @@ int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int);
void sqlite3VdbeCountChanges(Vdbe*);
sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n);
-const char *sqlite3VdbeGetSql(Vdbe*);
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+int sqlite3VdbeReleaseMemory(int);
+#endif
+UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,void*,int);
+void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
+int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
+
+
#ifndef NDEBUG
void sqlite3VdbeComment(Vdbe*, const char*, ...);
# define VdbeComment(X) sqlite3VdbeComment X
+ void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
+# define VdbeNoopComment(X) sqlite3VdbeNoopComment X
#else
# define VdbeComment(X)
+# define VdbeNoopComment(X)
#endif
#endif
diff --git a/third_party/sqlite/vdbeInt.h b/third_party/sqlite/src/vdbeInt.h
index 78550c4..6516f13 100644..100755
--- a/third_party/sqlite/vdbeInt.h
+++ b/third_party/sqlite/src/vdbeInt.h
@@ -14,6 +14,8 @@
** source code file "vdbe.c". When that file became too big (over
** 6000 lines long) it was split up into several smaller files and
** this header information was factored out.
+**
+** $Id: vdbeInt.h,v 1.153 2008/08/02 03:50:39 drh Exp $
*/
#ifndef _VDBEINT_H_
#define _VDBEINT_H_
@@ -25,13 +27,6 @@
#define keyToInt(X) (X)
#define intToKey(X) (X)
-/*
-** The makefile scans the vdbe.c source file and creates the following
-** array of string constants which are the names of all VDBE opcodes. This
-** array is defined in a separate source code file named opcode.c which is
-** automatically generated by the makefile.
-*/
-extern const char *const sqlite3OpcodeNames[];
/*
** SQL is translated into a sequence of instructions to be
@@ -72,6 +67,7 @@ struct Cursor {
Bool nullRow; /* True if pointing to a row with no data */
Bool nextRowidValid; /* True if the nextRowid field is valid */
Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
+ Bool ephemPseudoTable;
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
@@ -102,13 +98,6 @@ struct Cursor {
typedef struct Cursor Cursor;
/*
-** Number of bytes of string storage space available to each stack
-** layer without having to malloc. NBFS is short for Number of Bytes
-** For Strings.
-*/
-#define NBFS 32
-
-/*
** A value for Cursor.cacheValid that means the cache is always invalid.
*/
#define CACHE_STALE 0
@@ -130,15 +119,15 @@ struct Mem {
FuncDef *pDef; /* Used only when flags==MEM_Agg */
} u;
double r; /* Real value */
+ sqlite3 *db; /* The associated database connection */
char *z; /* String or BLOB value */
- int n; /* Number of characters in string value, including '\0' */
+ int n; /* Number of characters in string value, excluding '\0' */
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
- char zShort[NBFS]; /* Space for short strings */
+ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */
};
-typedef struct Mem Mem;
/* One or more of the following flags are set to indicate the validOK
** representations of the value stored in the Mem struct.
@@ -161,6 +150,9 @@ typedef struct Mem Mem;
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
+#define MemSetTypeFlag(p, f) \
+ ((p)->flags = ((p)->flags&~(MEM_Int|MEM_Real|MEM_Null|MEM_Blob|MEM_Str))|f)
+
/* Whenever Mem contains a valid string or blob representation, one of
** the following flags must be set to determine the memory management
** policy for Mem.z. The MEM_Term flag tells us whether or not the
@@ -170,7 +162,6 @@ typedef struct Mem Mem;
#define MEM_Dyn 0x0040 /* Need to call sqliteFree() on Mem.z */
#define MEM_Static 0x0080 /* Mem.z points to a static string */
#define MEM_Ephem 0x0100 /* Mem.z points to an ephemeral string */
-#define MEM_Short 0x0200 /* Mem.z points to Mem.zShort */
#define MEM_Agg 0x0400 /* Mem.z points to an agg function context */
#define MEM_Zero 0x0800 /* Mem.i contains count of 0s appended to blob */
@@ -197,7 +188,6 @@ struct VdbeFunc {
void (*xDelete)(void *); /* Destructor for the aux data */
} apAux[1]; /* One slot for each function argument */
};
-typedef struct VdbeFunc VdbeFunc;
/*
** The "context" argument for a installable function. A pointer to an
@@ -217,7 +207,7 @@ struct sqlite3_context {
VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
Mem s; /* The return value is stored here */
Mem *pMem; /* Memory cell used to store aggregate context */
- u8 isError; /* Set to true for an error */
+ int isError; /* Error code returned by the function. */
CollSeq *pColl; /* Collating sequence */
};
@@ -255,6 +245,7 @@ struct FifoPage {
typedef struct Fifo Fifo;
struct Fifo {
int nEntry; /* Total number of entries */
+ sqlite3 *db; /* The associated database connection */
FifoPage *pFirst; /* First page on the list */
FifoPage *pLast; /* Last page on the list */
};
@@ -299,8 +290,6 @@ struct Vdbe {
int nLabel; /* Number of labels used */
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
int *aLabel; /* Space to hold the labels */
- Mem *aStack; /* The operand stack, except string values */
- Mem *pTos; /* Top entry in the operand stack */
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
int nCursor; /* Number of slots in apCsr[] */
@@ -323,21 +312,19 @@ struct Vdbe {
unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
int errorAction; /* Recovery action to do in case of an error */
int inTempTrans; /* True if temp database is transactioned */
- int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */
- int returnDepth; /* Next unused element in returnStack[] */
int nResColumn; /* Number of columns in one row of the result set */
char **azResColumn; /* Values for one row of result */
- int popStack; /* Pop the stack this much on entry to VdbeExec() */
char *zErrMsg; /* Error message written here */
- u8 resOnStack; /* True if there are result values on the stack */
+ Mem *pResultSet; /* Pointer to an array of results */
u8 explain; /* True if EXPLAIN present on SQL command */
u8 changeCntOn; /* True to update the change-counter */
- u8 aborted; /* True if ROLLBACK in another VM causes an abort */
u8 expired; /* True if the VM needs to be recompiled */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
u8 inVtabMethod; /* See comments above */
int nChange; /* Number of db changes made since last reset */
i64 startTime; /* Time when query started - used for profiling */
+ int btreeMask; /* Bitmask of db->aDb[] entries referenced */
+ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
int nSql; /* Number of bytes in zSql */
char *zSql; /* Text of the SQL statement that generated this */
#ifdef SQLITE_DEBUG
@@ -348,6 +335,32 @@ struct Vdbe {
int fetchId; /* Statement number used by sqlite3_fetch_statement */
int lru; /* Counter used for LRU cache replacement */
#endif
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ Vdbe *pLruPrev;
+ Vdbe *pLruNext;
+#endif
+};
+
+/*
+** An instance of the following structure holds information about a
+** single index record that has already been parsed out into individual
+** values.
+**
+** A record is an object that contains one or more fields of data.
+** Records are used to store the content of a table row and to store
+** the key of an index. A blob encoding of a record is created by
+** the OP_MakeRecord opcode of the VDBE and is disassemblied by the
+** OP_Column opcode.
+**
+** This structure holds a record that has already been disassembled
+** into its constitutent fields.
+*/
+struct UnpackedRecord {
+ KeyInfo *pKeyInfo; /* Collation and sort-order information */
+ u16 nField; /* Number of entries in apMem[] */
+ u8 needFree; /* True if memory obtained from sqlite3_malloc() */
+ u8 needDestroy; /* True if apMem[]s should be destroyed on close */
+ Mem *aMem; /* Values */
};
/*
@@ -374,11 +387,10 @@ int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
-int sqlite3VdbeIdxKeyCompare(Cursor*, int , const unsigned char*, int*);
+int sqlite3VdbeIdxKeyCompare(Cursor*,UnpackedRecord *,int,const unsigned char*,int*);
int sqlite3VdbeIdxRowid(BtCursor *, i64 *);
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
-int sqlite3VdbeRecordCompare(void*,int,const void*,int, const void*);
-int sqlite3VdbeIdxRowidLen(const u8*);
+int sqlite3VdbeIdxRowidLen(const u8*, int, int*);
int sqlite3VdbeExec(Vdbe*);
int sqlite3VdbeList(Vdbe*);
int sqlite3VdbeHalt(Vdbe*);
@@ -386,7 +398,7 @@ int sqlite3VdbeChangeEncoding(Mem *, int);
int sqlite3VdbeMemTooBig(Mem*);
int sqlite3VdbeMemCopy(Mem*, const Mem*);
void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
-int sqlite3VdbeMemMove(Mem*, Mem*);
+void sqlite3VdbeMemMove(Mem*, Mem*);
int sqlite3VdbeMemNulTerminate(Mem*);
int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*));
void sqlite3VdbeMemSetInt64(Mem*, i64);
@@ -394,7 +406,6 @@ void sqlite3VdbeMemSetDouble(Mem*, double);
void sqlite3VdbeMemSetNull(Mem*);
void sqlite3VdbeMemSetZeroBlob(Mem*,int);
int sqlite3VdbeMemMakeWriteable(Mem*);
-int sqlite3VdbeMemDynamicify(Mem*);
int sqlite3VdbeMemStringify(Mem*, int);
i64 sqlite3VdbeIntValue(Mem*);
int sqlite3VdbeMemIntegerify(Mem*);
@@ -404,10 +415,17 @@ int sqlite3VdbeMemRealify(Mem*);
int sqlite3VdbeMemNumerify(Mem*);
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
+void sqlite3VdbeMemReleaseExternal(Mem *p);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
+const char *sqlite3OpcodeName(int);
+int sqlite3VdbeOpcodeHasProperty(int, int);
+int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+int sqlite3VdbeReleaseBuffers(Vdbe *p);
+#endif
+
#ifndef NDEBUG
void sqlite3VdbeMemSanity(Mem*);
- int sqlite3VdbeOpcodeNoPush(u8);
#endif
int sqlite3VdbeMemTranslate(Mem*, u8);
#ifdef SQLITE_DEBUG
@@ -415,7 +433,7 @@ int sqlite3VdbeMemTranslate(Mem*, u8);
void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf);
#endif
int sqlite3VdbeMemHandleBom(Mem *pMem);
-void sqlite3VdbeFifoInit(Fifo*);
+void sqlite3VdbeFifoInit(Fifo*, sqlite3*);
int sqlite3VdbeFifoPush(Fifo*, i64);
int sqlite3VdbeFifoPop(Fifo*, i64*);
void sqlite3VdbeFifoClear(Fifo*);
diff --git a/third_party/sqlite/vdbeapi.c b/third_party/sqlite/src/vdbeapi.c
index 2ef0d3c..880c4ae 100644..100755
--- a/third_party/sqlite/vdbeapi.c
+++ b/third_party/sqlite/src/vdbeapi.c
@@ -12,10 +12,167 @@
**
** This file contains code use to implement APIs that are part of the
** VDBE.
+**
+** $Id: vdbeapi.c,v 1.138 2008/08/02 03:50:39 drh Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
-#include "os.h"
+
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+/*
+** The following structure contains pointers to the end points of a
+** doubly-linked list of all compiled SQL statements that may be holding
+** buffers eligible for release when the sqlite3_release_memory() interface is
+** invoked. Access to this list is protected by the SQLITE_MUTEX_STATIC_LRU2
+** mutex.
+**
+** Statements are added to the end of this list when sqlite3_reset() is
+** called. They are removed either when sqlite3_step() or sqlite3_finalize()
+** is called. When statements are added to this list, the associated
+** register array (p->aMem[1..p->nMem]) may contain dynamic buffers that
+** can be freed using sqlite3VdbeReleaseMemory().
+**
+** When statements are added or removed from this list, the mutex
+** associated with the Vdbe being added or removed (Vdbe.db->mutex) is
+** already held. The LRU2 mutex is then obtained, blocking if necessary,
+** the linked-list pointers manipulated and the LRU2 mutex relinquished.
+*/
+struct StatementLruList {
+ Vdbe *pFirst;
+ Vdbe *pLast;
+};
+static struct StatementLruList sqlite3LruStatements;
+
+/*
+** Check that the list looks to be internally consistent. This is used
+** as part of an assert() statement as follows:
+**
+** assert( stmtLruCheck() );
+*/
+#ifndef NDEBUG
+static int stmtLruCheck(){
+ Vdbe *p;
+ for(p=sqlite3LruStatements.pFirst; p; p=p->pLruNext){
+ assert(p->pLruNext || p==sqlite3LruStatements.pLast);
+ assert(!p->pLruNext || p->pLruNext->pLruPrev==p);
+ assert(p->pLruPrev || p==sqlite3LruStatements.pFirst);
+ assert(!p->pLruPrev || p->pLruPrev->pLruNext==p);
+ }
+ return 1;
+}
+#endif
+
+/*
+** Add vdbe p to the end of the statement lru list. It is assumed that
+** p is not already part of the list when this is called. The lru list
+** is protected by the SQLITE_MUTEX_STATIC_LRU mutex.
+*/
+static void stmtLruAdd(Vdbe *p){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+
+ if( p->pLruPrev || p->pLruNext || sqlite3LruStatements.pFirst==p ){
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+ return;
+ }
+
+ assert( stmtLruCheck() );
+
+ if( !sqlite3LruStatements.pFirst ){
+ assert( !sqlite3LruStatements.pLast );
+ sqlite3LruStatements.pFirst = p;
+ sqlite3LruStatements.pLast = p;
+ }else{
+ assert( !sqlite3LruStatements.pLast->pLruNext );
+ p->pLruPrev = sqlite3LruStatements.pLast;
+ sqlite3LruStatements.pLast->pLruNext = p;
+ sqlite3LruStatements.pLast = p;
+ }
+
+ assert( stmtLruCheck() );
+
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+}
+
+/*
+** Assuming the SQLITE_MUTEX_STATIC_LRU2 mutext is already held, remove
+** statement p from the least-recently-used statement list. If the
+** statement is not currently part of the list, this call is a no-op.
+*/
+static void stmtLruRemoveNomutex(Vdbe *p){
+ if( p->pLruPrev || p->pLruNext || p==sqlite3LruStatements.pFirst ){
+ assert( stmtLruCheck() );
+ if( p->pLruNext ){
+ p->pLruNext->pLruPrev = p->pLruPrev;
+ }else{
+ sqlite3LruStatements.pLast = p->pLruPrev;
+ }
+ if( p->pLruPrev ){
+ p->pLruPrev->pLruNext = p->pLruNext;
+ }else{
+ sqlite3LruStatements.pFirst = p->pLruNext;
+ }
+ p->pLruNext = 0;
+ p->pLruPrev = 0;
+ assert( stmtLruCheck() );
+ }
+}
+
+/*
+** Assuming the SQLITE_MUTEX_STATIC_LRU2 mutext is not held, remove
+** statement p from the least-recently-used statement list. If the
+** statement is not currently part of the list, this call is a no-op.
+*/
+static void stmtLruRemove(Vdbe *p){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+ stmtLruRemoveNomutex(p);
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+}
+
+/*
+** Try to release n bytes of memory by freeing buffers associated
+** with the memory registers of currently unused vdbes.
+*/
+int sqlite3VdbeReleaseMemory(int n){
+ Vdbe *p;
+ Vdbe *pNext;
+ int nFree = 0;
+
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+ for(p=sqlite3LruStatements.pFirst; p && nFree<n; p=pNext){
+ pNext = p->pLruNext;
+
+ /* For each statement handle in the lru list, attempt to obtain the
+ ** associated database mutex. If it cannot be obtained, continue
+ ** to the next statement handle. It is not possible to block on
+ ** the database mutex - that could cause deadlock.
+ */
+ if( SQLITE_OK==sqlite3_mutex_try(p->db->mutex) ){
+ nFree += sqlite3VdbeReleaseBuffers(p);
+ stmtLruRemoveNomutex(p);
+ sqlite3_mutex_leave(p->db->mutex);
+ }
+ }
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU2));
+
+ return nFree;
+}
+
+/*
+** Call sqlite3Reprepare() on the statement. Remove it from the
+** lru list before doing so, as Reprepare() will free all the
+** memory register buffers anyway.
+*/
+int vdbeReprepare(Vdbe *p){
+ stmtLruRemove(p);
+ return sqlite3Reprepare(p);
+}
+
+#else /* !SQLITE_ENABLE_MEMORY_MANAGEMENT */
+ #define stmtLruRemove(x)
+ #define stmtLruAdd(x)
+ #define vdbeReprepare(x) sqlite3Reprepare(x)
+#endif
+
/*
** Return TRUE (non-zero) of the statement supplied as an argument needs
@@ -30,6 +187,76 @@ int sqlite3_expired(sqlite3_stmt *pStmt){
return p==0 || p->expired;
}
+/*
+** The following routine destroys a virtual machine that is created by
+** the sqlite3_compile() routine. The integer returned is an SQLITE_
+** success/failure code that describes the result of executing the virtual
+** machine.
+**
+** This routine sets the error code and string returned by
+** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
+*/
+int sqlite3_finalize(sqlite3_stmt *pStmt){
+ int rc;
+ if( pStmt==0 ){
+ rc = SQLITE_OK;
+ }else{
+ Vdbe *v = (Vdbe*)pStmt;
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = v->db->mutex;
+#endif
+ sqlite3_mutex_enter(mutex);
+ stmtLruRemove(v);
+ rc = sqlite3VdbeFinalize(v);
+ sqlite3_mutex_leave(mutex);
+ }
+ return rc;
+}
+
+/*
+** Terminate the current execution of an SQL statement and reset it
+** back to its starting state so that it can be reused. A success code from
+** the prior execution is returned.
+**
+** This routine sets the error code and string returned by
+** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
+*/
+int sqlite3_reset(sqlite3_stmt *pStmt){
+ int rc;
+ if( pStmt==0 ){
+ rc = SQLITE_OK;
+ }else{
+ Vdbe *v = (Vdbe*)pStmt;
+ sqlite3_mutex_enter(v->db->mutex);
+ rc = sqlite3VdbeReset(v);
+ stmtLruAdd(v);
+ sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
+ assert( (rc & (v->db->errMask))==rc );
+ sqlite3_mutex_leave(v->db->mutex);
+ }
+ return rc;
+}
+
+/*
+** Set all the parameters in the compiled SQL statement to NULL.
+*/
+int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
+ int i;
+ int rc = SQLITE_OK;
+ Vdbe *p = (Vdbe*)pStmt;
+#ifndef SQLITE_MUTEX_NOOP
+ sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+#endif
+ sqlite3_mutex_enter(mutex);
+ for(i=0; i<p->nVar; i++){
+ sqlite3VdbeMemRelease(&p->aVar[i]);
+ p->aVar[i].flags = MEM_Null;
+ }
+ sqlite3_mutex_leave(mutex);
+ return rc;
+}
+
+
/**************************** sqlite3_value_ *******************************
** The following routines extract information from a Mem or sqlite3_value
** structure.
@@ -77,7 +304,6 @@ const void *sqlite3_value_text16le(sqlite3_value *pVal){
int sqlite3_value_type(sqlite3_value* pVal){
return pVal->type;
}
-/* sqlite3_value_numeric_type() defined in vdbe.c */
/**************************** sqlite3_result_ *******************************
** The following routines are used by user-defined functions to specify
@@ -90,28 +316,35 @@ void sqlite3_result_blob(
void (*xDel)(void *)
){
assert( n>=0 );
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetStr(&pCtx->s, z, n, 0, xDel);
}
void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetDouble(&pCtx->s, rVal);
}
void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
- pCtx->isError = 1;
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
- pCtx->isError = 1;
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal);
}
void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetInt64(&pCtx->s, iVal);
}
void sqlite3_result_null(sqlite3_context *pCtx){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetNull(&pCtx->s);
}
void sqlite3_result_text(
@@ -120,6 +353,7 @@ void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, xDel);
}
#ifndef SQLITE_OMIT_UTF16
@@ -129,6 +363,7 @@ void sqlite3_result_text16(
int n,
void (*xDel)(void *)
){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, xDel);
}
void sqlite3_result_text16be(
@@ -137,6 +372,7 @@ void sqlite3_result_text16be(
int n,
void (*xDel)(void *)
){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16BE, xDel);
}
void sqlite3_result_text16le(
@@ -145,21 +381,37 @@ void sqlite3_result_text16le(
int n,
void (*xDel)(void *)
){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16LE, xDel);
}
#endif /* SQLITE_OMIT_UTF16 */
void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemCopy(&pCtx->s, pValue);
}
void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetZeroBlob(&pCtx->s, n);
}
+void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
+ pCtx->isError = errCode;
+}
/* Force an SQLITE_TOOBIG error. */
void sqlite3_result_error_toobig(sqlite3_context *pCtx){
- sqlite3VdbeMemSetZeroBlob(&pCtx->s, SQLITE_MAX_LENGTH+1);
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ pCtx->isError = SQLITE_TOOBIG;
+ sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1,
+ SQLITE_UTF8, SQLITE_STATIC);
}
+/* An SQLITE_NOMEM error. */
+void sqlite3_result_error_nomem(sqlite3_context *pCtx){
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ sqlite3VdbeMemSetNull(&pCtx->s);
+ pCtx->isError = SQLITE_NOMEM;
+ pCtx->s.db->mallocFailed = 1;
+}
/*
** Execute the statement pStmt, either until a row of data is ready, the
@@ -174,15 +426,15 @@ static int sqlite3Step(Vdbe *p){
sqlite3 *db;
int rc;
- /* Assert that malloc() has not failed */
- assert( !sqlite3MallocFailed() );
-
- if( p==0 || p->magic!=VDBE_MAGIC_RUN ){
+ assert(p);
+ if( p->magic!=VDBE_MAGIC_RUN ){
return SQLITE_MISUSE;
}
- if( p->aborted ){
- return SQLITE_ABORT;
- }
+
+ /* Assert that malloc() has not failed */
+ db = p->db;
+ assert( !db->mallocFailed );
+
if( p->pc<=0 && p->expired ){
if( p->rc==SQLITE_OK ){
p->rc = SQLITE_SCHEMA;
@@ -190,7 +442,6 @@ static int sqlite3Step(Vdbe *p){
rc = SQLITE_ERROR;
goto end_of_step;
}
- db = p->db;
if( sqlite3SafetyOn(db) ){
p->rc = SQLITE_MISUSE;
return SQLITE_MISUSE;
@@ -205,38 +456,16 @@ static int sqlite3Step(Vdbe *p){
}
#ifndef SQLITE_OMIT_TRACE
- /* Invoke the trace callback if there is one
- */
- if( db->xTrace && !db->init.busy ){
- assert( p->nOp>0 );
- assert( p->aOp[p->nOp-1].opcode==OP_Noop );
- assert( p->aOp[p->nOp-1].p3!=0 );
- assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC );
- sqlite3SafetyOff(db);
- db->xTrace(db->pTraceArg, p->aOp[p->nOp-1].p3);
- if( sqlite3SafetyOn(db) ){
- p->rc = SQLITE_MISUSE;
- return SQLITE_MISUSE;
- }
- }
if( db->xProfile && !db->init.busy ){
double rNow;
- sqlite3OsCurrentTime(&rNow);
+ sqlite3OsCurrentTime(db->pVfs, &rNow);
p->startTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0;
}
#endif
- /* Print a copy of SQL as it is executed if the SQL_TRACE pragma is turned
- ** on in debugging mode.
- */
-#ifdef SQLITE_DEBUG
- if( (db->flags & SQLITE_SqlTrace)!=0 ){
- sqlite3DebugPrintf("SQL-trace: %s\n", p->aOp[p->nOp-1].p3);
- }
-#endif /* SQLITE_DEBUG */
-
db->activeVdbeCnt++;
p->pc = 0;
+ stmtLruRemove(p);
}
#ifndef SQLITE_OMIT_EXPLAIN
if( p->explain ){
@@ -254,28 +483,27 @@ static int sqlite3Step(Vdbe *p){
#ifndef SQLITE_OMIT_TRACE
/* Invoke the profile callback if there is one
*/
- if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy ){
+ if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->nOp>0
+ && p->aOp[0].opcode==OP_Trace && p->aOp[0].p4.z!=0 ){
double rNow;
u64 elapseTime;
- sqlite3OsCurrentTime(&rNow);
+ sqlite3OsCurrentTime(db->pVfs, &rNow);
elapseTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0 - p->startTime;
- assert( p->nOp>0 );
- assert( p->aOp[p->nOp-1].opcode==OP_Noop );
- assert( p->aOp[p->nOp-1].p3!=0 );
- assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC );
- db->xProfile(db->pProfileArg, p->aOp[p->nOp-1].p3, elapseTime);
+ db->xProfile(db->pProfileArg, p->aOp[0].p4.z, elapseTime);
}
#endif
- sqlite3Error(p->db, rc, 0);
+ db->errCode = rc;
+ /*sqlite3Error(p->db, rc, 0);*/
p->rc = sqlite3ApiExit(p->db, p->rc);
end_of_step:
assert( (rc&0xff)==rc );
if( p->zSql && (rc&0xff)<SQLITE_ROW ){
/* This behavior occurs if sqlite3_prepare_v2() was used to build
** the prepared statement. Return error codes directly */
- sqlite3Error(p->db, p->rc, 0);
+ p->db->errCode = p->rc;
+ /* sqlite3Error(p->db, p->rc, 0); */
return p->rc;
}else{
/* This is for legacy sqlite3_prepare() builds and when the code
@@ -291,18 +519,50 @@ end_of_step:
*/
#ifdef SQLITE_OMIT_PARSER
int sqlite3_step(sqlite3_stmt *pStmt){
- return sqlite3Step((Vdbe*)pStmt);
+ int rc = SQLITE_MISUSE;
+ if( pStmt ){
+ Vdbe *v;
+ v = (Vdbe*)pStmt;
+ sqlite3_mutex_enter(v->db->mutex);
+ rc = sqlite3Step(v);
+ sqlite3_mutex_leave(v->db->mutex);
+ }
+ return rc;
}
#else
int sqlite3_step(sqlite3_stmt *pStmt){
- int cnt = 0;
- int rc;
- Vdbe *v = (Vdbe*)pStmt;
- while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
- && cnt++ < 5
- && sqlite3Reprepare(v) ){
- sqlite3_reset(pStmt);
- v->expired = 0;
+ int rc = SQLITE_MISUSE;
+ if( pStmt ){
+ int cnt = 0;
+ Vdbe *v = (Vdbe*)pStmt;
+ sqlite3 *db = v->db;
+ sqlite3_mutex_enter(db->mutex);
+ while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
+ && cnt++ < 5
+ && vdbeReprepare(v) ){
+ sqlite3_reset(pStmt);
+ v->expired = 0;
+ }
+ if( rc==SQLITE_SCHEMA && v->zSql && db->pErr ){
+ /* This case occurs after failing to recompile an sql statement.
+ ** The error message from the SQL compiler has already been loaded
+ ** into the database handle. This block copies the error message
+ ** from the database handle into the statement and sets the statement
+ ** program counter to 0 to ensure that when the statement is
+ ** finalized or reset the parser error message is available via
+ ** sqlite3_errmsg() and sqlite3_errcode().
+ */
+ const char *zErr = (const char *)sqlite3_value_text(db->pErr);
+ sqlite3DbFree(db, v->zErrMsg);
+ if( !db->mallocFailed ){
+ v->zErrMsg = sqlite3DbStrDup(db, zErr);
+ } else {
+ v->zErrMsg = 0;
+ v->rc = SQLITE_NOMEM;
+ }
+ }
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
}
return rc;
}
@@ -318,6 +578,15 @@ void *sqlite3_user_data(sqlite3_context *p){
}
/*
+** Extract the user data from a sqlite3_context structure and return a
+** pointer to it.
+*/
+sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
+ assert( p && p->pFunc );
+ return p->s.db;
+}
+
+/*
** The following is the implementation of an SQL function that always
** fails with an error message stating that the function is used in the
** wrong context. The sqlite3_overload_function() API might construct
@@ -332,10 +601,10 @@ void sqlite3InvalidFunction(
){
const char *zName = context->pFunc->zName;
char *zErr;
- zErr = sqlite3MPrintf(
+ zErr = sqlite3MPrintf(0,
"unable to use function %s in the requested context", zName);
sqlite3_result_error(context, zErr, -1);
- sqliteFree(zErr);
+ sqlite3_free(zErr);
}
/*
@@ -344,21 +613,21 @@ void sqlite3InvalidFunction(
** same context that was returned on prior calls.
*/
void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
- Mem *pMem = p->pMem;
+ Mem *pMem;
assert( p && p->pFunc && p->pFunc->xStep );
+ assert( sqlite3_mutex_held(p->s.db->mutex) );
+ pMem = p->pMem;
if( (pMem->flags & MEM_Agg)==0 ){
if( nByte==0 ){
- assert( pMem->flags==MEM_Null );
+ sqlite3VdbeMemReleaseExternal(pMem);
+ pMem->flags = MEM_Null;
pMem->z = 0;
}else{
+ sqlite3VdbeMemGrow(pMem, nByte, 0);
pMem->flags = MEM_Agg;
- pMem->xDel = sqlite3FreeX;
pMem->u.pDef = p->pFunc;
- if( nByte<=NBFS ){
- pMem->z = pMem->zShort;
+ if( pMem->z ){
memset(pMem->z, 0, nByte);
- }else{
- pMem->z = sqliteMalloc( nByte );
}
}
}
@@ -370,7 +639,10 @@ void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
** the user-function defined by pCtx.
*/
void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
- VdbeFunc *pVdbeFunc = pCtx->pVdbeFunc;
+ VdbeFunc *pVdbeFunc;
+
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ pVdbeFunc = pCtx->pVdbeFunc;
if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
return 0;
}
@@ -392,14 +664,17 @@ void sqlite3_set_auxdata(
VdbeFunc *pVdbeFunc;
if( iArg<0 ) goto failed;
+ assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pVdbeFunc = pCtx->pVdbeFunc;
if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){
+ int nAux = (pVdbeFunc ? pVdbeFunc->nAux : 0);
int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg;
- pVdbeFunc = sqliteRealloc(pVdbeFunc, nMalloc);
- if( !pVdbeFunc ) goto failed;
+ pVdbeFunc = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc);
+ if( !pVdbeFunc ){
+ goto failed;
+ }
pCtx->pVdbeFunc = pVdbeFunc;
- memset(&pVdbeFunc->apAux[pVdbeFunc->nAux], 0,
- sizeof(struct AuxData)*(iArg+1-pVdbeFunc->nAux));
+ memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux));
pVdbeFunc->nAux = iArg+1;
pVdbeFunc->pFunc = pCtx->pFunc;
}
@@ -446,7 +721,7 @@ int sqlite3_column_count(sqlite3_stmt *pStmt){
*/
int sqlite3_data_count(sqlite3_stmt *pStmt){
Vdbe *pVm = (Vdbe *)pStmt;
- if( pVm==0 || !pVm->resOnStack ) return 0;
+ if( pVm==0 || pVm->pResultSet==0 ) return 0;
return pVm->nResColumn;
}
@@ -458,14 +733,24 @@ int sqlite3_data_count(sqlite3_stmt *pStmt){
** of NULL.
*/
static Mem *columnMem(sqlite3_stmt *pStmt, int i){
- Vdbe *pVm = (Vdbe *)pStmt;
- int vals = sqlite3_data_count(pStmt);
- if( pVm==0 || pVm->resOnStack==0 || i>=pVm->nResColumn || i<0 ){
- static const Mem nullMem = {{0}, 0.0, "", 0, MEM_Null, SQLITE_NULL };
- sqlite3Error(pVm->db, SQLITE_RANGE, 0);
- return (Mem*)&nullMem;
+ Vdbe *pVm;
+ int vals;
+ Mem *pOut;
+
+ pVm = (Vdbe *)pStmt;
+ if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
+ sqlite3_mutex_enter(pVm->db->mutex);
+ vals = sqlite3_data_count(pStmt);
+ pOut = &pVm->pResultSet[i];
+ }else{
+ static const Mem nullMem = {{0}, 0.0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
+ if( pVm->db ){
+ sqlite3_mutex_enter(pVm->db->mutex);
+ sqlite3Error(pVm->db, SQLITE_RANGE, 0);
+ }
+ pOut = (Mem*)&nullMem;
}
- return &pVm->pTos[(1-vals)+i];
+ return pOut;
}
/*
@@ -475,7 +760,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
** malloc() has failed, the threads mallocFailed flag is cleared and the result
** code of statement pStmt set to SQLITE_NOMEM.
**
-** Specificly, this is called from within:
+** Specifically, this is called from within:
**
** sqlite3_column_int()
** sqlite3_column_int64()
@@ -495,7 +780,10 @@ static void columnMallocFailure(sqlite3_stmt *pStmt)
** and _finalize() will return NOMEM.
*/
Vdbe *p = (Vdbe *)pStmt;
- p->rc = sqlite3ApiExit(0, p->rc);
+ if( p ){
+ p->rc = sqlite3ApiExit(p->db, p->rc);
+ sqlite3_mutex_leave(p->db->mutex);
+ }
}
/**************************** sqlite3_column_ *******************************
@@ -543,7 +831,9 @@ const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
return val;
}
sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
- return columnMem(pStmt, i);
+ sqlite3_value *pOut = columnMem(pStmt, i);
+ columnMallocFailure(pStmt);
+ return pOut;
}
#ifndef SQLITE_OMIT_UTF16
const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){
@@ -553,7 +843,9 @@ const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){
}
#endif /* SQLITE_OMIT_UTF16 */
int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_type( columnMem(pStmt,i) );
+ int iType = sqlite3_value_type( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return iType;
}
/* The following function is experimental and subject to change or
@@ -585,20 +877,28 @@ static const void *columnName(
const void *(*xFunc)(Mem*),
int useType
){
- const void *ret;
+ const void *ret = 0;
Vdbe *p = (Vdbe *)pStmt;
- int n = sqlite3_column_count(pStmt);
+ int n;
+
- if( p==0 || N>=n || N<0 ){
- return 0;
- }
- N += useType*n;
- ret = xFunc(&p->aColName[N]);
+ if( p!=0 ){
+ n = sqlite3_column_count(pStmt);
+ if( N<n && N>=0 ){
+ N += useType*n;
+ sqlite3_mutex_enter(p->db->mutex);
+ ret = xFunc(&p->aColName[N]);
- /* A malloc may have failed inside of the xFunc() call. If this is the case,
- ** clear the mallocFailed flag and return NULL.
- */
- sqlite3ApiExit(0, 0);
+ /* A malloc may have failed inside of the xFunc() call. If this
+ ** is the case, clear the mallocFailed flag and return NULL.
+ */
+ if( p->db && p->db->mallocFailed ){
+ p->db->mallocFailed = 0;
+ ret = 0;
+ }
+ sqlite3_mutex_leave(p->db->mutex);
+ }
+ }
return ret;
}
@@ -618,6 +918,16 @@ const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
#endif
/*
+** Constraint: If you have ENABLE_COLUMN_METADATA then you must
+** not define OMIT_DECLTYPE.
+*/
+#if defined(SQLITE_OMIT_DECLTYPE) && defined(SQLITE_ENABLE_COLUMN_METADATA)
+# error "Must not define both SQLITE_OMIT_DECLTYPE \
+ and SQLITE_ENABLE_COLUMN_METADATA"
+#endif
+
+#ifndef SQLITE_OMIT_DECLTYPE
+/*
** Return the column declaration type (if applicable) of the 'i'th column
** of the result set of SQL statement pStmt.
*/
@@ -631,6 +941,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE);
}
#endif /* SQLITE_OMIT_UTF16 */
+#endif /* SQLITE_OMIT_DECLTYPE */
#ifdef SQLITE_ENABLE_COLUMN_METADATA
/*
@@ -717,29 +1028,33 @@ static int vdbeUnbind(Vdbe *p, int i){
** Bind a text or BLOB value.
*/
static int bindText(
- sqlite3_stmt *pStmt,
- int i,
- const void *zData,
- int nData,
- void (*xDel)(void*),
- int encoding
+ sqlite3_stmt *pStmt, /* The statement to bind against */
+ int i, /* Index of the parameter to bind */
+ const void *zData, /* Pointer to the data to be bound */
+ int nData, /* Number of bytes of data to be bound */
+ void (*xDel)(void*), /* Destructor for the data */
+ int encoding /* Encoding for the data */
){
Vdbe *p = (Vdbe *)pStmt;
Mem *pVar;
int rc;
- rc = vdbeUnbind(p, i);
- if( rc || zData==0 ){
- return rc;
+ if( p==0 ){
+ return SQLITE_MISUSE;
}
- pVar = &p->aVar[i-1];
- rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
- if( rc==SQLITE_OK && encoding!=0 ){
- rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
+ sqlite3_mutex_enter(p->db->mutex);
+ rc = vdbeUnbind(p, i);
+ if( rc==SQLITE_OK && zData!=0 ){
+ pVar = &p->aVar[i-1];
+ rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
+ if( rc==SQLITE_OK && encoding!=0 ){
+ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
+ }
+ sqlite3Error(p->db, rc, 0);
+ rc = sqlite3ApiExit(p->db, rc);
}
-
- sqlite3Error(((Vdbe *)pStmt)->db, rc, 0);
- return sqlite3ApiExit(((Vdbe *)pStmt)->db, rc);
+ sqlite3_mutex_leave(p->db->mutex);
+ return rc;
}
@@ -758,10 +1073,12 @@ int sqlite3_bind_blob(
int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
rc = vdbeUnbind(p, i);
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
}
+ sqlite3_mutex_leave(p->db->mutex);
return rc;
}
int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
@@ -770,14 +1087,21 @@ int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
rc = vdbeUnbind(p, i);
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
}
+ sqlite3_mutex_leave(p->db->mutex);
return rc;
}
-int sqlite3_bind_null(sqlite3_stmt* p, int i){
- return vdbeUnbind((Vdbe *)p, i);
+int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
+ int rc;
+ Vdbe *p = (Vdbe*)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
+ rc = vdbeUnbind(p, i);
+ sqlite3_mutex_leave(p->db->mutex);
+ return rc;
}
int sqlite3_bind_text(
sqlite3_stmt *pStmt,
@@ -802,19 +1126,27 @@ int sqlite3_bind_text16(
int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
rc = vdbeUnbind(p, i);
if( rc==SQLITE_OK ){
- sqlite3VdbeMemCopy(&p->aVar[i-1], pValue);
+ rc = sqlite3VdbeMemCopy(&p->aVar[i-1], pValue);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3VdbeChangeEncoding(&p->aVar[i-1], ENC(p->db));
+ }
}
+ rc = sqlite3ApiExit(p->db, rc);
+ sqlite3_mutex_leave(p->db->mutex);
return rc;
}
int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
rc = vdbeUnbind(p, i);
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
}
+ sqlite3_mutex_leave(p->db->mutex);
return rc;
}
@@ -834,15 +1166,19 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
*/
static void createVarMap(Vdbe *p){
if( !p->okVar ){
- int j;
- Op *pOp;
- for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
- if( pOp->opcode==OP_Variable ){
- assert( pOp->p1>0 && pOp->p1<=p->nVar );
- p->azVar[pOp->p1-1] = pOp->p3;
+ sqlite3_mutex_enter(p->db->mutex);
+ if( !p->okVar ){
+ int j;
+ Op *pOp;
+ for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
+ if( pOp->opcode==OP_Variable ){
+ assert( pOp->p1>0 && pOp->p1<=p->nVar );
+ p->azVar[pOp->p1-1] = pOp->p4.z;
+ }
}
+ p->okVar = 1;
}
- p->okVar = 1;
+ sqlite3_mutex_leave(p->db->mutex);
}
}
@@ -894,17 +1230,18 @@ int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){
Vdbe *pTo = (Vdbe*)pToStmt;
int i, rc = SQLITE_OK;
if( (pFrom->magic!=VDBE_MAGIC_RUN && pFrom->magic!=VDBE_MAGIC_HALT)
- || (pTo->magic!=VDBE_MAGIC_RUN && pTo->magic!=VDBE_MAGIC_HALT) ){
+ || (pTo->magic!=VDBE_MAGIC_RUN && pTo->magic!=VDBE_MAGIC_HALT)
+ || pTo->db!=pFrom->db ){
return SQLITE_MISUSE;
}
if( pFrom->nVar!=pTo->nVar ){
return SQLITE_ERROR;
}
+ sqlite3_mutex_enter(pTo->db->mutex);
for(i=0; rc==SQLITE_OK && i<pFrom->nVar; i++){
- sqlite3MallocDisallow();
- rc = sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]);
- sqlite3MallocAllow();
+ sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]);
}
+ sqlite3_mutex_leave(pTo->db->mutex);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
return rc;
}
@@ -918,3 +1255,21 @@ int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){
sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){
return pStmt ? ((Vdbe*)pStmt)->db : 0;
}
+
+/*
+** Return a pointer to the next prepared statement after pStmt associated
+** with database connection pDb. If pStmt is NULL, return the first
+** prepared statement for the database connection. Return NULL if there
+** are no more.
+*/
+sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
+ sqlite3_stmt *pNext;
+ sqlite3_mutex_enter(pDb->mutex);
+ if( pStmt==0 ){
+ pNext = (sqlite3_stmt*)pDb->pVdbe;
+ }else{
+ pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext;
+ }
+ sqlite3_mutex_leave(pDb->mutex);
+ return pNext;
+}
diff --git a/third_party/sqlite/vdbeaux.c b/third_party/sqlite/src/vdbeaux.c
index d949448..cded81f 100644..100755
--- a/third_party/sqlite/vdbeaux.c
+++ b/third_party/sqlite/src/vdbeaux.c
@@ -13,20 +13,22 @@
** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior
** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out.
+**
+** $Id: vdbeaux.c,v 1.405 2008/08/02 03:50:39 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"
+
/*
** When debugging the code generator in a symbolic debugger, one can
-** set the sqlite3_vdbe_addop_trace to 1 and all opcodes will be printed
+** set the sqlite3VdbeAddopTrace to 1 and all opcodes will be printed
** as they are added to the instruction stream.
*/
#ifdef SQLITE_DEBUG
-int sqlite3_vdbe_addop_trace = 0;
+int sqlite3VdbeAddopTrace = 0;
#endif
@@ -35,7 +37,7 @@ int sqlite3_vdbe_addop_trace = 0;
*/
Vdbe *sqlite3VdbeCreate(sqlite3 *db){
Vdbe *p;
- p = sqliteMalloc( sizeof(Vdbe) );
+ p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
if( p==0 ) return 0;
p->db = db;
if( db->pVdbe ){
@@ -54,14 +56,14 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){
if( p==0 ) return;
assert( p->zSql==0 );
- p->zSql = sqlite3StrNDup(z, n);
+ p->zSql = sqlite3DbStrNDup(p->db, z, n);
}
/*
** Return the SQL associated with a prepared statement
*/
-const char *sqlite3VdbeGetSql(Vdbe *p){
- return p->zSql;
+const char *sqlite3_sql(sqlite3_stmt *pStmt){
+ return ((Vdbe *)pStmt)->zSql;
}
/*
@@ -99,10 +101,7 @@ void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
/*
** Resize the Vdbe.aOp array so that it contains at least N
-** elements. If the Vdbe is in VDBE_MAGIC_RUN state, then
-** the Vdbe.aOp array will be sized to contain exactly N
-** elements. Vdbe.nOpAlloc is set to reflect the new size of
-** the array.
+** elements.
**
** If an out-of-memory error occurs while resizing the array,
** Vdbe.aOp and Vdbe.nOpAlloc remain unchanged (this is so that
@@ -110,19 +109,11 @@ void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
** along with the rest of the Vdbe).
*/
static void resizeOpArray(Vdbe *p, int N){
- int runMode = p->magic==VDBE_MAGIC_RUN;
- if( runMode || p->nOpAlloc<N ){
- VdbeOp *pNew;
- int nNew = N + 100*(!runMode);
- int oldSize = p->nOpAlloc;
- pNew = sqliteRealloc(p->aOp, nNew*sizeof(Op));
- if( pNew ){
- p->nOpAlloc = nNew;
- p->aOp = pNew;
- if( nNew>oldSize ){
- memset(&p->aOp[oldSize], 0, (nNew-oldSize)*sizeof(Op));
- }
- }
+ VdbeOp *pNew;
+ pNew = sqlite3DbRealloc(p->db, p->aOp, N*sizeof(Op));
+ if( pNew ){
+ p->nOpAlloc = N;
+ p->aOp = pNew;
}
}
@@ -136,44 +127,69 @@ static void resizeOpArray(Vdbe *p, int N){
**
** op The opcode for this instruction
**
-** p1, p2 First two of the three possible operands.
+** p1, p2, p3 Operands
**
** Use the sqlite3VdbeResolveLabel() function to fix an address and
-** the sqlite3VdbeChangeP3() function to change the value of the P3
+** the sqlite3VdbeChangeP4() function to change the value of the P4
** operand.
*/
-int sqlite3VdbeAddOp(Vdbe *p, int op, int p1, int p2){
+int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
int i;
VdbeOp *pOp;
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
if( p->nOpAlloc<=i ){
- resizeOpArray(p, i+1);
- if( sqlite3MallocFailed() ){
+ resizeOpArray(p, p->nOpAlloc ? p->nOpAlloc*2 : 1024/sizeof(Op));
+ if( p->db->mallocFailed ){
return 0;
}
}
p->nOp++;
pOp = &p->aOp[i];
pOp->opcode = op;
+ pOp->p5 = 0;
pOp->p1 = p1;
pOp->p2 = p2;
- pOp->p3 = 0;
- pOp->p3type = P3_NOTUSED;
+ pOp->p3 = p3;
+ pOp->p4.p = 0;
+ pOp->p4type = P4_NOTUSED;
p->expired = 0;
#ifdef SQLITE_DEBUG
- if( sqlite3_vdbe_addop_trace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+ pOp->zComment = 0;
+ if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+#endif
+#ifdef VDBE_PROFILE
+ pOp->cycles = 0;
+ pOp->cnt = 0;
#endif
return i;
}
+int sqlite3VdbeAddOp0(Vdbe *p, int op){
+ return sqlite3VdbeAddOp3(p, op, 0, 0, 0);
+}
+int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){
+ return sqlite3VdbeAddOp3(p, op, p1, 0, 0);
+}
+int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){
+ return sqlite3VdbeAddOp3(p, op, p1, p2, 0);
+}
+
/*
-** Add an opcode that includes the p3 value.
+** Add an opcode that includes the p4 value as a pointer.
*/
-int sqlite3VdbeOp3(Vdbe *p, int op, int p1, int p2, const char *zP3,int p3type){
- int addr = sqlite3VdbeAddOp(p, op, p1, p2);
- sqlite3VdbeChangeP3(p, addr, zP3, p3type);
+int sqlite3VdbeAddOp4(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ const char *zP4, /* The P4 operand */
+ int p4type /* P4 operand type */
+){
+ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+ sqlite3VdbeChangeP4(p, addr, zP4, p4type);
return addr;
}
@@ -197,7 +213,7 @@ int sqlite3VdbeMakeLabel(Vdbe *p){
assert( p->magic==VDBE_MAGIC_INIT );
if( i>=p->nLabelAlloc ){
p->nLabelAlloc = p->nLabelAlloc*2 + 10;
- p->aLabel = sqliteReallocOrFree(p->aLabel,
+ p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
p->nLabelAlloc*sizeof(p->aLabel[0]));
}
if( p->aLabel ){
@@ -221,51 +237,9 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
}
/*
-** Return non-zero if opcode 'op' is guarenteed not to push more values
-** onto the VDBE stack than it pops off.
-*/
-static int opcodeNoPush(u8 op){
- /* The 10 NOPUSH_MASK_n constants are defined in the automatically
- ** generated header file opcodes.h. Each is a 16-bit bitmask, one
- ** bit corresponding to each opcode implemented by the virtual
- ** machine in vdbe.c. The bit is true if the word "no-push" appears
- ** in a comment on the same line as the "case OP_XXX:" in
- ** sqlite3VdbeExec() in vdbe.c.
- **
- ** If the bit is true, then the corresponding opcode is guarenteed not
- ** to grow the stack when it is executed. Otherwise, it may grow the
- ** stack by at most one entry.
- **
- ** NOPUSH_MASK_0 corresponds to opcodes 0 to 15. NOPUSH_MASK_1 contains
- ** one bit for opcodes 16 to 31, and so on.
- **
- ** 16-bit bitmasks (rather than 32-bit) are specified in opcodes.h
- ** because the file is generated by an awk program. Awk manipulates
- ** all numbers as floating-point and we don't want to risk a rounding
- ** error if someone builds with an awk that uses (for example) 32-bit
- ** IEEE floats.
- */
- static const u32 masks[5] = {
- NOPUSH_MASK_0 + (((unsigned)NOPUSH_MASK_1)<<16),
- NOPUSH_MASK_2 + (((unsigned)NOPUSH_MASK_3)<<16),
- NOPUSH_MASK_4 + (((unsigned)NOPUSH_MASK_5)<<16),
- NOPUSH_MASK_6 + (((unsigned)NOPUSH_MASK_7)<<16),
- NOPUSH_MASK_8 + (((unsigned)NOPUSH_MASK_9)<<16)
- };
- assert( op<32*5 );
- return (masks[op>>5] & (1<<(op&0x1F)));
-}
-
-#ifndef NDEBUG
-int sqlite3VdbeOpcodeNoPush(u8 op){
- return opcodeNoPush(op);
-}
-#endif
-
-/*
-** Loop through the program looking for P2 values that are negative.
-** Each such value is a label. Resolve the label by setting the P2
-** value to its correct non-zero value.
+** Loop through the program looking for P2 values that are negative
+** on jump instructions. Each such value is a label. Resolve the
+** label by setting the P2 value to its correct non-zero value.
**
** This routine is called once after all opcodes have been inserted.
**
@@ -273,19 +247,22 @@ int sqlite3VdbeOpcodeNoPush(u8 op){
** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by
** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
**
-** The integer *pMaxStack is set to the maximum number of vdbe stack
-** entries that static analysis reveals this program might need.
-**
** This routine also does the following optimization: It scans for
-** Halt instructions where P1==SQLITE_CONSTRAINT or P2==OE_Abort or for
-** IdxInsert instructions where P2!=0. If no such instruction is
-** found, then every Statement instruction is changed to a Noop. In
-** this way, we avoid creating the statement journal file unnecessarily.
+** instructions that might cause a statement rollback. Such instructions
+** are:
+**
+** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort.
+** * OP_Destroy
+** * OP_VUpdate
+** * OP_VRename
+**
+** If no such instruction is found, then every Statement instruction
+** is changed to a Noop. In this way, we avoid creating the statement
+** journal file unnecessarily.
*/
-static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
+static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
int nMaxArgs = 0;
- int nMaxStack = p->nOp;
Op *pOp;
int *aLabel = p->aLabel;
int doesStatementRollback = 0;
@@ -293,12 +270,12 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- if( opcode==OP_Function || opcode==OP_AggStep
+ if( opcode==OP_Function || opcode==OP_AggStep ){
+ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- || opcode==OP_VUpdate
-#endif
- ){
+ }else if( opcode==OP_VUpdate ){
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+#endif
}
if( opcode==OP_Halt ){
if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){
@@ -306,30 +283,29 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
}
}else if( opcode==OP_Statement ){
hasStatementBegin = 1;
+ }else if( opcode==OP_Destroy ){
+ doesStatementRollback = 1;
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( opcode==OP_VUpdate || opcode==OP_VRename ){
doesStatementRollback = 1;
}else if( opcode==OP_VFilter ){
int n;
assert( p->nOp - i >= 3 );
- assert( pOp[-2].opcode==OP_Integer );
- n = pOp[-2].p1;
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
#endif
}
- if( opcodeNoPush(opcode) ){
- nMaxStack--;
- }
- if( pOp->p2>=0 ) continue;
- assert( -1-pOp->p2<p->nLabel );
- pOp->p2 = aLabel[-1-pOp->p2];
+ if( sqlite3VdbeOpcodeHasProperty(opcode, OPFLG_JUMP) && pOp->p2<0 ){
+ assert( -1-pOp->p2<p->nLabel );
+ pOp->p2 = aLabel[-1-pOp->p2];
+ }
}
- sqliteFree(p->aLabel);
+ sqlite3DbFree(p->db, p->aLabel);
p->aLabel = 0;
*pMaxFuncArgs = nMaxArgs;
- *pMaxStack = nMaxStack;
/* If we never rollback a statement transaction, then statement
** transactions are not needed. So change every OP_Statement
@@ -360,8 +336,11 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
int addr;
assert( p->magic==VDBE_MAGIC_INIT );
- resizeOpArray(p, p->nOp + nOp);
- if( sqlite3MallocFailed() ){
+ if( p->nOp + nOp > p->nOpAlloc ){
+ resizeOpArray(p, p->nOpAlloc ? p->nOpAlloc*2 : 1024/sizeof(Op));
+ assert( p->nOp+nOp<=p->nOpAlloc || p->db->mallocFailed );
+ }
+ if( p->db->mallocFailed ){
return 0;
}
addr = p->nOp;
@@ -373,11 +352,18 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
VdbeOp *pOut = &p->aOp[i+addr];
pOut->opcode = pIn->opcode;
pOut->p1 = pIn->p1;
- pOut->p2 = p2<0 ? addr + ADDR(p2) : p2;
+ if( p2<0 && sqlite3VdbeOpcodeHasProperty(pOut->opcode, OPFLG_JUMP) ){
+ pOut->p2 = addr + ADDR(p2);
+ }else{
+ pOut->p2 = p2;
+ }
pOut->p3 = pIn->p3;
- pOut->p3type = pIn->p3 ? P3_STATIC : P3_NOTUSED;
+ pOut->p4type = P4_NOTUSED;
+ pOut->p4.p = 0;
+ pOut->p5 = 0;
#ifdef SQLITE_DEBUG
- if( sqlite3_vdbe_addop_trace ){
+ pOut->zComment = 0;
+ if( sqlite3VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
}
#endif
@@ -405,7 +391,6 @@ void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
** This routine is useful for setting a jump destination.
*/
void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
- assert( val>=0 );
assert( p==0 || p->magic==VDBE_MAGIC_INIT );
if( p && addr>=0 && p->nOp>addr && p->aOp ){
p->aOp[addr].p2 = val;
@@ -413,6 +398,28 @@ void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
}
/*
+** Change the value of the P3 operand for a specific instruction.
+*/
+void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
+ assert( p==0 || p->magic==VDBE_MAGIC_INIT );
+ if( p && addr>=0 && p->nOp>addr && p->aOp ){
+ p->aOp[addr].p3 = val;
+ }
+}
+
+/*
+** Change the value of the P5 operand for the most recently
+** added operation.
+*/
+void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
+ assert( p==0 || p->magic==VDBE_MAGIC_INIT );
+ if( p && p->aOp ){
+ assert( p->nOp>0 );
+ p->aOp[p->nOp-1].p5 = val;
+ }
+}
+
+/*
** Change the P2 operand of instruction addr so that it points to
** the address of the next instruction to be coded.
*/
@@ -425,41 +432,41 @@ void sqlite3VdbeJumpHere(Vdbe *p, int addr){
** If the input FuncDef structure is ephemeral, then free it. If
** the FuncDef is not ephermal, then do nothing.
*/
-static void freeEphemeralFunction(FuncDef *pDef){
+static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
if( pDef && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){
- sqliteFree(pDef);
+ sqlite3DbFree(db, pDef);
}
}
/*
-** Delete a P3 value if necessary.
+** Delete a P4 value if necessary.
*/
-static void freeP3(int p3type, void *p3){
- if( p3 ){
- switch( p3type ){
- case P3_DYNAMIC:
- case P3_KEYINFO:
- case P3_KEYINFO_HANDOFF: {
- sqliteFree(p3);
- break;
- }
- case P3_MPRINTF: {
- sqlite3_free(p3);
+static void freeP4(sqlite3 *db, int p4type, void *p4){
+ if( p4 ){
+ switch( p4type ){
+ case P4_REAL:
+ case P4_INT64:
+ case P4_MPRINTF:
+ case P4_DYNAMIC:
+ case P4_KEYINFO:
+ case P4_INTARRAY:
+ case P4_KEYINFO_HANDOFF: {
+ sqlite3DbFree(db, p4);
break;
}
- case P3_VDBEFUNC: {
- VdbeFunc *pVdbeFunc = (VdbeFunc *)p3;
- freeEphemeralFunction(pVdbeFunc->pFunc);
+ case P4_VDBEFUNC: {
+ VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
+ freeEphemeralFunction(db, pVdbeFunc->pFunc);
sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
- sqliteFree(pVdbeFunc);
+ sqlite3DbFree(db, pVdbeFunc);
break;
}
- case P3_FUNCDEF: {
- freeEphemeralFunction((FuncDef*)p3);
+ case P4_FUNCDEF: {
+ freeEphemeralFunction(db, (FuncDef*)p4);
break;
}
- case P3_MEM: {
- sqlite3ValueFree((sqlite3_value*)p3);
+ case P4_MEM: {
+ sqlite3ValueFree((sqlite3_value*)p4);
break;
}
}
@@ -473,8 +480,9 @@ static void freeP3(int p3type, void *p3){
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
if( p && p->aOp ){
VdbeOp *pOp = &p->aOp[addr];
+ sqlite3 *db = p->db;
while( N-- ){
- freeP3(pOp->p3type, pOp->p3);
+ freeP4(db, pOp->p4type, pOp->p4.p);
memset(pOp, 0, sizeof(pOp[0]));
pOp->opcode = OP_Noop;
pOp++;
@@ -483,119 +491,149 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
}
/*
-** Change the value of the P3 operand for a specific instruction.
+** Change the value of the P4 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
** static array using sqlite3VdbeAddOpList but we want to make a
** few minor changes to the program.
**
-** If n>=0 then the P3 operand is dynamic, meaning that a copy of
-** the string is made into memory obtained from sqliteMalloc().
-** A value of n==0 means copy bytes of zP3 up to and including the
-** first null byte. If n>0 then copy n+1 bytes of zP3.
+** If n>=0 then the P4 operand is dynamic, meaning that a copy of
+** the string is made into memory obtained from sqlite3_malloc().
+** A value of n==0 means copy bytes of zP4 up to and including the
+** first null byte. If n>0 then copy n+1 bytes of zP4.
**
-** If n==P3_KEYINFO it means that zP3 is a pointer to a KeyInfo structure.
+** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure.
** A copy is made of the KeyInfo structure into memory obtained from
-** sqliteMalloc, to be freed when the Vdbe is finalized.
-** n==P3_KEYINFO_HANDOFF indicates that zP3 points to a KeyInfo structure
-** stored in memory that the caller has obtained from sqliteMalloc. The
+** sqlite3_malloc, to be freed when the Vdbe is finalized.
+** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure
+** stored in memory that the caller has obtained from sqlite3_malloc. The
** caller should not free the allocation, it will be freed when the Vdbe is
** finalized.
**
-** Other values of n (P3_STATIC, P3_COLLSEQ etc.) indicate that zP3 points
+** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
** to a string or structure that is guaranteed to exist for the lifetime of
** the Vdbe. In these cases we can just copy the pointer.
**
-** If addr<0 then change P3 on the most recently inserted instruction.
+** If addr<0 then change P4 on the most recently inserted instruction.
*/
-void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
+void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
Op *pOp;
- assert( p==0 || p->magic==VDBE_MAGIC_INIT );
- if( p==0 || p->aOp==0 || sqlite3MallocFailed() ){
- if (n != P3_KEYINFO) {
- freeP3(n, (void*)*(char**)&zP3);
+ sqlite3 *db;
+ assert( p!=0 );
+ db = p->db;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( p->aOp==0 || db->mallocFailed ){
+ if (n != P4_KEYINFO) {
+ freeP4(db, n, (void*)*(char**)&zP4);
}
return;
}
- if( addr<0 || addr>=p->nOp ){
+ assert( addr<p->nOp );
+ if( addr<0 ){
addr = p->nOp - 1;
if( addr<0 ) return;
}
pOp = &p->aOp[addr];
- freeP3(pOp->p3type, pOp->p3);
- pOp->p3 = 0;
- if( zP3==0 ){
- pOp->p3 = 0;
- pOp->p3type = P3_NOTUSED;
- }else if( n==P3_KEYINFO ){
+ freeP4(db, pOp->p4type, pOp->p4.p);
+ pOp->p4.p = 0;
+ if( n==P4_INT32 ){
+ /* Note: this cast is safe, because the origin data point was an int
+ ** that was cast to a (const char *). */
+ pOp->p4.i = SQLITE_PTR_TO_INT(zP4);
+ pOp->p4type = n;
+ }else if( zP4==0 ){
+ pOp->p4.p = 0;
+ pOp->p4type = P4_NOTUSED;
+ }else if( n==P4_KEYINFO ){
KeyInfo *pKeyInfo;
int nField, nByte;
- nField = ((KeyInfo*)zP3)->nField;
+ nField = ((KeyInfo*)zP4)->nField;
nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqliteMallocRaw( nByte );
- pOp->p3 = (char*)pKeyInfo;
+ pKeyInfo = sqlite3Malloc( nByte );
+ pOp->p4.pKeyInfo = pKeyInfo;
if( pKeyInfo ){
- unsigned char *aSortOrder;
- memcpy(pKeyInfo, zP3, nByte);
+ u8 *aSortOrder;
+ memcpy(pKeyInfo, zP4, nByte);
aSortOrder = pKeyInfo->aSortOrder;
if( aSortOrder ){
pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
memcpy(pKeyInfo->aSortOrder, aSortOrder, nField);
}
- pOp->p3type = P3_KEYINFO;
+ pOp->p4type = P4_KEYINFO;
}else{
- pOp->p3type = P3_NOTUSED;
+ p->db->mallocFailed = 1;
+ pOp->p4type = P4_NOTUSED;
}
- }else if( n==P3_KEYINFO_HANDOFF ){
- pOp->p3 = (char*)zP3;
- pOp->p3type = P3_KEYINFO;
+ }else if( n==P4_KEYINFO_HANDOFF ){
+ pOp->p4.p = (void*)zP4;
+ pOp->p4type = P4_KEYINFO;
}else if( n<0 ){
- pOp->p3 = (char*)zP3;
- pOp->p3type = n;
+ pOp->p4.p = (void*)zP4;
+ pOp->p4type = n;
}else{
- if( n==0 ) n = strlen(zP3);
- pOp->p3 = sqliteStrNDup(zP3, n);
- pOp->p3type = P3_DYNAMIC;
+ if( n==0 ) n = strlen(zP4);
+ pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
+ pOp->p4type = P4_DYNAMIC;
}
}
#ifndef NDEBUG
/*
-** Replace the P3 field of the most recently coded instruction with
-** comment text.
+** Change the comment on the the most recently coded instruction. Or
+** insert a No-op and add the comment to that new instruction. This
+** makes the code easier to read during debugging. None of this happens
+** in a production build.
*/
void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
assert( p->nOp>0 || p->aOp==0 );
- assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 || sqlite3MallocFailed() );
- va_start(ap, zFormat);
- sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC);
- va_end(ap);
+ assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
+ if( p->nOp ){
+ char **pz = &p->aOp[p->nOp-1].zComment;
+ va_start(ap, zFormat);
+ sqlite3DbFree(p->db, *pz);
+ *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ va_end(ap);
+ }
}
-#endif
+void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ sqlite3VdbeAddOp0(p, OP_Noop);
+ assert( p->nOp>0 || p->aOp==0 );
+ assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
+ if( p->nOp ){
+ char **pz = &p->aOp[p->nOp-1].zComment;
+ va_start(ap, zFormat);
+ sqlite3DbFree(p->db, *pz);
+ *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ va_end(ap);
+ }
+}
+#endif /* NDEBUG */
/*
** Return the opcode for a given address.
*/
VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
assert( p->magic==VDBE_MAGIC_INIT );
- assert( (addr>=0 && addr<p->nOp) || sqlite3MallocFailed() );
+ assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
return ((addr>=0 && addr<p->nOp)?(&p->aOp[addr]):0);
}
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
|| defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
-** Compute a string that describes the P3 parameter for an opcode.
+** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/
-static char *displayP3(Op *pOp, char *zTemp, int nTemp){
- char *zP3;
+static char *displayP4(Op *pOp, char *zTemp, int nTemp){
+ char *zP4 = zTemp;
assert( nTemp>=20 );
- switch( pOp->p3type ){
- case P3_KEYINFO: {
+ switch( pOp->p4type ){
+ case P4_KEYINFO_STATIC:
+ case P4_KEYINFO: {
int i, j;
- KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3;
+ KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField);
i = strlen(zTemp);
for(j=0; j<pKeyInfo->nField; j++){
@@ -620,54 +658,100 @@ static char *displayP3(Op *pOp, char *zTemp, int nTemp){
zTemp[i++] = ')';
zTemp[i] = 0;
assert( i<nTemp );
- zP3 = zTemp;
break;
}
- case P3_COLLSEQ: {
- CollSeq *pColl = (CollSeq*)pOp->p3;
+ case P4_COLLSEQ: {
+ CollSeq *pColl = pOp->p4.pColl;
sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName);
- zP3 = zTemp;
break;
}
- case P3_FUNCDEF: {
- FuncDef *pDef = (FuncDef*)pOp->p3;
+ case P4_FUNCDEF: {
+ FuncDef *pDef = pOp->p4.pFunc;
sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg);
- zP3 = zTemp;
+ break;
+ }
+ case P4_INT64: {
+ sqlite3_snprintf(nTemp, zTemp, "%lld", *pOp->p4.pI64);
+ break;
+ }
+ case P4_INT32: {
+ sqlite3_snprintf(nTemp, zTemp, "%d", pOp->p4.i);
+ break;
+ }
+ case P4_REAL: {
+ sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal);
+ break;
+ }
+ case P4_MEM: {
+ Mem *pMem = pOp->p4.pMem;
+ assert( (pMem->flags & MEM_Null)==0 );
+ if( pMem->flags & MEM_Str ){
+ zP4 = pMem->z;
+ }else if( pMem->flags & MEM_Int ){
+ sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
+ }else if( pMem->flags & MEM_Real ){
+ sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+ }
break;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- case P3_VTAB: {
- sqlite3_vtab *pVtab = (sqlite3_vtab*)pOp->p3;
+ case P4_VTAB: {
+ sqlite3_vtab *pVtab = pOp->p4.pVtab;
sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule);
- zP3 = zTemp;
break;
}
#endif
+ case P4_INTARRAY: {
+ sqlite3_snprintf(nTemp, zTemp, "intarray");
+ break;
+ }
default: {
- zP3 = pOp->p3;
- if( zP3==0 || pOp->opcode==OP_Noop ){
- zP3 = "";
+ zP4 = pOp->p4.z;
+ if( zP4==0 ){
+ zP4 = zTemp;
+ zTemp[0] = 0;
}
}
}
- assert( zP3!=0 );
- return zP3;
+ assert( zP4!=0 );
+ return zP4;
}
#endif
+/*
+** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
+**
+*/
+void sqlite3VdbeUsesBtree(Vdbe *p, int i){
+ int mask;
+ assert( i>=0 && i<p->db->nDb );
+ assert( i<sizeof(p->btreeMask)*8 );
+ mask = 1<<i;
+ if( (p->btreeMask & mask)==0 ){
+ p->btreeMask |= mask;
+ sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt);
+ }
+}
+
#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
** Print a single opcode. This routine is used for debugging only.
*/
void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
- char *zP3;
+ char *zP4;
char zPtr[50];
- static const char *zFormat1 = "%4d %-13s %4d %4d %s\n";
+ static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
if( pOut==0 ) pOut = stdout;
- zP3 = displayP3(pOp, zPtr, sizeof(zPtr));
- fprintf(pOut, zFormat1,
- pc, sqlite3OpcodeNames[pOp->opcode], pOp->p1, pOp->p2, zP3);
+ zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
+ fprintf(pOut, zFormat1, pc,
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
+#ifdef SQLITE_DEBUG
+ pOp->zComment ? pOp->zComment : ""
+#else
+ ""
+#endif
+ );
fflush(pOut);
}
#endif
@@ -676,13 +760,36 @@ void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
** Release an array of N Mem elements
*/
static void releaseMemArray(Mem *p, int N){
- if( p ){
+ if( p && N ){
+ sqlite3 *db = p->db;
+ int malloc_failed = db->mallocFailed;
while( N-->0 ){
- sqlite3VdbeMemRelease(p++);
+ assert( N<2 || p[0].db==p[1].db );
+ sqlite3VdbeMemRelease(p);
+ p->flags = MEM_Null;
+ p++;
}
+ db->mallocFailed = malloc_failed;
}
}
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+int sqlite3VdbeReleaseBuffers(Vdbe *p){
+ int ii;
+ int nFree = 0;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ for(ii=1; ii<=p->nMem; ii++){
+ Mem *pMem = &p->aMem[ii];
+ if( pMem->z && pMem->flags&MEM_Dyn ){
+ assert( !pMem->xDel );
+ nFree += sqlite3DbMallocSize(pMem->db, pMem->z);
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ return nFree;
+}
+#endif
+
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Give a listing of the program in the virtual machine.
@@ -690,6 +797,11 @@ static void releaseMemArray(Mem *p, int N){
** The interface is the same as sqlite3VdbeExec(). But instead of
** running the code, it invokes the callback once for each instruction.
** This feature is used to implement "EXPLAIN".
+**
+** When p->explain==1, each instruction is listed. When
+** p->explain==2, only OP_Explain instructions are listed and these
+** are shown in a different format. p->explain==2 is used to implement
+** EXPLAIN QUERY PLAN.
*/
int sqlite3VdbeList(
Vdbe *p /* The VDBE */
@@ -697,20 +809,18 @@ int sqlite3VdbeList(
sqlite3 *db = p->db;
int i;
int rc = SQLITE_OK;
+ Mem *pMem = p->pResultSet = &p->aMem[1];
assert( p->explain );
if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
assert( db->magic==SQLITE_MAGIC_BUSY );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
- /* Even though this opcode does not put dynamic strings onto the
- ** the stack, they may become dynamic if the user calls
+ /* Even though this opcode does not use dynamic strings for
+ ** the result, result columns may become dynamic if the user calls
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
- if( p->pTos==&p->aStack[4] ){
- releaseMemArray(p->aStack, 5);
- }
- p->resOnStack = 0;
+ releaseMemArray(pMem, p->nMem);
do{
i = p->pc++;
@@ -721,22 +831,24 @@ int sqlite3VdbeList(
}else if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(p->rc), (char*)0);
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
}else{
+ char *z;
Op *pOp = &p->aOp[i];
- Mem *pMem = p->aStack;
- pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
- pMem->u.i = i; /* Program counter */
- pMem++;
-
- pMem->flags = MEM_Static|MEM_Str|MEM_Term;
- pMem->z = (char*)sqlite3OpcodeNames[pOp->opcode]; /* Opcode */
- assert( pMem->z!=0 );
- pMem->n = strlen(pMem->z);
- pMem->type = SQLITE_TEXT;
- pMem->enc = SQLITE_UTF8;
- pMem++;
+ if( p->explain==1 ){
+ pMem->flags = MEM_Int;
+ pMem->type = SQLITE_INTEGER;
+ pMem->u.i = i; /* Program counter */
+ pMem++;
+
+ pMem->flags = MEM_Static|MEM_Str|MEM_Term;
+ pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
+ assert( pMem->z!=0 );
+ pMem->n = strlen(pMem->z);
+ pMem->type = SQLITE_TEXT;
+ pMem->enc = SQLITE_UTF8;
+ pMem++;
+ }
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
@@ -748,17 +860,57 @@ int sqlite3VdbeList(
pMem->type = SQLITE_INTEGER;
pMem++;
- pMem->flags = MEM_Ephem|MEM_Str|MEM_Term; /* P3 */
- pMem->z = displayP3(pOp, pMem->zShort, sizeof(pMem->zShort));
- assert( pMem->z!=0 );
- pMem->n = strlen(pMem->z);
+ if( p->explain==1 ){
+ pMem->flags = MEM_Int;
+ pMem->u.i = pOp->p3; /* P3 */
+ pMem->type = SQLITE_INTEGER;
+ pMem++;
+ }
+
+ if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
+ p->db->mallocFailed = 1;
+ return SQLITE_NOMEM;
+ }
+ pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
+ z = displayP4(pOp, pMem->z, 32);
+ if( z!=pMem->z ){
+ sqlite3VdbeMemSetStr(pMem, z, -1, SQLITE_UTF8, 0);
+ }else{
+ assert( pMem->z!=0 );
+ pMem->n = strlen(pMem->z);
+ pMem->enc = SQLITE_UTF8;
+ }
pMem->type = SQLITE_TEXT;
- pMem->enc = SQLITE_UTF8;
+ pMem++;
- p->nResColumn = 5 - 2*(p->explain-1);
- p->pTos = pMem;
+ if( p->explain==1 ){
+ if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
+ p->db->mallocFailed = 1;
+ return SQLITE_NOMEM;
+ }
+ pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
+ pMem->n = 2;
+ sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
+ pMem->type = SQLITE_TEXT;
+ pMem->enc = SQLITE_UTF8;
+ pMem++;
+
+#ifdef SQLITE_DEBUG
+ if( pOp->zComment ){
+ pMem->flags = MEM_Str|MEM_Term;
+ pMem->z = pOp->zComment;
+ pMem->n = strlen(pMem->z);
+ pMem->enc = SQLITE_UTF8;
+ }else
+#endif
+ {
+ pMem->flags = MEM_Null; /* Comment */
+ pMem->type = SQLITE_NULL;
+ }
+ }
+
+ p->nResColumn = 8 - 5*(p->explain-1);
p->rc = SQLITE_OK;
- p->resOnStack = 1;
rc = SQLITE_ROW;
}
return rc;
@@ -773,9 +925,9 @@ void sqlite3VdbePrintSql(Vdbe *p){
int nOp = p->nOp;
VdbeOp *pOp;
if( nOp<1 ) return;
- pOp = &p->aOp[nOp-1];
- if( pOp->opcode==OP_Noop && pOp->p3!=0 ){
- const char *z = pOp->p3;
+ pOp = &p->aOp[0];
+ if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
+ const char *z = pOp->p4.z;
while( isspace(*(u8*)z) ) z++;
printf("SQL: [%s]\n", z);
}
@@ -789,13 +941,13 @@ void sqlite3VdbePrintSql(Vdbe *p){
void sqlite3VdbeIOTraceSql(Vdbe *p){
int nOp = p->nOp;
VdbeOp *pOp;
- if( sqlite3_io_trace==0 ) return;
+ if( sqlite3IoTrace==0 ) return;
if( nOp<1 ) return;
- pOp = &p->aOp[nOp-1];
- if( pOp->opcode==OP_Noop && pOp->p3!=0 ){
+ pOp = &p->aOp[0];
+ if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
int i, j;
char z[1000];
- sqlite3_snprintf(sizeof(z), z, "%s", pOp->p3);
+ sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z);
for(i=0; isspace((unsigned char)z[i]); i++){}
for(j=0; z[i]; i++){
if( isspace((unsigned char)z[i]) ){
@@ -807,7 +959,7 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
}
}
z[j] = 0;
- sqlite3_io_trace("SQL %s\n", z);
+ sqlite3IoTrace("SQL %s\n", z);
}
}
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
@@ -830,6 +982,7 @@ void sqlite3VdbeMakeReady(
int isExplain /* True if the EXPLAIN keywords is present */
){
int n;
+ sqlite3 *db = p->db;
assert( p!=0 );
assert( p->magic==VDBE_MAGIC_INIT );
@@ -845,37 +998,39 @@ void sqlite3VdbeMakeReady(
*/
p->magic = VDBE_MAGIC_RUN;
- /* No instruction ever pushes more than a single element onto the
- ** stack. And the stack never grows on successive executions of the
- ** same loop. So the total number of instructions is an upper bound
- ** on the maximum stack depth required. (Added later:) The
- ** resolveP2Values() call computes a tighter upper bound on the
- ** stack size.
+ /* For each cursor required, also allocate a memory cell. Memory
+ ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
+ ** the vdbe program. Instead they are used to allocate space for
+ ** Cursor/BtCursor structures. The blob of memory associated with
+ ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
+ ** stores the blob of memory associated with cursor 1, etc.
**
- ** Allocation all the stack space we will ever need.
+ ** See also: allocateCursor().
*/
- if( p->aStack==0 ){
+ nMem += nCursor;
+
+ /*
+ ** Allocation space for registers.
+ */
+ if( p->aMem==0 ){
int nArg; /* Maximum number of args passed to a user function. */
- int nStack; /* Maximum number of stack entries required */
- resolveP2Values(p, &nArg, &nStack);
- resizeOpArray(p, p->nOp);
+ resolveP2Values(p, &nArg);
+ /*resizeOpArray(p, p->nOp);*/
assert( nVar>=0 );
- assert( nStack<p->nOp );
- if( isExplain ){
- nStack = 10;
+ if( isExplain && nMem<10 ){
+ p->nMem = nMem = 10;
}
- p->aStack = sqliteMalloc(
- nStack*sizeof(p->aStack[0]) /* aStack */
- + nArg*sizeof(Mem*) /* apArg */
+ p->aMem = sqlite3DbMallocZero(db,
+ nMem*sizeof(Mem) /* aMem */
+ nVar*sizeof(Mem) /* aVar */
+ + nArg*sizeof(Mem*) /* apArg */
+ nVar*sizeof(char*) /* azVar */
- + nMem*sizeof(Mem) /* aMem */
- + nCursor*sizeof(Cursor*) /* apCsr */
+ + nCursor*sizeof(Cursor*) + 1 /* apCsr */
);
- if( !sqlite3MallocFailed() ){
- p->aMem = &p->aStack[nStack];
- p->nMem = nMem;
- p->aVar = &p->aMem[nMem];
+ if( !db->mallocFailed ){
+ p->aMem--; /* aMem[] goes from 1..nMem */
+ p->nMem = nMem; /* not from 0..nMem-1 */
+ p->aVar = &p->aMem[nMem+1];
p->nVar = nVar;
p->okVar = 0;
p->apArg = (Mem**)&p->aVar[nVar];
@@ -884,20 +1039,24 @@ void sqlite3VdbeMakeReady(
p->nCursor = nCursor;
for(n=0; n<nVar; n++){
p->aVar[n].flags = MEM_Null;
+ p->aVar[n].db = db;
+ }
+ for(n=1; n<=nMem; n++){
+ p->aMem[n].flags = MEM_Null;
+ p->aMem[n].db = db;
}
}
}
- for(n=0; n<p->nMem; n++){
- p->aMem[n].flags = MEM_Null;
+#ifdef SQLITE_DEBUG
+ for(n=1; n<p->nMem; n++){
+ assert( p->aMem[n].db==db );
}
+#endif
- p->pTos = &p->aStack[-1];
p->pc = -1;
p->rc = SQLITE_OK;
p->uniqueCnt = 0;
- p->returnDepth = 0;
p->errorAction = OE_Abort;
- p->popStack = 0;
p->explain |= isExplain;
p->magic = VDBE_MAGIC_RUN;
p->nChange = 0;
@@ -916,44 +1075,47 @@ void sqlite3VdbeMakeReady(
}
/*
-** Close a cursor and release all the resources that cursor happens
-** to hold.
+** Close a VDBE cursor and release all the resources that cursor
+** happens to hold.
*/
void sqlite3VdbeFreeCursor(Vdbe *p, Cursor *pCx){
if( pCx==0 ){
return;
}
- if( pCx->pCursor ){
- sqlite3BtreeCloseCursor(pCx->pCursor);
- }
if( pCx->pBt ){
sqlite3BtreeClose(pCx->pBt);
+ /* The pCx->pCursor will be close automatically, if it exists, by
+ ** the call above. */
+ }else if( pCx->pCursor ){
+ sqlite3BtreeCloseCursor(pCx->pCursor);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pCx->pVtabCursor ){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
const sqlite3_module *pModule = pCx->pModule;
p->inVtabMethod = 1;
- sqlite3SafetyOff(p->db);
+ (void)sqlite3SafetyOff(p->db);
pModule->xClose(pVtabCursor);
- sqlite3SafetyOn(p->db);
+ (void)sqlite3SafetyOn(p->db);
p->inVtabMethod = 0;
}
#endif
- sqliteFree(pCx->pData);
- sqliteFree(pCx->aType);
- sqliteFree(pCx);
+ if( !pCx->ephemPseudoTable ){
+ sqlite3DbFree(p->db, pCx->pData);
+ }
}
/*
-** Close all cursors
+** Close all cursors except for VTab cursors that are currently
+** in use.
*/
-static void closeAllCursors(Vdbe *p){
+static void closeAllCursorsExceptActiveVtabs(Vdbe *p){
int i;
if( p->apCsr==0 ) return;
for(i=0; i<p->nCursor; i++){
- if( !p->inVtabMethod || (p->apCsr[i] && !p->apCsr[i]->pVtabCursor) ){
- sqlite3VdbeFreeCursor(p, p->apCsr[i]);
+ Cursor *pC = p->apCsr[i];
+ if( pC && (!p->inVtabMethod || !pC->pVtabCursor) ){
+ sqlite3VdbeFreeCursor(p, pC);
p->apCsr[i] = 0;
}
}
@@ -968,25 +1130,25 @@ static void closeAllCursors(Vdbe *p){
*/
static void Cleanup(Vdbe *p){
int i;
- if( p->aStack ){
- releaseMemArray(p->aStack, 1 + (p->pTos - p->aStack));
- p->pTos = &p->aStack[-1];
+ sqlite3 *db = p->db;
+ closeAllCursorsExceptActiveVtabs(p);
+ for(i=1; i<=p->nMem; i++){
+ MemSetTypeFlag(&p->aMem[i], MEM_Null);
}
- closeAllCursors(p);
- releaseMemArray(p->aMem, p->nMem);
+ releaseMemArray(&p->aMem[1], p->nMem);
sqlite3VdbeFifoClear(&p->sFifo);
if( p->contextStack ){
for(i=0; i<p->contextStackTop; i++){
sqlite3VdbeFifoClear(&p->contextStack[i].sFifo);
}
- sqliteFree(p->contextStack);
+ sqlite3DbFree(db, p->contextStack);
}
p->contextStack = 0;
p->contextStackDepth = 0;
p->contextStackTop = 0;
- sqliteFree(p->zErrMsg);
+ sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
- p->resOnStack = 0;
+ p->pResultSet = 0;
}
/*
@@ -998,14 +1160,18 @@ static void Cleanup(Vdbe *p){
void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
Mem *pColName;
int n;
+ sqlite3 *db = p->db;
+
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
- sqliteFree(p->aColName);
+ sqlite3DbFree(db, p->aColName);
n = nResColumn*COLNAME_N;
p->nResColumn = nResColumn;
- p->aColName = pColName = (Mem*)sqliteMalloc( sizeof(Mem)*n );
+ p->aColName = pColName = (Mem*)sqlite3DbMallocZero(db, sizeof(Mem)*n );
if( p->aColName==0 ) return;
while( n-- > 0 ){
- (pColName++)->flags = MEM_Null;
+ pColName->flags = MEM_Null;
+ pColName->db = p->db;
+ pColName++;
}
}
@@ -1015,9 +1181,9 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
**
** This call must be made after a call to sqlite3VdbeSetNumCols().
**
-** If N==P3_STATIC it means that zName is a pointer to a constant static
-** string and we can just copy the pointer. If it is P3_DYNAMIC, then
-** the string is freed using sqliteFree() when the vdbe is finished with
+** If N==P4_STATIC it means that zName is a pointer to a constant static
+** string and we can just copy the pointer. If it is P4_DYNAMIC, then
+** the string is freed using sqlite3DbFree(db, ) when the vdbe is finished with
** it. Otherwise, N bytes of zName are copied.
*/
int sqlite3VdbeSetColName(Vdbe *p, int idx, int var, const char *zName, int N){
@@ -1025,17 +1191,17 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, int var, const char *zName, int N){
Mem *pColName;
assert( idx<p->nResColumn );
assert( var<COLNAME_N );
- if( sqlite3MallocFailed() ) return SQLITE_NOMEM;
+ if( p->db->mallocFailed ) return SQLITE_NOMEM;
assert( p->aColName!=0 );
pColName = &(p->aColName[idx+var*p->nResColumn]);
- if( N==P3_DYNAMIC || N==P3_STATIC ){
+ if( N==P4_DYNAMIC || N==P4_STATIC ){
rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, SQLITE_STATIC);
}else{
rc = sqlite3VdbeMemSetStr(pColName, zName, N, SQLITE_UTF8,SQLITE_TRANSIENT);
}
- if( rc==SQLITE_OK && N==P3_DYNAMIC ){
- pColName->flags = (pColName->flags&(~MEM_Static))|MEM_Dyn;
- pColName->xDel = 0;
+ if( rc==SQLITE_OK && N==P4_DYNAMIC ){
+ pColName->flags &= (~MEM_Static);
+ pColName->zMalloc = pColName->z;
}
return rc;
}
@@ -1046,7 +1212,7 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, int var, const char *zName, int N){
** write-transaction spanning more than one database file, this routine
** takes care of the master journal trickery.
*/
-static int vdbeCommit(sqlite3 *db){
+static int vdbeCommit(sqlite3 *db, Vdbe *p){
int i;
int nTrans = 0; /* Number of databases with an active write-transaction */
int rc = SQLITE_OK;
@@ -1058,7 +1224,7 @@ static int vdbeCommit(sqlite3 *db){
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
- rc = sqlite3VtabSync(db, rc);
+ rc = sqlite3VtabSync(db, &p->zErrMsg);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -1071,7 +1237,7 @@ static int vdbeCommit(sqlite3 *db){
*/
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ if( sqlite3BtreeIsInTrans(pBt) ){
needXcommit = 1;
if( i!=1 ) nTrans++;
}
@@ -1079,9 +1245,9 @@ static int vdbeCommit(sqlite3 *db){
/* If there are any write-transactions at all, invoke the commit hook */
if( needXcommit && db->xCommitCallback ){
- sqlite3SafetyOff(db);
+ (void)sqlite3SafetyOff(db);
rc = db->xCommitCallback(db->pCommitArg);
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
if( rc ){
return SQLITE_CONSTRAINT;
}
@@ -1092,9 +1258,9 @@ static int vdbeCommit(sqlite3 *db){
** master-journal.
**
** If the return value of sqlite3BtreeGetFilename() is a zero length
- ** string, it means the main database is :memory:. In that case we do
- ** not support atomic multi-file commits, so use the simple case then
- ** too.
+ ** string, it means the main database is :memory: or a temp file. In
+ ** that case we do not support atomic multi-file commits, so use the
+ ** simple case then too.
*/
if( 0==strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
@@ -1126,26 +1292,34 @@ static int vdbeCommit(sqlite3 *db){
*/
#ifndef SQLITE_OMIT_DISKIO
else{
+ sqlite3_vfs *pVfs = db->pVfs;
int needSync = 0;
char *zMaster = 0; /* File-name for the master journal */
char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
- OsFile *master = 0;
+ sqlite3_file *pMaster = 0;
+ i64 offset = 0;
+ int res;
/* Select a master journal file name */
do {
u32 random;
- sqliteFree(zMaster);
- sqlite3Randomness(sizeof(random), &random);
- zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random&0x7fffffff);
+ sqlite3DbFree(db, zMaster);
+ sqlite3_randomness(sizeof(random), &random);
+ zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, random&0x7fffffff);
if( !zMaster ){
return SQLITE_NOMEM;
}
- }while( sqlite3OsFileExists(zMaster) );
-
- /* Open the master journal. */
- rc = sqlite3OsOpenExclusive(zMaster, &master, 0);
+ rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
+ }while( rc==SQLITE_OK && res );
+ if( rc==SQLITE_OK ){
+ /* Open the master journal. */
+ rc = sqlite3OsOpenMalloc(pVfs, zMaster, &pMaster,
+ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
+ SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
+ );
+ }
if( rc!=SQLITE_OK ){
- sqliteFree(zMaster);
+ sqlite3DbFree(db, zMaster);
return rc;
}
@@ -1155,36 +1329,36 @@ static int vdbeCommit(sqlite3 *db){
** still have 'null' as the master journal pointer, so they will roll
** back independently if a failure occurs.
*/
- for(i=0; i<db->nDb; i++){
+ for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( i==1 ) continue; /* Ignore the TEMP database */
- if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ if( sqlite3BtreeIsInTrans(pBt) ){
char const *zFile = sqlite3BtreeGetJournalname(pBt);
if( zFile[0]==0 ) continue; /* Ignore :memory: databases */
if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
needSync = 1;
}
- rc = sqlite3OsWrite(master, zFile, strlen(zFile)+1);
+ rc = sqlite3OsWrite(pMaster, zFile, strlen(zFile)+1, offset);
+ offset += strlen(zFile)+1;
if( rc!=SQLITE_OK ){
- sqlite3OsClose(&master);
- sqlite3OsDelete(zMaster);
- sqliteFree(zMaster);
+ sqlite3OsCloseFree(pMaster);
+ sqlite3OsDelete(pVfs, zMaster, 0);
+ sqlite3DbFree(db, zMaster);
return rc;
}
}
}
-
- /* Sync the master journal file. Before doing this, open the directory
- ** the master journal file is store in so that it gets synced too.
+ /* Sync the master journal file. If the IOCAP_SEQUENTIAL device
+ ** flag is set this is not required.
*/
zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt);
- rc = sqlite3OsOpenDirectory(master, zMainFile);
- if( rc!=SQLITE_OK ||
- (needSync && (rc=sqlite3OsSync(master,0))!=SQLITE_OK) ){
- sqlite3OsClose(&master);
- sqlite3OsDelete(zMaster);
- sqliteFree(zMaster);
+ if( (needSync
+ && (0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL))
+ && (rc=sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))!=SQLITE_OK) ){
+ sqlite3OsCloseFree(pMaster);
+ sqlite3OsDelete(pVfs, zMaster, 0);
+ sqlite3DbFree(db, zMaster);
return rc;
}
@@ -1200,13 +1374,13 @@ static int vdbeCommit(sqlite3 *db){
*/
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ if( pBt ){
rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
}
}
- sqlite3OsClose(&master);
+ sqlite3OsCloseFree(pMaster);
if( rc!=SQLITE_OK ){
- sqliteFree(zMaster);
+ sqlite3DbFree(db, zMaster);
return rc;
}
@@ -1214,23 +1388,12 @@ static int vdbeCommit(sqlite3 *db){
** doing this the directory is synced again before any individual
** transaction files are deleted.
*/
- rc = sqlite3OsDelete(zMaster);
- sqliteFree(zMaster);
+ rc = sqlite3OsDelete(pVfs, zMaster, 1);
+ sqlite3DbFree(db, zMaster);
zMaster = 0;
if( rc ){
return rc;
}
- rc = sqlite3OsSyncDirectory(zMainFile);
- if( rc!=SQLITE_OK ){
- /* This is not good. The master journal file has been deleted, but
- ** the directory sync failed. There is no completely safe course of
- ** action from here. The individual journals contain the name of the
- ** master journal file, but there is no way of knowing if that
- ** master journal exists now or if it will exist after the operating
- ** system crash that may follow the fsync() failure.
- */
- return rc;
- }
/* All files and directories have already been synced, so the following
** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and
@@ -1240,12 +1403,14 @@ static int vdbeCommit(sqlite3 *db){
** may be lying around. Returning an error code won't help matters.
*/
disable_simulated_io_errors();
+ sqlite3BeginBenignMalloc();
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
sqlite3BtreeCommitPhaseTwo(pBt);
}
}
+ sqlite3EndBenignMalloc();
enable_simulated_io_errors();
sqlite3VtabCommit(db);
@@ -1282,21 +1447,28 @@ static void checkActiveVdbeCnt(sqlite3 *db){
#endif
/*
-** Find every active VM other than pVdbe and change its status to
-** aborted. This happens when one VM causes a rollback due to an
-** ON CONFLICT ROLLBACK clause (for example). The other VMs must be
-** aborted so that they do not have data rolled out from underneath
-** them leading to a segfault.
+** For every Btree that in database connection db which
+** has been modified, "trip" or invalidate each cursor in
+** that Btree might have been modified so that the cursor
+** can never be used again. This happens when a rollback
+*** occurs. We have to trip all the other cursors, even
+** cursor from other VMs in different database connections,
+** so that none of them try to use the data at which they
+** were pointing and which now may have been changed due
+** to the rollback.
+**
+** Remember that a rollback can delete tables complete and
+** reorder rootpages. So it is not sufficient just to save
+** the state of the cursor. We have to invalidate the cursor
+** so that it is never used again.
*/
-void sqlite3AbortOtherActiveVdbes(sqlite3 *db, Vdbe *pExcept){
- Vdbe *pOther;
- for(pOther=db->pVdbe; pOther; pOther=pOther->pNext){
- if( pOther==pExcept ) continue;
- if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue;
- checkActiveVdbeCnt(db);
- closeAllCursors(pOther);
- checkActiveVdbeCnt(db);
- pOther->aborted = 1;
+static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
+ int i;
+ for(i=0; i<db->nDb; i++){
+ Btree *p = db->aDb[i].pBt;
+ if( p && sqlite3BtreeIsInTrans(p) ){
+ sqlite3BtreeTripAllCursors(p, SQLITE_ABORT);
+ }
}
}
@@ -1306,7 +1478,8 @@ void sqlite3AbortOtherActiveVdbes(sqlite3 *db, Vdbe *pExcept){
** changes. If a rollback is needed, then do the rollback.
**
** This routine is the only way to move the state of a VM from
-** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT.
+** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to
+** call this on a VM that is in the SQLITE_MAGIC_HALT state.
**
** Return an error code. If the commit could not complete because of
** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it
@@ -1322,51 +1495,38 @@ int sqlite3VdbeHalt(Vdbe *p){
** transaction will be committed or rolled back as a result of the
** execution of this virtual machine.
**
- ** Special errors:
- **
- ** If an SQLITE_NOMEM error has occured in a statement that writes to
- ** the database, then either a statement or transaction must be rolled
- ** back to ensure the tree-structures are in a consistent state. A
- ** statement transaction is rolled back if one is open, otherwise the
- ** entire transaction must be rolled back.
+ ** If any of the following errors occur:
**
- ** If an SQLITE_IOERR error has occured in a statement that writes to
- ** the database, then the entire transaction must be rolled back. The
- ** I/O error may have caused garbage to be written to the journal
- ** file. Were the transaction to continue and eventually be rolled
- ** back that garbage might end up in the database file.
- **
- ** In both of the above cases, the Vdbe.errorAction variable is
- ** ignored. If the sqlite3.autoCommit flag is false and a transaction
- ** is rolled back, it will be set to true.
- **
- ** Other errors:
- **
- ** No error:
+ ** SQLITE_NOMEM
+ ** SQLITE_IOERR
+ ** SQLITE_FULL
+ ** SQLITE_INTERRUPT
**
+ ** Then the internal cache might have been left in an inconsistent
+ ** state. We need to rollback the statement transaction, if there is
+ ** one, or the complete transaction if there is no statement transaction.
*/
- if( sqlite3MallocFailed() ){
+ if( p->db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
+ closeAllCursorsExceptActiveVtabs(p);
if( p->magic!=VDBE_MAGIC_RUN ){
- /* Already halted. Nothing to do. */
- assert( p->magic==VDBE_MAGIC_HALT );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- closeAllCursors(p);
-#endif
return SQLITE_OK;
}
- closeAllCursors(p);
checkActiveVdbeCnt(db);
/* No commit or rollback needed if the program never started */
if( p->pc>=0 ){
int mrc; /* Primary error code from p->rc */
- /* Check for one of the special errors - SQLITE_NOMEM or SQLITE_IOERR */
+
+ /* Lock all btrees used by the statement */
+ sqlite3BtreeMutexArrayEnter(&p->aMutex);
+
+ /* Check for one of the special errors */
mrc = p->rc & 0xff;
- isSpecialError = (
- (mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT)?1:0);
+ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
+ || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
/* This loop does static analysis of the query to see which of the
** following three categories it falls into:
@@ -1381,46 +1541,35 @@ int sqlite3VdbeHalt(Vdbe *p){
** this is probably easier. Todo: Might be an opportunity to reduce
** code size a very small amount though...
*/
- int isReadOnly = 1;
+ int notReadOnly = 0;
int isStatement = 0;
assert(p->aOp || p->nOp==0);
for(i=0; i<p->nOp; i++){
switch( p->aOp[i].opcode ){
case OP_Transaction:
- /* This is a bit strange. If we hit a malloc() or IO error and
- ** the statement did not open a statement transaction, we will
- ** rollback any active transaction and abort all other active
- ** statements. Or, if this is an SQLITE_INTERRUPT error, we
- ** will only rollback if the interrupted statement was a write.
- **
- ** It could be argued that read-only statements should never
- ** rollback anything. But careful analysis is required before
- ** making this change
- */
- if( p->aOp[i].p2 || mrc!=SQLITE_INTERRUPT ){
- isReadOnly = 0;
- }
+ notReadOnly |= p->aOp[i].p2;
break;
case OP_Statement:
isStatement = 1;
break;
}
}
-
+
+
/* If the query was read-only, we need do no rollback at all. Otherwise,
** proceed with the special handling.
*/
- if( !isReadOnly ){
+ if( notReadOnly || mrc!=SQLITE_INTERRUPT ){
if( p->rc==SQLITE_IOERR_BLOCKED && isStatement ){
xFunc = sqlite3BtreeRollbackStmt;
p->rc = SQLITE_BUSY;
- } else if( p->rc==SQLITE_NOMEM && isStatement ){
+ } else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && isStatement ){
xFunc = sqlite3BtreeRollbackStmt;
}else{
/* We are forced to roll back the active transaction. Before doing
** so, abort any other statements this handle currently has active.
*/
- sqlite3AbortOtherActiveVdbes(db, p);
+ invalidateCursorsOnModifiedBtrees(db);
sqlite3RollbackAll(db);
db->autoCommit = 1;
}
@@ -1439,8 +1588,9 @@ int sqlite3VdbeHalt(Vdbe *p){
** successful or hit an 'OR FAIL' constraint. This means a commit
** is required.
*/
- int rc = vdbeCommit(db);
+ int rc = vdbeCommit(db, p);
if( rc==SQLITE_BUSY ){
+ sqlite3BtreeMutexArrayLeave(&p->aMutex);
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
p->rc = rc;
@@ -1459,7 +1609,7 @@ int sqlite3VdbeHalt(Vdbe *p){
}else if( p->errorAction==OE_Abort ){
xFunc = sqlite3BtreeRollbackStmt;
}else{
- sqlite3AbortOtherActiveVdbes(db, p);
+ invalidateCursorsOnModifiedBtrees(db);
sqlite3RollbackAll(db);
db->autoCommit = 1;
}
@@ -1481,7 +1631,8 @@ int sqlite3VdbeHalt(Vdbe *p){
rc = xFunc(pBt);
if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
p->rc = rc;
- sqlite3SetString(&p->zErrMsg, 0);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
}
}
}
@@ -1503,6 +1654,9 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3ResetInternalSchema(db, 0);
db->flags = (db->flags | SQLITE_InternChanges);
}
+
+ /* Release the locks */
+ sqlite3BtreeMutexArrayLeave(&p->aMutex);
}
/* We have successfully halted and closed the VM. Record this fact. */
@@ -1511,10 +1665,14 @@ int sqlite3VdbeHalt(Vdbe *p){
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
+ if( p->db->mallocFailed ){
+ p->rc = SQLITE_NOMEM;
+ }
return SQLITE_OK;
}
+
/*
** Each VDBE holds the result of the most recent sqlite3_step() call
** in p->rc. This routine sets that result back to SQLITE_OK.
@@ -1542,9 +1700,9 @@ int sqlite3VdbeReset(Vdbe *p){
** error, then it might not have been halted properly. So halt
** it now.
*/
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
sqlite3VdbeHalt(p);
- sqlite3SafetyOff(db);
+ (void)sqlite3SafetyOff(db);
/* If the VDBE has be run even partially, then transfer the error code
** and error message from the VDBE into the main database structure. But
@@ -1553,8 +1711,9 @@ int sqlite3VdbeReset(Vdbe *p){
*/
if( p->pc>=0 ){
if( p->zErrMsg ){
- sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, sqlite3FreeX);
+ sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
db->errCode = p->rc;
+ sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}else if( p->rc ){
sqlite3Error(db, p->rc, 0);
@@ -1567,6 +1726,9 @@ int sqlite3VdbeReset(Vdbe *p){
** called), set the database error in this case as well.
*/
sqlite3Error(db, p->rc, 0);
+ sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
}
/* Reclaim all memory used by the VDBE
@@ -1575,7 +1737,6 @@ int sqlite3VdbeReset(Vdbe *p){
/* Save profiling information from this VDBE run.
*/
- assert( p->pTos<&p->aStack[p->pc<0?0:p->pc] || !p->aStack );
#ifdef VDBE_PROFILE
{
FILE *out = fopen("vdbe_profile.out", "a");
@@ -1599,7 +1760,6 @@ int sqlite3VdbeReset(Vdbe *p){
}
#endif
p->magic = VDBE_MAGIC_INIT;
- p->aborted = 0;
return p->rc & db->errMask;
}
@@ -1643,32 +1803,39 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
*/
void sqlite3VdbeDelete(Vdbe *p){
int i;
+ sqlite3 *db;
+
if( p==0 ) return;
- Cleanup(p);
+ db = p->db;
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
- assert( p->db->pVdbe==p );
- p->db->pVdbe = p->pNext;
+ assert( db->pVdbe==p );
+ db->pVdbe = p->pNext;
}
if( p->pNext ){
p->pNext->pPrev = p->pPrev;
}
if( p->aOp ){
- for(i=0; i<p->nOp; i++){
- Op *pOp = &p->aOp[i];
- freeP3(pOp->p3type, pOp->p3);
+ Op *pOp = p->aOp;
+ for(i=0; i<p->nOp; i++, pOp++){
+ freeP4(db, pOp->p4type, pOp->p4.p);
+#ifdef SQLITE_DEBUG
+ sqlite3DbFree(db, pOp->zComment);
+#endif
}
- sqliteFree(p->aOp);
+ sqlite3DbFree(db, p->aOp);
}
releaseMemArray(p->aVar, p->nVar);
- sqliteFree(p->aLabel);
- sqliteFree(p->aStack);
+ sqlite3DbFree(db, p->aLabel);
+ if( p->aMem ){
+ sqlite3DbFree(db, &p->aMem[1]);
+ }
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
- sqliteFree(p->aColName);
- sqliteFree(p->zSql);
+ sqlite3DbFree(db, p->aColName);
+ sqlite3DbFree(db, p->zSql);
p->magic = VDBE_MAGIC_DEAD;
- sqliteFree(p);
+ sqlite3DbFree(db, p);
}
/*
@@ -1683,7 +1850,7 @@ int sqlite3VdbeCursorMoveto(Cursor *p){
extern int sqlite3_search_count;
#endif
assert( p->isTable );
- rc = sqlite3BtreeMoveto(p->pCursor, 0, p->movetoTarget, 0, &res);
+ rc = sqlite3BtreeMoveto(p->pCursor, 0, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
*p->pIncrKey = 0;
p->lastRowid = keyToInt(p->movetoTarget);
@@ -1697,6 +1864,14 @@ int sqlite3VdbeCursorMoveto(Cursor *p){
#endif
p->deferredMoveto = 0;
p->cacheStatus = CACHE_STALE;
+ }else if( p->pCursor ){
+ int hasMoved;
+ int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved);
+ if( rc ) return rc;
+ if( hasMoved ){
+ p->cacheStatus = CACHE_STALE;
+ p->nullRow = 1;
+ }
}
return SQLITE_OK;
}
@@ -1706,9 +1881,9 @@ int sqlite3VdbeCursorMoveto(Cursor *p){
**
** sqlite3VdbeSerialType()
** sqlite3VdbeSerialTypeLen()
-** sqlite3VdbeSerialRead()
** sqlite3VdbeSerialLen()
-** sqlite3VdbeSerialWrite()
+** sqlite3VdbeSerialPut()
+** sqlite3VdbeSerialGet()
**
** encapsulate the code that serializes values for storage in SQLite
** data and index records. Each serialized value consists of a
@@ -1755,7 +1930,7 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
}
if( flags&MEM_Int ){
/* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
-# define MAX_6BYTE ((((i64)0x00001000)<<32)-1)
+# define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
i64 i = pMem->u.i;
u64 u;
if( file_format>=4 && (i&1)==i ){
@@ -1813,11 +1988,24 @@ int sqlite3VdbeSerialTypeLen(u32 serial_type){
** application using -DSQLITE_DEBUG=1 at least once. With DEBUG
** enabled, some asserts below will ensure that the byte order of
** floating point values is correct.
+**
+** (2007-08-30) Frank van Vugt has studied this problem closely
+** and has send his findings to the SQLite developers. Frank
+** writes that some Linux kernels offer floating point hardware
+** emulation that uses only 32-bit mantissas instead of a full
+** 48-bits as required by the IEEE standard. (This is the
+** CONFIG_FPE_FASTFPE option.) On such systems, floating point
+** byte swapping becomes very complicated. To avoid problems,
+** the necessary byte swapping is carried out using a 64-bit integer
+** rather than a 64-bit float. Frank assures us that the code here
+** works for him. We, the developers, have no way to independently
+** verify this, but Frank seems to know what he is talking about
+** so we trust him.
*/
#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
-static double floatSwap(double in){
+static u64 floatSwap(u64 in){
union {
- double r;
+ u64 r;
u32 i[2];
} u;
u32 t;
@@ -1861,8 +2049,8 @@ int sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
int i;
if( serial_type==7 ){
assert( sizeof(v)==sizeof(pMem->r) );
- swapMixedEndianFloat(pMem->r);
memcpy(&v, &pMem->r, sizeof(v));
+ swapMixedEndianFloat(v);
}else{
v = pMem->u.i;
}
@@ -1952,9 +2140,9 @@ int sqlite3VdbeSerialGet(
*/
static const u64 t1 = ((u64)0x3ff00000)<<32;
static const double r1 = 1.0;
- double r2 = r1;
- swapMixedEndianFloat(r2);
- assert( sizeof(r2)==sizeof(t1) && memcmp(&r2, &t1, sizeof(r1))==0 );
+ u64 t2 = t1;
+ swapMixedEndianFloat(t2);
+ assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
#endif
x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
@@ -1965,9 +2153,9 @@ int sqlite3VdbeSerialGet(
pMem->flags = MEM_Int;
}else{
assert( sizeof(x)==8 && sizeof(pMem->r)==8 );
+ swapMixedEndianFloat(x);
memcpy(&pMem->r, &x, sizeof(x));
- swapMixedEndianFloat(pMem->r);
- pMem->flags = MEM_Real;
+ pMem->flags = sqlite3IsNaN(pMem->r) ? MEM_Null : MEM_Real;
}
return 8;
}
@@ -1993,80 +2181,152 @@ int sqlite3VdbeSerialGet(
return 0;
}
+
/*
-** The header of a record consists of a sequence variable-length integers.
-** These integers are almost always small and are encoded as a single byte.
-** The following macro takes advantage this fact to provide a fast decode
-** of the integers in a record header. It is faster for the common case
-** where the integer is a single byte. It is a little slower when the
-** integer is two or more bytes. But overall it is faster.
-**
-** The following expressions are equivalent:
-**
-** x = sqlite3GetVarint32( A, &B );
+** Given the nKey-byte encoding of a record in pKey[], parse the
+** record into a UnpackedRecord structure. Return a pointer to
+** that structure.
**
-** x = GetVarint( A, B );
+** The calling function might provide szSpace bytes of memory
+** space at pSpace. This space can be used to hold the returned
+** VDbeParsedRecord structure if it is large enough. If it is
+** not big enough, space is obtained from sqlite3_malloc().
**
+** The returned structure should be closed by a call to
+** sqlite3VdbeDeleteUnpackedRecord().
+*/
+UnpackedRecord *sqlite3VdbeRecordUnpack(
+ KeyInfo *pKeyInfo, /* Information about the record format */
+ int nKey, /* Size of the binary record */
+ const void *pKey, /* The binary record */
+ void *pSpace, /* Space available to hold resulting object */
+ int szSpace /* Size of pSpace[] in bytes */
+){
+ const unsigned char *aKey = (const unsigned char *)pKey;
+ UnpackedRecord *p;
+ int nByte;
+ int idx, d;
+ u16 u; /* Unsigned loop counter */
+ u32 szHdr;
+ Mem *pMem;
+
+ assert( sizeof(Mem)>sizeof(*p) );
+ nByte = sizeof(Mem)*(pKeyInfo->nField+2);
+ if( nByte>szSpace ){
+ p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
+ if( p==0 ) return 0;
+ p->needFree = 1;
+ }else{
+ p = pSpace;
+ p->needFree = 0;
+ }
+ p->pKeyInfo = pKeyInfo;
+ p->nField = pKeyInfo->nField + 1;
+ p->needDestroy = 1;
+ p->aMem = pMem = &((Mem*)p)[1];
+ idx = getVarint32(aKey, szHdr);
+ d = szHdr;
+ u = 0;
+ while( idx<szHdr && u<p->nField ){
+ u32 serial_type;
+
+ idx += getVarint32( aKey+idx, serial_type);
+ if( d>=nKey && sqlite3VdbeSerialTypeLen(serial_type)>0 ) break;
+ pMem->enc = pKeyInfo->enc;
+ pMem->db = pKeyInfo->db;
+ pMem->flags = 0;
+ pMem->zMalloc = 0;
+ d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
+ pMem++;
+ u++;
+ }
+ p->nField = u;
+ return (void*)p;
+}
+
+/*
+** This routine destroys a UnpackedRecord object
*/
-#define GetVarint(A,B) ((B = *(A))<=0x7f ? 1 : sqlite3GetVarint32(A, &B))
+void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
+ if( p ){
+ if( p->needDestroy ){
+ int i;
+ Mem *pMem;
+ for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
+ if( pMem->zMalloc ){
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ }
+ if( p->needFree ){
+ sqlite3DbFree(p->pKeyInfo->db, p);
+ }
+ }
+}
/*
-** This function compares the two table rows or index records specified by
-** {nKey1, pKey1} and {nKey2, pKey2}, returning a negative, zero
+** This function compares the two table rows or index records
+** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
** or positive integer if {nKey1, pKey1} is less than, equal to or
-** greater than {nKey2, pKey2}. Both Key1 and Key2 must be byte strings
-** composed by the OP_MakeRecord opcode of the VDBE.
+** greater than pPKey2. The {nKey1, pKey1} key must be a blob
+** created by th OP_MakeRecord opcode of the VDBE. The pPKey2
+** key must be a parsed key such as obtained from
+** sqlite3VdbeParseRecord.
+**
+** Key1 and Key2 do not have to contain the same number of fields.
+** But if the lengths differ, Key2 must be the shorter of the two.
+**
+** Historical note: In earlier versions of this routine both Key1
+** and Key2 were blobs obtained from OP_MakeRecord. But we found
+** that in typical use the same Key2 would be submitted multiple times
+** in a row. So an optimization was added to parse the Key2 key
+** separately and submit the parsed version. In this way, we avoid
+** parsing the same Key2 multiple times in a row.
*/
int sqlite3VdbeRecordCompare(
- void *userData,
int nKey1, const void *pKey1,
- int nKey2, const void *pKey2
+ UnpackedRecord *pPKey2
){
- KeyInfo *pKeyInfo = (KeyInfo*)userData;
- u32 d1, d2; /* Offset into aKey[] of next data element */
- u32 idx1, idx2; /* Offset into aKey[] of next header element */
- u32 szHdr1, szHdr2; /* Number of bytes in header */
+ u32 d1; /* Offset into aKey[] of next data element */
+ u32 idx1; /* Offset into aKey[] of next header element */
+ u32 szHdr1; /* Number of bytes in header */
int i = 0;
int nField;
int rc = 0;
const unsigned char *aKey1 = (const unsigned char *)pKey1;
- const unsigned char *aKey2 = (const unsigned char *)pKey2;
-
+ KeyInfo *pKeyInfo;
Mem mem1;
- Mem mem2;
+
+ pKeyInfo = pPKey2->pKeyInfo;
mem1.enc = pKeyInfo->enc;
- mem2.enc = pKeyInfo->enc;
+ mem1.db = pKeyInfo->db;
+ mem1.flags = 0;
+ mem1.zMalloc = 0;
- idx1 = GetVarint(aKey1, szHdr1);
+ idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
- idx2 = GetVarint(aKey2, szHdr2);
- d2 = szHdr2;
nField = pKeyInfo->nField;
- while( idx1<szHdr1 && idx2<szHdr2 ){
+ while( idx1<szHdr1 && i<pPKey2->nField ){
u32 serial_type1;
- u32 serial_type2;
/* Read the serial types for the next element in each key. */
- idx1 += GetVarint( aKey1+idx1, serial_type1 );
+ idx1 += getVarint32( aKey1+idx1, serial_type1 );
if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
- idx2 += GetVarint( aKey2+idx2, serial_type2 );
- if( d2>=nKey2 && sqlite3VdbeSerialTypeLen(serial_type2)>0 ) break;
/* Extract the values to be compared.
*/
d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1);
- d2 += sqlite3VdbeSerialGet(&aKey2[d2], serial_type2, &mem2);
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &mem2, i<nField ? pKeyInfo->aColl[i] : 0);
- if( mem1.flags & MEM_Dyn ) sqlite3VdbeMemRelease(&mem1);
- if( mem2.flags & MEM_Dyn ) sqlite3VdbeMemRelease(&mem2);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
+ i<nField ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
break;
}
i++;
}
+ if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
/* One of the keys ran out of fields, but all the fields up to that point
** were equal. If the incrKey flag is true, then the second key is
@@ -2075,10 +2335,10 @@ int sqlite3VdbeRecordCompare(
if( rc==0 ){
if( pKeyInfo->incrKey ){
rc = -1;
- }else if( d1<nKey1 ){
- rc = 1;
- }else if( d2<nKey2 ){
- rc = -1;
+ }else if( !pKeyInfo->prefixIsEqual ){
+ if( d1<nKey1 ){
+ rc = 1;
+ }
}
}else if( pKeyInfo->aSortOrder && i<pKeyInfo->nField
&& pKeyInfo->aSortOrder[i] ){
@@ -2094,13 +2354,17 @@ int sqlite3VdbeRecordCompare(
** an integer rowid). This routine returns the number of bytes in
** that integer.
*/
-int sqlite3VdbeIdxRowidLen(const u8 *aKey){
+int sqlite3VdbeIdxRowidLen(const u8 *aKey, int nKey, int *pRowidLen){
u32 szHdr; /* Size of the header */
u32 typeRowid; /* Serial type of the rowid */
- sqlite3GetVarint32(aKey, &szHdr);
- sqlite3GetVarint32(&aKey[szHdr-1], &typeRowid);
- return sqlite3VdbeSerialTypeLen(typeRowid);
+ (void)getVarint32(aKey, szHdr);
+ if( szHdr>nKey ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ (void)getVarint32(&aKey[szHdr-1], typeRowid);
+ *pRowidLen = sqlite3VdbeSerialTypeLen(typeRowid);
+ return SQLITE_OK;
}
@@ -2121,12 +2385,15 @@ int sqlite3VdbeIdxRowid(BtCursor *pCur, i64 *rowid){
if( nCellKey<=0 ){
return SQLITE_CORRUPT_BKPT;
}
+ m.flags = 0;
+ m.db = 0;
+ m.zMalloc = 0;
rc = sqlite3VdbeMemFromBtree(pCur, 0, nCellKey, 1, &m);
if( rc ){
return rc;
}
- sqlite3GetVarint32((u8*)m.z, &szHdr);
- sqlite3GetVarint32((u8*)&m.z[szHdr-1], &typeRowid);
+ (void)getVarint32((u8*)m.z, szHdr);
+ (void)getVarint32((u8*)&m.z[szHdr-1], typeRowid);
lenRowid = sqlite3VdbeSerialTypeLen(typeRowid);
sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v);
*rowid = v.u.i;
@@ -2146,6 +2413,7 @@ int sqlite3VdbeIdxRowid(BtCursor *pCur, i64 *rowid){
*/
int sqlite3VdbeIdxKeyCompare(
Cursor *pC, /* The cursor to compare against */
+ UnpackedRecord *pUnpacked,
int nKey, const u8 *pKey, /* The key to compare */
int *res /* Write the comparison result here */
){
@@ -2154,18 +2422,35 @@ int sqlite3VdbeIdxKeyCompare(
BtCursor *pCur = pC->pCursor;
int lenRowid;
Mem m;
+ UnpackedRecord *pRec;
+ char zSpace[200];
sqlite3BtreeKeySize(pCur, &nCellKey);
if( nCellKey<=0 ){
*res = 0;
return SQLITE_OK;
}
- rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m);
- if( rc ){
+ m.db = 0;
+ m.flags = 0;
+ m.zMalloc = 0;
+ if( (rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m))
+ || (rc = sqlite3VdbeIdxRowidLen((u8*)m.z, m.n, &lenRowid))
+ ){
return rc;
}
- lenRowid = sqlite3VdbeIdxRowidLen((u8*)m.z);
- *res = sqlite3VdbeRecordCompare(pC->pKeyInfo, m.n-lenRowid, m.z, nKey, pKey);
+ if( !pUnpacked ){
+ pRec = sqlite3VdbeRecordUnpack(pC->pKeyInfo, nKey, pKey,
+ zSpace, sizeof(zSpace));
+ }else{
+ pRec = pUnpacked;
+ }
+ if( pRec==0 ){
+ return SQLITE_NOMEM;
+ }
+ *res = sqlite3VdbeRecordCompare(m.n-lenRowid, m.z, pRec);
+ if( !pUnpacked ){
+ sqlite3VdbeDeleteUnpackedRecord(pRec);
+ }
sqlite3VdbeMemRelease(&m);
return SQLITE_OK;
}
@@ -2175,6 +2460,7 @@ int sqlite3VdbeIdxKeyCompare(
** sqlite3_changes() on the database handle 'db'.
*/
void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){
+ assert( sqlite3_mutex_held(db->mutex) );
db->nChange = nChange;
db->nTotalChange += nChange;
}
diff --git a/third_party/sqlite/src/vdbeblob.c b/third_party/sqlite/src/vdbeblob.c
new file mode 100755
index 0000000..746bfe4
--- /dev/null
+++ b/third_party/sqlite/src/vdbeblob.c
@@ -0,0 +1,348 @@
+/*
+** 2007 May 1
+**
+** 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 contains code used to implement incremental BLOB I/O.
+**
+** $Id: vdbeblob.c,v 1.25 2008/07/28 19:34:54 drh Exp $
+*/
+
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
+#ifndef SQLITE_OMIT_INCRBLOB
+
+/*
+** Valid sqlite3_blob* handles point to Incrblob structures.
+*/
+typedef struct Incrblob Incrblob;
+struct Incrblob {
+ int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
+ int nByte; /* Size of open blob, in bytes */
+ int iOffset; /* Byte offset of blob in cursor data */
+ BtCursor *pCsr; /* Cursor pointing at blob row */
+ sqlite3_stmt *pStmt; /* Statement holding cursor open */
+ sqlite3 *db; /* The associated database */
+};
+
+/*
+** Open a blob handle.
+*/
+int sqlite3_blob_open(
+ sqlite3* db, /* The database connection */
+ const char *zDb, /* The attached database containing the blob */
+ const char *zTable, /* The table containing the blob */
+ const char *zColumn, /* The column containing the blob */
+ sqlite_int64 iRow, /* The row containing the glob */
+ int flags, /* True -> read/write access, false -> read-only */
+ sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */
+){
+ int nAttempt = 0;
+ int iCol; /* Index of zColumn in row-record */
+
+ /* This VDBE program seeks a btree cursor to the identified
+ ** db/table/row entry. The reason for using a vdbe program instead
+ ** of writing code to use the b-tree layer directly is that the
+ ** vdbe program will take advantage of the various transaction,
+ ** locking and error handling infrastructure built into the vdbe.
+ **
+ ** After seeking the cursor, the vdbe executes an OP_ResultRow.
+ ** Code external to the Vdbe then "borrows" the b-tree cursor and
+ ** uses it to implement the blob_read(), blob_write() and
+ ** blob_bytes() functions.
+ **
+ ** The sqlite3_blob_close() function finalizes the vdbe program,
+ ** which closes the b-tree cursor and (possibly) commits the
+ ** transaction.
+ */
+ static const VdbeOpList openBlob[] = {
+ {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
+ {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
+
+ /* One of the following two instructions is replaced by an
+ ** OP_Noop before exection.
+ */
+ {OP_SetNumColumns, 0, 0, 0}, /* 2: Num cols for cursor */
+ {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
+ {OP_SetNumColumns, 0, 0, 0}, /* 4: Num cols for cursor */
+ {OP_OpenWrite, 0, 0, 0}, /* 5: Open cursor 0 for read/write */
+
+ {OP_Variable, 1, 1, 0}, /* 6: Push the rowid to the stack */
+ {OP_NotExists, 0, 10, 1}, /* 7: Seek the cursor */
+ {OP_Column, 0, 0, 1}, /* 8 */
+ {OP_ResultRow, 1, 0, 0}, /* 9 */
+ {OP_Close, 0, 0, 0}, /* 10 */
+ {OP_Halt, 0, 0, 0}, /* 11 */
+ };
+
+ Vdbe *v = 0;
+ int rc = SQLITE_OK;
+ char zErr[128];
+
+ zErr[0] = 0;
+ sqlite3_mutex_enter(db->mutex);
+ do {
+ Parse sParse;
+ Table *pTab;
+
+ memset(&sParse, 0, sizeof(Parse));
+ sParse.db = db;
+
+ if( sqlite3SafetyOn(db) ){
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_MISUSE;
+ }
+
+ sqlite3BtreeEnterAll(db);
+ pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
+ if( pTab && IsVirtual(pTab) ){
+ pTab = 0;
+ sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
+ }
+#ifndef SQLITE_OMIT_VIEW
+ if( pTab && pTab->pSelect ){
+ pTab = 0;
+ sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
+ }
+#endif
+ if( !pTab ){
+ if( sParse.zErrMsg ){
+ sqlite3_snprintf(sizeof(zErr), zErr, "%s", sParse.zErrMsg);
+ }
+ sqlite3DbFree(db, sParse.zErrMsg);
+ rc = SQLITE_ERROR;
+ (void)sqlite3SafetyOff(db);
+ sqlite3BtreeLeaveAll(db);
+ goto blob_open_out;
+ }
+
+ /* Now search pTab for the exact column. */
+ for(iCol=0; iCol < pTab->nCol; iCol++) {
+ if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
+ break;
+ }
+ }
+ if( iCol==pTab->nCol ){
+ sqlite3_snprintf(sizeof(zErr), zErr, "no such column: \"%s\"", zColumn);
+ rc = SQLITE_ERROR;
+ (void)sqlite3SafetyOff(db);
+ sqlite3BtreeLeaveAll(db);
+ goto blob_open_out;
+ }
+
+ /* If the value is being opened for writing, check that the
+ ** column is not indexed. It is against the rules to open an
+ ** indexed column for writing.
+ */
+ if( flags ){
+ Index *pIdx;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int j;
+ for(j=0; j<pIdx->nColumn; j++){
+ if( pIdx->aiColumn[j]==iCol ){
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "cannot open indexed column for writing");
+ rc = SQLITE_ERROR;
+ (void)sqlite3SafetyOff(db);
+ sqlite3BtreeLeaveAll(db);
+ goto blob_open_out;
+ }
+ }
+ }
+ }
+
+ v = sqlite3VdbeCreate(db);
+ if( v ){
+ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
+
+ /* Configure the OP_Transaction */
+ sqlite3VdbeChangeP1(v, 0, iDb);
+ sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0));
+
+ /* Configure the OP_VerifyCookie */
+ sqlite3VdbeChangeP1(v, 1, iDb);
+ sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
+
+ /* Make sure a mutex is held on the table to be accessed */
+ sqlite3VdbeUsesBtree(v, iDb);
+
+ /* Remove either the OP_OpenWrite or OpenRead. Set the P2
+ ** parameter of the other to pTab->tnum.
+ */
+ sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1);
+ sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum);
+ sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb);
+
+ /* Configure the OP_SetNumColumns. Configure the cursor to
+ ** think that the table has one more column than it really
+ ** does. An OP_Column to retrieve this imaginary column will
+ ** always return an SQL NULL. This is useful because it means
+ ** we can invoke OP_Column to fill in the vdbe cursors type
+ ** and offset cache without causing any IO.
+ */
+ sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1);
+ sqlite3VdbeChangeP2(v, 8, pTab->nCol);
+ if( !db->mallocFailed ){
+ sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
+ }
+ }
+
+ sqlite3BtreeLeaveAll(db);
+ rc = sqlite3SafetyOff(db);
+ if( rc!=SQLITE_OK || db->mallocFailed ){
+ goto blob_open_out;
+ }
+
+ sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
+ rc = sqlite3_step((sqlite3_stmt *)v);
+ if( rc!=SQLITE_ROW ){
+ nAttempt++;
+ rc = sqlite3_finalize((sqlite3_stmt *)v);
+ sqlite3_snprintf(sizeof(zErr), zErr, sqlite3_errmsg(db));
+ v = 0;
+ }
+ } while( nAttempt<5 && rc==SQLITE_SCHEMA );
+
+ if( rc==SQLITE_ROW ){
+ /* The row-record has been opened successfully. Check that the
+ ** column in question contains text or a blob. If it contains
+ ** text, it is up to the caller to get the encoding right.
+ */
+ Incrblob *pBlob;
+ u32 type = v->apCsr[0]->aType[iCol];
+
+ if( type<12 ){
+ sqlite3_snprintf(sizeof(zErr), zErr, "cannot open value of type %s",
+ type==0?"null": type==7?"real": "integer"
+ );
+ rc = SQLITE_ERROR;
+ goto blob_open_out;
+ }
+ pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
+ if( db->mallocFailed ){
+ sqlite3DbFree(db, pBlob);
+ goto blob_open_out;
+ }
+ pBlob->flags = flags;
+ pBlob->pCsr = v->apCsr[0]->pCursor;
+ sqlite3BtreeEnterCursor(pBlob->pCsr);
+ sqlite3BtreeCacheOverflow(pBlob->pCsr);
+ sqlite3BtreeLeaveCursor(pBlob->pCsr);
+ pBlob->pStmt = (sqlite3_stmt *)v;
+ pBlob->iOffset = v->apCsr[0]->aOffset[iCol];
+ pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
+ pBlob->db = db;
+ *ppBlob = (sqlite3_blob *)pBlob;
+ rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK ){
+ sqlite3_snprintf(sizeof(zErr), zErr, "no such rowid: %lld", iRow);
+ rc = SQLITE_ERROR;
+ }
+
+blob_open_out:
+ zErr[sizeof(zErr)-1] = '\0';
+ if( rc!=SQLITE_OK || db->mallocFailed ){
+ sqlite3_finalize((sqlite3_stmt *)v);
+ }
+ sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
+/*
+** Close a blob handle that was previously created using
+** sqlite3_blob_open().
+*/
+int sqlite3_blob_close(sqlite3_blob *pBlob){
+ Incrblob *p = (Incrblob *)pBlob;
+ int rc;
+
+ rc = sqlite3_finalize(p->pStmt);
+ sqlite3DbFree(p->db, p);
+ return rc;
+}
+
+/*
+** Perform a read or write operation on a blob
+*/
+static int blobReadWrite(
+ sqlite3_blob *pBlob,
+ void *z,
+ int n,
+ int iOffset,
+ int (*xCall)(BtCursor*, u32, u32, void*)
+){
+ int rc;
+ Incrblob *p = (Incrblob *)pBlob;
+ Vdbe *v;
+ sqlite3 *db = p->db;
+
+ /* Request is out of range. Return a transient error. */
+ if( (iOffset+n)>p->nByte ){
+ return SQLITE_ERROR;
+ }
+ sqlite3_mutex_enter(db->mutex);
+
+ /* If there is no statement handle, then the blob-handle has
+ ** already been invalidated. Return SQLITE_ABORT in this case.
+ */
+ v = (Vdbe*)p->pStmt;
+ if( v==0 ){
+ rc = SQLITE_ABORT;
+ }else{
+ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
+ ** returned, clean-up the statement handle.
+ */
+ assert( db == v->db );
+ sqlite3BtreeEnterCursor(p->pCsr);
+ rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
+ sqlite3BtreeLeaveCursor(p->pCsr);
+ if( rc==SQLITE_ABORT ){
+ sqlite3VdbeFinalize(v);
+ p->pStmt = 0;
+ }else{
+ db->errCode = rc;
+ v->rc = rc;
+ }
+ }
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
+/*
+** Read data from a blob handle.
+*/
+int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
+ return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData);
+}
+
+/*
+** Write data to a blob handle.
+*/
+int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
+ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);
+}
+
+/*
+** Query a blob handle for the size of the data.
+**
+** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
+** so no mutex is required for access.
+*/
+int sqlite3_blob_bytes(sqlite3_blob *pBlob){
+ Incrblob *p = (Incrblob *)pBlob;
+ return p->nByte;
+}
+
+#endif /* #ifndef SQLITE_OMIT_INCRBLOB */
diff --git a/third_party/sqlite/vdbefifo.c b/third_party/sqlite/src/vdbefifo.c
index a7c419a..a5e270d 100644..100755
--- a/third_party/sqlite/vdbefifo.c
+++ b/third_party/sqlite/src/vdbefifo.c
@@ -11,20 +11,34 @@
*************************************************************************
** This file implements a FIFO queue of rowids used for processing
** UPDATE and DELETE statements.
+**
+** $Id: vdbefifo.c,v 1.8 2008/07/28 19:34:54 drh Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
/*
+** Constants FIFOSIZE_FIRST and FIFOSIZE_MAX are the initial
+** number of entries in a fifo page and the maximum number of
+** entries in a fifo page.
+*/
+#define FIFOSIZE_FIRST (((128-sizeof(FifoPage))/8)+1)
+#ifdef SQLITE_MALLOC_SOFT_LIMIT
+# define FIFOSIZE_MAX (((SQLITE_MALLOC_SOFT_LIMIT-sizeof(FifoPage))/8)+1)
+#else
+# define FIFOSIZE_MAX (((262144-sizeof(FifoPage))/8)+1)
+#endif
+
+/*
** Allocate a new FifoPage and return a pointer to it. Return NULL if
** we run out of memory. Leave space on the page for nEntry entries.
*/
-static FifoPage *allocateFifoPage(int nEntry){
+static FifoPage *allocateFifoPage(sqlite3 *db, int nEntry){
FifoPage *pPage;
- if( nEntry>32767 ){
- nEntry = 32767;
+ if( nEntry>FIFOSIZE_MAX ){
+ nEntry = FIFOSIZE_MAX;
}
- pPage = sqliteMallocRaw( sizeof(FifoPage) + sizeof(i64)*(nEntry-1) );
+ pPage = sqlite3DbMallocRaw(db, sizeof(FifoPage) + sizeof(i64)*(nEntry-1) );
if( pPage ){
pPage->nSlot = nEntry;
pPage->iWrite = 0;
@@ -37,8 +51,9 @@ static FifoPage *allocateFifoPage(int nEntry){
/*
** Initialize a Fifo structure.
*/
-void sqlite3VdbeFifoInit(Fifo *pFifo){
+void sqlite3VdbeFifoInit(Fifo *pFifo, sqlite3 *db){
memset(pFifo, 0, sizeof(*pFifo));
+ pFifo->db = db;
}
/*
@@ -50,12 +65,13 @@ int sqlite3VdbeFifoPush(Fifo *pFifo, i64 val){
FifoPage *pPage;
pPage = pFifo->pLast;
if( pPage==0 ){
- pPage = pFifo->pLast = pFifo->pFirst = allocateFifoPage(20);
+ pPage = pFifo->pLast = pFifo->pFirst =
+ allocateFifoPage(pFifo->db, FIFOSIZE_FIRST);
if( pPage==0 ){
return SQLITE_NOMEM;
}
}else if( pPage->iWrite>=pPage->nSlot ){
- pPage->pNext = allocateFifoPage(pFifo->nEntry);
+ pPage->pNext = allocateFifoPage(pFifo->db, pFifo->nEntry);
if( pPage->pNext==0 ){
return SQLITE_NOMEM;
}
@@ -87,7 +103,7 @@ int sqlite3VdbeFifoPop(Fifo *pFifo, i64 *pVal){
pFifo->nEntry--;
if( pPage->iRead>=pPage->iWrite ){
pFifo->pFirst = pPage->pNext;
- sqliteFree(pPage);
+ sqlite3DbFree(pFifo->db, pPage);
if( pFifo->nEntry==0 ){
assert( pFifo->pLast==pPage );
pFifo->pLast = 0;
@@ -108,7 +124,7 @@ void sqlite3VdbeFifoClear(Fifo *pFifo){
FifoPage *pPage, *pNextPage;
for(pPage=pFifo->pFirst; pPage; pPage=pNextPage){
pNextPage = pPage->pNext;
- sqliteFree(pPage);
+ sqlite3DbFree(pFifo->db, pPage);
}
- sqlite3VdbeFifoInit(pFifo);
+ sqlite3VdbeFifoInit(pFifo, pFifo->db);
}
diff --git a/third_party/sqlite/vdbemem.c b/third_party/sqlite/src/vdbemem.c
index f05e05cc..8b94e3e 100644..100755
--- a/third_party/sqlite/vdbemem.c
+++ b/third_party/sqlite/src/vdbemem.c
@@ -14,10 +14,10 @@
** stores a single value in the VDBE. Mem is an opaque structure visible
** only within the VDBE. Interface routines refer to a Mem using the
** name sqlite_value
+**
+** $Id: vdbemem.c,v 1.121 2008/08/01 20:10:09 drh Exp $
*/
#include "sqliteInt.h"
-#include "os.h"
-#include <math.h>
#include <ctype.h>
#include "vdbeInt.h"
@@ -45,11 +45,11 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
return SQLITE_OK;
}
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
#ifdef SQLITE_OMIT_UTF16
return SQLITE_ERROR;
#else
-
/* MemTranslate() may return SQLITE_OK or SQLITE_NOMEM. If NOMEM is returned,
** then the encoding of the value may not have changed.
*/
@@ -62,31 +62,75 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
}
/*
-** Make the given Mem object MEM_Dyn.
+** Make sure pMem->z points to a writable allocation of at least
+** n bytes.
**
-** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
+** If the memory cell currently contains string or blob data
+** and the third argument passed to this function is true, the
+** current content of the cell is preserved. Otherwise, it may
+** be discarded.
+**
+** This function sets the MEM_Dyn flag and clears any xDel callback.
+** It also clears MEM_Ephem and MEM_Static. If the preserve flag is
+** not set, Mem.n is zeroed.
*/
-int sqlite3VdbeMemDynamicify(Mem *pMem){
- int n;
- u8 *z;
- expandBlob(pMem);
- if( (pMem->flags & (MEM_Ephem|MEM_Static|MEM_Short))==0 ){
- return SQLITE_OK;
+int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){
+ assert( 1 >=
+ ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) +
+ (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) +
+ ((pMem->flags&MEM_Ephem) ? 1 : 0) +
+ ((pMem->flags&MEM_Static) ? 1 : 0)
+ );
+
+ if( n<32 ) n = 32;
+ if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)<n ){
+ if( preserve && pMem->z==pMem->zMalloc ){
+ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ if( !pMem->z ){
+ pMem->flags = MEM_Null;
+ }
+ preserve = 0;
+ }else{
+ sqlite3DbFree(pMem->db, pMem->zMalloc);
+ pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n);
+ }
}
- assert( (pMem->flags & MEM_Dyn)==0 );
- n = pMem->n;
- assert( pMem->flags & (MEM_Str|MEM_Blob) );
- z = sqliteMallocRaw( n+2 );
- if( z==0 ){
- return SQLITE_NOMEM;
+
+ if( preserve && pMem->z && pMem->zMalloc && pMem->z!=pMem->zMalloc ){
+ memcpy(pMem->zMalloc, pMem->z, pMem->n);
+ }
+ if( pMem->flags&MEM_Dyn && pMem->xDel ){
+ pMem->xDel((void *)(pMem->z));
}
- pMem->flags |= MEM_Dyn|MEM_Term;
+
+ pMem->z = pMem->zMalloc;
+ pMem->flags &= ~(MEM_Ephem|MEM_Static);
pMem->xDel = 0;
- memcpy(z, pMem->z, n );
- z[n] = 0;
- z[n+1] = 0;
- pMem->z = (char*)z;
- pMem->flags &= ~(MEM_Ephem|MEM_Static|MEM_Short);
+ return (pMem->z ? SQLITE_OK : SQLITE_NOMEM);
+}
+
+/*
+** Make the given Mem object MEM_Dyn. In other words, make it so
+** that any TEXT or BLOB content is stored in memory obtained from
+** malloc(). In this way, we know that the memory is safe to be
+** overwritten or altered.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
+*/
+int sqlite3VdbeMemMakeWriteable(Mem *pMem){
+ int f;
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ expandBlob(pMem);
+ f = pMem->flags;
+ if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){
+ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
+ return SQLITE_NOMEM;
+ }
+ pMem->z[pMem->n] = 0;
+ pMem->z[pMem->n+1] = 0;
+ pMem->flags |= MEM_Term;
+ }
+
return SQLITE_OK;
}
@@ -97,23 +141,22 @@ int sqlite3VdbeMemDynamicify(Mem *pMem){
#ifndef SQLITE_OMIT_INCRBLOB
int sqlite3VdbeMemExpandBlob(Mem *pMem){
if( pMem->flags & MEM_Zero ){
- char *pNew;
int nByte;
- assert( (pMem->flags & MEM_Blob)!=0 );
+ assert( pMem->flags&MEM_Blob );
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+
+ /* Set nByte to the number of bytes required to store the expanded blob. */
nByte = pMem->n + pMem->u.i;
- if( nByte<=0 ) nByte = 1;
- pNew = sqliteMalloc(nByte);
- if( pNew==0 ){
+ if( nByte<=0 ){
+ nByte = 1;
+ }
+ if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
return SQLITE_NOMEM;
}
- memcpy(pNew, pMem->z, pMem->n);
- memset(&pNew[pMem->n], 0, pMem->u.i);
- sqlite3VdbeMemRelease(pMem);
- pMem->z = pNew;
+
+ memset(&pMem->z[pMem->n], 0, pMem->u.i);
pMem->n += pMem->u.i;
- pMem->u.i = 0;
- pMem->flags &= ~(MEM_Zero|MEM_Static|MEM_Ephem|MEM_Short|MEM_Term);
- pMem->flags |= MEM_Dyn;
+ pMem->flags &= ~(MEM_Zero|MEM_Term);
}
return SQLITE_OK;
}
@@ -121,67 +164,19 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){
/*
-** Make the given Mem object either MEM_Short or MEM_Dyn so that bytes
-** of the Mem.z[] array can be modified.
-**
-** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
-*/
-int sqlite3VdbeMemMakeWriteable(Mem *pMem){
- int n;
- u8 *z;
- expandBlob(pMem);
- if( (pMem->flags & (MEM_Ephem|MEM_Static))==0 ){
- return SQLITE_OK;
- }
- assert( (pMem->flags & MEM_Dyn)==0 );
- assert( pMem->flags & (MEM_Str|MEM_Blob) );
- if( (n = pMem->n)+2<sizeof(pMem->zShort) ){
- z = (u8*)pMem->zShort;
- pMem->flags |= MEM_Short|MEM_Term;
- }else{
- z = sqliteMallocRaw( n+2 );
- if( z==0 ){
- return SQLITE_NOMEM;
- }
- pMem->flags |= MEM_Dyn|MEM_Term;
- pMem->xDel = 0;
- }
- memcpy(z, pMem->z, n );
- z[n] = 0;
- z[n+1] = 0;
- pMem->z = (char*)z;
- pMem->flags &= ~(MEM_Ephem|MEM_Static);
- assert(0==(1&(int)pMem->z));
- return SQLITE_OK;
-}
-
-/*
** Make sure the given Mem is \u0000 terminated.
*/
int sqlite3VdbeMemNulTerminate(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){
return SQLITE_OK; /* Nothing to do */
}
- if( pMem->flags & (MEM_Static|MEM_Ephem) ){
- return sqlite3VdbeMemMakeWriteable(pMem);
- }else{
- char *z;
- sqlite3VdbeMemExpandBlob(pMem);
- z = sqliteMalloc(pMem->n+2);
-
- if( !z ) return SQLITE_NOMEM;
- memcpy(z, pMem->z, pMem->n);
- z[pMem->n] = 0;
- z[pMem->n+1] = 0;
- if( pMem->xDel ){
- pMem->xDel(pMem->z);
- }else{
- sqliteFree(pMem->z);
- }
- pMem->xDel = 0;
- pMem->z = z;
- pMem->flags |= MEM_Term;
+ if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
+ return SQLITE_NOMEM;
}
+ pMem->z[pMem->n] = 0;
+ pMem->z[pMem->n+1] = 0;
+ pMem->flags |= MEM_Term;
return SQLITE_OK;
}
@@ -201,28 +196,32 @@ int sqlite3VdbeMemNulTerminate(Mem *pMem){
int sqlite3VdbeMemStringify(Mem *pMem, int enc){
int rc = SQLITE_OK;
int fg = pMem->flags;
- char *z = pMem->zShort;
+ const int nByte = 32;
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !(fg&MEM_Zero) );
assert( !(fg&(MEM_Str|MEM_Blob)) );
assert( fg&(MEM_Int|MEM_Real) );
- /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
+ if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ return SQLITE_NOMEM;
+ }
+
+ /* For a Real or Integer, use sqlite3_mprintf() to produce the UTF-8
** string representation of the value. Then, if the required encoding
** is UTF-16le or UTF-16be do a translation.
**
** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
*/
if( fg & MEM_Int ){
- sqlite3_snprintf(NBFS, z, "%lld", pMem->u.i);
+ sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
}else{
assert( fg & MEM_Real );
- sqlite3_snprintf(NBFS, z, "%!.15g", pMem->r);
+ sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->r);
}
- pMem->n = strlen(z);
- pMem->z = z;
+ pMem->n = strlen(pMem->z);
pMem->enc = SQLITE_UTF8;
- pMem->flags |= MEM_Str | MEM_Short | MEM_Term;
+ pMem->flags |= MEM_Str|MEM_Term;
sqlite3VdbeChangeEncoding(pMem, enc);
return rc;
}
@@ -240,46 +239,81 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
if( pFunc && pFunc->xFinalize ){
sqlite3_context ctx;
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
ctx.s.flags = MEM_Null;
- ctx.s.z = pMem->zShort;
+ ctx.s.db = pMem->db;
+ ctx.s.zMalloc = 0;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
ctx.isError = 0;
pFunc->xFinalize(&ctx);
- if( pMem->z && pMem->z!=pMem->zShort ){
- sqliteFree( pMem->z );
- }
+ assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
+ sqlite3DbFree(pMem->db, pMem->zMalloc);
*pMem = ctx.s;
- if( pMem->flags & MEM_Short ){
- pMem->z = pMem->zShort;
- }
- if( ctx.isError ){
- rc = SQLITE_ERROR;
- }
+ rc = (ctx.isError?SQLITE_ERROR:SQLITE_OK);
}
return rc;
}
/*
+** If the memory cell contains a string value that must be freed by
+** invoking an external callback, free it now. Calling this function
+** does not free any Mem.zMalloc buffer.
+*/
+void sqlite3VdbeMemReleaseExternal(Mem *p){
+ assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
+ if( p->flags&MEM_Agg ){
+ sqlite3VdbeMemFinalize(p, p->u.pDef);
+ assert( (p->flags & MEM_Agg)==0 );
+ sqlite3VdbeMemRelease(p);
+ }else if( p->flags&MEM_Dyn && p->xDel ){
+ p->xDel((void *)p->z);
+ p->xDel = 0;
+ }
+}
+
+/*
** Release any memory held by the Mem. This may leave the Mem in an
** inconsistent state, for example with (Mem.z==0) and
** (Mem.type==SQLITE_TEXT).
*/
void sqlite3VdbeMemRelease(Mem *p){
- if( p->flags & (MEM_Dyn|MEM_Agg) ){
- if( p->xDel ){
- if( p->flags & MEM_Agg ){
- sqlite3VdbeMemFinalize(p, p->u.pDef);
- assert( (p->flags & MEM_Agg)==0 );
- sqlite3VdbeMemRelease(p);
- }else{
- p->xDel((void *)p->z);
- }
- }else{
- sqliteFree(p->z);
- }
- p->z = 0;
- p->xDel = 0;
+ sqlite3VdbeMemReleaseExternal(p);
+ sqlite3DbFree(p->db, p->zMalloc);
+ p->z = 0;
+ p->zMalloc = 0;
+ p->xDel = 0;
+}
+
+/*
+** Convert a 64-bit IEEE double into a 64-bit signed integer.
+** If the double is too large, return 0x8000000000000000.
+**
+** Most systems appear to do this simply by assigning
+** variables and without the extra range tests. But
+** there are reports that windows throws an expection
+** if the floating point value is out of range. (See ticket #2880.)
+** Because we do not completely understand the problem, we will
+** take the conservative approach and always do range tests
+** before attempting the conversion.
+*/
+static i64 doubleToInt64(double r){
+ /*
+ ** Many compilers we encounter do not define constants for the
+ ** minimum and maximum 64-bit integers, or they define them
+ ** inconsistently. And many do not understand the "LL" notation.
+ ** So we define our own static constants here using nothing
+ ** larger than a 32-bit integer constant.
+ */
+ static const i64 maxInt = LARGEST_INT64;
+ static const i64 minInt = SMALLEST_INT64;
+
+ if( r<(double)minInt ){
+ return minInt;
+ }else if( r>(double)maxInt ){
+ return minInt;
+ }else{
+ return (i64)r;
}
}
@@ -294,11 +328,13 @@ void sqlite3VdbeMemRelease(Mem *p){
** If pMem is a string, its encoding might be changed.
*/
i64 sqlite3VdbeIntValue(Mem *pMem){
- int flags = pMem->flags;
+ int flags;
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ flags = pMem->flags;
if( flags & MEM_Int ){
return pMem->u.i;
}else if( flags & MEM_Real ){
- return (i64)pMem->r;
+ return doubleToInt64(pMem->r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value;
pMem->flags |= MEM_Str;
@@ -321,6 +357,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){
** If it is a NULL, return 0.0.
*/
double sqlite3VdbeRealValue(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
if( pMem->flags & MEM_Real ){
return pMem->r;
}else if( pMem->flags & MEM_Int ){
@@ -346,19 +383,25 @@ double sqlite3VdbeRealValue(Mem *pMem){
*/
void sqlite3VdbeIntegerAffinity(Mem *pMem){
assert( pMem->flags & MEM_Real );
- pMem->u.i = pMem->r;
- if( ((double)pMem->u.i)==pMem->r ){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+
+ pMem->u.i = doubleToInt64(pMem->r);
+ if( pMem->r==(double)pMem->u.i ){
pMem->flags |= MEM_Int;
}
}
+static void setTypeFlag(Mem *pMem, int f){
+ MemSetTypeFlag(pMem, f);
+}
+
/*
** Convert pMem to type integer. Invalidate any prior representations.
*/
int sqlite3VdbeMemIntegerify(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
pMem->u.i = sqlite3VdbeIntValue(pMem);
- sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Int;
+ setTypeFlag(pMem, MEM_Int);
return SQLITE_OK;
}
@@ -367,9 +410,9 @@ int sqlite3VdbeMemIntegerify(Mem *pMem){
** Invalidate any prior representations.
*/
int sqlite3VdbeMemRealify(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
pMem->r = sqlite3VdbeRealValue(pMem);
- sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Real;
+ setTypeFlag(pMem, MEM_Real);
return SQLITE_OK;
}
@@ -382,15 +425,15 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
i64 i;
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 );
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
r1 = sqlite3VdbeRealValue(pMem);
- i = (i64)r1;
+ i = doubleToInt64(r1);
r2 = (double)i;
if( r1==r2 ){
sqlite3VdbeMemIntegerify(pMem);
}else{
pMem->r = r1;
- pMem->flags = MEM_Real;
- sqlite3VdbeMemRelease(pMem);
+ setTypeFlag(pMem, MEM_Real);
}
return SQLITE_OK;
}
@@ -399,10 +442,8 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
** Delete any previous value and set the value stored in *pMem to NULL.
*/
void sqlite3VdbeMemSetNull(Mem *pMem){
- sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Null;
+ setTypeFlag(pMem, MEM_Null);
pMem->type = SQLITE_NULL;
- pMem->n = 0;
}
/*
@@ -411,12 +452,12 @@ void sqlite3VdbeMemSetNull(Mem *pMem){
*/
void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Blob|MEM_Zero|MEM_Short;
+ setTypeFlag(pMem, MEM_Blob);
+ pMem->flags = MEM_Blob|MEM_Zero;
pMem->type = SQLITE_BLOB;
pMem->n = 0;
if( n<0 ) n = 0;
pMem->u.i = n;
- pMem->z = pMem->zShort;
pMem->enc = SQLITE_UTF8;
}
@@ -436,7 +477,7 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
** manifest type REAL.
*/
void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
- if( sqlite3_isnan(val) ){
+ if( sqlite3IsNaN(val) ){
sqlite3VdbeMemSetNull(pMem);
}else{
sqlite3VdbeMemRelease(pMem);
@@ -451,27 +492,34 @@ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
** too large - whose size exceeds SQLITE_MAX_LENGTH.
*/
int sqlite3VdbeMemTooBig(Mem *p){
+ assert( p->db!=0 );
if( p->flags & (MEM_Str|MEM_Blob) ){
int n = p->n;
if( p->flags & MEM_Zero ){
n += p->u.i;
}
- return n>SQLITE_MAX_LENGTH;
+ return n>p->db->aLimit[SQLITE_LIMIT_LENGTH];
}
return 0;
}
/*
+** Size of struct Mem not including the Mem.zMalloc member.
+*/
+#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc))
+
+/*
** Make an shallow copy of pFrom into pTo. Prior contents of
-** pTo are overwritten. The pFrom->z field is not duplicated. If
+** pTo are freed. The pFrom->z field is not duplicated. If
** pFrom->z is used, then pTo->z points to the same thing as pFrom->z
** and flags gets srcType (either MEM_Ephem or MEM_Static).
*/
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
- memcpy(pTo, pFrom, sizeof(*pFrom)-sizeof(pFrom->zShort));
+ sqlite3VdbeMemReleaseExternal(pTo);
+ memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->xDel = 0;
- if( pTo->flags & (MEM_Str|MEM_Blob) ){
- pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Short|MEM_Ephem);
+ if( (pFrom->flags&MEM_Dyn)!=0 || pFrom->z==pFrom->zMalloc ){
+ pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem);
assert( srcType==MEM_Ephem || srcType==MEM_Static );
pTo->flags |= srcType;
}
@@ -482,16 +530,19 @@ void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
** freed before the copy is made.
*/
int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
- int rc;
- if( pTo->flags & MEM_Dyn ){
- sqlite3VdbeMemRelease(pTo);
- }
- sqlite3VdbeMemShallowCopy(pTo, pFrom, MEM_Ephem);
- if( pTo->flags & MEM_Ephem ){
- rc = sqlite3VdbeMemMakeWriteable(pTo);
- }else{
- rc = SQLITE_OK;
+ int rc = SQLITE_OK;
+
+ sqlite3VdbeMemReleaseExternal(pTo);
+ memcpy(pTo, pFrom, MEMCELLSIZE);
+ pTo->flags &= ~MEM_Dyn;
+
+ if( pTo->flags&(MEM_Str|MEM_Blob) ){
+ if( 0==(pFrom->flags&MEM_Static) ){
+ pTo->flags |= MEM_Ephem;
+ rc = sqlite3VdbeMemMakeWriteable(pTo);
+ }
}
+
return rc;
}
@@ -499,31 +550,28 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
** Transfer the contents of pFrom to pTo. Any existing value in pTo is
** freed. If pFrom contains ephemeral data, a copy is made.
**
-** pFrom contains an SQL NULL when this routine returns. SQLITE_NOMEM
-** might be returned if pFrom held ephemeral data and we were unable
-** to allocate enough space to make a copy.
+** pFrom contains an SQL NULL when this routine returns.
*/
-int sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
- int rc;
- if( pTo->flags & MEM_Dyn ){
- sqlite3VdbeMemRelease(pTo);
- }
+void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
+ assert( pFrom->db==0 || sqlite3_mutex_held(pFrom->db->mutex) );
+ assert( pTo->db==0 || sqlite3_mutex_held(pTo->db->mutex) );
+ assert( pFrom->db==0 || pTo->db==0 || pFrom->db==pTo->db );
+
+ sqlite3VdbeMemRelease(pTo);
memcpy(pTo, pFrom, sizeof(Mem));
- if( pFrom->flags & MEM_Short ){
- pTo->z = pTo->zShort;
- }
pFrom->flags = MEM_Null;
pFrom->xDel = 0;
- if( pTo->flags & MEM_Ephem ){
- rc = sqlite3VdbeMemMakeWriteable(pTo);
- }else{
- rc = SQLITE_OK;
- }
- return rc;
+ pFrom->zMalloc = 0;
}
/*
** Change the value of a Mem to be a string or a BLOB.
+**
+** The memory management strategy depends on the value of the xDel
+** parameter. If the value passed is SQLITE_TRANSIENT, then the
+** string is copied into a (possibly existing) buffer managed by the
+** Mem structure. Otherwise, any existing buffer is freed and the
+** pointer copied.
*/
int sqlite3VdbeMemSetStr(
Mem *pMem, /* Memory cell to set to string value */
@@ -532,59 +580,72 @@ int sqlite3VdbeMemSetStr(
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
- sqlite3VdbeMemRelease(pMem);
+ int nByte = n; /* New value for pMem->n */
+ int iLimit; /* Maximum allowed string or blob size */
+ int flags = 0; /* New value for pMem->flags */
+
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+
+ /* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
- pMem->flags = MEM_Null;
- pMem->type = SQLITE_NULL;
+ sqlite3VdbeMemSetNull(pMem);
return SQLITE_OK;
}
- pMem->z = (char *)z;
- if( xDel==SQLITE_STATIC ){
- pMem->flags = MEM_Static;
- }else if( xDel==SQLITE_TRANSIENT ){
- pMem->flags = MEM_Ephem;
+ if( pMem->db ){
+ iLimit = pMem->db->aLimit[SQLITE_LIMIT_LENGTH];
}else{
- pMem->flags = MEM_Dyn;
- pMem->xDel = xDel;
+ iLimit = SQLITE_MAX_LENGTH;
+ }
+ flags = (enc==0?MEM_Blob:MEM_Str);
+ if( nByte<0 ){
+ assert( enc!=0 );
+ if( enc==SQLITE_UTF8 ){
+ for(nByte=0; nByte<=iLimit && z[nByte]; nByte++){}
+ }else{
+ for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
+ }
+ flags |= MEM_Term;
+ }
+ if( nByte>iLimit ){
+ return SQLITE_TOOBIG;
}
- pMem->enc = enc;
- pMem->type = enc==0 ? SQLITE_BLOB : SQLITE_TEXT;
- pMem->n = n;
-
- assert( enc==0 || enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE
- || enc==SQLITE_UTF16BE );
- switch( enc ){
- case 0:
- pMem->flags |= MEM_Blob;
- pMem->enc = SQLITE_UTF8;
- break;
+ /* The following block sets the new values of Mem.z and Mem.xDel. It
+ ** also sets a flag in local variable "flags" to indicate the memory
+ ** management (one of MEM_Dyn or MEM_Static).
+ */
+ if( xDel==SQLITE_TRANSIENT ){
+ int nAlloc = nByte;
+ if( flags&MEM_Term ){
+ nAlloc += (enc==SQLITE_UTF8?1:2);
+ }
+ if( sqlite3VdbeMemGrow(pMem, nAlloc, 0) ){
+ return SQLITE_NOMEM;
+ }
+ memcpy(pMem->z, z, nAlloc);
+ }else if( xDel==SQLITE_DYNAMIC ){
+ sqlite3VdbeMemRelease(pMem);
+ pMem->zMalloc = pMem->z = (char *)z;
+ pMem->xDel = 0;
+ }else{
+ sqlite3VdbeMemRelease(pMem);
+ pMem->z = (char *)z;
+ pMem->xDel = xDel;
+ flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn);
+ }
- case SQLITE_UTF8:
- pMem->flags |= MEM_Str;
- if( n<0 ){
- pMem->n = strlen(z);
- pMem->flags |= MEM_Term;
- }
- break;
+ pMem->n = nByte;
+ pMem->flags = flags;
+ pMem->enc = (enc==0 ? SQLITE_UTF8 : enc);
+ pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT);
#ifndef SQLITE_OMIT_UTF16
- case SQLITE_UTF16LE:
- case SQLITE_UTF16BE:
- pMem->flags |= MEM_Str;
- if( pMem->n<0 ){
- pMem->n = sqlite3Utf16ByteLen(pMem->z,-1);
- pMem->flags |= MEM_Term;
- }
- if( sqlite3VdbeMemHandleBom(pMem) ){
- return SQLITE_NOMEM;
- }
-#endif /* SQLITE_OMIT_UTF16 */
- }
- if( pMem->flags&MEM_Ephem ){
- return sqlite3VdbeMemMakeWriteable(pMem);
+ if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
+ return SQLITE_NOMEM;
}
+#endif
+
return SQLITE_OK;
}
@@ -731,7 +792,11 @@ int sqlite3VdbeMemFromBtree(
){
char *zData; /* Data from the btree layer */
int available = 0; /* Number of bytes available on the local btree page */
+ sqlite3 *db; /* Database connection */
+ int rc = SQLITE_OK;
+ db = sqlite3BtreeCursorDb(pCur);
+ assert( sqlite3_mutex_held(db->mutex) );
if( key ){
zData = (char *)sqlite3BtreeKeyFetch(pCur, &available);
}else{
@@ -739,51 +804,31 @@ int sqlite3VdbeMemFromBtree(
}
assert( zData!=0 );
- pMem->n = amt;
- if( offset+amt<=available ){
+ if( offset+amt<=available && ((pMem->flags&MEM_Dyn)==0 || pMem->xDel) ){
+ sqlite3VdbeMemRelease(pMem);
pMem->z = &zData[offset];
pMem->flags = MEM_Blob|MEM_Ephem;
- }else{
- int rc;
- if( amt>NBFS-2 ){
- zData = (char *)sqliteMallocRaw(amt+2);
- if( !zData ){
- return SQLITE_NOMEM;
- }
- pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term;
- pMem->xDel = 0;
- }else{
- zData = &(pMem->zShort[0]);
- pMem->flags = MEM_Blob|MEM_Short|MEM_Term;
- }
- pMem->z = zData;
+ }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){
+ pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term;
pMem->enc = 0;
pMem->type = SQLITE_BLOB;
-
if( key ){
- rc = sqlite3BtreeKey(pCur, offset, amt, zData);
+ rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
}else{
- rc = sqlite3BtreeData(pCur, offset, amt, zData);
+ rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
}
- zData[amt] = 0;
- zData[amt+1] = 0;
+ pMem->z[amt] = 0;
+ pMem->z[amt+1] = 0;
if( rc!=SQLITE_OK ){
- if( amt>NBFS-2 ){
- assert( zData!=pMem->zShort );
- assert( pMem->flags & MEM_Dyn );
- sqliteFree(zData);
- } else {
- assert( zData==pMem->zShort );
- assert( pMem->flags & MEM_Short );
- }
- return rc;
+ sqlite3VdbeMemRelease(pMem);
}
}
+ pMem->n = amt;
- return SQLITE_OK;
+ return rc;
}
-#ifndef NDEBUG
+#if 0
/*
** Perform various checks on the memory cell pMem. An assert() will
** fail if pMem is internally inconsistent.
@@ -844,6 +889,8 @@ void sqlite3VdbeMemSanity(Mem *pMem){
*/
const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
if( !pVal ) return 0;
+
+ assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
if( pVal->flags&MEM_Null ){
@@ -854,7 +901,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
expandBlob(pVal);
if( pVal->flags&MEM_Str ){
sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
- if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&(int)pVal->z) ){
+ if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){
assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 );
if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){
return 0;
@@ -866,7 +913,8 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
sqlite3VdbeMemStringify(pVal, enc);
assert( 0==(1&(int)pVal->z) );
}
- assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || sqlite3MallocFailed() );
+ assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
+ || pVal->db->mallocFailed );
if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
return pVal->z;
}else{
@@ -877,11 +925,12 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
/*
** Create a new sqlite3_value object.
*/
-sqlite3_value *sqlite3ValueNew(void){
- Mem *p = sqliteMalloc(sizeof(*p));
+sqlite3_value *sqlite3ValueNew(sqlite3 *db){
+ Mem *p = sqlite3DbMallocZero(db, sizeof(*p));
if( p ){
p->flags = MEM_Null;
p->type = SQLITE_NULL;
+ p->db = db;
}
return p;
}
@@ -890,17 +939,18 @@ sqlite3_value *sqlite3ValueNew(void){
** Create a new sqlite3_value object, containing the value of pExpr.
**
** This only works for very simple expressions that consist of one constant
-** token (i.e. "5", "5.1", "NULL", "'a string'"). If the expression can
+** token (i.e. "5", "5.1", "'a string'"). If the expression can
** be converted directly into a value, then the value is allocated and
** a pointer written to *ppVal. The caller is responsible for deallocating
** the value by passing it to sqlite3ValueFree() later on. If the expression
** cannot be converted to a value, then *ppVal is set to NULL.
*/
int sqlite3ValueFromExpr(
- Expr *pExpr,
- u8 enc,
- u8 affinity,
- sqlite3_value **ppVal
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* Write the new value here */
){
int op;
char *zVal = 0;
@@ -913,18 +963,18 @@ int sqlite3ValueFromExpr(
op = pExpr->op;
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
- zVal = sqliteStrNDup((char*)pExpr->token.z, pExpr->token.n);
- pVal = sqlite3ValueNew();
+ zVal = sqlite3DbStrNDup(db, (char*)pExpr->token.z, pExpr->token.n);
+ pVal = sqlite3ValueNew(db);
if( !zVal || !pVal ) goto no_mem;
sqlite3Dequote(zVal);
- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, sqlite3FreeX);
+ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, enc);
}else{
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_UMINUS ) {
- if( SQLITE_OK==sqlite3ValueFromExpr(pExpr->pLeft, enc, affinity, &pVal) ){
+ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
pVal->u.i = -1 * pVal->u.i;
pVal->r = -1.0 * pVal->r;
}
@@ -932,13 +982,15 @@ int sqlite3ValueFromExpr(
#ifndef SQLITE_OMIT_BLOB_LITERAL
else if( op==TK_BLOB ){
int nVal;
- pVal = sqlite3ValueNew();
- zVal = sqliteStrNDup((char*)pExpr->token.z+1, pExpr->token.n-1);
- if( !zVal || !pVal ) goto no_mem;
- sqlite3Dequote(zVal);
- nVal = strlen(zVal)/2;
- sqlite3VdbeMemSetStr(pVal, sqlite3HexToBlob(zVal), nVal, 0, sqlite3FreeX);
- sqliteFree(zVal);
+ assert( pExpr->token.n>=3 );
+ assert( pExpr->token.z[0]=='x' || pExpr->token.z[0]=='X' );
+ assert( pExpr->token.z[1]=='\'' );
+ assert( pExpr->token.z[pExpr->token.n-1]=='\'' );
+ pVal = sqlite3ValueNew(db);
+ nVal = pExpr->token.n - 3;
+ zVal = (char*)pExpr->token.z + 2;
+ sqlite3VdbeMemSetStr(pVal, sqlite3HexToBlob(db, zVal, nVal), nVal/2,
+ 0, SQLITE_DYNAMIC);
}
#endif
@@ -946,7 +998,8 @@ int sqlite3ValueFromExpr(
return SQLITE_OK;
no_mem:
- sqliteFree(zVal);
+ db->mallocFailed = 1;
+ sqlite3DbFree(db, zVal);
sqlite3ValueFree(pVal);
*ppVal = 0;
return SQLITE_NOMEM;
@@ -956,11 +1009,11 @@ no_mem:
** Change the string value of an sqlite3_value object
*/
void sqlite3ValueSetStr(
- sqlite3_value *v,
- int n,
- const void *z,
- u8 enc,
- void (*xDel)(void*)
+ sqlite3_value *v, /* Value to be set */
+ int n, /* Length of string z */
+ const void *z, /* Text of the new string */
+ u8 enc, /* Encoding to use */
+ void (*xDel)(void*) /* Destructor for the string */
){
if( v ) sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel);
}
@@ -970,8 +1023,8 @@ void sqlite3ValueSetStr(
*/
void sqlite3ValueFree(sqlite3_value *v){
if( !v ) return;
- sqlite3ValueSetStr(v, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
- sqliteFree(v);
+ sqlite3VdbeMemRelease((Mem *)v);
+ sqlite3DbFree(((Mem*)v)->db, v);
}
/*
diff --git a/third_party/sqlite/vtab.c b/third_party/sqlite/src/vtab.c
index 611725d..bd18c479 100644..100755
--- a/third_party/sqlite/vtab.c
+++ b/third_party/sqlite/src/vtab.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to help implement virtual tables.
**
-** $Id: vtab.c,v 1.48 2007/06/26 10:38:55 danielk1977 Exp $
+** $Id: vtab.c,v 1.74 2008/08/02 03:50:39 drh Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"
@@ -23,23 +23,33 @@ static int createModule(
void *pAux, /* Context pointer for xCreate/xConnect */
void (*xDestroy)(void *) /* Module destructor function */
) {
- int nName = strlen(zName);
- Module *pMod = (Module *)sqliteMallocRaw(sizeof(Module) + nName + 1);
+ int rc, nName;
+ Module *pMod;
+
+ sqlite3_mutex_enter(db->mutex);
+ nName = strlen(zName);
+ pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
if( pMod ){
+ Module *pDel;
char *zCopy = (char *)(&pMod[1]);
memcpy(zCopy, zName, nName+1);
pMod->zName = zCopy;
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
- pMod = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
- if( pMod && pMod->xDestroy ){
- pMod->xDestroy(pMod->pAux);
+ pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
+ if( pDel && pDel->xDestroy ){
+ pDel->xDestroy(pDel->pAux);
+ }
+ sqlite3DbFree(db, pDel);
+ if( pDel==pMod ){
+ db->mallocFailed = 1;
}
- sqliteFree(pMod);
sqlite3ResetInternalSchema(db, 0);
}
- return sqlite3ApiExit(db, SQLITE_OK);
+ rc = sqlite3ApiExit(db, SQLITE_OK);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
@@ -87,12 +97,12 @@ void sqlite3VtabLock(sqlite3_vtab *pVtab){
void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
pVtab->nRef--;
assert(db);
- assert(!sqlite3SafetyCheck(db));
+ assert( sqlite3SafetyCheckOk(db) );
if( pVtab->nRef==0 ){
if( db->magic==SQLITE_MAGIC_BUSY ){
- sqlite3SafetyOff(db);
+ (void)sqlite3SafetyOff(db);
pVtab->pModule->xDisconnect(pVtab);
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
} else {
pVtab->pModule->xDisconnect(pVtab);
}
@@ -106,17 +116,18 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
*/
void sqlite3VtabClear(Table *p){
sqlite3_vtab *pVtab = p->pVtab;
+ sqlite3 *db = p->db;
if( pVtab ){
assert( p->pMod && p->pMod->pModule );
- sqlite3VtabUnlock(p->pSchema->db, pVtab);
+ sqlite3VtabUnlock(db, pVtab);
p->pVtab = 0;
}
if( p->azModuleArg ){
int i;
for(i=0; i<p->nModuleArg; i++){
- sqliteFree(p->azModuleArg[i]);
+ sqlite3DbFree(db, p->azModuleArg[i]);
}
- sqliteFree(p->azModuleArg);
+ sqlite3DbFree(db, p->azModuleArg);
}
}
@@ -126,18 +137,18 @@ void sqlite3VtabClear(Table *p){
** string will be freed automatically when the table is
** deleted.
*/
-static void addModuleArgument(Table *pTable, char *zArg){
+static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
int i = pTable->nModuleArg++;
int nBytes = sizeof(char *)*(1+pTable->nModuleArg);
char **azModuleArg;
- azModuleArg = sqliteRealloc(pTable->azModuleArg, nBytes);
+ azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
if( azModuleArg==0 ){
int j;
for(j=0; j<i; j++){
- sqliteFree(pTable->azModuleArg[j]);
+ sqlite3DbFree(db, pTable->azModuleArg[j]);
}
- sqliteFree(zArg);
- sqliteFree(pTable->azModuleArg);
+ sqlite3DbFree(db, zArg);
+ sqlite3DbFree(db, pTable->azModuleArg);
pTable->nModuleArg = 0;
}else{
azModuleArg[i] = zArg;
@@ -159,27 +170,27 @@ void sqlite3VtabBeginParse(
){
int iDb; /* The database the table is being created in */
Table *pTable; /* The new virtual table */
+ sqlite3 *db; /* Database connection */
-#ifndef SQLITE_OMIT_SHARED_CACHE
- if( sqlite3ThreadDataReadOnly()->useSharedData ){
+ if( pParse->db->flags & SQLITE_SharedCache ){
sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode");
return;
}
-#endif
sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0);
pTable = pParse->pNewTable;
if( pTable==0 || pParse->nErr ) return;
assert( 0==pTable->pIndex );
- iDb = sqlite3SchemaToIndex(pParse->db, pTable->pSchema);
+ db = pParse->db;
+ iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
assert( iDb>=0 );
pTable->isVirtual = 1;
pTable->nModuleArg = 0;
- addModuleArgument(pTable, sqlite3NameFromToken(pModuleName));
- addModuleArgument(pTable, sqlite3StrDup(pParse->db->aDb[iDb].zName));
- addModuleArgument(pTable, sqlite3StrDup(pTable->zName));
+ addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
+ addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName));
+ addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
pParse->sNameToken.n = pModuleName->z + pModuleName->n - pName1->z;
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -204,7 +215,8 @@ static void addArgumentToVtab(Parse *pParse){
if( pParse->sArg.z && pParse->pNewTable ){
const char *z = (const char*)pParse->sArg.z;
int n = pParse->sArg.n;
- addModuleArgument(pParse->pNewTable, sqliteStrNDup(z, n));
+ sqlite3 *db = pParse->db;
+ addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
}
}
@@ -246,35 +258,36 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
if( pEnd ){
pParse->sNameToken.n = pEnd->z - pParse->sNameToken.z + pEnd->n;
}
- zStmt = sqlite3MPrintf("CREATE VIRTUAL TABLE %T", &pParse->sNameToken);
+ zStmt = sqlite3MPrintf(db, "CREATE VIRTUAL TABLE %T", &pParse->sNameToken);
/* A slot for the record has already been allocated in the
** SQLITE_MASTER table. We just need to update that slot with all
** the information we've collected.
**
- ** The top of the stack is the rootpage allocated by sqlite3StartTable().
- ** This value is always 0 and is ignored, a virtual table does not have a
- ** rootpage. The next entry on the stack is the rowid of the record
- ** in the sqlite_master table.
+ ** The VM register number pParse->regRowid holds the rowid of an
+ ** entry in the sqlite_master table tht was created for this vtab
+ ** by sqlite3StartTable().
*/
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3NestedParse(pParse,
"UPDATE %Q.%s "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
- "WHERE rowid=#1",
+ "WHERE rowid=#%d",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
pTab->zName,
pTab->zName,
- zStmt
+ zStmt,
+ pParse->regRowid
);
- sqliteFree(zStmt);
+ sqlite3DbFree(db, zStmt);
v = sqlite3GetVdbe(pParse);
- sqlite3ChangeCookie(db, v, iDb);
+ sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp(v, OP_Expire, 0, 0);
- zWhere = sqlite3MPrintf("name='%q'", pTab->zName);
- sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 1, zWhere, P3_DYNAMIC);
- sqlite3VdbeOp3(v, OP_VCreate, iDb, 0, pTab->zName, strlen(pTab->zName) + 1);
+ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
+ zWhere = sqlite3MPrintf(db, "name='%q'", pTab->zName);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
+ pTab->zName, strlen(pTab->zName) + 1);
}
/* If we are rereading the sqlite_master table create the in-memory
@@ -288,6 +301,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
int nName = strlen(zName) + 1;
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab);
if( pOld ){
+ db->mallocFailed = 1;
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
@@ -335,11 +349,11 @@ static int vtabCallConstructor(
){
int rc;
int rc2;
- sqlite3_vtab *pVtab;
+ sqlite3_vtab *pVtab = 0;
const char *const*azArg = (const char *const*)pTab->azModuleArg;
int nArg = pTab->nModuleArg;
char *zErr = 0;
- char *zModuleName = sqlite3MPrintf("%s", pTab->zName);
+ char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
if( !zModuleName ){
return SQLITE_NOMEM;
@@ -351,31 +365,31 @@ static int vtabCallConstructor(
db->pVTab = pTab;
rc = sqlite3SafetyOff(db);
assert( rc==SQLITE_OK );
- rc = xConstruct(db, pMod->pAux, nArg, azArg, &pTab->pVtab, &zErr);
+ rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr);
rc2 = sqlite3SafetyOn(db);
- pVtab = pTab->pVtab;
if( rc==SQLITE_OK && pVtab ){
pVtab->pModule = pMod->pModule;
pVtab->nRef = 1;
+ pTab->pVtab = pVtab;
}
if( SQLITE_OK!=rc ){
if( zErr==0 ){
- *pzErr = sqlite3MPrintf("vtable constructor failed: %s", zModuleName);
+ *pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName);
}else {
- *pzErr = sqlite3MPrintf("%s", zErr);
- sqlite3_free(zErr);
+ *pzErr = sqlite3MPrintf(db, "%s", zErr);
+ sqlite3DbFree(db, zErr);
}
}else if( db->pVTab ){
const char *zFormat = "vtable constructor did not declare schema: %s";
- *pzErr = sqlite3MPrintf(zFormat, pTab->zName);
+ *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
rc = rc2;
}
db->pVTab = 0;
- sqliteFree(zModuleName);
+ sqlite3DbFree(db, zModuleName);
/* If everything went according to plan, loop through the columns
** of the table to see if any of them contain the token "hidden".
@@ -444,7 +458,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "%s", zErr);
}
- sqliteFree(zErr);
+ sqlite3DbFree(db, zErr);
}
return rc;
@@ -460,7 +474,7 @@ static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){
if( (db->nVTrans%ARRAY_INCR)==0 ){
sqlite3_vtab **aVTrans;
int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR);
- aVTrans = sqliteRealloc((void *)db->aVTrans, nBytes);
+ aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
return SQLITE_NOMEM;
}
@@ -480,7 +494,7 @@ static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){
**
** If an error occurs, *pzErr is set to point an an English language
** description of the error and an SQLITE_XXX error code is returned.
-** In this case the caller must call sqliteFree() on *pzErr.
+** In this case the caller must call sqlite3DbFree(db, ) on *pzErr.
*/
int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
int rc = SQLITE_OK;
@@ -498,7 +512,7 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
** error. Otherwise, do nothing.
*/
if( !pMod ){
- *pzErr = sqlite3MPrintf("no such module: %s", zModule);
+ *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule);
rc = SQLITE_ERROR;
}else{
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
@@ -520,11 +534,14 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Parse sParse;
int rc = SQLITE_OK;
- Table *pTab = db->pVTab;
+ Table *pTab;
char *zErr = 0;
+ sqlite3_mutex_enter(db->mutex);
+ pTab = db->pVTab;
if( !pTab ){
sqlite3Error(db, SQLITE_MISUSE, 0);
+ sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE;
}
assert(pTab->isVirtual && pTab->nCol==0 && pTab->aCol==0);
@@ -546,7 +563,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
db->pVTab = 0;
} else {
sqlite3Error(db, SQLITE_ERROR, zErr);
- sqliteFree(zErr);
+ sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}
sParse.declareVtab = 0;
@@ -556,7 +573,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sParse.pNewTable = 0;
assert( (rc&0xff)==rc );
- return sqlite3ApiExit(db, rc);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
}
/*
@@ -580,8 +599,15 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab)
if( xDestroy ){
rc = xDestroy(pTab->pVtab);
}
- sqlite3SafetyOn(db);
+ (void)sqlite3SafetyOn(db);
if( rc==SQLITE_OK ){
+ int i;
+ for(i=0; i<db->nVTrans; i++){
+ if( db->aVTrans[i]==pTab->pVtab ){
+ db->aVTrans[i] = db->aVTrans[--db->nVTrans];
+ break;
+ }
+ }
pTab->pVtab = 0;
}
}
@@ -607,24 +633,25 @@ static void callFinaliser(sqlite3 *db, int offset){
if( x ) x(pVtab);
sqlite3VtabUnlock(db, pVtab);
}
- sqliteFree(db->aVTrans);
+ sqlite3DbFree(db, db->aVTrans);
db->nVTrans = 0;
db->aVTrans = 0;
}
}
/*
-** If argument rc2 is not SQLITE_OK, then return it and do nothing.
-** Otherwise, invoke the xSync method of all virtual tables in the
-** sqlite3.aVTrans array. Return the error code for the first error
-** that occurs, or SQLITE_OK if all xSync operations are successful.
+** Invoke the xSync method of all virtual tables in the sqlite3.aVTrans
+** array. Return the error code for the first error that occurs, or
+** SQLITE_OK if all xSync operations are successful.
+**
+** Set *pzErrmsg to point to a buffer that should be released using
+** sqlite3DbFree() containing an error message, if one is available.
*/
-int sqlite3VtabSync(sqlite3 *db, int rc2){
+int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
int i;
int rc = SQLITE_OK;
int rcsafety;
sqlite3_vtab **aVTrans = db->aVTrans;
- if( rc2!=SQLITE_OK ) return rc2;
rc = sqlite3SafetyOff(db);
db->aVTrans = 0;
@@ -634,6 +661,9 @@ int sqlite3VtabSync(sqlite3 *db, int rc2){
x = pVtab->pModule->xSync;
if( x ){
rc = x(pVtab);
+ sqlite3DbFree(db, *pzErrmsg);
+ *pzErrmsg = pVtab->zErrMsg;
+ pVtab->zErrMsg = 0;
}
}
db->aVTrans = aVTrans;
@@ -650,7 +680,7 @@ int sqlite3VtabSync(sqlite3 *db, int rc2){
** sqlite3.aVTrans array. Then clear the array itself.
*/
int sqlite3VtabRollback(sqlite3 *db){
- callFinaliser(db, (int)(&((sqlite3_module *)0)->xRollback));
+ callFinaliser(db, offsetof(sqlite3_module,xRollback));
return SQLITE_OK;
}
@@ -659,7 +689,7 @@ int sqlite3VtabRollback(sqlite3 *db){
** sqlite3.aVTrans array. Then clear the array itself.
*/
int sqlite3VtabCommit(sqlite3 *db){
- callFinaliser(db, (int)(&((sqlite3_module *)0)->xCommit));
+ callFinaliser(db, offsetof(sqlite3_module,xCommit));
return SQLITE_OK;
}
@@ -701,11 +731,9 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
/* Invoke the xBegin method */
rc = pModule->xBegin(pVtab);
- if( rc!=SQLITE_OK ){
- return rc;
+ if( rc==SQLITE_OK ){
+ rc = addToVTrans(db, pVtab);
}
-
- rc = addToVTrans(db, pVtab);
}
return rc;
}
@@ -724,6 +752,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
** SQLITE_FUNC_EPHEM flag.
*/
FuncDef *sqlite3VtabOverloadFunction(
+ sqlite3 *db, /* Database connection for reporting malloc problems */
FuncDef *pDef, /* Function to possibly overload */
int nArg, /* Number of arguments to the function */
Expr *pExpr /* First argument to the function */
@@ -734,7 +763,7 @@ FuncDef *sqlite3VtabOverloadFunction(
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
void *pArg;
FuncDef *pNew;
- int rc;
+ int rc = 0;
char *zLowerName;
unsigned char *z;
@@ -751,22 +780,29 @@ FuncDef *sqlite3VtabOverloadFunction(
pMod = (sqlite3_module *)pVtab->pModule;
if( pMod->xFindFunction==0 ) return pDef;
- /* Call the xFuncFunction method on the virtual table implementation
+ /* Call the xFindFunction method on the virtual table implementation
** to see if the implementation wants to overload this function
*/
- zLowerName = sqlite3StrDup(pDef->zName);
- for(z=(unsigned char*)zLowerName; *z; z++){
- *z = sqlite3UpperToLower[*z];
+ zLowerName = sqlite3DbStrDup(db, pDef->zName);
+ if( zLowerName ){
+ for(z=(unsigned char*)zLowerName; *z; z++){
+ *z = sqlite3UpperToLower[*z];
+ }
+ rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg);
+ sqlite3DbFree(db, zLowerName);
+ if( pVtab->zErrMsg ){
+ sqlite3Error(db, rc, "%s", pVtab->zErrMsg);
+ sqlite3DbFree(db, pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+ }
}
- rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg);
- sqliteFree(zLowerName);
if( rc==0 ){
return pDef;
}
/* Create a new ephemeral function definition for the overloaded
** function */
- pNew = sqliteMalloc( sizeof(*pNew) + strlen(pDef->zName) );
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) + strlen(pDef->zName) );
if( pNew==0 ){
return pDef;
}
@@ -778,4 +814,25 @@ FuncDef *sqlite3VtabOverloadFunction(
return pNew;
}
+/*
+** Make sure virtual table pTab is contained in the pParse->apVirtualLock[]
+** array so that an OP_VBegin will get generated for it. Add pTab to the
+** array if it is missing. If pTab is already in the array, this routine
+** is a no-op.
+*/
+void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
+ int i, n;
+ assert( IsVirtual(pTab) );
+ for(i=0; i<pParse->nVtabLock; i++){
+ if( pTab==pParse->apVtabLock[i] ) return;
+ }
+ n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
+ pParse->apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);
+ if( pParse->apVtabLock ){
+ pParse->apVtabLock[pParse->nVtabLock++] = pTab;
+ }else{
+ pParse->db->mallocFailed = 1;
+ }
+}
+
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/third_party/sqlite/where.c b/third_party/sqlite/src/where.c
index bf255d177..9c70c31 100644..100755
--- a/third_party/sqlite/where.c
+++ b/third_party/sqlite/src/where.c
@@ -10,13 +10,13 @@
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
-** the WHERE clause of SQL statements. This module is reponsible for
+** the WHERE clause of SQL statements. This module is responsible for
** generating the code that loops through a table looking for applicable
** rows. Indices are selected and used to speed the search when doing
** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer".
**
-** $Id: where.c,v 1.254 2007/07/30 14:40:48 danielk1977 Exp $
+** $Id: where.c,v 1.319 2008/08/01 17:37:41 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -29,8 +29,10 @@
** Trace output macros
*/
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-int sqlite3_where_trace = 0;
-# define WHERETRACE(X) if(sqlite3_where_trace) sqlite3DebugPrintf X
+int sqlite3WhereTrace = 0;
+#endif
+#if 0
+# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X
#else
# define WHERETRACE(X)
#endif
@@ -90,7 +92,7 @@ struct WhereTerm {
/*
** Allowed values of WhereTerm.flags
*/
-#define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(pExpr) */
+#define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(db, pExpr) */
#define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */
#define TERM_CODED 0x04 /* This term is already coded */
#define TERM_COPIED 0x08 /* Has a child */
@@ -201,13 +203,14 @@ static void whereClauseInit(
static void whereClauseClear(WhereClause *pWC){
int i;
WhereTerm *a;
+ sqlite3 *db = pWC->pParse->db;
for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
if( a->flags & TERM_DYNAMIC ){
- sqlite3ExprDelete(a->pExpr);
+ sqlite3ExprDelete(db, a->pExpr);
}
}
if( pWC->a!=pWC->aStatic ){
- sqliteFree(pWC->a);
+ sqlite3DbFree(db, pWC->a);
}
}
@@ -219,7 +222,7 @@ static void whereClauseClear(WhereClause *pWC){
** for freeing the expression p is assumed by the WhereClause object.
**
** WARNING: This routine might reallocate the space used to store
-** WhereTerms. All pointers to WhereTerms should be invalided after
+** WhereTerms. All pointers to WhereTerms should be invalidated after
** calling this routine. Such pointers may be reinitialized by referencing
** the pWC->a[] array.
*/
@@ -228,16 +231,18 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, int flags){
int idx;
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
- pWC->a = sqliteMalloc( sizeof(pWC->a[0])*pWC->nSlot*2 );
+ sqlite3 *db = pWC->pParse->db;
+ pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( flags & TERM_DYNAMIC ){
- sqlite3ExprDelete(p);
+ sqlite3ExprDelete(db, p);
}
+ pWC->a = pOld;
return 0;
}
memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm);
if( pOld!=pWC->aStatic ){
- sqliteFree(pOld);
+ sqlite3DbFree(db, pOld);
}
pWC->nSlot *= 2;
}
@@ -349,15 +354,14 @@ static Bitmask exprListTableUsage(ExprMaskSet *pMaskSet, ExprList *pList){
return mask;
}
static Bitmask exprSelectTableUsage(ExprMaskSet *pMaskSet, Select *pS){
- Bitmask mask;
- if( pS==0 ){
- mask = 0;
- }else{
- mask = exprListTableUsage(pMaskSet, pS->pEList);
+ Bitmask mask = 0;
+ while( pS ){
+ mask |= exprListTableUsage(pMaskSet, pS->pEList);
mask |= exprListTableUsage(pMaskSet, pS->pGroupBy);
mask |= exprListTableUsage(pMaskSet, pS->pOrderBy);
mask |= exprTableUsage(pMaskSet, pS->pWhere);
mask |= exprTableUsage(pMaskSet, pS->pHaving);
+ pS = pS->pPrior;
}
return mask;
}
@@ -381,7 +385,7 @@ static int allowedOp(int op){
#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
/*
-** Commute a comparision operator. Expressions of the form "X op Y"
+** Commute a comparison operator. Expressions of the form "X op Y"
** are converted into "Y op X".
**
** If a collation sequence is associated with either the left or right
@@ -449,13 +453,14 @@ static WhereTerm *findTerm(
){
WhereTerm *pTerm;
int k;
+ assert( iCur>=0 );
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
if( pTerm->leftCursor==iCur
&& (pTerm->prereqRight & notReady)==0
&& pTerm->leftColumn==iColumn
&& (pTerm->eOperator & op)!=0
){
- if( iCur>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
+ if( pIdx && pTerm->eOperator!=WO_ISNULL ){
Expr *pX = pTerm->pExpr;
CollSeq *pColl;
char idxaff;
@@ -475,8 +480,9 @@ static WhereTerm *findTerm(
pColl = pParse->db->pDfltColl;
}
- for(j=0; j<pIdx->nColumn && pIdx->aiColumn[j]!=iColumn; j++){}
- assert( j<pIdx->nColumn );
+ for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
+ if( NEVER(j>=pIdx->nColumn) ) return 0;
+ }
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
}
return pTerm;
@@ -516,22 +522,26 @@ static int isLikeOrGlob(
sqlite3 *db, /* The database */
Expr *pExpr, /* Test this expression */
int *pnPattern, /* Number of non-wildcard prefix characters */
- int *pisComplete /* True if the only wildcard is % in the last character */
+ int *pisComplete, /* True if the only wildcard is % in the last character */
+ int *pnoCase /* True if uppercase is equivalent to lowercase */
){
const char *z;
Expr *pRight, *pLeft;
ExprList *pList;
int c, cnt;
- int noCase;
char wc[3];
CollSeq *pColl;
- if( !sqlite3IsLikeFunction(db, pExpr, &noCase, wc) ){
+ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
return 0;
}
+#ifdef SQLITE_EBCDIC
+ if( *pnoCase ) return 0;
+#endif
pList = pExpr->pList;
pRight = pList->a[0].pExpr;
- if( pRight->op!=TK_STRING ){
+ if( pRight->op!=TK_STRING
+ && (pRight->op!=TK_REGISTER || pRight->iColumn!=TK_STRING) ){
return 0;
}
pLeft = pList->a[1].pExpr;
@@ -539,20 +549,21 @@ static int isLikeOrGlob(
return 0;
}
pColl = pLeft->pColl;
+ assert( pColl!=0 || pLeft->iColumn==-1 );
if( pColl==0 ){
- /* TODO: Coverage testing doesn't get this case. Is it actually possible
- ** for an expression of type TK_COLUMN to not have an assigned collation
- ** sequence at this point?
- */
+ /* No collation is defined for the ROWID. Use the default. */
pColl = db->pDfltColl;
}
- if( (pColl->type!=SQLITE_COLL_BINARY || noCase) &&
- (pColl->type!=SQLITE_COLL_NOCASE || !noCase) ){
+ if( (pColl->type!=SQLITE_COLL_BINARY || *pnoCase) &&
+ (pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){
return 0;
}
- sqlite3DequoteExpr(pRight);
+ sqlite3DequoteExpr(db, pRight);
z = (char *)pRight->token.z;
- for(cnt=0; (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2]; cnt++){}
+ cnt = 0;
+ if( z ){
+ while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; }
+ }
if( cnt==0 || 255==(u8)z[cnt] ){
return 0;
}
@@ -615,7 +626,7 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
** a=<expr1> OR a=<expr2> OR b=<expr3> OR ...
**
** The pOrTerm input to this routine corresponds to a single term of
-** this OR clause. In order for the term to be a condidate for
+** this OR clause. In order for the term to be a candidate for
** conversion to an IN operator, the following must be true:
**
** * The left-hand side of the term must be the column which
@@ -670,7 +681,7 @@ static int orTermIsOptCandidate(WhereTerm *pOrTerm, int iCursor, int iColumn){
** This routine merely checks to see if pOrTerm has a duplicate that
** might qualify. If there is a duplicate that has not yet been
** disqualified, then return true. If there are no duplicates, or
-** the duplicate has also been disqualifed, return false.
+** the duplicate has also been disqualified, return false.
*/
static int orTermHasOkDuplicate(WhereClause *pOr, WhereTerm *pOrTerm){
if( pOrTerm->flags & TERM_COPIED ){
@@ -707,16 +718,25 @@ static void exprAnalyze(
WhereClause *pWC, /* the WHERE clause */
int idxTerm /* Index of the term to be analyzed */
){
- WhereTerm *pTerm = &pWC->a[idxTerm];
- ExprMaskSet *pMaskSet = pWC->pMaskSet;
- Expr *pExpr = pTerm->pExpr;
+ WhereTerm *pTerm;
+ ExprMaskSet *pMaskSet;
+ Expr *pExpr;
Bitmask prereqLeft;
Bitmask prereqAll;
+ Bitmask extraRight = 0;
int nPattern;
int isComplete;
+ int noCase;
int op;
+ Parse *pParse = pWC->pParse;
+ sqlite3 *db = pParse->db;
- if( sqlite3MallocFailed() ) return;
+ if( db->mallocFailed ){
+ return;
+ }
+ pTerm = &pWC->a[idxTerm];
+ pMaskSet = pWC->pMaskSet;
+ pExpr = pTerm->pExpr;
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
op = pExpr->op;
if( op==TK_IN ){
@@ -730,7 +750,10 @@ static void exprAnalyze(
}
prereqAll = exprTableUsage(pMaskSet, pExpr);
if( ExprHasProperty(pExpr, EP_FromJoin) ){
- prereqAll |= getMask(pMaskSet, pExpr->iRightJoinTable);
+ Bitmask x = getMask(pMaskSet, pExpr->iRightJoinTable);
+ prereqAll |= x;
+ extraRight = x-1; /* ON clause terms may not be used with an index
+ ** on left table of a LEFT JOIN. Ticket #3015 */
}
pTerm->prereqAll = prereqAll;
pTerm->leftCursor = -1;
@@ -749,9 +772,9 @@ static void exprAnalyze(
Expr *pDup;
if( pTerm->leftCursor>=0 ){
int idxNew;
- pDup = sqlite3ExprDup(pExpr);
- if( sqlite3MallocFailed() ){
- sqlite3ExprDelete(pDup);
+ pDup = sqlite3ExprDup(db, pExpr);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDup);
return;
}
idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
@@ -788,8 +811,8 @@ static void exprAnalyze(
for(i=0; i<2; i++){
Expr *pNewExpr;
int idxNew;
- pNewExpr = sqlite3Expr(ops[i], sqlite3ExprDup(pExpr->pLeft),
- sqlite3ExprDup(pList->a[i].pExpr), 0);
+ pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft),
+ sqlite3ExprDup(db, pList->a[i].pExpr), 0);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
exprAnalyze(pSrc, pWC, idxNew);
pTerm = &pWC->a[idxTerm];
@@ -825,6 +848,7 @@ static void exprAnalyze(
exprAnalyzeAll(pSrc, &sOr);
assert( sOr.nTerm>=2 );
j = 0;
+ if( db->mallocFailed ) goto or_not_possible;
do{
assert( j<sOr.nTerm );
iColumn = sOr.a[j].leftColumn;
@@ -847,15 +871,15 @@ static void exprAnalyze(
ExprList *pList = 0;
Expr *pNew, *pDup;
Expr *pLeft = 0;
- for(i=sOr.nTerm-1, pOrTerm=sOr.a; i>=0 && ok; i--, pOrTerm++){
+ for(i=sOr.nTerm-1, pOrTerm=sOr.a; i>=0; i--, pOrTerm++){
if( (pOrTerm->flags & TERM_OR_OK)==0 ) continue;
- pDup = sqlite3ExprDup(pOrTerm->pExpr->pRight);
- pList = sqlite3ExprListAppend(pList, pDup, 0);
+ pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight);
+ pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup, 0);
pLeft = pOrTerm->pExpr->pLeft;
}
assert( pLeft!=0 );
- pDup = sqlite3ExprDup(pLeft);
- pNew = sqlite3Expr(TK_IN, pDup, 0, 0);
+ pDup = sqlite3ExprDup(db, pLeft);
+ pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0);
if( pNew ){
int idxNew;
transferJoinMarkings(pNew, pExpr);
@@ -866,7 +890,7 @@ static void exprAnalyze(
pWC->a[idxNew].iParent = idxTerm;
pTerm->nChild = 1;
}else{
- sqlite3ExprListDelete(pList);
+ sqlite3ExprListDelete(db, pList);
}
}
or_not_possible:
@@ -877,8 +901,15 @@ or_not_possible:
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/* Add constraints to reduce the search space on a LIKE or GLOB
** operator.
+ **
+ ** A like pattern of the form "x LIKE 'abc%'" is changed into constraints
+ **
+ ** x>='abc' AND x<'abd' AND x LIKE 'abc%'
+ **
+ ** The last character of the prefix "abc" is incremented to form the
+ ** termination condition "abd".
*/
- if( isLikeOrGlob(pWC->pParse->db, pExpr, &nPattern, &isComplete) ){
+ if( isLikeOrGlob(db, pExpr, &nPattern, &isComplete, &noCase) ){
Expr *pLeft, *pRight;
Expr *pStr1, *pStr2;
Expr *pNewExpr1, *pNewExpr2;
@@ -886,21 +917,28 @@ or_not_possible:
pLeft = pExpr->pList->a[1].pExpr;
pRight = pExpr->pList->a[0].pExpr;
- pStr1 = sqlite3Expr(TK_STRING, 0, 0, 0);
+ pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0);
if( pStr1 ){
- sqlite3TokenCopy(&pStr1->token, &pRight->token);
+ sqlite3TokenCopy(db, &pStr1->token, &pRight->token);
pStr1->token.n = nPattern;
pStr1->flags = EP_Dequoted;
}
- pStr2 = sqlite3ExprDup(pStr1);
- if( pStr2 ){
+ pStr2 = sqlite3ExprDup(db, pStr1);
+ if( !db->mallocFailed ){
+ u8 c, *pC;
assert( pStr2->token.dyn );
- ++*(u8*)&pStr2->token.z[nPattern-1];
+ pC = (u8*)&pStr2->token.z[nPattern-1];
+ c = *pC;
+ if( noCase ){
+ if( c=='@' ) isComplete = 0;
+ c = sqlite3UpperToLower[c];
+ }
+ *pC = c + 1;
}
- pNewExpr1 = sqlite3Expr(TK_GE, sqlite3ExprDup(pLeft), pStr1, 0);
+ pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft), pStr1, 0);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
exprAnalyze(pSrc, pWC, idxNew1);
- pNewExpr2 = sqlite3Expr(TK_LT, sqlite3ExprDup(pLeft), pStr2, 0);
+ pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft), pStr2, 0);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
@@ -931,7 +969,7 @@ or_not_possible:
prereqColumn = exprTableUsage(pMaskSet, pLeft);
if( (prereqExpr & prereqColumn)==0 ){
Expr *pNewExpr;
- pNewExpr = sqlite3Expr(TK_MATCH, 0, sqlite3ExprDup(pRight), 0);
+ pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight), 0);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
pNewTerm = &pWC->a[idxNew];
pNewTerm->prereqRight = prereqExpr;
@@ -946,6 +984,11 @@ or_not_possible:
}
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+ /* Prevent ON clause terms of a LEFT JOIN from being used to drive
+ ** an index for tables to the left of the join.
+ */
+ pTerm->prereqRight |= extraRight;
}
/*
@@ -1051,6 +1094,9 @@ static int isSortingIndex(
** ORDER BY term, that is OK. Just ignore that column of the index
*/
continue;
+ }else if( i==pIdx->nColumn ){
+ /* Index column i is the rowid. All other terms match. */
+ break;
}else{
/* If an index column fails to match and is not constrained by ==
** then the index cannot satisfy the ORDER BY constraint.
@@ -1128,7 +1174,7 @@ static int sortableByRowid(
/*
** Prepare a crude estimate of the logarithm of the input value.
** The results need not be exact. This is only used for estimating
-** the total cost of performing operatings with O(logN) or O(NlogN)
+** the total cost of performing operations with O(logN) or O(NlogN)
** complexity. Because N is just a guess, it is no great tragedy if
** logN is a little off.
*/
@@ -1151,7 +1197,7 @@ static double estLog(double N){
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG)
static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
int i;
- if( !sqlite3_where_trace ) return;
+ if( !sqlite3WhereTrace ) return;
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n",
i,
@@ -1169,7 +1215,7 @@ static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
}
static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
int i;
- if( !sqlite3_where_trace ) return;
+ if( !sqlite3WhereTrace ) return;
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n",
i,
@@ -1213,6 +1259,7 @@ static double bestVirtualIndex(
sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */
){
Table *pTab = pSrc->pTab;
+ sqlite3_vtab *pVtab = pTab->pVtab;
sqlite3_index_info *pIdxInfo;
struct sqlite3_index_constraint *pIdxCons;
struct sqlite3_index_orderby *pIdxOrderBy;
@@ -1236,7 +1283,10 @@ static double bestVirtualIndex(
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- if( pTerm->eOperator==WO_IN ) continue;
+ if( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
+ testcase( pTerm->eOperator==WO_IN );
+ testcase( pTerm->eOperator==WO_ISNULL );
+ if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
nTerm++;
}
@@ -1257,7 +1307,7 @@ static double bestVirtualIndex(
/* Allocate the sqlite3_index_info structure
*/
- pIdxInfo = sqliteMalloc( sizeof(*pIdxInfo)
+ pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
+ sizeof(*pIdxOrderBy)*nOrderBy );
if( pIdxInfo==0 ){
@@ -1283,7 +1333,10 @@ static double bestVirtualIndex(
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- if( pTerm->eOperator==WO_IN ) continue;
+ if( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
+ testcase( pTerm->eOperator==WO_IN );
+ testcase( pTerm->eOperator==WO_ISNULL );
+ if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
pIdxCons[j].iColumn = pTerm->leftColumn;
pIdxCons[j].iTermOffset = i;
pIdxCons[j].op = pTerm->eOperator;
@@ -1318,7 +1371,7 @@ static double bestVirtualIndex(
** sqlite3ViewGetColumnNames() would have picked up the error.
*/
assert( pTab->azModuleArg && pTab->azModuleArg[0] );
- assert( pTab->pVtab );
+ assert( pVtab );
#if 0
if( pTab->pVtab==0 ){
sqlite3ErrorMsg(pParse, "undefined module %s for table %s",
@@ -1368,23 +1421,34 @@ static double bestVirtualIndex(
*(int*)&pIdxInfo->nOrderBy = 0;
}
- sqlite3SafetyOff(pParse->db);
+ (void)sqlite3SafetyOff(pParse->db);
WHERETRACE(("xBestIndex for %s\n", pTab->zName));
TRACE_IDX_INPUTS(pIdxInfo);
- rc = pTab->pVtab->pModule->xBestIndex(pTab->pVtab, pIdxInfo);
+ rc = pVtab->pModule->xBestIndex(pVtab, pIdxInfo);
TRACE_IDX_OUTPUTS(pIdxInfo);
+ (void)sqlite3SafetyOn(pParse->db);
+
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ){
- sqlite3FailedMalloc();
- }else {
+ pParse->db->mallocFailed = 1;
+ }else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
+ }else{
+ sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg);
}
- sqlite3SafetyOn(pParse->db);
- }else{
- rc = sqlite3SafetyOn(pParse->db);
}
- *(int*)&pIdxInfo->nOrderBy = nOrderBy;
+ sqlite3DbFree(pParse->db, pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+ for(i=0; i<pIdxInfo->nConstraint; i++){
+ if( !pIdxInfo->aConstraint[i].usable && pUsage[i].argvIndex>0 ){
+ sqlite3ErrorMsg(pParse,
+ "table %s: xBestIndex returned an invalid plan", pTab->zName);
+ return 0.0;
+ }
+ }
+
+ *(int*)&pIdxInfo->nOrderBy = nOrderBy;
return pIdxInfo->estimatedCost;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -1430,7 +1494,7 @@ static double bestIndex(
int eqTermMask; /* Mask of valid equality operators */
double cost; /* Cost of using pProbe */
- WHERETRACE(("bestIndex: tbl=%s notReady=%x\n", pSrc->pTab->zName, notReady));
+ WHERETRACE(("bestIndex: tbl=%s notReady=%llx\n", pSrc->pTab->zName, notReady));
lowestCost = SQLITE_BIG_DBL;
pProbe = pSrc->pTab->pIndex;
@@ -1552,7 +1616,7 @@ static double bestIndex(
flags |= WHERE_COLUMN_IN;
if( pExpr->pSelect!=0 ){
inMultiplier *= 25;
- }else if( pExpr->pList!=0 ){
+ }else if( ALWAYS(pExpr->pList) ){
inMultiplier *= pExpr->pList->nExpr + 1;
}
}
@@ -1563,7 +1627,7 @@ static double bestIndex(
&& nEq==pProbe->nColumn ){
flags |= WHERE_UNIQUE;
}
- WHERETRACE(("...... nEq=%d inMult=%.9g cost=%.9g\n", nEq, inMultiplier, cost));
+ WHERETRACE(("...... nEq=%d inMult=%.9g cost=%.9g\n",nEq,inMultiplier,cost));
/* Look for range constraints
*/
@@ -1624,10 +1688,9 @@ static double bestIndex(
/* If this index has achieved the lowest cost so far, then use it.
*/
- if( cost < lowestCost ){
+ if( flags && cost < lowestCost ){
bestIdx = pProbe;
lowestCost = cost;
- assert( flags!=0 );
bestFlags = flags;
bestNEq = nEq;
}
@@ -1669,7 +1732,7 @@ static double bestIndex(
*/
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
if( pTerm
- && (pTerm->flags & TERM_CODED)==0
+ && ALWAYS((pTerm->flags & TERM_CODED)==0)
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
){
pTerm->flags |= TERM_CODED;
@@ -1683,20 +1746,17 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
}
/*
-** Generate code that builds a probe for an index.
-**
-** There should be nColumn values on the stack. The index
-** to be probed is pIdx. Pop the values from the stack and
-** replace them all with a single record that is the index
-** problem.
+** Apply the affinities associated with the first n columns of index
+** pIdx to the values in the n registers starting at base.
*/
-static void buildIndexProbe(
- Vdbe *v, /* Generate code into this VM */
- int nColumn, /* The number of columns to check for NULL */
- Index *pIdx /* Index that we will be searching */
-){
- sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
- sqlite3IndexAffinityStr(v, pIdx);
+static void codeApplyAffinity(Parse *pParse, int base, int n, Index *pIdx){
+ if( n>0 ){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ sqlite3VdbeAddOp2(v, OP_Affinity, base, n);
+ sqlite3IndexAffinityStr(v, pIdx);
+ sqlite3ExprCacheAffinityChange(pParse, base, n);
+ }
}
@@ -1705,51 +1765,65 @@ static void buildIndexProbe(
** term can be either X=expr or X IN (...). pTerm is the term to be
** coded.
**
-** The current value for the constraint is left on the top of the stack.
+** The current value for the constraint is left in register iReg.
**
** For a constraint of the form X=expr, the expression is evaluated and its
** result is left on the stack. For constraints of the form X IN (...)
** this routine sets up a loop that will iterate over all values of X.
*/
-static void codeEqualityTerm(
+static int codeEqualityTerm(
Parse *pParse, /* The parsing context */
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
- WhereLevel *pLevel /* When level of the FROM clause we are working on */
+ WhereLevel *pLevel, /* When level of the FROM clause we are working on */
+ int iTarget /* Attempt to leave results in this register */
){
Expr *pX = pTerm->pExpr;
Vdbe *v = pParse->pVdbe;
+ int iReg; /* Register holding results */
+
+ if( iTarget<=0 ){
+ iReg = iTarget = sqlite3GetTempReg(pParse);
+ }
if( pX->op==TK_EQ ){
- sqlite3ExprCode(pParse, pX->pRight);
+ iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
}else if( pX->op==TK_ISNULL ){
- sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ iReg = iTarget;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
#ifndef SQLITE_OMIT_SUBQUERY
}else{
+ int eType;
int iTab;
struct InLoop *pIn;
assert( pX->op==TK_IN );
- sqlite3CodeSubselect(pParse, pX);
+ iReg = iTarget;
+ eType = sqlite3FindInIndex(pParse, pX, 0);
iTab = pX->iTable;
- sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
- VdbeComment((v, "# %.*s", pX->span.n, pX->span.z));
+ sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
+ VdbeComment((v, "%.*s", pX->span.n, pX->span.z));
if( pLevel->nIn==0 ){
pLevel->nxt = sqlite3VdbeMakeLabel(v);
}
pLevel->nIn++;
- pLevel->aInLoop = sqliteReallocOrFree(pLevel->aInLoop,
+ pLevel->aInLoop = sqlite3DbReallocOrFree(pParse->db, pLevel->aInLoop,
sizeof(pLevel->aInLoop[0])*pLevel->nIn);
pIn = pLevel->aInLoop;
if( pIn ){
pIn += pLevel->nIn - 1;
pIn->iCur = iTab;
- pIn->topAddr = sqlite3VdbeAddOp(v, OP_Column, iTab, 0);
- sqlite3VdbeAddOp(v, OP_IsNull, -1, 0);
+ if( eType==IN_INDEX_ROWID ){
+ pIn->topAddr = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
+ }else{
+ pIn->topAddr = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
+ }
+ sqlite3VdbeAddOp1(v, OP_IsNull, iReg);
}else{
pLevel->nIn = 0;
}
#endif
}
disableTerm(pLevel, pTerm);
+ return iReg;
}
/*
@@ -1775,55 +1849,50 @@ static void codeEqualityTerm(
** this routine allocates an additional nEq memory cells for internal
** use.
*/
-static void codeAllEqualityTerms(
+static int codeAllEqualityTerms(
Parse *pParse, /* Parsing context */
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
WhereClause *pWC, /* The WHERE clause */
- Bitmask notReady /* Which parts of FROM have not yet been coded */
+ Bitmask notReady, /* Which parts of FROM have not yet been coded */
+ int nExtraReg /* Number of extra registers to allocate */
){
int nEq = pLevel->nEq; /* The number of == or IN constraints to code */
- int termsInMem = 0; /* If true, store value in mem[] cells */
Vdbe *v = pParse->pVdbe; /* The virtual machine under construction */
Index *pIdx = pLevel->pIdx; /* The index being used for this loop */
int iCur = pLevel->iTabCur; /* The cursor of the table */
WhereTerm *pTerm; /* A single constraint term */
int j; /* Loop counter */
+ int regBase; /* Base register */
/* Figure out how many memory cells we will need then allocate them.
** We always need at least one used to store the loop terminator
** value. If there are IN operators we'll need one for each == or
** IN constraint.
*/
- pLevel->iMem = pParse->nMem++;
- if( pLevel->flags & WHERE_COLUMN_IN ){
- pParse->nMem += pLevel->nEq;
- termsInMem = 1;
- }
+ pLevel->iMem = pParse->nMem + 1;
+ regBase = pParse->nMem + 2;
+ pParse->nMem += pLevel->nEq + 2 + nExtraReg;
/* Evaluate the equality constraints
*/
assert( pIdx->nColumn>=nEq );
for(j=0; j<nEq; j++){
+ int r1;
int k = pIdx->aiColumn[j];
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->flags, pIdx);
- if( pTerm==0 ) break;
+ if( NEVER(pTerm==0) ) break;
assert( (pTerm->flags & TERM_CODED)==0 );
- codeEqualityTerm(pParse, pTerm, pLevel);
- if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
- sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), pLevel->brk);
- }
- if( termsInMem ){
- sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem+j+1, 1);
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j);
+ if( r1!=regBase+j ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
}
- }
-
- /* Make sure all the constraint values are on the top of the stack
- */
- if( termsInMem ){
- for(j=0; j<nEq; j++){
- sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem+j+1, 0);
+ testcase( pTerm->eOperator & WO_ISNULL );
+ testcase( pTerm->eOperator & WO_IN );
+ if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->brk);
}
}
+ return regBase;
}
#if defined(SQLITE_TEST)
@@ -1845,20 +1914,15 @@ static int nQPlan = 0; /* Next free slow in _query_plan[] */
static void whereInfoFree(WhereInfo *pWInfo){
if( pWInfo ){
int i;
+ sqlite3 *db = pWInfo->pParse->db;
for(i=0; i<pWInfo->nLevel; i++){
sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
if( pInfo ){
- if( pInfo->needToFreeIdxStr ){
- /* Coverage: Don't think this can be reached. By the time this
- ** function is called, the index-strings have been passed
- ** to the vdbe layer for deletion.
- */
- sqlite3_free(pInfo->idxStr);
- }
- sqliteFree(pInfo);
+ assert( pInfo->needToFreeIdxStr==0 );
+ sqlite3DbFree(db, pInfo);
}
}
- sqliteFree(pWInfo);
+ sqlite3DbFree(db, pWInfo);
}
}
@@ -1955,7 +2019,8 @@ WhereInfo *sqlite3WhereBegin(
Parse *pParse, /* The parser context */
SrcList *pTabList, /* A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */
- ExprList **ppOrderBy /* An ORDER BY clause, or NULL */
+ ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
+ u8 wflags /* One of the WHERE_* flags defined in sqliteInt.h */
){
int i; /* Loop counter */
WhereInfo *pWInfo; /* Will become the return value of this function */
@@ -1969,6 +2034,8 @@ WhereInfo *sqlite3WhereBegin(
WhereLevel *pLevel; /* A single level in the pWInfo list */
int iFrom; /* First unused FROM clause element */
int andFlags; /* AND-ed combination of all wc.a[].flags */
+ sqlite3 *db; /* Database connection */
+ ExprList *pOrderBy = 0;
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -1978,18 +2045,25 @@ WhereInfo *sqlite3WhereBegin(
return 0;
}
+ if( ppOrderBy ){
+ pOrderBy = *ppOrderBy;
+ }
+
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
initMaskSet(&maskSet);
whereClauseInit(&wc, pParse, &maskSet);
+ sqlite3ExprCodeConstants(pParse, pWhere);
whereSplit(&wc, pWhere, TK_AND);
/* Allocate and initialize the WhereInfo structure that will become the
** return value.
*/
- pWInfo = sqliteMalloc( sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel));
- if( sqlite3MallocFailed() ){
+ db = pParse->db;
+ pWInfo = sqlite3DbMallocZero(db,
+ sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel));
+ if( db->mallocFailed ){
goto whereBeginNoMem;
}
pWInfo->nLevel = pTabList->nSrc;
@@ -2001,20 +2075,42 @@ WhereInfo *sqlite3WhereBegin(
** expression and either jump over all of the code or fall thru.
*/
if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
- sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
+ sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
pWhere = 0;
}
+ /* Assign a bit from the bitmask to every term in the FROM clause.
+ **
+ ** When assigning bitmask values to FROM clause cursors, it must be
+ ** the case that if X is the bitmask for the N-th FROM clause term then
+ ** the bitmask for all FROM clause terms to the left of the N-th term
+ ** is (X-1). An expression from the ON clause of a LEFT JOIN can use
+ ** its Expr.iRightJoinTable value to find the bitmask of the right table
+ ** of the join. Subtracting one from the right table bitmask gives a
+ ** bitmask for all tables to the left of the join. Knowing the bitmask
+ ** for all tables to the left of a left join is important. Ticket #3015.
+ */
+ for(i=0; i<pTabList->nSrc; i++){
+ createMask(&maskSet, pTabList->a[i].iCursor);
+ }
+#ifndef NDEBUG
+ {
+ Bitmask toTheLeft = 0;
+ for(i=0; i<pTabList->nSrc; i++){
+ Bitmask m = getMask(&maskSet, pTabList->a[i].iCursor);
+ assert( (m-1)==toTheLeft );
+ toTheLeft |= m;
+ }
+ }
+#endif
+
/* Analyze all of the subexpressions. Note that exprAnalyze() might
** add new virtual terms onto the end of the WHERE clause. We do not
** want to analyze these virtual terms, so start analyzing at the end
** and work forward so that the added virtual terms are never processed.
*/
- for(i=0; i<pTabList->nSrc; i++){
- createMask(&maskSet, pTabList->a[i].iCursor);
- }
exprAnalyzeAll(pTabList, &wc);
- if( sqlite3MallocFailed() ){
+ if( db->mallocFailed ){
goto whereBeginNoMem;
}
@@ -2104,7 +2200,7 @@ WhereInfo *sqlite3WhereBegin(
}
if( doNotReorder ) break;
}
- WHERETRACE(("*** Optimizer choose table %d for loop %d\n", bestJ,
+ WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ,
pLevel-pWInfo->a));
if( (bestFlags & WHERE_ORDERBY)!=0 ){
*ppOrderBy = 0;
@@ -2132,6 +2228,17 @@ WhereInfo *sqlite3WhereBegin(
*ppOrderBy = 0;
}
+ /* If the caller is an UPDATE or DELETE statement that is requesting
+ ** to use a one-pass algorithm, determine if this is appropriate.
+ ** The one-pass algorithm only works if the WHERE clause constraints
+ ** the statement to update a single row.
+ */
+ assert( (wflags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
+ if( (wflags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){
+ pWInfo->okOnePass = 1;
+ pWInfo->a[0].flags &= ~WHERE_IDX_ONLY;
+ }
+
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
@@ -2146,26 +2253,26 @@ WhereInfo *sqlite3WhereBegin(
if( pParse->explain==2 ){
char *zMsg;
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
- zMsg = sqlite3MPrintf("TABLE %s", pItem->zName);
+ zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName);
if( pItem->zAlias ){
- zMsg = sqlite3MPrintf("%z AS %s", zMsg, pItem->zAlias);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
}
if( (pIx = pLevel->pIdx)!=0 ){
- zMsg = sqlite3MPrintf("%z WITH INDEX %s", zMsg, pIx->zName);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s", zMsg, pIx->zName);
}else if( pLevel->flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
- zMsg = sqlite3MPrintf("%z USING PRIMARY KEY", zMsg);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( pLevel->pBestIdx ){
sqlite3_index_info *pBestIdx = pLevel->pBestIdx;
- zMsg = sqlite3MPrintf("%z VIRTUAL TABLE INDEX %d:%s", zMsg,
+ zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
pBestIdx->idxNum, pBestIdx->idxStr);
}
#endif
if( pLevel->flags & WHERE_ORDERBY ){
- zMsg = sqlite3MPrintf("%z ORDER BY", zMsg);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg);
}
- sqlite3VdbeOp3(v, OP_Explain, i, pLevel->iFrom, zMsg, P3_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC);
}
#endif /* SQLITE_OMIT_EXPLAIN */
pTabItem = &pTabList->a[pLevel->iFrom];
@@ -2175,16 +2282,18 @@ WhereInfo *sqlite3WhereBegin(
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pLevel->pBestIdx ){
int iCur = pTabItem->iCursor;
- sqlite3VdbeOp3(v, OP_VOpen, iCur, 0, (const char*)pTab->pVtab, P3_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0,
+ (const char*)pTab->pVtab, P4_VTAB);
}else
#endif
if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
- sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, OP_OpenRead);
- if( pTab->nCol<(sizeof(Bitmask)*8) ){
+ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
+ sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
+ if( !pWInfo->okOnePass && pTab->nCol<(sizeof(Bitmask)*8) ){
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
- sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-1, n);
+ sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-2, n);
assert( n<=pTab->nCol );
}
}else{
@@ -2194,15 +2303,10 @@ WhereInfo *sqlite3WhereBegin(
if( (pIx = pLevel->pIdx)!=0 ){
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
assert( pIx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- VdbeComment((v, "# %s", pIx->zName));
- sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
- (char*)pKey, P3_KEYINFO_HANDOFF);
- }
- if( (pLevel->flags & (WHERE_IDX_ONLY|WHERE_COLUMN_RANGE))!=0 ){
- /* Only call OP_SetNumColumns on the index if we might later use
- ** OP_Column on the index. */
- sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
+ sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1);
+ sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
+ (char*)pKey, P4_KEYINFO_HANDOFF);
+ VdbeComment((v, "%s", pIx->zName));
}
sqlite3CodeVerifySchema(pParse, iDb);
}
@@ -2247,10 +2351,9 @@ WhereInfo *sqlite3WhereBegin(
** row of the left table of the join.
*/
if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){
- if( !pParse->nMem ) pParse->nMem++;
- pLevel->iLeftJoin = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, pLevel->iLeftJoin);
- VdbeComment((v, "# init LEFT JOIN no-match flag"));
+ pLevel->iLeftJoin = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
+ VdbeComment((v, "init LEFT JOIN no-match flag"));
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -2259,6 +2362,7 @@ WhereInfo *sqlite3WhereBegin(
** to access the data.
*/
int j;
+ int iReg; /* P3 Value for OP_VFilter */
sqlite3_index_info *pBestIdx = pLevel->pBestIdx;
int nConstraint = pBestIdx->nConstraint;
struct sqlite3_index_constraint_usage *aUsage =
@@ -2266,23 +2370,29 @@ WhereInfo *sqlite3WhereBegin(
const struct sqlite3_index_constraint *aConstraint =
pBestIdx->aConstraint;
+ iReg = sqlite3GetTempRange(pParse, nConstraint+2);
+ pParse->disableColCache++;
for(j=1; j<=nConstraint; j++){
int k;
for(k=0; k<nConstraint; k++){
if( aUsage[k].argvIndex==j ){
int iTerm = aConstraint[k].iTermOffset;
- sqlite3ExprCode(pParse, wc.a[iTerm].pExpr->pRight);
+ assert( pParse->disableColCache );
+ sqlite3ExprCode(pParse, wc.a[iTerm].pExpr->pRight, iReg+j+1);
break;
}
}
if( k==nConstraint ) break;
}
- sqlite3VdbeAddOp(v, OP_Integer, j-1, 0);
- sqlite3VdbeAddOp(v, OP_Integer, pBestIdx->idxNum, 0);
- sqlite3VdbeOp3(v, OP_VFilter, iCur, brk, pBestIdx->idxStr,
- pBestIdx->needToFreeIdxStr ? P3_MPRINTF : P3_STATIC);
+ assert( pParse->disableColCache );
+ pParse->disableColCache--;
+ sqlite3VdbeAddOp2(v, OP_Integer, pBestIdx->idxNum, iReg);
+ sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, brk, iReg, pBestIdx->idxStr,
+ pBestIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
+ sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
pBestIdx->needToFreeIdxStr = 0;
- for(j=0; j<pBestIdx->nConstraint; j++){
+ for(j=0; j<nConstraint; j++){
if( aUsage[j].omit ){
int iTerm = aConstraint[j].iTermOffset;
disableTerm(pLevel, &wc.a[iTerm]);
@@ -2300,15 +2410,16 @@ WhereInfo *sqlite3WhereBegin(
** we reference multiple rows using a "rowid IN (...)"
** construct.
*/
+ int r1;
pTerm = findTerm(&wc, iCur, -1, notReady, WO_EQ|WO_IN, 0);
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
assert( pTerm->leftCursor==iCur );
assert( omitTable==0 );
- codeEqualityTerm(pParse, pTerm, pLevel);
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, 0);
nxt = pLevel->nxt;
- sqlite3VdbeAddOp(v, OP_MustBeInt, 1, nxt);
- sqlite3VdbeAddOp(v, OP_NotExists, iCur, nxt);
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, nxt);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, nxt, r1);
VdbeComment((v, "pk"));
pLevel->op = OP_Noop;
}else if( pLevel->flags & WHERE_ROWID_RANGE ){
@@ -2328,25 +2439,27 @@ WhereInfo *sqlite3WhereBegin(
}
if( pStart ){
Expr *pX;
+ int r1, regFree1;
pX = pStart->pExpr;
assert( pX!=0 );
assert( pStart->leftCursor==iCur );
- sqlite3ExprCode(pParse, pX->pRight);
- sqlite3VdbeAddOp(v, OP_ForceInt, pX->op==TK_LE || pX->op==TK_GT, brk);
- sqlite3VdbeAddOp(v, bRev ? OP_MoveLt : OP_MoveGe, iCur, brk);
+ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_ForceInt, r1, brk,
+ pX->op==TK_LE || pX->op==TK_GT);
+ sqlite3VdbeAddOp3(v, bRev ? OP_MoveLt : OP_MoveGe, iCur, brk, r1);
VdbeComment((v, "pk"));
+ sqlite3ReleaseTempReg(pParse, regFree1);
disableTerm(pLevel, pStart);
}else{
- sqlite3VdbeAddOp(v, bRev ? OP_Last : OP_Rewind, iCur, brk);
+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, brk);
}
if( pEnd ){
Expr *pX;
pX = pEnd->pExpr;
assert( pX!=0 );
assert( pEnd->leftCursor==iCur );
- sqlite3ExprCode(pParse, pX->pRight);
- pLevel->iMem = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+ pLevel->iMem = ++pParse->nMem;
+ sqlite3ExprCode(pParse, pX->pRight, pLevel->iMem);
if( pX->op==TK_LT || pX->op==TK_GT ){
testOp = bRev ? OP_Le : OP_Ge;
}else{
@@ -2359,214 +2472,210 @@ WhereInfo *sqlite3WhereBegin(
pLevel->p1 = iCur;
pLevel->p2 = start;
if( testOp!=OP_Noop ){
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
- sqlite3VdbeAddOp(v, testOp, SQLITE_AFF_NUMERIC|0x100, brk);
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
+ /* sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0); */
+ sqlite3VdbeAddOp3(v, testOp, pLevel->iMem, brk, r1);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+ sqlite3ReleaseTempReg(pParse, r1);
}
- }else if( pLevel->flags & WHERE_COLUMN_RANGE ){
- /* Case 3: The WHERE clause term that refers to the right-most
- ** column of the index is an inequality. For example, if
- ** the index is on (x,y,z) and the WHERE clause is of the
- ** form "x=5 AND y<10" then this case is used. Only the
- ** right-most column can be an inequality - the rest must
- ** use the "==" and "IN" operators.
+ }else if( pLevel->flags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
+ /* Case 3: A scan using an index.
+ **
+ ** The WHERE clause may contain zero or more equality
+ ** terms ("==" or "IN" operators) that refer to the N
+ ** left-most columns of the index. It may also contain
+ ** inequality constraints (>, <, >= or <=) on the indexed
+ ** column that immediately follows the N equalities. Only
+ ** the right-most column can be an inequality - the rest must
+ ** use the "==" and "IN" operators. For example, if the
+ ** index is on (x,y,z), then the following clauses are all
+ ** optimized:
+ **
+ ** x=5
+ ** x=5 AND y=10
+ ** x=5 AND y<10
+ ** x=5 AND y>5 AND y<10
+ ** x=5 AND y=5 AND z<=10
+ **
+ ** The z<10 term of the following cannot be used, only
+ ** the x=5 term:
+ **
+ ** x=5 AND z<10
+ **
+ ** N may be zero if there are inequality constraints.
+ ** If there are no inequality constraints, then N is at
+ ** least one.
**
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
- */
- int start;
+ */
+ int aStartOp[] = {
+ 0,
+ 0,
+ OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
+ OP_Last, /* 3: (!start_constraints && startEq && bRev) */
+ OP_MoveGt, /* 4: (start_constraints && !startEq && !bRev) */
+ OP_MoveLt, /* 5: (start_constraints && !startEq && bRev) */
+ OP_MoveGe, /* 6: (start_constraints && startEq && !bRev) */
+ OP_MoveLe /* 7: (start_constraints && startEq && bRev) */
+ };
+ int aEndOp[] = {
+ OP_Noop, /* 0: (!end_constraints) */
+ OP_IdxGE, /* 1: (end_constraints && !bRev) */
+ OP_IdxLT /* 2: (end_constraints && bRev) */
+ };
int nEq = pLevel->nEq;
- int topEq=0; /* True if top limit uses ==. False is strictly < */
- int btmEq=0; /* True if btm limit uses ==. False if strictly > */
- int topOp, btmOp; /* Operators for the top and bottom search bounds */
- int testOp;
- int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0;
- int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0;
+ int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
+ int regBase; /* Base register holding constraint values */
+ int r1; /* Temp register */
+ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
+ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
+ int startEq; /* True if range start uses ==, >= or <= */
+ int endEq; /* True if range end uses ==, >= or <= */
+ int start_constraints; /* Start of range is constrained */
+ int k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */
+ int nConstraint; /* Number of constraint terms */
+ int op;
/* Generate code to evaluate all constraint terms using == or IN
- ** and level the values of those terms on the stack.
+ ** and store the values of those terms in an array of registers
+ ** starting at regBase.
*/
- codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
+ regBase = codeAllEqualityTerms(pParse, pLevel, &wc, notReady, 2);
+ nxt = pLevel->nxt;
- /* Duplicate the equality term values because they will all be
- ** used twice: once to make the termination key and once to make the
- ** start key.
+ /* If this loop satisfies a sort order (pOrderBy) request that
+ ** was passed to this function to implement a "SELECT min(x) ..."
+ ** query, then the caller will only allow the loop to run for
+ ** a single iteration. This means that the first row returned
+ ** should not have a NULL value stored in 'x'. If column 'x' is
+ ** the first one after the nEq equality constraints in the index,
+ ** this requires some special handling.
*/
- for(j=0; j<nEq; j++){
- sqlite3VdbeAddOp(v, OP_Dup, nEq-1, 0);
+ if( (wflags&WHERE_ORDERBY_MIN)!=0
+ && (pLevel->flags&WHERE_ORDERBY)
+ && (pIdx->nColumn>nEq)
+ ){
+ assert( pOrderBy->nExpr==1 );
+ assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] );
+ isMinQuery = 1;
}
- /* Figure out what comparison operators to use for top and bottom
- ** search bounds. For an ascending index, the bottom bound is a > or >=
- ** operator and the top bound is a < or <= operator. For a descending
- ** index the operators are reversed.
+ /* Find any inequality constraint terms for the start and end
+ ** of the range.
*/
- if( pIdx->aSortOrder[nEq]==SQLITE_SO_ASC ){
- topOp = WO_LT|WO_LE;
- btmOp = WO_GT|WO_GE;
- }else{
- topOp = WO_GT|WO_GE;
- btmOp = WO_LT|WO_LE;
- SWAP(int, topLimit, btmLimit);
+ if( pLevel->flags & WHERE_TOP_LIMIT ){
+ pRangeEnd = findTerm(&wc, iCur, k, notReady, (WO_LT|WO_LE), pIdx);
+ }
+ if( pLevel->flags & WHERE_BTM_LIMIT ){
+ pRangeStart = findTerm(&wc, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
}
- /* Generate the termination key. This is the key value that
- ** will end the search. There is no termination key if there
- ** are no equality terms and no "X<..." term.
- **
- ** 2002-Dec-04: On a reverse-order scan, the so-called "termination"
- ** key computed here really ends up being the start key.
+ /* If we are doing a reverse order scan on an ascending index, or
+ ** a forward order scan on a descending index, interchange the
+ ** start and end terms (pRangeStart and pRangeEnd).
*/
- nxt = pLevel->nxt;
- if( topLimit ){
- Expr *pX;
- int k = pIdx->aiColumn[j];
- pTerm = findTerm(&wc, iCur, k, notReady, topOp, pIdx);
- assert( pTerm!=0 );
- pX = pTerm->pExpr;
- assert( (pTerm->flags & TERM_CODED)==0 );
- sqlite3ExprCode(pParse, pX->pRight);
- sqlite3VdbeAddOp(v, OP_IsNull, -(nEq*2+1), nxt);
- topEq = pTerm->eOperator & (WO_LE|WO_GE);
- disableTerm(pLevel, pTerm);
- testOp = OP_IdxGE;
- }else{
- testOp = nEq>0 ? OP_IdxGE : OP_Noop;
- topEq = 1;
+ if( bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC) ){
+ SWAP(WhereTerm *, pRangeEnd, pRangeStart);
}
- if( testOp!=OP_Noop ){
- int nCol = nEq + topLimit;
- pLevel->iMem = pParse->nMem++;
- buildIndexProbe(v, nCol, pIdx);
- if( bRev ){
- int op = topEq ? OP_MoveLe : OP_MoveLt;
- sqlite3VdbeAddOp(v, op, iIdxCur, nxt);
- }else{
- sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+
+ testcase( pRangeStart && pRangeStart->eOperator & WO_LE );
+ testcase( pRangeStart && pRangeStart->eOperator & WO_GE );
+ testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE );
+ testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE );
+ startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
+ endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
+ start_constraints = pRangeStart || nEq>0;
+
+ /* Seek the index cursor to the start of the range. */
+ nConstraint = nEq;
+ if( pRangeStart ){
+ int dcc = pParse->disableColCache;
+ if( pRangeEnd ){
+ pParse->disableColCache++;
}
- }else if( bRev ){
- sqlite3VdbeAddOp(v, OP_Last, iIdxCur, brk);
+ sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq);
+ pParse->disableColCache = dcc;
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
+ nConstraint++;
+ }else if( isMinQuery ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ nConstraint++;
+ startEq = 0;
+ start_constraints = 1;
}
-
- /* Generate the start key. This is the key that defines the lower
- ** bound on the search. There is no start key if there are no
- ** equality terms and if there is no "X>..." term. In
- ** that case, generate a "Rewind" instruction in place of the
- ** start key search.
- **
- ** 2002-Dec-04: In the case of a reverse-order search, the so-called
- ** "start" key really ends up being used as the termination key.
+ codeApplyAffinity(pParse, regBase, nConstraint, pIdx);
+ op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
+ assert( op!=0 );
+ testcase( op==OP_Rewind );
+ testcase( op==OP_Last );
+ testcase( op==OP_MoveGt );
+ testcase( op==OP_MoveGe );
+ testcase( op==OP_MoveLe );
+ testcase( op==OP_MoveLt );
+ sqlite3VdbeAddOp4(v, op, iIdxCur, nxt, regBase,
+ SQLITE_INT_TO_PTR(nConstraint), P4_INT32);
+
+ /* Load the value for the inequality constraint at the end of the
+ ** range (if any).
*/
- if( btmLimit ){
- Expr *pX;
- int k = pIdx->aiColumn[j];
- pTerm = findTerm(&wc, iCur, k, notReady, btmOp, pIdx);
- assert( pTerm!=0 );
- pX = pTerm->pExpr;
- assert( (pTerm->flags & TERM_CODED)==0 );
- sqlite3ExprCode(pParse, pX->pRight);
- sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), nxt);
- btmEq = pTerm->eOperator & (WO_LE|WO_GE);
- disableTerm(pLevel, pTerm);
- }else{
- btmEq = 1;
- }
- if( nEq>0 || btmLimit ){
- int nCol = nEq + btmLimit;
- buildIndexProbe(v, nCol, pIdx);
- if( bRev ){
- pLevel->iMem = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
- testOp = OP_IdxLT;
- }else{
- int op = btmEq ? OP_MoveGe : OP_MoveGt;
- sqlite3VdbeAddOp(v, op, iIdxCur, nxt);
- }
- }else if( bRev ){
- testOp = OP_Noop;
- }else{
- sqlite3VdbeAddOp(v, OP_Rewind, iIdxCur, brk);
+ nConstraint = nEq;
+ if( pRangeEnd ){
+ sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, nxt);
+ codeApplyAffinity(pParse, regBase, nEq+1, pIdx);
+ nConstraint++;
}
- /* Generate the the top of the loop. If there is a termination
- ** key we have to test for that key and abort at the top of the
- ** loop.
+ /* Top of the loop body */
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+
+ /* Check if the index cursor is past the end of the range. */
+ op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)];
+ testcase( op==OP_Noop );
+ testcase( op==OP_IdxGE );
+ testcase( op==OP_IdxLT );
+ sqlite3VdbeAddOp4(v, op, iIdxCur, nxt, regBase,
+ SQLITE_INT_TO_PTR(nConstraint), P4_INT32);
+ sqlite3VdbeChangeP5(v, endEq!=bRev);
+
+ /* If there are inequality constraints, check that the value
+ ** of the table column that the inequality contrains is not NULL.
+ ** If it is, jump to the next iteration of the loop.
*/
- start = sqlite3VdbeCurrentAddr(v);
- if( testOp!=OP_Noop ){
- sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
- sqlite3VdbeAddOp(v, testOp, iIdxCur, nxt);
- if( (topEq && !bRev) || (!btmEq && bRev) ){
- sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
- }
- }
- if( topLimit | btmLimit ){
- sqlite3VdbeAddOp(v, OP_Column, iIdxCur, nEq);
- sqlite3VdbeAddOp(v, OP_IsNull, 1, cont);
+ r1 = sqlite3GetTempReg(pParse);
+ testcase( pLevel->flags & WHERE_BTM_LIMIT );
+ testcase( pLevel->flags & WHERE_TOP_LIMIT );
+ if( pLevel->flags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
+ sqlite3VdbeAddOp2(v, OP_IsNull, r1, cont);
}
+
+ /* Seek the table cursor, if required */
if( !omitTable ){
- sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0);
- sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, r1);
+ sqlite3VdbeAddOp3(v, OP_MoveGe, iCur, 0, r1); /* Deferred seek */
}
+ sqlite3ReleaseTempReg(pParse, r1);
- /* Record the instruction used to terminate the loop.
+ /* Record the instruction used to terminate the loop. Disable
+ ** WHERE clause terms made redundant by the index range scan.
*/
pLevel->op = bRev ? OP_Prev : OP_Next;
pLevel->p1 = iIdxCur;
- pLevel->p2 = start;
- }else if( pLevel->flags & WHERE_COLUMN_EQ ){
- /* Case 4: There is an index and all terms of the WHERE clause that
- ** refer to the index using the "==" or "IN" operators.
- */
- int start;
- int nEq = pLevel->nEq;
-
- /* Generate code to evaluate all constraint terms using == or IN
- ** and leave the values of those terms on the stack.
- */
- codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
- nxt = pLevel->nxt;
-
- /* Generate a single key that will be used to both start and terminate
- ** the search
- */
- buildIndexProbe(v, nEq, pIdx);
- sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
-
- /* Generate code (1) to move to the first matching element of the table.
- ** Then generate code (2) that jumps to "nxt" after the cursor is past
- ** the last matching element of the table. The code (1) is executed
- ** once to initialize the search, the code (2) is executed before each
- ** iteration of the scan to see if the scan has finished. */
- if( bRev ){
- /* Scan in reverse order */
- sqlite3VdbeAddOp(v, OP_MoveLe, iIdxCur, nxt);
- start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
- sqlite3VdbeAddOp(v, OP_IdxLT, iIdxCur, nxt);
- pLevel->op = OP_Prev;
- }else{
- /* Scan in the forward order */
- sqlite3VdbeAddOp(v, OP_MoveGe, iIdxCur, nxt);
- start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
- sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, nxt, "+", P3_STATIC);
- pLevel->op = OP_Next;
- }
- if( !omitTable ){
- sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0);
- sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
- }
- pLevel->p1 = iIdxCur;
- pLevel->p2 = start;
+ disableTerm(pLevel, pRangeStart);
+ disableTerm(pLevel, pRangeEnd);
}else{
- /* Case 5: There is no usable index. We must do a complete
+ /* Case 4: There is no usable index. We must do a complete
** scan of the entire table.
*/
assert( omitTable==0 );
assert( bRev==0 );
pLevel->op = OP_Next;
pLevel->p1 = iCur;
- pLevel->p2 = 1 + sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk);
+ pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, brk);
}
notReady &= ~getMask(&maskSet, iCur);
@@ -2575,6 +2684,8 @@ WhereInfo *sqlite3WhereBegin(
*/
for(pTerm=wc.a, j=wc.nTerm; j>0; j--, pTerm++){
Expr *pE;
+ testcase( pTerm->flags & TERM_VIRTUAL );
+ testcase( pTerm->flags & TERM_CODED );
if( pTerm->flags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( (pTerm->prereqAll & notReady)!=0 ) continue;
pE = pTerm->pExpr;
@@ -2582,7 +2693,7 @@ WhereInfo *sqlite3WhereBegin(
if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
continue;
}
- sqlite3ExprIfFalse(pParse, pE, cont, 1);
+ sqlite3ExprIfFalse(pParse, pE, cont, SQLITE_JUMPIFNULL);
pTerm->flags |= TERM_CODED;
}
@@ -2591,13 +2702,17 @@ WhereInfo *sqlite3WhereBegin(
*/
if( pLevel->iLeftJoin ){
pLevel->top = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_MemInt, 1, pLevel->iLeftJoin);
- VdbeComment((v, "# record LEFT JOIN hit"));
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
+ VdbeComment((v, "record LEFT JOIN hit"));
+ sqlite3ExprClearColumnCache(pParse, pLevel->iTabCur);
+ sqlite3ExprClearColumnCache(pParse, pLevel->iIdxCur);
for(pTerm=wc.a, j=0; j<wc.nTerm; j++, pTerm++){
+ testcase( pTerm->flags & TERM_VIRTUAL );
+ testcase( pTerm->flags & TERM_CODED );
if( pTerm->flags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( (pTerm->prereqAll & notReady)!=0 ) continue;
assert( pTerm->pExpr );
- sqlite3ExprIfFalse(pParse, pTerm->pExpr, cont, 1);
+ sqlite3ExprIfFalse(pParse, pTerm->pExpr, cont, SQLITE_JUMPIFNULL);
pTerm->flags |= TERM_CODED;
}
}
@@ -2628,6 +2743,8 @@ WhereInfo *sqlite3WhereBegin(
}
sqlite3_query_plan[nQPlan++] = ' ';
}
+ testcase( pLevel->flags & WHERE_ROWID_EQ );
+ testcase( pLevel->flags & WHERE_ROWID_RANGE );
if( pLevel->flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
memcpy(&sqlite3_query_plan[nQPlan], "* ", 2);
nQPlan += 2;
@@ -2669,18 +2786,21 @@ whereBeginNoMem:
** sqlite3WhereBegin() for additional information.
*/
void sqlite3WhereEnd(WhereInfo *pWInfo){
- Vdbe *v = pWInfo->pParse->pVdbe;
+ Parse *pParse = pWInfo->pParse;
+ Vdbe *v = pParse->pVdbe;
int i;
WhereLevel *pLevel;
SrcList *pTabList = pWInfo->pTabList;
+ sqlite3 *db = pParse->db;
/* Generate loop termination code.
*/
+ sqlite3ExprClearColumnCache(pParse, -1);
for(i=pTabList->nSrc-1; i>=0; i--){
pLevel = &pWInfo->a[i];
sqlite3VdbeResolveLabel(v, pLevel->cont);
if( pLevel->op!=OP_Noop ){
- sqlite3VdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
+ sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2);
}
if( pLevel->nIn ){
struct InLoop *pIn;
@@ -2688,20 +2808,20 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeResolveLabel(v, pLevel->nxt);
for(j=pLevel->nIn, pIn=&pLevel->aInLoop[j-1]; j>0; j--, pIn--){
sqlite3VdbeJumpHere(v, pIn->topAddr+1);
- sqlite3VdbeAddOp(v, OP_Next, pIn->iCur, pIn->topAddr);
+ sqlite3VdbeAddOp2(v, OP_Next, pIn->iCur, pIn->topAddr);
sqlite3VdbeJumpHere(v, pIn->topAddr-1);
}
- sqliteFree(pLevel->aInLoop);
+ sqlite3DbFree(db, pLevel->aInLoop);
}
sqlite3VdbeResolveLabel(v, pLevel->brk);
if( pLevel->iLeftJoin ){
int addr;
- addr = sqlite3VdbeAddOp(v, OP_IfMemPos, pLevel->iLeftJoin, 0);
- sqlite3VdbeAddOp(v, OP_NullRow, pTabList->a[i].iCursor, 0);
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin);
+ sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
if( pLevel->iIdxCur>=0 ){
- sqlite3VdbeAddOp(v, OP_NullRow, pLevel->iIdxCur, 0);
+ sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
- sqlite3VdbeAddOp(v, OP_Goto, 0, pLevel->top);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->top);
sqlite3VdbeJumpHere(v, addr);
}
}
@@ -2718,15 +2838,19 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
if( pTab->isEphem || pTab->pSelect ) continue;
- if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
- sqlite3VdbeAddOp(v, OP_Close, pTabItem->iCursor, 0);
+ if( !pWInfo->okOnePass && (pLevel->flags & WHERE_IDX_ONLY)==0 ){
+ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
if( pLevel->pIdx!=0 ){
- sqlite3VdbeAddOp(v, OP_Close, pLevel->iIdxCur, 0);
+ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
- /* Make cursor substitutions for cases where we want to use
- ** just the index and never reference the table.
+ /* If this scan uses an index, make code substitutions to read data
+ ** from the index in preference to the table. Sometimes, this means
+ ** the table need never be read from. This is a performance boost,
+ ** as the vdbe level waits until the table is read before actually
+ ** seeking the table cursor to the record corresponding to the current
+ ** position in the index.
**
** Calls to the code generator in between sqlite3WhereBegin and
** sqlite3WhereEnd will have created code that references the table
@@ -2734,10 +2858,11 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( pLevel->flags & WHERE_IDX_ONLY ){
+ if( pLevel->pIdx ){
int k, j, last;
VdbeOp *pOp;
Index *pIdx = pLevel->pIdx;
+ int useIndexOnly = pLevel->flags & WHERE_IDX_ONLY;
assert( pIdx!=0 );
pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
@@ -2745,17 +2870,18 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
for(k=pWInfo->iTop; k<last; k++, pOp++){
if( pOp->p1!=pLevel->iTabCur ) continue;
if( pOp->opcode==OP_Column ){
- pOp->p1 = pLevel->iIdxCur;
for(j=0; j<pIdx->nColumn; j++){
if( pOp->p2==pIdx->aiColumn[j] ){
pOp->p2 = j;
+ pOp->p1 = pLevel->iIdxCur;
break;
}
}
+ assert(!useIndexOnly || j<pIdx->nColumn);
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
- }else if( pOp->opcode==OP_NullRow ){
+ }else if( pOp->opcode==OP_NullRow && useIndexOnly ){
pOp->opcode = OP_Noop;
}
}
diff --git a/third_party/sqlite/tclinstaller.tcl b/third_party/sqlite/tclinstaller.tcl
new file mode 100755
index 0000000..29d343e
--- /dev/null
+++ b/third_party/sqlite/tclinstaller.tcl
@@ -0,0 +1,32 @@
+# This script attempts to install SQLite3 so that it can be used
+# by TCL. Invoke this script with single argument which is the
+# version number of SQLite. Example:
+#
+# tclsh tclinstaller.tcl 3.0
+#
+set VERSION [lindex $argv 0]
+set LIBFILE .libs/libtclsqlite3[info sharedlibextension]
+if { ![info exists env(DESTDIR)] } { set env(DESTDIR) "" }
+if { ![info exists env(TCLLIBDIR)] } { set env(TCLLIBDIR) [lindex $auto_path 0] }
+set LIBDIR $env(DESTDIR)$env(TCLLIBDIR)
+set LIBDIR_INSTALL $env(TCLLIBDIR)
+set LIBNAME [file tail $LIBFILE]
+set LIB $LIBDIR/sqlite3/$LIBNAME
+set LIB_INSTALL $LIBDIR_INSTALL/sqlite3/$LIBNAME
+
+file delete -force $LIBDIR/sqlite3
+file mkdir $LIBDIR/sqlite3
+set fd [open $LIBDIR/sqlite3/pkgIndex.tcl w]
+puts $fd "package ifneeded sqlite3 $VERSION \[list load $LIB_INSTALL sqlite3\]"
+close $fd
+
+# We cannot use [file copy] because that will just make a copy of
+# a symbolic link. We have to open and copy the file for ourselves.
+#
+set in [open $LIBFILE]
+fconfigure $in -translation binary
+set out [open $LIB w]
+fconfigure $out -translation binary
+puts -nonewline $out [read $in]
+close $in
+close $out
diff --git a/third_party/sqlite/test/aggerror.test b/third_party/sqlite/test/aggerror.test
new file mode 100755
index 0000000..f95d8b2
--- /dev/null
+++ b/third_party/sqlite/test/aggerror.test
@@ -0,0 +1,78 @@
+# 2006 January 20
+#
+# 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.
+#
+# This file implements tests for calling sqlite3_result_error()
+# from within an aggregate function implementation.
+#
+# $Id: aggerror.test,v 1.3 2006/05/03 23:34:06 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Add the x_count aggregate function to the database handle.
+# x_count will error out if its input is 40 or 41 or if its
+# final results is 42. Make sure that such errors are handled
+# appropriately.
+#
+do_test aggerror-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_create_aggregate $DB
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 SELECT a+2 FROM t1;
+ INSERT INTO t1 SELECT a+4 FROM t1;
+ INSERT INTO t1 SELECT a+8 FROM t1;
+ INSERT INTO t1 SELECT a+16 FROM t1;
+ INSERT INTO t1 SELECT a+32 FROM t1 ORDER BY a LIMIT 7;
+ SELECT x_count(*) FROM t1;
+ }
+} {39}
+do_test aggerror-1.2 {
+ execsql {
+ INSERT INTO t1 VALUES(40);
+ SELECT x_count(*) FROM t1;
+ }
+} {40}
+do_test aggerror-1.3 {
+ catchsql {
+ SELECT x_count(a) FROM t1;
+ }
+} {1 {value of 40 handed to x_count}}
+ifcapable utf16 {
+ do_test aggerror-1.4 {
+ execsql {
+ UPDATE t1 SET a=41 WHERE a=40
+ }
+ catchsql {
+ SELECT x_count(a) FROM t1;
+ }
+ } {1 abc}
+}
+do_test aggerror-1.5 {
+ execsql {
+ SELECT x_count(*) FROM t1
+ }
+} 40
+do_test aggerror-1.6 {
+ execsql {
+ INSERT INTO t1 VALUES(40);
+ INSERT INTO t1 VALUES(42);
+ }
+ catchsql {
+ SELECT x_count(*) FROM t1;
+ }
+} {1 {x_count totals to 42}}
+
+finish_test
diff --git a/third_party/sqlite/test/all.test b/third_party/sqlite/test/all.test
new file mode 100755
index 0000000..96d64ea
--- /dev/null
+++ b/third_party/sqlite/test/all.test
@@ -0,0 +1,149 @@
+# 2001 September 15
+#
+# 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 runs all tests.
+#
+# $Id: all.test,v 1.57 2008/07/14 15:11:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {
+ # no-op
+}
+
+if {[file exists ./sqlite_test_count]} {
+ set COUNT [exec cat ./sqlite_test_count]
+} else {
+ set COUNT 3
+}
+
+if {[llength $argv]>0} {
+ foreach {name value} $argv {
+ switch -- $name {
+ -count {
+ set COUNT $value
+ }
+ -quick {
+ set ISQUICK $value
+ }
+ -soak {
+ set SOAKTEST $value
+ }
+ default {
+ puts stderr "Unknown option: $name"
+ exit
+ }
+ }
+ }
+}
+set argv {}
+
+# LeakList will hold a list of the number of unfreed mallocs after
+# each round of the test. This number should be constant. If it
+# grows, it may mean there is a memory leak in the library.
+#
+set LeakList {}
+
+set EXCLUDE {}
+lappend EXCLUDE all.test ;# This file
+lappend EXCLUDE async.test
+lappend EXCLUDE crash.test ;# Run seperately later.
+lappend EXCLUDE crash2.test ;# Run seperately later.
+lappend EXCLUDE quick.test ;# Alternate test driver script
+lappend EXCLUDE veryquick.test ;# Alternate test driver script
+lappend EXCLUDE malloc.test ;# Run seperately later.
+lappend EXCLUDE misuse.test ;# Run seperately later.
+lappend EXCLUDE memleak.test ;# Alternate test driver script
+lappend EXCLUDE permutations.test ;# Run seperately later.
+lappend EXCLUDE fuzz.test
+lappend EXCLUDE soak.test ;# Takes a very long time (default 1 hr)
+lappend EXCLUDE fts3.test ;# Wrapper for muliple fts3*.tests
+lappend EXCLUDE mallocAll.test ;# Wrapper for running all malloc tests
+
+# Files to include in the test. If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
+for {set Counter 0} {$Counter<$COUNT && $nErr==0} {incr Counter} {
+ if {$Counter%2} {
+ set ::SETUP_SQL {PRAGMA default_synchronous=off;}
+ } else {
+ catch {unset ::SETUP_SQL}
+ }
+ foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
+ reset_prng_state
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+ }
+ if {[info exists Leak]} {
+ lappend LeakList $Leak
+ }
+}
+set argv all
+source $testdir/permutations.test
+set argv ""
+
+# Do one last test to look for a memory leak in the library. This will
+# only work if SQLite is compiled with the -DSQLITE_DEBUG=1 flag.
+#
+if {$LeakList!=""} {
+ puts -nonewline memory-leak-test...
+ incr ::nTest
+ foreach x $LeakList {
+ if {$x!=[lindex $LeakList 0]} {
+ puts " failed!"
+ puts "Expected: all values to be the same"
+ puts " Got: $LeakList"
+ incr ::nErr
+ lappend ::failList memory-leak-test
+ break
+ }
+ }
+ puts " Ok"
+}
+
+# Run the crashtest only on unix and only once. If the library does not
+# always create auto-vacuum databases, also run autovacuum_crash.test.
+#
+if {$::tcl_platform(platform)=="unix"} {
+ source $testdir/crash.test
+ source $testdir/crash2.test
+ ifcapable !default_autovacuum {
+ set argv autovacuum_crash
+ source $testdir/permutations.test
+ set argv ""
+ }
+}
+
+# Run the malloc tests and the misuse test after memory leak detection.
+# Both tests leak memory. Currently, misuse.test also leaks a handful of
+# file descriptors. This is not considered a problem, but can cause tests
+# in malloc.test to fail. So set the open-file count to zero before running
+# malloc.test to get around this.
+#
+catch {source $testdir/misuse.test}
+set sqlite_open_file_count 0
+catch {source $testdir/malloc.test}
+
+catch {db close}
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/alter.test b/third_party/sqlite/test/alter.test
new file mode 100755
index 0000000..002fb5a
--- /dev/null
+++ b/third_party/sqlite/test/alter.test
@@ -0,0 +1,814 @@
+# 2004 November 10
+#
+# 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 testing the ALTER TABLE statement.
+#
+# $Id: alter.test,v 1.30 2008/05/09 14:17:52 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------
+# Test organization:
+#
+# alter-1.1.* - alter-1.7.*: Basic tests of ALTER TABLE, including tables
+# with implicit and explicit indices. These tests came from an earlier
+# fork of SQLite that also supported ALTER TABLE.
+# alter-1.8.*: Tests for ALTER TABLE when the table resides in an
+# attached database.
+# alter-1.9.*: Tests for ALTER TABLE when their is whitespace between the
+# table name and left parenthesis token. i.e:
+# "CREATE TABLE abc (a, b, c);"
+# alter-2.*: Test error conditions and messages.
+# alter-3.*: Test ALTER TABLE on tables that have TRIGGERs attached to them.
+# alter-4.*: Test ALTER TABLE on tables that have AUTOINCREMENT fields.
+# ...
+# alter-12.*: Test ALTER TABLE on views.
+#
+
+# Create some tables to rename. Be sure to include some TEMP tables
+# and some tables with odd names.
+#
+do_test alter-1.1 {
+ ifcapable tempdb {
+ set ::temp TEMP
+ } else {
+ set ::temp {}
+ }
+ execsql [subst -nocommands {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY);
+ INSERT INTO [t1'x1] VALUES(3,4);
+ CREATE INDEX t1i1 ON T1(B);
+ CREATE INDEX t1i2 ON t1(a,b);
+ CREATE INDEX i3 ON [t1'x1](b,c);
+ CREATE $::temp TABLE "temp table"(e,f,g UNIQUE);
+ CREATE INDEX i2 ON [temp table](f);
+ INSERT INTO [temp table] VALUES(5,6,7);
+ }]
+ execsql {
+ SELECT 't1', * FROM t1;
+ SELECT 't1''x1', * FROM "t1'x1";
+ SELECT * FROM [temp table];
+ }
+} {t1 1 2 t1'x1 3 4 5 6 7}
+do_test alter-1.2 {
+ execsql [subst {
+ CREATE $::temp TABLE objlist(type, name, tbl_name);
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }]
+ ifcapable tempdb {
+ execsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_temp_master WHERE NAME!='objlist';
+ }
+ }
+
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table t1 t1 \
+ index t1i1 t1 \
+ index t1i2 t1 \
+ table t1'x1 t1'x1 \
+ index i3 t1'x1 \
+ index {sqlite_autoindex_t1'x1_1} t1'x1 \
+ index {sqlite_autoindex_t1'x1_2} t1'x1 \
+ table {temp table} {temp table} \
+ index i2 {temp table} \
+ index {sqlite_autoindex_temp table_1} {temp table} \
+ ]
+
+# Make some changes
+#
+integrity_check alter-1.3.0
+do_test alter-1.3 {
+ execsql {
+ ALTER TABLE [T1] RENAME to [-t1-];
+ ALTER TABLE "t1'x1" RENAME TO T2;
+ ALTER TABLE [temp table] RENAME to TempTab;
+ }
+} {}
+integrity_check alter-1.3.1
+do_test alter-1.4 {
+ execsql {
+ SELECT 't1', * FROM [-t1-];
+ SELECT 't2', * FROM t2;
+ SELECT * FROM temptab;
+ }
+} {t1 1 2 t2 3 4 5 6 7}
+do_test alter-1.5 {
+ execsql {
+ DELETE FROM objlist;
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }
+ catchsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_temp_master WHERE NAME!='objlist';
+ }
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table -t1- -t1- \
+ index t1i1 -t1- \
+ index t1i2 -t1- \
+ table T2 T2 \
+ index i3 T2 \
+ index {sqlite_autoindex_T2_1} T2 \
+ index {sqlite_autoindex_T2_2} T2 \
+ table {TempTab} {TempTab} \
+ index i2 {TempTab} \
+ index {sqlite_autoindex_TempTab_1} {TempTab} \
+ ]
+
+# Make sure the changes persist after restarting the database.
+# (The TEMP table will not persist, of course.)
+#
+ifcapable tempdb {
+ do_test alter-1.6 {
+ db close
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TEMP TABLE objlist(type, name, tbl_name);
+ INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master;
+ INSERT INTO objlist
+ SELECT type, name, tbl_name FROM sqlite_temp_master
+ WHERE NAME!='objlist';
+ SELECT type, name, tbl_name FROM objlist
+ ORDER BY tbl_name, type desc, name;
+ }
+ } [list \
+ table -t1- -t1- \
+ index t1i1 -t1- \
+ index t1i2 -t1- \
+ table T2 T2 \
+ index i3 T2 \
+ index {sqlite_autoindex_T2_1} T2 \
+ index {sqlite_autoindex_T2_2} T2 \
+ ]
+} else {
+ execsql {
+ DROP TABLE TempTab;
+ }
+}
+
+# Make sure the ALTER TABLE statements work with the
+# non-callback API
+#
+do_test alter-1.7 {
+ stepsql $DB {
+ ALTER TABLE [-t1-] RENAME to [*t1*];
+ ALTER TABLE T2 RENAME TO [<t2>];
+ }
+ execsql {
+ DELETE FROM objlist;
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }
+ catchsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_temp_master WHERE NAME!='objlist';
+ }
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table *t1* *t1* \
+ index t1i1 *t1* \
+ index t1i2 *t1* \
+ table <t2> <t2> \
+ index i3 <t2> \
+ index {sqlite_autoindex_<t2>_1} <t2> \
+ index {sqlite_autoindex_<t2>_2} <t2> \
+ ]
+
+# Check that ALTER TABLE works on attached databases.
+#
+ifcapable attach {
+ do_test alter-1.8.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ } {}
+ do_test alter-1.8.2 {
+ execsql {
+ CREATE TABLE t4(a PRIMARY KEY, b, c);
+ CREATE TABLE aux.t4(a PRIMARY KEY, b, c);
+ CREATE INDEX i4 ON t4(b);
+ CREATE INDEX aux.i4 ON t4(b);
+ }
+ } {}
+ do_test alter-1.8.3 {
+ execsql {
+ INSERT INTO t4 VALUES('main', 'main', 'main');
+ INSERT INTO aux.t4 VALUES('aux', 'aux', 'aux');
+ SELECT * FROM t4 WHERE a = 'main';
+ }
+ } {main main main}
+ do_test alter-1.8.4 {
+ execsql {
+ ALTER TABLE t4 RENAME TO t5;
+ SELECT * FROM t4 WHERE a = 'aux';
+ }
+ } {aux aux aux}
+ do_test alter-1.8.5 {
+ execsql {
+ SELECT * FROM t5;
+ }
+ } {main main main}
+ do_test alter-1.8.6 {
+ execsql {
+ SELECT * FROM t5 WHERE b = 'main';
+ }
+ } {main main main}
+ do_test alter-1.8.7 {
+ execsql {
+ ALTER TABLE aux.t4 RENAME TO t5;
+ SELECT * FROM aux.t5 WHERE b = 'aux';
+ }
+ } {aux aux aux}
+}
+
+do_test alter-1.9.1 {
+ execsql {
+ CREATE TABLE tbl1 (a, b, c);
+ INSERT INTO tbl1 VALUES(1, 2, 3);
+ }
+} {}
+do_test alter-1.9.2 {
+ execsql {
+ SELECT * FROM tbl1;
+ }
+} {1 2 3}
+do_test alter-1.9.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ SELECT * FROM tbl2;
+ }
+} {1 2 3}
+do_test alter-1.9.4 {
+ execsql {
+ DROP TABLE tbl2;
+ }
+} {}
+
+# Test error messages
+#
+do_test alter-2.1 {
+ catchsql {
+ ALTER TABLE none RENAME TO hi;
+ }
+} {1 {no such table: none}}
+do_test alter-2.2 {
+ execsql {
+ CREATE TABLE t3(p,q,r);
+ }
+ catchsql {
+ ALTER TABLE [<t2>] RENAME TO t3;
+ }
+} {1 {there is already another table or index with this name: t3}}
+do_test alter-2.3 {
+ catchsql {
+ ALTER TABLE [<t2>] RENAME TO i3;
+ }
+} {1 {there is already another table or index with this name: i3}}
+do_test alter-2.4 {
+ catchsql {
+ ALTER TABLE SqLiTe_master RENAME TO master;
+ }
+} {1 {table sqlite_master may not be altered}}
+do_test alter-2.5 {
+ catchsql {
+ ALTER TABLE t3 RENAME TO sqlite_t3;
+ }
+} {1 {object name reserved for internal use: sqlite_t3}}
+do_test alter-2.6 {
+ catchsql {
+ ALTER TABLE t3 ADD COLUMN (ALTER TABLE t3 ADD COLUMN);
+ }
+} {1 {near "(": syntax error}}
+
+# If this compilation does not include triggers, omit the alter-3.* tests.
+ifcapable trigger {
+
+#-----------------------------------------------------------------------
+# Tests alter-3.* test ALTER TABLE on tables that have triggers.
+#
+# alter-3.1.*: ALTER TABLE with triggers.
+# alter-3.2.*: Test that the ON keyword cannot be used as a database,
+# table or column name unquoted. This is done because part of the
+# ALTER TABLE code (specifically the implementation of SQL function
+# "sqlite_alter_trigger") will break in this case.
+# alter-3.3.*: ALTER TABLE with TEMP triggers (todo).
+#
+
+# An SQL user-function for triggers to fire, so that we know they
+# are working.
+proc trigfunc {args} {
+ set ::TRIGGER $args
+}
+db func trigfunc trigfunc
+
+do_test alter-3.1.0 {
+ execsql {
+ CREATE TABLE t6(a, b, c);
+ CREATE TRIGGER trig1 AFTER INSERT ON t6 BEGIN
+ SELECT trigfunc('trig1', new.a, new.b, new.c);
+ END;
+ }
+} {}
+do_test alter-3.1.1 {
+ execsql {
+ INSERT INTO t6 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig1 1 2 3}
+do_test alter-3.1.2 {
+ execsql {
+ ALTER TABLE t6 RENAME TO t7;
+ INSERT INTO t7 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig1 4 5 6}
+do_test alter-3.1.3 {
+ execsql {
+ DROP TRIGGER trig1;
+ }
+} {}
+do_test alter-3.1.4 {
+ execsql {
+ CREATE TRIGGER trig2 AFTER INSERT ON main.t7 BEGIN
+ SELECT trigfunc('trig2', new.a, new.b, new.c);
+ END;
+ INSERT INTO t7 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig2 1 2 3}
+do_test alter-3.1.5 {
+ execsql {
+ ALTER TABLE t7 RENAME TO t8;
+ INSERT INTO t8 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig2 4 5 6}
+do_test alter-3.1.6 {
+ execsql {
+ DROP TRIGGER trig2;
+ }
+} {}
+do_test alter-3.1.7 {
+ execsql {
+ CREATE TRIGGER trig3 AFTER INSERT ON main.'t8'BEGIN
+ SELECT trigfunc('trig3', new.a, new.b, new.c);
+ END;
+ INSERT INTO t8 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig3 1 2 3}
+do_test alter-3.1.8 {
+ execsql {
+ ALTER TABLE t8 RENAME TO t9;
+ INSERT INTO t9 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig3 4 5 6}
+
+# Make sure "ON" cannot be used as a database, table or column name without
+# quoting. Otherwise the sqlite_alter_trigger() function might not work.
+file delete -force test3.db
+file delete -force test3.db-journal
+ifcapable attach {
+ do_test alter-3.2.1 {
+ catchsql {
+ ATTACH 'test3.db' AS ON;
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.2 {
+ catchsql {
+ ATTACH 'test3.db' AS 'ON';
+ }
+ } {0 {}}
+ do_test alter-3.2.3 {
+ catchsql {
+ CREATE TABLE ON.t1(a, b, c);
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.4 {
+ catchsql {
+ CREATE TABLE 'ON'.t1(a, b, c);
+ }
+ } {0 {}}
+ do_test alter-3.2.4 {
+ catchsql {
+ CREATE TABLE 'ON'.ON(a, b, c);
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.5 {
+ catchsql {
+ CREATE TABLE 'ON'.'ON'(a, b, c);
+ }
+ } {0 {}}
+}
+do_test alter-3.2.6 {
+ catchsql {
+ CREATE TABLE t10(a, ON, c);
+ }
+} {1 {near "ON": syntax error}}
+do_test alter-3.2.7 {
+ catchsql {
+ CREATE TABLE t10(a, 'ON', c);
+ }
+} {0 {}}
+do_test alter-3.2.8 {
+ catchsql {
+ CREATE TRIGGER trig4 AFTER INSERT ON ON BEGIN SELECT 1; END;
+ }
+} {1 {near "ON": syntax error}}
+ifcapable attach {
+ do_test alter-3.2.9 {
+ catchsql {
+ CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END;
+ }
+ } {0 {}}
+}
+do_test alter-3.2.10 {
+ execsql {
+ DROP TABLE t10;
+ }
+} {}
+
+do_test alter-3.3.1 {
+ execsql [subst {
+ CREATE TABLE tbl1(a, b, c);
+ CREATE $::temp TRIGGER trig1 AFTER INSERT ON tbl1 BEGIN
+ SELECT trigfunc('trig1', new.a, new.b, new.c);
+ END;
+ }]
+} {}
+do_test alter-3.3.2 {
+ execsql {
+ INSERT INTO tbl1 VALUES('a', 'b', 'c');
+ }
+ set ::TRIGGER
+} {trig1 a b c}
+do_test alter-3.3.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ INSERT INTO tbl2 VALUES('d', 'e', 'f');
+ }
+ set ::TRIGGER
+} {trig1 d e f}
+do_test alter-3.3.4 {
+ execsql [subst {
+ CREATE $::temp TRIGGER trig2 AFTER UPDATE ON tbl2 BEGIN
+ SELECT trigfunc('trig2', new.a, new.b, new.c);
+ END;
+ }]
+} {}
+do_test alter-3.3.5 {
+ execsql {
+ ALTER TABLE tbl2 RENAME TO tbl3;
+ INSERT INTO tbl3 VALUES('g', 'h', 'i');
+ }
+ set ::TRIGGER
+} {trig1 g h i}
+do_test alter-3.3.6 {
+ execsql {
+ UPDATE tbl3 SET a = 'G' where a = 'g';
+ }
+ set ::TRIGGER
+} {trig2 G h i}
+do_test alter-3.3.7 {
+ execsql {
+ DROP TABLE tbl3;
+ }
+} {}
+ifcapable tempdb {
+ do_test alter-3.3.8 {
+ execsql {
+ SELECT * FROM sqlite_temp_master WHERE type = 'trigger';
+ }
+ } {}
+}
+
+} ;# ifcapable trigger
+
+# If the build does not include AUTOINCREMENT fields, omit alter-4.*.
+ifcapable autoinc {
+
+do_test alter-4.1 {
+ execsql {
+ CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT);
+ INSERT INTO tbl1 VALUES(10);
+ }
+} {}
+do_test alter-4.2 {
+ execsql {
+ INSERT INTO tbl1 VALUES(NULL);
+ SELECT a FROM tbl1;
+ }
+} {10 11}
+do_test alter-4.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ DELETE FROM tbl2;
+ INSERT INTO tbl2 VALUES(NULL);
+ SELECT a FROM tbl2;
+ }
+} {12}
+do_test alter-4.4 {
+ execsql {
+ DROP TABLE tbl2;
+ }
+} {}
+
+} ;# ifcapable autoinc
+
+# Test that it is Ok to execute an ALTER TABLE immediately after
+# opening a database.
+do_test alter-5.1 {
+ execsql {
+ CREATE TABLE tbl1(a, b, c);
+ INSERT INTO tbl1 VALUES('x', 'y', 'z');
+ }
+} {}
+do_test alter-5.2 {
+ sqlite3 db2 test.db
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ SELECT * FROM tbl2;
+ } db2
+} {x y z}
+do_test alter-5.3 {
+ db2 close
+} {}
+
+foreach tblname [execsql {
+ SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite%'
+}] {
+ execsql "DROP TABLE \"$tblname\""
+}
+
+set ::tbl_name "abc\uABCDdef"
+do_test alter-6.1 {
+ string length $::tbl_name
+} {7}
+do_test alter-6.2 {
+ execsql "
+ CREATE TABLE ${tbl_name}(a, b, c);
+ "
+ set ::oid [execsql {SELECT max(oid) FROM sqlite_master}]
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid;
+ "
+} "{CREATE TABLE ${::tbl_name}(a, b, c)}"
+execsql "
+ SELECT * FROM ${::tbl_name}
+"
+set ::tbl_name2 "abcXdef"
+do_test alter-6.3 {
+ execsql "
+ ALTER TABLE $::tbl_name RENAME TO $::tbl_name2
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name2}\"(a, b, c)}"
+do_test alter-6.4 {
+ execsql "
+ ALTER TABLE $::tbl_name2 RENAME TO $::tbl_name
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c)}"
+set ::col_name ghi\1234\jkl
+do_test alter-6.5 {
+ execsql "
+ ALTER TABLE $::tbl_name ADD COLUMN $::col_name VARCHAR
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR)}"
+set ::col_name2 B\3421\A
+do_test alter-6.6 {
+ db close
+ sqlite3 db test.db
+ execsql "
+ ALTER TABLE $::tbl_name ADD COLUMN $::col_name2
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR, $::col_name2)}"
+do_test alter-6.7 {
+ execsql "
+ INSERT INTO ${::tbl_name} VALUES(1, 2, 3, 4, 5);
+ SELECT $::col_name, $::col_name2 FROM $::tbl_name;
+ "
+} {4 5}
+
+# Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table
+# that includes a COLLATE clause.
+#
+do_test alter-7.1 {
+ execsql {
+ CREATE TABLE t1(a TEXT COLLATE BINARY);
+ ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE;
+ INSERT INTO t1 VALUES(1,'-2');
+ INSERT INTO t1 VALUES(5.4e-08,'5.4e-08');
+ SELECT typeof(a), a, typeof(b), b FROM t1;
+ }
+} {text 1 integer -2 text 5.4e-08 real 5.4e-08}
+
+# Make sure that when a column is added by ALTER TABLE ADD COLUMN and has
+# a default value that the default value is used by aggregate functions.
+#
+do_test alter-8.1 {
+ execsql {
+ CREATE TABLE t2(a INTEGER);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ ALTER TABLE t2 ADD COLUMN b INTEGER DEFAULT 9;
+ SELECT sum(b) FROM t2;
+ }
+} {27}
+do_test alter-8.2 {
+ execsql {
+ SELECT a, sum(b) FROM t2 GROUP BY a;
+ }
+} {1 18 2 9}
+
+#--------------------------------------------------------------------------
+# alter-9.X - Special test: Make sure the sqlite_rename_trigger() and
+# rename_table() functions do not crash when handed bad input.
+#
+ifcapable trigger {
+ do_test alter-9.1 {
+ execsql {SELECT SQLITE_RENAME_TRIGGER(0,0)}
+ } {{}}
+}
+do_test alter-9.2 {
+ execsql {
+ SELECT SQLITE_RENAME_TABLE(0,0);
+ SELECT SQLITE_RENAME_TABLE(10,20);
+ SELECT SQLITE_RENAME_TABLE("foo", "foo");
+ }
+} {{} {} {}}
+
+#------------------------------------------------------------------------
+# alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters
+# in the names.
+#
+do_test alter-10.1 {
+ execsql "CREATE TABLE xyz(x UNIQUE)"
+ execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc"
+ execsql {SELECT name FROM sqlite_master WHERE name LIKE 'xyz%'}
+} [list xyz\u1234abc]
+do_test alter-10.2 {
+ execsql {SELECT name FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
+} [list sqlite_autoindex_xyz\u1234abc_1]
+do_test alter-10.3 {
+ execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc"
+ execsql {SELECT name FROM sqlite_master WHERE name LIKE 'xyz%'}
+} [list xyzabc]
+do_test alter-10.4 {
+ execsql {SELECT name FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
+} [list sqlite_autoindex_xyzabc_1]
+
+do_test alter-11.1 {
+ sqlite3_exec db {CREATE TABLE t11(%c6%c6)}
+ execsql {
+ ALTER TABLE t11 ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11 ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+set isutf16 [regexp 16 [db one {PRAGMA encoding}]]
+if {!$isutf16} {
+ do_test alter-11.2 {
+ execsql {INSERT INTO t11 VALUES(1,2)}
+ sqlite3_exec db {SELECT %c6%c6 AS xyz, abc FROM t11}
+ } {0 {xyz abc 1 2}}
+}
+do_test alter-11.3 {
+ sqlite3_exec db {CREATE TABLE t11b("%81%82%83" text)}
+ execsql {
+ ALTER TABLE t11b ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11b ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+if {!$isutf16} {
+ do_test alter-11.4 {
+ execsql {INSERT INTO t11b VALUES(3,4)}
+ sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+ do_test alter-11.5 {
+ sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+ do_test alter-11.6 {
+ sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+}
+do_test alter-11.7 {
+ sqlite3_exec db {CREATE TABLE t11c(%81%82%83 text)}
+ execsql {
+ ALTER TABLE t11c ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11c ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+if {!$isutf16} {
+ do_test alter-11.8 {
+ execsql {INSERT INTO t11c VALUES(5,6)}
+ sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+ do_test alter-11.9 {
+ sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+ do_test alter-11.10 {
+ sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+}
+
+do_test alter-12.1 {
+ execsql {
+ CREATE TABLE t12(a, b, c);
+ CREATE VIEW v1 AS SELECT * FROM t12;
+ }
+} {}
+do_test alter-12.2 {
+ catchsql {
+ ALTER TABLE v1 RENAME TO v2;
+ }
+} {1 {view v1 may not be altered}}
+do_test alter-12.3 {
+ execsql { SELECT * FROM v1; }
+} {}
+do_test alter-12.4 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM v1; }
+} {}
+do_test alter-12.5 {
+ catchsql {
+ ALTER TABLE v1 ADD COLUMN new_column;
+ }
+} {1 {Cannot add a column to a view}}
+
+# Ticket #3102:
+# Verify that comments do not interfere with the table rename
+# algorithm.
+#
+do_test alter-13.1 {
+ execsql {
+ CREATE TABLE /* hi */ t3102a(x);
+ CREATE TABLE t3102b -- comment
+ (y);
+ CREATE INDEX t3102c ON t3102a(x);
+ SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
+ }
+} {t3102a t3102b t3102c}
+do_test alter-13.2 {
+ execsql {
+ ALTER TABLE t3102a RENAME TO t3102a_rename;
+ SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
+ }
+} {t3102a_rename t3102b t3102c}
+do_test alter-13.3 {
+ execsql {
+ ALTER TABLE t3102b RENAME TO t3102b_rename;
+ SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
+ }
+} {t3102a_rename t3102b_rename t3102c}
+
+finish_test
diff --git a/third_party/sqlite/test/alter2.test b/third_party/sqlite/test/alter2.test
new file mode 100755
index 0000000..e56c976
--- /dev/null
+++ b/third_party/sqlite/test/alter2.test
@@ -0,0 +1,450 @@
+# 2005 February 18
+#
+# 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 testing that SQLite can handle a subtle
+# file format change that may be used in the future to implement
+# "ALTER TABLE ... ADD COLUMN".
+#
+# $Id: alter2.test,v 1.13 2008/03/19 00:21:31 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We have to have pragmas in order to do this test
+ifcapable {!pragma} return
+
+# These tests do not work if there is a codec.
+#
+#if {[catch {sqlite3 -has_codec} r] || $r} return
+
+# The file format change affects the way row-records stored in tables (but
+# not indices) are interpreted. Before version 3.1.3, a row-record for a
+# table with N columns was guaranteed to contain exactly N fields. As
+# of version 3.1.3, the record may contain up to N fields. In this case
+# the M fields that are present are the values for the left-most M
+# columns. The (N-M) rightmost columns contain NULL.
+#
+# If any records in the database contain less fields than their table
+# has columns, then the file-format meta value should be set to (at least) 2.
+#
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+# This procedure sets the SQL statement stored for table $tbl in the
+# sqlite_master table of file 'test.db' to $sql. Also set the file format
+# to the supplied value. This is 2 if the added column has a default that is
+# NULL, or 3 otherwise.
+#
+proc alter_table {tbl sql {file_format 2}} {
+ sqlite3 dbat test.db
+ set s [string map {' ''} $sql]
+ set t [string map {' ''} $tbl]
+ dbat eval [subst {
+ PRAGMA writable_schema = 1;
+ UPDATE sqlite_master SET sql = '$s' WHERE name = '$t' AND type = 'table';
+ PRAGMA writable_schema = 0;
+ }]
+ dbat close
+ set_file_format 2
+}
+
+#-----------------------------------------------------------------------
+# Some basic tests to make sure short rows are handled.
+#
+do_test alter2-1.1 {
+ execsql {
+ CREATE TABLE abc(a, b);
+ INSERT INTO abc VALUES(1, 2);
+ INSERT INTO abc VALUES(3, 4);
+ INSERT INTO abc VALUES(5, 6);
+ }
+} {}
+do_test alter2-1.2 {
+ # ALTER TABLE abc ADD COLUMN c;
+ alter_table abc {CREATE TABLE abc(a, b, c);}
+} {}
+do_test alter2-1.3 {
+ execsql {
+ SELECT * FROM abc;
+ }
+} {1 2 {} 3 4 {} 5 6 {}}
+do_test alter2-1.4 {
+ execsql {
+ UPDATE abc SET c = 10 WHERE a = 1;
+ SELECT * FROM abc;
+ }
+} {1 2 10 3 4 {} 5 6 {}}
+do_test alter2-1.5 {
+ execsql {
+ CREATE INDEX abc_i ON abc(c);
+ }
+} {}
+do_test alter2-1.6 {
+ execsql {
+ SELECT c FROM abc ORDER BY c;
+ }
+} {{} {} 10}
+do_test alter2-1.7 {
+ execsql {
+ SELECT * FROM abc WHERE c = 10;
+ }
+} {1 2 10}
+do_test alter2-1.8 {
+ execsql {
+ SELECT sum(a), c FROM abc GROUP BY c;
+ }
+} {8 {} 1 10}
+do_test alter2-1.9 {
+ # ALTER TABLE abc ADD COLUMN d;
+ alter_table abc {CREATE TABLE abc(a, b, c, d);}
+ execsql { SELECT * FROM abc; }
+ execsql {
+ UPDATE abc SET d = 11 WHERE c IS NULL AND a<4;
+ SELECT * FROM abc;
+ }
+} {1 2 10 {} 3 4 {} 11 5 6 {} {}}
+do_test alter2-1.10 {
+ execsql {
+ SELECT typeof(d) FROM abc;
+ }
+} {null integer null}
+do_test alter2-1.99 {
+ execsql {
+ DROP TABLE abc;
+ }
+} {}
+
+#-----------------------------------------------------------------------
+# Test that views work when the underlying table structure is changed.
+#
+ifcapable view {
+ do_test alter2-2.1 {
+ execsql {
+ CREATE TABLE abc2(a, b, c);
+ INSERT INTO abc2 VALUES(1, 2, 10);
+ INSERT INTO abc2 VALUES(3, 4, NULL);
+ INSERT INTO abc2 VALUES(5, 6, NULL);
+ CREATE VIEW abc2_v AS SELECT * FROM abc2;
+ SELECT * FROM abc2_v;
+ }
+ } {1 2 10 3 4 {} 5 6 {}}
+ do_test alter2-2.2 {
+ # ALTER TABLE abc ADD COLUMN d;
+ alter_table abc2 {CREATE TABLE abc2(a, b, c, d);}
+ execsql {
+ SELECT * FROM abc2_v;
+ }
+ } {1 2 10 {} 3 4 {} {} 5 6 {} {}}
+ do_test alter2-2.3 {
+ execsql {
+ DROP TABLE abc2;
+ DROP VIEW abc2_v;
+ }
+ } {}
+}
+
+#-----------------------------------------------------------------------
+# Test that triggers work when a short row is copied to the old.*
+# trigger pseudo-table.
+#
+ifcapable trigger {
+ do_test alter2-3.1 {
+ execsql {
+ CREATE TABLE abc3(a, b);
+ CREATE TABLE blog(o, n);
+ CREATE TRIGGER abc3_t AFTER UPDATE OF b ON abc3 BEGIN
+ INSERT INTO blog VALUES(old.b, new.b);
+ END;
+ }
+ } {}
+ do_test alter2-3.2 {
+ execsql {
+ INSERT INTO abc3 VALUES(1, 4);
+ UPDATE abc3 SET b = 2 WHERE b = 4;
+ SELECT * FROM blog;
+ }
+ } {4 2}
+ do_test alter2-3.3 {
+ execsql {
+ INSERT INTO abc3 VALUES(3, 4);
+ INSERT INTO abc3 VALUES(5, 6);
+ }
+ alter_table abc3 {CREATE TABLE abc3(a, b, c);}
+ execsql {
+ SELECT * FROM abc3;
+ }
+ } {1 2 {} 3 4 {} 5 6 {}}
+ do_test alter2-3.4 {
+ execsql {
+ UPDATE abc3 SET b = b*2 WHERE a<4;
+ SELECT * FROM abc3;
+ }
+ } {1 4 {} 3 8 {} 5 6 {}}
+ do_test alter2-3.5 {
+ execsql {
+ SELECT * FROM blog;
+ }
+ } {4 2 2 4 4 8}
+
+ do_test alter2-3.6 {
+ execsql {
+ CREATE TABLE clog(o, n);
+ CREATE TRIGGER abc3_t2 AFTER UPDATE OF c ON abc3 BEGIN
+ INSERT INTO clog VALUES(old.c, new.c);
+ END;
+ UPDATE abc3 SET c = a*2;
+ SELECT * FROM clog;
+ }
+ } {{} 2 {} 6 {} 10}
+}
+
+#---------------------------------------------------------------------
+# Check that an error occurs if the database is upgraded to a file
+# format that SQLite does not support (in this case 5). Note: The
+# file format is checked each time the schema is read, so changing the
+# file format requires incrementing the schema cookie.
+#
+do_test alter2-4.1 {
+ db close
+ set_file_format 5
+ sqlite3 db test.db
+} {}
+do_test alter2-4.2 {
+ # We have to run two queries here because the Tcl interface uses
+ # sqlite3_prepare_v2(). In this case, the first query encounters an
+ # SQLITE_SCHEMA error. Then, when trying to recompile the statement, the
+ # "unsupported file format" error is encountered. So the error code
+ # returned is SQLITE_SCHEMA, not SQLITE_ERROR as required by the following
+ # test case.
+ #
+ # When the query is attempted a second time, the same error message is
+ # returned but the error code is SQLITE_ERROR, because the unsupported
+ # file format was detected during a call to sqlite3_prepare(), not
+ # sqlite3_step().
+ #
+ catchsql { SELECT * FROM sqlite_master; }
+ catchsql { SELECT * FROM sqlite_master; }
+} {1 {unsupported file format}}
+do_test alter2-4.3 {
+ sqlite3_errcode db
+} {SQLITE_ERROR}
+do_test alter2-4.4 {
+ set ::DB [sqlite3_connection_pointer db]
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+} {1 {unsupported file format}}
+do_test alter2-4.5 {
+ sqlite3_errcode db
+} {SQLITE_ERROR}
+
+#---------------------------------------------------------------------
+# Check that executing VACUUM on a file with file-format version 2
+# resets the file format to 1.
+#
+set default_file_format [expr $SQLITE_DEFAULT_FILE_FORMAT==4 ? 4 : 1]
+ifcapable vacuum {
+ do_test alter2-5.1 {
+ set_file_format 2
+ db close
+ sqlite3 db test.db
+ execsql {SELECT 1 FROM sqlite_master LIMIT 1;}
+ get_file_format
+ } {2}
+ do_test alter2-5.2 {
+ execsql {
+ VACUUM;
+ }
+ } {}
+ do_test alter2-5.3 {
+ get_file_format
+ } $default_file_format
+}
+
+#---------------------------------------------------------------------
+# Test that when a database with file-format 2 is opened, new
+# databases are still created with file-format 1.
+#
+do_test alter2-6.1 {
+ db close
+ set_file_format 2
+ sqlite3 db test.db
+ get_file_format
+} {2}
+ifcapable attach {
+ do_test alter2-6.2 {
+ file delete -force test2.db-journal
+ file delete -force test2.db
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.t1(a, b);
+ }
+ get_file_format test2.db
+ } $default_file_format
+}
+do_test alter2-6.3 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ }
+ get_file_format
+} {2}
+
+#---------------------------------------------------------------------
+# Test that types and values for columns added with default values
+# other than NULL work with SELECT statements.
+#
+do_test alter2-7.1 {
+ execsql {
+ DROP TABLE t1;
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ SELECT * FROM t1;
+ }
+} {1 2 3 4}
+do_test alter2-7.2 {
+ set sql {CREATE TABLE t1(a, b DEFAULT '123', c INTEGER DEFAULT '123')}
+ alter_table t1 $sql 3
+ execsql {
+ SELECT * FROM t1 LIMIT 1;
+ }
+} {1 123 123}
+do_test alter2-7.3 {
+ execsql {
+ SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
+ }
+} {1 integer 123 text 123 integer}
+do_test alter2-7.4 {
+ execsql {
+ SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
+ }
+} {1 integer 123 text 123 integer}
+do_test alter2-7.5 {
+ set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)}
+ alter_table t1 $sql 3
+ execsql {
+ SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
+ }
+} {1 integer -123 integer 5 text}
+
+#-----------------------------------------------------------------------
+# Test that UPDATE trigger tables work with default values, and that when
+# a row is updated the default values are correctly transfered to the
+# new row.
+#
+ifcapable trigger {
+db function set_val {set ::val}
+ do_test alter2-8.1 {
+ execsql {
+ CREATE TRIGGER trig1 BEFORE UPDATE ON t1 BEGIN
+ SELECT set_val(
+ old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)||' '||
+ new.b||' '||typeof(new.b)||' '||new.c||' '||typeof(new.c)
+ );
+ END;
+ }
+ list
+ } {}
+}
+do_test alter2-8.2 {
+ execsql {
+ UPDATE t1 SET c = 10 WHERE a = 1;
+ SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
+ }
+} {1 integer -123 integer 10 text}
+ifcapable trigger {
+ do_test alter2-8.3 {
+ set ::val
+ } {-123 integer 5 text -123 integer 10 text}
+}
+
+#-----------------------------------------------------------------------
+# Test that DELETE trigger tables work with default values, and that when
+# a row is updated the default values are correctly transfered to the
+# new row.
+#
+ifcapable trigger {
+ do_test alter2-9.1 {
+ execsql {
+ CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN
+ SELECT set_val(
+ old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)
+ );
+ END;
+ }
+ list
+ } {}
+ do_test alter2-9.2 {
+ execsql {
+ DELETE FROM t1 WHERE a = 2;
+ }
+ set ::val
+ } {-123 integer 5 text}
+}
+
+#-----------------------------------------------------------------------
+# Test creating an index on a column added with a default value.
+#
+ifcapable bloblit {
+ do_test alter2-10.1 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES('a');
+ INSERT INTO t2 VALUES('b');
+ INSERT INTO t2 VALUES('c');
+ INSERT INTO t2 VALUES('d');
+ }
+ alter_table t2 {CREATE TABLE t2(a, b DEFAULT X'ABCD', c DEFAULT NULL);} 3
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ execsql {
+ SELECT quote(a), quote(b), quote(c) FROM t2 LIMIT 1;
+ }
+ } {'a' X'ABCD' NULL}
+ do_test alter2-10.2 {
+ execsql {
+ CREATE INDEX i1 ON t2(b);
+ SELECT a FROM t2 WHERE b = X'ABCD';
+ }
+ } {a b c d}
+ do_test alter2-10.3 {
+ execsql {
+ DELETE FROM t2 WHERE a = 'c';
+ SELECT a FROM t2 WHERE b = X'ABCD';
+ }
+ } {a b d}
+ do_test alter2-10.4 {
+ execsql {
+ SELECT count(b) FROM t2 WHERE b = X'ABCD';
+ }
+ } {3}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/alter3.test b/third_party/sqlite/test/alter3.test
new file mode 100755
index 0000000..69efc50
--- /dev/null
+++ b/third_party/sqlite/test/alter3.test
@@ -0,0 +1,395 @@
+# 2005 February 19
+#
+# 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 testing that SQLite can handle a subtle
+# file format change that may be used in the future to implement
+# "ALTER TABLE ... ADD COLUMN".
+#
+# $Id: alter3.test,v 1.11 2008/03/19 00:21:31 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+
+source $testdir/tester.tcl
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+# Determine if there is a codec available on this test.
+#
+if {[catch {sqlite3 -has_codec} r] || $r} {
+ set has_codec 1
+} else {
+ set has_codec 0
+}
+
+
+# Test Organisation:
+# ------------------
+#
+# alter3-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
+# alter3-2.*: Test error messages.
+# alter3-3.*: Test adding columns with default value NULL.
+# alter3-4.*: Test adding columns with default values other than NULL.
+# alter3-5.*: Test adding columns to tables in ATTACHed databases.
+# alter3-6.*: Test that temp triggers are not accidentally dropped.
+# alter3-7.*: Test that VACUUM resets the file-format.
+#
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+do_test alter3-1.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ SELECT sql FROM sqlite_master;
+ }
+} {{CREATE TABLE abc(a, b, c)}}
+do_test alter3-1.2 {
+ execsql {ALTER TABLE abc ADD d INTEGER;}
+ execsql {
+ SELECT sql FROM sqlite_master;
+ }
+} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
+do_test alter3-1.3 {
+ execsql {ALTER TABLE abc ADD e}
+ execsql {
+ SELECT sql FROM sqlite_master;
+ }
+} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
+do_test alter3-1.4 {
+ execsql {
+ CREATE TABLE main.t1(a, b);
+ ALTER TABLE t1 ADD c;
+ SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
+ }
+} {{CREATE TABLE t1(a, b, c)}}
+do_test alter3-1.5 {
+ execsql {
+ ALTER TABLE t1 ADD d CHECK (a>d);
+ SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
+ }
+} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
+ifcapable foreignkey {
+ do_test alter3-1.6 {
+ execsql {
+ CREATE TABLE t2(a, b, UNIQUE(a, b));
+ ALTER TABLE t2 ADD c REFERENCES t1(c) ;
+ SELECT sql FROM sqlite_master WHERE tbl_name = 't2' AND type = 'table';
+ }
+ } {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
+}
+do_test alter3-1.7 {
+ execsql {
+ CREATE TABLE t3(a, b, UNIQUE(a, b));
+ ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
+ SELECT sql FROM sqlite_master WHERE tbl_name = 't3' AND type = 'table';
+ }
+} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
+do_test alter3-1.99 {
+ catchsql {
+ # May not exist if foriegn-keys are omitted at compile time.
+ DROP TABLE t2;
+ }
+ execsql {
+ DROP TABLE abc;
+ DROP TABLE t1;
+ DROP TABLE t3;
+ }
+} {}
+
+do_test alter3-2.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ }
+ catchsql {
+ ALTER TABLE t1 ADD c PRIMARY KEY;
+ }
+} {1 {Cannot add a PRIMARY KEY column}}
+do_test alter3-2.2 {
+ catchsql {
+ ALTER TABLE t1 ADD c UNIQUE
+ }
+} {1 {Cannot add a UNIQUE column}}
+do_test alter3-2.3 {
+ catchsql {
+ ALTER TABLE t1 ADD b VARCHAR(10)
+ }
+} {1 {duplicate column name: b}}
+do_test alter3-2.3 {
+ catchsql {
+ ALTER TABLE t1 ADD c NOT NULL;
+ }
+} {1 {Cannot add a NOT NULL column with default value NULL}}
+do_test alter3-2.4 {
+ catchsql {
+ ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
+ }
+} {0 {}}
+ifcapable view {
+ do_test alter3-2.5 {
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ }
+ catchsql {
+ alter table v1 add column d;
+ }
+ } {1 {Cannot add a column to a view}}
+}
+do_test alter3-2.6 {
+ catchsql {
+ alter table t1 add column d DEFAULT CURRENT_TIME;
+ }
+} {1 {Cannot add a column with non-constant default}}
+do_test alter3-2.99 {
+ execsql {
+ DROP TABLE t1;
+ }
+} {}
+
+do_test alter3-3.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 100);
+ INSERT INTO t1 VALUES(2, 300);
+ SELECT * FROM t1;
+ }
+} {1 100 2 300}
+do_test alter3-3.1 {
+ execsql {
+ PRAGMA schema_version = 10;
+ }
+} {}
+do_test alter3-3.2 {
+ execsql {
+ ALTER TABLE t1 ADD c;
+ SELECT * FROM t1;
+ }
+} {1 100 {} 2 300 {}}
+if {!$has_codec} {
+ do_test alter3-3.3 {
+ get_file_format
+ } {3}
+}
+ifcapable schema_version {
+ do_test alter3-3.4 {
+ execsql {
+ PRAGMA schema_version;
+ }
+ } {11}
+}
+
+do_test alter3-4.1 {
+ db close
+ file delete -force test.db
+ set ::DB [sqlite3 db test.db]
+ execsql {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 100);
+ INSERT INTO t1 VALUES(2, 300);
+ SELECT * FROM t1;
+ }
+} {1 100 2 300}
+do_test alter3-4.1 {
+ execsql {
+ PRAGMA schema_version = 20;
+ }
+} {}
+do_test alter3-4.2 {
+ execsql {
+ ALTER TABLE t1 ADD c DEFAULT 'hello world';
+ SELECT * FROM t1;
+ }
+} {1 100 {hello world} 2 300 {hello world}}
+if {!$has_codec} {
+ do_test alter3-4.3 {
+ get_file_format
+ } {3}
+}
+ifcapable schema_version {
+ do_test alter3-4.4 {
+ execsql {
+ PRAGMA schema_version;
+ }
+ } {21}
+}
+do_test alter3-4.99 {
+ execsql {
+ DROP TABLE t1;
+ }
+} {}
+
+ifcapable attach {
+ do_test alter3-5.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.t1 AS SELECT * FROM t1;
+ PRAGMA aux.schema_version = 30;
+ SELECT sql FROM aux.sqlite_master;
+ }
+ } {{CREATE TABLE t1(a,b)}}
+ do_test alter3-5.2 {
+ execsql {
+ ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
+ SELECT sql FROM aux.sqlite_master;
+ }
+ } {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
+ do_test alter3-5.3 {
+ execsql {
+ SELECT * FROM aux.t1;
+ }
+ } {1 one {} 2 two {}}
+ ifcapable schema_version {
+ do_test alter3-5.4 {
+ execsql {
+ PRAGMA aux.schema_version;
+ }
+ } {31}
+ }
+ if {!$has_codec} {
+ do_test alter3-5.5 {
+ list [get_file_format test2.db] [get_file_format]
+ } {2 3}
+ }
+ do_test alter3-5.6 {
+ execsql {
+ ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
+ SELECT sql FROM aux.sqlite_master;
+ }
+ } {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
+ do_test alter3-5.7 {
+ execsql {
+ SELECT * FROM aux.t1;
+ }
+ } {1 one {} 1000 2 two {} 1000}
+ ifcapable schema_version {
+ do_test alter3-5.8 {
+ execsql {
+ PRAGMA aux.schema_version;
+ }
+ } {32}
+ }
+ do_test alter3-5.9 {
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {1 one 2 two}
+ do_test alter3-5.99 {
+ execsql {
+ DROP TABLE aux.t1;
+ DROP TABLE t1;
+ }
+ } {}
+}
+
+#----------------------------------------------------------------
+# Test that the table schema is correctly reloaded when a column
+# is added to a table.
+#
+ifcapable trigger&&tempdb {
+ do_test alter3-6.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE log(trig, a, b);
+
+ CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
+ INSERT INTO log VALUES('a', new.a, new.b);
+ END;
+ CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
+ INSERT INTO log VALUES('b', new.a, new.b);
+ END;
+
+ INSERT INTO t1 VALUES(1, 2);
+ SELECT * FROM log;
+ }
+ } {b 1 2 a 1 2}
+ do_test alter3-6.2 {
+ execsql {
+ ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
+ INSERT INTO t1(a, b) VALUES(3, 4);
+ SELECT * FROM log;
+ }
+ } {b 1 2 a 1 2 b 3 4 a 3 4}
+}
+
+if {!$has_codec} {
+ ifcapable vacuum {
+ do_test alter3-7.1 {
+ execsql {
+ VACUUM;
+ }
+ get_file_format
+ } {1}
+ do_test alter3-7.2 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ ALTER TABLE abc ADD d DEFAULT NULL;
+ }
+ get_file_format
+ } {2}
+ do_test alter3-7.3 {
+ execsql {
+ ALTER TABLE abc ADD e DEFAULT 10;
+ }
+ get_file_format
+ } {3}
+ do_test alter3-7.4 {
+ execsql {
+ ALTER TABLE abc ADD f DEFAULT NULL;
+ }
+ get_file_format
+ } {3}
+ do_test alter3-7.5 {
+ execsql {
+ VACUUM;
+ }
+ get_file_format
+ } {1}
+ }
+}
+
+# Ticket #1183 - Make sure adding columns to large tables does not cause
+# memory corruption (as was the case before this bug was fixed).
+do_test alter3-8.1 {
+ execsql {
+ CREATE TABLE t4(c1);
+ }
+} {}
+set ::sql ""
+do_test alter3-8.2 {
+ set cols c1
+ for {set i 2} {$i < 100} {incr i} {
+ execsql "
+ ALTER TABLE t4 ADD c$i
+ "
+ lappend cols c$i
+ }
+ set ::sql "CREATE TABLE t4([join $cols {, }])"
+ list
+} {}
+do_test alter3-8.2 {
+ execsql {
+ SELECT sql FROM sqlite_master WHERE name = 't4';
+ }
+} [list $::sql]
+
+finish_test
diff --git a/third_party/sqlite/test/altermalloc.test b/third_party/sqlite/test/altermalloc.test
new file mode 100755
index 0000000..be08d0e
--- /dev/null
+++ b/third_party/sqlite/test/altermalloc.test
@@ -0,0 +1,70 @@
+# 2005 September 19
+#
+# 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 testing the ALTER TABLE statement and
+# specifically out-of-memory conditions within that command.
+#
+# $Id: altermalloc.test,v 1.9 2008/08/04 20:13:27 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable||!memdebug {
+ finish_test
+ return
+}
+
+source $testdir/malloc_common.tcl
+
+do_malloc_test altermalloc-1 -tclprep {
+ db close
+} -tclbody {
+ if {[catch {sqlite3 db test.db}]} {
+ error "out of memory"
+ }
+ sqlite3_db_config_lookaside db 0 0 0
+ sqlite3_extended_result_codes db 1
+} -sqlbody {
+ CREATE TABLE t1(a int);
+ ALTER TABLE t1 ADD COLUMN b INTEGER DEFAULT NULL;
+ ALTER TABLE t1 ADD COLUMN c TEXT DEFAULT 'default-text';
+ ALTER TABLE t1 RENAME TO t2;
+}
+
+# Test malloc() failure on an ALTER TABLE on a virtual table.
+#
+ifcapable vtab {
+ do_malloc_test altermalloc-vtab -tclprep {
+ sqlite3 db2 test.db
+ sqlite3_db_config_lookaside db2 0 0 0
+ sqlite3_extended_result_codes db2 1
+ register_echo_module [sqlite3_connection_pointer db2]
+ db2 eval {
+ CREATE TABLE t1(a, b VARCHAR, c INTEGER);
+ CREATE VIRTUAL TABLE t1echo USING echo(t1);
+ }
+ db2 close
+
+ register_echo_module [sqlite3_connection_pointer db]
+ } -tclbody {
+ set rc [catch {db eval { ALTER TABLE t1echo RENAME TO t1_echo }} msg]
+ if {$msg eq "vtable constructor failed: t1echo"} {
+ set msg "out of memory"
+ }
+ if {$rc} {
+ error $msg
+ }
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/analyze.test b/third_party/sqlite/test/analyze.test
new file mode 100755
index 0000000..12aef75
--- /dev/null
+++ b/third_party/sqlite/test/analyze.test
@@ -0,0 +1,296 @@
+# 2005 July 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.
+# This file implements tests for the ANALYZE command.
+#
+# $Id: analyze.test,v 1.8 2008/08/01 18:47:02 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# There is nothing to test if ANALYZE is disable for this build.
+#
+ifcapable {!analyze} {
+ finish_test
+ return
+}
+
+# Basic sanity checks.
+#
+do_test analyze-1.1 {
+ catchsql {
+ ANALYZE no_such_table
+ }
+} {1 {no such table: no_such_table}}
+do_test analyze-1.2 {
+ execsql {
+ SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
+ }
+} {0}
+do_test analyze-1.3 {
+ catchsql {
+ ANALYZE no_such_db.no_such_table
+ }
+} {1 {unknown database no_such_db}}
+do_test analyze-1.4 {
+ execsql {
+ SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
+ }
+} {0}
+do_test analyze-1.5.1 {
+ catchsql {
+ ANALYZE
+ }
+} {0 {}}
+do_test analyze-1.5.2 {
+ catchsql {
+ PRAGMA empty_result_callbacks=1;
+ ANALYZE
+ }
+} {0 {}}
+do_test analyze-1.6 {
+ execsql {
+ SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
+ }
+} {1}
+do_test analyze-1.7 {
+ execsql {
+ SELECT * FROM sqlite_stat1
+ }
+} {}
+do_test analyze-1.8 {
+ catchsql {
+ ANALYZE main
+ }
+} {0 {}}
+do_test analyze-1.9 {
+ execsql {
+ SELECT * FROM sqlite_stat1
+ }
+} {}
+do_test analyze-1.10 {
+ catchsql {
+ CREATE TABLE t1(a,b);
+ ANALYZE main.t1;
+ }
+} {0 {}}
+do_test analyze-1.11 {
+ execsql {
+ SELECT * FROM sqlite_stat1
+ }
+} {}
+do_test analyze-1.12 {
+ catchsql {
+ ANALYZE t1;
+ }
+} {0 {}}
+do_test analyze-1.13 {
+ execsql {
+ SELECT * FROM sqlite_stat1
+ }
+} {}
+
+# Create some indices that can be analyzed. But do not yet add
+# data. Without data in the tables, no analysis is done.
+#
+do_test analyze-2.1 {
+ execsql {
+ CREATE INDEX t1i1 ON t1(a);
+ ANALYZE main.t1;
+ SELECT * FROM sqlite_stat1 ORDER BY idx;
+ }
+} {}
+do_test analyze-2.2 {
+ execsql {
+ CREATE INDEX t1i2 ON t1(b);
+ ANALYZE t1;
+ SELECT * FROM sqlite_stat1 ORDER BY idx;
+ }
+} {}
+do_test analyze-2.3 {
+ execsql {
+ CREATE INDEX t1i3 ON t1(a,b);
+ ANALYZE main;
+ SELECT * FROM sqlite_stat1 ORDER BY idx;
+ }
+} {}
+
+# Start adding data to the table. Verify that the analysis
+# is done correctly.
+#
+do_test analyze-3.1 {
+ execsql {
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(1,3);
+ ANALYZE main.t1;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {2 2} t1i2 {2 1} t1i3 {2 2 1}}
+do_test analyze-3.2 {
+ execsql {
+ INSERT INTO t1 VALUES(1,4);
+ INSERT INTO t1 VALUES(1,5);
+ ANALYZE t1;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {4 4} t1i2 {4 1} t1i3 {4 4 1}}
+do_test analyze-3.3 {
+ execsql {
+ INSERT INTO t1 VALUES(2,5);
+ ANALYZE main;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1}}
+do_test analyze-3.4 {
+ execsql {
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE INDEX t2i1 ON t2(a);
+ CREATE INDEX t2i2 ON t2(b);
+ CREATE INDEX t2i3 ON t2(a,b);
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2} t2i3 {5 3 1}}
+do_test analyze-3.5 {
+ execsql {
+ DROP INDEX t2i3;
+ ANALYZE t1;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
+do_test analyze-3.6 {
+ execsql {
+ ANALYZE t2;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
+do_test analyze-3.7 {
+ execsql {
+ DROP INDEX t2i2;
+ ANALYZE t2;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3}}
+do_test analyze-3.8 {
+ execsql {
+ CREATE TABLE t3 AS SELECT a, b, rowid AS c, 'hi' AS d FROM t1;
+ CREATE INDEX t3i1 ON t3(a);
+ CREATE INDEX t3i2 ON t3(a,b,c,d);
+ CREATE INDEX t3i3 ON t3(d,b,c,a);
+ DROP TABLE t1;
+ DROP TABLE t2;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {}
+do_test analyze-3.9 {
+ execsql {
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
+
+do_test analyze-3.10 {
+ execsql {
+ CREATE TABLE [silly " name](a, b, c);
+ CREATE INDEX 'foolish '' name' ON [silly " name](a, b);
+ CREATE INDEX 'another foolish '' name' ON [silly " name](c);
+ INSERT INTO [silly " name] VALUES(1, 2, 3);
+ INSERT INTO [silly " name] VALUES(4, 5, 6);
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {{another foolish ' name} {2 1} {foolish ' name} {2 1 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
+do_test analyze-3.11 {
+ execsql {
+ DROP INDEX "foolish ' name";
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {{another foolish ' name} {2 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
+do_test analyze-3.11 {
+ execsql {
+ DROP TABLE "silly "" name";
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
+
+# Try corrupting the sqlite_stat1 table and make sure the
+# database is still able to function.
+#
+do_test analyze-4.0 {
+ sqlite3 db2 test.db
+ db2 eval {
+ CREATE TABLE t4(x,y,z);
+ CREATE INDEX t4i1 ON t4(x);
+ CREATE INDEX t4i2 ON t4(y);
+ INSERT INTO t4 SELECT a,b,c FROM t3;
+ }
+ db2 close
+ db close
+ sqlite3 db test.db
+ execsql {
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ }
+} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1} t4i1 {5 3} t4i2 {5 2}}
+do_test analyze-4.1 {
+ execsql {
+ PRAGMA writable_schema=on;
+ INSERT INTO sqlite_stat1 VALUES(null,null,null);
+ PRAGMA writable_schema=off;
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM t4 WHERE x=1234;
+ }
+} {}
+do_test analyze-4.2 {
+ execsql {
+ PRAGMA writable_schema=on;
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1 VALUES('t4','t4i1','nonsense');
+ INSERT INTO sqlite_stat1 VALUES('t4','t4i2','120897349817238741092873198273409187234918720394817209384710928374109827172901827349871928741910');
+ PRAGMA writable_schema=off;
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM t4 WHERE x=1234;
+ }
+} {}
+do_test analyze-4.3 {
+ execsql {
+ INSERT INTO sqlite_stat1 VALUES('t4','xyzzy','0 1 2 3');
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM t4 WHERE x=1234;
+ }
+} {}
+
+# This test corrupts the database file so it must be the last test
+# in the series.
+#
+do_test analyze-99.1 {
+ execsql {
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET sql='nonsense';
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ ANALYZE
+ }
+} {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/async.test b/third_party/sqlite/test/async.test
new file mode 100755
index 0000000..5d53efe
--- /dev/null
+++ b/third_party/sqlite/test/async.test
@@ -0,0 +1,81 @@
+#
+# 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 runs all tests.
+#
+# $Id: async.test,v 1.13 2007/11/05 17:01:08 danielk1977 Exp $
+
+
+if {[catch {sqlite3async_enable}]} {
+ # The async logic is not built into this system
+ return
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {
+ catch {db close}
+ catch {db2 close}
+ catch {db3 close}
+}
+set ISQUICK 1
+
+set INCLUDE {
+ insert.test
+ insert2.test
+ insert3.test
+ lock.test
+ lock2.test
+ lock3.test
+ select1.test
+ select2.test
+ select3.test
+ select4.test
+ trans.test
+}
+
+# Enable asynchronous IO.
+sqlite3async_enable 1
+
+rename do_test really_do_test
+proc do_test {name args} {
+ uplevel really_do_test async_io-$name $args
+ sqlite3async_start
+ sqlite3async_halt idle
+ sqlite3async_wait
+ sqlite3async_halt never
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $INCLUDE $tail]<0} continue
+ source $testfile
+
+ # Make sure everything is flushed through. This is because [source]ing
+ # the next test file will delete the database file on disk (using
+ # [file delete]). If the asynchronous backend still has the file
+ # open, it will become confused.
+ #
+ sqlite3async_halt idle
+ sqlite3async_start
+ sqlite3async_wait
+ sqlite3async_halt never
+}
+
+# Flush the write-queue and disable asynchronous IO. This should ensure
+# all allocated memory is cleaned up.
+set sqlite3async_trace 1
+sqlite3async_halt idle
+sqlite3async_start
+sqlite3async_wait
+sqlite3async_halt never
+sqlite3async_enable 0
+set sqlite3async_trace 0
+
+really_finish_test
+rename really_do_test do_test
+rename really_finish_test finish_test
diff --git a/third_party/sqlite/test/async2.test b/third_party/sqlite/test/async2.test
new file mode 100755
index 0000000..a687a6c
--- /dev/null
+++ b/third_party/sqlite/test/async2.test
@@ -0,0 +1,129 @@
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: async2.test,v 1.9 2007/11/05 17:01:08 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {
+ [info commands sqlite3async_enable]=="" ||
+ [info command sqlite3_memdebug_fail]==""
+} {
+ # The async logic is not built into this system
+ puts "Skipping async2 tests: not compiled with required features"
+ finish_test
+ return
+}
+
+# Enable asynchronous IO.
+
+set setup_script {
+ CREATE TABLE counter(c);
+ INSERT INTO counter(c) VALUES (1);
+}
+
+set sql_script {
+ BEGIN;
+ UPDATE counter SET c = 2;
+ CREATE TABLE t1(a PRIMARY KEY, b, c);
+ CREATE TABLE t2(a PRIMARY KEY, b, c);
+ COMMIT;
+
+ BEGIN;
+ UPDATE counter SET c = 3;
+ INSERT INTO t1 VALUES('abcdefghij', 'four', 'score');
+ INSERT INTO t2 VALUES('klmnopqrst', 'and', 'seven');
+ COMMIT;
+
+ UPDATE counter SET c = 'FIN';
+}
+
+db close
+
+foreach err [list ioerr malloc-transient malloc-persistent] {
+ set ::go 1
+ for {set n 1} {$::go} {incr n} {
+ set ::sqlite_io_error_pending 0
+ sqlite3_memdebug_fail -1
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql $::setup_script
+ db close
+
+ sqlite3async_enable 1
+ sqlite3 db test.db
+
+ switch -- $err {
+ ioerr { set ::sqlite_io_error_pending $n }
+ malloc-persistent { sqlite3_memdebug_fail $n -repeat 1 }
+ malloc-transient { sqlite3_memdebug_fail $n -repeat 0 }
+ }
+
+ catchsql $::sql_script
+ db close
+
+ sqlite3async_halt idle
+ sqlite3async_start
+ sqlite3async_wait
+ sqlite3async_halt never
+ sqlite3async_enable 0
+
+ set ::sqlite_io_error_pending 0
+ sqlite3_memdebug_fail -1
+
+ sqlite3 db test.db
+ set c [db eval {SELECT c FROM counter LIMIT 1}]
+ switch -- $c {
+ 1 {
+ do_test async-$err-1.1.$n {
+ execsql {
+ SELECT name FROM sqlite_master;
+ }
+ } {counter}
+ }
+ 2 {
+ do_test async-$err-1.2.$n.1 {
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {}
+ do_test async-$err-1.2.$n.2 {
+ execsql {
+ SELECT * FROM t2;
+ }
+ } {}
+ }
+ 3 {
+ do_test async-$err-1.3.$n.1 {
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {abcdefghij four score}
+ do_test async-$err-1.3.$n.2 {
+ execsql {
+ SELECT * FROM t2;
+ }
+ } {klmnopqrst and seven}
+ }
+ FIN {
+ set ::go 0
+ }
+ }
+
+ db close
+ }
+}
+
+catch {db close}
+sqlite3async_halt idle
+sqlite3async_start
+sqlite3async_wait
+sqlite3async_halt never
+
+finish_test
diff --git a/third_party/sqlite/test/async3.test b/third_party/sqlite/test/async3.test
new file mode 100755
index 0000000..c45ab32
--- /dev/null
+++ b/third_party/sqlite/test/async3.test
@@ -0,0 +1,74 @@
+# 2007 September 5
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the code in test_async.c.
+# Specifically, it tests that the xFullPathname() method of
+# of the asynchronous vfs works correctly.
+#
+# $Id: async3.test,v 1.3 2007/11/05 17:01:08 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if { [info commands sqlite3async_enable]=="" } {
+ # The async logic is not built into this system
+ puts "Skipping async3 tests: not compiled with required features"
+ finish_test
+ return
+}
+
+db close
+sqlite3async_enable 1
+sqlite3async_start
+
+set paths {
+ chocolate/banana/vanilla/file.db
+ chocolate//banana/vanilla/file.db
+ chocolate/./banana//vanilla/file.db
+ chocolate/banana/./vanilla/file.db
+ chocolate/banana/../banana/vanilla/file.db
+ chocolate/banana/./vanilla/extra_bit/../file.db
+}
+
+do_test async3-1.0 {
+ file mkdir [file join chocolate banana vanilla]
+ file delete -force chocolate/banana/vanilla/file.db
+ file delete -force chocolate/banana/vanilla/file.db-journal
+} {}
+
+do_test async3-1.1 {
+ sqlite3 db chocolate/banana/vanilla/file.db
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+} {}
+
+set N 2
+foreach p $paths {
+ sqlite3 db2 $p
+ do_test async3-1.$N.1 {
+ execsql {SELECT * FROM abc} db2
+ } {}
+ do_test async3-1.$N.2 {
+ catchsql {INSERT INTO abc VALUES(4, 5, 6)} db2
+ } {1 {database is locked}}
+ db2 close
+ incr N
+}
+
+db close
+sqlite3async_halt idle
+sqlite3async_wait
+sqlite3async_halt never
+sqlite3async_enable 0
+finish_test
diff --git a/third_party/sqlite/test/attach.test b/third_party/sqlite/test/attach.test
new file mode 100755
index 0000000..1bddc46
--- /dev/null
+++ b/third_party/sqlite/test/attach.test
@@ -0,0 +1,792 @@
+# 2003 April 4
+#
+# 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 testing the ATTACH and DETACH commands
+# and related functionality.
+#
+# $Id: attach.test,v 1.49 2008/07/12 14:52:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !attach {
+ finish_test
+ return
+}
+
+for {set i 2} {$i<=15} {incr i} {
+ file delete -force test$i.db
+ file delete -force test$i.db-journal
+}
+
+do_test attach-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1;
+ }
+} {1 2 3 4}
+do_test attach-1.2 {
+ sqlite3 db2 test2.db
+ execsql {
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,'x');
+ INSERT INTO t2 VALUES(2,'y');
+ SELECT * FROM t2;
+ } db2
+} {1 x 2 y}
+do_test attach-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT * FROM two.t2;
+ }
+} {1 x 2 y}
+do_test attach-1.4 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 x 2 y}
+do_test attach-1.5 {
+ execsql {
+ DETACH DATABASE two;
+ SELECT * FROM t1;
+ }
+} {1 2 3 4}
+do_test attach-1.6 {
+ catchsql {
+ SELECT * FROM t2;
+ }
+} {1 {no such table: t2}}
+do_test attach-1.7 {
+ catchsql {
+ SELECT * FROM two.t2;
+ }
+} {1 {no such table: two.t2}}
+do_test attach-1.8 {
+ catchsql {
+ ATTACH DATABASE 'test3.db' AS three;
+ }
+} {0 {}}
+do_test attach-1.9 {
+ catchsql {
+ SELECT * FROM three.sqlite_master;
+ }
+} {0 {}}
+do_test attach-1.10 {
+ catchsql {
+ DETACH DATABASE [three];
+ }
+} {0 {}}
+do_test attach-1.11 {
+ execsql {
+ ATTACH 'test.db' AS db2;
+ ATTACH 'test.db' AS db3;
+ ATTACH 'test.db' AS db4;
+ ATTACH 'test.db' AS db5;
+ ATTACH 'test.db' AS db6;
+ ATTACH 'test.db' AS db7;
+ ATTACH 'test.db' AS db8;
+ ATTACH 'test.db' AS db9;
+ }
+} {}
+proc db_list {db} {
+ set list {}
+ foreach {idx name file} [execsql {PRAGMA database_list} $db] {
+ lappend list $idx $name
+ }
+ return $list
+}
+ifcapable schema_pragmas {
+do_test attach-1.11b {
+ db_list db
+} {0 main 2 db2 3 db3 4 db4 5 db5 6 db6 7 db7 8 db8 9 db9}
+} ;# ifcapable schema_pragmas
+do_test attach-1.12 {
+ catchsql {
+ ATTACH 'test.db' as db2;
+ }
+} {1 {database db2 is already in use}}
+do_test attach-1.12.2 {
+ db errorcode
+} {1}
+do_test attach-1.13 {
+ catchsql {
+ ATTACH 'test.db' as db5;
+ }
+} {1 {database db5 is already in use}}
+do_test attach-1.14 {
+ catchsql {
+ ATTACH 'test.db' as db9;
+ }
+} {1 {database db9 is already in use}}
+do_test attach-1.15 {
+ catchsql {
+ ATTACH 'test.db' as main;
+ }
+} {1 {database main is already in use}}
+ifcapable tempdb {
+ do_test attach-1.16 {
+ catchsql {
+ ATTACH 'test.db' as temp;
+ }
+ } {1 {database temp is already in use}}
+}
+do_test attach-1.17 {
+ catchsql {
+ ATTACH 'test.db' as MAIN;
+ }
+} {1 {database MAIN is already in use}}
+do_test attach-1.18 {
+ catchsql {
+ ATTACH 'test.db' as db10;
+ ATTACH 'test.db' as db11;
+ }
+} {0 {}}
+do_test attach-1.19 {
+ catchsql {
+ ATTACH 'test.db' as db12;
+ }
+} {1 {too many attached databases - max 10}}
+do_test attach-1.19.1 {
+ db errorcode
+} {1}
+do_test attach-1.20.1 {
+ execsql {
+ DETACH db5;
+ }
+} {}
+ifcapable schema_pragmas {
+do_test attach-1.20.2 {
+ db_list db
+} {0 main 2 db2 3 db3 4 db4 5 db6 6 db7 7 db8 8 db9 9 db10 10 db11}
+} ;# ifcapable schema_pragmas
+integrity_check attach-1.20.3
+ifcapable tempdb {
+ execsql {select * from sqlite_temp_master}
+}
+do_test attach-1.21 {
+ catchsql {
+ ATTACH 'test.db' as db12;
+ }
+} {0 {}}
+do_test attach-1.22 {
+ catchsql {
+ ATTACH 'test.db' as db13;
+ }
+} {1 {too many attached databases - max 10}}
+do_test attach-1.22.1 {
+ db errorcode
+} {1}
+do_test attach-1.23 {
+ catchsql {
+ DETACH "db14";
+ }
+} {1 {no such database: db14}}
+do_test attach-1.24 {
+ catchsql {
+ DETACH db12;
+ }
+} {0 {}}
+do_test attach-1.25 {
+ catchsql {
+ DETACH db12;
+ }
+} {1 {no such database: db12}}
+do_test attach-1.26 {
+ catchsql {
+ DETACH main;
+ }
+} {1 {cannot detach database main}}
+
+ifcapable tempdb {
+ do_test attach-1.27 {
+ catchsql {
+ DETACH Temp;
+ }
+ } {1 {cannot detach database Temp}}
+} else {
+ do_test attach-1.27 {
+ catchsql {
+ DETACH Temp;
+ }
+ } {1 {no such database: Temp}}
+}
+
+do_test attach-1.28 {
+ catchsql {
+ DETACH db11;
+ DETACH db10;
+ DETACH db9;
+ DETACH db8;
+ DETACH db7;
+ DETACH db6;
+ DETACH db4;
+ DETACH db3;
+ DETACH db2;
+ }
+} {0 {}}
+ifcapable schema_pragmas {
+ ifcapable tempdb {
+ do_test attach-1.29 {
+ db_list db
+ } {0 main 1 temp}
+ } else {
+ do_test attach-1.29 {
+ db_list db
+ } {0 main}
+ }
+} ;# ifcapable schema_pragmas
+
+ifcapable {trigger} { # Only do the following tests if triggers are enabled
+do_test attach-2.1 {
+ execsql {
+ CREATE TABLE tx(x1,x2,y1,y2);
+ CREATE TRIGGER r1 AFTER UPDATE ON t2 FOR EACH ROW BEGIN
+ INSERT INTO tx(x1,x2,y1,y2) VALUES(OLD.x,NEW.x,OLD.y,NEW.y);
+ END;
+ SELECT * FROM tx;
+ } db2;
+} {}
+do_test attach-2.2 {
+ execsql {
+ UPDATE t2 SET x=x+10;
+ SELECT * FROM tx;
+ } db2;
+} {1 11 x x 2 12 y y}
+do_test attach-2.3 {
+ execsql {
+ CREATE TABLE tx(x1,x2,y1,y2);
+ SELECT * FROM tx;
+ }
+} {}
+do_test attach-2.4 {
+ execsql {
+ ATTACH 'test2.db' AS db2;
+ }
+} {}
+do_test attach-2.5 {
+ execsql {
+ UPDATE db2.t2 SET x=x+10;
+ SELECT * FROM db2.tx;
+ }
+} {1 11 x x 2 12 y y 11 21 x x 12 22 y y}
+do_test attach-2.6 {
+ execsql {
+ SELECT * FROM main.tx;
+ }
+} {}
+do_test attach-2.7 {
+ execsql {
+ SELECT type, name, tbl_name FROM db2.sqlite_master;
+ }
+} {table t2 t2 table tx tx trigger r1 t2}
+
+ifcapable schema_pragmas&&tempdb {
+ do_test attach-2.8 {
+ db_list db
+ } {0 main 1 temp 2 db2}
+} ;# ifcapable schema_pragmas&&tempdb
+ifcapable schema_pragmas&&!tempdb {
+ do_test attach-2.8 {
+ db_list db
+ } {0 main 2 db2}
+} ;# ifcapable schema_pragmas&&!tempdb
+
+do_test attach-2.9 {
+ execsql {
+ CREATE INDEX i2 ON t2(x);
+ SELECT * FROM t2 WHERE x>5;
+ } db2
+} {21 x 22 y}
+do_test attach-2.10 {
+ execsql {
+ SELECT type, name, tbl_name FROM sqlite_master;
+ } db2
+} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
+#do_test attach-2.11 {
+# catchsql {
+# SELECT * FROM t2 WHERE x>5;
+# }
+#} {1 {database schema has changed}}
+ifcapable schema_pragmas {
+ ifcapable tempdb {
+ do_test attach-2.12 {
+ db_list db
+ } {0 main 1 temp 2 db2}
+ } else {
+ do_test attach-2.12 {
+ db_list db
+ } {0 main 2 db2}
+ }
+} ;# ifcapable schema_pragmas
+do_test attach-2.13 {
+ catchsql {
+ SELECT * FROM t2 WHERE x>5;
+ }
+} {0 {21 x 22 y}}
+do_test attach-2.14 {
+ execsql {
+ SELECT type, name, tbl_name FROM sqlite_master;
+ }
+} {table t1 t1 table tx tx}
+do_test attach-2.15 {
+ execsql {
+ SELECT type, name, tbl_name FROM db2.sqlite_master;
+ }
+} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
+do_test attach-2.16 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ ATTACH 'test2.db' AS db2;
+ SELECT type, name, tbl_name FROM db2.sqlite_master;
+ }
+} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
+} ;# End of ifcapable {trigger}
+
+do_test attach-3.1 {
+ db close
+ db2 close
+ sqlite3 db test.db
+ sqlite3 db2 test2.db
+ execsql {
+ SELECT * FROM t1
+ }
+} {1 2 3 4}
+
+# If we are testing a version of the code that lacks trigger support,
+# adjust the database contents so that they are the same if triggers
+# had been enabled.
+ifcapable {!trigger} {
+ db2 eval {
+ DELETE FROM t2;
+ INSERT INTO t2 VALUES(21, 'x');
+ INSERT INTO t2 VALUES(22, 'y');
+ CREATE TABLE tx(x1,x2,y1,y2);
+ INSERT INTO tx VALUES(1, 11, 'x', 'x');
+ INSERT INTO tx VALUES(2, 12, 'y', 'y');
+ INSERT INTO tx VALUES(11, 21, 'x', 'x');
+ INSERT INTO tx VALUES(12, 22, 'y', 'y');
+ CREATE INDEX i2 ON t2(x);
+ }
+}
+
+do_test attach-3.2 {
+ catchsql {
+ SELECT * FROM t2
+ }
+} {1 {no such table: t2}}
+do_test attach-3.3 {
+ catchsql {
+ ATTACH DATABASE 'test2.db' AS db2;
+ SELECT * FROM t2
+ }
+} {0 {21 x 22 y}}
+
+# Even though 'db' has started a transaction, it should not yet have
+# a lock on test2.db so 'db2' should be readable.
+do_test attach-3.4 {
+ execsql BEGIN
+ catchsql {
+ SELECT * FROM t2;
+ } db2;
+} {0 {21 x 22 y}}
+
+# Reading from test2.db from db within a transaction should not
+# prevent test2.db from being read by db2.
+do_test attach-3.5 {
+ execsql {SELECT * FROM t2}
+ catchsql {
+ SELECT * FROM t2;
+ } db2;
+} {0 {21 x 22 y}}
+
+# Making a change to test2.db through db causes test2.db to get
+# a reserved lock. It should still be accessible through db2.
+do_test attach-3.6 {
+ execsql {
+ UPDATE t2 SET x=x+1 WHERE x=50;
+ }
+ catchsql {
+ SELECT * FROM t2;
+ } db2;
+} {0 {21 x 22 y}}
+
+do_test attach-3.7 {
+ execsql ROLLBACK
+ execsql {SELECT * FROM t2} db2
+} {21 x 22 y}
+
+# Start transactions on both db and db2. Once again, just because
+# we make a change to test2.db using db2, only a RESERVED lock is
+# obtained, so test2.db should still be readable using db.
+#
+do_test attach-3.8 {
+ execsql BEGIN
+ execsql BEGIN db2
+ execsql {UPDATE t2 SET x=0 WHERE 0} db2
+ catchsql {SELECT * FROM t2}
+} {0 {21 x 22 y}}
+
+# It is also still accessible from db2.
+do_test attach-3.9 {
+ catchsql {SELECT * FROM t2} db2
+} {0 {21 x 22 y}}
+
+do_test attach-3.10 {
+ execsql {SELECT * FROM t1}
+} {1 2 3 4}
+
+do_test attach-3.11 {
+ catchsql {UPDATE t1 SET a=a+1}
+} {0 {}}
+do_test attach-3.12 {
+ execsql {SELECT * FROM t1}
+} {2 2 4 4}
+
+# db2 has a RESERVED lock on test2.db, so db cannot write to any tables
+# in test2.db.
+do_test attach-3.13 {
+ catchsql {UPDATE t2 SET x=x+1 WHERE x=50}
+} {1 {database is locked}}
+
+# Change for version 3. Transaction is no longer rolled back
+# for a locked database.
+execsql {ROLLBACK}
+
+# db is able to reread its schema because db2 still only holds a
+# reserved lock.
+do_test attach-3.14 {
+ catchsql {SELECT * FROM t1}
+} {0 {1 2 3 4}}
+do_test attach-3.15 {
+ execsql COMMIT db2
+ execsql {SELECT * FROM t1}
+} {1 2 3 4}
+
+# Ticket #323
+do_test attach-4.1 {
+ execsql {DETACH db2}
+ db2 close
+ sqlite3 db2 test2.db
+ execsql {
+ CREATE TABLE t3(x,y);
+ CREATE UNIQUE INDEX t3i1 ON t3(x);
+ INSERT INTO t3 VALUES(1,2);
+ SELECT * FROM t3;
+ } db2;
+} {1 2}
+do_test attach-4.2 {
+ execsql {
+ CREATE TABLE t3(a,b);
+ CREATE UNIQUE INDEX t3i1b ON t3(a);
+ INSERT INTO t3 VALUES(9,10);
+ SELECT * FROM t3;
+ }
+} {9 10}
+do_test attach-4.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS db2;
+ SELECT * FROM db2.t3;
+ }
+} {1 2}
+do_test attach-4.4 {
+ execsql {
+ SELECT * FROM main.t3;
+ }
+} {9 10}
+do_test attach-4.5 {
+ execsql {
+ INSERT INTO db2.t3 VALUES(9,10);
+ SELECT * FROM db2.t3;
+ }
+} {1 2 9 10}
+execsql {
+ DETACH db2;
+}
+ifcapable {trigger} {
+ do_test attach-4.6 {
+ execsql {
+ CREATE TABLE t4(x);
+ CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
+ INSERT INTO t4 VALUES('db2.' || NEW.x);
+ END;
+ INSERT INTO t3 VALUES(6,7);
+ SELECT * FROM t4;
+ } db2
+ } {db2.6}
+ do_test attach-4.7 {
+ execsql {
+ CREATE TABLE t4(y);
+ CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
+ INSERT INTO t4 VALUES('main.' || NEW.a);
+ END;
+ INSERT INTO main.t3 VALUES(11,12);
+ SELECT * FROM main.t4;
+ }
+ } {main.11}
+}
+ifcapable {!trigger} {
+ # When we do not have trigger support, set up the table like they
+ # would have been had triggers been there. The tests that follow need
+ # this setup.
+ execsql {
+ CREATE TABLE t4(x);
+ INSERT INTO t3 VALUES(6,7);
+ INSERT INTO t4 VALUES('db2.6');
+ INSERT INTO t4 VALUES('db2.13');
+ } db2
+ execsql {
+ CREATE TABLE t4(y);
+ INSERT INTO main.t3 VALUES(11,12);
+ INSERT INTO t4 VALUES('main.11');
+ }
+}
+
+
+# This one is tricky. On the UNION ALL select, we have to make sure
+# the schema for both main and db2 is valid before starting to execute
+# the first query of the UNION ALL. If we wait to test the validity of
+# the schema for main until after the first query has run, that test will
+# fail and the query will abort but we will have already output some
+# results. When the query is retried, the results will be repeated.
+#
+ifcapable compound {
+do_test attach-4.8 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS db2;
+ INSERT INTO db2.t3 VALUES(13,14);
+ SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
+ }
+} {db2.6 db2.13 main.11}
+
+do_test attach-4.9 {
+ ifcapable {!trigger} {execsql {INSERT INTO main.t4 VALUES('main.15')}}
+ execsql {
+ INSERT INTO main.t3 VALUES(15,16);
+ SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
+ }
+} {db2.6 db2.13 main.11 main.15}
+} ;# ifcapable compound
+
+ifcapable !compound {
+ ifcapable {!trigger} {execsql {INSERT INTO main.t4 VALUES('main.15')}}
+ execsql {
+ ATTACH DATABASE 'test2.db' AS db2;
+ INSERT INTO db2.t3 VALUES(13,14);
+ INSERT INTO main.t3 VALUES(15,16);
+ }
+} ;# ifcapable !compound
+
+ifcapable view {
+do_test attach-4.10 {
+ execsql {
+ DETACH DATABASE db2;
+ }
+ execsql {
+ CREATE VIEW v3 AS SELECT x*100+y FROM t3;
+ SELECT * FROM v3;
+ } db2
+} {102 910 607 1314}
+do_test attach-4.11 {
+ execsql {
+ CREATE VIEW v3 AS SELECT a*100+b FROM t3;
+ SELECT * FROM v3;
+ }
+} {910 1112 1516}
+do_test attach-4.12 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS db2;
+ SELECT * FROM db2.v3;
+ }
+} {102 910 607 1314}
+do_test attach-4.13 {
+ execsql {
+ SELECT * FROM main.v3;
+ }
+} {910 1112 1516}
+} ;# ifcapable view
+
+# Tests for the sqliteFix...() routines in attach.c
+#
+ifcapable {trigger} {
+do_test attach-5.1 {
+ db close
+ sqlite3 db test.db
+ db2 close
+ file delete -force test2.db
+ sqlite3 db2 test2.db
+ catchsql {
+ ATTACH DATABASE 'test.db' AS orig;
+ CREATE TRIGGER r1 AFTER INSERT ON orig.t1 BEGIN
+ SELECT 'no-op';
+ END;
+ } db2
+} {1 {trigger r1 cannot reference objects in database orig}}
+do_test attach-5.2 {
+ catchsql {
+ CREATE TABLE t5(x,y);
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op';
+ END;
+ } db2
+} {0 {}}
+do_test attach-5.3 {
+ catchsql {
+ DROP TRIGGER r5;
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op' FROM orig.t1;
+ END;
+ } db2
+} {1 {trigger r5 cannot reference objects in database orig}}
+ifcapable tempdb {
+ do_test attach-5.4 {
+ catchsql {
+ CREATE TEMP TABLE t6(p,q,r);
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op' FROM temp.t6;
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+}
+ifcapable subquery {
+ do_test attach-5.5 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op' || (SELECT * FROM temp.t6);
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+ do_test attach-5.6 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op' FROM t1 WHERE x<(SELECT min(x) FROM temp.t6);
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+ do_test attach-5.7 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT 'no-op' FROM t1 GROUP BY 1 HAVING x<(SELECT min(x) FROM temp.t6);
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+ do_test attach-5.7 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ SELECT max(1,x,(SELECT min(x) FROM temp.t6)) FROM t1;
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+ do_test attach-5.8 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ INSERT INTO t1 VALUES((SELECT min(x) FROM temp.t6),5);
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+ do_test attach-5.9 {
+ catchsql {
+ CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
+ DELETE FROM t1 WHERE x<(SELECT min(x) FROM temp.t6);
+ END;
+ } db2
+ } {1 {trigger r5 cannot reference objects in database temp}}
+} ;# endif subquery
+} ;# endif trigger
+
+# Check to make sure we get a sensible error if unable to open
+# the file that we are trying to attach.
+#
+do_test attach-6.1 {
+ catchsql {
+ ATTACH DATABASE 'no-such-file' AS nosuch;
+ }
+} {0 {}}
+if {$tcl_platform(platform)=="unix"} {
+ do_test attach-6.2 {
+ sqlite3 dbx cannot-read
+ dbx eval {CREATE TABLE t1(a,b,c)}
+ dbx close
+ file attributes cannot-read -permission 0000
+ if {[file writable cannot-read]} {
+ puts "\n**** Tests do not work when run as root ****"
+ file delete -force cannot-read
+ exit 1
+ }
+ catchsql {
+ ATTACH DATABASE 'cannot-read' AS noread;
+ }
+ } {1 {unable to open database: cannot-read}}
+ do_test attach-6.2.2 {
+ db errorcode
+ } {14}
+ file delete -force cannot-read
+}
+
+# Check the error message if we try to access a database that has
+# not been attached.
+do_test attach-6.3 {
+ catchsql {
+ CREATE TABLE no_such_db.t1(a, b, c);
+ }
+} {1 {unknown database no_such_db}}
+for {set i 2} {$i<=15} {incr i} {
+ catch {db$i close}
+}
+db close
+file delete -force test2.db
+file delete -force no-such-file
+
+ifcapable subquery {
+ do_test attach-7.1 {
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ catchsql {
+ DETACH RAISE ( IGNORE ) IN ( SELECT "AAAAAA" . * ORDER BY
+ REGISTER LIMIT "AAAAAA" . "AAAAAA" OFFSET RAISE ( IGNORE ) NOT NULL )
+ }
+ } {1 {invalid name: "RAISE ( IGNORE ) IN ( SELECT "AAAAAA" . * ORDER BY
+ REGISTER LIMIT "AAAAAA" . "AAAAAA" OFFSET RAISE ( IGNORE ) NOT NULL )"}}
+}
+
+# Create a malformed file (a file that is not a valid database)
+# and try to attach it
+#
+do_test attach-8.1 {
+ set fd [open test2.db w]
+ puts $fd "This file is not a valid SQLite database"
+ close $fd
+ catchsql {
+ ATTACH 'test2.db' AS t2;
+ }
+} {1 {file is encrypted or is not a database}}
+do_test attach-8.2 {
+ db errorcode
+} {26}
+file delete -force test2.db
+do_test attach-8.3 {
+ sqlite3 db2 test2.db
+ db2 eval {CREATE TABLE t1(x); BEGIN EXCLUSIVE}
+ catchsql {
+ ATTACH 'test2.db' AS t2;
+ }
+} {1 {database is locked}}
+do_test attach-8.4 {
+ db errorcode
+} {5}
+db2 close
+file delete -force test2.db
+
+
+finish_test
diff --git a/third_party/sqlite/test/attach2.test b/third_party/sqlite/test/attach2.test
new file mode 100755
index 0000000..3cc5af9
--- /dev/null
+++ b/third_party/sqlite/test/attach2.test
@@ -0,0 +1,394 @@
+# 2003 July 1
+#
+# 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 testing the ATTACH and DETACH commands
+# and related functionality.
+#
+# $Id: attach2.test,v 1.38 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !attach {
+ finish_test
+ return
+}
+
+# Ticket #354
+#
+# Databases test.db and test2.db contain identical schemas. Make
+# sure we can attach test2.db from test.db.
+#
+do_test attach2-1.1 {
+ db eval {
+ CREATE TABLE t1(a,b);
+ CREATE INDEX x1 ON t1(a);
+ }
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ sqlite3 db2 test2.db
+ db2 eval {
+ CREATE TABLE t1(a,b);
+ CREATE INDEX x1 ON t1(a);
+ }
+ catchsql {
+ ATTACH 'test2.db' AS t2;
+ }
+} {0 {}}
+
+# Ticket #514
+#
+proc db_list {db} {
+ set list {}
+ foreach {idx name file} [execsql {PRAGMA database_list} $db] {
+ lappend list $idx $name
+ }
+ return $list
+}
+db eval {DETACH t2}
+do_test attach2-2.1 {
+ # lock test2.db then try to attach it. This is no longer an error because
+ # db2 just RESERVES the database. It does not obtain a write-lock until
+ # we COMMIT.
+ db2 eval {BEGIN}
+ db2 eval {UPDATE t1 SET a = 0 WHERE 0}
+ catchsql {
+ ATTACH 'test2.db' AS t2;
+ }
+} {0 {}}
+ifcapable schema_pragmas {
+do_test attach2-2.2 {
+ # make sure test2.db did get attached.
+ db_list db
+} {0 main 2 t2}
+} ;# ifcapable schema_pragmas
+db2 eval {COMMIT}
+
+do_test attach2-2.5 {
+ # Make sure we can read test2.db from db
+ catchsql {
+ SELECT name FROM t2.sqlite_master;
+ }
+} {0 {t1 x1}}
+do_test attach2-2.6 {
+ # lock test2.db and try to read from it. This should still work because
+ # the lock is only a RESERVED lock which does not prevent reading.
+ #
+ db2 eval BEGIN
+ db2 eval {UPDATE t1 SET a = 0 WHERE 0}
+ catchsql {
+ SELECT name FROM t2.sqlite_master;
+ }
+} {0 {t1 x1}}
+do_test attach2-2.7 {
+ # but we can still read from test1.db even though test2.db is locked.
+ catchsql {
+ SELECT name FROM main.sqlite_master;
+ }
+} {0 {t1 x1}}
+do_test attach2-2.8 {
+ # start a transaction on test.db even though test2.db is locked.
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(8,9);
+ }
+} {0 {}}
+do_test attach2-2.9 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {8 9}
+do_test attach2-2.10 {
+ # now try to write to test2.db. the write should fail
+ catchsql {
+ INSERT INTO t2.t1 VALUES(1,2);
+ }
+} {1 {database is locked}}
+do_test attach2-2.11 {
+ # when the write failed in the previous test, the transaction should
+ # have rolled back.
+ #
+ # Update for version 3: A transaction is no longer rolled back if a
+ # database is found to be busy.
+ execsql {rollback}
+ db2 eval ROLLBACK
+ execsql {
+ SELECT * FROM t1
+ }
+} {}
+do_test attach2-2.12 {
+ catchsql {
+ COMMIT
+ }
+} {1 {cannot commit - no transaction is active}}
+
+# Ticket #574: Make sure it works using the non-callback API
+#
+do_test attach2-3.1 {
+ set DB [sqlite3_connection_pointer db]
+ set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
+ if {$rc} {lappend rc $VM}
+ sqlite3_step $VM
+ sqlite3_finalize $VM
+ set rc
+} {0}
+do_test attach2-3.2 {
+ set rc [catch {sqlite3_prepare $DB "DETACH t2" -1 TAIL} VM]
+ if {$rc} {lappend rc $VM}
+ sqlite3_step $VM
+ sqlite3_finalize $VM
+ set rc
+} {0}
+
+db close
+for {set i 2} {$i<=15} {incr i} {
+ catch {db$i close}
+}
+
+# A procedure to verify the status of locks on a database.
+#
+proc lock_status {testnum db expected_result} {
+ # If the database was compiled with OMIT_TEMPDB set, then
+ # the lock_status list will not contain an entry for the temp
+ # db. But the test code doesn't know this, so its easiest
+ # to filter it out of the $expected_result list here.
+ ifcapable !tempdb {
+ set expected_result [concat \
+ [lrange $expected_result 0 1] \
+ [lrange $expected_result 4 end] \
+ ]
+ }
+ do_test attach2-$testnum [subst {
+ $db cache flush ;# The lock_status pragma should not be cached
+ execsql {PRAGMA lock_status} $db
+ }] $expected_result
+}
+set sqlite_os_trace 0
+
+# Tests attach2-4.* test that read-locks work correctly with attached
+# databases.
+do_test attach2-4.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {ATTACH 'test2.db' as file2}
+ execsql {ATTACH 'test2.db' as file2} db2
+} {}
+
+lock_status 4.1.1 db {main unlocked temp closed file2 unlocked}
+lock_status 4.1.2 db2 {main unlocked temp closed file2 unlocked}
+
+do_test attach2-4.2 {
+ # Handle 'db' read-locks test.db
+ execsql {BEGIN}
+ execsql {SELECT * FROM t1}
+ # Lock status:
+ # db - shared(main)
+ # db2 -
+} {}
+
+lock_status 4.2.1 db {main shared temp closed file2 unlocked}
+lock_status 4.2.2 db2 {main unlocked temp closed file2 unlocked}
+
+do_test attach2-4.3 {
+ # The read lock held by db does not prevent db2 from reading test.db
+ execsql {SELECT * FROM t1} db2
+} {}
+
+lock_status 4.3.1 db {main shared temp closed file2 unlocked}
+lock_status 4.3.2 db2 {main unlocked temp closed file2 unlocked}
+
+do_test attach2-4.4 {
+ # db is holding a read lock on test.db, so we should not be able
+ # to commit a write to test.db from db2
+ catchsql {
+ INSERT INTO t1 VALUES(1, 2)
+ } db2
+} {1 {database is locked}}
+
+lock_status 4.4.1 db {main shared temp closed file2 unlocked}
+lock_status 4.4.2 db2 {main unlocked temp closed file2 unlocked}
+
+# We have to make sure that the cache_size and the soft_heap_limit
+# are large enough to hold the entire change in memory. If either
+# is set too small, then changes will spill to the database, forcing
+# a reserved lock to promote to exclusive. That will mess up our
+# test results.
+
+set soft_limit [sqlite3_soft_heap_limit 0]
+
+
+do_test attach2-4.5 {
+ # Handle 'db2' reserves file2.
+ execsql {BEGIN} db2
+ execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2
+ # Lock status:
+ # db - shared(main)
+ # db2 - reserved(file2)
+} {}
+
+lock_status 4.5.1 db {main shared temp closed file2 unlocked}
+lock_status 4.5.2 db2 {main unlocked temp closed file2 reserved}
+
+do_test attach2-4.6.1 {
+ # Reads are allowed against a reserved database.
+ catchsql {
+ SELECT * FROM file2.t1;
+ }
+ # Lock status:
+ # db - shared(main), shared(file2)
+ # db2 - reserved(file2)
+} {0 {}}
+
+lock_status 4.6.1.1 db {main shared temp closed file2 shared}
+lock_status 4.6.1.2 db2 {main unlocked temp closed file2 reserved}
+
+do_test attach2-4.6.2 {
+ # Writes against a reserved database are not allowed.
+ catchsql {
+ UPDATE file2.t1 SET a=0;
+ }
+} {1 {database is locked}}
+
+lock_status 4.6.2.1 db {main shared temp closed file2 shared}
+lock_status 4.6.2.2 db2 {main unlocked temp closed file2 reserved}
+
+do_test attach2-4.7 {
+ # Ensure handle 'db' retains the lock on the main file after
+ # failing to obtain a write-lock on file2.
+ catchsql {
+ INSERT INTO t1 VALUES(1, 2)
+ } db2
+} {0 {}}
+
+lock_status 4.7.1 db {main shared temp closed file2 shared}
+lock_status 4.7.2 db2 {main reserved temp closed file2 reserved}
+
+do_test attach2-4.8 {
+ # We should still be able to read test.db from db2
+ execsql {SELECT * FROM t1} db2
+} {1 2}
+
+lock_status 4.8.1 db {main shared temp closed file2 shared}
+lock_status 4.8.2 db2 {main reserved temp closed file2 reserved}
+
+do_test attach2-4.9 {
+ # Try to upgrade the handle 'db' lock.
+ catchsql {
+ INSERT INTO t1 VALUES(1, 2)
+ }
+} {1 {database is locked}}
+
+lock_status 4.9.1 db {main shared temp closed file2 shared}
+lock_status 4.9.2 db2 {main reserved temp closed file2 reserved}
+
+do_test attach2-4.10 {
+ # We cannot commit db2 while db is holding a read-lock
+ catchsql {COMMIT} db2
+} {1 {database is locked}}
+
+lock_status 4.10.1 db {main shared temp closed file2 shared}
+lock_status 4.10.2 db2 {main pending temp closed file2 reserved}
+
+set sqlite_os_trace 0
+do_test attach2-4.11 {
+ # db is able to commit.
+ catchsql {COMMIT}
+} {0 {}}
+
+lock_status 4.11.1 db {main unlocked temp closed file2 unlocked}
+lock_status 4.11.2 db2 {main pending temp closed file2 reserved}
+
+do_test attach2-4.12 {
+ # Now we can commit db2
+ catchsql {COMMIT} db2
+} {0 {}}
+
+lock_status 4.12.1 db {main unlocked temp closed file2 unlocked}
+lock_status 4.12.2 db2 {main unlocked temp closed file2 unlocked}
+
+do_test attach2-4.13 {
+ execsql {SELECT * FROM file2.t1}
+} {1 2}
+do_test attach2-4.14 {
+ execsql {INSERT INTO t1 VALUES(1, 2)}
+} {}
+do_test attach2-4.15 {
+ execsql {SELECT * FROM t1} db2
+} {1 2 1 2}
+
+db close
+db2 close
+file delete -force test2.db
+sqlite3_soft_heap_limit $soft_limit
+
+# These tests - attach2-5.* - check that the master journal file is deleted
+# correctly when a multi-file transaction is committed or rolled back.
+#
+# Update: It's not actually created if a rollback occurs, so that test
+# doesn't really prove too much.
+foreach f [glob test.db*] {file delete -force $f}
+do_test attach2-5.1 {
+ sqlite3 db test.db
+ execsql {
+ ATTACH 'test.db2' AS aux;
+ }
+} {}
+do_test attach2-5.2 {
+ execsql {
+ BEGIN;
+ CREATE TABLE tbl(a, b, c);
+ CREATE TABLE aux.tbl(a, b, c);
+ COMMIT;
+ }
+} {}
+do_test attach2-5.3 {
+ lsort [glob test.db*]
+} {test.db test.db2}
+do_test attach2-5.4 {
+ execsql {
+ BEGIN;
+ DROP TABLE aux.tbl;
+ DROP TABLE tbl;
+ ROLLBACK;
+ }
+} {}
+do_test attach2-5.5 {
+ lsort [glob test.db*]
+} {test.db test.db2}
+
+# Check that a database cannot be ATTACHed or DETACHed during a transaction.
+do_test attach2-6.1 {
+ execsql {
+ BEGIN;
+ }
+} {}
+do_test attach2-6.2 {
+ catchsql {
+ ATTACH 'test3.db' as aux2;
+ }
+} {1 {cannot ATTACH database within transaction}}
+
+do_test attach2-6.3 {
+ catchsql {
+ DETACH aux;
+ }
+} {1 {cannot DETACH database within transaction}}
+do_test attach2-6.4 {
+ execsql {
+ COMMIT;
+ DETACH aux;
+ }
+} {}
+
+db close
+
+finish_test
diff --git a/third_party/sqlite/test/attach3.test b/third_party/sqlite/test/attach3.test
new file mode 100755
index 0000000..98ad347
--- /dev/null
+++ b/third_party/sqlite/test/attach3.test
@@ -0,0 +1,348 @@
+# 2003 July 1
+#
+# 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 testing the ATTACH and DETACH commands
+# and schema changes to attached databases.
+#
+# $Id: attach3.test,v 1.18 2007/10/09 08:29:32 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !attach {
+ finish_test
+ return
+}
+
+# Create tables t1 and t2 in the main database
+execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+}
+
+# Create tables t1 and t2 in database file test2.db
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+} db2
+db2 close
+
+# Create a table in the auxilary database.
+do_test attach3-1.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+} {}
+do_test attach3-1.2 {
+ execsql {
+ CREATE TABLE aux.t3(e, f);
+ }
+} {}
+do_test attach3-1.3 {
+ execsql {
+ SELECT * FROM sqlite_master WHERE name = 't3';
+ }
+} {}
+do_test attach3-1.4 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 't3';
+ }
+} "table t3 t3 [expr $AUTOVACUUM?5:4] {CREATE TABLE t3(e, f)}"
+do_test attach3-1.5 {
+ execsql {
+ INSERT INTO t3 VALUES(1, 2);
+ SELECT * FROM t3;
+ }
+} {1 2}
+
+# Create an index on the auxilary database table.
+do_test attach3-2.1 {
+ execsql {
+ CREATE INDEX aux.i1 on t3(e);
+ }
+} {}
+do_test attach3-2.2 {
+ execsql {
+ SELECT * FROM sqlite_master WHERE name = 'i1';
+ }
+} {}
+do_test attach3-2.3 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 'i1';
+ }
+} "index i1 t3 [expr $AUTOVACUUM?6:5] {CREATE INDEX i1 on t3(e)}"
+
+# Drop the index on the aux database table.
+do_test attach3-3.1 {
+ execsql {
+ DROP INDEX aux.i1;
+ SELECT * FROM aux.sqlite_master WHERE name = 'i1';
+ }
+} {}
+do_test attach3-3.2 {
+ execsql {
+ CREATE INDEX aux.i1 on t3(e);
+ SELECT * FROM aux.sqlite_master WHERE name = 'i1';
+ }
+} "index i1 t3 [expr $AUTOVACUUM?6:5] {CREATE INDEX i1 on t3(e)}"
+do_test attach3-3.3 {
+ execsql {
+ DROP INDEX i1;
+ SELECT * FROM aux.sqlite_master WHERE name = 'i1';
+ }
+} {}
+
+# Drop tables t1 and t2 in the auxilary database.
+do_test attach3-4.1 {
+ execsql {
+ DROP TABLE aux.t1;
+ SELECT name FROM aux.sqlite_master;
+ }
+} {t2 t3}
+do_test attach3-4.2 {
+ # This will drop main.t2
+ execsql {
+ DROP TABLE t2;
+ SELECT name FROM aux.sqlite_master;
+ }
+} {t2 t3}
+do_test attach3-4.3 {
+ execsql {
+ DROP TABLE t2;
+ SELECT name FROM aux.sqlite_master;
+ }
+} {t3}
+
+# Create a view in the auxilary database.
+ifcapable view {
+do_test attach3-5.1 {
+ execsql {
+ CREATE VIEW aux.v1 AS SELECT * FROM t3;
+ }
+} {}
+do_test attach3-5.2 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 'v1';
+ }
+} {view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t3}}
+do_test attach3-5.3 {
+ execsql {
+ INSERT INTO aux.t3 VALUES('hello', 'world');
+ SELECT * FROM v1;
+ }
+} {1 2 hello world}
+
+# Drop the view
+do_test attach3-6.1 {
+ execsql {
+ DROP VIEW aux.v1;
+ }
+} {}
+do_test attach3-6.2 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 'v1';
+ }
+} {}
+} ;# ifcapable view
+
+ifcapable {trigger} {
+# Create a trigger in the auxilary database.
+do_test attach3-7.1 {
+ execsql {
+ CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN
+ INSERT INTO t3 VALUES(new.e*2, new.f*2);
+ END;
+ }
+} {}
+do_test attach3-7.2 {
+ execsql {
+ DELETE FROM t3;
+ INSERT INTO t3 VALUES(10, 20);
+ SELECT * FROM t3;
+ }
+} {10 20 20 40}
+do_test attach3-5.3 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
+ }
+} {trigger tr1 t3 0 {CREATE TRIGGER tr1 AFTER INSERT ON t3 BEGIN
+ INSERT INTO t3 VALUES(new.e*2, new.f*2);
+ END}}
+
+# Drop the trigger
+do_test attach3-8.1 {
+ execsql {
+ DROP TRIGGER aux.tr1;
+ }
+} {}
+do_test attach3-8.2 {
+ execsql {
+ SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
+ }
+} {}
+
+ifcapable tempdb {
+ # Try to trick SQLite into dropping the wrong temp trigger.
+ do_test attach3-9.0 {
+ execsql {
+ CREATE TABLE main.t4(a, b, c);
+ CREATE TABLE aux.t4(a, b, c);
+ CREATE TEMP TRIGGER tst_trigger BEFORE INSERT ON aux.t4 BEGIN
+ SELECT 'hello world';
+ END;
+ SELECT count(*) FROM sqlite_temp_master;
+ }
+ } {1}
+ do_test attach3-9.1 {
+ execsql {
+ DROP TABLE main.t4;
+ SELECT count(*) FROM sqlite_temp_master;
+ }
+ } {1}
+ do_test attach3-9.2 {
+ execsql {
+ DROP TABLE aux.t4;
+ SELECT count(*) FROM sqlite_temp_master;
+ }
+ } {0}
+}
+} ;# endif trigger
+
+# Make sure the aux.sqlite_master table is read-only
+do_test attach3-10.0 {
+ catchsql {
+ INSERT INTO aux.sqlite_master VALUES(1, 2, 3, 4, 5);
+ }
+} {1 {table sqlite_master may not be modified}}
+
+# Failure to attach leaves us in a workable state.
+# Ticket #811
+#
+do_test attach3-11.0 {
+ catchsql {
+ ATTACH DATABASE '/nodir/nofile.x' AS notadb;
+ }
+} {1 {unable to open database: /nodir/nofile.x}}
+do_test attach3-11.1 {
+ catchsql {
+ ATTACH DATABASE ':memory:' AS notadb;
+ }
+} {0 {}}
+do_test attach3-11.2 {
+ catchsql {
+ DETACH DATABASE notadb;
+ }
+} {0 {}}
+
+# Return a list of attached databases
+#
+proc db_list {} {
+ set x [execsql {
+ PRAGMA database_list;
+ }]
+ set y {}
+ foreach {n id file} $x {lappend y $id}
+ return $y
+}
+
+ifcapable schema_pragmas&&tempdb {
+
+ifcapable !trigger {
+ execsql {create temp table dummy(dummy)}
+}
+
+# Ticket #1825
+#
+do_test attach3-12.1 {
+ db_list
+} {main temp aux}
+do_test attach3-12.2 {
+ execsql {
+ ATTACH DATABASE ? AS ?
+ }
+ db_list
+} {main temp aux {}}
+do_test attach3-12.3 {
+ execsql {
+ DETACH aux
+ }
+ db_list
+} {main temp {}}
+do_test attach3-12.4 {
+ execsql {
+ DETACH ?
+ }
+ db_list
+} {main temp}
+do_test attach3-12.5 {
+ execsql {
+ ATTACH DATABASE '' AS ''
+ }
+ db_list
+} {main temp {}}
+do_test attach3-12.6 {
+ execsql {
+ DETACH ''
+ }
+ db_list
+} {main temp}
+do_test attach3-12.7 {
+ execsql {
+ ATTACH DATABASE '' AS ?
+ }
+ db_list
+} {main temp {}}
+do_test attach3-12.8 {
+ execsql {
+ DETACH ''
+ }
+ db_list
+} {main temp}
+do_test attach3-12.9 {
+ execsql {
+ ATTACH DATABASE '' AS NULL
+ }
+ db_list
+} {main temp {}}
+do_test attach3-12.10 {
+ execsql {
+ DETACH ?
+ }
+ db_list
+} {main temp}
+do_test attach3-12.11 {
+ catchsql {
+ DETACH NULL
+ }
+} {1 {no such database: }}
+do_test attach3-12.12 {
+ catchsql {
+ ATTACH null AS null;
+ ATTACH '' AS '';
+ }
+} {1 {database is already in use}}
+do_test attach3-12.13 {
+ db_list
+} {main temp {}}
+do_test attach3-12.14 {
+ execsql {
+ DETACH '';
+ }
+ db_list
+} {main temp}
+
+} ;# ifcapable pragma
+
+finish_test
diff --git a/third_party/sqlite/test/attachmalloc.test b/third_party/sqlite/test/attachmalloc.test
new file mode 100755
index 0000000..a6de5ce
--- /dev/null
+++ b/third_party/sqlite/test/attachmalloc.test
@@ -0,0 +1,49 @@
+# 2005 September 19
+#
+# 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 testing the ATTACH statement and
+# specifically out-of-memory conditions within that command.
+#
+# $Id: attachmalloc.test,v 1.9 2008/08/04 20:13:27 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !memdebug||!attach {
+ finish_test
+ return
+}
+
+source $testdir/malloc_common.tcl
+
+do_malloc_test attachmalloc-1 -tclprep {
+ db close
+ for {set i 2} {$i<=4} {incr i} {
+ file delete -force test$i.db
+ file delete -force test$i.db-journal
+ }
+} -tclbody {
+ if {[catch {sqlite3 db test.db}]} {
+ error "out of memory"
+ }
+ sqlite3_db_config_lookaside db 0 0 0
+ sqlite3_extended_result_codes db 1
+} -sqlbody {
+ ATTACH 'test2.db' AS two;
+ CREATE TABLE two.t1(x);
+ ATTACH 'test3.db' AS three;
+ CREATE TABLE three.t1(x);
+ ATTACH 'test4.db' AS four;
+ CREATE TABLE four.t1(x);
+}
+
+finish_test
diff --git a/third_party/sqlite/test/auth.test b/third_party/sqlite/test/auth.test
new file mode 100755
index 0000000..ada28d2
--- /dev/null
+++ b/third_party/sqlite/test/auth.test
@@ -0,0 +1,2324 @@
+# 2003 April 4
+#
+# 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 testing the sqlite3_set_authorizer() API
+# and related functionality.
+#
+# $Id: auth.test,v 1.43 2008/07/02 13:13:52 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
+# defined during compilation.
+if {[catch {db auth {}} msg]} {
+ finish_test
+ return
+}
+
+rename proc proc_real
+proc_real proc {name arguments script} {
+ proc_real $name $arguments $script
+ if {$name=="auth"} {
+ db authorizer ::auth
+ }
+}
+
+do_test auth-1.1.1 {
+ db close
+ set ::DB [sqlite3 db test.db]
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ db authorizer ::auth
+ catchsql {CREATE TABLE t1(a,b,c)}
+} {1 {not authorized}}
+do_test auth-1.1.2 {
+ db errorcode
+} {23}
+do_test auth-1.1.3 {
+ db authorizer
+} {::auth}
+do_test auth-1.1.4 {
+ # Ticket #896.
+ catchsql {
+ SELECT x;
+ }
+} {1 {no such column: x}}
+do_test auth-1.2 {
+ execsql {SELECT name FROM sqlite_master}
+} {}
+do_test auth-1.3.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TABLE t1(a,b,c)}
+} {1 {not authorized}}
+do_test auth-1.3.2 {
+ db errorcode
+} {23}
+do_test auth-1.3.3 {
+ set ::authargs
+} {t1 {} main {}}
+do_test auth-1.4 {
+ execsql {SELECT name FROM sqlite_master}
+} {}
+
+ifcapable tempdb {
+ do_test auth-1.5 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMP TABLE t1(a,b,c)}
+ } {1 {not authorized}}
+ do_test auth-1.6 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {}
+ do_test auth-1.7.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMP TABLE t1(a,b,c)}
+ } {1 {not authorized}}
+ do_test auth-1.7.2 {
+ set ::authargs
+ } {t1 {} temp {}}
+ do_test auth-1.8 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {}
+}
+
+do_test auth-1.9 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TABLE t1(a,b,c)}
+} {0 {}}
+do_test auth-1.10 {
+ execsql {SELECT name FROM sqlite_master}
+} {}
+do_test auth-1.11 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TABLE t1(a,b,c)}
+} {0 {}}
+do_test auth-1.12 {
+ execsql {SELECT name FROM sqlite_master}
+} {}
+
+ifcapable tempdb {
+ do_test auth-1.13 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMP TABLE t1(a,b,c)}
+ } {0 {}}
+ do_test auth-1.14 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {}
+ do_test auth-1.15 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMP TABLE t1(a,b,c)}
+ } {0 {}}
+ do_test auth-1.16 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {}
+
+ do_test auth-1.17 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMP TABLE t1(a,b,c)}
+ } {0 {}}
+ do_test auth-1.18 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.19.1 {
+ set ::authargs {}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TABLE t2(a,b,c)}
+} {0 {}}
+do_test auth-1.19.2 {
+ set ::authargs
+} {}
+do_test auth-1.20 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+do_test auth-1.21.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {1 {not authorized}}
+do_test auth-1.21.2 {
+ set ::authargs
+} {t2 {} main {}}
+do_test auth-1.22 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.23.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {0 {}}
+do_test auth-1.23.2 {
+ set ::authargs
+} {t2 {} main {}}
+do_test auth-1.24 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.25 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {1 {not authorized}}
+ do_test auth-1.26 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.27 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {0 {}}
+ do_test auth-1.28 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.29 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="t2"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {INSERT INTO t2 VALUES(1,2,3)}
+} {1 {not authorized}}
+do_test auth-1.30 {
+ execsql {SELECT * FROM t2}
+} {}
+do_test auth-1.31 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="t2"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {INSERT INTO t2 VALUES(1,2,3)}
+} {0 {}}
+do_test auth-1.32 {
+ execsql {SELECT * FROM t2}
+} {}
+do_test auth-1.33 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="t1"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {INSERT INTO t2 VALUES(1,2,3)}
+} {0 {}}
+do_test auth-1.34 {
+ execsql {SELECT * FROM t2}
+} {1 2 3}
+
+do_test auth-1.35.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2}
+} {1 {access to t2.b is prohibited}}
+ifcapable attach {
+ do_test auth-1.35.2 {
+ execsql {ATTACH DATABASE 'test.db' AS two}
+ catchsql {SELECT * FROM two.t2}
+ } {1 {access to two.t2.b is prohibited}}
+ execsql {DETACH DATABASE two}
+}
+do_test auth-1.36 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2}
+} {0 {1 {} 3}}
+do_test auth-1.37 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2 WHERE b=2}
+} {0 {}}
+do_test auth-1.38 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="a"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2 WHERE b=2}
+} {0 {{} 2 3}}
+do_test auth-1.39 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2 WHERE b IS NULL}
+} {0 {1 {} 3}}
+do_test auth-1.40 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT a,c FROM t2 WHERE b IS NULL}
+} {1 {access to t2.b is prohibited}}
+
+do_test auth-1.41 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_UPDATE" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {UPDATE t2 SET a=11}
+} {0 {}}
+do_test auth-1.42 {
+ execsql {SELECT * FROM t2}
+} {11 2 3}
+do_test auth-1.43 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_UPDATE" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {UPDATE t2 SET b=22, c=33}
+} {1 {not authorized}}
+do_test auth-1.44 {
+ execsql {SELECT * FROM t2}
+} {11 2 3}
+do_test auth-1.45 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_UPDATE" && $arg1=="t2" && $arg2=="b"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {UPDATE t2 SET b=22, c=33}
+} {0 {}}
+do_test auth-1.46 {
+ execsql {SELECT * FROM t2}
+} {11 2 33}
+
+do_test auth-1.47 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t2"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DELETE FROM t2 WHERE a=11}
+} {1 {not authorized}}
+do_test auth-1.48 {
+ execsql {SELECT * FROM t2}
+} {11 2 33}
+do_test auth-1.49 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t2"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DELETE FROM t2 WHERE a=11}
+} {0 {}}
+do_test auth-1.50 {
+ execsql {SELECT * FROM t2}
+} {11 2 33}
+
+do_test auth-1.51 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_SELECT"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2}
+} {1 {not authorized}}
+do_test auth-1.52 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_SELECT"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2}
+} {0 {}}
+do_test auth-1.53 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_SELECT"} {
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2}
+} {0 {11 2 33}}
+
+# Update for version 3: There used to be a handful of test here that
+# tested the authorisation callback with the COPY command. The following
+# test makes the same database modifications as they used to.
+do_test auth-1.54 {
+ execsql {INSERT INTO t2 VALUES(7, 8, 9);}
+} {}
+do_test auth-1.55 {
+ execsql {SELECT * FROM t2}
+} {11 2 33 7 8 9}
+
+do_test auth-1.63 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {1 {not authorized}}
+do_test auth-1.64 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.65 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t2"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {1 {not authorized}}
+do_test auth-1.66 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.67 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {1 {not authorized}}
+ do_test auth-1.68 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.69 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t1"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {1 {not authorized}}
+ do_test auth-1.70 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.71 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {0 {}}
+do_test auth-1.72 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.73 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t2"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t2}
+} {0 {}}
+do_test auth-1.74 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.75 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {0 {}}
+ do_test auth-1.76 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.77 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="t1"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TABLE t1}
+ } {0 {}}
+ do_test auth-1.78 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+# Test cases auth-1.79 to auth-1.124 test creating and dropping views.
+# Omit these if the library was compiled with views omitted.
+ifcapable view {
+do_test auth-1.79 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE VIEW v1 AS SELECT a+1,b+1 FROM t2}
+} {1 {not authorized}}
+do_test auth-1.80 {
+ set ::authargs
+} {v1 {} main {}}
+do_test auth-1.81 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.82 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE VIEW v1 AS SELECT a+1,b+1 FROM t2}
+} {0 {}}
+do_test auth-1.83 {
+ set ::authargs
+} {v1 {} main {}}
+do_test auth-1.84 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.85 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2}
+ } {1 {not authorized}}
+ do_test auth-1.86 {
+ set ::authargs
+ } {v1 {} temp {}}
+ do_test auth-1.87 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.88 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2}
+ } {0 {}}
+ do_test auth-1.89 {
+ set ::authargs
+ } {v1 {} temp {}}
+ do_test auth-1.90 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.91 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE VIEW v1 AS SELECT a+1,b+1 FROM t2}
+} {1 {not authorized}}
+do_test auth-1.92 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.93 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE VIEW v1 AS SELECT a+1,b+1 FROM t2}
+} {0 {}}
+do_test auth-1.94 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.95 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2}
+ } {1 {not authorized}}
+ do_test auth-1.96 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.97 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE TEMPORARY VIEW v1 AS SELECT a+1,b+1 FROM t2}
+ } {0 {}}
+ do_test auth-1.98 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.99 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE VIEW v2 AS SELECT a+1,b+1 FROM t2;
+ DROP VIEW v2
+ }
+} {1 {not authorized}}
+do_test auth-1.100 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 v2}
+do_test auth-1.101 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v2}
+} {1 {not authorized}}
+do_test auth-1.102 {
+ set ::authargs
+} {v2 {} main {}}
+do_test auth-1.103 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 v2}
+do_test auth-1.104 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v2}
+} {0 {}}
+do_test auth-1.105 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 v2}
+do_test auth-1.106 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v2}
+} {0 {}}
+do_test auth-1.107 {
+ set ::authargs
+} {v2 {} main {}}
+do_test auth-1.108 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 v2}
+do_test auth-1.109 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v2}
+} {0 {}}
+do_test auth-1.110 {
+ set ::authargs
+} {v2 {} main {}}
+do_test auth-1.111 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+
+ifcapable tempdb {
+ do_test auth-1.112 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TEMP VIEW v1 AS SELECT a+1,b+1 FROM t1;
+ DROP VIEW v1
+ }
+ } {1 {not authorized}}
+ do_test auth-1.113 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 v1}
+ do_test auth-1.114 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v1}
+ } {1 {not authorized}}
+ do_test auth-1.115 {
+ set ::authargs
+ } {v1 {} temp {}}
+ do_test auth-1.116 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 v1}
+ do_test auth-1.117 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v1}
+ } {0 {}}
+ do_test auth-1.118 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 v1}
+ do_test auth-1.119 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v1}
+ } {0 {}}
+ do_test auth-1.120 {
+ set ::authargs
+ } {v1 {} temp {}}
+ do_test auth-1.121 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 v1}
+ do_test auth-1.122 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_VIEW"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP VIEW v1}
+ } {0 {}}
+ do_test auth-1.123 {
+ set ::authargs
+ } {v1 {} temp {}}
+ do_test auth-1.124 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+} ;# ifcapable view
+
+# Test cases auth-1.125 to auth-1.176 test creating and dropping triggers.
+# Omit these if the library was compiled with triggers omitted.
+#
+ifcapable trigger&&tempdb {
+do_test auth-1.125 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r2 DELETE on t2 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {1 {not authorized}}
+do_test auth-1.126 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.127 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.128 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r2 DELETE on t2 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {1 {not authorized}}
+do_test auth-1.129 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.130 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r2 DELETE on t2 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {0 {}}
+do_test auth-1.131 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.132 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.133 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r2 DELETE on t2 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {0 {}}
+do_test auth-1.134 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.135 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TABLE tx(id);
+ CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN
+ INSERT INTO tx VALUES(NEW.rowid);
+ END;
+ }
+} {0 {}}
+do_test auth-1.136.1 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.136.2 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type='trigger'
+ }
+} {r2}
+do_test auth-1.136.3 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ lappend ::authargs $code $arg1 $arg2 $arg3 $arg4
+ return SQLITE_OK
+ }
+ set ::authargs {}
+ execsql {
+ INSERT INTO t2 VALUES(1,2,3);
+ }
+ set ::authargs
+} {SQLITE_INSERT t2 {} main {} SQLITE_INSERT tx {} main r2 SQLITE_READ t2 ROWID main r2}
+do_test auth-1.136.4 {
+ execsql {
+ SELECT * FROM tx;
+ }
+} {3}
+do_test auth-1.137 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 tx r2}
+do_test auth-1.138 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r1 DELETE on t1 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {1 {not authorized}}
+do_test auth-1.139 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.140 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1}
+do_test auth-1.141 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r1 DELETE on t1 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {1 {not authorized}}
+do_test auth-1.142 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1}
+do_test auth-1.143 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r1 DELETE on t1 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {0 {}}
+do_test auth-1.144 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.145 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1}
+do_test auth-1.146 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r1 DELETE on t1 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {0 {}}
+do_test auth-1.147 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1}
+do_test auth-1.148 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ CREATE TRIGGER r1 DELETE on t1 BEGIN
+ SELECT NULL;
+ END;
+ }
+} {0 {}}
+do_test auth-1.149 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.150 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1 r1}
+
+do_test auth-1.151 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r2}
+} {1 {not authorized}}
+do_test auth-1.152 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 tx r2}
+do_test auth-1.153 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r2}
+} {1 {not authorized}}
+do_test auth-1.154 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.155 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 tx r2}
+do_test auth-1.156 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r2}
+} {0 {}}
+do_test auth-1.157 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 tx r2}
+do_test auth-1.158 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r2}
+} {0 {}}
+do_test auth-1.159 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.160 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 tx r2}
+do_test auth-1.161 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r2}
+} {0 {}}
+do_test auth-1.162 {
+ set ::authargs
+} {r2 t2 main {}}
+do_test auth-1.163 {
+ execsql {
+ DROP TABLE tx;
+ DELETE FROM t2 WHERE a=1 AND b=2 AND c=3;
+ SELECT name FROM sqlite_master;
+ }
+} {t2}
+
+do_test auth-1.164 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r1}
+} {1 {not authorized}}
+do_test auth-1.165 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1 r1}
+do_test auth-1.166 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r1}
+} {1 {not authorized}}
+do_test auth-1.167 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.168 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1 r1}
+do_test auth-1.169 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r1}
+} {0 {}}
+do_test auth-1.170 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1 r1}
+do_test auth-1.171 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r1}
+} {0 {}}
+do_test auth-1.172 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.173 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1 r1}
+do_test auth-1.174 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_TRIGGER"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP TRIGGER r1}
+} {0 {}}
+do_test auth-1.175 {
+ set ::authargs
+} {r1 t1 temp {}}
+do_test auth-1.176 {
+ execsql {SELECT name FROM sqlite_temp_master}
+} {t1}
+} ;# ifcapable trigger
+
+do_test auth-1.177 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i2 ON t2(a)}
+} {1 {not authorized}}
+do_test auth-1.178 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.179 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.180 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i2 ON t2(a)}
+} {1 {not authorized}}
+do_test auth-1.181 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.182 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i2 ON t2(b)}
+} {0 {}}
+do_test auth-1.183 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.184 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.185 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i2 ON t2(b)}
+} {0 {}}
+do_test auth-1.186 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+do_test auth-1.187 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i2 ON t2(a)}
+} {0 {}}
+do_test auth-1.188 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.189 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 i2}
+
+ifcapable tempdb {
+ do_test auth-1.190 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i1 ON t1(a)}
+ } {1 {not authorized}}
+ do_test auth-1.191 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.192 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.193 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i1 ON t1(b)}
+ } {1 {not authorized}}
+ do_test auth-1.194 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.195 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i1 ON t1(b)}
+ } {0 {}}
+ do_test auth-1.196 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.197 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.198 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_INSERT" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i1 ON t1(c)}
+ } {0 {}}
+ do_test auth-1.199 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+ do_test auth-1.200 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_CREATE_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {CREATE INDEX i1 ON t1(a)}
+ } {0 {}}
+ do_test auth-1.201 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.202 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 i1}
+}
+
+do_test auth-1.203 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i2}
+} {1 {not authorized}}
+do_test auth-1.204 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 i2}
+do_test auth-1.205 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i2}
+} {1 {not authorized}}
+do_test auth-1.206 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.207 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 i2}
+do_test auth-1.208 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i2}
+} {0 {}}
+do_test auth-1.209 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 i2}
+do_test auth-1.210 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i2}
+} {0 {}}
+do_test auth-1.211 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.212 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2 i2}
+do_test auth-1.213 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i2}
+} {0 {}}
+do_test auth-1.214 {
+ set ::authargs
+} {i2 t2 main {}}
+do_test auth-1.215 {
+ execsql {SELECT name FROM sqlite_master}
+} {t2}
+
+ifcapable tempdb {
+ do_test auth-1.216 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i1}
+ } {1 {not authorized}}
+ do_test auth-1.217 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 i1}
+ do_test auth-1.218 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i1}
+ } {1 {not authorized}}
+ do_test auth-1.219 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.220 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 i1}
+ do_test auth-1.221 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DELETE" && $arg1=="sqlite_temp_master"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i1}
+ } {0 {}}
+ do_test auth-1.222 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 i1}
+ do_test auth-1.223 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i1}
+ } {0 {}}
+ do_test auth-1.224 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.225 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1 i1}
+ do_test auth-1.226 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DROP_TEMP_INDEX"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {DROP INDEX i1}
+ } {0 {}}
+ do_test auth-1.227 {
+ set ::authargs
+ } {i1 t1 temp {}}
+ do_test auth-1.228 {
+ execsql {SELECT name FROM sqlite_temp_master}
+ } {t1}
+}
+
+do_test auth-1.229 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_PRAGMA"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {PRAGMA full_column_names=on}
+} {1 {not authorized}}
+do_test auth-1.230 {
+ set ::authargs
+} {full_column_names on {} {}}
+do_test auth-1.231 {
+ execsql2 {SELECT a FROM t2}
+} {a 11 a 7}
+do_test auth-1.232 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_PRAGMA"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {PRAGMA full_column_names=on}
+} {0 {}}
+do_test auth-1.233 {
+ set ::authargs
+} {full_column_names on {} {}}
+do_test auth-1.234 {
+ execsql2 {SELECT a FROM t2}
+} {a 11 a 7}
+do_test auth-1.235 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_PRAGMA"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {PRAGMA full_column_names=on}
+} {0 {}}
+do_test auth-1.236 {
+ execsql2 {SELECT a FROM t2}
+} {t2.a 11 t2.a 7}
+do_test auth-1.237 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_PRAGMA"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {PRAGMA full_column_names=OFF}
+} {0 {}}
+do_test auth-1.238 {
+ set ::authargs
+} {full_column_names OFF {} {}}
+do_test auth-1.239 {
+ execsql2 {SELECT a FROM t2}
+} {a 11 a 7}
+
+do_test auth-1.240 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_TRANSACTION"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {BEGIN}
+} {1 {not authorized}}
+do_test auth-1.241 {
+ set ::authargs
+} {BEGIN {} {} {}}
+do_test auth-1.242 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_TRANSACTION" && $arg1!="BEGIN"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {BEGIN; INSERT INTO t2 VALUES(44,55,66); COMMIT}
+} {1 {not authorized}}
+do_test auth-1.243 {
+ set ::authargs
+} {COMMIT {} {} {}}
+do_test auth-1.244 {
+ execsql {SELECT * FROM t2}
+} {11 2 33 7 8 9 44 55 66}
+do_test auth-1.245 {
+ catchsql {ROLLBACK}
+} {1 {not authorized}}
+do_test auth-1.246 {
+ set ::authargs
+} {ROLLBACK {} {} {}}
+do_test auth-1.247 {
+ catchsql {END TRANSACTION}
+} {1 {not authorized}}
+do_test auth-1.248 {
+ set ::authargs
+} {COMMIT {} {} {}}
+do_test auth-1.249 {
+ db authorizer {}
+ catchsql {ROLLBACK}
+} {0 {}}
+do_test auth-1.250 {
+ execsql {SELECT * FROM t2}
+} {11 2 33 7 8 9}
+
+# ticket #340 - authorization for ATTACH and DETACH.
+#
+ifcapable attach {
+ do_test auth-1.251 {
+ db authorizer ::auth
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ATTACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ATTACH DATABASE ':memory:' AS test1
+ }
+ } {0 {}}
+ do_test auth-1.252 {
+ set ::authargs
+ } {:memory: {} {} {}}
+ do_test auth-1.253 {
+ catchsql {DETACH DATABASE test1}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ATTACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ATTACH DATABASE ':memory:' AS test1;
+ }
+ } {1 {not authorized}}
+ do_test auth-1.254 {
+ lindex [execsql {PRAGMA database_list}] 7
+ } {}
+ do_test auth-1.255 {
+ catchsql {DETACH DATABASE test1}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ATTACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ATTACH DATABASE ':memory:' AS test1;
+ }
+ } {0 {}}
+ do_test auth-1.256 {
+ lindex [execsql {PRAGMA database_list}] 7
+ } {}
+ do_test auth-1.257 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DETACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ execsql {ATTACH DATABASE ':memory:' AS test1}
+ catchsql {
+ DETACH DATABASE test1;
+ }
+ } {0 {}}
+ do_test auth-1.258 {
+ lindex [execsql {PRAGMA database_list}] 7
+ } {}
+ do_test auth-1.259 {
+ execsql {ATTACH DATABASE ':memory:' AS test1}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DETACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ DETACH DATABASE test1;
+ }
+ } {0 {}}
+ ifcapable tempdb {
+ ifcapable schema_pragmas {
+ do_test auth-1.260 {
+ lindex [execsql {PRAGMA database_list}] 7
+ } {test1}
+ } ;# ifcapable schema_pragmas
+ do_test auth-1.261 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_DETACH"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ DETACH DATABASE test1;
+ }
+ } {1 {not authorized}}
+ ifcapable schema_pragmas {
+ do_test auth-1.262 {
+ lindex [execsql {PRAGMA database_list}] 7
+ } {test1}
+ } ;# ifcapable schema_pragmas
+ db authorizer {}
+ execsql {DETACH DATABASE test1}
+ db authorizer ::auth
+
+ # Authorization for ALTER TABLE. These tests are omitted if the library
+ # was built without ALTER TABLE support.
+ ifcapable altertable {
+
+ do_test auth-1.263 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t1 RENAME TO t1x
+ }
+ } {0 {}}
+ do_test auth-1.264 {
+ execsql {SELECT name FROM sqlite_temp_master WHERE type='table'}
+ } {t1x}
+ do_test auth-1.265 {
+ set authargs
+ } {temp t1 {} {}}
+ do_test auth-1.266 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t1x RENAME TO t1
+ }
+ } {0 {}}
+ do_test auth-1.267 {
+ execsql {SELECT name FROM sqlite_temp_master WHERE type='table'}
+ } {t1x}
+ do_test auth-1.268 {
+ set authargs
+ } {temp t1x {} {}}
+ do_test auth-1.269 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t1x RENAME TO t1
+ }
+ } {1 {not authorized}}
+ do_test auth-1.270 {
+ execsql {SELECT name FROM sqlite_temp_master WHERE type='table'}
+ } {t1x}
+
+ do_test auth-1.271 {
+ set authargs
+ } {temp t1x {} {}}
+ } ;# ifcapable altertable
+
+ } else {
+ db authorizer {}
+ db eval {
+ DETACH DATABASE test1;
+ }
+ }
+}
+
+ifcapable altertable {
+db authorizer {}
+catchsql {ALTER TABLE t1x RENAME TO t1}
+db authorizer ::auth
+do_test auth-1.272 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t2 RENAME TO t2x
+ }
+} {0 {}}
+do_test auth-1.273 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table'}
+} {t2x}
+do_test auth-1.274 {
+ set authargs
+} {main t2 {} {}}
+do_test auth-1.275 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t2x RENAME TO t2
+ }
+} {0 {}}
+do_test auth-1.276 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table'}
+} {t2x}
+do_test auth-1.277 {
+ set authargs
+} {main t2x {} {}}
+do_test auth-1.278 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t2x RENAME TO t2
+ }
+} {1 {not authorized}}
+do_test auth-1.279 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table'}
+} {t2x}
+do_test auth-1.280 {
+ set authargs
+} {main t2x {} {}}
+db authorizer {}
+catchsql {ALTER TABLE t2x RENAME TO t2}
+
+} ;# ifcapable altertable
+
+# Test the authorization callbacks for the REINDEX command.
+ifcapable reindex {
+
+proc auth {code args} {
+ if {$code=="SQLITE_REINDEX"} {
+ set ::authargs [concat $::authargs $args]
+ }
+ return SQLITE_OK
+}
+db authorizer auth
+do_test auth-1.281 {
+ execsql {
+ CREATE TABLE t3(a PRIMARY KEY, b, c);
+ CREATE INDEX t3_idx1 ON t3(c COLLATE BINARY);
+ CREATE INDEX t3_idx2 ON t3(b COLLATE NOCASE);
+ }
+} {}
+do_test auth-1.282 {
+ set ::authargs {}
+ execsql {
+ REINDEX t3_idx1;
+ }
+ set ::authargs
+} {t3_idx1 {} main {}}
+do_test auth-1.283 {
+ set ::authargs {}
+ execsql {
+ REINDEX BINARY;
+ }
+ set ::authargs
+} {t3_idx1 {} main {} sqlite_autoindex_t3_1 {} main {}}
+do_test auth-1.284 {
+ set ::authargs {}
+ execsql {
+ REINDEX NOCASE;
+ }
+ set ::authargs
+} {t3_idx2 {} main {}}
+do_test auth-1.285 {
+ set ::authargs {}
+ execsql {
+ REINDEX t3;
+ }
+ set ::authargs
+} {t3_idx2 {} main {} t3_idx1 {} main {} sqlite_autoindex_t3_1 {} main {}}
+do_test auth-1.286 {
+ execsql {
+ DROP TABLE t3;
+ }
+} {}
+ifcapable tempdb {
+ do_test auth-1.287 {
+ execsql {
+ CREATE TEMP TABLE t3(a PRIMARY KEY, b, c);
+ CREATE INDEX t3_idx1 ON t3(c COLLATE BINARY);
+ CREATE INDEX t3_idx2 ON t3(b COLLATE NOCASE);
+ }
+ } {}
+ do_test auth-1.288 {
+ set ::authargs {}
+ execsql {
+ REINDEX temp.t3_idx1;
+ }
+ set ::authargs
+ } {t3_idx1 {} temp {}}
+ do_test auth-1.289 {
+ set ::authargs {}
+ execsql {
+ REINDEX BINARY;
+ }
+ set ::authargs
+ } {t3_idx1 {} temp {} sqlite_autoindex_t3_1 {} temp {}}
+ do_test auth-1.290 {
+ set ::authargs {}
+ execsql {
+ REINDEX NOCASE;
+ }
+ set ::authargs
+ } {t3_idx2 {} temp {}}
+ do_test auth-1.291 {
+ set ::authargs {}
+ execsql {
+ REINDEX temp.t3;
+ }
+ set ::authargs
+ } {t3_idx2 {} temp {} t3_idx1 {} temp {} sqlite_autoindex_t3_1 {} temp {}}
+ proc auth {code args} {
+ if {$code=="SQLITE_REINDEX"} {
+ set ::authargs [concat $::authargs $args]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ do_test auth-1.292 {
+ set ::authargs {}
+ catchsql {
+ REINDEX temp.t3;
+ }
+ } {1 {not authorized}}
+ do_test auth-1.293 {
+ execsql {
+ DROP TABLE t3;
+ }
+ } {}
+}
+
+} ;# ifcapable reindex
+
+ifcapable analyze {
+ proc auth {code args} {
+ if {$code=="SQLITE_ANALYZE"} {
+ set ::authargs [concat $::authargs $args]
+ }
+ return SQLITE_OK
+ }
+ do_test auth-1.294 {
+ set ::authargs {}
+ execsql {
+ CREATE TABLE t4(a,b,c);
+ CREATE INDEX t4i1 ON t4(a);
+ CREATE INDEX t4i2 ON t4(b,a,c);
+ INSERT INTO t4 VALUES(1,2,3);
+ ANALYZE;
+ }
+ set ::authargs
+ } {t4 {} main {}}
+ do_test auth-1.295 {
+ execsql {
+ SELECT count(*) FROM sqlite_stat1;
+ }
+ } 2
+ proc auth {code args} {
+ if {$code=="SQLITE_ANALYZE"} {
+ set ::authargs [concat $::authargs $args]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ do_test auth-1.296 {
+ set ::authargs {}
+ catchsql {
+ ANALYZE;
+ }
+ } {1 {not authorized}}
+ do_test auth-1.297 {
+ execsql {
+ SELECT count(*) FROM sqlite_stat1;
+ }
+ } 2
+} ;# ifcapable analyze
+
+
+# Authorization for ALTER TABLE ADD COLUMN.
+# These tests are omitted if the library
+# was built without ALTER TABLE support.
+ifcapable {altertable} {
+ do_test auth-1.300 {
+ execsql {CREATE TABLE t5(x)}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_OK
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t5 ADD COLUMN new_col_1;
+ }
+ } {0 {}}
+ do_test auth-1.301 {
+ set x [execsql {SELECT sql FROM sqlite_master WHERE name='t5'}]
+ regexp new_col_1 $x
+ } {1}
+ do_test auth-1.302 {
+ set authargs
+ } {main t5 {} {}}
+ do_test auth-1.303 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t5 ADD COLUMN new_col_2;
+ }
+ } {0 {}}
+ do_test auth-1.304 {
+ set x [execsql {SELECT sql FROM sqlite_master WHERE name='t5'}]
+ regexp new_col_2 $x
+ } {0}
+ do_test auth-1.305 {
+ set authargs
+ } {main t5 {} {}}
+ do_test auth-1.306 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_ALTER_TABLE"} {
+ set ::authargs [list $arg1 $arg2 $arg3 $arg4]
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ catchsql {
+ ALTER TABLE t5 ADD COLUMN new_col_3
+ }
+ } {1 {not authorized}}
+ do_test auth-1.307 {
+ set x [execsql {SELECT sql FROM sqlite_temp_master WHERE type='t5'}]
+ regexp new_col_3 $x
+ } {0}
+
+ do_test auth-1.308 {
+ set authargs
+ } {main t5 {} {}}
+ execsql {DROP TABLE t5}
+} ;# ifcapable altertable
+
+do_test auth-2.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t3" && $arg2=="x"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ db authorizer ::auth
+ execsql {CREATE TABLE t3(x INTEGER PRIMARY KEY, y, z)}
+ catchsql {SELECT * FROM t3}
+} {1 {access to t3.x is prohibited}}
+do_test auth-2.1 {
+ catchsql {SELECT y,z FROM t3}
+} {0 {}}
+do_test auth-2.2 {
+ catchsql {SELECT ROWID,y,z FROM t3}
+} {1 {access to t3.x is prohibited}}
+do_test auth-2.3 {
+ catchsql {SELECT OID,y,z FROM t3}
+} {1 {access to t3.x is prohibited}}
+do_test auth-2.4 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t3" && $arg2=="x"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ execsql {INSERT INTO t3 VALUES(44,55,66)}
+ catchsql {SELECT * FROM t3}
+} {0 {{} 55 66}}
+do_test auth-2.5 {
+ catchsql {SELECT rowid,y,z FROM t3}
+} {0 {{} 55 66}}
+do_test auth-2.6 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t3" && $arg2=="ROWID"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t3}
+} {0 {44 55 66}}
+do_test auth-2.7 {
+ catchsql {SELECT ROWID,y,z FROM t3}
+} {0 {44 55 66}}
+do_test auth-2.8 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="ROWID"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT ROWID,b,c FROM t2}
+} {0 {{} 2 33 {} 8 9}}
+do_test auth-2.9.1 {
+ # We have to flush the cache here in case the Tcl interface tries to
+ # reuse a statement compiled with sqlite3_prepare_v2(). In this case,
+ # the first error encountered is an SQLITE_SCHEMA error. Then, when
+ # trying to recompile the statement, the authorization error is encountered.
+ # If we do not flush the cache, the correct error message is returned, but
+ # the error code is SQLITE_SCHEMA, not SQLITE_ERROR as required by the test
+ # case after this one.
+ #
+ db cache flush
+
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="ROWID"} {
+ return bogus
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT ROWID,b,c FROM t2}
+} {1 {illegal return value (999) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
+do_test auth-2.9.2 {
+ db errorcode
+} {1}
+do_test auth-2.10 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_SELECT"} {
+ return bogus
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT ROWID,b,c FROM t2}
+} {1 {illegal return value (1) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
+do_test auth-2.11.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg2=="a"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2, t3}
+} {0 {{} 2 33 44 55 66 {} 8 9 44 55 66}}
+do_test auth-2.11.2 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg2=="x"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ catchsql {SELECT * FROM t2, t3}
+} {0 {11 2 33 {} 55 66 7 8 9 {} 55 66}}
+
+# Make sure the OLD and NEW pseudo-tables of a trigger get authorized.
+#
+ifcapable trigger {
+ do_test auth-3.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ return SQLITE_OK
+ }
+ execsql {
+ CREATE TABLE tx(a1,a2,b1,b2,c1,c2);
+ CREATE TRIGGER r1 AFTER UPDATE ON t2 FOR EACH ROW BEGIN
+ INSERT INTO tx VALUES(OLD.a,NEW.a,OLD.b,NEW.b,OLD.c,NEW.c);
+ END;
+ UPDATE t2 SET a=a+1;
+ SELECT * FROM tx;
+ }
+ } {11 12 2 2 33 33 7 8 8 8 9 9}
+ do_test auth-3.2 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="c"} {
+ return SQLITE_IGNORE
+ }
+ return SQLITE_OK
+ }
+ execsql {
+ DELETE FROM tx;
+ UPDATE t2 SET a=a+100;
+ SELECT * FROM tx;
+ }
+ } {12 112 2 2 {} {} 8 108 8 8 {} {}}
+} ;# ifcapable trigger
+
+# Make sure the names of views and triggers are passed on on arg4.
+#
+ifcapable trigger {
+do_test auth-4.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ lappend ::authargs $code $arg1 $arg2 $arg3 $arg4
+ return SQLITE_OK
+ }
+ set authargs {}
+ execsql {
+ UPDATE t2 SET a=a+1;
+ }
+ set authargs
+} [list \
+ SQLITE_READ t2 a main {} \
+ SQLITE_UPDATE t2 a main {} \
+ SQLITE_INSERT tx {} main r1 \
+ SQLITE_READ t2 a main r1 \
+ SQLITE_READ t2 a main r1 \
+ SQLITE_READ t2 b main r1 \
+ SQLITE_READ t2 b main r1 \
+ SQLITE_READ t2 c main r1 \
+ SQLITE_READ t2 c main r1]
+}
+
+ifcapable {view && trigger} {
+do_test auth-4.2 {
+ execsql {
+ CREATE VIEW v1 AS SELECT a+b AS x FROM t2;
+ CREATE TABLE v1chng(x1,x2);
+ CREATE TRIGGER r2 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO v1chng VALUES(OLD.x,NEW.x);
+ END;
+ SELECT * FROM v1;
+ }
+} {115 117}
+do_test auth-4.3 {
+ set authargs {}
+ execsql {
+ UPDATE v1 SET x=1 WHERE x=117
+ }
+ set authargs
+} [list \
+ SQLITE_UPDATE v1 x main {} \
+ SQLITE_INSERT v1chng {} main r2 \
+ SQLITE_READ v1 x main r2 \
+ SQLITE_READ v1 x main r2 \
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_READ t2 a main v1 \
+ SQLITE_READ t2 b main v1 \
+ SQLITE_SELECT {} {} {} {} \
+ SQLITE_READ v1 x main v1 \
+]
+do_test auth-4.4 {
+ execsql {
+ CREATE TRIGGER r3 INSTEAD OF DELETE ON v1 BEGIN
+ INSERT INTO v1chng VALUES(OLD.x,NULL);
+ END;
+ SELECT * FROM v1;
+ }
+} {115 117}
+do_test auth-4.5 {
+ set authargs {}
+ execsql {
+ DELETE FROM v1 WHERE x=117
+ }
+ set authargs
+} [list \
+ SQLITE_DELETE v1 {} main {} \
+ SQLITE_INSERT v1chng {} main r3 \
+ SQLITE_READ v1 x main r3 \
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_READ t2 a main v1 \
+ SQLITE_READ t2 b main v1 \
+ SQLITE_SELECT {} {} {} {} \
+ SQLITE_READ v1 x main v1 \
+]
+
+} ;# ifcapable view && trigger
+
+# Ticket #1338: Make sure authentication works in the presence of an AS
+# clause.
+#
+do_test auth-5.1 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ return SQLITE_OK
+ }
+ execsql {
+ SELECT count(a) AS cnt FROM t4 ORDER BY cnt
+ }
+} {1}
+
+# Ticket #1607
+#
+ifcapable compound&&subquery {
+ ifcapable trigger {
+ execsql {
+ DROP TABLE tx;
+ }
+ ifcapable view {
+ execsql {
+ DROP TABLE v1chng;
+ }
+ }
+ }
+ do_test auth-5.2 {
+ execsql {
+ SELECT name FROM (
+ SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master)
+ WHERE type='table'
+ ORDER BY name
+ }
+ } {sqlite_stat1 t1 t2 t3 t4}
+}
+
+
+rename proc {}
+rename proc_real proc
+
+
+finish_test
diff --git a/third_party/sqlite/test/auth2.test b/third_party/sqlite/test/auth2.test
new file mode 100755
index 0000000..98270ae
--- /dev/null
+++ b/third_party/sqlite/test/auth2.test
@@ -0,0 +1,168 @@
+# 2006 Aug 24
+#
+# 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 testing the sqlite3_set_authorizer() API
+# and related functionality.
+#
+# $Id: auth2.test,v 1.3 2008/07/02 13:13:53 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
+# defined during compilation.
+if {[catch {db auth {}} msg]} {
+ finish_test
+ return
+}
+
+do_test auth2-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ }
+ set ::flist {}
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_FUNCTION"} {
+ lappend ::flist $arg2
+ if {$arg2=="max"} {
+ return SQLITE_DENY
+ } elseif {$arg2=="min"} {
+ return SQLITE_IGNORE
+ } else {
+ return SQLITE_OK
+ }
+ }
+ return SQLITE_OK
+ }
+ db authorizer ::auth
+ catchsql {SELECT max(a,b,c) FROM t1}
+} {1 {not authorized to use function: max}}
+do_test auth2-1.2 {
+ set ::flist
+} max
+do_test auth2-1.3 {
+ set ::flist {}
+ catchsql {SELECT min(a,b,c) FROM t1}
+} {0 {{}}}
+do_test auth2-1.4 {
+ set ::flist
+} min
+do_test auth2-1.5 {
+ set ::flist {}
+ catchsql {SELECT coalesce(min(a,b,c),999) FROM t1}
+} {0 999}
+do_test auth2-1.6 {
+ set ::flist
+} {coalesce min}
+do_test auth2-1.7 {
+ set ::flist {}
+ catchsql {SELECT coalesce(a,b,c) FROM t1}
+} {0 1}
+do_test auth2-1.8 {
+ set ::flist
+} coalesce
+
+# Make sure the authorizer is not called when parsing the schema
+# and when computing the result set of a view.
+#
+db close
+sqlite3 db test.db
+sqlite3 db2 test.db
+proc auth {args} {
+ global authargs
+ append authargs $args\n
+ return SQLITE_OK
+}
+db auth auth
+do_test auth2-2.1 {
+ set ::authargs {}
+ db eval {
+ CREATE TABLE t2(x,y,z);
+ }
+ set ::authargs
+} {SQLITE_INSERT sqlite_master {} main {}
+SQLITE_CREATE_TABLE t2 {} main {}
+SQLITE_UPDATE sqlite_master type main {}
+SQLITE_UPDATE sqlite_master name main {}
+SQLITE_UPDATE sqlite_master tbl_name main {}
+SQLITE_UPDATE sqlite_master rootpage main {}
+SQLITE_UPDATE sqlite_master sql main {}
+SQLITE_READ sqlite_master ROWID main {}
+SQLITE_READ sqlite_master name main {}
+SQLITE_READ sqlite_master rootpage main {}
+SQLITE_READ sqlite_master sql main {}
+SQLITE_READ sqlite_master tbl_name main {}
+}
+do_test auth2-2.2 {
+ set ::authargs {}
+ db eval {
+ CREATE VIEW v2 AS SELECT x+y AS a, y+z AS b from t2;
+ }
+ set ::authargs
+} {SQLITE_INSERT sqlite_master {} main {}
+SQLITE_CREATE_VIEW v2 {} main {}
+SQLITE_UPDATE sqlite_master type main {}
+SQLITE_UPDATE sqlite_master name main {}
+SQLITE_UPDATE sqlite_master tbl_name main {}
+SQLITE_UPDATE sqlite_master rootpage main {}
+SQLITE_UPDATE sqlite_master sql main {}
+SQLITE_READ sqlite_master ROWID main {}
+SQLITE_READ sqlite_master name main {}
+SQLITE_READ sqlite_master rootpage main {}
+SQLITE_READ sqlite_master sql main {}
+SQLITE_READ sqlite_master tbl_name main {}
+}
+do_test auth2-2.3 {
+ set ::authargs {}
+ db eval {
+ SELECT a, b FROM v2;
+ }
+ set ::authargs
+} {SQLITE_SELECT {} {} {} {}
+SQLITE_READ v2 a main {}
+SQLITE_READ v2 b main {}
+SQLITE_READ t2 x main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 z main v2
+SQLITE_SELECT {} {} {} v2
+}
+do_test auth2-2.4 {
+ db2 eval {
+ CREATE TABLE t3(p,q,r);
+ }
+ set ::authargs {}
+ db eval {
+ SELECT b, a FROM v2;
+ }
+ set ::authargs
+} {SQLITE_SELECT {} {} {} {}
+SQLITE_READ v2 b main {}
+SQLITE_READ v2 a main {}
+SQLITE_READ t2 x main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 z main v2
+SQLITE_SELECT {} {} {} v2
+SQLITE_SELECT {} {} {} {}
+SQLITE_READ v2 b main {}
+SQLITE_READ v2 a main {}
+SQLITE_READ t2 x main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 y main v2
+SQLITE_READ t2 z main v2
+SQLITE_SELECT {} {} {} v2
+}
+db2 close
+
+finish_test
diff --git a/third_party/sqlite/test/autoinc.test b/third_party/sqlite/test/autoinc.test
new file mode 100755
index 0000000..1c19fc7
--- /dev/null
+++ b/third_party/sqlite/test/autoinc.test
@@ -0,0 +1,555 @@
+# 2004 November 12
+#
+# 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 testing the AUTOINCREMENT features.
+#
+# $Id: autoinc.test,v 1.12 2008/05/29 03:20:59 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If the library is not compiled with autoincrement support then
+# skip all tests in this file.
+#
+ifcapable {!autoinc} {
+ finish_test
+ return
+}
+
+# The database is initially empty.
+#
+do_test autoinc-1.1 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type='table';
+ }
+} {}
+
+# Add a table with the AUTOINCREMENT feature. Verify that the
+# SQLITE_SEQUENCE table gets created.
+#
+do_test autoinc-1.2 {
+ execsql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
+ SELECT name FROM sqlite_master WHERE type='table';
+ }
+} {t1 sqlite_sequence}
+
+# The SQLITE_SEQUENCE table is initially empty
+#
+do_test autoinc-1.3 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {}
+
+# Close and reopen the database. Verify that everything is still there.
+#
+do_test autoinc-1.4 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {}
+
+# We are not allowed to drop the sqlite_sequence table.
+#
+do_test autoinc-1.5 {
+ catchsql {DROP TABLE sqlite_sequence}
+} {1 {table sqlite_sequence may not be dropped}}
+do_test autoinc-1.6 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table'}
+} {t1 sqlite_sequence}
+
+# Insert an entries into the t1 table and make sure the largest key
+# is always recorded in the sqlite_sequence table.
+#
+do_test autoinc-2.1 {
+ execsql {
+ SELECT * FROM sqlite_sequence
+ }
+} {}
+do_test autoinc-2.2 {
+ execsql {
+ INSERT INTO t1 VALUES(12,34);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 12}
+do_test autoinc-2.3 {
+ execsql {
+ INSERT INTO t1 VALUES(1,23);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 12}
+do_test autoinc-2.4 {
+ execsql {
+ INSERT INTO t1 VALUES(123,456);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 123}
+do_test autoinc-2.5 {
+ execsql {
+ INSERT INTO t1 VALUES(NULL,567);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 124}
+do_test autoinc-2.6 {
+ execsql {
+ DELETE FROM t1 WHERE y=567;
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 124}
+do_test autoinc-2.7 {
+ execsql {
+ INSERT INTO t1 VALUES(NULL,567);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 125}
+do_test autoinc-2.8 {
+ execsql {
+ DELETE FROM t1;
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 125}
+do_test autoinc-2.9 {
+ execsql {
+ INSERT INTO t1 VALUES(12,34);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 125}
+do_test autoinc-2.10 {
+ execsql {
+ INSERT INTO t1 VALUES(125,456);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 125}
+do_test autoinc-2.11 {
+ execsql {
+ INSERT INTO t1 VALUES(-1234567,-1);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 125}
+do_test autoinc-2.12 {
+ execsql {
+ INSERT INTO t1 VALUES(234,5678);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 234}
+do_test autoinc-2.13 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(NULL,1);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 235}
+do_test autoinc-2.14 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {235 1}
+
+# Manually change the autoincrement values in sqlite_sequence.
+#
+do_test autoinc-2.20 {
+ execsql {
+ UPDATE sqlite_sequence SET seq=1234 WHERE name='t1';
+ INSERT INTO t1 VALUES(NULL,2);
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2}
+do_test autoinc-2.21 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 1235}
+do_test autoinc-2.22 {
+ execsql {
+ UPDATE sqlite_sequence SET seq=NULL WHERE name='t1';
+ INSERT INTO t1 VALUES(NULL,3);
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2 1236 3}
+do_test autoinc-2.23 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 1236}
+do_test autoinc-2.24 {
+ execsql {
+ UPDATE sqlite_sequence SET seq='a-string' WHERE name='t1';
+ INSERT INTO t1 VALUES(NULL,4);
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2 1236 3 1237 4}
+do_test autoinc-2.25 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 1237}
+do_test autoinc-2.26 {
+ execsql {
+ DELETE FROM sqlite_sequence WHERE name='t1';
+ INSERT INTO t1 VALUES(NULL,5);
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2 1236 3 1237 4 1238 5}
+do_test autoinc-2.27 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 1238}
+do_test autoinc-2.28 {
+ execsql {
+ UPDATE sqlite_sequence SET seq='12345678901234567890'
+ WHERE name='t1';
+ INSERT INTO t1 VALUES(NULL,6);
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2 1236 3 1237 4 1238 5 1239 6}
+do_test autoinc-2.29 {
+ execsql {
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 1239}
+
+# Test multi-row inserts
+#
+do_test autoinc-2.50 {
+ execsql {
+ DELETE FROM t1 WHERE y>=3;
+ INSERT INTO t1 SELECT NULL, y+2 FROM t1;
+ SELECT * FROM t1;
+ }
+} {235 1 1235 2 1240 3 1241 4}
+do_test autoinc-2.51 {
+ execsql {
+ SELECT * FROM sqlite_sequence
+ }
+} {t1 1241}
+
+ifcapable tempdb {
+ do_test autoinc-2.52 {
+ execsql {
+ CREATE TEMP TABLE t2 AS SELECT y FROM t1;
+ }
+ execsql {
+ INSERT INTO t1 SELECT NULL, y+4 FROM t2;
+ SELECT * FROM t1;
+ }
+ } {235 1 1235 2 1240 3 1241 4 1242 5 1243 6 1244 7 1245 8}
+ do_test autoinc-2.53 {
+ execsql {
+ SELECT * FROM sqlite_sequence
+ }
+ } {t1 1245}
+ do_test autoinc-2.54 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT NULL, y FROM t2;
+ SELECT * FROM t1;
+ }
+ } {1246 1 1247 2 1248 3 1249 4}
+ do_test autoinc-2.55 {
+ execsql {
+ SELECT * FROM sqlite_sequence
+ }
+ } {t1 1249}
+}
+
+# Create multiple AUTOINCREMENT tables. Make sure all sequences are
+# tracked separately and do not interfere with one another.
+#
+do_test autoinc-2.70 {
+ catchsql {
+ DROP TABLE t2;
+ }
+ execsql {
+ CREATE TABLE t2(d, e INTEGER PRIMARY KEY AUTOINCREMENT, f);
+ INSERT INTO t2(d) VALUES(1);
+ SELECT * FROM sqlite_sequence;
+ }
+} [ifcapable tempdb {list t1 1249 t2 1} else {list t1 1241 t2 1}]
+do_test autoinc-2.71 {
+ execsql {
+ INSERT INTO t2(d) VALUES(2);
+ SELECT * FROM sqlite_sequence;
+ }
+} [ifcapable tempdb {list t1 1249 t2 2} else {list t1 1241 t2 2}]
+do_test autoinc-2.72 {
+ execsql {
+ INSERT INTO t1(x) VALUES(10000);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 10000 t2 2}
+do_test autoinc-2.73 {
+ execsql {
+ CREATE TABLE t3(g INTEGER PRIMARY KEY AUTOINCREMENT, h);
+ INSERT INTO t3(h) VALUES(1);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 10000 t2 2 t3 1}
+do_test autoinc-2.74 {
+ execsql {
+ INSERT INTO t2(d,e) VALUES(3,100);
+ SELECT * FROM sqlite_sequence;
+ }
+} {t1 10000 t2 100 t3 1}
+
+
+# When a table with an AUTOINCREMENT is deleted, the corresponding entry
+# in the SQLITE_SEQUENCE table should also be deleted. But the SQLITE_SEQUENCE
+# table itself should remain behind.
+#
+do_test autoinc-3.1 {
+ execsql {SELECT name FROM sqlite_sequence}
+} {t1 t2 t3}
+do_test autoinc-3.2 {
+ execsql {
+ DROP TABLE t1;
+ SELECT name FROM sqlite_sequence;
+ }
+} {t2 t3}
+do_test autoinc-3.3 {
+ execsql {
+ DROP TABLE t3;
+ SELECT name FROM sqlite_sequence;
+ }
+} {t2}
+do_test autoinc-3.4 {
+ execsql {
+ DROP TABLE t2;
+ SELECT name FROM sqlite_sequence;
+ }
+} {}
+
+# AUTOINCREMENT on TEMP tables.
+#
+ifcapable tempdb {
+ do_test autoinc-4.1 {
+ execsql {
+ SELECT 1, name FROM sqlite_master WHERE type='table';
+ SELECT 2, name FROM sqlite_temp_master WHERE type='table';
+ }
+ } {1 sqlite_sequence}
+ do_test autoinc-4.2 {
+ execsql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
+ CREATE TEMP TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
+ SELECT 1, name FROM sqlite_master WHERE type='table';
+ SELECT 2, name FROM sqlite_temp_master WHERE type='table';
+ }
+ } {1 sqlite_sequence 1 t1 2 t3 2 sqlite_sequence}
+ do_test autoinc-4.3 {
+ execsql {
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {}
+ do_test autoinc-4.4 {
+ execsql {
+ INSERT INTO t1 VALUES(10,1);
+ INSERT INTO t3 VALUES(20,2);
+ INSERT INTO t1 VALUES(NULL,3);
+ INSERT INTO t3 VALUES(NULL,4);
+ }
+ } {}
+
+ ifcapable compound {
+ do_test autoinc-4.4.1 {
+ execsql {
+ SELECT * FROM t1 UNION ALL SELECT * FROM t3;
+ }
+ } {10 1 11 3 20 2 21 4}
+ } ;# ifcapable compound
+
+ do_test autoinc-4.5 {
+ execsql {
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {1 t1 11 2 t3 21}
+ do_test autoinc-4.6 {
+ execsql {
+ INSERT INTO t1 SELECT * FROM t3;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {1 t1 21 2 t3 21}
+ do_test autoinc-4.7 {
+ execsql {
+ INSERT INTO t3 SELECT x+100, y FROM t1;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {1 t1 21 2 t3 121}
+ do_test autoinc-4.8 {
+ execsql {
+ DROP TABLE t3;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {1 t1 21}
+ do_test autoinc-4.9 {
+ execsql {
+ CREATE TEMP TABLE t2(p INTEGER PRIMARY KEY AUTOINCREMENT, q);
+ INSERT INTO t2 SELECT * FROM t1;
+ DROP TABLE t1;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {2 t2 21}
+ do_test autoinc-4.10 {
+ execsql {
+ DROP TABLE t2;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ }
+ } {}
+}
+
+# Make sure AUTOINCREMENT works on ATTACH-ed tables.
+#
+ifcapable tempdb&&attach {
+ do_test autoinc-5.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ sqlite3 db2 test2.db
+ execsql {
+ CREATE TABLE t4(m INTEGER PRIMARY KEY AUTOINCREMENT, n);
+ CREATE TABLE t5(o, p INTEGER PRIMARY KEY AUTOINCREMENT);
+ } db2;
+ execsql {
+ ATTACH 'test2.db' as aux;
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ SELECT 3, * FROM aux.sqlite_sequence;
+ }
+ } {}
+ do_test autoinc-5.2 {
+ execsql {
+ INSERT INTO t4 VALUES(NULL,1);
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ SELECT 3, * FROM aux.sqlite_sequence;
+ }
+ } {3 t4 1}
+ do_test autoinc-5.3 {
+ execsql {
+ INSERT INTO t5 VALUES(100,200);
+ SELECT * FROM sqlite_sequence
+ } db2
+ } {t4 1 t5 200}
+ do_test autoinc-5.4 {
+ execsql {
+ SELECT 1, * FROM main.sqlite_sequence;
+ SELECT 2, * FROM temp.sqlite_sequence;
+ SELECT 3, * FROM aux.sqlite_sequence;
+ }
+ } {3 t4 1 3 t5 200}
+}
+
+# Requirement REQ00310: Make sure an insert fails if the sequence is
+# already at its maximum value.
+#
+ifcapable {rowid32} {
+ do_test autoinc-6.1 {
+ execsql {
+ CREATE TABLE t6(v INTEGER PRIMARY KEY AUTOINCREMENT, w);
+ INSERT INTO t6 VALUES(2147483647,1);
+ SELECT seq FROM main.sqlite_sequence WHERE name='t6';
+ }
+ } 2147483647
+}
+ifcapable {!rowid32} {
+ do_test autoinc-6.1 {
+ execsql {
+ CREATE TABLE t6(v INTEGER PRIMARY KEY AUTOINCREMENT, w);
+ INSERT INTO t6 VALUES(9223372036854775807,1);
+ SELECT seq FROM main.sqlite_sequence WHERE name='t6';
+ }
+ } 9223372036854775807
+}
+do_test autoinc-6.2 {
+ catchsql {
+ INSERT INTO t6 VALUES(NULL,1);
+ }
+} {1 {database or disk is full}}
+
+# Allow the AUTOINCREMENT keyword inside the parentheses
+# on a separate PRIMARY KEY designation.
+#
+do_test autoinc-7.1 {
+ execsql {
+ CREATE TABLE t7(x INTEGER, y REAL, PRIMARY KEY(x AUTOINCREMENT));
+ INSERT INTO t7(y) VALUES(123);
+ INSERT INTO t7(y) VALUES(234);
+ DELETE FROM t7;
+ INSERT INTO t7(y) VALUES(345);
+ SELECT * FROM t7;
+ }
+} {3 345.0}
+
+# Test that if the AUTOINCREMENT is applied to a non integer primary key
+# the error message is sensible.
+do_test autoinc-7.2 {
+ catchsql {
+ CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT);
+ }
+} {1 {AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY}}
+
+
+# Ticket #1283. Make sure that preparing but never running a statement
+# that creates the sqlite_sequence table does not mess up the database.
+#
+do_test autoinc-8.1 {
+ catch {db2 close}
+ catch {db close}
+ file delete -force test.db
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ set STMT [sqlite3_prepare $DB {
+ CREATE TABLE t1(
+ x INTEGER PRIMARY KEY AUTOINCREMENT
+ )
+ } -1 TAIL]
+ sqlite3_finalize $STMT
+ set STMT [sqlite3_prepare $DB {
+ CREATE TABLE t1(
+ x INTEGER PRIMARY KEY AUTOINCREMENT
+ )
+ } -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {
+ INSERT INTO t1 VALUES(NULL);
+ SELECT * FROM t1;
+ }
+} {1}
+
+# Ticket #3148
+# Make sure the sqlite_sequence table is not damaged when doing
+# an empty insert - an INSERT INTO ... SELECT ... where the SELECT
+# clause returns an empty set.
+#
+do_test autoinc-9.1 {
+ db eval {
+ CREATE TABLE t2(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
+ INSERT INTO t2 VALUES(NULL, 1);
+ CREATE TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
+ INSERT INTO t3 SELECT * FROM t2 WHERE y>1;
+
+ SELECT * FROM sqlite_sequence WHERE name='t3';
+ }
+} {t3 0}
+
+
+finish_test
diff --git a/third_party/sqlite/test/autovacuum.test b/third_party/sqlite/test/autovacuum.test
new file mode 100755
index 0000000..9b3ab4a
--- /dev/null
+++ b/third_party/sqlite/test/autovacuum.test
@@ -0,0 +1,665 @@
+# 2001 September 15
+#
+# 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 file is testing the SELECT statement.
+#
+# $Id: autovacuum.test,v 1.27 2008/08/02 03:50:39 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum || !pragma} {
+ finish_test
+ return
+}
+
+# Return a string $len characters long. The returned string is $char repeated
+# over and over. For example, [make_str abc 8] returns "abcabcab".
+proc make_str {char len} {
+ set str [string repeat $char. $len]
+ return [string range $str 0 [expr $len-1]]
+}
+
+# Return the number of pages in the file test.db by looking at the file system.
+proc file_pages {} {
+ return [expr [file size test.db] / 1024]
+}
+
+#-------------------------------------------------------------------------
+# Test cases autovacuum-1.* work as follows:
+#
+# 1. A table with a single indexed field is created.
+# 2. Approximately 20 rows are inserted into the table. Each row is long
+# enough such that it uses at least 2 overflow pages for both the table
+# and index entry.
+# 3. The rows are deleted in a psuedo-random order. Sometimes only one row
+# is deleted per transaction, sometimes more than one.
+# 4. After each transaction the table data is checked to ensure it is correct
+# and a "PRAGMA integrity_check" is executed.
+# 5. Once all the rows are deleted the file is checked to make sure it
+# consists of exactly 4 pages.
+#
+# Steps 2-5 are repeated for a few different psuedo-random delete patterns
+# (defined by the $delete_orders list).
+set delete_orders [list]
+lappend delete_orders {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
+lappend delete_orders {20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1}
+lappend delete_orders {8 18 2 4 14 11 13 3 10 7 9 5 12 17 19 15 20 6 16 1}
+lappend delete_orders {10 3 11 17 19 20 7 4 13 6 1 14 16 12 9 18 8 15 5 2}
+lappend delete_orders {{1 2 3 4 5 6 7 8 9 10} {11 12 13 14 15 16 17 18 19 20}}
+lappend delete_orders {{19 8 17 15} {16 11 9 14} {18 5 3 1} {13 20 7 2} {6 12}}
+
+# The length of each table entry.
+# set ENTRY_LEN 3500
+set ENTRY_LEN 3500
+
+do_test autovacuum-1.1 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE av1(a);
+ CREATE INDEX av1_idx ON av1(a);
+ }
+} {}
+
+set tn 0
+foreach delete_order $delete_orders {
+ incr tn
+
+ # Set up the table.
+ set ::tbl_data [list]
+ foreach i [lsort -integer [eval concat $delete_order]] {
+ execsql "INSERT INTO av1 (oid, a) VALUES($i, '[make_str $i $ENTRY_LEN]')"
+ lappend ::tbl_data [make_str $i $ENTRY_LEN]
+ }
+
+ # Make sure the integrity check passes with the initial data.
+ ifcapable {integrityck} {
+ do_test autovacuum-1.$tn.1 {
+ execsql {
+ pragma integrity_check
+ }
+ } {ok}
+ }
+
+ foreach delete $delete_order {
+ # Delete one set of rows from the table.
+ do_test autovacuum-1.$tn.($delete).1 {
+ execsql "
+ DELETE FROM av1 WHERE oid = [join $delete " OR oid = "]
+ "
+ } {}
+
+ # Do the integrity check.
+ ifcapable {integrityck} {
+ do_test autovacuum-1.$tn.($delete).2 {
+ execsql {
+ pragma integrity_check
+ }
+ } {ok}
+ }
+ # Ensure the data remaining in the table is what was expected.
+ foreach d $delete {
+ set idx [lsearch $::tbl_data [make_str $d $ENTRY_LEN]]
+ set ::tbl_data [lreplace $::tbl_data $idx $idx]
+ }
+ do_test autovacuum-1.$tn.($delete).3 {
+ execsql {
+ select a from av1
+ }
+ } $::tbl_data
+ }
+
+ # All rows have been deleted. Ensure the file has shrunk to 4 pages.
+ do_test autovacuum-1.$tn.3 {
+ file_pages
+ } {4}
+}
+
+#---------------------------------------------------------------------------
+# Tests cases autovacuum-2.* test that root pages are allocated
+# and deallocated correctly at the start of the file. Operation is roughly as
+# follows:
+#
+# autovacuum-2.1.*: Drop the tables that currently exist in the database.
+# autovacuum-2.2.*: Create some tables. Ensure that data pages can be
+# moved correctly to make space for new root-pages.
+# autovacuum-2.3.*: Drop one of the tables just created (not the last one),
+# and check that one of the other tables is moved to
+# the free root-page location.
+# autovacuum-2.4.*: Check that a table can be created correctly when the
+# root-page it requires is on the free-list.
+# autovacuum-2.5.*: Check that a table with indices can be dropped. This
+# is slightly tricky because dropping one of the
+# indices/table btrees could move the root-page of another.
+# The code-generation layer of SQLite overcomes this problem
+# by dropping the btrees in descending order of root-pages.
+# This test ensures that this actually happens.
+#
+do_test autovacuum-2.1.1 {
+ execsql {
+ DROP TABLE av1;
+ }
+} {}
+do_test autovacuum-2.1.2 {
+ file_pages
+} {1}
+
+# Create a table and put some data in it.
+do_test autovacuum-2.2.1 {
+ execsql {
+ CREATE TABLE av1(x);
+ SELECT rootpage FROM sqlite_master ORDER BY rootpage;
+ }
+} {3}
+do_test autovacuum-2.2.2 {
+ execsql "
+ INSERT INTO av1 VALUES('[make_str abc 3000]');
+ INSERT INTO av1 VALUES('[make_str def 3000]');
+ INSERT INTO av1 VALUES('[make_str ghi 3000]');
+ INSERT INTO av1 VALUES('[make_str jkl 3000]');
+ "
+ set ::av1_data [db eval {select * from av1}]
+ file_pages
+} {15}
+
+# Create another table. Check it is located immediately after the first.
+# This test case moves the second page in an over-flow chain.
+do_test autovacuum-2.2.3 {
+ execsql {
+ CREATE TABLE av2(x);
+ SELECT rootpage FROM sqlite_master ORDER BY rootpage;
+ }
+} {3 4}
+do_test autovacuum-2.2.4 {
+ file_pages
+} {16}
+
+# Create another table. Check it is located immediately after the second.
+# This test case moves the first page in an over-flow chain.
+do_test autovacuum-2.2.5 {
+ execsql {
+ CREATE TABLE av3(x);
+ SELECT rootpage FROM sqlite_master ORDER BY rootpage;
+ }
+} {3 4 5}
+do_test autovacuum-2.2.6 {
+ file_pages
+} {17}
+
+# Create another table. Check it is located immediately after the second.
+# This test case moves a btree leaf page.
+do_test autovacuum-2.2.7 {
+ execsql {
+ CREATE TABLE av4(x);
+ SELECT rootpage FROM sqlite_master ORDER BY rootpage;
+ }
+} {3 4 5 6}
+do_test autovacuum-2.2.8 {
+ file_pages
+} {18}
+do_test autovacuum-2.2.9 {
+ execsql {
+ select * from av1
+ }
+} $av1_data
+
+do_test autovacuum-2.3.1 {
+ execsql {
+ INSERT INTO av2 SELECT 'av1' || x FROM av1;
+ INSERT INTO av3 SELECT 'av2' || x FROM av1;
+ INSERT INTO av4 SELECT 'av3' || x FROM av1;
+ }
+ set ::av2_data [execsql {select x from av2}]
+ set ::av3_data [execsql {select x from av3}]
+ set ::av4_data [execsql {select x from av4}]
+ file_pages
+} {54}
+do_test autovacuum-2.3.2 {
+ execsql {
+ DROP TABLE av2;
+ SELECT rootpage FROM sqlite_master ORDER BY rootpage;
+ }
+} {3 4 5}
+do_test autovacuum-2.3.3 {
+ file_pages
+} {41}
+do_test autovacuum-2.3.4 {
+ execsql {
+ SELECT x FROM av3;
+ }
+} $::av3_data
+do_test autovacuum-2.3.5 {
+ execsql {
+ SELECT x FROM av4;
+ }
+} $::av4_data
+
+# Drop all the tables in the file. This puts all pages except the first 2
+# (the sqlite_master root-page and the first pointer map page) on the
+# free-list.
+do_test autovacuum-2.4.1 {
+ execsql {
+ DROP TABLE av1;
+ DROP TABLE av3;
+ BEGIN;
+ DROP TABLE av4;
+ }
+ file_pages
+} {15}
+do_test autovacuum-2.4.2 {
+ for {set i 3} {$i<=10} {incr i} {
+ execsql "CREATE TABLE av$i (x)"
+ }
+ file_pages
+} {15}
+do_test autovacuum-2.4.3 {
+ execsql {
+ SELECT rootpage FROM sqlite_master ORDER by rootpage
+ }
+} {3 4 5 6 7 8 9 10}
+
+# Right now there are 5 free pages in the database. Consume and then free
+# a 520 pages. Then create 520 tables. This ensures that at least some of the
+# desired root-pages reside on the second free-list trunk page, and that the
+# trunk itself is required at some point.
+do_test autovacuum-2.4.4 {
+ execsql "
+ INSERT INTO av3 VALUES ('[make_str abcde [expr 1020*520 + 500]]');
+ DELETE FROM av3;
+ "
+} {}
+set root_page_list [list]
+set pending_byte_page [expr ($::sqlite_pending_byte / 1024) + 1]
+for {set i 3} {$i<=532} {incr i} {
+ # 207 and 412 are pointer-map pages.
+ if { $i!=207 && $i!=412 && $i != $pending_byte_page} {
+ lappend root_page_list $i
+ }
+}
+if {$i >= $pending_byte_page} {
+ lappend root_page_list $i
+}
+do_test autovacuum-2.4.5 {
+ for {set i 11} {$i<=530} {incr i} {
+ execsql "CREATE TABLE av$i (x)"
+ }
+ execsql {
+ SELECT rootpage FROM sqlite_master ORDER by rootpage
+ }
+} $root_page_list
+
+# Just for fun, delete all those tables and see if the database is 1 page.
+do_test autovacuum-2.4.6 {
+ execsql COMMIT;
+ file_pages
+} [expr 561 + (($i >= $pending_byte_page)?1:0)]
+integrity_check autovacuum-2.4.6
+do_test autovacuum-2.4.7 {
+ execsql BEGIN
+ for {set i 3} {$i<=530} {incr i} {
+ execsql "DROP TABLE av$i"
+ }
+ execsql COMMIT
+ file_pages
+} 1
+
+# Create some tables with indices to drop.
+do_test autovacuum-2.5.1 {
+ execsql {
+ CREATE TABLE av1(a PRIMARY KEY, b, c);
+ INSERT INTO av1 VALUES('av1 a', 'av1 b', 'av1 c');
+
+ CREATE TABLE av2(a PRIMARY KEY, b, c);
+ CREATE INDEX av2_i1 ON av2(b);
+ CREATE INDEX av2_i2 ON av2(c);
+ INSERT INTO av2 VALUES('av2 a', 'av2 b', 'av2 c');
+
+ CREATE TABLE av3(a PRIMARY KEY, b, c);
+ CREATE INDEX av3_i1 ON av3(b);
+ INSERT INTO av3 VALUES('av3 a', 'av3 b', 'av3 c');
+
+ CREATE TABLE av4(a, b, c);
+ CREATE INDEX av4_i1 ON av4(a);
+ CREATE INDEX av4_i2 ON av4(b);
+ CREATE INDEX av4_i3 ON av4(c);
+ CREATE INDEX av4_i4 ON av4(a, b, c);
+ INSERT INTO av4 VALUES('av4 a', 'av4 b', 'av4 c');
+ }
+} {}
+
+do_test autovacuum-2.5.2 {
+ execsql {
+ SELECT name, rootpage FROM sqlite_master;
+ }
+} [list av1 3 sqlite_autoindex_av1_1 4 \
+ av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
+ av3 9 sqlite_autoindex_av3_1 10 av3_i1 11 \
+ av4 12 av4_i1 13 av4_i2 14 av4_i3 15 av4_i4 16 \
+]
+
+# The following 4 tests are SELECT queries that use the indices created.
+# If the root-pages in the internal schema are not updated correctly when
+# a table or indice is moved, these queries will fail. They are repeated
+# after each table is dropped (i.e. as test cases 2.5.*.[1..4]).
+do_test autovacuum-2.5.2.1 {
+ execsql {
+ SELECT * FROM av1 WHERE a = 'av1 a';
+ }
+} {{av1 a} {av1 b} {av1 c}}
+do_test autovacuum-2.5.2.2 {
+ execsql {
+ SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
+ }
+} {{av2 a} {av2 b} {av2 c}}
+do_test autovacuum-2.5.2.3 {
+ execsql {
+ SELECT * FROM av3 WHERE a = 'av3 a' AND b = 'av3 b';
+ }
+} {{av3 a} {av3 b} {av3 c}}
+do_test autovacuum-2.5.2.4 {
+ execsql {
+ SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
+ }
+} {{av4 a} {av4 b} {av4 c}}
+
+# Drop table av3. Indices av4_i2, av4_i3 and av4_i4 are moved to fill the two
+# root pages vacated. The operation proceeds as:
+# Step 1: Delete av3_i1 (root-page 11). Move root-page of av4_i4 to page 11.
+# Step 2: Delete av3 (root-page 10). Move root-page of av4_i3 to page 10.
+# Step 3: Delete sqlite_autoindex_av1_3 (root-page 9). Move av4_i2 to page 9.
+do_test autovacuum-2.5.3 {
+ execsql {
+ DROP TABLE av3;
+ SELECT name, rootpage FROM sqlite_master;
+ }
+} [list av1 3 sqlite_autoindex_av1_1 4 \
+ av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
+ av4 12 av4_i1 13 av4_i2 9 av4_i3 10 av4_i4 11 \
+]
+do_test autovacuum-2.5.3.1 {
+ execsql {
+ SELECT * FROM av1 WHERE a = 'av1 a';
+ }
+} {{av1 a} {av1 b} {av1 c}}
+do_test autovacuum-2.5.3.2 {
+ execsql {
+ SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
+ }
+} {{av2 a} {av2 b} {av2 c}}
+do_test autovacuum-2.5.3.3 {
+ execsql {
+ SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
+ }
+} {{av4 a} {av4 b} {av4 c}}
+
+# Drop table av1:
+# Step 1: Delete av1 (root page 4). Root-page of av4_i1 fills the gap.
+# Step 2: Delete sqlite_autoindex_av1_1 (root page 3). Move av4 to the gap.
+do_test autovacuum-2.5.4 {
+ execsql {
+ DROP TABLE av1;
+ SELECT name, rootpage FROM sqlite_master;
+ }
+} [list av2 5 sqlite_autoindex_av2_1 6 av2_i1 7 av2_i2 8 \
+ av4 3 av4_i1 4 av4_i2 9 av4_i3 10 av4_i4 11 \
+]
+do_test autovacuum-2.5.4.2 {
+ execsql {
+ SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
+ }
+} {{av2 a} {av2 b} {av2 c}}
+do_test autovacuum-2.5.4.4 {
+ execsql {
+ SELECT * FROM av4 WHERE a = 'av4 a' AND b = 'av4 b' AND c = 'av4 c';
+ }
+} {{av4 a} {av4 b} {av4 c}}
+
+# Drop table av4:
+# Step 1: Delete av4_i4.
+# Step 2: Delete av4_i3.
+# Step 3: Delete av4_i2.
+# Step 4: Delete av4_i1. av2_i2 replaces it.
+# Step 5: Delete av4. av2_i1 replaces it.
+do_test autovacuum-2.5.5 {
+ execsql {
+ DROP TABLE av4;
+ SELECT name, rootpage FROM sqlite_master;
+ }
+} [list av2 5 sqlite_autoindex_av2_1 6 av2_i1 3 av2_i2 4]
+do_test autovacuum-2.5.5.2 {
+ execsql {
+ SELECT * FROM av2 WHERE a = 'av2 a' AND b = 'av2 b' AND c = 'av2 c'
+ }
+} {{av2 a} {av2 b} {av2 c}}
+
+#--------------------------------------------------------------------------
+# Test cases autovacuum-3.* test the operation of the "PRAGMA auto_vacuum"
+# command.
+#
+do_test autovacuum-3.1 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {1}
+do_test autovacuum-3.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {1}
+do_test autovacuum-3.3 {
+ execsql {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA auto_vacuum;
+ }
+} {1}
+
+do_test autovacuum-3.4 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} $AUTOVACUUM
+do_test autovacuum-3.5 {
+ execsql {
+ CREATE TABLE av1(x);
+ PRAGMA auto_vacuum;
+ }
+} $AUTOVACUUM
+do_test autovacuum-3.6 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ PRAGMA auto_vacuum;
+ }
+} $AUTOVACUUM
+do_test autovacuum-3.7 {
+ execsql {
+ DROP TABLE av1;
+ }
+ file_pages
+} [expr $AUTOVACUUM?1:2]
+
+
+#-----------------------------------------------------------------------
+# Test that if a statement transaction around a CREATE INDEX statement is
+# rolled back no corruption occurs.
+#
+do_test autovacuum-4.0 {
+ # The last round of tests may have left the db in non-autovacuum mode.
+ # Reset everything just in case.
+ #
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ PRAGMA auto_vacuum;
+ }
+} {1}
+do_test autovacuum-4.1 {
+ execsql {
+ CREATE TABLE av1(a, b);
+ BEGIN;
+ }
+ for {set i 0} {$i<100} {incr i} {
+ execsql "INSERT INTO av1 VALUES($i, '[string repeat X 200]');"
+ }
+ execsql "INSERT INTO av1 VALUES(99, '[string repeat X 200]');"
+ execsql {
+ SELECT sum(a) FROM av1;
+ }
+} {5049}
+do_test autovacuum-4.2 {
+ catchsql {
+ CREATE UNIQUE INDEX av1_i ON av1(a);
+ }
+} {1 {indexed columns are not unique}}
+do_test autovacuum-4.3 {
+ execsql {
+ SELECT sum(a) FROM av1;
+ }
+} {5049}
+do_test autovacuum-4.4 {
+ execsql {
+ COMMIT;
+ }
+} {}
+
+ifcapable integrityck {
+
+# Ticket #1727
+do_test autovacuum-5.1 {
+ db close
+ sqlite3 db :memory:
+ db eval {
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE t1(a);
+ CREATE TABLE t2(a);
+ DROP TABLE t1;
+ PRAGMA integrity_check;
+ }
+} ok
+
+}
+
+# Ticket #1728.
+#
+# In autovacuum mode, when tables or indices are deleted, the rootpage
+# values in the symbol table have to be updated. There was a bug in this
+# logic so that if an index/table was moved twice, the second move might
+# not occur. This would leave the internal symbol table in an inconsistent
+# state causing subsequent statements to fail.
+#
+# The problem is difficult to reproduce. The sequence of statements in
+# the following test are carefully designed make it occur and thus to
+# verify that this very obscure bug has been resolved.
+#
+ifcapable integrityck&&memorydb {
+
+do_test autovacuum-6.1 {
+ db close
+ sqlite3 db :memory:
+ db eval {
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a);
+ CREATE TABLE t2(a);
+ CREATE INDEX i2 ON t2(a);
+ CREATE TABLE t3(a);
+ CREATE INDEX i3 ON t2(a);
+ CREATE INDEX x ON t1(b);
+ DROP TABLE t3;
+ PRAGMA integrity_check;
+ DROP TABLE t2;
+ PRAGMA integrity_check;
+ DROP TABLE t1;
+ PRAGMA integrity_check;
+ }
+} {ok ok ok}
+
+}
+
+#---------------------------------------------------------------------
+# Test cases autovacuum-7.X test the case where a page must be moved
+# and the destination location collides with at least one other
+# entry in the page hash-table (internal to the pager.c module.
+#
+do_test autovacuum-7.1 {
+ db close
+ file delete -force test.db
+ file delete -force test.db-journal
+ sqlite3 db test.db
+
+ execsql {
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t1 VALUES(randstr(400,400),randstr(400,400));
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 4
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 8
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 16
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 32
+ }
+
+ expr {[file size test.db] / 1024}
+} {73}
+
+do_test autovacuum-7.2 {
+ execsql {
+ CREATE TABLE t2(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t2 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ CREATE TABLE t3(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t3 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ CREATE TABLE t4(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t4 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ CREATE TABLE t5(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t5 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ }
+ expr {[file size test.db] / 1024}
+} {354}
+
+do_test autovacuum-7.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ DELETE FROM t4;
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+ expr {[file size test.db] / 1024}
+} {286}
+
+#------------------------------------------------------------------------
+# Additional tests.
+#
+# Try to determine the autovacuum setting for a database that is locked.
+#
+do_test autovacuum-8.1 {
+ db close
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ db eval {PRAGMA auto_vacuum}
+} {1}
+do_test autovacuum-8.2 {
+ db eval {BEGIN EXCLUSIVE}
+ catchsql {PRAGMA auto_vacuum} db2
+} {1 {database is locked}}
+catch {db2 close}
+catch {db eval {COMMIT}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/autovacuum_ioerr2.test b/third_party/sqlite/test/autovacuum_ioerr2.test
new file mode 100755
index 0000000..dadb29c
--- /dev/null
+++ b/third_party/sqlite/test/autovacuum_ioerr2.test
@@ -0,0 +1,132 @@
+# 2001 October 12
+#
+# 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 file is testing for correct handling of I/O errors
+# such as writes failing because the disk is full.
+#
+# The tests in this file use special facilities that are only
+# available in the SQLite test fixture.
+#
+# $Id: autovacuum_ioerr2.test,v 1.7 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum} {
+ finish_test
+ return
+}
+
+do_ioerr_test autovacuum-ioerr2-1 -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1500,1500));
+} -sqlbody {
+ CREATE TABLE abc2(a);
+ BEGIN;
+ DELETE FROM abc;
+ INSERT INTO abc VALUES(randstr(1500,1500));
+ CREATE TABLE abc3(a);
+ COMMIT;
+}
+
+do_ioerr_test autovacuum-ioerr2-2 -tclprep {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
+ INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
+ }
+ for {set i 0} {$i<150} {incr i} {
+ execsql {
+ INSERT INTO abc VALUES(randstr(100,100));
+ }
+ }
+ execsql COMMIT
+} -sqlbody {
+ BEGIN;
+ DELETE FROM abc WHERE length(a)>100;
+ UPDATE abc SET a = randstr(90,90);
+ CREATE TABLE abc3(a);
+ COMMIT;
+}
+
+do_ioerr_test autovacuum-ioerr2-3 -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE abc(a);
+ CREATE TABLE abc2(b);
+} -sqlbody {
+ BEGIN;
+ INSERT INTO abc2 VALUES(10);
+ DROP TABLE abc;
+ COMMIT;
+ DROP TABLE abc2;
+}
+
+file delete -force backup.db
+ifcapable subquery {
+ do_ioerr_test autovacuum-ioerr2-4 -tclprep {
+ if {![file exists backup.db]} {
+ sqlite3 dbb backup.db
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ BEGIN;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 4 is overflow
+ INSERT INTO abc VALUES(randstr(1100,1100)); -- Page 5 is overflow
+ } dbb
+ for {set i 0} {$i<2500} {incr i} {
+ execsql {
+ INSERT INTO abc VALUES(randstr(100,100));
+ } dbb
+ }
+ execsql {
+ COMMIT;
+ PRAGMA cache_size = 10;
+ } dbb
+ dbb close
+ }
+ db close
+ file delete -force test.db
+ file delete -force test.db-journal
+ copy_file backup.db test.db
+ set ::DB [sqlite3 db test.db]
+ execsql {
+ PRAGMA cache_size = 10;
+ }
+ } -sqlbody {
+ BEGIN;
+ DELETE FROM abc WHERE oid < 3;
+ UPDATE abc SET a = randstr(100,100) WHERE oid > 2300;
+ UPDATE abc SET a = randstr(1100,1100) WHERE oid =
+ (select max(oid) from abc);
+ COMMIT;
+ }
+}
+
+do_ioerr_test autovacuum-ioerr2-1 -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1500,1500));
+} -sqlbody {
+ CREATE TABLE abc2(a);
+ BEGIN;
+ DELETE FROM abc;
+ INSERT INTO abc VALUES(randstr(1500,1500));
+ CREATE TABLE abc3(a);
+ COMMIT;
+}
+
+finish_test
diff --git a/third_party/sqlite/test/avtrans.test b/third_party/sqlite/test/avtrans.test
new file mode 100755
index 0000000..328e028
--- /dev/null
+++ b/third_party/sqlite/test/avtrans.test
@@ -0,0 +1,921 @@
+# 2001 September 15
+#
+# 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. This
+# file is a copy of "trans.test" modified to run under autovacuum mode.
+# the point is to stress the autovacuum logic and try to get it to fail.
+#
+# $Id: avtrans.test,v 1.6 2007/09/12 17:01:45 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Create several tables to work with.
+#
+do_test avtrans-1.0 {
+ execsql {
+ PRAGMA auto_vacuum=ON;
+ CREATE TABLE one(a int PRIMARY KEY, b text);
+ INSERT INTO one VALUES(1,'one');
+ INSERT INTO one VALUES(2,'two');
+ INSERT INTO one VALUES(3,'three');
+ SELECT b FROM one ORDER BY a;
+ }
+} {one two three}
+do_test avtrans-1.1 {
+ execsql {
+ CREATE TABLE two(a int PRIMARY KEY, b text);
+ INSERT INTO two VALUES(1,'I');
+ INSERT INTO two VALUES(5,'V');
+ INSERT INTO two VALUES(10,'X');
+ SELECT b FROM two ORDER BY a;
+ }
+} {I V X}
+do_test avtrans-1.9 {
+ sqlite3 altdb test.db
+ execsql {SELECT b FROM one ORDER BY a} altdb
+} {one two three}
+do_test avtrans-1.10 {
+ execsql {SELECT b FROM two ORDER BY a} altdb
+} {I V X}
+integrity_check avtrans-1.11
+
+# Basic transactions
+#
+do_test avtrans-2.1 {
+ set v [catch {execsql {BEGIN}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.2 {
+ set v [catch {execsql {END}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.3 {
+ set v [catch {execsql {BEGIN TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.4 {
+ set v [catch {execsql {COMMIT TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.5 {
+ set v [catch {execsql {BEGIN TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.6 {
+ set v [catch {execsql {ROLLBACK TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test avtrans-2.10 {
+ execsql {
+ BEGIN;
+ SELECT a FROM one ORDER BY a;
+ SELECT a FROM two ORDER BY a;
+ END;
+ }
+} {1 2 3 1 5 10}
+integrity_check avtrans-2.11
+
+# Check the locking behavior
+#
+sqlite3_soft_heap_limit 0
+do_test avtrans-3.1 {
+ execsql {
+ BEGIN;
+ UPDATE one SET a = 0 WHERE 0;
+ SELECT a FROM one ORDER BY a;
+ }
+} {1 2 3}
+do_test avtrans-3.2 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+do_test avtrans-3.3 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test avtrans-3.4 {
+ catchsql {
+ INSERT INTO one VALUES(4,'four');
+ }
+} {0 {}}
+do_test avtrans-3.5 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+do_test avtrans-3.6 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test avtrans-3.7 {
+ catchsql {
+ INSERT INTO two VALUES(4,'IV');
+ }
+} {0 {}}
+do_test avtrans-3.8 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+do_test avtrans-3.9 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test avtrans-3.10 {
+ execsql {END TRANSACTION}
+} {}
+do_test avtrans-3.11 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test avtrans-3.12 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+do_test avtrans-3.13 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test avtrans-3.14 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+sqlite3_soft_heap_limit $soft_limit
+integrity_check avtrans-3.15
+
+do_test avtrans-4.1 {
+ set v [catch {execsql {
+ COMMIT;
+ } db} msg]
+ lappend v $msg
+} {1 {cannot commit - no transaction is active}}
+do_test avtrans-4.2 {
+ set v [catch {execsql {
+ ROLLBACK;
+ } db} msg]
+ lappend v $msg
+} {1 {cannot rollback - no transaction is active}}
+do_test avtrans-4.3 {
+ catchsql {
+ BEGIN TRANSACTION;
+ UPDATE two SET a = 0 WHERE 0;
+ SELECT a FROM two ORDER BY a;
+ } db
+} {0 {1 4 5 10}}
+do_test avtrans-4.4 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 4 5 10}}
+do_test avtrans-4.5 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3 4}}
+do_test avtrans-4.6 {
+ catchsql {
+ BEGIN TRANSACTION;
+ SELECT a FROM one ORDER BY a;
+ } db
+} {1 {cannot start a transaction within a transaction}}
+do_test avtrans-4.7 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 4 5 10}}
+do_test avtrans-4.8 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3 4}}
+do_test avtrans-4.9 {
+ set v [catch {execsql {
+ END TRANSACTION;
+ SELECT a FROM two ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test avtrans-4.10 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test avtrans-4.11 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+integrity_check avtrans-4.12
+do_test avtrans-4.98 {
+ altdb close
+ execsql {
+ DROP TABLE one;
+ DROP TABLE two;
+ }
+} {}
+integrity_check avtrans-4.99
+
+# Check out the commit/rollback behavior of the database
+#
+do_test avtrans-5.1 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test avtrans-5.2 {
+ execsql {BEGIN TRANSACTION}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test avtrans-5.3 {
+ execsql {CREATE TABLE one(a text, b int)}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {one}
+do_test avtrans-5.4 {
+ execsql {SELECT a,b FROM one ORDER BY b}
+} {}
+do_test avtrans-5.5 {
+ execsql {INSERT INTO one(a,b) VALUES('hello', 1)}
+ execsql {SELECT a,b FROM one ORDER BY b}
+} {hello 1}
+do_test avtrans-5.6 {
+ execsql {ROLLBACK}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test avtrans-5.7 {
+ set v [catch {
+ execsql {SELECT a,b FROM one ORDER BY b}
+ } msg]
+ lappend v $msg
+} {1 {no such table: one}}
+
+# Test commits and rollbacks of table CREATE TABLEs, CREATE INDEXs
+# DROP TABLEs and DROP INDEXs
+#
+do_test avtrans-5.8 {
+ execsql {
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name
+ }
+} {}
+do_test avtrans-5.9 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a int, b int, c int);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {t1}
+do_test avtrans-5.10 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test avtrans-5.11 {
+ execsql {
+ COMMIT;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test avtrans-5.12 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE t2(a int, b int, c int);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ DROP TABLE t1;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2a i2b t2}
+do_test avtrans-5.13 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test avtrans-5.14 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {t1}
+do_test avtrans-5.15 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test avtrans-5.16 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE TABLE t2(x int, y int, z int);
+ CREATE INDEX i2x ON t2(x);
+ CREATE INDEX i2y ON t2(y);
+ INSERT INTO t2 VALUES(1,2,3);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test avtrans-5.17 {
+ execsql {
+ COMMIT;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test avtrans-5.18 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test avtrans-5.19 {
+ execsql {
+ SELECT x FROM t2 WHERE y=2;
+ }
+} {1}
+do_test avtrans-5.20 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ DROP TABLE t2;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {}
+do_test avtrans-5.21 {
+ set r [catch {execsql {
+ SELECT * FROM t2
+ }} msg]
+ lappend r $msg
+} {1 {no such table: t2}}
+do_test avtrans-5.22 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test avtrans-5.23 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+integrity_check avtrans-5.23
+
+
+# Try to DROP and CREATE tables and indices with the same name
+# within a transaction. Make sure ROLLBACK works.
+#
+do_test avtrans-6.1 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p,q,r);
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {a 1 b 2 c 3}
+do_test avtrans-6.2 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p,q,r);
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {}
+do_test avtrans-6.3 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test avtrans-6.4 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test avtrans-6.5 {
+ execsql2 {
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test avtrans-6.6 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test avtrans-6.7 {
+ catchsql {
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {1 {no such table: t1}}
+
+# Repeat on a table with an automatically generated index.
+#
+do_test avtrans-6.10 {
+ execsql2 {
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p unique,q,r);
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {a 1 b 2 c 3}
+do_test avtrans-6.11 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p unique,q,r);
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {}
+do_test avtrans-6.12 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test avtrans-6.13 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test avtrans-6.14 {
+ execsql2 {
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test avtrans-6.15 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test avtrans-6.16 {
+ catchsql {
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {1 {no such table: t1}}
+
+do_test avtrans-6.20 {
+ execsql {
+ CREATE TABLE t1(a integer primary key,b,c);
+ INSERT INTO t1 VALUES(1,-2,-3);
+ INSERT INTO t1 VALUES(4,-5,-6);
+ SELECT * FROM t1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.21 {
+ execsql {
+ CREATE INDEX i1 ON t1(b);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.22 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE b<1;
+ ROLLBACK;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.23 {
+ execsql {
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.24 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+
+do_test avtrans-6.25 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE INDEX i1 ON t1(c);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.26 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.27 {
+ execsql {
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.28 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+
+# The following repeats steps 6.20 through 6.28, but puts a "unique"
+# constraint the first field of the table in order to generate an
+# automatic index.
+#
+do_test avtrans-6.30 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a int unique,b,c);
+ COMMIT;
+ INSERT INTO t1 VALUES(1,-2,-3);
+ INSERT INTO t1 VALUES(4,-5,-6);
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.31 {
+ execsql {
+ CREATE INDEX i1 ON t1(b);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.32 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE b<1;
+ ROLLBACK;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.33 {
+ execsql {
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.34 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+
+do_test avtrans-6.35 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE INDEX i1 ON t1(c);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.36 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.37 {
+ execsql {
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test avtrans-6.38 {
+ execsql {
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test avtrans-6.39 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+integrity_check avtrans-6.40
+
+ifcapable !floatingpoint {
+ finish_test
+ return
+}
+
+# Test to make sure rollback restores the database back to its original
+# state.
+#
+do_test avtrans-7.1 {
+ execsql {BEGIN}
+ for {set i 0} {$i<1000} {incr i} {
+ set r1 [expr {rand()}]
+ set r2 [expr {rand()}]
+ set r3 [expr {rand()}]
+ execsql "INSERT INTO t2 VALUES($r1,$r2,$r3)"
+ }
+ execsql {COMMIT}
+ set ::checksum [execsql {SELECT md5sum(x,y,z) FROM t2}]
+ set ::checksum2 [
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+ ]
+ execsql {SELECT count(*) FROM t2}
+} {1001}
+do_test avtrans-7.2 {
+ execsql {SELECT md5sum(x,y,z) FROM t2}
+} $checksum
+do_test avtrans-7.2.1 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+do_test avtrans-7.3 {
+ execsql {
+ BEGIN;
+ DELETE FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test avtrans-7.4 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 SELECT * FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test avtrans-7.5 {
+ execsql {
+ BEGIN;
+ DELETE FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test avtrans-7.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 SELECT * FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test avtrans-7.7 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test avtrans-7.8 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test avtrans-7.9 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test avtrans-7.10 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test avtrans-7.11 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ DROP INDEX i2x;
+ DROP INDEX i2y;
+ CREATE INDEX i3a ON t3(x);
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test avtrans-7.12 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test avtrans-7.13 {
+ execsql {
+ BEGIN;
+ DROP TABLE t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test avtrans-7.14 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+integrity_check avtrans-7.15
+
+# Arrange for another process to begin modifying the database but abort
+# and die in the middle of the modification. Then have this process read
+# the database. This process should detect the journal file and roll it
+# back. Verify that this happens correctly.
+#
+set fd [open test.tcl w]
+puts $fd {
+ sqlite3 db test.db
+ db eval {
+ PRAGMA default_cache_size=20;
+ BEGIN;
+ CREATE TABLE t3 AS SELECT * FROM t2;
+ DELETE FROM t2;
+ }
+ sqlite_abort
+}
+close $fd
+do_test avtrans-8.1 {
+ catch {exec [info nameofexec] test.tcl}
+ execsql {SELECT md5sum(x,y,z) FROM t2}
+} $checksum
+do_test avtrans-8.2 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+integrity_check avtrans-8.3
+
+# In the following sequence of tests, compute the MD5 sum of the content
+# of a table, make lots of modifications to that table, then do a rollback.
+# Verify that after the rollback, the MD5 checksum is unchanged.
+#
+do_test avtrans-9.1 {
+ execsql {
+ PRAGMA default_cache_size=10;
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x TEXT);
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ COMMIT;
+ SELECT count(*) FROM t3;
+ }
+} {1024}
+
+# The following procedure computes a "signature" for table "t3". If
+# T3 changes in any way, the signature should change.
+#
+# This is used to test ROLLBACK. We gather a signature for t3, then
+# make lots of changes to t3, then rollback and take another signature.
+# The two signatures should be the same.
+#
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(x) FROM t3}]
+}
+
+# Repeat the following group of tests 20 times for quick testing and
+# 40 times for full testing. Each iteration of the test makes table
+# t3 a little larger, and thus takes a little longer, so doing 40 tests
+# is more than 2.0 times slower than doing 20 tests. Considerably more.
+#
+if {[info exists ISQUICK]} {
+ set limit 20
+} else {
+ set limit 40
+}
+
+# Do rollbacks. Make sure the signature does not change.
+#
+for {set i 2} {$i<=$limit} {incr i} {
+ set ::sig [signature]
+ set cnt [lindex $::sig 0]
+ if {$i%2==0} {
+ execsql {PRAGMA fullfsync=ON}
+ } else {
+ execsql {PRAGMA fullfsync=OFF}
+ }
+ set sqlite_sync_count 0
+ set sqlite_fullsync_count 0
+ do_test avtrans-9.$i.1-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+ } $sig
+ do_test avtrans-9.$i.2-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+ } $sig
+ if {$i<$limit} {
+ do_test avtrans-9.$i.3-$cnt {
+ execsql {
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0;
+ }
+ } {}
+ if {$tcl_platform(platform)=="unix"} {
+ do_test avtrans-9.$i.4-$cnt {
+ expr {$sqlite_sync_count>0}
+ } 1
+ ifcapable pager_pragmas {
+ do_test avtrans-9.$i.5-$cnt {
+ expr {$sqlite_fullsync_count>0}
+ } [expr {$i%2==0}]
+ } else {
+ do_test avtrans-9.$i.5-$cnt {
+ expr {$sqlite_fullsync_count==0}
+ } {1}
+ }
+ }
+ }
+ set ::pager_old_format 0
+}
+integrity_check avtrans-10.1
+
+finish_test
diff --git a/third_party/sqlite/test/badutf.test b/third_party/sqlite/test/badutf.test
new file mode 100755
index 0000000..d09c933
--- /dev/null
+++ b/third_party/sqlite/test/badutf.test
@@ -0,0 +1,143 @@
+# 2007 May 15
+#
+# 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.
+#
+# This file checks to make sure SQLite is able to gracefully
+# handle malformed UTF-8.
+#
+# $Id: badutf.test,v 1.2 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test badutf-1.1 {
+ db eval {PRAGMA encoding=UTF8}
+ sqlite3_exec db {SELECT hex('%80') AS x}
+} {0 {x 80}}
+do_test badutf-1.2 {
+ sqlite3_exec db {SELECT hex('%81') AS x}
+} {0 {x 81}}
+do_test badutf-1.3 {
+ sqlite3_exec db {SELECT hex('%bf') AS x}
+} {0 {x BF}}
+do_test badutf-1.4 {
+ sqlite3_exec db {SELECT hex('%c0') AS x}
+} {0 {x C0}}
+do_test badutf-1.5 {
+ sqlite3_exec db {SELECT hex('%e0') AS x}
+} {0 {x E0}}
+do_test badutf-1.6 {
+ sqlite3_exec db {SELECT hex('%f0') AS x}
+} {0 {x F0}}
+do_test badutf-1.7 {
+ sqlite3_exec db {SELECT hex('%ff') AS x}
+} {0 {x FF}}
+
+sqlite3 db2 {}
+ifcapable utf16 {
+ do_test badutf-1.10 {
+ db2 eval {PRAGMA encoding=UTF16be}
+ sqlite3_exec db2 {SELECT hex('%80') AS x}
+ } {0 {x 0080}}
+ do_test badutf-1.11 {
+ sqlite3_exec db2 {SELECT hex('%81') AS x}
+ } {0 {x 0081}}
+ do_test badutf-1.12 {
+ sqlite3_exec db2 {SELECT hex('%bf') AS x}
+ } {0 {x 00BF}}
+ do_test badutf-1.13 {
+ sqlite3_exec db2 {SELECT hex('%c0') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.14 {
+ sqlite3_exec db2 {SELECT hex('%c1') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.15 {
+ sqlite3_exec db2 {SELECT hex('%c0%bf') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.16 {
+ sqlite3_exec db2 {SELECT hex('%c1%bf') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.17 {
+ sqlite3_exec db2 {SELECT hex('%c3%bf') AS x}
+ } {0 {x 00FF}}
+ do_test badutf-1.18 {
+ sqlite3_exec db2 {SELECT hex('%e0') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.19 {
+ sqlite3_exec db2 {SELECT hex('%f0') AS x}
+ } {0 {x FFFD}}
+ do_test badutf-1.20 {
+ sqlite3_exec db2 {SELECT hex('%ff') AS x}
+ } {0 {x FFFD}}
+}
+
+
+ifcapable bloblit {
+ do_test badutf-2.1 {
+ sqlite3_exec db {SELECT '%80'=CAST(x'80' AS text) AS x}
+ } {0 {x 1}}
+ do_test badutf-2.2 {
+ sqlite3_exec db {SELECT CAST('%80' AS blob)=x'80' AS x}
+ } {0 {x 1}}
+}
+
+do_test badutf-3.1 {
+ sqlite3_exec db {SELECT length('%80') AS x}
+} {0 {x 1}}
+do_test badutf-3.2 {
+ sqlite3_exec db {SELECT length('%61%62%63') AS x}
+} {0 {x 3}}
+do_test badutf-3.3 {
+ sqlite3_exec db {SELECT length('%7f%80%81') AS x}
+} {0 {x 3}}
+do_test badutf-3.4 {
+ sqlite3_exec db {SELECT length('%61%c0') AS x}
+} {0 {x 2}}
+do_test badutf-3.5 {
+ sqlite3_exec db {SELECT length('%61%c0%80%80%80%80%80%80%80%80%80%80') AS x}
+} {0 {x 2}}
+do_test badutf-3.6 {
+ sqlite3_exec db {SELECT length('%c0%80%80%80%80%80%80%80%80%80%80') AS x}
+} {0 {x 1}}
+do_test badutf-3.7 {
+ sqlite3_exec db {SELECT length('%80%80%80%80%80%80%80%80%80%80') AS x}
+} {0 {x 10}}
+do_test badutf-3.8 {
+ sqlite3_exec db {SELECT length('%80%80%80%80%80%f0%80%80%80%80') AS x}
+} {0 {x 6}}
+do_test badutf-3.9 {
+ sqlite3_exec db {SELECT length('%80%80%80%80%80%f0%80%80%80%ff') AS x}
+} {0 {x 7}}
+
+do_test badutf-4.1 {
+ sqlite3_exec db {SELECT hex(trim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
+} {0 {x F0}}
+do_test badutf-4.2 {
+ sqlite3_exec db {SELECT hex(ltrim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
+} {0 {x F0808080FF}}
+do_test badutf-4.3 {
+ sqlite3_exec db {SELECT hex(rtrim('%80%80%80%f0%80%80%80%ff','%80%ff')) AS x}
+} {0 {x 808080F0}}
+do_test badutf-4.4 {
+ sqlite3_exec db {SELECT hex(trim('%80%80%80%f0%80%80%80%ff','%ff%80')) AS x}
+} {0 {x 808080F0808080FF}}
+do_test badutf-4.5 {
+ sqlite3_exec db {SELECT hex(trim('%ff%80%80%f0%80%80%80%ff','%ff%80')) AS x}
+} {0 {x 80F0808080FF}}
+do_test badutf-4.6 {
+ sqlite3_exec db {SELECT hex(trim('%ff%80%f0%80%80%80%ff','%ff%80')) AS x}
+} {0 {x F0808080FF}}
+do_test badutf-4.7 {
+ sqlite3_exec db {SELECT hex(trim('%ff%80%f0%80%80%80%ff','%ff%80%80')) AS x}
+} {0 {x FF80F0808080FF}}
+
+db2 close
+finish_test
diff --git a/third_party/sqlite/test/between.test b/third_party/sqlite/test/between.test
new file mode 100755
index 0000000..4543675
--- /dev/null
+++ b/third_party/sqlite/test/between.test
@@ -0,0 +1,113 @@
+# 2005 July 28
+#
+# 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 file is testing the use of indices in WHERE clauses
+# when the WHERE clause contains the BETWEEN operator.
+#
+# $Id: between.test,v 1.2 2006/01/17 09:35:02 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test between-1.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(w int, x int, y int, z int);
+ }
+ for {set i 1} {$i<=100} {incr i} {
+ set w $i
+ set x [expr {int(log($i)/log(2))}]
+ set y [expr {$i*$i + 2*$i + 1}]
+ set z [expr {$x+$y}]
+ ifcapable tclvar {
+ # Random unplanned test of the $varname variable syntax.
+ execsql {INSERT INTO t1 VALUES($::w,$::x,$::y,$::z)}
+ } else {
+ # If the $varname syntax is not available, use the regular variable
+ # declaration syntax.
+ execsql {INSERT INTO t1 VALUES(:w,:x,:y,:z)}
+ }
+ }
+ execsql {
+ CREATE UNIQUE INDEX i1w ON t1(w);
+ CREATE INDEX i1xy ON t1(x,y);
+ CREATE INDEX i1zyx ON t1(z,y,x);
+ COMMIT;
+ }
+} {}
+
+# This procedure executes the SQL. Then it appends to the result the
+# "sort" or "nosort" keyword depending on whether or not any sorting
+# is done. Then it appends the ::sqlite_query_plan variable.
+#
+proc queryplan {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return [concat $data $::sqlite_query_plan]
+}
+
+do_test between-1.1.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w BETWEEN 5 AND 6 ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 i1w}
+do_test between-1.1.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE +w BETWEEN 5 AND 6 ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 {}}
+do_test between-1.2.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w BETWEEN 5 AND 65-y ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 i1w}
+do_test between-1.2.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE +w BETWEEN 5 AND 65-y ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 {}}
+do_test between-1.3.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w BETWEEN 41-y AND 6 ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 i1w}
+do_test between-1.3.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE +w BETWEEN 41-y AND 6 ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 {}}
+do_test between-1.4 {
+ queryplan {
+ SELECT * FROM t1 WHERE w BETWEEN 41-y AND 65-y ORDER BY +w
+ }
+} {5 2 36 38 6 2 49 51 sort t1 {}}
+do_test between-1.5.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE 26 BETWEEN y AND z ORDER BY +w
+ }
+} {4 2 25 27 sort t1 i1zyx}
+do_test between-1.5.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE 26 BETWEEN +y AND z ORDER BY +w
+ }
+} {4 2 25 27 sort t1 i1zyx}
+do_test between-1.5.3 {
+ queryplan {
+ SELECT * FROM t1 WHERE 26 BETWEEN y AND +z ORDER BY +w
+ }
+} {4 2 25 27 sort t1 {}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/bigfile.test b/third_party/sqlite/test/bigfile.test
new file mode 100755
index 0000000..20ace5c
--- /dev/null
+++ b/third_party/sqlite/test/bigfile.test
@@ -0,0 +1,193 @@
+# 2002 November 30
+#
+# 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 testing the ability of SQLite to handle database
+# files larger than 4GB.
+#
+# $Id: bigfile.test,v 1.10 2007/08/18 10:59:21 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_DISABLE_LFS is defined, omit this file.
+ifcapable !lfs {
+ finish_test
+ return
+}
+
+# These tests only work for Tcl version 8.4 and later. Prior to 8.4,
+# Tcl was unable to handle large files.
+#
+scan $::tcl_version %f vx
+if {$vx<8.4} return
+
+# Mac OS X does not handle large files efficiently. So skip this test
+# on that platform.
+if {$tcl_platform(os)=="Darwin"} return
+
+# This is the md5 checksum of all the data in table t1 as created
+# by the first test. We will use this number to make sure that data
+# never changes.
+#
+set MAGIC_SUM {593f1efcfdbe698c28b4b1b693f7e4cf}
+
+do_test bigfile-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ INSERT INTO t1 SELECT rowid || ' ' || x FROM t1;
+ COMMIT;
+ }
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+
+# Try to create a large file - a file that is larger than 2^32 bytes.
+# If this fails, it means that the system being tested does not support
+# large files. So skip all of the remaining tests in this file.
+#
+db close
+if {[catch {fake_big_file 4096 test.db} msg]} {
+ puts "**** Unable to create a file larger than 4096 MB. *****"
+ puts "$msg"
+ finish_test
+ return
+}
+
+do_test bigfile-1.2 {
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+
+# The previous test may fail on some systems because they are unable
+# to handle large files. If that is so, then skip all of the following
+# tests. We will know the above test failed because the "db" command
+# does not exist.
+#
+if {[llength [info command db]]>0} {
+
+do_test bigfile-1.3 {
+ execsql {
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ SELECT md5sum(x) FROM t2;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.4 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.5 {
+ execsql {
+ SELECT md5sum(x) FROM t2;
+ }
+} $::MAGIC_SUM
+
+db close
+if {[catch {fake_big_file 8192 test.db}]} {
+ puts "**** Unable to create a file larger than 8192 MB. *****"
+ finish_test
+ return
+}
+
+do_test bigfile-1.6 {
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.7 {
+ execsql {
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ SELECT md5sum(x) FROM t3;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.8 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.9 {
+ execsql {
+ SELECT md5sum(x) FROM t2;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.10 {
+ execsql {
+ SELECT md5sum(x) FROM t3;
+ }
+} $::MAGIC_SUM
+
+db close
+if {[catch {fake_big_file 16384 test.db}]} {
+ puts "**** Unable to create a file larger than 16384 MB. *****"
+ finish_test
+ return
+}
+
+do_test bigfile-1.11 {
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.12 {
+ execsql {
+ CREATE TABLE t4 AS SELECT * FROM t1;
+ SELECT md5sum(x) FROM t4;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.13 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT md5sum(x) FROM t1;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.14 {
+ execsql {
+ SELECT md5sum(x) FROM t2;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.15 {
+ execsql {
+ SELECT md5sum(x) FROM t3;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.16 {
+ execsql {
+ SELECT md5sum(x) FROM t3;
+ }
+} $::MAGIC_SUM
+do_test bigfile-1.17 {
+ execsql {
+ SELECT md5sum(x) FROM t4;
+ }
+} $::MAGIC_SUM
+
+} ;# End of the "if( db command exists )"
+
+finish_test
diff --git a/third_party/sqlite/test/bigrow.test b/third_party/sqlite/test/bigrow.test
new file mode 100755
index 0000000..fa59c36
--- /dev/null
+++ b/third_party/sqlite/test/bigrow.test
@@ -0,0 +1,223 @@
+# 2001 September 23
+#
+# 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 file is stressing the library by putting large amounts
+# of data in a single row of a table.
+#
+# $Id: bigrow.test,v 1.5 2004/08/07 23:54:48 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Make a big string that we can use for test data
+#
+do_test bigrow-1.0 {
+ set ::bigstr {}
+ for {set i 1} {$i<=9999} {incr i} {
+ set sep [string index "abcdefghijklmnopqrstuvwxyz" [expr {$i%26}]]
+ append ::bigstr "$sep [format %04d $i] "
+ }
+ string length $::bigstr
+} {69993}
+
+# Make a table into which we can insert some but records.
+#
+do_test bigrow-1.1 {
+ execsql {
+ CREATE TABLE t1(a text, b text, c text);
+ SELECT name FROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name
+ }
+} {t1}
+
+do_test bigrow-1.2 {
+ set ::big1 [string range $::bigstr 0 65519]
+ set sql "INSERT INTO t1 VALUES('abc',"
+ append sql "'$::big1', 'xyz');"
+ execsql $sql
+ execsql {SELECT a, c FROM t1}
+} {abc xyz}
+do_test bigrow-1.3 {
+ execsql {SELECT b FROM t1}
+} [list $::big1]
+do_test bigrow-1.4 {
+ set ::big2 [string range $::bigstr 0 65520]
+ set sql "INSERT INTO t1 VALUES('abc2',"
+ append sql "'$::big2', 'xyz2');"
+ set r [catch {execsql $sql} msg]
+ lappend r $msg
+} {0 {}}
+do_test bigrow-1.4.1 {
+ execsql {SELECT b FROM t1 ORDER BY c}
+} [list $::big1 $::big2]
+do_test bigrow-1.4.2 {
+ execsql {SELECT c FROM t1 ORDER BY c}
+} {xyz xyz2}
+do_test bigrow-1.4.3 {
+ execsql {DELETE FROM t1 WHERE a='abc2'}
+ execsql {SELECT c FROM t1}
+} {xyz}
+
+do_test bigrow-1.5 {
+ execsql {
+ UPDATE t1 SET a=b, b=a;
+ SELECT b,c FROM t1
+ }
+} {abc xyz}
+do_test bigrow-1.6 {
+ execsql {
+ SELECT * FROM t1
+ }
+} [list $::big1 abc xyz]
+do_test bigrow-1.7 {
+ execsql {
+ INSERT INTO t1 VALUES('1','2','3');
+ INSERT INTO t1 VALUES('A','B','C');
+ SELECT b FROM t1 WHERE a=='1';
+ }
+} {2}
+do_test bigrow-1.8 {
+ execsql "SELECT b FROM t1 WHERE a=='$::big1'"
+} {abc}
+do_test bigrow-1.9 {
+ execsql "SELECT b FROM t1 WHERE a!='$::big1' ORDER BY a"
+} {2 B}
+
+# Try doing some indexing on big columns
+#
+do_test bigrow-2.1 {
+ execsql {
+ CREATE INDEX i1 ON t1(a)
+ }
+ execsql "SELECT b FROM t1 WHERE a=='$::big1'"
+} {abc}
+do_test bigrow-2.2 {
+ execsql {
+ UPDATE t1 SET a=b, b=a
+ }
+ execsql "SELECT b FROM t1 WHERE a=='abc'"
+} [list $::big1]
+do_test bigrow-2.3 {
+ execsql {
+ UPDATE t1 SET a=b, b=a
+ }
+ execsql "SELECT b FROM t1 WHERE a=='$::big1'"
+} {abc}
+catch {unset ::bigstr}
+catch {unset ::big1}
+catch {unset ::big2}
+
+# Mosts of the tests above were created back when rows were limited in
+# size to 64K. Now rows can be much bigger. Test that logic. Also
+# make sure things work correctly at the transition boundries between
+# row sizes of 256 to 257 bytes and from 65536 to 65537 bytes.
+#
+# We begin by testing the 256..257 transition.
+#
+do_test bigrow-3.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c) VALUES('one','abcdefghijklmnopqrstuvwxyz0123','hi');
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 30 hi}
+do_test bigrow-3.2 {
+ execsql {
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 240 hi}
+for {set i 1} {$i<10} {incr i} {
+ do_test bigrow-3.3.$i {
+ execsql "UPDATE t1 SET b=b||'$i'"
+ execsql {SELECT a,length(b),c FROM t1}
+ } "one [expr {240+$i}] hi"
+}
+
+# Now test the 65536..65537 row-size transition.
+#
+do_test bigrow-4.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c) VALUES('one','abcdefghijklmnopqrstuvwxyz0123','hi');
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 30 hi}
+do_test bigrow-4.2 {
+ execsql {
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 122880 hi}
+do_test bigrow-4.3 {
+ execsql {
+ UPDATE t1 SET b=substr(b,1,65515)
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 65515 hi}
+for {set i 1} {$i<10} {incr i} {
+ do_test bigrow-4.4.$i {
+ execsql "UPDATE t1 SET b=b||'$i'"
+ execsql {SELECT a,length(b),c FROM t1}
+ } "one [expr {65515+$i}] hi"
+}
+
+# Check to make sure the library recovers safely if a row contains
+# too much data.
+#
+do_test bigrow-5.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c) VALUES('one','abcdefghijklmnopqrstuvwxyz0123','hi');
+ }
+ execsql {SELECT a,length(b),c FROM t1}
+} {one 30 hi}
+set i 1
+for {set sz 60} {$sz<1048560} {incr sz $sz} {
+ do_test bigrow-5.2.$i {
+ execsql {
+ UPDATE t1 SET b=b||b;
+ SELECT a,length(b),c FROM t1;
+ }
+ } "one $sz hi"
+ incr i
+}
+do_test bigrow-5.3 {
+ catchsql {UPDATE t1 SET b=b||b}
+} {0 {}}
+do_test bigrow-5.4 {
+ execsql {SELECT length(b) FROM t1}
+} 1966080
+do_test bigrow-5.5 {
+ catchsql {UPDATE t1 SET b=b||b}
+} {0 {}}
+do_test bigrow-5.6 {
+ execsql {SELECT length(b) FROM t1}
+} 3932160
+do_test bigrow-5.99 {
+ execsql {DROP TABLE t1}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/bind.test b/third_party/sqlite/test/bind.test
new file mode 100755
index 0000000..57cec03
--- /dev/null
+++ b/third_party/sqlite/test/bind.test
@@ -0,0 +1,660 @@
+# 2003 September 6
+#
+# 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 testing the sqlite_bind API.
+#
+# $Id: bind.test,v 1.44 2008/07/09 01:39:44 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc sqlite_step {stmt N VALS COLS} {
+ upvar VALS vals
+ upvar COLS cols
+ set vals [list]
+ set cols [list]
+
+ set rc [sqlite3_step $stmt]
+ for {set i 0} {$i < [sqlite3_column_count $stmt]} {incr i} {
+ lappend cols [sqlite3_column_name $stmt $i]
+ }
+ for {set i 0} {$i < [sqlite3_data_count $stmt]} {incr i} {
+ lappend vals [sqlite3_column_text $stmt $i]
+ }
+
+ return $rc
+}
+
+do_test bind-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ execsql {CREATE TABLE t1(a,b,c);}
+ set VM [sqlite3_prepare $DB {INSERT INTO t1 VALUES(:1,?,:abc)} -1 TAIL]
+ set TAIL
+} {}
+do_test bind-1.1.1 {
+ sqlite3_bind_parameter_count $VM
+} 3
+do_test bind-1.1.2 {
+ sqlite3_bind_parameter_name $VM 1
+} {:1}
+do_test bind-1.1.3 {
+ sqlite3_bind_parameter_name $VM 2
+} {}
+do_test bind-1.1.4 {
+ sqlite3_bind_parameter_name $VM 3
+} {:abc}
+do_test bind-1.2 {
+ sqlite_step $VM N VALUES COLNAMES
+} {SQLITE_DONE}
+do_test bind-1.3 {
+ execsql {SELECT rowid, * FROM t1}
+} {1 {} {} {}}
+do_test bind-1.4 {
+ sqlite3_reset $VM
+ sqlite_bind $VM 1 {test value 1} normal
+ sqlite_step $VM N VALUES COLNAMES
+} SQLITE_DONE
+do_test bind-1.5 {
+ execsql {SELECT rowid, * FROM t1}
+} {1 {} {} {} 2 {test value 1} {} {}}
+do_test bind-1.6 {
+ sqlite3_reset $VM
+ sqlite_bind $VM 3 {'test value 2'} normal
+ sqlite_step $VM N VALUES COLNAMES
+} SQLITE_DONE
+do_test bind-1.7 {
+ execsql {SELECT rowid, * FROM t1}
+} {1 {} {} {} 2 {test value 1} {} {} 3 {test value 1} {} {'test value 2'}}
+do_test bind-1.8 {
+ sqlite3_reset $VM
+ set sqlite_static_bind_value 123
+ sqlite_bind $VM 1 {} static
+ sqlite_bind $VM 2 {abcdefg} normal
+ sqlite_bind $VM 3 {} null
+ execsql {DELETE FROM t1}
+ sqlite_step $VM N VALUES COLNAMES
+ execsql {SELECT rowid, * FROM t1}
+} {1 123 abcdefg {}}
+do_test bind-1.9 {
+ sqlite3_reset $VM
+ sqlite_bind $VM 1 {456} normal
+ sqlite_step $VM N VALUES COLNAMES
+ execsql {SELECT rowid, * FROM t1}
+} {1 123 abcdefg {} 2 456 abcdefg {}}
+
+do_test bind-1.10 {
+ set rc [catch {
+ sqlite3_prepare db {INSERT INTO t1 VALUES($abc:123,?,:abc)} -1 TAIL
+ } msg]
+ lappend rc $msg
+} {1 {(1) near ":123": syntax error}}
+do_test bind-1.11 {
+ set rc [catch {
+ sqlite3_prepare db {INSERT INTO t1 VALUES(@abc:xyz,?,:abc)} -1 TAIL
+ } msg]
+ lappend rc $msg
+} {1 {(1) near ":xyz": syntax error}}
+
+do_test bind-1.99 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+
+# Prepare the statement in different ways depending on whether or not
+# the $var processing is compiled into the library.
+#
+ifcapable {tclvar} {
+ do_test bind-2.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+ set VM [sqlite3_prepare $DB {INSERT INTO t1 VALUES($one,$::two,$x(-z-))}\
+ -1 TX]
+ set TX
+ } {}
+ set v1 {$one}
+ set v2 {$::two}
+ set v3 {$x(-z-)}
+}
+ifcapable {!tclvar} {
+ do_test bind-2.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+ set VM [sqlite3_prepare $DB {INSERT INTO t1 VALUES(:one,:two,:_)} -1 TX]
+ set TX
+ } {}
+ set v1 {:one}
+ set v2 {:two}
+ set v3 {:_}
+}
+
+do_test bind-2.1.1 {
+ sqlite3_bind_parameter_count $VM
+} 3
+do_test bind-2.1.2 {
+ sqlite3_bind_parameter_name $VM 1
+} $v1
+do_test bind-2.1.3 {
+ sqlite3_bind_parameter_name $VM 2
+} $v2
+do_test bind-2.1.4 {
+ sqlite3_bind_parameter_name $VM 3
+} $v3
+do_test bind-2.1.5 {
+ sqlite3_bind_parameter_index $VM $v1
+} 1
+do_test bind-2.1.6 {
+ sqlite3_bind_parameter_index $VM $v2
+} 2
+do_test bind-2.1.7 {
+ sqlite3_bind_parameter_index $VM $v3
+} 3
+do_test bind-2.1.8 {
+ sqlite3_bind_parameter_index $VM {:hi}
+} 0
+
+# 32 bit Integers
+do_test bind-2.2 {
+ sqlite3_bind_int $VM 1 123
+ sqlite3_bind_int $VM 2 456
+ sqlite3_bind_int $VM 3 789
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+} {1 123 456 789}
+do_test bind-2.3 {
+ sqlite3_bind_int $VM 2 -2000000000
+ sqlite3_bind_int $VM 3 2000000000
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+} {1 123 456 789 2 123 -2000000000 2000000000}
+do_test bind-2.4 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {integer integer integer integer integer integer}
+do_test bind-2.5 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# 64 bit Integers
+do_test bind-3.1 {
+ sqlite3_bind_int64 $VM 1 32
+ sqlite3_bind_int64 $VM 2 -2000000000000
+ sqlite3_bind_int64 $VM 3 2000000000000
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+} {1 32 -2000000000000 2000000000000}
+do_test bind-3.2 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {integer integer integer}
+do_test bind-3.3 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# Doubles
+do_test bind-4.1 {
+ sqlite3_bind_double $VM 1 1234.1234
+ sqlite3_bind_double $VM 2 0.00001
+ sqlite3_bind_double $VM 3 123456789
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ set x [execsql {SELECT rowid, * FROM t1}]
+ regsub {1e-005} $x {1e-05} y
+ set y
+} {1 1234.1234 1e-05 123456789.0}
+do_test bind-4.2 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {real real real}
+do_test bind-4.3 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+do_test bind-4.4 {
+ sqlite3_bind_double $VM 1 NaN
+ sqlite3_bind_double $VM 2 1e300
+ sqlite3_bind_double $VM 3 -1e-300
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ set x [execsql {SELECT rowid, * FROM t1}]
+ regsub {1e-005} $x {1e-05} y
+ set y
+} {1 {} 1e+300 -1e-300}
+do_test bind-4.5 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {null real real}
+do_test bind-4.6 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# NULL
+do_test bind-5.1 {
+ sqlite3_bind_null $VM 1
+ sqlite3_bind_null $VM 2
+ sqlite3_bind_null $VM 3
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+} {1 {} {} {}}
+do_test bind-5.2 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {null null null}
+do_test bind-5.3 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# UTF-8 text
+do_test bind-6.1 {
+ sqlite3_bind_text $VM 1 hellothere 5
+ sqlite3_bind_text $VM 2 ".." 1
+ sqlite3_bind_text $VM 3 world\000 -1
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+} {1 hello . world}
+do_test bind-6.2 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {text text text}
+do_test bind-6.3 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# Make sure zeros in a string work.
+#
+do_test bind-6.4 {
+ db eval {DELETE FROM t1}
+ sqlite3_bind_text $VM 1 hello\000there\000 12
+ sqlite3_bind_text $VM 2 hello\000there\000 11
+ sqlite3_bind_text $VM 3 hello\000there\000 -1
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT * FROM t1}
+} {hello hello hello}
+set enc [db eval {PRAGMA encoding}]
+if {$enc=="UTF-8"} {
+ do_test bind-6.5 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {68656C6C6F00746865726500 68656C6C6F007468657265 68656C6C6F}
+} elseif {$enc=="UTF-16le"} {
+ do_test bind-6.5 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {680065006C006C006F000000740068006500720065000000 680065006C006C006F00000074006800650072006500 680065006C006C006F00}
+} elseif {$enc=="UTF-16be"} {
+ do_test bind-6.5 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {00680065006C006C006F0000007400680065007200650000 00680065006C006C006F000000740068006500720065 00680065006C006C006F}
+} else {
+ do_test bind-6.5 {
+ set "Unknown database encoding: $::enc"
+ } {}
+}
+do_test bind-6.6 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+} {text text text}
+do_test bind-6.7 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+
+# UTF-16 text
+ifcapable {utf16} {
+ do_test bind-7.1 {
+ sqlite3_bind_text16 $VM 1 [encoding convertto unicode hellothere] 10
+ sqlite3_bind_text16 $VM 2 [encoding convertto unicode ""] 0
+ sqlite3_bind_text16 $VM 3 [encoding convertto unicode world] 10
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT rowid, * FROM t1}
+ } {1 hello {} world}
+ do_test bind-7.2 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+ } {text text text}
+ do_test bind-7.3 {
+ db eval {DELETE FROM t1}
+ sqlite3_bind_text16 $VM 1 [encoding convertto unicode hi\000yall\000] 16
+ sqlite3_bind_text16 $VM 2 [encoding convertto unicode hi\000yall\000] 14
+ sqlite3_bind_text16 $VM 3 [encoding convertto unicode hi\000yall\000] -1
+ sqlite_step $VM N VALUES COLNAMES
+ sqlite3_reset $VM
+ execsql {SELECT * FROM t1}
+ } {hi hi hi}
+ if {$enc=="UTF-8"} {
+ do_test bind-7.4 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {68690079616C6C00 68690079616C6C 6869}
+ } elseif {$enc=="UTF-16le"} {
+ do_test bind-7.4 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {680069000000790061006C006C000000 680069000000790061006C006C00 68006900}
+ } elseif {$enc=="UTF-16be"} {
+ do_test bind-7.4 {
+ execsql {SELECT hex(a), hex(b), hex(c) FROM t1}
+ } {00680069000000790061006C006C0000 00680069000000790061006C006C 00680069}
+ }
+ do_test bind-7.5 {
+ execsql {SELECT typeof(a), typeof(b), typeof(c) FROM t1}
+ } {text text text}
+}
+do_test bind-7.99 {
+ execsql {DELETE FROM t1;}
+} {}
+
+# Test that the 'out of range' error works.
+do_test bind-8.1 {
+ catch { sqlite3_bind_null $VM 0 }
+} {1}
+do_test bind-8.2 {
+ sqlite3_errmsg $DB
+} {bind or column index out of range}
+ifcapable {utf16} {
+ do_test bind-8.3 {
+ encoding convertfrom unicode [sqlite3_errmsg16 $DB]
+ } {bind or column index out of range}
+}
+do_test bind-8.4 {
+ sqlite3_bind_null $VM 1
+ sqlite3_errmsg $DB
+} {not an error}
+do_test bind-8.5 {
+ catch { sqlite3_bind_null $VM 4 }
+} {1}
+do_test bind-8.6 {
+ sqlite3_errmsg $DB
+} {bind or column index out of range}
+ifcapable {utf16} {
+ do_test bind-8.7 {
+ encoding convertfrom unicode [sqlite3_errmsg16 $DB]
+ } {bind or column index out of range}
+}
+
+do_test bind-8.8 {
+ catch { sqlite3_bind_blob $VM 0 "abc" 3 }
+} {1}
+do_test bind-8.9 {
+ catch { sqlite3_bind_blob $VM 4 "abc" 3 }
+} {1}
+do_test bind-8.10 {
+ catch { sqlite3_bind_text $VM 0 "abc" 3 }
+} {1}
+ifcapable {utf16} {
+ do_test bind-8.11 {
+ catch { sqlite3_bind_text16 $VM 4 "abc" 2 }
+ } {1}
+}
+do_test bind-8.12 {
+ catch { sqlite3_bind_int $VM 0 5 }
+} {1}
+do_test bind-8.13 {
+ catch { sqlite3_bind_int $VM 4 5 }
+} {1}
+do_test bind-8.14 {
+ catch { sqlite3_bind_double $VM 0 5.0 }
+} {1}
+do_test bind-8.15 {
+ catch { sqlite3_bind_double $VM 4 6.0 }
+} {1}
+
+do_test bind-8.99 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+
+do_test bind-9.1 {
+ execsql {
+ CREATE TABLE t2(a,b,c,d,e,f);
+ }
+ set rc [catch {
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a) VALUES(?0)
+ } -1 TAIL
+ } msg]
+ lappend rc $msg
+} {1 {(1) variable number must be between ?1 and ?999}}
+do_test bind-9.2 {
+ set rc [catch {
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a) VALUES(?1000)
+ } -1 TAIL
+ } msg]
+ lappend rc $msg
+} {1 {(1) variable number must be between ?1 and ?999}}
+do_test bind-9.3.1 {
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b) VALUES(?1,?999)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+} {999}
+catch {sqlite3_finalize $VM}
+do_test bind-9.3.2 {
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b) VALUES(?2,?998)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+} {998}
+catch {sqlite3_finalize $VM}
+do_test bind-9.4 {
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b,c,d) VALUES(?1,?997,?,?)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+} {999}
+do_test bind-9.5 {
+ sqlite3_bind_int $VM 1 1
+ sqlite3_bind_int $VM 997 999
+ sqlite3_bind_int $VM 998 1000
+ sqlite3_bind_int $VM 999 1001
+ sqlite3_step $VM
+} SQLITE_DONE
+do_test bind-9.6 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+do_test bind-9.7 {
+ execsql {SELECT * FROM t2}
+} {1 999 1000 1001 {} {}}
+
+ifcapable {tclvar} {
+ do_test bind-10.1 {
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b,c,d,e,f) VALUES(:abc,$abc,:abc,$ab,$abc,:abc)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+ } 3
+ set v1 {$abc}
+ set v2 {$ab}
+}
+ifcapable {!tclvar} {
+ do_test bind-10.1 {
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b,c,d,e,f) VALUES(:abc,:xyz,:abc,:xy,:xyz,:abc)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+ } 3
+ set v1 {:xyz}
+ set v2 {:xy}
+}
+do_test bind-10.2 {
+ sqlite3_bind_parameter_index $VM :abc
+} 1
+do_test bind-10.3 {
+ sqlite3_bind_parameter_index $VM $v1
+} 2
+do_test bind-10.4 {
+ sqlite3_bind_parameter_index $VM $v2
+} 3
+do_test bind-10.5 {
+ sqlite3_bind_parameter_name $VM 1
+} :abc
+do_test bind-10.6 {
+ sqlite3_bind_parameter_name $VM 2
+} $v1
+do_test bind-10.7 {
+ sqlite3_bind_parameter_name $VM 3
+} $v2
+do_test bind-10.7.1 {
+ sqlite3_bind_parameter_name 0 1 ;# Ignore if VM is NULL
+} {}
+do_test bind-10.7.2 {
+ sqlite3_bind_parameter_name $VM 0 ;# Ignore if index too small
+} {}
+do_test bind-10.7.3 {
+ sqlite3_bind_parameter_name $VM 4 ;# Ignore if index is too big
+} {}
+do_test bind-10.8 {
+ sqlite3_bind_int $VM 1 1
+ sqlite3_bind_int $VM 2 2
+ sqlite3_bind_int $VM 3 3
+ sqlite3_step $VM
+} SQLITE_DONE
+do_test bind-10.8.1 {
+ # Binding attempts after program start should fail
+ set rc [catch {
+ sqlite3_bind_int $VM 1 1
+ } msg]
+ lappend rc $msg
+} {1 {}}
+do_test bind-10.9 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+do_test bind-10.10 {
+ execsql {SELECT * FROM t2}
+} {1 999 1000 1001 {} {} 1 2 1 3 2 1}
+
+# Ticket #918
+#
+do_test bind-10.11 {
+ # catch {sqlite3_finalize $VM}
+ set VM [
+ sqlite3_prepare $DB {
+ INSERT INTO t2(a,b,c,d,e,f) VALUES(:abc,?,?4,:pqr,:abc,?4)
+ } -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $VM
+} 5
+do_test bind-10.11.1 {
+ sqlite3_bind_parameter_index 0 :xyz ;# ignore NULL VM arguments
+} 0
+do_test bind-10.12 {
+ sqlite3_bind_parameter_index $VM :xyz
+} 0
+do_test bind-10.13 {
+ sqlite3_bind_parameter_index $VM {}
+} 0
+do_test bind-10.14 {
+ sqlite3_bind_parameter_index $VM :pqr
+} 5
+do_test bind-10.15 {
+ sqlite3_bind_parameter_index $VM ?4
+} 4
+do_test bind-10.16 {
+ sqlite3_bind_parameter_name $VM 1
+} :abc
+do_test bind-10.17 {
+ sqlite3_bind_parameter_name $VM 2
+} {}
+do_test bind-10.18 {
+ sqlite3_bind_parameter_name $VM 3
+} {}
+do_test bind-10.19 {
+ sqlite3_bind_parameter_name $VM 4
+} {?4}
+do_test bind-10.20 {
+ sqlite3_bind_parameter_name $VM 5
+} :pqr
+catch {sqlite3_finalize $VM}
+
+# Make sure we catch an unterminated "(" in a Tcl-style variable name
+#
+ifcapable tclvar {
+ do_test bind-11.1 {
+ catchsql {SELECT * FROM sqlite_master WHERE name=$abc(123 and sql NOT NULL;}
+ } {1 {unrecognized token: "$abc(123"}}
+}
+
+if {[execsql {pragma encoding}]=="UTF-8"} {
+ # Test the ability to bind text that contains embedded '\000' characters.
+ # Make sure we can recover the entire input string.
+ #
+ do_test bind-12.1 {
+ execsql {
+ CREATE TABLE t3(x BLOB);
+ }
+ set VM [sqlite3_prepare $DB {INSERT INTO t3 VALUES(?)} -1 TAIL]
+ sqlite_bind $VM 1 not-used blob10
+ sqlite3_step $VM
+ sqlite3_finalize $VM
+ execsql {
+ SELECT typeof(x), length(x), quote(x),
+ length(cast(x AS BLOB)), quote(cast(x AS BLOB)) FROM t3
+ }
+ } {text 3 'abc' 10 X'6162630078797A007071'}
+ do_test bind-12.2 {
+ sqlite3_create_function $DB
+ execsql {
+ SELECT quote(cast(x_coalesce(x) AS blob)) FROM t3
+ }
+ } {X'6162630078797A007071'}
+}
+
+# Test the operation of sqlite3_clear_bindings
+#
+do_test bind-13.1 {
+ set VM [sqlite3_prepare $DB {SELECT ?,?,?} -1 TAIL]
+ sqlite3_step $VM
+ list [sqlite3_column_type $VM 0] [sqlite3_column_type $VM 1] \
+ [sqlite3_column_type $VM 2]
+} {NULL NULL NULL}
+do_test bind-13.2 {
+ sqlite3_reset $VM
+ sqlite3_bind_int $VM 1 1
+ sqlite3_bind_int $VM 2 2
+ sqlite3_bind_int $VM 3 3
+ sqlite3_step $VM
+ list [sqlite3_column_type $VM 0] [sqlite3_column_type $VM 1] \
+ [sqlite3_column_type $VM 2]
+} {INTEGER INTEGER INTEGER}
+do_test bind-13.3 {
+ sqlite3_reset $VM
+ sqlite3_step $VM
+ list [sqlite3_column_type $VM 0] [sqlite3_column_type $VM 1] \
+ [sqlite3_column_type $VM 2]
+} {INTEGER INTEGER INTEGER}
+do_test bind-13.4 {
+ sqlite3_reset $VM
+ sqlite3_clear_bindings $VM
+ sqlite3_step $VM
+ list [sqlite3_column_type $VM 0] [sqlite3_column_type $VM 1] \
+ [sqlite3_column_type $VM 2]
+} {NULL NULL NULL}
+sqlite3_finalize $VM
+
+finish_test
diff --git a/third_party/sqlite/test/bindxfer.test b/third_party/sqlite/test/bindxfer.test
new file mode 100755
index 0000000..303db24
--- /dev/null
+++ b/third_party/sqlite/test/bindxfer.test
@@ -0,0 +1,84 @@
+# 2005 April 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script testing the sqlite_transfer_bindings() API.
+#
+# $Id: bindxfer.test,v 1.5 2008/03/17 16:23:27 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc sqlite_step {stmt VALS COLS} {
+ upvar #0 $VALS vals
+ upvar #0 $COLS cols
+ set vals [list]
+ set cols [list]
+
+ set rc [sqlite3_step $stmt]
+ for {set i 0} {$i < [sqlite3_column_count $stmt]} {incr i} {
+ lappend cols [sqlite3_column_name $stmt $i]
+ }
+ for {set i 0} {$i < [sqlite3_data_count $stmt]} {incr i} {
+ lappend vals [sqlite3_column_text $stmt $i]
+ }
+
+ return $rc
+}
+
+do_test bindxfer-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ execsql {CREATE TABLE t1(a,b,c);}
+ set VM1 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL]
+ set TAIL
+} {}
+do_test bindxfer-1.2 {
+ sqlite3_bind_parameter_count $VM1
+} 3
+do_test bindxfer-1.3 {
+ set VM2 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL]
+ set TAIL
+} {}
+do_test bindxfer-1.4 {
+ sqlite3_bind_parameter_count $VM2
+} 3
+do_test bindxfer-1.5 {
+ sqlite_bind $VM1 1 one normal
+ set sqlite_static_bind_value two
+ sqlite_bind $VM1 2 {} static
+ sqlite_bind $VM1 3 {} null
+ sqlite3_transfer_bindings $VM1 $VM2
+ sqlite_step $VM1 VALUES COLNAMES
+} SQLITE_ROW
+do_test bindxfer-1.6 {
+ set VALUES
+} {{} {} {}}
+do_test bindxfer-1.7 {
+ sqlite_step $VM2 VALUES COLNAMES
+} SQLITE_ROW
+do_test bindxfer-1.8 {
+ set VALUES
+} {one two {}}
+do_test bindxfer-1.9-misuse {
+ catch {sqlite3_finalize $VM1}
+ catch {sqlite3_finalize $VM2}
+ sqlite3_transfer_bindings $VM1 $VM2
+} 21 ;# SQLITE_MISUSE
+do_test bindxfer-1.10 {
+ set VM1 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL]
+ set VM2 [sqlite3_prepare $DB {SELECT ?, ?, ?, ?} -1 TAIL]
+ sqlite3_transfer_bindings $VM1 $VM2
+} 1 ;# SQLITE_ERROR
+catch {sqlite3_finalize $VM1}
+catch {sqlite3_finalize $VM2}
+
+
+finish_test
diff --git a/third_party/sqlite/test/bitvec.test b/third_party/sqlite/test/bitvec.test
new file mode 100755
index 0000000..aff2bf7
--- /dev/null
+++ b/third_party/sqlite/test/bitvec.test
@@ -0,0 +1,191 @@
+# 2008 February 18
+#
+# 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.
+#
+#***********************************************************************
+#
+# Unit testing of the Bitvec object.
+#
+# $Id: bitvec.test,v 1.2 2008/03/21 16:45:48 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# The built-in test logic must be operational in order for
+# this test to work.
+ifcapable !builtin_test {
+ finish_test
+ return
+}
+
+# Test that sqlite3BitvecBuiltinTest correctly reports errors
+# that are deliberately introduced.
+#
+do_test bitvec-1.0.1 {
+ sqlite3BitvecBuiltinTest 400 {5 1 1 1 0}
+} 1
+do_test bitvec-1.0.2 {
+ sqlite3BitvecBuiltinTest 400 {5 1 234 1 0}
+} 234
+
+# Run test cases that set every bit in vectors of various sizes.
+# for larger cases, this should cycle the bit vector representation
+# from hashing into subbitmaps. The subbitmaps should start as
+# hashes then change to either subbitmaps or linear maps, depending
+# on their size.
+#
+do_test bitvec-1.1 {
+ sqlite3BitvecBuiltinTest 400 {1 400 1 1 0}
+} 0
+do_test bitvec-1.2 {
+ sqlite3BitvecBuiltinTest 4000 {1 4000 1 1 0}
+} 0
+do_test bitvec-1.3 {
+ sqlite3BitvecBuiltinTest 40000 {1 40000 1 1 0}
+} 0
+do_test bitvec-1.4 {
+ sqlite3BitvecBuiltinTest 400000 {1 400000 1 1 0}
+} 0
+
+# By specifying a larger increments, we spread the load around.
+#
+do_test bitvec-1.5 {
+ sqlite3BitvecBuiltinTest 400 {1 400 1 7 0}
+} 0
+do_test bitvec-1.6 {
+ sqlite3BitvecBuiltinTest 4000 {1 4000 1 7 0}
+} 0
+do_test bitvec-1.7 {
+ sqlite3BitvecBuiltinTest 40000 {1 40000 1 7 0}
+} 0
+do_test bitvec-1.8 {
+ sqlite3BitvecBuiltinTest 400000 {1 400000 1 7 0}
+} 0
+
+# First fill up the bitmap with ones, then go through and
+# clear all the bits. This will stress the clearing mechanism.
+#
+do_test bitvec-1.9 {
+ sqlite3BitvecBuiltinTest 400 {1 400 1 1 2 400 1 1 0}
+} 0
+do_test bitvec-1.10 {
+ sqlite3BitvecBuiltinTest 4000 {1 4000 1 1 2 4000 1 1 0}
+} 0
+do_test bitvec-1.11 {
+ sqlite3BitvecBuiltinTest 40000 {1 40000 1 1 2 40000 1 1 0}
+} 0
+do_test bitvec-1.12 {
+ sqlite3BitvecBuiltinTest 400000 {1 400000 1 1 2 400000 1 1 0}
+} 0
+
+do_test bitvec-1.13 {
+ sqlite3BitvecBuiltinTest 400 {1 400 1 1 2 400 1 7 0}
+} 0
+do_test bitvec-1.15 {
+ sqlite3BitvecBuiltinTest 4000 {1 4000 1 1 2 4000 1 7 0}
+} 0
+do_test bitvec-1.16 {
+ sqlite3BitvecBuiltinTest 40000 {1 40000 1 1 2 40000 1 77 0}
+} 0
+do_test bitvec-1.17 {
+ sqlite3BitvecBuiltinTest 400000 {1 400000 1 1 2 400000 1 777 0}
+} 0
+
+do_test bitvec-1.18 {
+ sqlite3BitvecBuiltinTest 400000 {1 5000 100000 1 2 400000 1 37 0}
+} 0
+
+# Attempt to induce hash collisions.
+#
+unset -nocomplain start
+unset -nocomplain incr
+foreach start {1 2 3 4 5 6 7 8} {
+ foreach incr {124 125} {
+ do_test bitvec-1.20.$start.$incr {
+ set prog [list 1 60 $::start $::incr 2 5000 1 1 0]
+ sqlite3BitvecBuiltinTest 5000 $prog
+ } 0
+ }
+}
+
+do_test bitvec-1.30.big_and_slow {
+ sqlite3BitvecBuiltinTest 17000000 {1 17000000 1 1 2 17000000 1 1 0}
+} 0
+
+
+# Test setting and clearing a random subset of bits.
+#
+do_test bitvec-2.1 {
+ sqlite3BitvecBuiltinTest 4000 {3 2000 4 2000 0}
+} 0
+do_test bitvec-2.2 {
+ sqlite3BitvecBuiltinTest 4000 {3 1000 4 1000 3 1000 4 1000 3 1000 4 1000
+ 3 1000 4 1000 3 1000 4 1000 3 1000 4 1000 0}
+} 0
+do_test bitvec-2.3 {
+ sqlite3BitvecBuiltinTest 400000 {3 10 0}
+} 0
+do_test bitvec-2.4 {
+ sqlite3BitvecBuiltinTest 4000 {3 10 2 4000 1 1 0}
+} 0
+do_test bitvec-2.5 {
+ sqlite3BitvecBuiltinTest 5000 {3 20 2 5000 1 1 0}
+} 0
+do_test bitvec-2.6 {
+ sqlite3BitvecBuiltinTest 50000 {3 60 2 50000 1 1 0}
+} 0
+
+# This procedure runs sqlite3BitvecBuiltinTest with argments "n" and
+# "program". But it also causes a malloc error to occur after the
+# "failcnt"-th malloc. The result should be "0" if no malloc failure
+# occurs or "-1" if there is a malloc failure.
+#
+proc bitvec_malloc_test {label failcnt n program} {
+ do_test $label [subst {
+ sqlite3_memdebug_fail $failcnt
+ set x \[sqlite3BitvecBuiltinTest $n [list $program]\]
+ set nFail \[sqlite3_memdebug_fail -1\]
+ if {\$nFail==0} {
+ set ::go 0
+ set x -1
+ }
+ set x
+ }] -1
+}
+
+# Make sure malloc failures are handled sanily.
+#
+unset -nocomplain n
+unset -nocomplain go
+set go 1
+save_prng_state
+for {set n 0} {$go} {incr n} {
+ restore_prng_state
+ bitvec_malloc_test bitvec-3.1.$n $n 5000 {
+ 3 60 2 5000 1 1 3 60 2 5000 1 1 3 60 2 5000 1 1 0
+ }
+}
+set go 1
+for {set n 0} {$go} {incr n} {
+ restore_prng_state
+ bitvec_malloc_test bitvec-3.2.$n $n 5000 {
+ 3 600 2 5000 1 1 3 600 2 5000 1 1 3 600 2 5000 1 1 0
+ }
+}
+set go 1
+for {set n 1} {$go} {incr n} {
+ bitvec_malloc_test bitvec-3.3.$n $n 50000 {1 50000 1 1 0}
+}
+
+finish_test
+return
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/blob.test b/third_party/sqlite/test/blob.test
new file mode 100755
index 0000000..0021ddb
--- /dev/null
+++ b/third_party/sqlite/test/blob.test
@@ -0,0 +1,147 @@
+# 2001 September 15
+#
+# 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.
+#
+# $Id: blob.test,v 1.7 2008/04/04 15:12:22 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!bloblit} {
+ finish_test
+ return
+}
+
+proc bin_to_hex {blob} {
+ set bytes {}
+ binary scan $blob \c* bytes
+ set bytes2 [list]
+ foreach b $bytes {lappend bytes2 [format %02X [expr $b & 0xFF]]}
+ join $bytes2 {}
+}
+
+# Simplest possible case. Specify a blob literal
+do_test blob-1.0 {
+ set blob [execsql {SELECT X'01020304';}]
+ bin_to_hex [lindex $blob 0]
+} {01020304}
+do_test blob-1.1 {
+ set blob [execsql {SELECT x'ABCDEF';}]
+ bin_to_hex [lindex $blob 0]
+} {ABCDEF}
+do_test blob-1.2 {
+ set blob [execsql {SELECT x'';}]
+ bin_to_hex [lindex $blob 0]
+} {}
+do_test blob-1.3 {
+ set blob [execsql {SELECT x'abcdEF12';}]
+ bin_to_hex [lindex $blob 0]
+} {ABCDEF12}
+do_test blob-1.3.2 {
+ set blob [execsql {SELECT x'0123456789abcdefABCDEF';}]
+ bin_to_hex [lindex $blob 0]
+} {0123456789ABCDEFABCDEF}
+
+# Try some syntax errors in blob literals.
+do_test blob-1.4 {
+ catchsql {SELECT X'01020k304', 100}
+} {1 {unrecognized token: "X'01020k304'"}}
+do_test blob-1.5 {
+ catchsql {SELECT X'01020, 100}
+} {1 {unrecognized token: "X'01020, 100"}}
+do_test blob-1.6 {
+ catchsql {SELECT X'01020 100'}
+} {1 {unrecognized token: "X'01020 100'"}}
+do_test blob-1.7 {
+ catchsql {SELECT X'01001'}
+} {1 {unrecognized token: "X'01001'"}}
+do_test blob-1.8 {
+ catchsql {SELECT x'012/45'}
+} {1 {unrecognized token: "x'012/45'"}}
+do_test blob-1.9 {
+ catchsql {SELECT x'012:45'}
+} {1 {unrecognized token: "x'012:45'"}}
+do_test blob-1.10 {
+ catchsql {SELECT x'012@45'}
+} {1 {unrecognized token: "x'012@45'"}}
+do_test blob-1.11 {
+ catchsql {SELECT x'012G45'}
+} {1 {unrecognized token: "x'012G45'"}}
+do_test blob-1.12 {
+ catchsql {SELECT x'012`45'}
+} {1 {unrecognized token: "x'012`45'"}}
+do_test blob-1.13 {
+ catchsql {SELECT x'012g45'}
+} {1 {unrecognized token: "x'012g45'"}}
+
+
+# Insert a blob into a table and retrieve it.
+do_test blob-2.0 {
+ execsql {
+ CREATE TABLE t1(a BLOB, b BLOB);
+ INSERT INTO t1 VALUES(X'123456', x'7890ab');
+ INSERT INTO t1 VALUES(X'CDEF12', x'345678');
+ }
+ set blobs [execsql {SELECT * FROM t1}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {123456 7890AB CDEF12 345678}
+
+# An index on a blob column
+do_test blob-2.1 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ }
+ set blobs [execsql {SELECT * FROM t1}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {123456 7890AB CDEF12 345678}
+do_test blob-2.2 {
+ set blobs [execsql {SELECT * FROM t1 where a = X'123456'}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {123456 7890AB}
+do_test blob-2.3 {
+ set blobs [execsql {SELECT * FROM t1 where a = X'CDEF12'}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {CDEF12 345678}
+do_test blob-2.4 {
+ set blobs [execsql {SELECT * FROM t1 where a = X'CD12'}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {}
+
+# Try to bind a blob value to a prepared statement.
+do_test blob-3.0 {
+ sqlite3 db2 test.db
+ set DB [sqlite3_connection_pointer db2]
+ set STMT [sqlite3_prepare $DB "DELETE FROM t1 WHERE a = ?" -1 DUMMY]
+ sqlite3_bind_blob $STMT 1 "\x12\x34\x56" 3
+ sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test blob-3.1 {
+ sqlite3_finalize $STMT
+ db2 close
+} {}
+do_test blob-2.3 {
+ set blobs [execsql {SELECT * FROM t1}]
+ set blobs2 [list]
+ foreach b $blobs {lappend blobs2 [bin_to_hex $b]}
+ set blobs2
+} {CDEF12 345678}
+
+finish_test
diff --git a/third_party/sqlite/test/busy.test b/third_party/sqlite/test/busy.test
new file mode 100755
index 0000000..585f764
--- /dev/null
+++ b/third_party/sqlite/test/busy.test
@@ -0,0 +1,61 @@
+# 2005 july 8
+#
+# 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 test the busy handler
+#
+# $Id: busy.test,v 1.3 2008/03/15 02:09:22 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test busy-1.1 {
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ SELECT * FROM t1
+ }
+} 1
+proc busy x {
+ lappend ::busyargs $x
+ if {$x>2} {return 1}
+ return 0
+}
+set busyargs {}
+do_test busy-1.2 {
+ db busy busy
+ db2 eval {BEGIN EXCLUSIVE}
+ catchsql {BEGIN IMMEDIATE}
+} {1 {database is locked}}
+do_test busy-1.3 {
+ set busyargs
+} {0 1 2 3}
+do_test busy-1.4 {
+ set busyargs {}
+ catchsql {BEGIN IMMEDIATE}
+ set busyargs
+} {0 1 2 3}
+
+do_test busy-2.1 {
+ db2 eval {COMMIT}
+ db eval {BEGIN; INSERT INTO t1 VALUES(5)}
+ db2 eval {BEGIN; SELECT * FROM t1}
+ set busyargs {}
+ catchsql COMMIT
+} {1 {database is locked}}
+do_test busy-2.2 {
+ set busyargs
+} {0 1 2 3}
+
+
+db2 close
+
+finish_test
diff --git a/third_party/sqlite/test/cache.test b/third_party/sqlite/test/cache.test
new file mode 100755
index 0000000..dd51c7c
--- /dev/null
+++ b/third_party/sqlite/test/cache.test
@@ -0,0 +1,63 @@
+# 2007 March 24
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: cache.test,v 1.4 2007/08/22 02:56:44 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+ finish_test
+ return
+}
+sqlite3_soft_heap_limit 0
+
+proc pager_cache_size {db} {
+ set bt [btree_from_db $db]
+ db_enter $db
+ array set stats [btree_pager_stats $bt]
+ db_leave $db
+ return $stats(page)
+}
+
+do_test cache-1.1 {
+ pager_cache_size db
+} {0}
+
+do_test cache-1.2 {
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+ pager_cache_size db
+} {2}
+
+# At one point, repeatedly locking and unlocking the cache was causing
+# a resource leak of one page per repetition. The page wasn't actually
+# leaked, but would not be reused until the pager-cache was full (i.e.
+# 2000 pages by default).
+#
+# This tests that once the pager-cache is initialised, it can be locked
+# and unlocked repeatedly without internally allocating any new pages.
+#
+set cache_size [pager_cache_size db]
+for {set ii 0} {$ii < 10} {incr ii} {
+
+ do_test cache-1.3.$ii {
+ execsql {SELECT * FROM abc}
+ pager_cache_size db
+ } $::cache_size
+
+}
+sqlite3_soft_heap_limit $soft_limit
+
+finish_test
diff --git a/third_party/sqlite/test/capi2.test b/third_party/sqlite/test/capi2.test
new file mode 100755
index 0000000..147b234
--- /dev/null
+++ b/third_party/sqlite/test/capi2.test
@@ -0,0 +1,791 @@
+# 2003 January 29
+#
+# 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 testing the callback-free C/C++ API.
+#
+# $Id: capi2.test,v 1.35 2008/01/16 17:46:38 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Return the text values from the current row pointed at by STMT as a list.
+proc get_row_values {STMT} {
+ set VALUES [list]
+ for {set i 0} {$i < [sqlite3_data_count $STMT]} {incr i} {
+ lappend VALUES [sqlite3_column_text $STMT $i]
+ }
+ return $VALUES
+}
+
+# Return the column names followed by declaration types for the result set
+# of the SQL statement STMT.
+#
+# i.e. for:
+# CREATE TABLE abc(a text, b integer);
+# SELECT * FROM abc;
+#
+# The result is {a b text integer}
+proc get_column_names {STMT} {
+ set VALUES [list]
+ for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
+ lappend VALUES [sqlite3_column_name $STMT $i]
+ }
+ for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
+ lappend VALUES [sqlite3_column_decltype $STMT $i]
+ }
+ return $VALUES
+}
+
+# Check basic functionality
+#
+do_test capi2-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ execsql {CREATE TABLE t1(a,b,c)}
+ set VM [sqlite3_prepare $DB {SELECT name, rowid FROM sqlite_master} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-1.2 {
+ sqlite3_step $VM
+} {SQLITE_ROW}
+do_test capi2-1.3 {
+ sqlite3_data_count $VM
+} {2}
+do_test capi2-1.4 {
+ get_row_values $VM
+} {t1 1}
+do_test capi2-1.5 {
+ get_column_names $VM
+} {name rowid text INTEGER}
+do_test capi2-1.6 {
+ sqlite3_step $VM
+} {SQLITE_DONE}
+do_test capi2-1.7 {
+ list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM]
+} {2 {} {name rowid text INTEGER}}
+do_test capi2-1.8-misuse {
+ sqlite3_step $VM
+} {SQLITE_MISUSE}
+
+# Update: In v2, once SQLITE_MISUSE is returned the statement handle cannot
+# be interrogated for more information. However in v3, since the column
+# count, names and types are determined at compile time, these are still
+# accessible after an SQLITE_MISUSE error.
+do_test capi2-1.9 {
+ list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM]
+} {2 {} {name rowid text INTEGER}}
+do_test capi2-1.10 {
+ sqlite3_data_count $VM
+} {0}
+
+do_test capi2-1.11 {
+ sqlite3_finalize $VM
+} {SQLITE_OK}
+
+# Check to make sure that the "tail" of a multi-statement SQL script
+# is returned by sqlite3_prepare.
+#
+do_test capi2-2.1 {
+ set SQL {
+ SELECT name, rowid FROM sqlite_master;
+ SELECT name, rowid FROM sqlite_master WHERE 0;
+ -- A comment at the end
+ }
+ set VM [sqlite3_prepare $DB $SQL -1 SQL]
+ set SQL
+} {
+ SELECT name, rowid FROM sqlite_master WHERE 0;
+ -- A comment at the end
+ }
+do_test capi2-2.2 {
+ set r [sqlite3_step $VM]
+ lappend r [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_ROW 2 {t1 1} {name rowid text INTEGER}}
+do_test capi2-2.3 {
+ set r [sqlite3_step $VM]
+ lappend r [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_DONE 2 {} {name rowid text INTEGER}}
+do_test capi2-2.4 {
+ sqlite3_finalize $VM
+} {SQLITE_OK}
+do_test capi2-2.5 {
+ set VM [sqlite3_prepare $DB $SQL -1 SQL]
+ set SQL
+} {
+ -- A comment at the end
+ }
+do_test capi2-2.6 {
+ set r [sqlite3_step $VM]
+ lappend r [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_DONE 2 {} {name rowid text INTEGER}}
+do_test capi2-2.7 {
+ sqlite3_finalize $VM
+} {SQLITE_OK}
+do_test capi2-2.8 {
+ set VM [sqlite3_prepare $DB $SQL -1 SQL]
+ list $SQL $VM
+} {{} {}}
+
+# Check the error handling.
+#
+do_test capi2-3.1 {
+ set rc [catch {
+ sqlite3_prepare $DB {select bogus from sqlite_master} -1 TAIL
+ } msg]
+ lappend rc $msg $TAIL
+} {1 {(1) no such column: bogus} {}}
+do_test capi2-3.2 {
+ set rc [catch {
+ sqlite3_prepare $DB {select bogus from } -1 TAIL
+ } msg]
+ lappend rc $msg $TAIL
+} {1 {(1) near " ": syntax error} {}}
+do_test capi2-3.3 {
+ set rc [catch {
+ sqlite3_prepare $DB {;;;;select bogus from sqlite_master} -1 TAIL
+ } msg]
+ lappend rc $msg $TAIL
+} {1 {(1) no such column: bogus} {}}
+do_test capi2-3.4 {
+ set rc [catch {
+ sqlite3_prepare $DB {select bogus from sqlite_master;x;} -1 TAIL
+ } msg]
+ lappend rc $msg $TAIL
+} {1 {(1) no such column: bogus} {x;}}
+do_test capi2-3.5 {
+ set rc [catch {
+ sqlite3_prepare $DB {select bogus from sqlite_master;;;x;} -1 TAIL
+ } msg]
+ lappend rc $msg $TAIL
+} {1 {(1) no such column: bogus} {;;x;}}
+do_test capi2-3.6 {
+ set rc [catch {
+ sqlite3_prepare $DB {select 5/0} -1 TAIL
+ } VM]
+ lappend rc $TAIL
+} {0 {}}
+do_test capi2-3.7 {
+ list [sqlite3_step $VM] \
+ [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_ROW 1 {{}} {5/0 {}}}
+do_test capi2-3.8 {
+ sqlite3_finalize $VM
+} {SQLITE_OK}
+do_test capi2-3.9 {
+ execsql {CREATE UNIQUE INDEX i1 ON t1(a)}
+ set VM [sqlite3_prepare $DB {INSERT INTO t1 VALUES(1,2,3)} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-3.9b {db changes} {0}
+do_test capi2-3.10 {
+ list [sqlite3_step $VM] \
+ [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_DONE 0 {} {}}
+
+# Update for v3 - the change has not actually happened until the query is
+# finalized. Is this going to cause trouble for anyone? Lee Nelson maybe?
+# (Later:) The change now happens just before SQLITE_DONE is returned.
+do_test capi2-3.10b {db changes} {1}
+do_test capi2-3.11 {
+ sqlite3_finalize $VM
+} {SQLITE_OK}
+do_test capi2-3.11b {db changes} {1}
+#do_test capi2-3.12-misuse {
+# sqlite3_finalize $VM
+#} {SQLITE_MISUSE}
+do_test capi2-3.13 {
+ set VM [sqlite3_prepare $DB {INSERT INTO t1 VALUES(1,3,4)} -1 TAIL]
+ list [sqlite3_step $VM] \
+ [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_ERROR 0 {} {}}
+
+# Update for v3: Preparing a statement does not affect the change counter.
+# (Test result changes from 0 to 1). (Later:) change counter updates occur
+# when sqlite3_step returns, not at finalize time.
+do_test capi2-3.13b {db changes} {0}
+
+do_test capi2-3.14 {
+ list [sqlite3_finalize $VM] [sqlite3_errmsg $DB]
+} {SQLITE_CONSTRAINT {column a is not unique}}
+do_test capi2-3.15 {
+ set VM [sqlite3_prepare $DB {CREATE TABLE t2(a NOT NULL, b)} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-3.16 {
+ list [sqlite3_step $VM] \
+ [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_DONE 0 {} {}}
+do_test capi2-3.17 {
+ list [sqlite3_finalize $VM] [sqlite3_errmsg $DB]
+} {SQLITE_OK {not an error}}
+do_test capi2-3.18 {
+ set VM [sqlite3_prepare $DB {INSERT INTO t2 VALUES(NULL,2)} -1 TAIL]
+ list [sqlite3_step $VM] \
+ [sqlite3_column_count $VM] \
+ [get_row_values $VM] \
+ [get_column_names $VM]
+} {SQLITE_ERROR 0 {} {}}
+do_test capi2-3.19 {
+ list [sqlite3_finalize $VM] [sqlite3_errmsg $DB]
+} {SQLITE_CONSTRAINT {t2.a may not be NULL}}
+
+do_test capi2-3.20 {
+ execsql {
+ CREATE TABLE a1(message_id, name , UNIQUE(message_id, name) );
+ INSERT INTO a1 VALUES(1, 1);
+ }
+} {}
+do_test capi2-3.21 {
+ set VM [sqlite3_prepare $DB {INSERT INTO a1 VALUES(1, 1)} -1 TAIL]
+ sqlite3_step $VM
+} {SQLITE_ERROR}
+do_test capi2-3.22 {
+ sqlite3_errcode $DB
+} {SQLITE_ERROR}
+do_test capi2-3.23 {
+ sqlite3_finalize $VM
+} {SQLITE_CONSTRAINT}
+do_test capi2-3.24 {
+ sqlite3_errcode $DB
+} {SQLITE_CONSTRAINT}
+
+# Two or more virtual machines exists at the same time.
+#
+do_test capi2-4.1 {
+ set VM1 [sqlite3_prepare $DB {INSERT INTO t2 VALUES(1,2)} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-4.2 {
+ set VM2 [sqlite3_prepare $DB {INSERT INTO t2 VALUES(2,3)} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-4.3 {
+ set VM3 [sqlite3_prepare $DB {INSERT INTO t2 VALUES(3,4)} -1 TAIL]
+ set TAIL
+} {}
+do_test capi2-4.4 {
+ list [sqlite3_step $VM2] \
+ [sqlite3_column_count $VM2] \
+ [get_row_values $VM2] \
+ [get_column_names $VM2]
+} {SQLITE_DONE 0 {} {}}
+do_test capi2-4.5 {
+ execsql {SELECT * FROM t2 ORDER BY a}
+} {2 3}
+do_test capi2-4.6 {
+ sqlite3_finalize $VM2
+} {SQLITE_OK}
+do_test capi2-4.7 {
+ list [sqlite3_step $VM3] \
+ [sqlite3_column_count $VM3] \
+ [get_row_values $VM3] \
+ [get_column_names $VM3]
+} {SQLITE_DONE 0 {} {}}
+do_test capi2-4.8 {
+ execsql {SELECT * FROM t2 ORDER BY a}
+} {2 3 3 4}
+do_test capi2-4.9 {
+ sqlite3_finalize $VM3
+} {SQLITE_OK}
+do_test capi2-4.10 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_DONE 0 {} {}}
+do_test capi2-4.11 {
+ execsql {SELECT * FROM t2 ORDER BY a}
+} {1 2 2 3 3 4}
+do_test capi2-4.12 {
+ sqlite3_finalize $VM1
+} {SQLITE_OK}
+
+# Interleaved SELECTs
+#
+do_test capi2-5.1 {
+ set VM1 [sqlite3_prepare $DB {SELECT * FROM t2} -1 TAIL]
+ set VM2 [sqlite3_prepare $DB {SELECT * FROM t2} -1 TAIL]
+ set VM3 [sqlite3_prepare $DB {SELECT * FROM t2} -1 TAIL]
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 2 {2 3} {a b {} {}}}
+do_test capi2-5.2 {
+ list [sqlite3_step $VM2] \
+ [sqlite3_column_count $VM2] \
+ [get_row_values $VM2] \
+ [get_column_names $VM2]
+} {SQLITE_ROW 2 {2 3} {a b {} {}}}
+do_test capi2-5.3 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 2 {3 4} {a b {} {}}}
+do_test capi2-5.4 {
+ list [sqlite3_step $VM3] \
+ [sqlite3_column_count $VM3] \
+ [get_row_values $VM3] \
+ [get_column_names $VM3]
+} {SQLITE_ROW 2 {2 3} {a b {} {}}}
+do_test capi2-5.5 {
+ list [sqlite3_step $VM3] \
+ [sqlite3_column_count $VM3] \
+ [get_row_values $VM3] \
+ [get_column_names $VM3]
+} {SQLITE_ROW 2 {3 4} {a b {} {}}}
+do_test capi2-5.6 {
+ list [sqlite3_step $VM3] \
+ [sqlite3_column_count $VM3] \
+ [get_row_values $VM3] \
+ [get_column_names $VM3]
+} {SQLITE_ROW 2 {1 2} {a b {} {}}}
+do_test capi2-5.7 {
+ list [sqlite3_step $VM3] \
+ [sqlite3_column_count $VM3] \
+ [get_row_values $VM3] \
+ [get_column_names $VM3]
+} {SQLITE_DONE 2 {} {a b {} {}}}
+do_test capi2-5.8 {
+ sqlite3_finalize $VM3
+} {SQLITE_OK}
+do_test capi2-5.9 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 2 {1 2} {a b {} {}}}
+do_test capi2-5.10 {
+ sqlite3_finalize $VM1
+} {SQLITE_OK}
+do_test capi2-5.11 {
+ list [sqlite3_step $VM2] \
+ [sqlite3_column_count $VM2] \
+ [get_row_values $VM2] \
+ [get_column_names $VM2]
+} {SQLITE_ROW 2 {3 4} {a b {} {}}}
+do_test capi2-5.12 {
+ list [sqlite3_step $VM2] \
+ [sqlite3_column_count $VM2] \
+ [get_row_values $VM2] \
+ [get_column_names $VM2]
+} {SQLITE_ROW 2 {1 2} {a b {} {}}}
+do_test capi2-5.11 {
+ sqlite3_finalize $VM2
+} {SQLITE_OK}
+
+# Check for proper SQLITE_BUSY returns.
+#
+do_test capi2-6.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x counter);
+ INSERT INTO t3 VALUES(1);
+ INSERT INTO t3 VALUES(2);
+ INSERT INTO t3 SELECT x+2 FROM t3;
+ INSERT INTO t3 SELECT x+4 FROM t3;
+ INSERT INTO t3 SELECT x+8 FROM t3;
+ COMMIT;
+ }
+ set VM1 [sqlite3_prepare $DB {SELECT * FROM t3} -1 TAIL]
+ sqlite3 db2 test.db
+ execsql {BEGIN} db2
+} {}
+# Update for v3: BEGIN doesn't write-lock the database. It is quite
+# difficult to get v3 to write-lock the database, which causes a few
+# problems for test scripts.
+#
+# do_test capi2-6.2 {
+# list [sqlite3_step $VM1] \
+# [sqlite3_column_count $VM1] \
+# [get_row_values $VM1] \
+# [get_column_names $VM1]
+# } {SQLITE_BUSY 0 {} {}}
+do_test capi2-6.3 {
+ execsql {COMMIT} db2
+} {}
+do_test capi2-6.4 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 1 {x counter}}
+do_test capi2-6.5 {
+ catchsql {INSERT INTO t3 VALUES(10);} db2
+} {1 {database is locked}}
+do_test capi2-6.6 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 2 {x counter}}
+do_test capi2-6.7 {
+ execsql {SELECT * FROM t2} db2
+} {2 3 3 4 1 2}
+do_test capi2-6.8 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 3 {x counter}}
+do_test capi2-6.9 {
+ execsql {SELECT * FROM t2}
+} {2 3 3 4 1 2}
+do_test capi2-6.10 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 4 {x counter}}
+do_test capi2-6.11 {
+ execsql {BEGIN}
+} {}
+do_test capi2-6.12 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 5 {x counter}}
+
+# A read no longer blocks a write in the same connection.
+#do_test capi2-6.13 {
+# catchsql {UPDATE t3 SET x=x+1}
+#} {1 {database table is locked}}
+
+do_test capi2-6.14 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 6 {x counter}}
+do_test capi2-6.15 {
+ execsql {SELECT * FROM t1}
+} {1 2 3}
+do_test capi2-6.16 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 7 {x counter}}
+do_test capi2-6.17 {
+ catchsql {UPDATE t1 SET b=b+1}
+} {0 {}}
+do_test capi2-6.18 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 8 {x counter}}
+do_test capi2-6.19 {
+ execsql {SELECT * FROM t1}
+} {1 3 3}
+do_test capi2-6.20 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 9 {x counter}}
+#do_test capi2-6.21 {
+# execsql {ROLLBACK; SELECT * FROM t1}
+#} {1 2 3}
+do_test capi2-6.22 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 10 {x counter}}
+#do_test capi2-6.23 {
+# execsql {BEGIN TRANSACTION;}
+#} {}
+do_test capi2-6.24 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 11 {x counter}}
+do_test capi2-6.25 {
+ execsql {
+ INSERT INTO t1 VALUES(2,3,4);
+ SELECT * FROM t1;
+ }
+} {1 3 3 2 3 4}
+do_test capi2-6.26 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 12 {x counter}}
+do_test capi2-6.27 {
+ catchsql {
+ INSERT INTO t1 VALUES(2,4,5);
+ SELECT * FROM t1;
+ }
+} {1 {column a is not unique}}
+do_test capi2-6.28 {
+ list [sqlite3_step $VM1] \
+ [sqlite3_column_count $VM1] \
+ [get_row_values $VM1] \
+ [get_column_names $VM1]
+} {SQLITE_ROW 1 13 {x counter}}
+do_test capi2-6.99 {
+ sqlite3_finalize $VM1
+} {SQLITE_OK}
+catchsql {ROLLBACK}
+
+do_test capi2-7.1 {
+ stepsql $DB {
+ SELECT * FROM t1
+ }
+} {0 1 2 3}
+do_test capi2-7.2 {
+ stepsql $DB {
+ PRAGMA count_changes=on
+ }
+} {0}
+do_test capi2-7.3 {
+ stepsql $DB {
+ UPDATE t1 SET a=a+10;
+ }
+} {0 1}
+do_test capi2-7.4 {
+ stepsql $DB {
+ INSERT INTO t1 SELECT a+1,b+1,c+1 FROM t1;
+ }
+} {0 1}
+do_test capi2-7.4b {sqlite3_changes $DB} {1}
+do_test capi2-7.5 {
+ stepsql $DB {
+ UPDATE t1 SET a=a+10;
+ }
+} {0 2}
+do_test capi2-7.5b {sqlite3_changes $DB} {2}
+do_test capi2-7.6 {
+ stepsql $DB {
+ SELECT * FROM t1;
+ }
+} {0 21 2 3 22 3 4}
+do_test capi2-7.7 {
+ stepsql $DB {
+ INSERT INTO t1 SELECT a+2,b+2,c+2 FROM t1;
+ }
+} {0 2}
+do_test capi2-7.8 {
+ sqlite3_changes $DB
+} {2}
+do_test capi2-7.9 {
+ stepsql $DB {
+ SELECT * FROM t1;
+ }
+} {0 21 2 3 22 3 4 23 4 5 24 5 6}
+do_test capi2-7.10 {
+ stepsql $DB {
+ UPDATE t1 SET a=a-20;
+ SELECT * FROM t1;
+ }
+} {0 4 1 2 3 2 3 4 3 4 5 4 5 6}
+
+# Update for version 3: A SELECT statement no longer resets the change
+# counter (Test result changes from 0 to 4).
+do_test capi2-7.11 {
+ sqlite3_changes $DB
+} {4}
+do_test capi2-7.11a {
+ execsql {SELECT count(*) FROM t1}
+} {4}
+
+ifcapable {explain} {
+ do_test capi2-7.12 {
+ set x [stepsql $DB {EXPLAIN SELECT * FROM t1}]
+ lindex $x 0
+ } {0}
+}
+
+# Ticket #261 - make sure we can finalize before the end of a query.
+#
+do_test capi2-8.1 {
+ set VM1 [sqlite3_prepare $DB {SELECT * FROM t2} -1 TAIL]
+ sqlite3_finalize $VM1
+} {SQLITE_OK}
+
+# Tickets #384 and #385 - make sure the TAIL argument to sqlite3_prepare
+# and all of the return pointers in sqlite_step can be null.
+#
+do_test capi2-9.1 {
+ set VM1 [sqlite3_prepare $DB {SELECT * FROM t2} -1 DUMMY]
+ sqlite3_step $VM1
+ sqlite3_finalize $VM1
+} {SQLITE_OK}
+
+# Test that passing a NULL pointer to sqlite3_finalize() or sqlite3_reset
+# does not cause an error.
+do_test capi2-10.1 {
+ sqlite3_finalize 0
+} {SQLITE_OK}
+do_test capi2-10.2 {
+ sqlite3_reset 0
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------------
+# The following tests - capi2-11.* - test the "column origin" APIs.
+#
+# sqlite3_column_origin_name()
+# sqlite3_column_database_name()
+# sqlite3_column_table_name()
+#
+
+ifcapable columnmetadata {
+
+# This proc uses the database handle $::DB to compile the SQL statement passed
+# as a parameter. The return value of this procedure is a list with one
+# element for each column returned by the compiled statement. Each element of
+# this list is itself a list of length three, consisting of the origin
+# database, table and column for the corresponding returned column.
+proc check_origins {sql} {
+ set ret [list]
+ set ::STMT [sqlite3_prepare $::DB $sql -1 dummy]
+ for {set i 0} {$i < [sqlite3_column_count $::STMT]} {incr i} {
+ lappend ret [list \
+ [sqlite3_column_database_name $::STMT $i] \
+ [sqlite3_column_table_name $::STMT $i] \
+ [sqlite3_column_origin_name $::STMT $i] \
+ ]
+ }
+ sqlite3_finalize $::STMT
+ return $ret
+}
+do_test capi2-11.1 {
+ execsql {
+ CREATE TABLE tab1(col1, col2);
+ }
+} {}
+do_test capi2-11.2 {
+ check_origins {SELECT col2, col1 FROM tab1}
+} [list {main tab1 col2} {main tab1 col1}]
+do_test capi2-11.3 {
+ check_origins {SELECT col2 AS hello, col1 AS world FROM tab1}
+} [list {main tab1 col2} {main tab1 col1}]
+
+ifcapable subquery {
+ do_test capi2-11.4 {
+ check_origins {SELECT b, a FROM (SELECT col1 AS a, col2 AS b FROM tab1)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-11.5 {
+ check_origins {SELECT (SELECT col2 FROM tab1), (SELECT col1 FROM tab1)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-11.6 {
+ check_origins {SELECT (SELECT col2), (SELECT col1) FROM tab1}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-11.7 {
+ check_origins {SELECT * FROM tab1}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-11.8 {
+ check_origins {SELECT * FROM (SELECT * FROM tab1)}
+ } [list {main tab1 col1} {main tab1 col2}]
+}
+
+ifcapable view&&subquery {
+ do_test capi2-12.1 {
+ execsql {
+ CREATE VIEW view1 AS SELECT * FROM tab1;
+ }
+ } {}
+ do_test capi2-12.2 {
+ check_origins {SELECT col2, col1 FROM view1}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-12.3 {
+ check_origins {SELECT col2 AS hello, col1 AS world FROM view1}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-12.4 {
+ check_origins {SELECT b, a FROM (SELECT col1 AS a, col2 AS b FROM view1)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-12.5 {
+ check_origins {SELECT (SELECT col2 FROM view1), (SELECT col1 FROM view1)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-12.6 {
+ check_origins {SELECT (SELECT col2), (SELECT col1) FROM view1}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-12.7 {
+ check_origins {SELECT * FROM view1}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-12.8 {
+ check_origins {select * from (select * from view1)}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-12.9 {
+ check_origins {select * from (select * from (select * from view1))}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-12.10 {
+ db close
+ sqlite3 db test.db
+ set ::DB [sqlite3_connection_pointer db]
+ check_origins {select * from (select * from (select * from view1))}
+ } [list {main tab1 col1} {main tab1 col2}]
+
+ # This view will thwart the flattening optimization.
+ do_test capi2-13.1 {
+ execsql {
+ CREATE VIEW view2 AS SELECT * FROM tab1 limit 10 offset 10;
+ }
+ } {}
+ do_test capi2-13.2 {
+ check_origins {SELECT col2, col1 FROM view2}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-13.3 {
+ check_origins {SELECT col2 AS hello, col1 AS world FROM view2}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-13.4 {
+ check_origins {SELECT b, a FROM (SELECT col1 AS a, col2 AS b FROM view2)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-13.5 {
+ check_origins {SELECT (SELECT col2 FROM view2), (SELECT col1 FROM view2)}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-13.6 {
+ check_origins {SELECT (SELECT col2), (SELECT col1) FROM view2}
+ } [list {main tab1 col2} {main tab1 col1}]
+ do_test capi2-13.7 {
+ check_origins {SELECT * FROM view2}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-13.8 {
+ check_origins {select * from (select * from view2)}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-13.9 {
+ check_origins {select * from (select * from (select * from view2))}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-13.10 {
+ db close
+ sqlite3 db test.db
+ set ::DB [sqlite3_connection_pointer db]
+ check_origins {select * from (select * from (select * from view2))}
+ } [list {main tab1 col1} {main tab1 col2}]
+ do_test capi2-13.11 {
+ check_origins {select * from (select * from tab1 limit 10 offset 10)}
+ } [list {main tab1 col1} {main tab1 col2}]
+}
+
+
+} ;# ifcapable columnmetadata
+
+db2 close
+finish_test
diff --git a/third_party/sqlite/test/capi3.test b/third_party/sqlite/test/capi3.test
new file mode 100755
index 0000000..5ac7487
--- /dev/null
+++ b/third_party/sqlite/test/capi3.test
@@ -0,0 +1,1198 @@
+# 2003 January 29
+#
+# 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 testing the callback-free C/C++ API.
+#
+# $Id: capi3.test,v 1.67 2008/07/12 15:55:55 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+proc utf16 {str {nt 1}} {
+ set r [encoding convertto unicode $str]
+ if {$nt} {
+ append r "\x00\x00"
+ }
+ return $r
+}
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str.
+proc utf8 {str} {
+ # If $str ends in two 0x00 0x00 bytes, knock these off before
+ # converting to UTF-8 using TCL.
+ binary scan $str \c* vals
+ if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+ set str [binary format \c* [lrange $vals 0 end-2]]
+ }
+
+ set r [encoding convertfrom unicode $str]
+ return $r
+}
+
+# These tests complement those in capi2.test. They are organized
+# as follows:
+#
+# capi3-1.*: Test sqlite3_prepare
+# capi3-2.*: Test sqlite3_prepare16
+# capi3-3.*: Test sqlite3_open
+# capi3-4.*: Test sqlite3_open16
+# capi3-5.*: Test the various sqlite3_result_* APIs
+# capi3-6.*: Test that sqlite3_close fails if there are outstanding VMs.
+#
+
+set DB [sqlite3_connection_pointer db]
+
+do_test capi3-1.0 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3-1.1 {
+ set STMT [sqlite3_prepare $DB {SELECT name FROM sqlite_master} -1 TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {}
+do_test capi3-1.2 {
+ sqlite3_errcode $DB
+} {SQLITE_OK}
+do_test capi3-1.3 {
+ sqlite3_errmsg $DB
+} {not an error}
+do_test capi3-1.4 {
+ set sql {SELECT name FROM sqlite_master;SELECT 10}
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {SELECT 10}
+do_test capi3-1.5 {
+ set sql {SELECT name FROM sqlite_master;SELECT 10}
+ set STMT [sqlite3_prepare $DB $sql [string length $sql] TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {SELECT 10}
+do_test capi3-1.6 {
+ set sql {SELECT name FROM sqlite_master;SELECT 10}
+ set STMT [sqlite3_prepare $DB $sql [expr [string length $sql]+1] TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {SELECT 10}
+
+do_test capi3-1.7 {
+ set sql {SELECT namex FROM sqlite_master}
+ catch {
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ }
+} {1}
+do_test capi3-1.8 {
+ sqlite3_errcode $DB
+} {SQLITE_ERROR}
+do_test capi3-1.9 {
+ sqlite3_errmsg $DB
+} {no such column: namex}
+
+ifcapable {utf16} {
+ do_test capi3-2.1 {
+ set sql16 [utf16 {SELECT name FROM sqlite_master}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 ::TAIL]
+ sqlite3_finalize $STMT
+ utf8 $::TAIL
+ } {}
+ do_test capi3-2.2 {
+ set sql [utf16 {SELECT name FROM sqlite_master;SELECT 10}]
+ set STMT [sqlite3_prepare16 $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ utf8 $TAIL
+ } {SELECT 10}
+ do_test capi3-2.3 {
+ set sql [utf16 {SELECT namex FROM sqlite_master}]
+ catch {
+ set STMT [sqlite3_prepare16 $DB $sql -1 TAIL]
+ }
+ } {1}
+ do_test capi3-2.4 {
+ sqlite3_errcode $DB
+ } {SQLITE_ERROR}
+ do_test capi3-2.5 {
+ sqlite3_errmsg $DB
+ } {no such column: namex}
+
+ ifcapable schema_pragmas {
+ do_test capi3-2.6 {
+ execsql {CREATE TABLE tablename(x)}
+ set sql16 [utf16 {PRAGMA table_info("TableName")}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ } SQLITE_ROW
+ do_test capi3-2.7 {
+ sqlite3_step $STMT
+ } SQLITE_DONE
+ do_test capi3-2.8 {
+ sqlite3_finalize $STMT
+ } SQLITE_OK
+ }
+
+} ;# endif utf16
+
+# rename sqlite3_open sqlite3_open_old
+# proc sqlite3_open {fname options} {sqlite3_open_new $fname $options}
+
+do_test capi3-3.1 {
+ set db2 [sqlite3_open test.db {}]
+ sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3-3.2 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-3.3 {
+ catch {
+ set db2 [sqlite3_open /bogus/path/test.db {}]
+ }
+ sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3-3.4 {
+ sqlite3_errmsg $db2
+} {unable to open database file}
+do_test capi3-3.5 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-3.6.1-misuse {
+ sqlite3_close $db2
+} {SQLITE_MISUSE}
+do_test capi3-3.6.2-misuse {
+ sqlite3_errmsg $db2
+} {library routine called out of sequence}
+ifcapable {utf16} {
+ do_test capi3-3.6.3-misuse {
+ utf8 [sqlite3_errmsg16 $db2]
+ } {library routine called out of sequence}
+}
+
+do_test capi3-3.7 {
+ set db2 [sqlite3_open]
+ sqlite3_errcode $db2
+} {SQLITE_OK}
+do_test capi3-3.8 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+
+# rename sqlite3_open ""
+# rename sqlite3_open_old sqlite3_open
+
+ifcapable {utf16} {
+do_test capi3-4.1 {
+ set db2 [sqlite3_open16 [utf16 test.db] {}]
+ sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3-4.2 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-4.3 {
+ catch {
+ set db2 [sqlite3_open16 [utf16 /bogus/path/test.db] {}]
+ }
+ sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3-4.4 {
+ utf8 [sqlite3_errmsg16 $db2]
+} {unable to open database file}
+do_test capi3-4.5 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+} ;# utf16
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_count
+# sqlite3_column_name
+# sqlite3_column_name16
+# sqlite3_column_decltype
+# sqlite3_column_decltype16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_header {STMT test names decltypes} {
+
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set ::numcols [sqlite3_column_count $STMT]
+ for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+
+ # Column names in UTF-8
+ do_test $test.1 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]}
+ set cnamelist
+ } $names
+
+ # Column names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.2 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $names
+ }
+
+ # Column names in UTF-8
+ do_test $test.3 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]}
+ set cnamelist
+ } $names
+
+ # Column names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.4 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $names
+ }
+
+ # Column names in UTF-8
+ do_test $test.5 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_decltype $STMT $i]}
+ set cnamelist
+ } $decltypes
+
+ # Column declaration types in UTF-16
+ ifcapable {utf16} {
+ do_test $test.6 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_decltype16 $STMT $i]]
+ }
+ set cnamelist
+ } $decltypes
+ }
+
+
+ # Test some out of range conditions:
+ ifcapable {utf16} {
+ do_test $test.7 {
+ list \
+ [sqlite3_column_name $STMT -1] \
+ [sqlite3_column_name16 $STMT -1] \
+ [sqlite3_column_decltype $STMT -1] \
+ [sqlite3_column_decltype16 $STMT -1] \
+ [sqlite3_column_name $STMT $numcols] \
+ [sqlite3_column_name16 $STMT $numcols] \
+ [sqlite3_column_decltype $STMT $numcols] \
+ [sqlite3_column_decltype16 $STMT $numcols]
+ } {{} {} {} {} {} {} {} {}}
+ }
+}
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_origin_name
+# sqlite3_column_origin_name16
+# sqlite3_column_table_name
+# sqlite3_column_table_name16
+# sqlite3_column_database_name
+# sqlite3_column_database_name16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_origin_header {STMT test dbs tables cols} {
+ # If sqlite3_column_origin_name() and friends are not compiled into
+ # this build, this proc is a no-op.
+ ifcapable columnmetadata {
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set ::numcols [sqlite3_column_count $STMT]
+ for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+
+ # Database names in UTF-8
+ do_test $test.8 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_database_name $STMT $i]
+ }
+ set cnamelist
+ } $dbs
+
+ # Database names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.9 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_database_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $dbs
+ }
+
+ # Table names in UTF-8
+ do_test $test.10 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_table_name $STMT $i]
+ }
+ set cnamelist
+ } $tables
+
+ # Table names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.11 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_table_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $tables
+ }
+
+ # Origin names in UTF-8
+ do_test $test.12 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_origin_name $STMT $i]
+ }
+ set cnamelist
+ } $cols
+
+ # Origin declaration types in UTF-16
+ ifcapable {utf16} {
+ do_test $test.13 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_origin_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $cols
+ }
+ }
+}
+
+# This proc is used to test the following APIs:
+#
+# sqlite3_data_count
+# sqlite3_column_type
+# sqlite3_column_int
+# sqlite3_column_text
+# sqlite3_column_text16
+# sqlite3_column_double
+#
+# $STMT is a compiled SQL statement for which the previous call
+# to sqlite3_step returned SQLITE_ROW. $test is a prefix to use
+# for test names within this proc. $types is a list of the
+# manifest types for the current row. $ints, $doubles and $strings
+# are lists of the integer, real and string representations of
+# the values in the current row.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare "SELECT 'hello', 1.1, NULL" -1 DUMMY]
+# sqlite3_step $STMT
+# check_data test1.2 {TEXT REAL NULL} {0 1 0} {0 1.1 0} {hello 1.1 {}}
+#
+proc check_data {STMT test types ints doubles strings} {
+
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set numcols [sqlite3_data_count $STMT]
+ for {set i 0} {$i < $numcols} {incr i} {lappend ::idxlist $i}
+
+# types
+do_test $test.1 {
+ set types [list]
+ foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+ set types
+} $types
+
+# Integers
+do_test $test.2 {
+ set ints [list]
+ foreach i $idxlist {lappend ints [sqlite3_column_int64 $STMT $i]}
+ set ints
+} $ints
+
+# bytes
+set lens [list]
+foreach i $::idxlist {
+ lappend lens [string length [lindex $strings $i]]
+}
+do_test $test.3 {
+ set bytes [list]
+ set lens [list]
+ foreach i $idxlist {
+ lappend bytes [sqlite3_column_bytes $STMT $i]
+ }
+ set bytes
+} $lens
+
+# bytes16
+ifcapable {utf16} {
+ set lens [list]
+ foreach i $::idxlist {
+ lappend lens [expr 2 * [string length [lindex $strings $i]]]
+ }
+ do_test $test.4 {
+ set bytes [list]
+ set lens [list]
+ foreach i $idxlist {
+ lappend bytes [sqlite3_column_bytes16 $STMT $i]
+ }
+ set bytes
+ } $lens
+}
+
+# Blob
+do_test $test.5 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_blob $STMT $i]}
+ set utf8
+} $strings
+
+# UTF-8
+do_test $test.6 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+ set utf8
+} $strings
+
+# Floats
+do_test $test.7 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+ set utf8
+} $doubles
+
+# UTF-16
+ifcapable {utf16} {
+ do_test $test.8 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [utf8 [sqlite3_column_text16 $STMT $i]]}
+ set utf8
+ } $strings
+}
+
+# Integers
+do_test $test.9 {
+ set ints [list]
+ foreach i $idxlist {lappend ints [sqlite3_column_int $STMT $i]}
+ set ints
+} $ints
+
+# Floats
+do_test $test.10 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+ set utf8
+} $doubles
+
+# UTF-8
+do_test $test.11 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+ set utf8
+} $strings
+
+# Types
+do_test $test.12 {
+ set types [list]
+ foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+ set types
+} $types
+
+# Test that an out of range request returns the equivalent of NULL
+do_test $test.13 {
+ sqlite3_column_int $STMT -1
+} {0}
+do_test $test.13 {
+ sqlite3_column_text $STMT -1
+} {}
+
+}
+
+ifcapable !floatingpoint {
+ finish_test
+ return
+}
+
+do_test capi3-5.0 {
+ execsql {
+ CREATE TABLE t1(a VARINT, b BLOB, c VARCHAR(16));
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES('one', 'two', NULL);
+ INSERT INTO t1 VALUES(1.2, 1.3, 1.4);
+ }
+ set sql "SELECT * FROM t1"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3-5.1 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.1 {main main main} {t1 t1 t1} {a b c}
+do_test capi3-5.2 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.3 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.3 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.4 {INTEGER INTEGER TEXT} {1 2 3} {1.0 2.0 3.0} {1 2 3}
+
+do_test capi3-5.5 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.6 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.6 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.7 {TEXT TEXT NULL} {0 0 0} {0.0 0.0 0.0} {one two {}}
+
+do_test capi3-5.8 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.9 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.9 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.10 {FLOAT FLOAT TEXT} {1 1 1} {1.2 1.3 1.4} {1.2 1.3 1.4}
+
+do_test capi3-5.11 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+
+do_test capi3-5.12 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+do_test capi3-5.20 {
+ set sql "SELECT a, sum(b), max(c) FROM t1 GROUP BY a"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3-5.21 {a sum(b) max(c)} {VARINT {} {}}
+check_origin_header $STMT capi3-5.22 {main {} {}} {t1 {} {}} {a {} {}}
+do_test capi3-5.23 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+do_test capi3-5.30 {
+ set sql "SELECT a AS x, sum(b) AS y, max(c) AS z FROM t1 AS m GROUP BY x"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3-5.31 {x y z} {VARINT {} {}}
+check_origin_header $STMT capi3-5.32 {main {} {}} {t1 {} {}} {a {} {}}
+do_test capi3-5.33 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+
+set ::ENC [execsql {pragma encoding}]
+db close
+
+do_test capi3-6.0 {
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_key $DB xyzzy
+ set sql {SELECT a FROM t1 order by rowid}
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ expr 0
+} {0}
+do_test capi3-6.1 {
+ db cache flush
+ sqlite3_close $DB
+} {SQLITE_BUSY}
+do_test capi3-6.2 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
+do_test capi3-6.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-6.4-misuse {
+ db cache flush
+ sqlite3_close $DB
+} {SQLITE_OK}
+db close
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+if {![sqlite3 -has-codec]} {
+ # Test what happens when the library encounters a newer file format.
+ do_test capi3-7.1 {
+ set_file_format 5
+ } {}
+ do_test capi3-7.2 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {unsupported file format}}
+ db close
+}
+
+if {![sqlite3 -has-codec]} {
+ # Now test that the library correctly handles bogus entries in the
+ # sqlite_master table (schema corruption).
+ do_test capi3-8.1 {
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(a);
+ }
+ db close
+ } {}
+ do_test capi3-8.2 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA writable_schema=ON;
+ INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL);
+ }
+ db close
+ } {}
+ do_test capi3-8.3 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {malformed database schema (?)}}
+ do_test capi3-8.4 {
+ # Build a 5-field row record. The first field is a string 'table', and
+ # subsequent fields are all NULL.
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(a);
+ PRAGMA writable_schema=ON;
+ INSERT INTO sqlite_master VALUES('table',NULL,NULL,NULL,NULL);
+ }
+ db close
+ } {};
+ do_test capi3-8.5 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {malformed database schema (?)}}
+ db close
+}
+file delete -force test.db
+file delete -force test.db-journal
+
+
+# Test the english language string equivalents for sqlite error codes
+set code2english [list \
+SQLITE_OK {not an error} \
+SQLITE_ERROR {SQL logic error or missing database} \
+SQLITE_PERM {access permission denied} \
+SQLITE_ABORT {callback requested query abort} \
+SQLITE_BUSY {database is locked} \
+SQLITE_LOCKED {database table is locked} \
+SQLITE_NOMEM {out of memory} \
+SQLITE_READONLY {attempt to write a readonly database} \
+SQLITE_INTERRUPT {interrupted} \
+SQLITE_IOERR {disk I/O error} \
+SQLITE_CORRUPT {database disk image is malformed} \
+SQLITE_FULL {database or disk is full} \
+SQLITE_CANTOPEN {unable to open database file} \
+SQLITE_EMPTY {table contains no data} \
+SQLITE_SCHEMA {database schema has changed} \
+SQLITE_CONSTRAINT {constraint failed} \
+SQLITE_MISMATCH {datatype mismatch} \
+SQLITE_MISUSE {library routine called out of sequence} \
+SQLITE_NOLFS {large file support is disabled} \
+SQLITE_AUTH {authorization denied} \
+SQLITE_FORMAT {auxiliary database format error} \
+SQLITE_RANGE {bind or column index out of range} \
+SQLITE_NOTADB {file is encrypted or is not a database} \
+unknownerror {unknown error} \
+]
+
+set test_number 1
+foreach {code english} $code2english {
+ do_test capi3-9.$test_number "sqlite3_test_errstr $code" $english
+ incr test_number
+}
+
+# Test the error message when a "real" out of memory occurs.
+ifcapable memdebug {
+ do_test capi3-10-1 {
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_memdebug_fail 1
+ catchsql {
+ select * from sqlite_master;
+ }
+ } {1 {out of memory}}
+ do_test capi3-10-2 {
+ sqlite3_errmsg $::DB
+ } {out of memory}
+ ifcapable {utf16} {
+ do_test capi3-10-3 {
+ utf8 [sqlite3_errmsg16 $::DB]
+ } {out of memory}
+ }
+ db close
+ sqlite3_memdebug_fail -1
+ do_test capi3-10-4 {
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_memdebug_fail 1
+ catchsql {
+ select * from sqlite_master where rowid>5;
+ }
+ } {1 {out of memory}}
+ do_test capi3-10-5 {
+ sqlite3_errmsg $::DB
+ } {out of memory}
+ ifcapable {utf16} {
+ do_test capi3-10-6 {
+ utf8 [sqlite3_errmsg16 $::DB]
+ } {out of memory}
+ }
+ db close
+ sqlite3_memdebug_fail -1
+}
+
+# The following tests - capi3-11.* - test that a COMMIT or ROLLBACK
+# statement issued while there are still outstanding VMs that are part of
+# the transaction fails.
+sqlite3 db test.db
+set DB [sqlite3_connection_pointer db]
+sqlite_register_test_function $DB func
+do_test capi3-11.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 'int');
+ INSERT INTO t1 VALUES(2, 'notatype');
+ }
+} {}
+do_test capi3-11.1.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.2 {
+ set STMT [sqlite3_prepare $DB "SELECT func(b, a) FROM t1" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.3 {
+ catchsql {
+ COMMIT;
+ }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3-11.3.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.4 {
+ sqlite3_step $STMT
+} {SQLITE_ERROR}
+do_test capi3-11.5 {
+ sqlite3_finalize $STMT
+} {SQLITE_ERROR}
+do_test capi3-11.6 {
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {0 {1 int 2 notatype}}
+do_test capi3-11.6.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.7 {
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3-11.7.1 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3-11.8 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ BEGIN;
+ INSERT INTO t2 VALUES(3);
+ }
+} {}
+do_test capi3-11.8.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.9 {
+ set STMT [sqlite3_prepare $DB "SELECT a FROM t2" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.9.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.9.2 {
+ catchsql {
+ ROLLBACK;
+ }
+} {1 {cannot rollback transaction - SQL statements in progress}}
+do_test capi3-11.9.3 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.10 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.11 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.12 {
+ sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3-11.13 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-11.14 {
+ execsql {
+ SELECT a FROM t2;
+ }
+} {1 2 3}
+do_test capi3-11.14.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.15 {
+ catchsql {
+ ROLLBACK;
+ }
+} {0 {}}
+do_test capi3-11.15.1 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3-11.16 {
+ execsql {
+ SELECT a FROM t2;
+ }
+} {1 2}
+
+# Sanity check on the definition of 'outstanding VM'. This means any VM
+# that has had sqlite3_step() called more recently than sqlite3_finalize() or
+# sqlite3_reset(). So a VM that has just been prepared or reset does not
+# count as an active VM.
+do_test capi3-11.17 {
+ execsql {
+ BEGIN;
+ }
+} {}
+do_test capi3-11.18 {
+ set STMT [sqlite3_prepare $DB "SELECT a FROM t1" -1 TAIL]
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3-11.19 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.20 {
+ catchsql {
+ BEGIN;
+ COMMIT;
+ }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3-11.20 {
+ sqlite3_reset $STMT
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3-11.21 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# The following tests - capi3-12.* - check that its Ok to start a
+# transaction while other VMs are active, and that its Ok to execute
+# atomic updates in the same situation
+#
+do_test capi3-12.1 {
+ set STMT [sqlite3_prepare $DB "SELECT a FROM t2" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.2 {
+ catchsql {
+ INSERT INTO t1 VALUES(3, NULL);
+ }
+} {0 {}}
+do_test capi3-12.3 {
+ catchsql {
+ INSERT INTO t2 VALUES(4);
+ }
+} {0 {}}
+do_test capi3-12.4 {
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(4, NULL);
+ }
+} {0 {}}
+do_test capi3-12.5 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.5.1 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.6 {
+ sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3-12.7 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-12.8 {
+ execsql {
+ COMMIT;
+ SELECT a FROM t1;
+ }
+} {1 2 3 4}
+
+# Test cases capi3-13.* test the sqlite3_clear_bindings() and
+# sqlite3_sleep APIs.
+#
+if {[llength [info commands sqlite3_clear_bindings]]>0} {
+ do_test capi3-13.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+ set STMT [sqlite3_prepare $DB "INSERT INTO t1 VALUES(?, ?)" -1 TAIL]
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3-13.2 {
+ sqlite3_reset $STMT
+ sqlite3_bind_text $STMT 1 hello 5
+ sqlite3_bind_text $STMT 2 world 5
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3-13.3 {
+ sqlite3_reset $STMT
+ sqlite3_clear_bindings $STMT
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3-13-4 {
+ sqlite3_finalize $STMT
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {{} {} hello world {} {}}
+}
+if {[llength [info commands sqlite3_sleep]]>0} {
+ do_test capi3-13-5 {
+ set ms [sqlite3_sleep 80]
+ expr {$ms==80 || $ms==1000}
+ } {1}
+}
+
+# Ticket #1219: Make sure binding APIs can handle a NULL pointer.
+#
+do_test capi3-14.1-misuse {
+ set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
+ lappend rc $msg
+} {1 SQLITE_MISUSE}
+
+# Ticket #1650: Honor the nBytes parameter to sqlite3_prepare.
+#
+do_test capi3-15.1 {
+ set sql {SELECT * FROM t2}
+ set nbytes [string length $sql]
+ append sql { WHERE a==1}
+ set STMT [sqlite3_prepare $DB $sql $nbytes TAIL]
+ sqlite3_step $STMT
+ sqlite3_column_int $STMT 0
+} {1}
+do_test capi3-15.2 {
+ sqlite3_step $STMT
+ sqlite3_column_int $STMT 0
+} {2}
+do_test capi3-15.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-15.4 {
+ # 123456789 1234567
+ set sql {SELECT 1234567890}
+ set STMT [sqlite3_prepare $DB $sql 8 TAIL]
+ sqlite3_step $STMT
+ set v1 [sqlite3_column_int $STMT 0]
+ sqlite3_finalize $STMT
+ set v1
+} {1}
+do_test capi3-15.5 {
+ # 123456789 1234567
+ set sql {SELECT 1234567890}
+ set STMT [sqlite3_prepare $DB $sql 9 TAIL]
+ sqlite3_step $STMT
+ set v1 [sqlite3_column_int $STMT 0]
+ sqlite3_finalize $STMT
+ set v1
+} {12}
+do_test capi3-15.6 {
+ # 123456789 1234567
+ set sql {SELECT 1234567890}
+ set STMT [sqlite3_prepare $DB $sql 12 TAIL]
+ sqlite3_step $STMT
+ set v1 [sqlite3_column_int $STMT 0]
+ sqlite3_finalize $STMT
+ set v1
+} {12345}
+do_test capi3-15.7 {
+ # 123456789 1234567
+ set sql {SELECT 12.34567890}
+ set STMT [sqlite3_prepare $DB $sql 12 TAIL]
+ sqlite3_step $STMT
+ set v1 [sqlite3_column_double $STMT 0]
+ sqlite3_finalize $STMT
+ set v1
+} {12.34}
+do_test capi3-15.8 {
+ # 123456789 1234567
+ set sql {SELECT 12.34567890}
+ set STMT [sqlite3_prepare $DB $sql 14 TAIL]
+ sqlite3_step $STMT
+ set v1 [sqlite3_column_double $STMT 0]
+ sqlite3_finalize $STMT
+ set v1
+} {12.3456}
+
+# Make sure code is always generated even if an IF EXISTS or
+# IF NOT EXISTS clause is present that the table does not or
+# does exists. That way we will always have a prepared statement
+# to expire when the schema changes.
+#
+do_test capi3-16.1 {
+ set sql {DROP TABLE IF EXISTS t3}
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT!=""}
+} {1}
+do_test capi3-16.2 {
+ set sql {CREATE TABLE IF NOT EXISTS t1(x,y)}
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT!=""}
+} {1}
+
+# But still we do not generate code if there is no SQL
+#
+do_test capi3-16.3 {
+ set STMT [sqlite3_prepare $DB {} -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT==""}
+} {1}
+do_test capi3-16.4 {
+ set STMT [sqlite3_prepare $DB {;} -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT==""}
+} {1}
+
+# Ticket #2426: Misuse of sqlite3_column_* by calling it after
+# a sqlite3_reset should be harmless.
+#
+do_test capi3-17.1 {
+ set STMT [sqlite3_prepare $DB {SELECT * FROM t2} -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_column_int $STMT 0
+} {1}
+do_test capi3-17.2 {
+ sqlite3_reset $STMT
+ sqlite3_column_int $STMT 0
+} {0}
+do_test capi3-17.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# Verify that sqlite3_step() fails with an SQLITE_SCHEMA error
+# when the statement is prepared with sqlite3_prepare() (not
+# sqlite3_prepare_v2()) and the schema has changed.
+#
+do_test capi3-18.1 {
+ set STMT [sqlite3_prepare db {SELECT * FROM t2} -1 TAIL]
+ sqlite3 db2 test.db
+ db2 eval {CREATE TABLE t3(x)}
+ db2 close
+ sqlite3_step $STMT
+} {SQLITE_ERROR}
+do_test capi3-18.2 {
+ sqlite3_reset $STMT
+ sqlite3_errcode db
+} {SQLITE_SCHEMA}
+do_test capi3-18.3 {
+ sqlite3_errmsg db
+} {database schema has changed}
+# The error persist on retry when sqlite3_prepare() has been used.
+do_test capi3-18.4 {
+ sqlite3_step $STMT
+} {SQLITE_ERROR}
+do_test capi3-18.5 {
+ sqlite3_reset $STMT
+ sqlite3_errcode db
+} {SQLITE_SCHEMA}
+do_test capi3-18.6 {
+ sqlite3_errmsg db
+} {database schema has changed}
+sqlite3_finalize $STMT
+
+# Ticket #3134. Prepare a statement with an nBytes parameter of 0.
+# Make sure this works correctly and does not reference memory out of
+# range.
+#
+do_test capi3-19.1 {
+ sqlite3_prepare_tkt3134 db
+} {}
+
+# Tests of the interface when no VFS is registered.
+#
+if {![info exists tester_do_binarylog]} {
+ db close
+ vfs_unregister_all
+ do_test capi3-20.1 {
+ sqlite3_sleep 100
+ } {0}
+ vfs_reregister_all
+}
+
+finish_test
diff --git a/third_party/sqlite/test/capi3b.test b/third_party/sqlite/test/capi3b.test
new file mode 100755
index 0000000..44790c7
--- /dev/null
+++ b/third_party/sqlite/test/capi3b.test
@@ -0,0 +1,145 @@
+# 2004 September 2
+#
+# 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 testing the callback-free C/C++ API and in
+# particular the behavior of sqlite3_step() when trying to commit
+# with lock contention.
+#
+# $Id: capi3b.test,v 1.4 2007/08/10 19:46:14 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# These tests depend on the pager holding changes in cache
+# until it is time to commit. But that won't happen if the
+# soft-heap-limit is set too low. So disable the soft heap limit
+# for the duration of this test.
+#
+sqlite3_soft_heap_limit 0
+
+
+set DB [sqlite3_connection_pointer db]
+sqlite3 db2 test.db
+set DB2 [sqlite3_connection_pointer db2]
+
+# Create some data in the database
+#
+do_test capi3b-1.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ SELECT * FROM t1
+ }
+} {1 2}
+
+# Make sure the second database connection can see the data
+#
+do_test capi3b-1.2 {
+ execsql {
+ SELECT * FROM t1
+ } db2
+} {1 2}
+
+# First database connection acquires a shared lock
+#
+do_test capi3b-1.3 {
+ execsql {
+ BEGIN;
+ SELECT * FROM t1;
+ }
+} {1 2}
+
+# Second database connection tries to write. The sqlite3_step()
+# function returns SQLITE_BUSY because it cannot commit.
+#
+do_test capi3b-1.4 {
+ set VM [sqlite3_prepare $DB2 {INSERT INTO t1 VALUES(3)} -1 TAIL]
+ sqlite3_step $VM
+} SQLITE_BUSY
+
+# The sqlite3_step call can be repeated multiple times.
+#
+do_test capi3b-1.5.1 {
+ sqlite3_step $VM
+} SQLITE_BUSY
+do_test capi3b-1.5.2 {
+ sqlite3_step $VM
+} SQLITE_BUSY
+
+# The first connection closes its transaction. This allows the second
+# connections sqlite3_step to succeed.
+#
+do_test capi3b-1.6 {
+ execsql COMMIT
+ sqlite3_step $VM
+} SQLITE_DONE
+do_test capi3b-1.7 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+do_test capi3b-1.8 {
+ execsql {SELECT * FROM t1} db2
+} {1 2 3}
+do_test capi3b-1.9 {
+ execsql {SELECT * FROM t1}
+} {1 2 3}
+
+# Start doing a SELECT with one connection. This gets a SHARED lock.
+# Then do an INSERT with the other connection. The INSERT should
+# not be able to complete until the SELECT finishes.
+#
+do_test capi3b-2.1 {
+ set VM1 [sqlite3_prepare $DB {SELECT * FROM t1} -1 TAIL]
+ sqlite3_step $VM1
+} SQLITE_ROW
+do_test capi3b-2.2 {
+ sqlite3_column_text $VM1 0
+} 1
+do_test capi3b-2.3 {
+ set VM2 [sqlite3_prepare $DB2 {INSERT INTO t1 VALUES(4)} -1 TAIL]
+ sqlite3_step $VM2
+} SQLITE_BUSY
+do_test capi3b-2.4 {
+ sqlite3_step $VM1
+} SQLITE_ROW
+do_test capi3b-2.5 {
+ sqlite3_column_text $VM1 0
+} 2
+do_test capi3b-2.6 {
+ sqlite3_step $VM2
+} SQLITE_BUSY
+do_test capi3b-2.7 {
+ sqlite3_step $VM1
+} SQLITE_ROW
+do_test capi3b-2.8 {
+ sqlite3_column_text $VM1 0
+} 3
+do_test capi3b-2.9 {
+ sqlite3_step $VM2
+} SQLITE_BUSY
+do_test capi3b-2.10 {
+ sqlite3_step $VM1
+} SQLITE_DONE
+do_test capi3b-2.11 {
+ sqlite3_step $VM2
+} SQLITE_DONE
+do_test capi3b-2.12 {
+ sqlite3_finalize $VM1
+ sqlite3_finalize $VM2
+ execsql {SELECT * FROM t1}
+} {1 2 3 4}
+
+catch {db2 close}
+
+sqlite3_soft_heap_limit $soft_limit
+finish_test
diff --git a/third_party/sqlite/test/capi3c.test b/third_party/sqlite/test/capi3c.test
new file mode 100755
index 0000000..cf1a6a9
--- /dev/null
+++ b/third_party/sqlite/test/capi3c.test
@@ -0,0 +1,1301 @@
+# 2006 November 08
+#
+# 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.
+#
+# This is a copy of the capi3.test file that has been adapted to
+# test the new sqlite3_prepare_v2 interface.
+#
+# $Id: capi3c.test,v 1.19 2008/05/05 16:56:35 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+proc utf16 {str {nt 1}} {
+ set r [encoding convertto unicode $str]
+ if {$nt} {
+ append r "\x00\x00"
+ }
+ return $r
+}
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str.
+proc utf8 {str} {
+ # If $str ends in two 0x00 0x00 bytes, knock these off before
+ # converting to UTF-8 using TCL.
+ binary scan $str \c* vals
+ if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+ set str [binary format \c* [lrange $vals 0 end-2]]
+ }
+
+ set r [encoding convertfrom unicode $str]
+ return $r
+}
+
+# These tests complement those in capi2.test. They are organized
+# as follows:
+#
+# capi3c-1.*: Test sqlite3_prepare_v2
+# capi3c-2.*: Test sqlite3_prepare16_v2
+# capi3c-3.*: Test sqlite3_open
+# capi3c-4.*: Test sqlite3_open16
+# capi3c-5.*: Test the various sqlite3_result_* APIs
+# capi3c-6.*: Test that sqlite3_close fails if there are outstanding VMs.
+#
+
+set DB [sqlite3_connection_pointer db]
+
+do_test capi3c-1.0 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3c-1.1 {
+ set STMT [sqlite3_prepare_v2 $DB {SELECT name FROM sqlite_master} -1 TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {}
+do_test capi3c-1.2 {
+ sqlite3_errcode $DB
+} {SQLITE_OK}
+do_test capi3c-1.3 {
+ sqlite3_errmsg $DB
+} {not an error}
+do_test capi3c-1.4 {
+ set sql {SELECT name FROM sqlite_master;SELECT 10}
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ set TAIL
+} {SELECT 10}
+do_test capi3c-1.5 {
+ set sql {SELECT namex FROM sqlite_master}
+ catch {
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ }
+} {1}
+do_test capi3c-1.6 {
+ sqlite3_errcode $DB
+} {SQLITE_ERROR}
+do_test capi3c-1.7 {
+ sqlite3_errmsg $DB
+} {no such column: namex}
+
+
+ifcapable {utf16} {
+ do_test capi3c-2.1 {
+ set sql16 [utf16 {SELECT name FROM sqlite_master}]
+ set STMT [sqlite3_prepare16_v2 $DB $sql16 -1 ::TAIL]
+ sqlite3_finalize $STMT
+ utf8 $::TAIL
+ } {}
+ do_test capi3c-2.2 {
+ set sql [utf16 {SELECT name FROM sqlite_master;SELECT 10}]
+ set STMT [sqlite3_prepare16_v2 $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ utf8 $TAIL
+ } {SELECT 10}
+ do_test capi3c-2.3 {
+ set sql [utf16 {SELECT namex FROM sqlite_master}]
+ catch {
+ set STMT [sqlite3_prepare16_v2 $DB $sql -1 TAIL]
+ }
+ } {1}
+ do_test capi3c-2.4 {
+ sqlite3_errcode $DB
+ } {SQLITE_ERROR}
+ do_test capi3c-2.5 {
+ sqlite3_errmsg $DB
+ } {no such column: namex}
+
+ ifcapable schema_pragmas {
+ do_test capi3c-2.6 {
+ execsql {CREATE TABLE tablename(x)}
+ set sql16 [utf16 {PRAGMA table_info("TableName")}]
+ set STMT [sqlite3_prepare16_v2 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ } SQLITE_ROW
+ do_test capi3c-2.7 {
+ sqlite3_step $STMT
+ } SQLITE_DONE
+ do_test capi3c-2.8 {
+ sqlite3_finalize $STMT
+ } SQLITE_OK
+ }
+
+} ;# endif utf16
+
+# rename sqlite3_open sqlite3_open_old
+# proc sqlite3_open {fname options} {sqlite3_open_new $fname $options}
+
+do_test capi3c-3.1 {
+ set db2 [sqlite3_open test.db {}]
+ sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3c-3.2 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3c-3.3 {
+ catch {
+ set db2 [sqlite3_open /bogus/path/test.db {}]
+ }
+ sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3c-3.4 {
+ sqlite3_errmsg $db2
+} {unable to open database file}
+do_test capi3c-3.5 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3c-3.6.1-misuse {
+ sqlite3_close $db2
+} {SQLITE_MISUSE}
+do_test capi3c-3.6.2-misuse {
+ sqlite3_errmsg $db2
+} {library routine called out of sequence}
+ifcapable {utf16} {
+ do_test capi3c-3.6.3-misuse {
+ utf8 [sqlite3_errmsg16 $db2]
+ } {library routine called out of sequence}
+}
+
+# rename sqlite3_open ""
+# rename sqlite3_open_old sqlite3_open
+
+ifcapable {utf16} {
+do_test capi3c-4.1 {
+ set db2 [sqlite3_open16 [utf16 test.db] {}]
+ sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3c-4.2 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3c-4.3 {
+ catch {
+ set db2 [sqlite3_open16 [utf16 /bogus/path/test.db] {}]
+ }
+ sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3c-4.4 {
+ utf8 [sqlite3_errmsg16 $db2]
+} {unable to open database file}
+do_test capi3c-4.5 {
+ sqlite3_close $db2
+} {SQLITE_OK}
+} ;# utf16
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_count
+# sqlite3_column_name
+# sqlite3_column_name16
+# sqlite3_column_decltype
+# sqlite3_column_decltype16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_header {STMT test names decltypes} {
+
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set ::numcols [sqlite3_column_count $STMT]
+ for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+
+ # Column names in UTF-8
+ do_test $test.1 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]}
+ set cnamelist
+ } $names
+
+ # Column names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.2 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $names
+ }
+
+ # Column names in UTF-8
+ do_test $test.3 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]}
+ set cnamelist
+ } $names
+
+ # Column names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.4 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $names
+ }
+
+ # Column names in UTF-8
+ do_test $test.5 {
+ set cnamelist [list]
+ foreach i $idxlist {lappend cnamelist [sqlite3_column_decltype $STMT $i]}
+ set cnamelist
+ } $decltypes
+
+ # Column declaration types in UTF-16
+ ifcapable {utf16} {
+ do_test $test.6 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_decltype16 $STMT $i]]
+ }
+ set cnamelist
+ } $decltypes
+ }
+
+
+ # Test some out of range conditions:
+ ifcapable {utf16} {
+ do_test $test.7 {
+ list \
+ [sqlite3_column_name $STMT -1] \
+ [sqlite3_column_name16 $STMT -1] \
+ [sqlite3_column_decltype $STMT -1] \
+ [sqlite3_column_decltype16 $STMT -1] \
+ [sqlite3_column_name $STMT $numcols] \
+ [sqlite3_column_name16 $STMT $numcols] \
+ [sqlite3_column_decltype $STMT $numcols] \
+ [sqlite3_column_decltype16 $STMT $numcols]
+ } {{} {} {} {} {} {} {} {}}
+ }
+}
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_origin_name
+# sqlite3_column_origin_name16
+# sqlite3_column_table_name
+# sqlite3_column_table_name16
+# sqlite3_column_database_name
+# sqlite3_column_database_name16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_origin_header {STMT test dbs tables cols} {
+ # If sqlite3_column_origin_name() and friends are not compiled into
+ # this build, this proc is a no-op.
+ifcapable columnmetadata {
+
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set ::numcols [sqlite3_column_count $STMT]
+ for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+
+ # Database names in UTF-8
+ do_test $test.8 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_database_name $STMT $i]
+ }
+ set cnamelist
+ } $dbs
+
+ # Database names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.9 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_database_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $dbs
+ }
+
+ # Table names in UTF-8
+ do_test $test.10 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_table_name $STMT $i]
+ }
+ set cnamelist
+ } $tables
+
+ # Table names in UTF-16
+ ifcapable {utf16} {
+ do_test $test.11 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_table_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $tables
+ }
+
+ # Origin names in UTF-8
+ do_test $test.12 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [sqlite3_column_origin_name $STMT $i]
+ }
+ set cnamelist
+ } $cols
+
+ # Origin declaration types in UTF-16
+ ifcapable {utf16} {
+ do_test $test.13 {
+ set cnamelist [list]
+ foreach i $idxlist {
+ lappend cnamelist [utf8 [sqlite3_column_origin_name16 $STMT $i]]
+ }
+ set cnamelist
+ } $cols
+ }
+ }
+}
+
+# This proc is used to test the following APIs:
+#
+# sqlite3_data_count
+# sqlite3_column_type
+# sqlite3_column_int
+# sqlite3_column_text
+# sqlite3_column_text16
+# sqlite3_column_double
+#
+# $STMT is a compiled SQL statement for which the previous call
+# to sqlite3_step returned SQLITE_ROW. $test is a prefix to use
+# for test names within this proc. $types is a list of the
+# manifest types for the current row. $ints, $doubles and $strings
+# are lists of the integer, real and string representations of
+# the values in the current row.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 'hello', 1.1, NULL" -1 DUMMY]
+# sqlite3_step $STMT
+# check_data test1.2 {TEXT REAL NULL} {0 1 0} {0 1.1 0} {hello 1.1 {}}
+#
+proc check_data {STMT test types ints doubles strings} {
+
+ # Use the return value of sqlite3_column_count() to build
+ # a list of column indexes. i.e. If sqlite3_column_count
+ # is 3, build the list {0 1 2}.
+ set ::idxlist [list]
+ set numcols [sqlite3_data_count $STMT]
+ for {set i 0} {$i < $numcols} {incr i} {lappend ::idxlist $i}
+
+# types
+do_test $test.1 {
+ set types [list]
+ foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+ set types
+} $types
+
+# Integers
+do_test $test.2 {
+ set ints [list]
+ foreach i $idxlist {lappend ints [sqlite3_column_int64 $STMT $i]}
+ set ints
+} $ints
+
+# bytes
+set lens [list]
+foreach i $::idxlist {
+ lappend lens [string length [lindex $strings $i]]
+}
+do_test $test.3 {
+ set bytes [list]
+ set lens [list]
+ foreach i $idxlist {
+ lappend bytes [sqlite3_column_bytes $STMT $i]
+ }
+ set bytes
+} $lens
+
+# bytes16
+ifcapable {utf16} {
+ set lens [list]
+ foreach i $::idxlist {
+ lappend lens [expr 2 * [string length [lindex $strings $i]]]
+ }
+ do_test $test.4 {
+ set bytes [list]
+ set lens [list]
+ foreach i $idxlist {
+ lappend bytes [sqlite3_column_bytes16 $STMT $i]
+ }
+ set bytes
+ } $lens
+}
+
+# Blob
+do_test $test.5 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_blob $STMT $i]}
+ set utf8
+} $strings
+
+# UTF-8
+do_test $test.6 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+ set utf8
+} $strings
+
+# Floats
+do_test $test.7 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+ set utf8
+} $doubles
+
+# UTF-16
+ifcapable {utf16} {
+ do_test $test.8 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [utf8 [sqlite3_column_text16 $STMT $i]]}
+ set utf8
+ } $strings
+}
+
+# Integers
+do_test $test.9 {
+ set ints [list]
+ foreach i $idxlist {lappend ints [sqlite3_column_int $STMT $i]}
+ set ints
+} $ints
+
+# Floats
+do_test $test.10 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+ set utf8
+} $doubles
+
+# UTF-8
+do_test $test.11 {
+ set utf8 [list]
+ foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+ set utf8
+} $strings
+
+# Types
+do_test $test.12 {
+ set types [list]
+ foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+ set types
+} $types
+
+# Test that an out of range request returns the equivalent of NULL
+do_test $test.13 {
+ sqlite3_column_int $STMT -1
+} {0}
+do_test $test.13 {
+ sqlite3_column_text $STMT -1
+} {}
+
+}
+
+ifcapable !floatingpoint {
+ finish_test
+ return
+}
+
+do_test capi3c-5.0 {
+ execsql {
+ CREATE TABLE t1(a VARINT, b BLOB, c VARCHAR(16));
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES('one', 'two', NULL);
+ INSERT INTO t1 VALUES(1.2, 1.3, 1.4);
+ }
+ set sql "SELECT * FROM t1"
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3c-5.1 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3c-5.1 {main main main} {t1 t1 t1} {a b c}
+do_test capi3c-5.2 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3c-5.3 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3c-5.3 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3c-5.4 {INTEGER INTEGER TEXT} {1 2 3} {1.0 2.0 3.0} {1 2 3}
+
+do_test capi3c-5.5 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3c-5.6 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3c-5.6 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3c-5.7 {TEXT TEXT NULL} {0 0 0} {0.0 0.0 0.0} {one two {}}
+
+do_test capi3c-5.8 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3c-5.9 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3c-5.9 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3c-5.10 {FLOAT FLOAT TEXT} {1 1 1} {1.2 1.3 1.4} {1.2 1.3 1.4}
+
+do_test capi3c-5.11 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+
+do_test capi3c-5.12 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+do_test capi3c-5.20 {
+ set sql "SELECT a, sum(b), max(c) FROM t1 GROUP BY a"
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3c-5.21 {a sum(b) max(c)} {VARINT {} {}}
+check_origin_header $STMT capi3c-5.22 {main {} {}} {t1 {} {}} {a {} {}}
+do_test capi3c-5.23 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+
+set ::ENC [execsql {pragma encoding}]
+db close
+
+do_test capi3c-6.0 {
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_key $DB xyzzy
+ set sql {SELECT a FROM t1 order by rowid}
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ expr 0
+} {0}
+do_test capi3c-6.1 {
+ db cache flush
+ sqlite3_close $DB
+} {SQLITE_BUSY}
+do_test capi3c-6.2 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+check_data $STMT capi3c-6.3 {INTEGER} {1} {1.0} {1}
+do_test capi3c-6.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3c-6.4 {
+ db cache flush
+ sqlite3_close $DB
+} {SQLITE_OK}
+do_test capi3c-6.99-misuse {
+ db close
+} {}
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+if {![sqlite3 -has-codec]} {
+ # Test what happens when the library encounters a newer file format.
+ do_test capi3c-7.1 {
+ set_file_format 5
+ } {}
+ do_test capi3c-7.2 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {unsupported file format}}
+ db close
+}
+
+if {![sqlite3 -has-codec]} {
+ # Now test that the library correctly handles bogus entries in the
+ # sqlite_master table (schema corruption).
+ do_test capi3c-8.1 {
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(a);
+ }
+ db close
+ } {}
+ do_test capi3c-8.2 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA writable_schema=ON;
+ INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL);
+ }
+ db close
+ } {}
+ do_test capi3c-8.3 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {malformed database schema (?)}}
+ do_test capi3c-8.4 {
+ # Build a 5-field row record. The first field is a string 'table', and
+ # subsequent fields are all NULL.
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(a);
+ PRAGMA writable_schema=ON;
+ INSERT INTO sqlite_master VALUES('table',NULL,NULL,NULL,NULL);
+ }
+ db close
+ } {};
+ do_test capi3c-8.5 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ }
+ } {1 {malformed database schema (?)}}
+ db close
+}
+file delete -force test.db
+file delete -force test.db-journal
+
+
+# Test the english language string equivalents for sqlite error codes
+set code2english [list \
+SQLITE_OK {not an error} \
+SQLITE_ERROR {SQL logic error or missing database} \
+SQLITE_PERM {access permission denied} \
+SQLITE_ABORT {callback requested query abort} \
+SQLITE_BUSY {database is locked} \
+SQLITE_LOCKED {database table is locked} \
+SQLITE_NOMEM {out of memory} \
+SQLITE_READONLY {attempt to write a readonly database} \
+SQLITE_INTERRUPT {interrupted} \
+SQLITE_IOERR {disk I/O error} \
+SQLITE_CORRUPT {database disk image is malformed} \
+SQLITE_FULL {database or disk is full} \
+SQLITE_CANTOPEN {unable to open database file} \
+SQLITE_EMPTY {table contains no data} \
+SQLITE_SCHEMA {database schema has changed} \
+SQLITE_CONSTRAINT {constraint failed} \
+SQLITE_MISMATCH {datatype mismatch} \
+SQLITE_MISUSE {library routine called out of sequence} \
+SQLITE_NOLFS {large file support is disabled} \
+SQLITE_AUTH {authorization denied} \
+SQLITE_FORMAT {auxiliary database format error} \
+SQLITE_RANGE {bind or column index out of range} \
+SQLITE_NOTADB {file is encrypted or is not a database} \
+unknownerror {unknown error} \
+]
+
+set test_number 1
+foreach {code english} $code2english {
+ do_test capi3c-9.$test_number "sqlite3_test_errstr $code" $english
+ incr test_number
+}
+
+# Test the error message when a "real" out of memory occurs.
+ifcapable memdebug {
+ do_test capi3c-10-1 {
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_memdebug_fail 0
+ catchsql {
+ select * from sqlite_master;
+ }
+ } {1 {out of memory}}
+ do_test capi3c-10-2 {
+ sqlite3_errmsg $::DB
+ } {out of memory}
+ ifcapable {utf16} {
+ do_test capi3c-10-3 {
+ utf8 [sqlite3_errmsg16 $::DB]
+ } {out of memory}
+ }
+ db close
+ sqlite3_memdebug_fail -1
+}
+
+# The following tests - capi3c-11.* - test that a COMMIT or ROLLBACK
+# statement issued while there are still outstanding VMs that are part of
+# the transaction fails.
+sqlite3 db test.db
+set DB [sqlite3_connection_pointer db]
+sqlite_register_test_function $DB func
+do_test capi3c-11.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 'int');
+ INSERT INTO t1 VALUES(2, 'notatype');
+ }
+} {}
+do_test capi3c-11.1.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.2 {
+ set STMT [sqlite3_prepare_v2 $DB "SELECT func(b, a) FROM t1" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-11.3 {
+ catchsql {
+ COMMIT;
+ }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3c-11.3.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.4 {
+ sqlite3_step $STMT
+} {SQLITE_ERROR}
+do_test capi3c-11.5 {
+ sqlite3_finalize $STMT
+} {SQLITE_ERROR}
+do_test capi3c-11.6 {
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {0 {1 int 2 notatype}}
+do_test capi3c-11.6.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.7 {
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3c-11.7.1 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3c-11.8 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ BEGIN;
+ INSERT INTO t2 VALUES(3);
+ }
+} {}
+do_test capi3c-11.8.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.9 {
+ set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t2" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-11.9.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.9.2 {
+ catchsql {
+ ROLLBACK;
+ }
+} {1 {cannot rollback transaction - SQL statements in progress}}
+do_test capi3c-11.9.3 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.10 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-11.11 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-11.12 {
+ sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3c-11.13 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3c-11.14 {
+ execsql {
+ SELECT a FROM t2;
+ }
+} {1 2 3}
+do_test capi3c-11.14.1 {
+ sqlite3_get_autocommit $DB
+} 0
+do_test capi3c-11.15 {
+ catchsql {
+ ROLLBACK;
+ }
+} {0 {}}
+do_test capi3c-11.15.1 {
+ sqlite3_get_autocommit $DB
+} 1
+do_test capi3c-11.16 {
+ execsql {
+ SELECT a FROM t2;
+ }
+} {1 2}
+
+# Sanity check on the definition of 'outstanding VM'. This means any VM
+# that has had sqlite3_step() called more recently than sqlite3_finalize() or
+# sqlite3_reset(). So a VM that has just been prepared or reset does not
+# count as an active VM.
+do_test capi3c-11.17 {
+ execsql {
+ BEGIN;
+ }
+} {}
+do_test capi3c-11.18 {
+ set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t1" -1 TAIL]
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3c-11.19 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-11.20 {
+ catchsql {
+ BEGIN;
+ COMMIT;
+ }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3c-11.20 {
+ sqlite3_reset $STMT
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test capi3c-11.21 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# The following tests - capi3c-12.* - check that its Ok to start a
+# transaction while other VMs are active, and that its Ok to execute
+# atomic updates in the same situation
+#
+do_test capi3c-12.1 {
+ set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t2" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-12.2 {
+ catchsql {
+ INSERT INTO t1 VALUES(3, NULL);
+ }
+} {0 {}}
+do_test capi3c-12.3 {
+ catchsql {
+ INSERT INTO t2 VALUES(4);
+ }
+} {0 {}}
+do_test capi3c-12.4 {
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(4, NULL);
+ }
+} {0 {}}
+do_test capi3c-12.5 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-12.5.1 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-12.6 {
+ sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3c-12.7 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3c-12.8 {
+ execsql {
+ COMMIT;
+ SELECT a FROM t1;
+ }
+} {1 2 3 4}
+
+# Test cases capi3c-13.* test the sqlite3_clear_bindings() and
+# sqlite3_sleep APIs.
+#
+if {[llength [info commands sqlite3_clear_bindings]]>0} {
+ do_test capi3c-13.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+ set STMT [sqlite3_prepare_v2 $DB "INSERT INTO t1 VALUES(?, ?)" -1 TAIL]
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3c-13.2 {
+ sqlite3_reset $STMT
+ sqlite3_bind_text $STMT 1 hello 5
+ sqlite3_bind_text $STMT 2 world 5
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3c-13.3 {
+ sqlite3_reset $STMT
+ sqlite3_clear_bindings $STMT
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test capi3c-13-4 {
+ sqlite3_finalize $STMT
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {{} {} hello world {} {}}
+}
+if {[llength [info commands sqlite3_sleep]]>0} {
+ do_test capi3c-13-5 {
+ set ms [sqlite3_sleep 80]
+ expr {$ms==80 || $ms==1000}
+ } {1}
+}
+
+# Ticket #1219: Make sure binding APIs can handle a NULL pointer.
+#
+do_test capi3c-14.1 {
+ set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
+ lappend rc $msg
+} {1 SQLITE_MISUSE}
+
+# Ticket #1650: Honor the nBytes parameter to sqlite3_prepare.
+#
+do_test capi3c-15.1 {
+ set sql {SELECT * FROM t2}
+ set nbytes [string length $sql]
+ append sql { WHERE a==1}
+ set STMT [sqlite3_prepare_v2 $DB $sql $nbytes TAIL]
+ sqlite3_step $STMT
+ sqlite3_column_int $STMT 0
+} {1}
+do_test capi3c-15.2 {
+ sqlite3_step $STMT
+ sqlite3_column_int $STMT 0
+} {2}
+do_test capi3c-15.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# Make sure code is always generated even if an IF EXISTS or
+# IF NOT EXISTS clause is present that the table does not or
+# does exists. That way we will always have a prepared statement
+# to expire when the schema changes.
+#
+do_test capi3c-16.1 {
+ set sql {DROP TABLE IF EXISTS t3}
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT!=""}
+} {1}
+do_test capi3c-16.2 {
+ set sql {CREATE TABLE IF NOT EXISTS t1(x,y)}
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT!=""}
+} {1}
+
+# But still we do not generate code if there is no SQL
+#
+do_test capi3c-16.3 {
+ set STMT [sqlite3_prepare_v2 $DB {} -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT==""}
+} {1}
+do_test capi3c-16.4 {
+ set STMT [sqlite3_prepare_v2 $DB {;} -1 TAIL]
+ sqlite3_finalize $STMT
+ expr {$STMT==""}
+} {1}
+
+# Ticket #2154.
+#
+do_test capi3c-17.1 {
+ set STMT [sqlite3_prepare_v2 $DB {SELECT max(a) FROM t2} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.2 {
+ sqlite3_column_int $STMT 0
+} 4
+do_test capi3c-17.3 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.4 {
+ sqlite3_reset $STMT
+ db eval {CREATE INDEX i2 ON t2(a)}
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.5 {
+ sqlite3_column_int $STMT 0
+} 4
+do_test capi3c-17.6 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.7 {
+ sqlite3_reset $STMT
+ db eval {DROP INDEX i2}
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.8 {
+ sqlite3_column_int $STMT 0
+} 4
+do_test capi3c-17.9 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.10 {
+ sqlite3_finalize $STMT
+ set STMT [sqlite3_prepare_v2 $DB {SELECT b FROM t1 WHERE a=?} -1 TAIL]
+ sqlite3_bind_int $STMT 1 2
+ db eval {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,'one');
+ INSERT INTO t1 VALUES(2,'two');
+ INSERT INTO t1 VALUES(3,'three');
+ INSERT INTO t1 VALUES(4,'four');
+ }
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.11 {
+ sqlite3_column_text $STMT 0
+} two
+do_test capi3c-17.12 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.13 {
+ sqlite3_reset $STMT
+ db eval {CREATE INDEX i1 ON t1(a)}
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.14 {
+ sqlite3_column_text $STMT 0
+} two
+do_test capi3c-17.15 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.16 {
+ sqlite3_reset $STMT
+ db eval {DROP INDEX i1}
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-17.17 {
+ sqlite3_column_text $STMT 0
+} two
+do_test capi3c-17.18 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-17.99 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+# On the mailing list it has been reported that finalizing after
+# an SQLITE_BUSY return leads to a segfault. Here we test that case.
+#
+do_test capi3c-18.1 {
+ sqlite3 db2 test.db
+ set STMT [sqlite3_prepare_v2 $DB {SELECT max(a) FROM t1} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-18.2 {
+ sqlite3_column_int $STMT 0
+} 4
+do_test capi3c-18.3 {
+ sqlite3_reset $STMT
+ db2 eval {BEGIN EXCLUSIVE}
+ sqlite3_step $STMT
+} SQLITE_BUSY
+do_test capi3c-18.4 {
+ sqlite3_finalize $STMT
+} SQLITE_BUSY
+do_test capi3c-18.5 {
+ db2 eval {COMMIT}
+ db2 close
+} {}
+
+# Ticket #2158. The sqlite3_step() will still return SQLITE_SCHEMA
+# if the database schema changes in a way that makes the statement
+# no longer valid.
+#
+do_test capi3c-19.1 {
+ db eval {
+ CREATE TABLE t3(x,y);
+ INSERT INTO t3 VALUES(1,2);
+ }
+ set STMT [sqlite3_prepare_v2 $DB {SELECT * FROM t3} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-19.2 {
+ sqlite3_column_int $STMT 0
+} 1
+do_test capi3c-19.3 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-19.4 {
+ sqlite3_reset $STMT
+ db eval {DROP TABLE t3}
+ sqlite3_step $STMT
+} SQLITE_SCHEMA
+do_test capi3c-19.4.1 {
+ sqlite3_errmsg $DB
+} {no such table: t3}
+do_test capi3c-19.4.2 {
+ sqlite3_expired $STMT
+} 1
+do_test capi3c-19.4.3 {
+ sqlite3_errmsg $DB
+} {no such table: t3}
+do_test capi3c-19.4.4 {
+ sqlite3_expired 0
+} 1
+do_test capi3c-19.5 {
+ sqlite3_reset $STMT
+ db eval {
+ CREATE TABLE t3(x,y);
+ INSERT INTO t3 VALUES(1,2);
+ }
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-19.5.2 {
+ sqlite3_expired $STMT
+} 0
+do_test capi3c-19.6 {
+ sqlite3_column_int $STMT 1
+} 2
+do_test capi3c-19.99 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+# Make sure a change in a separate database connection does not
+# cause an SQLITE_SCHEMA return.
+#
+do_test capi3c-20.1 {
+ set STMT [sqlite3_prepare_v2 $DB {SELECT * FROM t3} -1 TAIL]
+ sqlite3 db2 test.db
+ db2 eval {CREATE TABLE t4(x)}
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test capi3c-20.2 {
+ sqlite3_column_int $STMT 1
+} 2
+do_test capi3c-20.3 {
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test capi3c-20.4 {
+ db2 close
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+# Test that sqlite3_step() sets the database error code correctly.
+# See ticket #2497.
+#
+ifcapable progress {
+ do_test capi3c-21.1 {
+ set STMT [sqlite3_prepare_v2 $DB {SELECT * FROM t3} -1 TAIL]
+ db progress 5 "expr 1"
+ sqlite3_step $STMT
+ } {SQLITE_INTERRUPT}
+ do_test capi3c-21.2 {
+ sqlite3_errcode $DB
+ } {SQLITE_INTERRUPT}
+ do_test capi3c-21.3 {
+ sqlite3_finalize $STMT
+ } {SQLITE_INTERRUPT}
+ do_test capi3c-21.4 {
+ set STMT [sqlite3_prepare $DB {SELECT * FROM t3} -1 TAIL]
+ db progress 5 "expr 1"
+ sqlite3_step $STMT
+ } {SQLITE_ERROR}
+ do_test capi3c-21.5 {
+ sqlite3_errcode $DB
+ } {SQLITE_ERROR}
+ do_test capi3c-21.6 {
+ sqlite3_finalize $STMT
+ } {SQLITE_INTERRUPT}
+ do_test capi3c-21.7 {
+ sqlite3_errcode $DB
+ } {SQLITE_INTERRUPT}
+}
+
+# Make sure sqlite3_result_error_code() returns the correct error code.
+# See ticket #2940
+#
+do_test capi3c-22.1 {
+ db progress 0 {}
+ set STMT [sqlite3_prepare_v2 db {SELECT test_error('the message',3)} -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_PERM}
+sqlite3_finalize $STMT
+do_test capi3c-22.2 {
+ set STMT [sqlite3_prepare_v2 db {SELECT test_error('the message',4)} -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ABORT}
+sqlite3_finalize $STMT
+do_test capi3c-22.3 {
+ set STMT [sqlite3_prepare_v2 db {SELECT test_error('the message',16)} -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_EMPTY}
+sqlite3_finalize $STMT
+
+# For a multi-column result set where the same table column is repeated
+# in multiple columns of the output, verify that doing a UTF-8 to UTF-16
+# conversion (or vice versa) on one column does not change the value of
+# the second.
+#
+do_test capi3c-23.1 {
+ set STMT [sqlite3_prepare_v2 db {SELECT b,b,b,b FROM t1} -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-23.2 {
+ sqlite3_column_text16 $STMT 0
+ sqlite3_column_text $STMT 1
+} {one}
+do_test capi3c-23.3 {
+ sqlite3_column_text16 $STMT 2
+ sqlite3_column_text $STMT 3
+} {one}
+sqlite3_finalize $STMT
+do_test capi3c-23.4 {
+ set STMT [sqlite3_prepare_v2 db {SELECT b||'x',b,b,b FROM t1} -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3c-23.5 {
+ sqlite3_column_text16 $STMT 0
+ sqlite3_column_text $STMT 1
+} {one}
+do_test capi3c-23.6 {
+ sqlite3_column_text16 $STMT 2
+ sqlite3_column_text $STMT 3
+} {one}
+sqlite3_finalize $STMT
+
+finish_test
diff --git a/third_party/sqlite/test/capi3d.test b/third_party/sqlite/test/capi3d.test
new file mode 100755
index 0000000..e346029
--- /dev/null
+++ b/third_party/sqlite/test/capi3d.test
@@ -0,0 +1,93 @@
+# 2008 June 18
+#
+# 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.
+#
+# This file is devoted to testing the sqlite3_next_stmt interface.
+#
+# $Id: capi3d.test,v 1.2 2008/07/14 15:11:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create N prepared statements against database connection db
+# and return a list of all the generated prepared statements.
+#
+proc make_prepared_statements {N} {
+ set plist {}
+ for {set i 0} {$i<$N} {incr i} {
+ set sql "SELECT $i FROM sqlite_master WHERE name LIKE '%$i%'"
+ if {rand()<0.33} {
+ set s [sqlite3_prepare_v2 db $sql -1 notused]
+ } else {
+ ifcapable utf16 {
+ if {rand()<0.5} {
+ set sql [encoding convertto unicode $sql]\x00\x00
+ set s [sqlite3_prepare16 db $sql -1 notused]
+ } else {
+ set s [sqlite3_prepare db $sql -1 notused]
+ }
+ }
+ ifcapable !utf16 {
+ set s [sqlite3_prepare db $sql -1 notused]
+ }
+ }
+ lappend plist $s
+ }
+ return $plist
+}
+
+
+# Scramble the $inlist into a random order.
+#
+proc scramble {inlist} {
+ set y {}
+ foreach x $inlist {
+ lappend y [list [expr {rand()}] $x]
+ }
+ set y [lsort $y]
+ set outlist {}
+ foreach x $y {
+ lappend outlist [lindex $x 1]
+ }
+ return $outlist
+}
+
+# Database initially has no prepared statements.
+#
+do_test capi3d-1.1 {
+ db cache flush
+ sqlite3_next_stmt db 0
+} {}
+
+# Run the following tests for between 1 and 100 prepared statements.
+#
+for {set i 1} {$i<=100} {incr i} {
+ set stmtlist [make_prepared_statements $i]
+ do_test capi3d-1.2.$i.1 {
+ set p [sqlite3_next_stmt db 0]
+ set x {}
+ while {$p!=""} {
+ lappend x $p
+ set p [sqlite3_next_stmt db $p]
+ }
+ lsort $x
+ } [lsort $stmtlist]
+ do_test capi3-1.2.$i.2 {
+ foreach p [scramble $::stmtlist] {
+ sqlite3_finalize $p
+ }
+ sqlite3_next_stmt db 0
+ } {}
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/cast.test b/third_party/sqlite/test/cast.test
new file mode 100755
index 0000000..10db5f8
--- /dev/null
+++ b/third_party/sqlite/test/cast.test
@@ -0,0 +1,322 @@
+# 2005 June 25
+#
+# 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 file is testing the CAST operator.
+#
+# $Id: cast.test,v 1.9 2008/01/19 20:11:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if the build includes the CAST operator
+ifcapable !cast {
+ finish_test
+ return
+}
+
+# Tests for the CAST( AS blob), CAST( AS text) and CAST( AS numeric) built-ins
+#
+ifcapable bloblit {
+ do_test cast-1.1 {
+ execsql {SELECT x'616263'}
+ } abc
+ do_test cast-1.2 {
+ execsql {SELECT typeof(x'616263')}
+ } blob
+ do_test cast-1.3 {
+ execsql {SELECT CAST(x'616263' AS text)}
+ } abc
+ do_test cast-1.4 {
+ execsql {SELECT typeof(CAST(x'616263' AS text))}
+ } text
+ do_test cast-1.5 {
+ execsql {SELECT CAST(x'616263' AS numeric)}
+ } 0
+ do_test cast-1.6 {
+ execsql {SELECT typeof(CAST(x'616263' AS numeric))}
+ } integer
+ do_test cast-1.7 {
+ execsql {SELECT CAST(x'616263' AS blob)}
+ } abc
+ do_test cast-1.8 {
+ execsql {SELECT typeof(CAST(x'616263' AS blob))}
+ } blob
+ do_test cast-1.9 {
+ execsql {SELECT CAST(x'616263' AS integer)}
+ } 0
+ do_test cast-1.10 {
+ execsql {SELECT typeof(CAST(x'616263' AS integer))}
+ } integer
+}
+do_test cast-1.11 {
+ execsql {SELECT null}
+} {{}}
+do_test cast-1.12 {
+ execsql {SELECT typeof(NULL)}
+} null
+do_test cast-1.13 {
+ execsql {SELECT CAST(NULL AS text)}
+} {{}}
+do_test cast-1.14 {
+ execsql {SELECT typeof(CAST(NULL AS text))}
+} null
+do_test cast-1.15 {
+ execsql {SELECT CAST(NULL AS numeric)}
+} {{}}
+do_test cast-1.16 {
+ execsql {SELECT typeof(CAST(NULL AS numeric))}
+} null
+do_test cast-1.17 {
+ execsql {SELECT CAST(NULL AS blob)}
+} {{}}
+do_test cast-1.18 {
+ execsql {SELECT typeof(CAST(NULL AS blob))}
+} null
+do_test cast-1.19 {
+ execsql {SELECT CAST(NULL AS integer)}
+} {{}}
+do_test cast-1.20 {
+ execsql {SELECT typeof(CAST(NULL AS integer))}
+} null
+do_test cast-1.21 {
+ execsql {SELECT 123}
+} {123}
+do_test cast-1.22 {
+ execsql {SELECT typeof(123)}
+} integer
+do_test cast-1.23 {
+ execsql {SELECT CAST(123 AS text)}
+} {123}
+do_test cast-1.24 {
+ execsql {SELECT typeof(CAST(123 AS text))}
+} text
+do_test cast-1.25 {
+ execsql {SELECT CAST(123 AS numeric)}
+} 123
+do_test cast-1.26 {
+ execsql {SELECT typeof(CAST(123 AS numeric))}
+} integer
+do_test cast-1.27 {
+ execsql {SELECT CAST(123 AS blob)}
+} {123}
+do_test cast-1.28 {
+ execsql {SELECT typeof(CAST(123 AS blob))}
+} blob
+do_test cast-1.29 {
+ execsql {SELECT CAST(123 AS integer)}
+} {123}
+do_test cast-1.30 {
+ execsql {SELECT typeof(CAST(123 AS integer))}
+} integer
+do_test cast-1.31 {
+ execsql {SELECT 123.456}
+} {123.456}
+do_test cast-1.32 {
+ execsql {SELECT typeof(123.456)}
+} real
+do_test cast-1.33 {
+ execsql {SELECT CAST(123.456 AS text)}
+} {123.456}
+do_test cast-1.34 {
+ execsql {SELECT typeof(CAST(123.456 AS text))}
+} text
+do_test cast-1.35 {
+ execsql {SELECT CAST(123.456 AS numeric)}
+} 123.456
+do_test cast-1.36 {
+ execsql {SELECT typeof(CAST(123.456 AS numeric))}
+} real
+do_test cast-1.37 {
+ execsql {SELECT CAST(123.456 AS blob)}
+} {123.456}
+do_test cast-1.38 {
+ execsql {SELECT typeof(CAST(123.456 AS blob))}
+} blob
+do_test cast-1.39 {
+ execsql {SELECT CAST(123.456 AS integer)}
+} {123}
+do_test cast-1.38 {
+ execsql {SELECT typeof(CAST(123.456 AS integer))}
+} integer
+do_test cast-1.41 {
+ execsql {SELECT '123abc'}
+} {123abc}
+do_test cast-1.42 {
+ execsql {SELECT typeof('123abc')}
+} text
+do_test cast-1.43 {
+ execsql {SELECT CAST('123abc' AS text)}
+} {123abc}
+do_test cast-1.44 {
+ execsql {SELECT typeof(CAST('123abc' AS text))}
+} text
+do_test cast-1.45 {
+ execsql {SELECT CAST('123abc' AS numeric)}
+} 123
+do_test cast-1.46 {
+ execsql {SELECT typeof(CAST('123abc' AS numeric))}
+} integer
+do_test cast-1.47 {
+ execsql {SELECT CAST('123abc' AS blob)}
+} {123abc}
+do_test cast-1.48 {
+ execsql {SELECT typeof(CAST('123abc' AS blob))}
+} blob
+do_test cast-1.49 {
+ execsql {SELECT CAST('123abc' AS integer)}
+} 123
+do_test cast-1.50 {
+ execsql {SELECT typeof(CAST('123abc' AS integer))}
+} integer
+do_test cast-1.51 {
+ execsql {SELECT CAST('123.5abc' AS numeric)}
+} 123.5
+do_test cast-1.53 {
+ execsql {SELECT CAST('123.5abc' AS integer)}
+} 123
+
+do_test case-1.60 {
+ execsql {SELECT CAST(null AS REAL)}
+} {{}}
+do_test case-1.61 {
+ execsql {SELECT typeof(CAST(null AS REAL))}
+} {null}
+do_test case-1.62 {
+ execsql {SELECT CAST(1 AS REAL)}
+} {1.0}
+do_test case-1.63 {
+ execsql {SELECT typeof(CAST(1 AS REAL))}
+} {real}
+do_test case-1.64 {
+ execsql {SELECT CAST('1' AS REAL)}
+} {1.0}
+do_test case-1.65 {
+ execsql {SELECT typeof(CAST('1' AS REAL))}
+} {real}
+do_test case-1.66 {
+ execsql {SELECT CAST('abc' AS REAL)}
+} {0.0}
+do_test case-1.67 {
+ execsql {SELECT typeof(CAST('abc' AS REAL))}
+} {real}
+do_test case-1.68 {
+ execsql {SELECT CAST(x'31' AS REAL)}
+} {1.0}
+do_test case-1.69 {
+ execsql {SELECT typeof(CAST(x'31' AS REAL))}
+} {real}
+
+
+# Ticket #1662. Ignore leading spaces in numbers when casting.
+#
+do_test cast-2.1 {
+ execsql {SELECT CAST(' 123' AS integer)}
+} 123
+do_test cast-2.2 {
+ execsql {SELECT CAST(' -123.456' AS real)}
+} -123.456
+
+# ticket #2364. Use full percision integers if possible when casting
+# to numeric. Do not fallback to real (and the corresponding 48-bit
+# mantissa) unless absolutely necessary.
+#
+do_test cast-3.1 {
+ execsql {SELECT CAST(9223372036854774800 AS integer)}
+} 9223372036854774800
+do_test cast-3.2 {
+ execsql {SELECT CAST(9223372036854774800 AS numeric)}
+} 9223372036854774800
+do_test cast-3.3 {
+ execsql {SELECT CAST(9223372036854774800 AS real)}
+} 9.22337203685477e+18
+do_test cast-3.4 {
+ execsql {SELECT CAST(CAST(9223372036854774800 AS real) AS integer)}
+} 9223372036854774784
+do_test cast-3.5 {
+ execsql {SELECT CAST(-9223372036854774800 AS integer)}
+} -9223372036854774800
+do_test cast-3.6 {
+ execsql {SELECT CAST(-9223372036854774800 AS numeric)}
+} -9223372036854774800
+do_test cast-3.7 {
+ execsql {SELECT CAST(-9223372036854774800 AS real)}
+} -9.22337203685477e+18
+do_test cast-3.8 {
+ execsql {SELECT CAST(CAST(-9223372036854774800 AS real) AS integer)}
+} -9223372036854774784
+do_test cast-3.11 {
+ execsql {SELECT CAST('9223372036854774800' AS integer)}
+} 9223372036854774800
+do_test cast-3.12 {
+ execsql {SELECT CAST('9223372036854774800' AS numeric)}
+} 9223372036854774800
+do_test cast-3.13 {
+ execsql {SELECT CAST('9223372036854774800' AS real)}
+} 9.22337203685477e+18
+ifcapable long_double {
+ do_test cast-3.14 {
+ execsql {SELECT CAST(CAST('9223372036854774800' AS real) AS integer)}
+ } 9223372036854774784
+}
+do_test cast-3.15 {
+ execsql {SELECT CAST('-9223372036854774800' AS integer)}
+} -9223372036854774800
+do_test cast-3.16 {
+ execsql {SELECT CAST('-9223372036854774800' AS numeric)}
+} -9223372036854774800
+do_test cast-3.17 {
+ execsql {SELECT CAST('-9223372036854774800' AS real)}
+} -9.22337203685477e+18
+ifcapable long_double {
+ do_test cast-3.18 {
+ execsql {SELECT CAST(CAST('-9223372036854774800' AS real) AS integer)}
+ } -9223372036854774784
+}
+if {[db eval {PRAGMA encoding}]=="UTF-8"} {
+ do_test cast-3.21 {
+ execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS integer)}
+ } 9223372036854774800
+ do_test cast-3.22 {
+ execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS numeric)}
+ } 9223372036854774800
+ do_test cast-3.23 {
+ execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS real)}
+ } 9.22337203685477e+18
+ ifcapable long_double {
+ do_test cast-3.24 {
+ execsql {
+ SELECT CAST(CAST(x'39323233333732303336383534373734383030' AS real)
+ AS integer)
+ }
+ } 9223372036854774784
+ }
+}
+do_test case-3.31 {
+ execsql {SELECT CAST(NULL AS numeric)}
+} {{}}
+
+# Test to see if it is possible to trick SQLite into reading past
+# the end of a blob when converting it to a number.
+do_test cast-3.32.1 {
+ set blob "1234567890"
+ set DB [sqlite3_connection_pointer db]
+ set ::STMT [sqlite3_prepare $DB {SELECT CAST(? AS real)} -1 TAIL]
+ sqlite3_bind_blob -static $::STMT 1 $blob 5
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test cast-3.32.2 {
+ sqlite3_column_int $::STMT 0
+} {12345}
+do_test cast-3.32.3 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+finish_test
diff --git a/third_party/sqlite/test/check.test b/third_party/sqlite/test/check.test
new file mode 100755
index 0000000..cdf2b46
--- /dev/null
+++ b/third_party/sqlite/test/check.test
@@ -0,0 +1,372 @@
+# 2005 November 2
+#
+# 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 file is testing CHECK constraints
+#
+# $Id: check.test,v 1.11 2007/07/23 19:39:47 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if the build includes support for CHECK constraints
+ifcapable !check {
+ finish_test
+ return
+}
+
+do_test check-1.1 {
+ execsql {
+ CREATE TABLE t1(
+ x INTEGER CHECK( x<5 ),
+ y REAL CHECK( y>x )
+ );
+ }
+} {}
+do_test check-1.2 {
+ execsql {
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1;
+ }
+} {3 4.0}
+do_test check-1.3 {
+ catchsql {
+ INSERT INTO t1 VALUES(6,7);
+ }
+} {1 {constraint failed}}
+do_test check-1.4 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {3 4.0}
+do_test check-1.5 {
+ catchsql {
+ INSERT INTO t1 VALUES(4,3);
+ }
+} {1 {constraint failed}}
+do_test check-1.6 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {3 4.0}
+do_test check-1.7 {
+ catchsql {
+ INSERT INTO t1 VALUES(NULL,6);
+ }
+} {0 {}}
+do_test check-1.8 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {3 4.0 {} 6.0}
+do_test check-1.9 {
+ catchsql {
+ INSERT INTO t1 VALUES(2,NULL);
+ }
+} {0 {}}
+do_test check-1.10 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {3 4.0 {} 6.0 2 {}}
+do_test check-1.11 {
+ execsql {
+ DELETE FROM t1 WHERE x IS NULL OR x!=3;
+ UPDATE t1 SET x=2 WHERE x==3;
+ SELECT * FROM t1;
+ }
+} {2 4.0}
+do_test check-1.12 {
+ catchsql {
+ UPDATE t1 SET x=7 WHERE x==2
+ }
+} {1 {constraint failed}}
+do_test check-1.13 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {2 4.0}
+do_test check-1.14 {
+ catchsql {
+ UPDATE t1 SET x=5 WHERE x==2
+ }
+} {1 {constraint failed}}
+do_test check-1.15 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {2 4.0}
+do_test check-1.16 {
+ catchsql {
+ UPDATE t1 SET x=4, y=11 WHERE x==2
+ }
+} {0 {}}
+do_test check-1.17 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {4 11.0}
+
+do_test check-2.1 {
+ execsql {
+ CREATE TABLE t2(
+ x INTEGER CHECK( typeof(coalesce(x,0))=="integer" ),
+ y REAL CHECK( typeof(coalesce(y,0.1))=="real" ),
+ z TEXT CHECK( typeof(coalesce(z,''))=="text" )
+ );
+ }
+} {}
+do_test check-2.2 {
+ execsql {
+ INSERT INTO t2 VALUES(1,2.2,'three');
+ SELECT * FROM t2;
+ }
+} {1 2.2 three}
+do_test check-2.3 {
+ execsql {
+ INSERT INTO t2 VALUES(NULL, NULL, NULL);
+ SELECT * FROM t2;
+ }
+} {1 2.2 three {} {} {}}
+do_test check-2.4 {
+ catchsql {
+ INSERT INTO t2 VALUES(1.1, NULL, NULL);
+ }
+} {1 {constraint failed}}
+do_test check-2.5 {
+ catchsql {
+ INSERT INTO t2 VALUES(NULL, 5, NULL);
+ }
+} {1 {constraint failed}}
+do_test check-2.6 {
+ catchsql {
+ INSERT INTO t2 VALUES(NULL, NULL, 3.14159);
+ }
+} {1 {constraint failed}}
+
+ifcapable subquery {
+ do_test check-3.1 {
+ catchsql {
+ CREATE TABLE t3(
+ x, y, z,
+ CHECK( x<(SELECT min(x) FROM t1) )
+ );
+ }
+ } {1 {subqueries prohibited in CHECK constraints}}
+}
+
+do_test check-3.2 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY name
+ }
+} {t1 t2}
+do_test check-3.3 {
+ catchsql {
+ CREATE TABLE t3(
+ x, y, z,
+ CHECK( q<x )
+ );
+ }
+} {1 {no such column: q}}
+do_test check-3.4 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY name
+ }
+} {t1 t2}
+do_test check-3.5 {
+ catchsql {
+ CREATE TABLE t3(
+ x, y, z,
+ CHECK( t2.x<x )
+ );
+ }
+} {1 {no such column: t2.x}}
+do_test check-3.6 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY name
+ }
+} {t1 t2}
+do_test check-3.7 {
+ catchsql {
+ CREATE TABLE t3(
+ x, y, z,
+ CHECK( t3.x<25 )
+ );
+ }
+} {0 {}}
+do_test check-3.8 {
+ execsql {
+ INSERT INTO t3 VALUES(1,2,3);
+ SELECT * FROM t3;
+ }
+} {1 2 3}
+do_test check-3.9 {
+ catchsql {
+ INSERT INTO t3 VALUES(111,222,333);
+ }
+} {1 {constraint failed}}
+
+do_test check-4.1 {
+ execsql {
+ CREATE TABLE t4(x, y,
+ CHECK (
+ x+y==11
+ OR x*y==12
+ OR x/y BETWEEN 5 AND 8
+ OR -x==y+10
+ )
+ );
+ }
+} {}
+do_test check-4.2 {
+ execsql {
+ INSERT INTO t4 VALUES(1,10);
+ SELECT * FROM t4
+ }
+} {1 10}
+do_test check-4.3 {
+ execsql {
+ UPDATE t4 SET x=4, y=3;
+ SELECT * FROM t4
+ }
+} {4 3}
+do_test check-4.3 {
+ execsql {
+ UPDATE t4 SET x=12, y=2;
+ SELECT * FROM t4
+ }
+} {12 2}
+do_test check-4.4 {
+ execsql {
+ UPDATE t4 SET x=12, y=-22;
+ SELECT * FROM t4
+ }
+} {12 -22}
+do_test check-4.5 {
+ catchsql {
+ UPDATE t4 SET x=0, y=1;
+ }
+} {1 {constraint failed}}
+do_test check-4.6 {
+ execsql {
+ SELECT * FROM t4;
+ }
+} {12 -22}
+do_test check-4.7 {
+ execsql {
+ PRAGMA ignore_check_constraints=ON;
+ UPDATE t4 SET x=0, y=1;
+ SELECT * FROM t4;
+ }
+} {0 1}
+do_test check-4.8 {
+ catchsql {
+ PRAGMA ignore_check_constraints=OFF;
+ UPDATE t4 SET x=0, y=2;
+ }
+} {1 {constraint failed}}
+ifcapable vacuum {
+ do_test check_4.9 {
+ catchsql {
+ VACUUM
+ }
+ } {0 {}}
+}
+
+do_test check-5.1 {
+ catchsql {
+ CREATE TABLE t5(x, y,
+ CHECK( x*y<:abc )
+ );
+ }
+} {1 {parameters prohibited in CHECK constraints}}
+do_test check-5.2 {
+ catchsql {
+ CREATE TABLE t5(x, y,
+ CHECK( x*y<? )
+ );
+ }
+} {1 {parameters prohibited in CHECK constraints}}
+
+ifcapable conflict {
+
+do_test check-6.1 {
+ execsql {SELECT * FROM t1}
+} {4 11.0}
+do_test check-6.2 {
+ execsql {
+ UPDATE OR IGNORE t1 SET x=5;
+ SELECT * FROM t1;
+ }
+} {4 11.0}
+do_test check-6.3 {
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(5,4.0);
+ SELECT * FROM t1;
+ }
+} {4 11.0}
+do_test check-6.4 {
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(2,20.0);
+ SELECT * FROM t1;
+ }
+} {4 11.0 2 20.0}
+do_test check-6.5 {
+ catchsql {
+ UPDATE OR FAIL t1 SET x=7-x, y=y+1;
+ }
+} {1 {constraint failed}}
+do_test check-6.6 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {3 12.0 2 20.0}
+do_test check-6.7 {
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(1,30.0);
+ INSERT OR ROLLBACK INTO t1 VALUES(8,40.0);
+ }
+} {1 {constraint failed}}
+do_test check-6.8 {
+ catchsql {
+ COMMIT;
+ }
+} {1 {cannot commit - no transaction is active}}
+do_test check-6.9 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {3 12.0 2 20.0}
+
+do_test check-6.11 {
+ execsql {SELECT * FROM t1}
+} {3 12.0 2 20.0}
+do_test check-6.12 {
+ catchsql {
+ REPLACE INTO t1 VALUES(6,7);
+ }
+} {1 {constraint failed}}
+do_test check-6.13 {
+ execsql {SELECT * FROM t1}
+} {3 12.0 2 20.0}
+do_test check-6.14 {
+ catchsql {
+ INSERT OR IGNORE INTO t1 VALUES(6,7);
+ }
+} {0 {}}
+do_test check-6.15 {
+ execsql {SELECT * FROM t1}
+} {3 12.0 2 20.0}
+
+
+}
+
+finish_test
diff --git a/third_party/sqlite/test/collate1.test b/third_party/sqlite/test/collate1.test
new file mode 100755
index 0000000..ac2c75b
--- /dev/null
+++ b/third_party/sqlite/test/collate1.test
@@ -0,0 +1,307 @@
+#
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: collate1.test,v 1.5 2007/02/01 23:02:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+#
+# Tests are roughly organised as follows:
+#
+# collate1-1.* - Single-field ORDER BY with an explicit COLLATE clause.
+# collate1-2.* - Multi-field ORDER BY with an explicit COLLATE clause.
+# collate1-3.* - ORDER BY using a default collation type. Also that an
+# explict collate type overrides a default collate type.
+# collate1-4.* - ORDER BY using a data type.
+#
+
+#
+# Collation type 'HEX'. If an argument can be interpreted as a hexadecimal
+# number, then it is converted to one before the comparison is performed.
+# Numbers are less than other strings. If neither argument is a number,
+# [string compare] is used.
+#
+db collate HEX hex_collate
+proc hex_collate {lhs rhs} {
+ set lhs_ishex [regexp {^(0x|)[1234567890abcdefABCDEF]+$} $lhs]
+ set rhs_ishex [regexp {^(0x|)[1234567890abcdefABCDEF]+$} $rhs]
+ if {$lhs_ishex && $rhs_ishex} {
+ set lhsx [scan $lhs %x]
+ set rhsx [scan $rhs %x]
+ if {$lhs < $rhs} {return -1}
+ if {$lhs == $rhs} {return 0}
+ if {$lhs > $rhs} {return 1}
+ }
+ if {$lhs_ishex} {
+ return -1;
+ }
+ if {$rhs_ishex} {
+ return 1;
+ }
+ return [string compare $lhs $rhs]
+}
+db function hex {format 0x%X}
+
+# Mimic the SQLite 2 collation type NUMERIC.
+db collate numeric numeric_collate
+proc numeric_collate {lhs rhs} {
+ if {$lhs == $rhs} {return 0}
+ return [expr ($lhs>$rhs)?1:-1]
+}
+
+do_test collate1-1.0 {
+ execsql {
+ CREATE TABLE collate1t1(c1, c2);
+ INSERT INTO collate1t1 VALUES(45, hex(45));
+ INSERT INTO collate1t1 VALUES(NULL, NULL);
+ INSERT INTO collate1t1 VALUES(281, hex(281));
+ }
+} {}
+do_test collate1-1.1 {
+ execsql {
+ SELECT c2 FROM collate1t1 ORDER BY 1;
+ }
+} {{} 0x119 0x2D}
+do_test collate1-1.2 {
+ execsql {
+ SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex;
+ }
+} {{} 0x2D 0x119}
+do_test collate1-1.3 {
+ execsql {
+ SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex DESC;
+ }
+} {0x119 0x2D {}}
+do_test collate1-1.4 {
+ execsql {
+ SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex ASC;
+ }
+} {{} 0x2D 0x119}
+do_test collate1-1.5 {
+ execsql {
+ SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1
+ }
+} {{} 0x2D 0x119}
+do_test collate1-1.6 {
+ execsql {
+ SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1 ASC
+ }
+} {{} 0x2D 0x119}
+do_test collate1-1.7 {
+ execsql {
+ SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1 DESC
+ }
+} {0x119 0x2D {}}
+do_test collate1-1.99 {
+ execsql {
+ DROP TABLE collate1t1;
+ }
+} {}
+
+do_test collate1-2.0 {
+ execsql {
+ CREATE TABLE collate1t1(c1, c2);
+ INSERT INTO collate1t1 VALUES('5', '0x11');
+ INSERT INTO collate1t1 VALUES('5', '0xA');
+ INSERT INTO collate1t1 VALUES(NULL, NULL);
+ INSERT INTO collate1t1 VALUES('7', '0xA');
+ INSERT INTO collate1t1 VALUES('11', '0x11');
+ INSERT INTO collate1t1 VALUES('11', '0x101');
+ }
+} {}
+do_test collate1-2.2 {
+ execsql {
+ SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE numeric, 2 COLLATE hex;
+ }
+} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
+do_test collate1-2.3 {
+ execsql {
+ SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE binary, 2 COLLATE hex;
+ }
+} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
+do_test collate1-2.4 {
+ execsql {
+ SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex;
+ }
+} {7 0xA 5 0xA 5 0x11 11 0x11 11 0x101 {} {}}
+do_test collate1-2.5 {
+ execsql {
+ SELECT c1, c2 FROM collate1t1
+ ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex DESC;
+ }
+} {7 0xA 5 0x11 5 0xA 11 0x101 11 0x11 {} {}}
+do_test collate1-2.6 {
+ execsql {
+ SELECT c1, c2 FROM collate1t1
+ ORDER BY 1 COLLATE binary ASC, 2 COLLATE hex ASC;
+ }
+} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
+do_test collate1-2.12.1 {
+ execsql {
+ SELECT c1 COLLATE numeric, c2 FROM collate1t1
+ ORDER BY 1, 2 COLLATE hex;
+ }
+} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
+do_test collate1-2.12.2 {
+ execsql {
+ SELECT c1 COLLATE hex, c2 FROM collate1t1
+ ORDER BY 1 COLLATE numeric, 2 COLLATE hex;
+ }
+} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
+do_test collate1-2.12.3 {
+ execsql {
+ SELECT c1, c2 COLLATE hex FROM collate1t1
+ ORDER BY 1 COLLATE numeric, 2;
+ }
+} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
+do_test collate1-2.12.4 {
+ execsql {
+ SELECT c1 COLLATE numeric, c2 COLLATE hex
+ FROM collate1t1
+ ORDER BY 1, 2;
+ }
+} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
+do_test collate1-2.13 {
+ execsql {
+ SELECT c1 COLLATE binary, c2 COLLATE hex
+ FROM collate1t1
+ ORDER BY 1, 2;
+ }
+} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
+do_test collate1-2.14 {
+ execsql {
+ SELECT c1, c2
+ FROM collate1t1 ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex;
+ }
+} {7 0xA 5 0xA 5 0x11 11 0x11 11 0x101 {} {}}
+do_test collate1-2.15 {
+ execsql {
+ SELECT c1 COLLATE binary, c2 COLLATE hex
+ FROM collate1t1
+ ORDER BY 1 DESC, 2 DESC;
+ }
+} {7 0xA 5 0x11 5 0xA 11 0x101 11 0x11 {} {}}
+do_test collate1-2.16 {
+ execsql {
+ SELECT c1 COLLATE hex, c2 COLLATE binary
+ FROM collate1t1
+ ORDER BY 1 COLLATE binary ASC, 2 COLLATE hex ASC;
+ }
+} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
+do_test collate1-2.99 {
+ execsql {
+ DROP TABLE collate1t1;
+ }
+} {}
+
+#
+# These tests ensure that the default collation type for a column is used
+# by an ORDER BY clause correctly. The focus is all the different ways
+# the column can be referenced. i.e. a, collate2t1.a, main.collate2t1.a etc.
+#
+do_test collate1-3.0 {
+ execsql {
+ CREATE TABLE collate1t1(a COLLATE hex, b);
+ INSERT INTO collate1t1 VALUES( '0x5', 5 );
+ INSERT INTO collate1t1 VALUES( '1', 1 );
+ INSERT INTO collate1t1 VALUES( '0x45', 69 );
+ INSERT INTO collate1t1 VALUES( NULL, NULL );
+ SELECT * FROM collate1t1 ORDER BY a;
+ }
+} {{} {} 1 1 0x5 5 0x45 69}
+
+do_test collate1-3.1 {
+ execsql {
+ SELECT * FROM collate1t1 ORDER BY 1;
+ }
+} {{} {} 1 1 0x5 5 0x45 69}
+do_test collate1-3.2 {
+ execsql {
+ SELECT * FROM collate1t1 ORDER BY collate1t1.a;
+ }
+} {{} {} 1 1 0x5 5 0x45 69}
+do_test collate1-3.3 {
+ execsql {
+ SELECT * FROM collate1t1 ORDER BY main.collate1t1.a;
+ }
+} {{} {} 1 1 0x5 5 0x45 69}
+do_test collate1-3.4 {
+ execsql {
+ SELECT a as c1, b as c2 FROM collate1t1 ORDER BY c1;
+ }
+} {{} {} 1 1 0x5 5 0x45 69}
+do_test collate1-3.5 {
+ execsql {
+ SELECT a as c1, b as c2 FROM collate1t1 ORDER BY c1 COLLATE binary;
+ }
+} {{} {} 0x45 69 0x5 5 1 1}
+do_test collate1-3.5.1 {
+ execsql {
+ SELECT a COLLATE binary as c1, b as c2
+ FROM collate1t1 ORDER BY c1;
+ }
+} {{} {} 0x45 69 0x5 5 1 1}
+do_test collate1-3.6 {
+ execsql {
+ DROP TABLE collate1t1;
+ }
+} {}
+
+# Update for SQLite version 3. The collate1-4.* test cases were written
+# before manifest types were introduced. The following test cases still
+# work, due to the 'affinity' mechanism, but they don't prove anything
+# about collation sequences.
+#
+do_test collate1-4.0 {
+ execsql {
+ CREATE TABLE collate1t1(c1 numeric, c2 text);
+ INSERT INTO collate1t1 VALUES(1, 1);
+ INSERT INTO collate1t1 VALUES(12, 12);
+ INSERT INTO collate1t1 VALUES(NULL, NULL);
+ INSERT INTO collate1t1 VALUES(101, 101);
+ }
+} {}
+do_test collate1-4.1 {
+ execsql {
+ SELECT c1 FROM collate1t1 ORDER BY 1;
+ }
+} {{} 1 12 101}
+do_test collate1-4.2 {
+ execsql {
+ SELECT c2 FROM collate1t1 ORDER BY 1;
+ }
+} {{} 1 101 12}
+do_test collate1-4.3 {
+ execsql {
+ SELECT c2+0 FROM collate1t1 ORDER BY 1;
+ }
+} {{} 1 12 101}
+do_test collate1-4.4 {
+ execsql {
+ SELECT c1||'' FROM collate1t1 ORDER BY 1;
+ }
+} {{} 1 101 12}
+do_test collate1-4.4.1 {
+ execsql {
+ SELECT (c1||'') COLLATE numeric FROM collate1t1 ORDER BY 1;
+ }
+} {{} 1 12 101}
+do_test collate1-4.5 {
+ execsql {
+ DROP TABLE collate1t1;
+ }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/collate2.test b/third_party/sqlite/test/collate2.test
new file mode 100755
index 0000000..28f4a91
--- /dev/null
+++ b/third_party/sqlite/test/collate2.test
@@ -0,0 +1,664 @@
+#
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: collate2.test,v 1.5 2007/02/01 23:02:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+#
+# Tests are organised as follows:
+#
+# collate2-1.* WHERE <expr> expressions (sqliteExprIfTrue).
+# collate2-2.* WHERE NOT <expr> expressions (sqliteExprIfFalse).
+# collate2-3.* SELECT <expr> expressions (sqliteExprCode).
+# collate2-4.* Precedence of collation/data types in binary comparisons
+# collate2-5.* JOIN syntax.
+#
+
+# Create a collation type BACKWARDS for use in testing. This collation type
+# is similar to the built-in TEXT collation type except the order of
+# characters in each string is reversed before the comparison is performed.
+db collate BACKWARDS backwards_collate
+proc backwards_collate {a b} {
+ set ra {};
+ set rb {}
+ foreach c [split $a {}] { set ra $c$ra }
+ foreach c [split $b {}] { set rb $c$rb }
+ return [string compare $ra $rb]
+}
+
+# The following values are used in these tests:
+# NULL aa ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB
+#
+# The collation orders for each of the tested collation types are:
+#
+# BINARY: NULL AA AB Aa Ab BA BB Ba Bb aA aB aa ab bA bB ba bb
+# NOCASE: NULL aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB
+# BACKWARDS: NULL AA BA aA bA AB BB aB bB Aa Ba aa ba Ab Bb ab bb
+#
+# These tests verify that the default collation type for a column is used
+# for comparison operators (<, >, <=, >=, =) involving that column and
+# an expression that is not a column with a default collation type.
+#
+# The collation sequences BINARY and NOCASE are built-in, the BACKWARDS
+# collation sequence is implemented by the TCL proc backwards_collate
+# above.
+#
+do_test collate2-1.0 {
+ execsql {
+ CREATE TABLE collate2t1(
+ a COLLATE BINARY,
+ b COLLATE NOCASE,
+ c COLLATE BACKWARDS
+ );
+ INSERT INTO collate2t1 VALUES( NULL, NULL, NULL );
+
+ INSERT INTO collate2t1 VALUES( 'aa', 'aa', 'aa' );
+ INSERT INTO collate2t1 VALUES( 'ab', 'ab', 'ab' );
+ INSERT INTO collate2t1 VALUES( 'ba', 'ba', 'ba' );
+ INSERT INTO collate2t1 VALUES( 'bb', 'bb', 'bb' );
+
+ INSERT INTO collate2t1 VALUES( 'aA', 'aA', 'aA' );
+ INSERT INTO collate2t1 VALUES( 'aB', 'aB', 'aB' );
+ INSERT INTO collate2t1 VALUES( 'bA', 'bA', 'bA' );
+ INSERT INTO collate2t1 VALUES( 'bB', 'bB', 'bB' );
+
+ INSERT INTO collate2t1 VALUES( 'Aa', 'Aa', 'Aa' );
+ INSERT INTO collate2t1 VALUES( 'Ab', 'Ab', 'Ab' );
+ INSERT INTO collate2t1 VALUES( 'Ba', 'Ba', 'Ba' );
+ INSERT INTO collate2t1 VALUES( 'Bb', 'Bb', 'Bb' );
+
+ INSERT INTO collate2t1 VALUES( 'AA', 'AA', 'AA' );
+ INSERT INTO collate2t1 VALUES( 'AB', 'AB', 'AB' );
+ INSERT INTO collate2t1 VALUES( 'BA', 'BA', 'BA' );
+ INSERT INTO collate2t1 VALUES( 'BB', 'BB', 'BB' );
+ }
+ if {[info exists collate_test_use_index]} {
+ execsql {
+ CREATE INDEX collate2t1_i1 ON collate2t1(a);
+ CREATE INDEX collate2t1_i2 ON collate2t1(b);
+ CREATE INDEX collate2t1_i3 ON collate2t1(c);
+ }
+ }
+} {}
+do_test collate2-1.1 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a > 'aa' ORDER BY 1;
+ }
+} {ab bA bB ba bb}
+do_test collate2-1.1.1 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a COLLATE binary > 'aa' ORDER BY 1;
+ }
+} {ab bA bB ba bb}
+do_test collate2-1.1.2 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE b COLLATE binary > 'aa' ORDER BY 1;
+ }
+} {ab bA bB ba bb}
+do_test collate2-1.1.3 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE c COLLATE binary > 'aa' ORDER BY 1;
+ }
+} {ab bA bB ba bb}
+do_test collate2-1.2 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b > 'aa' ORDER BY 1, oid;
+ }
+} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.2.1 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE a COLLATE nocase > 'aa'
+ ORDER BY 1, oid;
+ }
+} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.2.2 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b COLLATE nocase > 'aa'
+ ORDER BY 1, oid;
+ }
+} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.2.3 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE c COLLATE nocase > 'aa'
+ ORDER BY 1, oid;
+ }
+} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.3 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c > 'aa' ORDER BY 1;
+ }
+} {ba Ab Bb ab bb}
+do_test collate2-1.3.1 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE a COLLATE backwards > 'aa'
+ ORDER BY 1;
+ }
+} {ba Ab Bb ab bb}
+do_test collate2-1.3.2 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE b COLLATE backwards > 'aa'
+ ORDER BY 1;
+ }
+} {ba Ab Bb ab bb}
+do_test collate2-1.3.3 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c COLLATE backwards > 'aa'
+ ORDER BY 1;
+ }
+} {ba Ab Bb ab bb}
+do_test collate2-1.4 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a < 'aa' ORDER BY 1;
+ }
+} {AA AB Aa Ab BA BB Ba Bb aA aB}
+do_test collate2-1.5 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b < 'aa' ORDER BY 1, oid;
+ }
+} {}
+do_test collate2-1.6 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c < 'aa' ORDER BY 1;
+ }
+} {AA BA aA bA AB BB aB bB Aa Ba}
+do_test collate2-1.7 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a = 'aa';
+ }
+} {aa}
+do_test collate2-1.8 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b = 'aa' ORDER BY oid;
+ }
+} {aa aA Aa AA}
+do_test collate2-1.9 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c = 'aa';
+ }
+} {aa}
+do_test collate2-1.10 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a >= 'aa' ORDER BY 1;
+ }
+} {aa ab bA bB ba bb}
+do_test collate2-1.11 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b >= 'aa' ORDER BY 1, oid;
+ }
+} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.12 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c >= 'aa' ORDER BY 1;
+ }
+} {aa ba Ab Bb ab bb}
+do_test collate2-1.13 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a <= 'aa' ORDER BY 1;
+ }
+} {AA AB Aa Ab BA BB Ba Bb aA aB aa}
+do_test collate2-1.14 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b <= 'aa' ORDER BY 1, oid;
+ }
+} {aa aA Aa AA}
+do_test collate2-1.15 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c <= 'aa' ORDER BY 1;
+ }
+} {AA BA aA bA AB BB aB bB Aa Ba aa}
+do_test collate2-1.16 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
+ }
+} {Aa Ab BA BB Ba Bb}
+do_test collate2-1.17 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b BETWEEN 'Aa' AND 'Bb' ORDER BY 1, oid;
+ }
+} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-1.18 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
+ }
+} {Aa Ba aa ba Ab Bb}
+do_test collate2-1.19 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE
+ CASE a WHEN 'aa' THEN 1 ELSE 0 END
+ ORDER BY 1, oid;
+ }
+} {aa}
+do_test collate2-1.20 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE
+ CASE b WHEN 'aa' THEN 1 ELSE 0 END
+ ORDER BY 1, oid;
+ }
+} {aa aA Aa AA}
+do_test collate2-1.21 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE
+ CASE c WHEN 'aa' THEN 1 ELSE 0 END
+ ORDER BY 1, oid;
+ }
+} {aa}
+
+ifcapable subquery {
+ do_test collate2-1.22 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb') ORDER BY 1, oid;
+ }
+ } {aa bb}
+ do_test collate2-1.23 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b IN ('aa', 'bb') ORDER BY 1, oid;
+ }
+ } {aa aA Aa AA bb bB Bb BB}
+ do_test collate2-1.24 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c IN ('aa', 'bb') ORDER BY 1, oid;
+ }
+ } {aa bb}
+ do_test collate2-1.25 {
+ execsql {
+ SELECT a FROM collate2t1
+ WHERE a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {aa bb}
+ do_test collate2-1.26 {
+ execsql {
+ SELECT b FROM collate2t1
+ WHERE b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {aa bb aA bB Aa Bb AA BB}
+ do_test collate2-1.27 {
+ execsql {
+ SELECT c FROM collate2t1
+ WHERE c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {aa bb}
+} ;# ifcapable subquery
+
+do_test collate2-2.1 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a > 'aa' ORDER BY 1;
+ }
+} {AA AB Aa Ab BA BB Ba Bb aA aB aa}
+do_test collate2-2.2 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b > 'aa' ORDER BY 1, oid;
+ }
+} {aa aA Aa AA}
+do_test collate2-2.3 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c > 'aa' ORDER BY 1;
+ }
+} {AA BA aA bA AB BB aB bB Aa Ba aa}
+do_test collate2-2.4 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a < 'aa' ORDER BY 1;
+ }
+} {aa ab bA bB ba bb}
+do_test collate2-2.5 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b < 'aa' ORDER BY 1, oid;
+ }
+} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-2.6 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c < 'aa' ORDER BY 1;
+ }
+} {aa ba Ab Bb ab bb}
+do_test collate2-2.7 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a = 'aa';
+ }
+} {ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+do_test collate2-2.8 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b = 'aa';
+ }
+} {ab ba bb aB bA bB Ab Ba Bb AB BA BB}
+do_test collate2-2.9 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c = 'aa';
+ }
+} {ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+do_test collate2-2.10 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a >= 'aa' ORDER BY 1;
+ }
+} {AA AB Aa Ab BA BB Ba Bb aA aB}
+do_test collate2-2.11 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b >= 'aa' ORDER BY 1, oid;
+ }
+} {}
+do_test collate2-2.12 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c >= 'aa' ORDER BY 1;
+ }
+} {AA BA aA bA AB BB aB bB Aa Ba}
+do_test collate2-2.13 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a <= 'aa' ORDER BY 1;
+ }
+} {ab bA bB ba bb}
+do_test collate2-2.14 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b <= 'aa' ORDER BY 1, oid;
+ }
+} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
+do_test collate2-2.15 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c <= 'aa' ORDER BY 1;
+ }
+} {ba Ab Bb ab bb}
+do_test collate2-2.16 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE a NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
+ }
+} {AA AB aA aB aa ab bA bB ba bb}
+do_test collate2-2.17 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE b NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1, oid;
+ }
+} {}
+do_test collate2-2.18 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE c NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
+ }
+} {AA BA aA bA AB BB aB bB ab bb}
+do_test collate2-2.19 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT CASE a WHEN 'aa' THEN 1 ELSE 0 END;
+ }
+} {{} ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+do_test collate2-2.20 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT CASE b WHEN 'aa' THEN 1 ELSE 0 END;
+ }
+} {{} ab ba bb aB bA bB Ab Ba Bb AB BA BB}
+do_test collate2-2.21 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT CASE c WHEN 'aa' THEN 1 ELSE 0 END;
+ }
+} {{} ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+
+ifcapable subquery {
+ do_test collate2-2.22 {
+ execsql {
+ SELECT a FROM collate2t1 WHERE NOT a IN ('aa', 'bb');
+ }
+ } {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+ do_test collate2-2.23 {
+ execsql {
+ SELECT b FROM collate2t1 WHERE NOT b IN ('aa', 'bb');
+ }
+ } {ab ba aB bA Ab Ba AB BA}
+ do_test collate2-2.24 {
+ execsql {
+ SELECT c FROM collate2t1 WHERE NOT c IN ('aa', 'bb');
+ }
+ } {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+ do_test collate2-2.25 {
+ execsql {
+ SELECT a FROM collate2t1
+ WHERE NOT a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+ do_test collate2-2.26 {
+ execsql {
+ SELECT b FROM collate2t1
+ WHERE NOT b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {ab ba aB bA Ab Ba AB BA}
+ do_test collate2-2.27 {
+ execsql {
+ SELECT c FROM collate2t1
+ WHERE NOT c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
+ }
+ } {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
+}
+
+do_test collate2-3.1 {
+ execsql {
+ SELECT a > 'aa' FROM collate2t1;
+ }
+} {{} 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0}
+do_test collate2-3.2 {
+ execsql {
+ SELECT b > 'aa' FROM collate2t1;
+ }
+} {{} 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1}
+do_test collate2-3.3 {
+ execsql {
+ SELECT c > 'aa' FROM collate2t1;
+ }
+} {{} 0 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0}
+do_test collate2-3.4 {
+ execsql {
+ SELECT a < 'aa' FROM collate2t1;
+ }
+} {{} 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1}
+do_test collate2-3.5 {
+ execsql {
+ SELECT b < 'aa' FROM collate2t1;
+ }
+} {{} 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
+do_test collate2-3.6 {
+ execsql {
+ SELECT c < 'aa' FROM collate2t1;
+ }
+} {{} 0 0 0 0 1 1 1 1 1 0 1 0 1 1 1 1}
+do_test collate2-3.7 {
+ execsql {
+ SELECT a = 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
+do_test collate2-3.8 {
+ execsql {
+ SELECT b = 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
+do_test collate2-3.9 {
+ execsql {
+ SELECT c = 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
+do_test collate2-3.10 {
+ execsql {
+ SELECT a <= 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1}
+do_test collate2-3.11 {
+ execsql {
+ SELECT b <= 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
+do_test collate2-3.12 {
+ execsql {
+ SELECT c <= 'aa' FROM collate2t1;
+ }
+} {{} 1 0 0 0 1 1 1 1 1 0 1 0 1 1 1 1}
+do_test collate2-3.13 {
+ execsql {
+ SELECT a >= 'aa' FROM collate2t1;
+ }
+} {{} 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0}
+do_test collate2-3.14 {
+ execsql {
+ SELECT b >= 'aa' FROM collate2t1;
+ }
+} {{} 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
+do_test collate2-3.15 {
+ execsql {
+ SELECT c >= 'aa' FROM collate2t1;
+ }
+} {{} 1 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0}
+do_test collate2-3.16 {
+ execsql {
+ SELECT a BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
+ }
+} {{} 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1}
+do_test collate2-3.17 {
+ execsql {
+ SELECT b BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
+ }
+} {{} 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
+do_test collate2-3.18 {
+ execsql {
+ SELECT c BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
+ }
+} {{} 1 0 1 0 0 0 0 0 1 1 1 1 0 0 0 0}
+do_test collate2-3.19 {
+ execsql {
+ SELECT CASE a WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
+ }
+} {0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
+do_test collate2-3.20 {
+ execsql {
+ SELECT CASE b WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
+ }
+} {0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
+do_test collate2-3.21 {
+ execsql {
+ SELECT CASE c WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
+ }
+} {0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
+
+ifcapable subquery {
+ do_test collate2-3.22 {
+ execsql {
+ SELECT a IN ('aa', 'bb') FROM collate2t1;
+ }
+ } {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
+ do_test collate2-3.23 {
+ execsql {
+ SELECT b IN ('aa', 'bb') FROM collate2t1;
+ }
+ } {{} 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1}
+ do_test collate2-3.24 {
+ execsql {
+ SELECT c IN ('aa', 'bb') FROM collate2t1;
+ }
+ } {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
+ do_test collate2-3.25 {
+ execsql {
+ SELECT a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
+ FROM collate2t1;
+ }
+ } {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
+ do_test collate2-3.26 {
+ execsql {
+ SELECT b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
+ FROM collate2t1;
+ }
+ } {{} 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1}
+ do_test collate2-3.27 {
+ execsql {
+ SELECT c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
+ FROM collate2t1;
+ }
+ } {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
+}
+
+do_test collate2-4.0 {
+ execsql {
+ CREATE TABLE collate2t2(b COLLATE binary);
+ CREATE TABLE collate2t3(b text);
+ INSERT INTO collate2t2 VALUES('aa');
+ INSERT INTO collate2t3 VALUES('aa');
+ }
+} {}
+
+# Test that when both sides of a binary comparison operator have
+# default collation types, the collate type for the leftmost term
+# is used.
+do_test collate2-4.1 {
+ execsql {
+ SELECT collate2t1.a FROM collate2t1, collate2t2
+ WHERE collate2t1.b = collate2t2.b;
+ }
+} {aa aA Aa AA}
+do_test collate2-4.2 {
+ execsql {
+ SELECT collate2t1.a FROM collate2t1, collate2t2
+ WHERE collate2t2.b = collate2t1.b;
+ }
+} {aa}
+
+# Test that when one side has a default collation type and the other
+# does not, the collation type is used.
+do_test collate2-4.3 {
+ execsql {
+ SELECT collate2t1.a FROM collate2t1, collate2t3
+ WHERE collate2t1.b = collate2t3.b||'';
+ }
+} {aa aA Aa AA}
+do_test collate2-4.4 {
+ execsql {
+ SELECT collate2t1.a FROM collate2t1, collate2t3
+ WHERE collate2t3.b||'' = collate2t1.b;
+ }
+} {aa aA Aa AA}
+
+do_test collate2-4.5 {
+ execsql {
+ DROP TABLE collate2t3;
+ }
+} {}
+
+#
+# Test that the default collation types are used when the JOIN syntax
+# is used in place of a WHERE clause.
+#
+# SQLite transforms the JOIN syntax into a WHERE clause internally, so
+# the focus of these tests is to ensure that the table on the left-hand-side
+# of the join determines the collation type used.
+#
+do_test collate2-5.0 {
+ execsql {
+ SELECT collate2t1.b FROM collate2t1 JOIN collate2t2 USING (b);
+ }
+} {aa aA Aa AA}
+do_test collate2-5.1 {
+ execsql {
+ SELECT collate2t1.b FROM collate2t2 JOIN collate2t1 USING (b);
+ }
+} {aa}
+do_test collate2-5.2 {
+ execsql {
+ SELECT collate2t1.b FROM collate2t1 NATURAL JOIN collate2t2;
+ }
+} {aa aA Aa AA}
+do_test collate2-5.3 {
+ execsql {
+ SELECT collate2t1.b FROM collate2t2 NATURAL JOIN collate2t1;
+ }
+} {aa}
+do_test collate2-5.4 {
+ execsql {
+ SELECT collate2t2.b FROM collate2t1 LEFT OUTER JOIN collate2t2 USING (b) order by collate2t1.oid;
+ }
+} {{} aa {} {} {} aa {} {} {} aa {} {} {} aa {} {} {}}
+do_test collate2-5.5 {
+ execsql {
+ SELECT collate2t1.b, collate2t2.b FROM collate2t2 LEFT OUTER JOIN collate2t1 USING (b);
+ }
+} {aa aa}
+
+finish_test
diff --git a/third_party/sqlite/test/collate3.test b/third_party/sqlite/test/collate3.test
new file mode 100755
index 0000000..1a26ab8
--- /dev/null
+++ b/third_party/sqlite/test/collate3.test
@@ -0,0 +1,428 @@
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: collate3.test,v 1.12 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+#
+# Tests are organised as follows:
+#
+# collate3.1.* - Errors related to unknown collation sequences.
+# collate3.2.* - Errors related to undefined collation sequences.
+# collate3.3.* - Writing to a table that has an index with an undefined c.s.
+# collate3.4.* - Misc errors.
+# collate3.5.* - Collation factory.
+#
+
+#
+# These tests ensure that when a user executes a statement with an
+# unknown collation sequence an error is returned.
+#
+do_test collate3-1.0 {
+ execsql {
+ CREATE TABLE collate3t1(c1);
+ }
+} {}
+do_test collate3-1.1 {
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY 1 collate garbage;
+ }
+} {1 {no such collation sequence: garbage}}
+do_test collate3-1.2 {
+ catchsql {
+ CREATE TABLE collate3t2(c1 collate garbage);
+ }
+} {1 {no such collation sequence: garbage}}
+do_test collate3-1.3 {
+ catchsql {
+ CREATE INDEX collate3i1 ON collate3t1(c1 COLLATE garbage);
+ }
+} {1 {no such collation sequence: garbage}}
+
+execsql {
+ DROP TABLE collate3t1;
+}
+
+#
+# Create a table with a default collation sequence, then close
+# and re-open the database without re-registering the collation
+# sequence. Then make sure the library stops us from using
+# the collation sequence in:
+# * an explicitly collated ORDER BY
+# * an ORDER BY that uses the default collation sequence
+# * an expression (=)
+# * a CREATE TABLE statement
+# * a CREATE INDEX statement that uses a default collation sequence
+# * a GROUP BY that uses the default collation sequence
+# * a SELECT DISTINCT that uses the default collation sequence
+# * Compound SELECTs that uses the default collation sequence
+# * An ORDER BY on a compound SELECT with an explicit ORDER BY.
+#
+do_test collate3-2.0 {
+ db collate string_compare {string compare}
+ execsql {
+ CREATE TABLE collate3t1(c1 COLLATE string_compare, c2);
+ }
+ db close
+ sqlite3 db test.db
+ expr 0
+} 0
+do_test collate3-2.1 {
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY 1 COLLATE string_compare;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.2 {
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY c1;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.3 {
+ catchsql {
+ SELECT * FROM collate3t1 WHERE c1 = 'xxx';
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.4 {
+ catchsql {
+ CREATE TABLE collate3t2(c1 COLLATE string_compare);
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.5 {
+ catchsql {
+ CREATE INDEX collate3t1_i1 ON collate3t1(c1);
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.6 {
+ catchsql {
+ SELECT * FROM collate3t1;
+ }
+} {0 {}}
+do_test collate3-2.7.1 {
+ catchsql {
+ SELECT count(*) FROM collate3t1 GROUP BY c1;
+ }
+} {1 {no such collation sequence: string_compare}}
+# do_test collate3-2.7.2 {
+# catchsql {
+# SELECT * FROM collate3t1 GROUP BY c1;
+# }
+# } {1 {GROUP BY may only be used on aggregate queries}}
+do_test collate3-2.7.2 {
+ catchsql {
+ SELECT * FROM collate3t1 GROUP BY c1;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-2.8 {
+ catchsql {
+ SELECT DISTINCT c1 FROM collate3t1;
+ }
+} {1 {no such collation sequence: string_compare}}
+
+ifcapable compound {
+ do_test collate3-2.9 {
+ catchsql {
+ SELECT c1 FROM collate3t1 UNION SELECT c1 FROM collate3t1;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.10 {
+ catchsql {
+ SELECT c1 FROM collate3t1 EXCEPT SELECT c1 FROM collate3t1;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.11 {
+ catchsql {
+ SELECT c1 FROM collate3t1 INTERSECT SELECT c1 FROM collate3t1;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.12 {
+ catchsql {
+ SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1;
+ }
+ } {0 {}}
+ do_test collate3-2.13 {
+ catchsql {
+ SELECT 10 UNION ALL SELECT 20 ORDER BY 1 COLLATE string_compare;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.14 {
+ catchsql {
+ SELECT 10 INTERSECT SELECT 20 ORDER BY 1 COLLATE string_compare;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.15 {
+ catchsql {
+ SELECT 10 EXCEPT SELECT 20 ORDER BY 1 COLLATE string_compare;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.16 {
+ catchsql {
+ SELECT 10 UNION SELECT 20 ORDER BY 1 COLLATE string_compare;
+ }
+ } {1 {no such collation sequence: string_compare}}
+ do_test collate3-2.17 {
+ catchsql {
+ SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1 ORDER BY 1;
+ }
+ } {1 {no such collation sequence: string_compare}}
+} ;# ifcapable compound
+
+#
+# Create an index that uses a collation sequence then close and
+# re-open the database without re-registering the collation
+# sequence. Then check that for the table with the index
+# * An INSERT fails,
+# * An UPDATE on the column with the index fails,
+# * An UPDATE on a different column succeeds.
+# * A DELETE with a WHERE clause fails
+# * A DELETE without a WHERE clause succeeds
+#
+# Also, ensure that the restrictions tested by collate3-2.* still
+# apply after the index has been created.
+#
+do_test collate3-3.0 {
+ db collate string_compare {string compare}
+ execsql {
+ CREATE INDEX collate3t1_i1 ON collate3t1(c1);
+ INSERT INTO collate3t1 VALUES('xxx', 'yyy');
+ }
+ db close
+ sqlite3 db test.db
+ expr 0
+} 0
+db eval {select * from collate3t1}
+do_test collate3-3.1 {
+ catchsql {
+ INSERT INTO collate3t1 VALUES('xxx', 0);
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.2 {
+ catchsql {
+ UPDATE collate3t1 SET c1 = 'xxx';
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.3 {
+ catchsql {
+ UPDATE collate3t1 SET c2 = 'xxx';
+ }
+} {0 {}}
+do_test collate3-3.4 {
+ catchsql {
+ DELETE FROM collate3t1 WHERE 1;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.5 {
+ catchsql {
+ SELECT * FROM collate3t1;
+ }
+} {0 {xxx xxx}}
+do_test collate3-3.6 {
+ catchsql {
+ DELETE FROM collate3t1;
+ }
+} {0 {}}
+ifcapable {integrityck} {
+ do_test collate3-3.8 {
+ catchsql {
+ PRAGMA integrity_check
+ }
+ } {1 {no such collation sequence: string_compare}}
+}
+do_test collate3-3.9 {
+ catchsql {
+ SELECT * FROM collate3t1;
+ }
+} {0 {}}
+do_test collate3-3.10 {
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY 1 COLLATE string_compare;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.11 {
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY c1;
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.12 {
+ catchsql {
+ SELECT * FROM collate3t1 WHERE c1 = 'xxx';
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.13 {
+ catchsql {
+ CREATE TABLE collate3t2(c1 COLLATE string_compare);
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.14 {
+ catchsql {
+ CREATE INDEX collate3t1_i2 ON collate3t1(c1);
+ }
+} {1 {no such collation sequence: string_compare}}
+do_test collate3-3.15 {
+ execsql {
+ DROP TABLE collate3t1;
+ }
+} {}
+
+# Check we can create an index that uses an explicit collation
+# sequence and then close and re-open the database.
+do_test collate3-4.6 {
+ db collate user_defined "string compare"
+ execsql {
+ CREATE TABLE collate3t1(a, b);
+ INSERT INTO collate3t1 VALUES('hello', NULL);
+ CREATE INDEX collate3i1 ON collate3t1(a COLLATE user_defined);
+ }
+} {}
+do_test collate3-4.7 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY a COLLATE user_defined;
+ }
+} {1 {no such collation sequence: user_defined}}
+do_test collate3-4.8 {
+ db collate user_defined "string compare"
+ catchsql {
+ SELECT * FROM collate3t1 ORDER BY a COLLATE user_defined;
+ }
+} {0 {hello {}}}
+do_test collate3-4.8 {
+ db close
+ lindex [catch {
+ sqlite3 db test.db
+ }] 0
+} {0}
+do_test collate3-4.8 {
+ execsql {
+ DROP TABLE collate3t1;
+ }
+} {}
+
+# Compare strings as numbers.
+proc numeric_compare {lhs rhs} {
+ if {$rhs > $lhs} {
+ set res -1
+ } else {
+ set res [expr ($lhs > $rhs)?1:0]
+ }
+ return $res
+}
+
+# Check we can create a view that uses an explicit collation
+# sequence and then close and re-open the database.
+ifcapable view {
+do_test collate3-4.9 {
+ db collate user_defined numeric_compare
+ execsql {
+ CREATE TABLE collate3t1(a, b);
+ INSERT INTO collate3t1 VALUES('2', NULL);
+ INSERT INTO collate3t1 VALUES('101', NULL);
+ INSERT INTO collate3t1 VALUES('12', NULL);
+ CREATE VIEW collate3v1 AS SELECT * FROM collate3t1
+ ORDER BY 1 COLLATE user_defined;
+ SELECT * FROM collate3v1;
+ }
+} {2 {} 12 {} 101 {}}
+do_test collate3-4.10 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM collate3v1;
+ }
+} {1 {no such collation sequence: user_defined}}
+do_test collate3-4.11 {
+ db collate user_defined numeric_compare
+ catchsql {
+ SELECT * FROM collate3v1;
+ }
+} {0 {2 {} 12 {} 101 {}}}
+do_test collate3-4.12 {
+ execsql {
+ DROP TABLE collate3t1;
+ }
+} {}
+} ;# ifcapable view
+
+#
+# Test the collation factory. In the code, the "no such collation sequence"
+# message is only generated in two places. So these tests just test that
+# the collation factory can be called once from each of those points.
+#
+do_test collate3-5.0 {
+ catchsql {
+ CREATE TABLE collate3t1(a);
+ INSERT INTO collate3t1 VALUES(10);
+ SELECT a FROM collate3t1 ORDER BY 1 COLLATE unk;
+ }
+} {1 {no such collation sequence: unk}}
+do_test collate3-5.1 {
+ set ::cfact_cnt 0
+ proc cfact {nm} {
+ db collate $nm {string compare}
+ incr ::cfact_cnt
+ }
+ db collation_needed cfact
+} {}
+do_test collate3-5.2 {
+ catchsql {
+ SELECT a FROM collate3t1 ORDER BY 1 COLLATE unk;
+ }
+} {0 10}
+do_test collate3-5.3 {
+ set ::cfact_cnt
+} {1}
+do_test collate3-5.4 {
+ catchsql {
+ SELECT a FROM collate3t1 ORDER BY 1 COLLATE unk;
+ }
+} {0 10}
+do_test collate3-5.5 {
+ set ::cfact_cnt
+} {1}
+do_test collate3-5.6 {
+ catchsql {
+ SELECT a FROM collate3t1 ORDER BY 1 COLLATE unk;
+ }
+} {0 10}
+do_test collate3-5.7 {
+ execsql {
+ DROP TABLE collate3t1;
+ CREATE TABLE collate3t1(a COLLATE unk);
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT a FROM collate3t1 ORDER BY 1;
+ }
+} {1 {no such collation sequence: unk}}
+do_test collate3-5.8 {
+ set ::cfact_cnt 0
+ proc cfact {nm} {
+ db collate $nm {string compare}
+ incr ::cfact_cnt
+ }
+ db collation_needed cfact
+ catchsql {
+ SELECT a FROM collate3t1 ORDER BY 1;
+ }
+} {0 {}}
+
+do_test collate3-5.9 {
+ execsql {
+ DROP TABLE collate3t1;
+ }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/collate4.test b/third_party/sqlite/test/collate4.test
new file mode 100755
index 0000000..4db4c31
--- /dev/null
+++ b/third_party/sqlite/test/collate4.test
@@ -0,0 +1,700 @@
+#
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: collate4.test,v 1.9 2008/01/05 17:39:30 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db collate TEXT text_collate
+proc text_collate {a b} {
+ return [string compare $a $b]
+}
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# This procedure executes the SQL. Then it checks the generated program
+# for the SQL and appends a "nosort" to the result if the program contains the
+# SortCallback opcode. If the program does not contain the SortCallback
+# opcode it appends "sort"
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+#
+# Test cases are organized roughly as follows:
+#
+# collate4-1.* ORDER BY.
+# collate4-2.* WHERE clauses.
+# collate4-3.* constraints (primary key, unique).
+# collate4-4.* simple min() or max() queries.
+# collate4-5.* REINDEX command
+# collate4-6.* INTEGER PRIMARY KEY indices.
+#
+
+#
+# These tests - collate4-1.* - check that indices are correctly
+# selected or not selected to implement ORDER BY clauses when
+# user defined collation sequences are involved.
+#
+# Because these tests also exercise all the different ways indices
+# can be created, they also serve to verify that indices are correctly
+# initialised with user-defined collation sequences when they are
+# created.
+#
+# Tests named collate4-1.1.* use indices with a single column. Tests
+# collate4-1.2.* use indices with two columns.
+#
+do_test collate4-1.1.0 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE NOCASE, b COLLATE TEXT);
+ INSERT INTO collate4t1 VALUES( 'a', 'a' );
+ INSERT INTO collate4t1 VALUES( 'b', 'b' );
+ INSERT INTO collate4t1 VALUES( NULL, NULL );
+ INSERT INTO collate4t1 VALUES( 'B', 'B' );
+ INSERT INTO collate4t1 VALUES( 'A', 'A' );
+ CREATE INDEX collate4i1 ON collate4t1(a);
+ CREATE INDEX collate4i2 ON collate4t1(b);
+ }
+} {}
+do_test collate4-1.1.1 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a}
+} {{} a A b B nosort}
+do_test collate4-1.1.2 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE NOCASE}
+} {{} a A b B nosort}
+do_test collate4-1.1.3 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE TEXT}
+} {{} A B a b sort}
+do_test collate4-1.1.4 {
+ cksort {SELECT b FROM collate4t1 ORDER BY b}
+} {{} A B a b nosort}
+do_test collate4-1.1.5 {
+ cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE TEXT}
+} {{} A B a b nosort}
+do_test collate4-1.1.6 {
+ cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE}
+} {{} a A b B sort}
+
+do_test collate4-1.1.7 {
+ execsql {
+ CREATE TABLE collate4t2(
+ a PRIMARY KEY COLLATE NOCASE,
+ b UNIQUE COLLATE TEXT
+ );
+ INSERT INTO collate4t2 VALUES( 'a', 'a' );
+ INSERT INTO collate4t2 VALUES( NULL, NULL );
+ INSERT INTO collate4t2 VALUES( 'B', 'B' );
+ }
+} {}
+do_test collate4-1.1.8 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a}
+} {{} a B nosort}
+do_test collate4-1.1.9 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE NOCASE}
+} {{} a B nosort}
+do_test collate4-1.1.10 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE TEXT}
+} {{} B a sort}
+do_test collate4-1.1.11 {
+ cksort {SELECT b FROM collate4t2 ORDER BY b}
+} {{} B a nosort}
+do_test collate4-1.1.12 {
+ cksort {SELECT b FROM collate4t2 ORDER BY b COLLATE TEXT}
+} {{} B a nosort}
+do_test collate4-1.1.13 {
+ cksort {SELECT b FROM collate4t2 ORDER BY b COLLATE NOCASE}
+} {{} a B sort}
+
+do_test collate4-1.1.14 {
+ execsql {
+ CREATE TABLE collate4t3(
+ b COLLATE TEXT,
+ a COLLATE NOCASE,
+ UNIQUE(a), PRIMARY KEY(b)
+ );
+ INSERT INTO collate4t3 VALUES( 'a', 'a' );
+ INSERT INTO collate4t3 VALUES( NULL, NULL );
+ INSERT INTO collate4t3 VALUES( 'B', 'B' );
+ }
+} {}
+do_test collate4-1.1.15 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a}
+} {{} a B nosort}
+do_test collate4-1.1.16 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE NOCASE}
+} {{} a B nosort}
+do_test collate4-1.1.17 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE TEXT}
+} {{} B a sort}
+do_test collate4-1.1.18 {
+ cksort {SELECT b FROM collate4t3 ORDER BY b}
+} {{} B a nosort}
+do_test collate4-1.1.19 {
+ cksort {SELECT b FROM collate4t3 ORDER BY b COLLATE TEXT}
+} {{} B a nosort}
+do_test collate4-1.1.20 {
+ cksort {SELECT b FROM collate4t3 ORDER BY b COLLATE NOCASE}
+} {{} a B sort}
+
+do_test collate4-1.1.21 {
+ execsql {
+ CREATE TABLE collate4t4(a COLLATE NOCASE, b COLLATE TEXT);
+ INSERT INTO collate4t4 VALUES( 'a', 'a' );
+ INSERT INTO collate4t4 VALUES( 'b', 'b' );
+ INSERT INTO collate4t4 VALUES( NULL, NULL );
+ INSERT INTO collate4t4 VALUES( 'B', 'B' );
+ INSERT INTO collate4t4 VALUES( 'A', 'A' );
+ CREATE INDEX collate4i3 ON collate4t4(a COLLATE TEXT);
+ CREATE INDEX collate4i4 ON collate4t4(b COLLATE NOCASE);
+ }
+} {}
+do_test collate4-1.1.22 {
+ cksort {SELECT a FROM collate4t4 ORDER BY a}
+} {{} a A b B sort}
+do_test collate4-1.1.23 {
+ cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE}
+} {{} a A b B sort}
+do_test collate4-1.1.24 {
+ cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT}
+} {{} A B a b nosort}
+do_test collate4-1.1.25 {
+ cksort {SELECT b FROM collate4t4 ORDER BY b}
+} {{} A B a b sort}
+do_test collate4-1.1.26 {
+ cksort {SELECT b FROM collate4t4 ORDER BY b COLLATE TEXT}
+} {{} A B a b sort}
+do_test collate4-1.1.27 {
+ cksort {SELECT b FROM collate4t4 ORDER BY b COLLATE NOCASE}
+} {{} a A b B nosort}
+
+do_test collate4-1.1.30 {
+ execsql {
+ DROP TABLE collate4t1;
+ DROP TABLE collate4t2;
+ DROP TABLE collate4t3;
+ DROP TABLE collate4t4;
+ }
+} {}
+
+do_test collate4-1.2.0 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE NOCASE, b COLLATE TEXT);
+ INSERT INTO collate4t1 VALUES( 'a', 'a' );
+ INSERT INTO collate4t1 VALUES( 'b', 'b' );
+ INSERT INTO collate4t1 VALUES( NULL, NULL );
+ INSERT INTO collate4t1 VALUES( 'B', 'B' );
+ INSERT INTO collate4t1 VALUES( 'A', 'A' );
+ CREATE INDEX collate4i1 ON collate4t1(a, b);
+ }
+} {}
+do_test collate4-1.2.1 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a}
+} {{} A a B b nosort}
+do_test collate4-1.2.2 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE nocase}
+} {{} A a B b nosort}
+do_test collate4-1.2.3 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE text}
+} {{} A B a b sort}
+do_test collate4-1.2.4 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a, b}
+} {{} A a B b nosort}
+do_test collate4-1.2.5 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase}
+} {{} a A b B sort}
+do_test collate4-1.2.6 {
+ cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE text}
+} {{} A a B b nosort}
+
+do_test collate4-1.2.7 {
+ execsql {
+ CREATE TABLE collate4t2(
+ a COLLATE NOCASE,
+ b COLLATE TEXT,
+ PRIMARY KEY(a, b)
+ );
+ INSERT INTO collate4t2 VALUES( 'a', 'a' );
+ INSERT INTO collate4t2 VALUES( NULL, NULL );
+ INSERT INTO collate4t2 VALUES( 'B', 'B' );
+ }
+} {}
+do_test collate4-1.2.8 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a}
+} {{} a B nosort}
+do_test collate4-1.2.9 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE nocase}
+} {{} a B nosort}
+do_test collate4-1.2.10 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE text}
+} {{} B a sort}
+do_test collate4-1.2.11 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a, b}
+} {{} a B nosort}
+do_test collate4-1.2.12 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a, b COLLATE nocase}
+} {{} a B sort}
+do_test collate4-1.2.13 {
+ cksort {SELECT a FROM collate4t2 ORDER BY a, b COLLATE text}
+} {{} a B nosort}
+
+do_test collate4-1.2.14 {
+ execsql {
+ CREATE TABLE collate4t3(a COLLATE NOCASE, b COLLATE TEXT);
+ INSERT INTO collate4t3 VALUES( 'a', 'a' );
+ INSERT INTO collate4t3 VALUES( 'b', 'b' );
+ INSERT INTO collate4t3 VALUES( NULL, NULL );
+ INSERT INTO collate4t3 VALUES( 'B', 'B' );
+ INSERT INTO collate4t3 VALUES( 'A', 'A' );
+ CREATE INDEX collate4i2 ON collate4t3(a COLLATE TEXT, b COLLATE NOCASE);
+ }
+} {}
+do_test collate4-1.2.15 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a}
+} {{} a A b B sort}
+do_test collate4-1.2.16 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase}
+} {{} a A b B sort}
+do_test collate4-1.2.17 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text}
+} {{} A B a b nosort}
+do_test collate4-1.2.18 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b}
+} {{} A B a b sort}
+do_test collate4-1.2.19 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b COLLATE nocase}
+} {{} A B a b nosort}
+do_test collate4-1.2.20 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b COLLATE text}
+} {{} A B a b sort}
+do_test collate4-1.2.21 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text DESC}
+} {b a B A {} nosort}
+do_test collate4-1.2.22 {
+ cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text DESC, b}
+} {b a B A {} sort}
+do_test collate4-1.2.23 {
+ cksort {SELECT a FROM collate4t3
+ ORDER BY a COLLATE text DESC, b COLLATE nocase}
+} {b a B A {} sort}
+do_test collate4-1.2.24 {
+ cksort {SELECT a FROM collate4t3
+ ORDER BY a COLLATE text DESC, b COLLATE nocase DESC}
+} {b a B A {} nosort}
+
+do_test collate4-1.2.25 {
+ execsql {
+ DROP TABLE collate4t1;
+ DROP TABLE collate4t2;
+ DROP TABLE collate4t3;
+ }
+} {}
+
+#
+# These tests - collate4-2.* - check that indices are correctly
+# selected or not selected to implement WHERE clauses when user
+# defined collation sequences are involved.
+#
+# Indices may optimise WHERE clauses using <, >, <=, >=, = or IN
+# operators.
+#
+do_test collate4-2.1.0 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE NOCASE);
+ CREATE TABLE collate4t2(b COLLATE TEXT);
+
+ INSERT INTO collate4t1 VALUES('a');
+ INSERT INTO collate4t1 VALUES('A');
+ INSERT INTO collate4t1 VALUES('b');
+ INSERT INTO collate4t1 VALUES('B');
+ INSERT INTO collate4t1 VALUES('c');
+ INSERT INTO collate4t1 VALUES('C');
+ INSERT INTO collate4t1 VALUES('d');
+ INSERT INTO collate4t1 VALUES('D');
+ INSERT INTO collate4t1 VALUES('e');
+ INSERT INTO collate4t1 VALUES('D');
+
+ INSERT INTO collate4t2 VALUES('A');
+ INSERT INTO collate4t2 VALUES('Z');
+ }
+} {}
+do_test collate4-2.1.1 {
+ count {
+ SELECT * FROM collate4t2, collate4t1 WHERE a = b;
+ }
+} {A a A A 19}
+do_test collate4-2.1.2 {
+ execsql {
+ CREATE INDEX collate4i1 ON collate4t1(a);
+ }
+ count {
+ SELECT * FROM collate4t2, collate4t1 WHERE a = b;
+ }
+} {A a A A 5}
+do_test collate4-2.1.3 {
+ count {
+ SELECT * FROM collate4t2, collate4t1 WHERE b = a;
+ }
+} {A A 19}
+do_test collate4-2.1.4 {
+ execsql {
+ DROP INDEX collate4i1;
+ CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT);
+ }
+ count {
+ SELECT * FROM collate4t2, collate4t1 WHERE a = b;
+ }
+} {A a A A 19}
+do_test collate4-2.1.5 {
+ count {
+ SELECT * FROM collate4t2, collate4t1 WHERE b = a;
+ }
+} {A A 4}
+ifcapable subquery {
+ do_test collate4-2.1.6 {
+ count {
+ SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
+ }
+ } {a A 10}
+ do_test collate4-2.1.7 {
+ execsql {
+ DROP INDEX collate4i1;
+ CREATE INDEX collate4i1 ON collate4t1(a);
+ }
+ count {
+ SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
+ }
+ } {a A 6}
+ do_test collate4-2.1.8 {
+ count {
+ SELECT a FROM collate4t1 WHERE a IN ('z', 'a');
+ }
+ } {a A 5}
+ do_test collate4-2.1.9 {
+ execsql {
+ DROP INDEX collate4i1;
+ CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT);
+ }
+ count {
+ SELECT a FROM collate4t1 WHERE a IN ('z', 'a');
+ }
+ } {a A 9}
+}
+do_test collate4-2.1.10 {
+ execsql {
+ DROP TABLE collate4t1;
+ DROP TABLE collate4t2;
+ }
+} {}
+
+do_test collate4-2.2.0 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE nocase, b COLLATE text, c);
+ CREATE TABLE collate4t2(a COLLATE nocase, b COLLATE text, c COLLATE TEXT);
+
+ INSERT INTO collate4t1 VALUES('0', '0', '0');
+ INSERT INTO collate4t1 VALUES('0', '0', '1');
+ INSERT INTO collate4t1 VALUES('0', '1', '0');
+ INSERT INTO collate4t1 VALUES('0', '1', '1');
+ INSERT INTO collate4t1 VALUES('1', '0', '0');
+ INSERT INTO collate4t1 VALUES('1', '0', '1');
+ INSERT INTO collate4t1 VALUES('1', '1', '0');
+ INSERT INTO collate4t1 VALUES('1', '1', '1');
+ insert into collate4t2 SELECT * FROM collate4t1;
+ }
+} {}
+do_test collate4-2.2.1 {
+ count {
+ SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
+ }
+} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 63}
+do_test collate4-2.2.1b {
+ execsql {
+ CREATE INDEX collate4i1 ON collate4t1(a, b, c);
+ }
+ count {
+ SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
+ }
+} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 29}
+do_test collate4-2.2.2 {
+ execsql {
+ DROP INDEX collate4i1;
+ CREATE INDEX collate4i1 ON collate4t1(a, b, c COLLATE text);
+ }
+ count {
+ SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
+ }
+} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 22}
+
+do_test collate4-2.2.10 {
+ execsql {
+ DROP TABLE collate4t1;
+ DROP TABLE collate4t2;
+ }
+} {}
+
+#
+# These tests - collate4-3.* verify that indices that implement
+# UNIQUE and PRIMARY KEY constraints operate correctly with user
+# defined collation sequences.
+#
+do_test collate4-3.0 {
+ execsql {
+ CREATE TABLE collate4t1(a PRIMARY KEY COLLATE NOCASE);
+ }
+} {}
+do_test collate4-3.1 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES('abc');
+ INSERT INTO collate4t1 VALUES('ABC');
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.2 {
+ execsql {
+ SELECT * FROM collate4t1;
+ }
+} {abc}
+do_test collate4-3.3 {
+ catchsql {
+ INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.4 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES(1);
+ UPDATE collate4t1 SET a = 'abc';
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.5 {
+ execsql {
+ DROP TABLE collate4t1;
+ CREATE TABLE collate4t1(a COLLATE NOCASE UNIQUE);
+ }
+} {}
+do_test collate4-3.6 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES('abc');
+ INSERT INTO collate4t1 VALUES('ABC');
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.7 {
+ execsql {
+ SELECT * FROM collate4t1;
+ }
+} {abc}
+do_test collate4-3.8 {
+ catchsql {
+ INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.9 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES(1);
+ UPDATE collate4t1 SET a = 'abc';
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.10 {
+ execsql {
+ DROP TABLE collate4t1;
+ CREATE TABLE collate4t1(a);
+ CREATE UNIQUE INDEX collate4i1 ON collate4t1(a COLLATE NOCASE);
+ }
+} {}
+do_test collate4-3.11 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES('abc');
+ INSERT INTO collate4t1 VALUES('ABC');
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.12 {
+ execsql {
+ SELECT * FROM collate4t1;
+ }
+} {abc}
+do_test collate4-3.13 {
+ catchsql {
+ INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
+ }
+} {1 {column a is not unique}}
+do_test collate4-3.14 {
+ catchsql {
+ INSERT INTO collate4t1 VALUES(1);
+ UPDATE collate4t1 SET a = 'abc';
+ }
+} {1 {column a is not unique}}
+
+do_test collate4-3.15 {
+ execsql {
+ DROP TABLE collate4t1;
+ }
+} {}
+
+# Mimic the SQLite 2 collation type NUMERIC.
+db collate numeric numeric_collate
+proc numeric_collate {lhs rhs} {
+ if {$lhs == $rhs} {return 0}
+ return [expr ($lhs>$rhs)?1:-1]
+}
+
+#
+# These tests - collate4-4.* check that min() and max() only ever
+# use indices constructed with built-in collation type numeric.
+#
+# CHANGED: min() and max() now use the collation type. If there
+# is an indice that can be used, it is used.
+#
+do_test collate4-4.0 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE TEXT);
+ INSERT INTO collate4t1 VALUES('2');
+ INSERT INTO collate4t1 VALUES('10');
+ INSERT INTO collate4t1 VALUES('20');
+ INSERT INTO collate4t1 VALUES('104');
+ }
+} {}
+do_test collate4-4.1 {
+ count {
+ SELECT max(a) FROM collate4t1
+ }
+} {20 3}
+do_test collate4-4.2 {
+ count {
+ SELECT min(a) FROM collate4t1
+ }
+} {10 3}
+do_test collate4-4.3 {
+ # Test that the index with collation type TEXT is used.
+ execsql {
+ CREATE INDEX collate4i1 ON collate4t1(a);
+ }
+ count {
+ SELECT min(a) FROM collate4t1;
+ }
+} {10 1}
+do_test collate4-4.4 {
+ count {
+ SELECT max(a) FROM collate4t1;
+ }
+} {20 0}
+do_test collate4-4.5 {
+ # Test that the index with collation type NUMERIC is not used.
+ execsql {
+ DROP INDEX collate4i1;
+ CREATE INDEX collate4i1 ON collate4t1(a COLLATE NUMERIC);
+ }
+ count {
+ SELECT min(a) FROM collate4t1;
+ }
+} {10 3}
+do_test collate4-4.6 {
+ count {
+ SELECT max(a) FROM collate4t1;
+ }
+} {20 3}
+do_test collate4-4.7 {
+ execsql {
+ DROP TABLE collate4t1;
+ }
+} {}
+
+# Also test the scalar min() and max() functions.
+#
+do_test collate4-4.8 {
+ execsql {
+ CREATE TABLE collate4t1(a COLLATE TEXT, b COLLATE NUMERIC);
+ INSERT INTO collate4t1 VALUES('11', '101');
+ INSERT INTO collate4t1 VALUES('101', '11')
+ }
+} {}
+do_test collate4-4.9 {
+ execsql {
+ SELECT max(a, b) FROM collate4t1;
+ }
+} {11 11}
+do_test collate4-4.10 {
+ execsql {
+ SELECT max(b, a) FROM collate4t1;
+ }
+} {101 101}
+do_test collate4-4.11 {
+ execsql {
+ SELECT max(a, '101') FROM collate4t1;
+ }
+} {11 101}
+do_test collate4-4.12 {
+ execsql {
+ SELECT max('101', a) FROM collate4t1;
+ }
+} {11 101}
+do_test collate4-4.13 {
+ execsql {
+ SELECT max(b, '101') FROM collate4t1;
+ }
+} {101 101}
+do_test collate4-4.14 {
+ execsql {
+ SELECT max('101', b) FROM collate4t1;
+ }
+} {101 101}
+
+do_test collate4-4.15 {
+ execsql {
+ DROP TABLE collate4t1;
+ }
+} {}
+
+#
+# These tests - collate4.6.* - ensure that implict INTEGER PRIMARY KEY
+# indices do not confuse collation sequences.
+#
+# These indices are never used for sorting in SQLite. And you can't
+# create another index on an INTEGER PRIMARY KEY column, so we don't have
+# to test that.
+# (Revised 2004-Nov-22): The ROWID can be used for sorting now.
+#
+do_test collate4-6.0 {
+ execsql {
+ CREATE TABLE collate4t1(a INTEGER PRIMARY KEY);
+ INSERT INTO collate4t1 VALUES(101);
+ INSERT INTO collate4t1 VALUES(10);
+ INSERT INTO collate4t1 VALUES(15);
+ }
+} {}
+do_test collate4-6.1 {
+ cksort {
+ SELECT * FROM collate4t1 ORDER BY 1;
+ }
+} {10 15 101 nosort}
+do_test collate4-6.2 {
+ cksort {
+ SELECT * FROM collate4t1 ORDER BY oid;
+ }
+} {10 15 101 nosort}
+do_test collate4-6.3 {
+ cksort {
+ SELECT * FROM collate4t1 ORDER BY oid||'' COLLATE TEXT;
+ }
+} {10 101 15 sort}
+
+finish_test
diff --git a/third_party/sqlite/test/collate5.test b/third_party/sqlite/test/collate5.test
new file mode 100755
index 0000000..b8dc581
--- /dev/null
+++ b/third_party/sqlite/test/collate5.test
@@ -0,0 +1,270 @@
+#
+# 2001 September 15
+#
+# 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 file is testing DISTINCT, UNION, INTERSECT and EXCEPT
+# SELECT statements that use user-defined collation sequences. Also
+# GROUP BY clauses that use user-defined collation sequences.
+#
+# $Id: collate5.test,v 1.6 2007/11/12 15:29:19 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+#
+# Tests are organised as follows:
+# collate5-1.* - DISTINCT
+# collate5-2.* - Compound SELECT
+# collate5-3.* - ORDER BY on compound SELECT
+# collate5-4.* - GROUP BY
+
+# Create the collation sequence 'TEXT', purely for asthetic reasons. The
+# test cases in this script could just as easily use BINARY.
+db collate TEXT [list string compare]
+
+# Mimic the SQLite 2 collation type NUMERIC.
+db collate numeric numeric_collate
+proc numeric_collate {lhs rhs} {
+ if {$lhs == $rhs} {return 0}
+ return [expr ($lhs>$rhs)?1:-1]
+}
+
+#
+# These tests - collate5-1.* - focus on the DISTINCT keyword.
+#
+do_test collate5-1.0 {
+ execsql {
+ CREATE TABLE collate5t1(a COLLATE nocase, b COLLATE text);
+
+ INSERT INTO collate5t1 VALUES('a', 'apple');
+ INSERT INTO collate5t1 VALUES('A', 'Apple');
+ INSERT INTO collate5t1 VALUES('b', 'banana');
+ INSERT INTO collate5t1 VALUES('B', 'banana');
+ INSERT INTO collate5t1 VALUES('n', NULL);
+ INSERT INTO collate5t1 VALUES('N', NULL);
+ }
+} {}
+do_test collate5-1.1 {
+ execsql {
+ SELECT DISTINCT a FROM collate5t1;
+ }
+} {A B N}
+do_test collate5-1.2 {
+ execsql {
+ SELECT DISTINCT b FROM collate5t1;
+ }
+} {{} Apple apple banana}
+do_test collate5-1.3 {
+ execsql {
+ SELECT DISTINCT a, b FROM collate5t1;
+ }
+} {A Apple a apple B banana N {}}
+
+# The remainder of this file tests compound SELECT statements.
+# Omit it if the library is compiled such that they are omitted.
+#
+ifcapable !compound {
+ finish_test
+ return
+}
+
+#
+# Tests named collate5-2.* focus on UNION, EXCEPT and INTERSECT
+# queries that use user-defined collation sequences.
+#
+# collate5-2.1.* - UNION
+# collate5-2.2.* - INTERSECT
+# collate5-2.3.* - EXCEPT
+#
+do_test collate5-2.0 {
+ execsql {
+ CREATE TABLE collate5t2(a COLLATE text, b COLLATE nocase);
+
+ INSERT INTO collate5t2 VALUES('a', 'apple');
+ INSERT INTO collate5t2 VALUES('A', 'apple');
+ INSERT INTO collate5t2 VALUES('b', 'banana');
+ INSERT INTO collate5t2 VALUES('B', 'Banana');
+ }
+} {}
+
+do_test collate5-2.1.1 {
+ execsql {
+ SELECT a FROM collate5t1 UNION select a FROM collate5t2;
+ }
+} {A B N}
+do_test collate5-2.1.2 {
+ execsql {
+ SELECT a FROM collate5t2 UNION select a FROM collate5t1;
+ }
+} {A B N a b n}
+do_test collate5-2.1.3 {
+ execsql {
+ SELECT a, b FROM collate5t1 UNION select a, b FROM collate5t2;
+ }
+} {A Apple A apple B Banana b banana N {}}
+do_test collate5-2.1.4 {
+ execsql {
+ SELECT a, b FROM collate5t2 UNION select a, b FROM collate5t1;
+ }
+} {A Apple B banana N {} a apple b banana n {}}
+
+do_test collate5-2.2.1 {
+ execsql {
+ SELECT a FROM collate5t1 EXCEPT select a FROM collate5t2;
+ }
+} {N}
+do_test collate5-2.2.2 {
+ execsql {
+ SELECT a FROM collate5t2 EXCEPT select a FROM collate5t1 WHERE a != 'a';
+ }
+} {A a}
+do_test collate5-2.2.3 {
+ execsql {
+ SELECT a, b FROM collate5t1 EXCEPT select a, b FROM collate5t2;
+ }
+} {A Apple N {}}
+do_test collate5-2.2.4 {
+ execsql {
+ SELECT a, b FROM collate5t2 EXCEPT select a, b FROM collate5t1
+ where a != 'a';
+ }
+} {A apple a apple}
+
+do_test collate5-2.3.1 {
+ execsql {
+ SELECT a FROM collate5t1 INTERSECT select a FROM collate5t2;
+ }
+} {A B}
+do_test collate5-2.3.2 {
+ execsql {
+ SELECT a FROM collate5t2 INTERSECT select a FROM collate5t1 WHERE a != 'a';
+ }
+} {B b}
+do_test collate5-2.3.3 {
+ execsql {
+ SELECT a, b FROM collate5t1 INTERSECT select a, b FROM collate5t2;
+ }
+} {a apple B banana}
+do_test collate5-2.3.4 {
+ execsql {
+ SELECT a, b FROM collate5t2 INTERSECT select a, b FROM collate5t1;
+ }
+} {A apple B Banana a apple b banana}
+
+#
+# This test ensures performs a UNION operation with a bunch of different
+# length records. The goal is to test that the logic that compares records
+# for the compound SELECT operators works with record lengths that lie
+# either side of the troublesome 256 and 65536 byte marks.
+#
+set ::lens [list \
+ 0 1 2 3 4 5 6 7 8 9 \
+ 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 \
+ 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 \
+ 65520 65521 65522 65523 65524 65525 65526 65527 65528 65529 65530 \
+ 65531 65532 65533 65534 65535 65536 65537 65538 65539 65540 65541 \
+ 65542 65543 65544 65545 65546 65547 65548 65549 65550 65551 ]
+do_test collate5-2.4.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE collate5t3(a, b);
+ }
+ foreach ii $::lens {
+ execsql "INSERT INTO collate5t3 VALUES($ii, '[string repeat a $ii]');"
+ }
+ expr [llength [execsql {
+ COMMIT;
+ SELECT * FROM collate5t3 UNION SELECT * FROM collate5t3;
+ }]] / 2
+} [llength $::lens]
+do_test collate5-2.4.1 {
+ execsql {DROP TABLE collate5t3;}
+} {}
+unset ::lens
+
+#
+# These tests - collate5-3.* - focus on compound SELECT queries that
+# feature ORDER BY clauses.
+#
+do_test collate5-3.0 {
+ execsql {
+ SELECT a FROM collate5t1 UNION ALL SELECT a FROM collate5t2 ORDER BY 1;
+ }
+} {a A a A b B b B n N}
+do_test collate5-3.1 {
+ execsql {
+ SELECT a FROM collate5t2 UNION ALL SELECT a FROM collate5t1 ORDER BY 1;
+ }
+} {A A B B N a a b b n}
+do_test collate5-3.2 {
+ execsql {
+ SELECT a FROM collate5t1 UNION ALL SELECT a FROM collate5t2
+ ORDER BY 1 COLLATE TEXT;
+ }
+} {A A B B N a a b b n}
+
+do_test collate5-3.3 {
+ execsql {
+ CREATE TABLE collate5t_cn(a COLLATE NUMERIC);
+ CREATE TABLE collate5t_ct(a COLLATE TEXT);
+ INSERT INTO collate5t_cn VALUES('1');
+ INSERT INTO collate5t_cn VALUES('11');
+ INSERT INTO collate5t_cn VALUES('101');
+ INSERT INTO collate5t_ct SELECT * FROM collate5t_cn;
+ }
+} {}
+do_test collate5-3.4 {
+ execsql {
+ SELECT a FROM collate5t_cn INTERSECT SELECT a FROM collate5t_ct ORDER BY 1;
+ }
+} {1 11 101}
+do_test collate5-3.5 {
+ execsql {
+ SELECT a FROM collate5t_ct INTERSECT SELECT a FROM collate5t_cn ORDER BY 1;
+ }
+} {1 101 11}
+
+do_test collate5-3.20 {
+ execsql {
+ DROP TABLE collate5t_cn;
+ DROP TABLE collate5t_ct;
+ DROP TABLE collate5t1;
+ DROP TABLE collate5t2;
+ }
+} {}
+
+do_test collate5-4.0 {
+ execsql {
+ CREATE TABLE collate5t1(a COLLATE NOCASE, b COLLATE NUMERIC);
+ INSERT INTO collate5t1 VALUES('a', '1');
+ INSERT INTO collate5t1 VALUES('A', '1.0');
+ INSERT INTO collate5t1 VALUES('b', '2');
+ INSERT INTO collate5t1 VALUES('B', '3');
+ }
+} {}
+do_test collate5-4.1 {
+ string tolower [execsql {
+ SELECT a, count(*) FROM collate5t1 GROUP BY a;
+ }]
+} {a 2 b 2}
+do_test collate5-4.2 {
+ execsql {
+ SELECT a, b, count(*) FROM collate5t1 GROUP BY a, b ORDER BY a, b;
+ }
+} {A 1.0 2 b 2 1 B 3 1}
+do_test collate5-4.3 {
+ execsql {
+ DROP TABLE collate5t1;
+ }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/collate6.test b/third_party/sqlite/test/collate6.test
new file mode 100755
index 0000000..d238639
--- /dev/null
+++ b/third_party/sqlite/test/collate6.test
@@ -0,0 +1,153 @@
+#
+# 2001 September 15
+#
+# 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 collation sequences in concert with triggers.
+#
+# $Id: collate6.test,v 1.4 2007/07/30 14:40:48 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# There are no tests in this file that will work without
+# trigger support.
+#
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# Create a case-insensitive collation type NOCASE for use in testing.
+# Normally, capital letters are less than their lower-case counterparts.
+db collate NOCASE nocase_collate
+proc nocase_collate {a b} {
+ return [string compare -nocase $a $b]
+}
+
+#
+# Tests are organized as follows:
+# collate6-1.* - triggers.
+#
+
+do_test collate6-1.0 {
+ execsql {
+ CREATE TABLE collate6log(a, b);
+ CREATE TABLE collate6tab(a COLLATE NOCASE, b COLLATE BINARY);
+ }
+} {}
+
+# Test that the default collation sequence applies to new.* references
+# in WHEN clauses.
+do_test collate6-1.1 {
+ execsql {
+ CREATE TRIGGER collate6trig BEFORE INSERT ON collate6tab
+ WHEN new.a = 'a' BEGIN
+ INSERT INTO collate6log VALUES(new.a, new.b);
+ END;
+ }
+} {}
+do_test collate6-1.2 {
+ execsql {
+ INSERT INTO collate6tab VALUES('a', 'b');
+ SELECT * FROM collate6log;
+ }
+} {a b}
+do_test collate6-1.3 {
+ execsql {
+ INSERT INTO collate6tab VALUES('A', 'B');
+ SELECT * FROM collate6log;
+ }
+} {a b A B}
+do_test collate6-1.4 {
+ execsql {
+ DROP TRIGGER collate6trig;
+ DELETE FROM collate6log;
+ }
+} {}
+
+# Test that the default collation sequence applies to new.* references
+# in the body of triggers.
+do_test collate6-1.5 {
+ execsql {
+ CREATE TRIGGER collate6trig BEFORE INSERT ON collate6tab BEGIN
+ INSERT INTO collate6log VALUES(new.a='a', new.b='b');
+ END;
+ }
+} {}
+do_test collate6-1.6 {
+ execsql {
+ INSERT INTO collate6tab VALUES('a', 'b');
+ SELECT * FROM collate6log;
+ }
+} {1 1}
+do_test collate6-1.7 {
+ execsql {
+ INSERT INTO collate6tab VALUES('A', 'B');
+ SELECT * FROM collate6log;
+ }
+} {1 1 1 0}
+do_test collate6-1.8 {
+ execsql {
+ DROP TRIGGER collate6trig;
+ DELETE FROM collate6log;
+ }
+} {}
+
+do_test collate6-1.9 {
+ execsql {
+ DROP TABLE collate6tab;
+ }
+} {}
+
+# Test that an explicit collation sequence overrides an implicit
+# one attached to a 'new' reference.
+#
+do_test collate6-2.1 {
+ execsql {
+ CREATE TABLE abc(a COLLATE binary, b, c);
+ CREATE TABLE def(a, b, c);
+ CREATE TRIGGER abc_t1 AFTER INSERT ON abc BEGIN
+ INSERT INTO def SELECT * FROM abc WHERE a < new.a COLLATE nocase;
+ END
+ }
+} {}
+do_test collate6-2.2 {
+ execsql {
+ INSERT INTO abc VALUES('One', 'Two', 'Three');
+ INSERT INTO abc VALUES('one', 'two', 'three');
+ SELECT * FROM def;
+ }
+} {}
+do_test collate6-2.3 {
+ execsql {
+ UPDATE abc SET a = 'four' WHERE a = 'one';
+ CREATE TRIGGER abc_t2 AFTER UPDATE ON abc BEGIN
+ INSERT INTO def SELECT * FROM abc WHERE a < new.a COLLATE nocase;
+ END;
+ SELECT * FROM def;
+ }
+} {}
+
+# At one point the 6-3.2 (but not 6-3.1) was causing an assert() to fail.
+#
+do_test collate6-3.1 {
+ execsql {
+ SELECT 1 FROM sqlite_master WHERE name COLLATE nocase = 'hello';
+ }
+} {}
+do_test collate6-3.2 {
+ execsql {
+ SELECT 1 FROM sqlite_master WHERE 'hello' = name COLLATE nocase;
+ }
+} {}
+
+
+finish_test
diff --git a/third_party/sqlite/test/collate7.test b/third_party/sqlite/test/collate7.test
new file mode 100755
index 0000000..22b6731
--- /dev/null
+++ b/third_party/sqlite/test/collate7.test
@@ -0,0 +1,72 @@
+#
+# 2007 May 7
+#
+# 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 the experimental sqlite3_create_collation_v2()
+# API.
+#
+# $Id: collate7.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set ::caseless_del 0
+proc caseless_cmp {zLeft zRight} {
+ string compare -nocase $zLeft $zRight
+}
+
+do_test collate7-1.1 {
+ set cmd [list incr ::caseless_del]
+ sqlite3_create_collation_v2 db CASELESS caseless_cmp $cmd
+ set ::caseless_del
+} {0}
+do_test collate7-1.2 {
+ sqlite_delete_collation db CASELESS
+ set ::caseless_del
+} {1}
+do_test collate7-1.3 {
+ catchsql {
+ CREATE TABLE abc(a COLLATE CASELESS, b, c);
+ }
+} {1 {no such collation sequence: CASELESS}}
+do_test collate7-1.4 {
+ sqlite3_create_collation_v2 db CASELESS caseless_cmp {incr ::caseless_del}
+ db close
+ set ::caseless_del
+} {2}
+
+do_test collate7-2.1 {
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ sqlite3_create_collation_v2 db CASELESS caseless_cmp {incr ::caseless_del}
+ execsql {
+ PRAGMA encoding='utf-16';
+ CREATE TABLE abc16(a COLLATE CASELESS, b, c);
+ } db
+ set ::caseless_del
+} {2}
+do_test collate7-2.2 {
+ execsql {
+ SELECT * FROM abc16 WHERE a < 'abc';
+ }
+ set ::caseless_del
+} {2}
+do_test collate7-2.3 {
+ sqlite_delete_collation db CASELESS
+ set ::caseless_del
+} {3}
+do_test collate7-2.4 {
+ catchsql {
+ SELECT * FROM abc16 WHERE a < 'abc';
+ }
+} {1 {no such collation sequence: CASELESS}}
+
+finish_test
diff --git a/third_party/sqlite/test/collate8.test b/third_party/sqlite/test/collate8.test
new file mode 100755
index 0000000..3e90c38
--- /dev/null
+++ b/third_party/sqlite/test/collate8.test
@@ -0,0 +1,52 @@
+#
+# 2007 June 20
+#
+# 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 making sure collations pass through the
+# unary + operator.
+#
+# $Id: collate8.test,v 1.1 2007/06/20 16:13:23 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test collate8-1.1 {
+ execsql {
+ CREATE TABLE t1(a TEXT COLLATE nocase);
+ INSERT INTO t1 VALUES('aaa');
+ INSERT INTO t1 VALUES('BBB');
+ INSERT INTO t1 VALUES('ccc');
+ INSERT INTO t1 VALUES('DDD');
+ SELECT a FROM t1 ORDER BY a;
+ }
+} {aaa BBB ccc DDD}
+do_test collate8-1.2 {
+ execsql {
+ SELECT rowid FROM t1 WHERE a<'ccc' ORDER BY 1
+ }
+} {1 2}
+do_test collate8-1.3 {
+ execsql {
+ SELECT rowid FROM t1 WHERE a<'ccc' COLLATE binary ORDER BY 1
+ }
+} {1 2 4}
+do_test collate8-1.4 {
+ execsql {
+ SELECT rowid FROM t1 WHERE +a<'ccc' ORDER BY 1
+ }
+} {1 2}
+do_test collate8-1.5 {
+ execsql {
+ SELECT a FROM t1 ORDER BY +a
+ }
+} {aaa BBB ccc DDD}
+
+finish_test
diff --git a/third_party/sqlite/test/collate9.test b/third_party/sqlite/test/collate9.test
new file mode 100755
index 0000000..5a07773
--- /dev/null
+++ b/third_party/sqlite/test/collate9.test
@@ -0,0 +1,178 @@
+#
+# 2007 November 12
+#
+# 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 making sure that the names of collation
+# sequences may be quoted using double quotes in SQL statements.
+#
+# $Id: collate9.test,v 1.2 2008/07/10 00:32:42 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc reverse_sort {lhs rhs} {
+ return [string compare $rhs $lhs]
+}
+db collate "reverse sort" reverse_sort
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+# Test plan:
+#
+# collate9-1.* - Test collation sequences attached to table columns
+# collate9-2.* - Test collation sequences attached to expressions
+# collate9-3.* - Test collation sequences attached to an index
+# collate9-4.* - Test collation sequences as an argument to REINDEX
+#
+
+do_test collate9-1.1 {
+ execsql {
+ CREATE TABLE xy(x COLLATE "reverse sort", y COLLATE binary);
+ INSERT INTO xy VALUES('one', 'one');
+ INSERT INTO xy VALUES('two', 'two');
+ INSERT INTO xy VALUES('three', 'three');
+ }
+} {}
+do_test collate9-1.2 {
+ execsql {
+ SELECT x FROM xy ORDER BY x
+ }
+} {two three one}
+do_test collate9-1.3 {
+ execsql {
+ SELECT y FROM xy ORDER BY y
+ }
+} {one three two}
+do_test collate9-1.4 {
+ cksort {
+ SELECT x FROM xy ORDER BY x
+ }
+} {two three one sort}
+do_test collate9-1.5 {
+ execsql {
+ CREATE INDEX xy_i ON xy(x)
+ }
+} {}
+do_test collate9-1.6 {
+ cksort {
+ SELECT x FROM xy ORDER BY x
+ }
+} {two three one nosort}
+
+do_test collate9-2.1 {
+ execsql {
+ SELECT x, x < 'seven' FROM xy ORDER BY x
+ }
+} {two 1 three 1 one 0}
+do_test collate9-2.2 {
+ execsql {
+ SELECT y, y < 'seven' FROM xy ORDER BY x
+ }
+} {two 0 three 0 one 1}
+do_test collate9-2.3 {
+ execsql {
+ SELECT y, y COLLATE "reverse sort" < 'seven' FROM xy ORDER BY x
+ }
+} {two 1 three 1 one 0}
+do_test collate9-2.4 {
+ execsql {
+ SELECT y FROM xy ORDER BY y
+ }
+} {one three two}
+do_test collate9-2.5 {
+ execsql {
+ SELECT y FROM xy ORDER BY y COLLATE "reverse sort"
+ }
+} {two three one}
+do_test collate9-2.6 {
+ execsql {
+ SELECT y COLLATE "reverse sort" AS aaa FROM xy ORDER BY aaa
+ }
+} {two three one}
+
+do_test collate9-3.1 {
+ execsql {
+ CREATE INDEX xy_i2 ON xy(y COLLATE "reverse sort");
+ }
+} {}
+do_test collate9-3.2 {
+ cksort {
+ SELECT y FROM xy ORDER BY y
+ }
+} {one three two sort}
+do_test collate9-3.3 {
+ cksort {
+ SELECT y FROM xy ORDER BY y COLLATE "reverse sort"
+ }
+} {two three one nosort}
+do_test collate9-3.4 {
+ cksort {
+ SELECT y AS aaa FROM xy ORDER BY aaa
+ }
+} {one three two sort}
+do_test collate9-3.5 {
+ cksort {
+ SELECT y COLLATE "reverse sort" AS aaa FROM xy ORDER BY aaa
+ }
+} {two three one nosort}
+
+ifcapable reindex {
+ do_test collate9-4.1 {
+ execsql {
+ REINDEX "reverse sort"
+ }
+ } {}
+
+ # Modify the "reverse sort" collation so that it now sorts in the same
+ # order as binary.
+ proc reverse_sort {lhs rhs} {
+ return [string compare $lhs $rhs]
+ }
+
+ # The integrity check should now fail because the indexes created using
+ # "reverse sort" are no longer in sync with the collation sequence
+ # implementation.
+ do_test collate9-4.2 {
+ expr {"ok" eq [execsql { PRAGMA integrity_check }]}
+ } {0}
+
+ do_test collate9-4.3 {
+ execsql {
+ REINDEX "reverse sort"
+ }
+ } {}
+
+ # Integrity check should now pass.
+ do_test collate9-4.4 {
+ expr {"ok" eq [execsql { PRAGMA integrity_check }]}
+ } {1}
+
+ do_test collate9-4.5 {
+ cksort {
+ SELECT x FROM xy ORDER BY x COLLATE "reverse sort"
+ }
+ } {one three two nosort}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/collateA.test b/third_party/sqlite/test/collateA.test
new file mode 100755
index 0000000..3f7ebd9
--- /dev/null
+++ b/third_party/sqlite/test/collateA.test
@@ -0,0 +1,217 @@
+#
+# 2008 January 20
+#
+# 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 the built-in RTRIM collating
+# API.
+#
+# $Id: collateA.test,v 1.3 2008/04/15 04:02:41 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test collateA-1.1 {
+ execsql {
+ CREATE TABLE t1(
+ a INTEGER PRIMARY KEY,
+ b TEXT COLLATE BINARY,
+ c TEXT COLLATE RTRIM
+ );
+ INSERT INTO t1 VALUES(1, 'abcde','abcde');
+ INSERT INTO t1 VALUES(2, 'xyzzy ','xyzzy ');
+ INSERT INTO t1 VALUES(3, 'xyzzy ','xyzzy ');
+ INSERT INTO t1 VALUES(4, 'xyzzy ','xyzzy ');
+ INSERT INTO t1 VALUES(5, ' ', ' ');
+ INSERT INTO t1 VALUES(6, '', '');
+ SELECT count(*) FROM t1;
+ }
+} {6}
+do_test collateA-1.2 {
+ execsql {SELECT a FROM t1 WHERE b='abcde '}
+} {}
+do_test collateA-1.3 {
+ execsql {SELECT a FROM t1 WHERE c='abcde '}
+} {1}
+do_test collateA-1.4 {
+ execsql {SELECT a FROM t1 WHERE b='xyzzy'}
+} {}
+do_test collateA-1.5 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy'}
+} {2 3 4}
+do_test collateA-1.6 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-1.7 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-1.8 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-1.9 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-1.10 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-1.11 {
+ execsql {SELECT 'abc123'='abc123 ' COLLATE RTRIM;}
+} {1}
+do_test collateA-1.12 {
+ execsql {SELECT 'abc123 '='abc123' COLLATE RTRIM;}
+} {1}
+do_test collateA-1.13 {
+ execsql {SELECT ' '='' COLLATE RTRIM, ' '='' COLLATE BINARY, ' '=''}
+} {1 0 0}
+do_test collateA-1.14 {
+ execsql {SELECT ''=' ' COLLATE RTRIM, ''=' ' COLLATE BINARY, ''=' '}
+} {1 0 0}
+do_test collateA-1.15 {
+ execsql {SELECT ' '=' ' COLLATE RTRIM, ' '=' '}
+} {1 0}
+do_test collateA-1.16 {
+ execsql {SELECT ''<>' ' COLLATE RTRIM, ''<>' ' COLLATE BINARY, ''<>' '}
+} {0 1 1}
+do_test collateA-1.17 {
+ execsql {SELECT a FROM t1 WHERE c='xyzz'}
+} {}
+do_test collateA-1.18 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzyy '}
+} {}
+do_test collateA-1.19 {
+ execsql {SELECT a FROM t1 WHERE c='xyzz '}
+} {}
+do_test collateA-1.20 {
+ execsql {SELECT a FROM t1 WHERE c='abcd '}
+} {}
+do_test collateA-1.21 {
+ execsql {SELECT a FROM t1 WHERE c='abcd'}
+} {}
+do_test collateA-1.22 {
+ execsql {SELECT a FROM t1 WHERE c='abc'}
+} {}
+do_test collateA-1.23 {
+ execsql {SELECT a FROM t1 WHERE c='abcdef '}
+} {}
+do_test collateA-1.24 {
+ execsql {SELECT a FROM t1 WHERE c=''}
+} {5 6}
+do_test collateA-1.25 {
+ execsql {SELECT a FROM t1 WHERE c=' '}
+} {5 6}
+do_test collateA-1.26 {
+ execsql {SELECT a FROM t1 WHERE c=' '}
+} {5 6}
+
+
+do_test collateA-2.1 {
+ execsql {
+ CREATE INDEX i1b ON t1(b);
+ CREATE INDEX i1c ON t1(c);
+ PRAGMA integrity_check;
+ }
+} {ok}
+do_test collateA-2.2 {
+ execsql {SELECT a FROM t1 WHERE b='abcde '}
+} {}
+do_test collateA-2.3 {
+ execsql {SELECT a FROM t1 WHERE c='abcde '}
+} {1}
+do_test collateA-2.4 {
+ execsql {SELECT a FROM t1 WHERE b='xyzzy'}
+} {}
+do_test collateA-2.5 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy'}
+} {2 3 4}
+do_test collateA-2.6 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-2.7 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-2.8 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-2.9 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-2.10 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-2.17 {
+ execsql {SELECT a FROM t1 WHERE c='xyzz'}
+} {}
+do_test collateA-2.18 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzyy '}
+} {}
+do_test collateA-2.19 {
+ execsql {SELECT a FROM t1 WHERE c='xyzz '}
+} {}
+do_test collateA-2.20 {
+ execsql {SELECT a FROM t1 WHERE c='abcd '}
+} {}
+do_test collateA-2.21 {
+ execsql {SELECT a FROM t1 WHERE c='abcd'}
+} {}
+do_test collateA-2.22 {
+ execsql {SELECT a FROM t1 WHERE c='abc'}
+} {}
+do_test collateA-2.23 {
+ execsql {SELECT a FROM t1 WHERE c='abcdef '}
+} {}
+do_test collateA-2.24 {
+ execsql {SELECT a FROM t1 WHERE c=''}
+} {5 6}
+do_test collateA-2.25 {
+ execsql {SELECT a FROM t1 WHERE c=' '}
+} {5 6}
+do_test collateA-2.26 {
+ execsql {SELECT a FROM t1 WHERE c=' '}
+} {5 6}
+
+
+do_test collateA-3.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ REINDEX;
+ PRAGMA integrity_check;
+ }
+} {ok}
+do_test collateA-3.2 {
+ execsql {SELECT a FROM t1 WHERE b='abcde '}
+} {}
+do_test collateA-3.3 {
+ execsql {SELECT a FROM t1 WHERE c='abcde '}
+} {1}
+do_test collateA-3.4 {
+ execsql {SELECT a FROM t1 WHERE b='xyzzy'}
+} {}
+do_test collateA-3.5 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy'}
+} {2 3 4}
+do_test collateA-3.6 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-3.7 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-3.8 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-3.9 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+do_test collateA-3.10 {
+ execsql {SELECT a FROM t1 WHERE c='xyzzy '}
+} {2 3 4}
+
+
+finish_test
diff --git a/third_party/sqlite/test/colmeta.test b/third_party/sqlite/test/colmeta.test
new file mode 100755
index 0000000..3939f82
--- /dev/null
+++ b/third_party/sqlite/test/colmeta.test
@@ -0,0 +1,94 @@
+#
+# 2006 February 9
+#
+# 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 the sqlite3_table_column_metadata() API.
+#
+# $Id: colmeta.test,v 1.4 2008/01/23 12:52:41 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !columnmetadata {
+ finish_test
+ return
+}
+
+# Set up a schema in the main and temp test databases.
+do_test colmeta-0 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE abc2(a PRIMARY KEY COLLATE NOCASE, b VARCHAR(32), c);
+ CREATE TABLE abc3(a NOT NULL, b INTEGER PRIMARY KEY, c);
+ }
+ ifcapable autoinc {
+ execsql {
+ CREATE TABLE abc4(a, b INTEGER PRIMARY KEY AUTOINCREMENT, c);
+ }
+ }
+ ifcapable view {
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM abc2;
+ }
+ }
+} {}
+
+
+# Return values are of the form:
+#
+# {<decl-type> <collation> <not null> <primary key> <auto increment>}
+#
+set tests {
+ 1 {main abc a} {0 {{} BINARY 0 0 0}}
+ 2 {{} abc a} {0 {{} BINARY 0 0 0}}
+ 3 {{} abc2 b} {0 {VARCHAR(32) BINARY 0 0 0}}
+ 4 {main abc2 b} {0 {VARCHAR(32) BINARY 0 0 0}}
+ 5 {{} abc2 a} {0 {{} NOCASE 0 1 0}}
+ 6 {{} abc3 a} {0 {{} BINARY 1 0 0}}
+ 7 {{} abc3 b} {0 {INTEGER BINARY 0 1 0}}
+ 13 {main abc rowid} {0 {INTEGER BINARY 0 1 0}}
+ 14 {main abc3 rowid} {0 {INTEGER BINARY 0 1 0}}
+ 16 {main abc d} {1 {no such table column: abc.d}}
+}
+ifcapable view {
+ set tests [concat $tests {
+ 8 {{} abc4 b} {0 {INTEGER BINARY 0 1 1}}
+ 15 {main abc4 rowid} {0 {INTEGER BINARY 0 1 1}}
+ }]
+}
+ifcapable view {
+ set tests [concat $tests {
+ 9 {{} v1 a} {1 {no such table column: v1.a}}
+ 10 {main v1 b} {1 {no such table column: v1.b}}
+ 11 {main v1 badname} {1 {no such table column: v1.badname}}
+ 12 {main v1 rowid} {1 {no such table column: v1.rowid}}
+ }]
+}
+
+foreach {tn params results} $tests {
+ set ::DB [sqlite3_connection_pointer db]
+
+ set tstbody [concat sqlite3_table_column_metadata $::DB $params]
+ do_test colmeta-$tn.1 {
+ list [catch $tstbody msg] [set msg]
+ } $results
+
+ db close
+ sqlite3 db test.db
+
+ set ::DB [sqlite3_connection_pointer db]
+ set tstbody [concat sqlite3_table_column_metadata $::DB $params]
+ do_test colmeta-$tn.2 {
+ list [catch $tstbody msg] [set msg]
+ } $results
+}
+
+finish_test
diff --git a/third_party/sqlite/test/colname.test b/third_party/sqlite/test/colname.test
new file mode 100755
index 0000000..d2292aa5
--- /dev/null
+++ b/third_party/sqlite/test/colname.test
@@ -0,0 +1,258 @@
+# 2008 July 15
+#
+# 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 file is testing how SQLite generates the names
+# of columns in a result set.
+#
+# $Id: colname.test,v 1.4 2008/08/02 20:09:37 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Rules (applied in order):
+#
+# (1) If there is an AS clause, use it.
+#
+# (2) A non-trival expression (not a table column name) then the name is
+# a copy of the expression text.
+#
+# (3) If short_column_names=ON, then just the abbreviated column name without
+# the table name.
+#
+# (4) When short_column_names=OFF and full_column_names=OFF then
+# use case (2) for simple queries and case (5) for joins.
+#
+# (5) When short_column_names=OFF and full_column_names=ON then
+# use the form: TABLE.COLUMN
+#
+
+
+# Verify the default settings for short_column_name and full_column_name
+#
+do_test colname-1.1 {
+ db eval {PRAGMA short_column_names}
+} {1}
+do_test colname-1.2 {
+ db eval {PRAGMA full_column_names}
+} {0}
+
+# Tests for then short=ON and full=any
+#
+do_test colname-2.1 {
+ db eval {
+ CREATE TABLE tabc(a,b,c);
+ INSERT INTO tabc VALUES(1,2,3);
+ CREATE TABLE txyz(x,y,z);
+ INSERT INTO txyz VALUES(4,5,6);
+ CREATE TABLE tboth(a,b,c,x,y,z);
+ INSERT INTO tboth VALUES(11,12,13,14,15,16);
+ CREATE VIEW v1 AS SELECT tabC.a, txyZ.x, *
+ FROM tabc, txyz ORDER BY 1 LIMIT 1;
+ CREATE VIEW v2 AS SELECT tabC.a, txyZ.x, tboTh.a, tbotH.x, *
+ FROM tabc, txyz, tboth ORDER BY 1 LIMIT 1;
+ }
+ execsql2 {
+ SELECT * FROM tabc;
+ }
+} {a 1 b 2 c 3}
+do_test colname-2.2 {
+ execsql2 {
+ SELECT Tabc.a, tAbc.b, taBc.c, * FROM tabc
+ }
+} {a 1 b 2 c 3 a 1 b 2 c 3}
+do_test colname-2.3 {
+ execsql2 {
+ SELECT +tabc.a, -tabc.b, tabc.c, * FROM tabc
+ }
+} {+tabc.a 1 -tabc.b -2 c 3 a 1 b 2 c 3}
+do_test colname-2.4 {
+ execsql2 {
+ SELECT +tabc.a AS AAA, -tabc.b AS BBB, tabc.c CCC, * FROM tabc
+ }
+} {AAA 1 BBB -2 CCC 3 a 1 b 2 c 3}
+do_test colname-2.5 {
+ execsql2 {
+ SELECT tabc.a, txyz.x, * FROM tabc, txyz;
+ }
+} {a 1 x 4 a 1 b 2 c 3 x 4 y 5 z 6}
+do_test colname-2.6 {
+ execsql2 {
+ SELECT tabc.a, txyz.x, tabc.*, txyz.* FROM tabc, txyz;
+ }
+} {a 1 x 4 a 1 b 2 c 3 x 4 y 5 z 6}
+do_test colname-2.7 {
+ execsql2 {
+ SELECT tabc.a, txyz.x, tboth.a, tboth.x, * FROM tabc, txyz, tboth;
+ }
+} {a 11 x 14 a 11 x 14 a 11 b 12 c 13 x 14 y 15 z 16 a 11 b 12 c 13 x 14 y 15 z 16}
+do_test colname-2.8 {
+ execsql2 {
+ SELECT * FROM v1 ORDER BY 2;
+ }
+} {a 1 x 4 a:1 1 b 2 c 3 x:1 4 y 5 z 6}
+do_test colname-2.9 {
+ execsql2 {
+ SELECT * FROM v2 ORDER BY 2;
+ }
+} {a 1 x 4 a:1 11 x:1 14 a:2 1 b 2 c 3 x:2 4 y 5 z 6 a:3 11 b:1 12 c:1 13 x:3 14 y:1 15 z:1 16}
+
+
+# Tests for short=OFF and full=OFF
+#
+do_test colname-3.1 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=OFF;
+ CREATE VIEW v3 AS SELECT tabC.a, txyZ.x, *
+ FROM tabc, txyz ORDER BY 1 LIMIT 1;
+ CREATE VIEW v4 AS SELECT tabC.a, txyZ.x, tboTh.a, tbotH.x, *
+ FROM tabc, txyz, tboth ORDER BY 1 LIMIT 1;
+ }
+ execsql2 {
+ SELECT * FROM tabc;
+ }
+} {a 1 b 2 c 3}
+do_test colname-3.2 {
+ execsql2 {
+ SELECT Tabc.a, tAbc.b, taBc.c FROM tabc
+ }
+} {Tabc.a 1 tAbc.b 2 taBc.c 3}
+do_test colname-3.3 {
+ execsql2 {
+ SELECT +tabc.a, -tabc.b, tabc.c FROM tabc
+ }
+} {+tabc.a 1 -tabc.b -2 tabc.c 3}
+do_test colname-3.4 {
+ execsql2 {
+ SELECT +tabc.a AS AAA, -tabc.b AS BBB, tabc.c CCC FROM tabc
+ }
+} {AAA 1 BBB -2 CCC 3}
+do_test colname-3.5 {
+ execsql2 {
+ SELECT Tabc.a, Txyz.x, * FROM tabc, txyz;
+ }
+} {Tabc.a 1 Txyz.x 4 a 1 b 2 c 3 x 4 y 5 z 6}
+do_test colname-3.6 {
+ execsql2 {
+ SELECT tabc.*, txyz.* FROM tabc, txyz;
+ }
+} {a 1 b 2 c 3 x 4 y 5 z 6}
+do_test colname-3.7 {
+ execsql2 {
+ SELECT * FROM tabc, txyz, tboth;
+ }
+} {a 11 b 12 c 13 x 14 y 15 z 16 a 11 b 12 c 13 x 14 y 15 z 16}
+do_test colname-3.8 {
+ execsql2 {
+ SELECT v1.a, * FROM v1 ORDER BY 2;
+ }
+} {v1.a 1 a 1 x 4 a:1 1 b 2 c 3 x:1 4 y 5 z 6}
+do_test colname-3.9 {
+ execsql2 {
+ SELECT * FROM v2 ORDER BY 2;
+ }
+} {a 1 x 4 a:1 11 x:1 14 a:2 1 b 2 c 3 x:2 4 y 5 z 6 a:3 11 b:1 12 c:1 13 x:3 14 y:1 15 z:1 16}
+do_test colname-3.10 {
+ execsql2 {
+ SELECT * FROM v3 ORDER BY 2;
+ }
+} {a 1 x 4 a:1 1 b 2 c 3 x:1 4 y 5 z 6}
+do_test colname-3.11 {
+ execsql2 {
+ SELECT * FROM v4 ORDER BY 2;
+ }
+} {a 1 x 4 a:1 11 x:1 14 a:2 1 b 2 c 3 x:2 4 y 5 z 6 a:3 11 b:1 12 c:1 13 x:3 14 y:1 15 z:1 16}
+
+# Test for short=OFF and full=ON
+#
+do_test colname-4.1 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=ON;
+ CREATE VIEW v5 AS SELECT tabC.a, txyZ.x, *
+ FROM tabc, txyz ORDER BY 1 LIMIT 1;
+ CREATE VIEW v6 AS SELECT tabC.a, txyZ.x, tboTh.a, tbotH.x, *
+ FROM tabc, txyz, tboth ORDER BY 1 LIMIT 1;
+ }
+ execsql2 {
+ SELECT * FROM tabc;
+ }
+} {tabc.a 1 tabc.b 2 tabc.c 3}
+do_test colname-4.2 {
+ execsql2 {
+ SELECT Tabc.a, tAbc.b, taBc.c FROM tabc
+ }
+} {tabc.a 1 tabc.b 2 tabc.c 3}
+do_test colname-4.3 {
+ execsql2 {
+ SELECT +tabc.a, -tabc.b, tabc.c FROM tabc
+ }
+} {+tabc.a 1 -tabc.b -2 tabc.c 3}
+do_test colname-4.4 {
+ execsql2 {
+ SELECT +tabc.a AS AAA, -tabc.b AS BBB, tabc.c CCC FROM tabc
+ }
+} {AAA 1 BBB -2 CCC 3}
+do_test colname-4.5 {
+ execsql2 {
+ SELECT Tabc.a, Txyz.x, * FROM tabc, txyz;
+ }
+} {tabc.a 1 txyz.x 4 tabc.a 1 tabc.b 2 tabc.c 3 txyz.x 4 txyz.y 5 txyz.z 6}
+do_test colname-4.6 {
+ execsql2 {
+ SELECT tabc.*, txyz.* FROM tabc, txyz;
+ }
+} {tabc.a 1 tabc.b 2 tabc.c 3 txyz.x 4 txyz.y 5 txyz.z 6}
+do_test colname-4.7 {
+ execsql2 {
+ SELECT * FROM tabc, txyz, tboth;
+ }
+} {tabc.a 1 tabc.b 2 tabc.c 3 txyz.x 4 txyz.y 5 txyz.z 6 tboth.a 11 tboth.b 12 tboth.c 13 tboth.x 14 tboth.y 15 tboth.z 16}
+do_test colname-4.8 {
+ execsql2 {
+ SELECT * FROM v1 ORDER BY 2;
+ }
+} {v1.a 1 v1.x 4 v1.a:1 1 v1.b 2 v1.c 3 v1.x:1 4 v1.y 5 v1.z 6}
+do_test colname-4.9 {
+ execsql2 {
+ SELECT * FROM v2 ORDER BY 2;
+ }
+} {v2.a 1 v2.x 4 v2.a:1 11 v2.x:1 14 v2.a:2 1 v2.b 2 v2.c 3 v2.x:2 4 v2.y 5 v2.z 6 v2.a:3 11 v2.b:1 12 v2.c:1 13 v2.x:3 14 v2.y:1 15 v2.z:1 16}
+do_test colname-4.10 {
+ execsql2 {
+ SELECT * FROM v3 ORDER BY 2;
+ }
+} {v3.a 1 v3.x 4 v3.a:1 1 v3.b 2 v3.c 3 v3.x:1 4 v3.y 5 v3.z 6}
+do_test colname-4.11 {
+ execsql2 {
+ SELECT * FROM v4 ORDER BY 2;
+ }
+} {v4.a 1 v4.x 4 v4.a:1 11 v4.x:1 14 v4.a:2 1 v4.b 2 v4.c 3 v4.x:2 4 v4.y 5 v4.z 6 v4.a:3 11 v4.b:1 12 v4.c:1 13 v4.x:3 14 v4.y:1 15 v4.z:1 16}
+do_test colname-4.12 {
+ execsql2 {
+ SELECT * FROM v5 ORDER BY 2;
+ }
+} {v5.a 1 v5.x 4 v5.a:1 1 v5.b 2 v5.c 3 v5.x:1 4 v5.y 5 v5.z 6}
+do_test colname-4.13 {
+ execsql2 {
+ SELECT * FROM v6 ORDER BY 2;
+ }
+} {v6.a 1 v6.x 4 v6.a:1 11 v6.x:1 14 v6.a:2 1 v6.b 2 v6.c 3 v6.x:2 4 v6.y 5 v6.z 6 v6.a:3 11 v6.b:1 12 v6.c:1 13 v6.x:3 14 v6.y:1 15 v6.z:1 16}
+
+# ticket #3229
+do_test colname-5.1 {
+ lreplace [db eval {
+ SELECT x.* FROM sqlite_master X LIMIT 1;
+ }] 3 3 x
+} {table tabc tabc x {CREATE TABLE tabc(a,b,c)}}
+
+finish_test
diff --git a/third_party/sqlite/test/conflict.test b/third_party/sqlite/test/conflict.test
new file mode 100755
index 0000000..8eb25b1
--- /dev/null
+++ b/third_party/sqlite/test/conflict.test
@@ -0,0 +1,792 @@
+# 2002 January 29
+#
+# 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.
+#
+# This file implements tests for the conflict resolution extension
+# to SQLite.
+#
+# $Id: conflict.test,v 1.31 2008/01/21 16:22:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !conflict {
+ finish_test
+ return
+}
+
+# Create tables for the first group of tests.
+#
+do_test conflict-1.0 {
+ execsql {
+ CREATE TABLE t1(a, b, c, UNIQUE(a,b));
+ CREATE TABLE t2(x);
+ SELECT c FROM t1 ORDER BY c;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+# t3 Number of temporary files created by this test
+#
+foreach {i cmd t0 t1 t2 t3} {
+ 1 INSERT 1 {} 1 0
+ 2 {INSERT OR IGNORE} 0 3 1 0
+ 3 {INSERT OR REPLACE} 0 4 1 0
+ 4 REPLACE 0 4 1 0
+ 5 {INSERT OR FAIL} 1 {} 1 0
+ 6 {INSERT OR ABORT} 1 {} 1 0
+ 7 {INSERT OR ROLLBACK} 1 {} {} 0
+} {
+ do_test conflict-1.$i {
+ set ::sqlite_opentemp_count 0
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ set r3 $::sqlite_opentemp_count
+ list $r0 $r1 $r2 $r3
+ } [list $t0 $t1 $t2 $t3]
+}
+
+# Create tables for the first group of tests.
+#
+do_test conflict-2.0 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(a,b));
+ CREATE TABLE t2(x);
+ SELECT c FROM t1 ORDER BY c;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i cmd t0 t1 t2} {
+ 1 INSERT 1 {} 1
+ 2 {INSERT OR IGNORE} 0 3 1
+ 3 {INSERT OR REPLACE} 0 4 1
+ 4 REPLACE 0 4 1
+ 5 {INSERT OR FAIL} 1 {} 1
+ 6 {INSERT OR ABORT} 1 {} 1
+ 7 {INSERT OR ROLLBACK} 1 {} {}
+} {
+ do_test conflict-2.$i {
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+# Create tables for the first group of tests.
+#
+do_test conflict-3.0 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ CREATE TABLE t1(a, b, c INTEGER, PRIMARY KEY(c), UNIQUE(a,b));
+ CREATE TABLE t2(x);
+ SELECT c FROM t1 ORDER BY c;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i cmd t0 t1 t2} {
+ 1 INSERT 1 {} 1
+ 2 {INSERT OR IGNORE} 0 3 1
+ 3 {INSERT OR REPLACE} 0 4 1
+ 4 REPLACE 0 4 1
+ 5 {INSERT OR FAIL} 1 {} 1
+ 6 {INSERT OR ABORT} 1 {} 1
+ 7 {INSERT OR ROLLBACK} 1 {} {}
+} {
+ do_test conflict-3.$i {
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test conflict-4.0 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(x);
+ SELECT x FROM t2;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the UNIQUE constraint
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf1 cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 REPLACE INSERT 0 4 1
+ 3 IGNORE INSERT 0 3 1
+ 4 FAIL INSERT 1 {} 1
+ 5 ABORT INSERT 1 {} 1
+ 6 ROLLBACK INSERT 1 {} {}
+ 7 REPLACE {INSERT OR IGNORE} 0 3 1
+ 8 IGNORE {INSERT OR REPLACE} 0 4 1
+ 9 FAIL {INSERT OR IGNORE} 0 3 1
+ 10 ABORT {INSERT OR REPLACE} 0 4 1
+ 11 ROLLBACK {INSERT OR IGNORE } 0 3 1
+} {
+ do_test conflict-4.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,UNIQUE(a,b) $conf1);
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test conflict-5.0 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(x);
+ SELECT x FROM t2;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the NOT NULL constraint
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf1 cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 REPLACE INSERT 0 5 1
+ 3 IGNORE INSERT 0 {} 1
+ 4 FAIL INSERT 1 {} 1
+ 5 ABORT INSERT 1 {} 1
+ 6 ROLLBACK INSERT 1 {} {}
+ 7 REPLACE {INSERT OR IGNORE} 0 {} 1
+ 8 IGNORE {INSERT OR REPLACE} 0 5 1
+ 9 FAIL {INSERT OR IGNORE} 0 {} 1
+ 10 ABORT {INSERT OR REPLACE} 0 5 1
+ 11 ROLLBACK {INSERT OR IGNORE} 0 {} 1
+ 12 {} {INSERT OR IGNORE} 0 {} 1
+ 13 {} {INSERT OR REPLACE} 0 5 1
+ 14 {} {INSERT OR FAIL} 1 {} 1
+ 15 {} {INSERT OR ABORT} 1 {} 1
+ 16 {} {INSERT OR ROLLBACK} 1 {} {}
+} {
+ if {$t0} {set t1 {t1.c may not be NULL}}
+ do_test conflict-5.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c NOT NULL $conf1 DEFAULT 5);
+ DELETE FROM t2;
+ BEGIN;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,NULL);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {!$r0} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test conflict-6.0 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(a,b,c);
+ INSERT INTO t2 VALUES(1,2,1);
+ INSERT INTO t2 VALUES(2,3,2);
+ INSERT INTO t2 VALUES(3,4,1);
+ INSERT INTO t2 VALUES(4,5,4);
+ SELECT c FROM t2 ORDER BY b;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(1);
+ }
+} {1 2 1 4}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the UNIQUE constraint
+# cmd An UPDATE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "b" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t3
+# t3 Number of temporary files for tables
+# t4 Number of temporary files for statement journals
+#
+# Update: Since temporary table files are now opened lazily, and none
+# of the following tests use large quantities of data, t3 is always 0.
+#
+foreach {i conf1 cmd t0 t1 t2 t3 t4} {
+ 1 {} UPDATE 1 {6 7 8 9} 1 0 1
+ 2 REPLACE UPDATE 0 {7 6 9} 1 0 0
+ 3 IGNORE UPDATE 0 {6 7 3 9} 1 0 0
+ 4 FAIL UPDATE 1 {6 7 3 4} 1 0 0
+ 5 ABORT UPDATE 1 {1 2 3 4} 1 0 1
+ 6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0 0
+ 7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
+ 8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0
+ 9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
+ 10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0
+ 11 ROLLBACK {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
+ 12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
+ 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0
+ 14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0
+ 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1
+ 16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0
+} {
+ if {$t0} {set t1 {column a is not unique}}
+ if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
+ set t3 $t4
+ } else {
+ set t3 [expr {$t3+$t4}]
+ }
+ do_test conflict-6.$i {
+ db close
+ sqlite3 db test.db
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ execsql {pragma temp_store=file}
+ set ::sqlite_opentemp_count 0
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
+ INSERT INTO t1 SELECT * FROM t2;
+ UPDATE t3 SET x=0;
+ BEGIN;
+ $cmd t3 SET x=1;
+ $cmd t1 SET b=b*2;
+ $cmd t1 SET a=c+5;
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
+ set r2 [execsql {SELECT x FROM t3}]
+ list $r0 $r1 $r2 $::sqlite_opentemp_count
+ } [list $t0 $t1 $t2 $t3]
+}
+
+# Test to make sure a lot of IGNOREs don't cause a stack overflow
+#
+do_test conflict-7.1 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP TABLE t3;
+ CREATE TABLE t1(a unique, b);
+ }
+ for {set i 1} {$i<=50} {incr i} {
+ execsql "INSERT into t1 values($i,[expr {$i+1}]);"
+ }
+ execsql {
+ SELECT count(*), min(a), max(b) FROM t1;
+ }
+} {50 1 51}
+do_test conflict-7.2 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE OR IGNORE t1 SET a=1000;
+ }
+} {1}
+do_test conflict-7.2.1 {
+ db changes
+} {1}
+do_test conflict-7.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a=1000;
+ }
+} {2}
+do_test conflict-7.4 {
+ execsql {
+ SELECT count(*) FROM t1;
+ }
+} {50}
+do_test conflict-7.5 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE OR REPLACE t1 SET a=1001;
+ }
+} {50}
+do_test conflict-7.5.1 {
+ db changes
+} {50}
+do_test conflict-7.6 {
+ execsql {
+ SELECT b FROM t1 WHERE a=1001;
+ }
+} {51}
+do_test conflict-7.7 {
+ execsql {
+ SELECT count(*) FROM t1;
+ }
+} {1}
+
+# Update for version 3: A SELECT statement no longer resets the change
+# counter (Test result changes from 0 to 50).
+do_test conflict-7.7.1 {
+ db changes
+} {50}
+
+# Make sure the row count is right for rows that are ignored on
+# an insert.
+#
+do_test conflict-8.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2);
+ }
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(2,3);
+ }
+} {1}
+do_test conflict-8.1.1 {
+ db changes
+} {1}
+do_test conflict-8.2 {
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(2,4);
+ }
+} {0}
+do_test conflict-8.2.1 {
+ db changes
+} {0}
+do_test conflict-8.3 {
+ execsql {
+ INSERT OR REPLACE INTO t1 VALUES(2,4);
+ }
+} {1}
+do_test conflict-8.3.1 {
+ db changes
+} {1}
+do_test conflict-8.4 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT * FROM t1;
+ }
+} {0}
+do_test conflict-8.4.1 {
+ db changes
+} {0}
+do_test conflict-8.5 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1;
+ }
+} {2}
+do_test conflict-8.5.1 {
+ db changes
+} {2}
+do_test conflict-8.6 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1;
+ }
+} {3}
+do_test conflict-8.6.1 {
+ db changes
+} {3}
+
+integrity_check conflict-8.99
+
+do_test conflict-9.1 {
+ execsql {
+ PRAGMA count_changes=0;
+ CREATE TABLE t2(
+ a INTEGER UNIQUE ON CONFLICT IGNORE,
+ b INTEGER UNIQUE ON CONFLICT FAIL,
+ c INTEGER UNIQUE ON CONFLICT REPLACE,
+ d INTEGER UNIQUE ON CONFLICT ABORT,
+ e INTEGER UNIQUE ON CONFLICT ROLLBACK
+ );
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(1);
+ SELECT * FROM t3;
+ }
+} {1}
+do_test conflict-9.2 {
+ catchsql {
+ INSERT INTO t2 VALUES(1,1,1,1,1);
+ INSERT INTO t2 VALUES(2,2,2,2,2);
+ SELECT * FROM t2;
+ }
+} {0 {1 1 1 1 1 2 2 2 2 2}}
+do_test conflict-9.3 {
+ catchsql {
+ INSERT INTO t2 VALUES(1,3,3,3,3);
+ SELECT * FROM t2;
+ }
+} {0 {1 1 1 1 1 2 2 2 2 2}}
+do_test conflict-9.4 {
+ catchsql {
+ UPDATE t2 SET a=a+1 WHERE a=1;
+ SELECT * FROM t2;
+ }
+} {0 {1 1 1 1 1 2 2 2 2 2}}
+do_test conflict-9.5 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,1,3,3,3);
+ SELECT * FROM t2;
+ }
+} {1 {column b is not unique}}
+do_test conflict-9.6 {
+ catchsql {
+ UPDATE t2 SET b=b+1 WHERE b=1;
+ SELECT * FROM t2;
+ }
+} {1 {column b is not unique}}
+do_test conflict-9.7 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,1,3,3,3);
+ SELECT * FROM t2;
+ }
+} {1 {column b is not unique}}
+do_test conflict-9.8 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {2}
+do_test conflict-9.9 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET b=b+1 WHERE b=1;
+ SELECT * FROM t2;
+ }
+} {1 {column b is not unique}}
+do_test conflict-9.10 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {3}
+do_test conflict-9.11 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,3,3,1,3);
+ SELECT * FROM t2;
+ }
+} {1 {column d is not unique}}
+do_test conflict-9.12 {
+ catchsql {
+ UPDATE t2 SET d=d+1 WHERE d=1;
+ SELECT * FROM t2;
+ }
+} {1 {column d is not unique}}
+do_test conflict-9.13 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,3,3,1,3);
+ SELECT * FROM t2;
+ }
+} {1 {column d is not unique}}
+do_test conflict-9.14 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {4}
+do_test conflict-9.15 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET d=d+1 WHERE d=1;
+ SELECT * FROM t2;
+ }
+} {1 {column d is not unique}}
+do_test conflict-9.16 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict-9.17 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,3,3,3,1);
+ SELECT * FROM t2;
+ }
+} {1 {column e is not unique}}
+do_test conflict-9.18 {
+ catchsql {
+ UPDATE t2 SET e=e+1 WHERE e=1;
+ SELECT * FROM t2;
+ }
+} {1 {column e is not unique}}
+do_test conflict-9.19 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,3,3,3,1);
+ SELECT * FROM t2;
+ }
+} {1 {column e is not unique}}
+do_test conflict-9.20 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict-9.21 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET e=e+1 WHERE e=1;
+ SELECT * FROM t2;
+ }
+} {1 {column e is not unique}}
+do_test conflict-9.22 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict-9.23 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,3,1,3,3);
+ SELECT * FROM t2;
+ }
+} {0 {2 2 2 2 2 3 3 1 3 3}}
+do_test conflict-9.24 {
+ catchsql {
+ UPDATE t2 SET c=c-1 WHERE c=2;
+ SELECT * FROM t2;
+ }
+} {0 {2 2 1 2 2}}
+do_test conflict-9.25 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,3,1,3,3);
+ SELECT * FROM t2;
+ }
+} {0 {3 3 1 3 3}}
+do_test conflict-9.26 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {6}
+
+do_test conflict-10.1 {
+ catchsql {
+ DELETE FROM t1;
+ BEGIN;
+ INSERT OR ROLLBACK INTO t1 VALUES(1,2);
+ INSERT OR ROLLBACK INTO t1 VALUES(1,3);
+ COMMIT;
+ }
+ execsql {SELECT * FROM t1}
+} {}
+do_test conflict-10.2 {
+ catchsql {
+ CREATE TABLE t4(x);
+ CREATE UNIQUE INDEX t4x ON t4(x);
+ BEGIN;
+ INSERT OR ROLLBACK INTO t4 VALUES(1);
+ INSERT OR ROLLBACK INTO t4 VALUES(1);
+ COMMIT;
+ }
+ execsql {SELECT * FROM t4}
+} {}
+
+# Ticket #1171. Make sure statement rollbacks do not
+# damage the database.
+#
+do_test conflict-11.1 {
+ execsql {
+ -- Create a database object (pages 2, 3 of the file)
+ BEGIN;
+ CREATE TABLE abc(a UNIQUE, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ INSERT INTO abc VALUES(7, 8, 9);
+ COMMIT;
+ }
+
+
+ # Set a small cache size so that changes will spill into
+ # the database file.
+ execsql {
+ PRAGMA cache_size = 10;
+ }
+
+ # Make lots of changes. Because of the small cache, some
+ # (most?) of these changes will spill into the disk file.
+ # In other words, some of the changes will not be held in
+ # cache.
+ #
+ execsql {
+ BEGIN;
+ -- Make sure the pager is in EXCLUSIVE state.
+ CREATE TABLE def(d, e, f);
+ INSERT INTO def VALUES
+ ('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ DELETE FROM abc WHERE a = 4;
+ }
+
+ # Execute a statement that does a statement rollback due to
+ # a constraint failure.
+ #
+ catchsql {
+ INSERT INTO abc SELECT 10, 20, 30 FROM def;
+ }
+
+ # Rollback the database. Verify that the state of the ABC table
+ # is unchanged from the beginning of the transaction. In other words,
+ # make sure the DELETE on table ABC that occurred within the transaction
+ # had no effect.
+ #
+ execsql {
+ ROLLBACK;
+ SELECT * FROM abc;
+ }
+} {1 2 3 4 5 6 7 8 9}
+integrity_check conflict-11.2
+
+# Repeat test conflict-11.1 but this time commit.
+#
+do_test conflict-11.3 {
+ execsql {
+ BEGIN;
+ -- Make sure the pager is in EXCLUSIVE state.
+ UPDATE abc SET a=a+1;
+ CREATE TABLE def(d, e, f);
+ INSERT INTO def VALUES
+ ('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ DELETE FROM abc WHERE a = 4;
+ }
+ catchsql {
+ INSERT INTO abc SELECT 10, 20, 30 FROM def;
+ }
+ execsql {
+ ROLLBACK;
+ SELECT * FROM abc;
+ }
+} {1 2 3 4 5 6 7 8 9}
+# Repeat test conflict-11.1 but this time commit.
+#
+do_test conflict-11.5 {
+ execsql {
+ BEGIN;
+ -- Make sure the pager is in EXCLUSIVE state.
+ CREATE TABLE def(d, e, f);
+ INSERT INTO def VALUES
+ ('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ INSERT INTO def SELECT * FROM def;
+ DELETE FROM abc WHERE a = 4;
+ }
+ catchsql {
+ INSERT INTO abc SELECT 10, 20, 30 FROM def;
+ }
+ execsql {
+ COMMIT;
+ SELECT * FROM abc;
+ }
+} {1 2 3 7 8 9}
+integrity_check conflict-11.6
+
+# Make sure UPDATE OR REPLACE works on tables that have only
+# an INTEGER PRIMARY KEY.
+#
+do_test conflict-12.1 {
+ execsql {
+ CREATE TABLE t5(a INTEGER PRIMARY KEY, b text);
+ INSERT INTO t5 VALUES(1,'one');
+ INSERT INTO t5 VALUES(2,'two');
+ SELECT * FROM t5
+ }
+} {1 one 2 two}
+do_test conflict-12.2 {
+ execsql {
+ UPDATE OR IGNORE t5 SET a=a+1 WHERE a=1;
+ SELECT * FROM t5;
+ }
+} {1 one 2 two}
+do_test conflict-12.3 {
+ catchsql {
+ UPDATE t5 SET a=a+1 WHERE a=1;
+ }
+} {1 {PRIMARY KEY must be unique}}
+do_test conflict-12.4 {
+ execsql {
+ UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1;
+ SELECT * FROM t5;
+ }
+} {2 one}
+
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt.test b/third_party/sqlite/test/corrupt.test
new file mode 100755
index 0000000..30ceeea
--- /dev/null
+++ b/third_party/sqlite/test/corrupt.test
@@ -0,0 +1,178 @@
+# 2004 August 30
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.
+#
+# $Id: corrupt.test,v 1.9 2008/05/05 12:09:33 danielk1977 Exp $
+
+catch {file delete -force test.db}
+catch {file delete -force test.db-journal}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Construct a large database for testing.
+#
+do_test corrupt-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(randstr(100,100));
+ INSERT INTO t1 VALUES(randstr(90,90));
+ INSERT INTO t1 VALUES(randstr(80,80));
+ INSERT INTO t1 SELECT x || randstr(5,5) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(6,6) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(7,7) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(8,8) FROM t1;
+ INSERT INTO t1 VALUES(randstr(3000,3000));
+ INSERT INTO t1 SELECT x || randstr(9,9) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(10,10) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(11,11) FROM t1;
+ INSERT INTO t1 SELECT x || randstr(12,12) FROM t1;
+ CREATE INDEX t1i1 ON t1(x);
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ DELETE FROM t2 WHERE rowid%5!=0;
+ COMMIT;
+ }
+} {}
+integrity_check corrupt-1.2
+
+# Copy file $from into $to
+#
+proc copy_file {from to} {
+ set f [open $from]
+ fconfigure $f -translation binary
+ set t [open $to w]
+ fconfigure $t -translation binary
+ puts -nonewline $t [read $f [file size $from]]
+ close $t
+ close $f
+}
+
+# Setup for the tests. Make a backup copy of the good database in test.bu.
+# Create a string of garbage data that is 256 bytes long.
+#
+copy_file test.db test.bu
+set fsize [file size test.db]
+set junk "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+while {[string length $junk]<256} {append junk $junk}
+set junk [string range $junk 0 255]
+
+# Go through the database and write garbage data into each 256 segment
+# of the file. Then do various operations on the file to make sure that
+# the database engine can recover gracefully from the corruption.
+#
+for {set i [expr {1*256}]} {$i<$fsize-256} {incr i 256} {
+ set tn [expr {$i/256}]
+ db close
+ copy_file test.bu test.db
+ set fd [open test.db r+]
+ fconfigure $fd -translation binary
+ seek $fd $i
+ puts -nonewline $fd $junk
+ close $fd
+ do_test corrupt-2.$tn.1 {
+ sqlite3 db test.db
+ catchsql {SELECT count(*) FROM sqlite_master}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.2 {
+ catchsql {SELECT count(*) FROM t1}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.3 {
+ catchsql {SELECT count(*) FROM t1 WHERE x>'abcdef'}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.4 {
+ catchsql {SELECT count(*) FROM t2}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.5 {
+ catchsql {CREATE TABLE t3 AS SELECT * FROM t1}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.6 {
+ catchsql {DROP TABLE t1}
+ set x {}
+ } {}
+ do_test corrupt-2.$tn.7 {
+ catchsql {PRAGMA integrity_check}
+ set x {}
+ } {}
+
+ # Check that no page references were leaked.
+ do_test corrupt-2.$tn.8 {
+ set bt [btree_from_db db]
+ db_enter db
+ array set stats [btree_pager_stats $bt]
+ db_leave db
+ set stats(ref)
+ } {0}
+}
+
+#------------------------------------------------------------------------
+# For these tests, swap the rootpage entries of t1 (a table) and t1i1 (an
+# index on t1) in sqlite_master. Then perform a few different queries
+# and make sure this is detected as corruption.
+#
+do_test corrupt-3.1 {
+ db close
+ copy_file test.bu test.db
+ sqlite3 db test.db
+ list
+} {}
+do_test corrupt-3.2 {
+ set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
+ set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
+ set cookie [expr [execsql {PRAGMA schema_version}] + 1]
+ execsql "
+ PRAGMA writable_schema = 1;
+ UPDATE sqlite_master SET rootpage = $t1_r WHERE name = 't1';
+ UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
+ PRAGMA writable_schema = 0;
+ PRAGMA schema_version = $cookie;
+ "
+} {}
+
+# This one tests the case caught by code in checkin [2313].
+do_test corrupt-3.3 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ INSERT INTO t1 VALUES('abc');
+ }
+} {1 {database disk image is malformed}}
+do_test corrupt-3.4 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {1 {database disk image is malformed}}
+do_test corrupt-3.5 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM t1 WHERE oid = 10;
+ }
+} {1 {database disk image is malformed}}
+do_test corrupt-3.6 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM t1 WHERE x = 'abcde';
+ }
+} {1 {database disk image is malformed}}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt2.test b/third_party/sqlite/test/corrupt2.test
new file mode 100755
index 0000000..35f24c6
--- /dev/null
+++ b/third_party/sqlite/test/corrupt2.test
@@ -0,0 +1,442 @@
+# 2004 August 30
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.
+#
+# $Id: corrupt2.test,v 1.14 2008/08/02 20:09:37 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# The following tests - corrupt2-1.* - create some databases corrupted in
+# specific ways and ensure that SQLite detects them as corrupt.
+#
+do_test corrupt2-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=0;
+ PRAGMA page_size=1024;
+ CREATE TABLE abc(a, b, c);
+ }
+} {}
+
+do_test corrupt2-1.2 {
+
+ # Corrupt the 16 byte magic string at the start of the file
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ copy_file test.db corrupt.db
+ set f [open corrupt.db RDWR]
+ seek $f 8 start
+ puts $f blah
+ close $f
+
+ sqlite3 db2 corrupt.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {file is encrypted or is not a database}}
+
+do_test corrupt2-1.3 {
+ db2 close
+
+ # Corrupt the page-size (bytes 16 and 17 of page 1).
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ copy_file test.db corrupt.db
+ set f [open corrupt.db RDWR]
+ fconfigure $f -encoding binary
+ seek $f 16 start
+ puts -nonewline $f "\x00\xFF"
+ close $f
+
+ sqlite3 db2 corrupt.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {file is encrypted or is not a database}}
+
+do_test corrupt2-1.4 {
+ db2 close
+
+ # Corrupt the free-block list on page 1.
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ copy_file test.db corrupt.db
+ set f [open corrupt.db RDWR]
+ fconfigure $f -encoding binary
+ seek $f 101 start
+ puts -nonewline $f "\xFF\xFF"
+ close $f
+
+ sqlite3 db2 corrupt.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {database disk image is malformed}}
+
+do_test corrupt2-1.5 {
+ db2 close
+
+ # Corrupt the free-block list on page 1.
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ copy_file test.db corrupt.db
+ set f [open corrupt.db RDWR]
+ fconfigure $f -encoding binary
+ seek $f 101 start
+ puts -nonewline $f "\x00\xC8"
+ seek $f 200 start
+ puts -nonewline $f "\x00\x00"
+ puts -nonewline $f "\x10\x00"
+ close $f
+
+ sqlite3 db2 corrupt.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {database disk image is malformed}}
+db2 close
+
+# Corrupt a database by having 2 indices of the same name:
+do_test corrupt2-2.1 {
+
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ copy_file test.db corrupt.db
+
+ sqlite3 db2 corrupt.db
+ execsql {
+ CREATE INDEX a1 ON abc(a);
+ CREATE INDEX a2 ON abc(b);
+ PRAGMA writable_schema = 1;
+ UPDATE sqlite_master
+ SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000)
+ WHERE type = 'index';
+ PRAGMA writable_schema = 0;
+ } db2
+
+ db2 close
+ sqlite3 db2 corrupt.db
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {malformed database schema (a3) - index a3 already exists}}
+
+db2 close
+
+do_test corrupt2-3.1 {
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ sqlite3 db2 corrupt.db
+
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+ INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ } db2
+
+ db2 close
+
+ # On the root page of table t2 (page 4), set one of the child page-numbers
+ # to 0. This corruption will be detected when SQLite attempts to update
+ # the pointer-map after moving the content of page 4 to page 3 as part
+ # of the DROP TABLE operation below.
+ #
+ set fd [open corrupt.db r+]
+ fconfigure $fd -encoding binary -translation binary
+ seek $fd [expr 1024*3 + 12]
+ set zCelloffset [read $fd 2]
+ binary scan $zCelloffset S iCelloffset
+ seek $fd [expr 1024*3 + $iCelloffset]
+ puts -nonewline $fd "\00\00\00\00"
+ close $fd
+
+ sqlite3 db2 corrupt.db
+ catchsql {
+ DROP TABLE t1;
+ } db2
+} {1 {database disk image is malformed}}
+
+do_test corrupt2-4.1 {
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {1 {database disk image is malformed}}
+
+do_test corrupt2-5.1 {
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+ sqlite3 db2 corrupt.db
+
+ execsql {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+ INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ INSERT INTO t1 SELECT * FROM t2;
+ } db2
+
+ db2 close
+
+ # This block links a page from table t2 into the t1 table structure.
+ #
+ set fd [open corrupt.db r+]
+ fconfigure $fd -encoding binary -translation binary
+ seek $fd [expr 1024 + 12]
+ set zCelloffset [read $fd 2]
+ binary scan $zCelloffset S iCelloffset
+ seek $fd [expr 1024 + $iCelloffset]
+ set zChildPage [read $fd 4]
+ seek $fd [expr 2*1024 + 12]
+ set zCelloffset [read $fd 2]
+ binary scan $zCelloffset S iCelloffset
+ seek $fd [expr 2*1024 + $iCelloffset]
+ puts -nonewline $fd $zChildPage
+ close $fd
+
+ sqlite3 db2 corrupt.db
+ db2 eval {SELECT rowid FROM t1} {
+ set result [db2 eval {pragma integrity_check}]
+ break
+ }
+ set result
+} {{*** in database main ***
+Page 10: sqlite3BtreeInitPage() returns error code 11
+On tree page 3 cell 1: Child page depth differs
+On tree page 2 cell 0: 2nd reference to page 10
+On tree page 2 cell 1: Child page depth differs
+Page 4 is never used}}
+
+db2 close
+
+proc corruption_test {args} {
+ array set A $args
+
+ catch {db close}
+ file delete -force corrupt.db
+ file delete -force corrupt.db-journal
+
+ sqlite3 db corrupt.db
+ db eval $A(-sqlprep)
+ db close
+
+ eval $A(-corrupt)
+
+ sqlite3 db corrupt.db
+ eval $A(-test)
+}
+
+ifcapable autovacuum {
+ # The tests within this block - corrupt2-6.* - aim to test corruption
+ # detection within an incremental-vacuum. When an incremental-vacuum
+ # step is executed, the last non-free page of the database file is
+ # moved into a free space in the body of the file. After doing so,
+ # the page reference in the parent page must be updated to refer
+ # to the new location. These tests test the outcome of corrupting
+ # that page reference before performing the incremental vacuum.
+ #
+
+ # The last page in the database page is the second page
+ # in an overflow chain.
+ #
+ corruption_test -sqlprep {
+ PRAGMA auto_vacuum = incremental;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, randomblob(2500));
+ INSERT INTO t1 VALUES(2, randomblob(2500));
+ DELETE FROM t1 WHERE a = 1;
+ } -corrupt {
+ hexio_write corrupt.db [expr 1024*5] 00000008
+ } -test {
+ do_test corrupt2-6.1 {
+ catchsql { pragma incremental_vacuum = 1 }
+ } {1 {database disk image is malformed}}
+ }
+
+ # The last page in the database page is a non-root b-tree page.
+ #
+ corruption_test -sqlprep {
+ PRAGMA auto_vacuum = incremental;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, randomblob(2500));
+ INSERT INTO t1 VALUES(2, randomblob(50));
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ DELETE FROM t1 WHERE a = 1;
+ } -corrupt {
+ hexio_write corrupt.db [expr 1024*2 + 8] 00000009
+ } -test {
+ do_test corrupt2-6.2 {
+ catchsql { pragma incremental_vacuum = 1 }
+ } {1 {database disk image is malformed}}
+ }
+
+ # Set up a pointer-map entry so that the last page of the database
+ # file appears to be a b-tree root page. This should be detected
+ # as corruption.
+ #
+ corruption_test -sqlprep {
+ PRAGMA auto_vacuum = incremental;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, randomblob(2500));
+ INSERT INTO t1 VALUES(2, randomblob(2500));
+ INSERT INTO t1 VALUES(3, randomblob(2500));
+ DELETE FROM t1 WHERE a = 1;
+ } -corrupt {
+ set nPage [expr [file size corrupt.db] / 1024]
+ hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000
+ } -test {
+ do_test corrupt2-6.3 {
+ catchsql { pragma incremental_vacuum = 1 }
+ } {1 {database disk image is malformed}}
+ }
+
+ corruption_test -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, randomblob(2500));
+ DELETE FROM t1 WHERE a = 1;
+ } -corrupt {
+ set nAppend [expr 1024*207 - [file size corrupt.db]]
+ set fd [open corrupt.db r+]
+ seek $fd 0 end
+ puts -nonewline $fd [string repeat x $nAppend]
+ close $fd
+ } -test {
+ do_test corrupt2-6.4 {
+ catchsql {
+ BEGIN EXCLUSIVE;
+ COMMIT;
+ }
+ } {1 {database disk image is malformed}}
+ }
+}
+
+
+set sqlprep {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ CREATE INDEX i1 ON t1(b);
+ INSERT INTO t1 VALUES(1, randomblob(50));
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+ INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
+}
+
+corruption_test -sqlprep $sqlprep -corrupt {
+ # Set the page-flags of one of the leaf pages of the index B-Tree to
+ # 0x0D (interpreted by SQLite as "leaf page of a table B-Tree").
+ #
+ set fd [open corrupt.db r+]
+ fconfigure $fd -translation binary -encoding binary
+ seek $fd [expr 1024*2 + 8]
+ set zRightChild [read $fd 4]
+ binary scan $zRightChild I iRightChild
+ seek $fd [expr 1024*($iRightChild-1)]
+ puts -nonewline $fd "\x0D"
+ close $fd
+} -test {
+ do_test corrupt2-7.1 {
+ catchsql { SELECT b FROM t1 ORDER BY b ASC }
+ } {1 {database disk image is malformed}}
+}
+
+corruption_test -sqlprep $sqlprep -corrupt {
+ # Mess up the page-header of one of the leaf pages of the index B-Tree.
+ # The corruption is detected as part of an OP_Prev opcode.
+ #
+ set fd [open corrupt.db r+]
+ fconfigure $fd -translation binary -encoding binary
+ seek $fd [expr 1024*2 + 12]
+ set zCellOffset [read $fd 2]
+ binary scan $zCellOffset S iCellOffset
+ seek $fd [expr 1024*2 + $iCellOffset]
+ set zChild [read $fd 4]
+ binary scan $zChild I iChild
+ seek $fd [expr 1024*($iChild-1)+3]
+ puts -nonewline $fd "\xFFFF"
+ close $fd
+} -test {
+ do_test corrupt2-7.1 {
+ catchsql { SELECT b FROM t1 ORDER BY b DESC }
+ } {1 {database disk image is malformed}}
+}
+
+corruption_test -sqlprep $sqlprep -corrupt {
+ # Set the page-flags of one of the leaf pages of the table B-Tree to
+ # 0x0A (interpreted by SQLite as "leaf page of an index B-Tree").
+ #
+ set fd [open corrupt.db r+]
+ fconfigure $fd -translation binary -encoding binary
+ seek $fd [expr 1024*1 + 8]
+ set zRightChild [read $fd 4]
+ binary scan $zRightChild I iRightChild
+ seek $fd [expr 1024*($iRightChild-1)]
+ puts -nonewline $fd "\x0A"
+ close $fd
+} -test {
+ do_test corrupt2-8.1 {
+ catchsql { SELECT * FROM t1 WHERE rowid=1000 }
+ } {1 {database disk image is malformed}}
+}
+
+corruption_test -sqlprep {
+ CREATE TABLE t1(a, b, c); CREATE TABLE t8(a, b, c); CREATE TABLE tE(a, b, c);
+ CREATE TABLE t2(a, b, c); CREATE TABLE t9(a, b, c); CREATE TABLE tF(a, b, c);
+ CREATE TABLE t3(a, b, c); CREATE TABLE tA(a, b, c); CREATE TABLE tG(a, b, c);
+ CREATE TABLE t4(a, b, c); CREATE TABLE tB(a, b, c); CREATE TABLE tH(a, b, c);
+ CREATE TABLE t5(a, b, c); CREATE TABLE tC(a, b, c); CREATE TABLE tI(a, b, c);
+ CREATE TABLE t6(a, b, c); CREATE TABLE tD(a, b, c); CREATE TABLE tJ(a, b, c);
+ CREATE TABLE x1(a, b, c); CREATE TABLE x8(a, b, c); CREATE TABLE xE(a, b, c);
+ CREATE TABLE x2(a, b, c); CREATE TABLE x9(a, b, c); CREATE TABLE xF(a, b, c);
+ CREATE TABLE x3(a, b, c); CREATE TABLE xA(a, b, c); CREATE TABLE xG(a, b, c);
+ CREATE TABLE x4(a, b, c); CREATE TABLE xB(a, b, c); CREATE TABLE xH(a, b, c);
+ CREATE TABLE x5(a, b, c); CREATE TABLE xC(a, b, c); CREATE TABLE xI(a, b, c);
+ CREATE TABLE x6(a, b, c); CREATE TABLE xD(a, b, c); CREATE TABLE xJ(a, b, c);
+} -corrupt {
+ set fd [open corrupt.db r+]
+ fconfigure $fd -translation binary -encoding binary
+ seek $fd 108
+ set zRightChild [read $fd 4]
+ binary scan $zRightChild I iRightChild
+ seek $fd [expr 1024*($iRightChild-1)+3]
+ puts -nonewline $fd "\x00\x00"
+ close $fd
+} -test {
+ do_test corrupt2-9.1 {
+ catchsql { SELECT sql FROM sqlite_master }
+ } {1 {database disk image is malformed}}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt3.test b/third_party/sqlite/test/corrupt3.test
new file mode 100755
index 0000000..af69f2e
--- /dev/null
+++ b/third_party/sqlite/test/corrupt3.test
@@ -0,0 +1,109 @@
+# 2007 April 6
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.
+#
+# $Id: corrupt3.test,v 1.2 2007/04/06 21:42:22 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Create a database with an overflow page.
+#
+do_test corrupt3-1.1 {
+ set bigstring [string repeat 0123456789 200]
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES($bigstring);
+ }
+ file size test.db
+} [expr {1024*3}]
+
+# Verify that the file format is as we expect. The page size
+# should be 1024 bytes. The only record should have a single
+# overflow page. The overflow page is page 3. The pointer to
+# the overflow page is on the last 4 bytes of page 2.
+#
+do_test corrupt3-1.2 {
+ hexio_get_int [hexio_read test.db 16 2]
+} 1024 ;# The page size is 1024
+do_test corrupt3-1.3 {
+ hexio_get_int [hexio_read test.db 20 1]
+} 0 ;# Unused bytes per page is 0
+do_test corrupt3-1.4 {
+ hexio_get_int [hexio_read test.db 2044 4]
+} 3 ;# Overflow page is 3
+do_test corrupt3-1.5 {
+ hexio_get_int [hexio_read test.db 2048 4]
+} 0 ;# First chained overflow is 0
+
+integrity_check corrupt3-1.6
+
+# Make the overflow chain loop back on itself. See if the
+# corruption is detected. (Actually, the last pointer in
+# an overflow chain is ignored, so this is not an error.)
+#
+do_test corrupt3-1.7 {
+ db close
+ hexio_write test.db 2048 [hexio_render_int32 3]
+ sqlite3 db test.db
+ catchsql {
+ SELECT x FROM t1
+ }
+} [list 0 $bigstring]
+integrity_check corrupt3-1.8
+
+# Change the pointer for the first page of the overflow
+# change to be a non-existant page.
+#
+do_test corrupt3-1.9 {
+ db close
+ hexio_write test.db 2044 [hexio_render_int32 4]
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,10) FROM t1
+ }
+} [list 0 0123456789]
+do_test corrupt3-1.10 {
+ catchsql {
+ PRAGMA integrity_check
+ }
+} {0 {{*** in database main ***
+On tree page 2 cell 0: invalid page number 4
+Page 3 is never used}}}
+do_test corrupt3-1.11 {
+ db close
+ hexio_write test.db 2044 [hexio_render_int32 0]
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,10) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+do_test corrupt3-1.12 {
+ catchsql {
+ PRAGMA integrity_check
+ }
+} {0 {{*** in database main ***
+On tree page 2 cell 0: 1 of 1 pages missing from overflow list starting at 0
+Page 3 is never used}}}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt4.test b/third_party/sqlite/test/corrupt4.test
new file mode 100755
index 0000000..952df70
--- /dev/null
+++ b/third_party/sqlite/test/corrupt4.test
@@ -0,0 +1,74 @@
+# 2007 Sept 7
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.
+#
+# $Id: corrupt4.test,v 1.1 2007/09/07 14:32:07 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Create a database with a freelist containing at least two pages.
+#
+do_test corrupt4-1.1 {
+ set bigstring [string repeat 0123456789 200]
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES($bigstring);
+ CREATE TABLE t2(y);
+ INSERT INTO t2 VALUES(1);
+ DROP TABLE t1;
+ }
+ file size test.db
+} [expr {1024*4}]
+
+# Verify that there are two pages on the freelist.
+#
+do_test corrupt4-1.2 {
+ execsql {PRAGMA freelist_count}
+} {2}
+
+# Get the page number for the trunk of the freelist.
+#
+set trunkpgno [hexio_get_int [hexio_read test.db 32 4]]
+set baseaddr [expr {($trunkpgno-1)*1024}]
+
+# Verify that the trunk of the freelist has exactly one
+# leaf.
+#
+do_test corrupt4-1.3 {
+ hexio_get_int [hexio_read test.db [expr {$::baseaddr+4}] 4]
+} {1}
+
+# Insert a negative number as the number of leaves on the trunk.
+# Then try to add a new element to the freelist.
+#
+do_test corrupt4-1.4 {
+ hexio_write test.db [expr {$::baseaddr+4}] [hexio_render_int32 -100000000]
+ db close
+ sqlite3 db test.db
+ catchsql {
+ DROP TABLE t2
+ }
+} {1 {database disk image is malformed}}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt5.test b/third_party/sqlite/test/corrupt5.test
new file mode 100755
index 0000000..0ee85f8
--- /dev/null
+++ b/third_party/sqlite/test/corrupt5.test
@@ -0,0 +1,44 @@
+# 2008 Jan 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.
+#
+# $Id: corrupt5.test,v 1.2 2008/03/19 13:03:34 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Create a database with a freelist containing at least two pages.
+#
+do_test corrupt5-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ CREATE INDEX i1 ON t1(a,b);
+ PRAGMA writable_schema=ON;
+ UPDATE sqlite_master SET name=NULL, sql=NULL WHERE name='i1';
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM t1
+ }
+} {1 {malformed database schema (?)}}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt6.test b/third_party/sqlite/test/corrupt6.test
new file mode 100755
index 0000000..2fed806
--- /dev/null
+++ b/third_party/sqlite/test/corrupt6.test
@@ -0,0 +1,251 @@
+# 2008 May 6
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file. It specifically focuses
+# on corrupt SerialTypeLen values.
+#
+# $Id: corrupt6.test,v 1.2 2008/05/19 15:37:10 shane Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Create a simple, small database.
+#
+do_test corrupt6-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES('varint32-01234567890123456789012345678901234567890123456789');
+ INSERT INTO t1(x) VALUES('varint32-01234567890123456789012345678901234567890123456789');
+ }
+ file size test.db
+} [expr {1024*2}]
+
+# Verify that the file format is as we expect. The page size
+# should be 1024 bytes.
+#
+do_test corrupt6-1.2 {
+ hexio_get_int [hexio_read test.db 16 2]
+} 1024 ;# The page size is 1024
+do_test corrupt6-1.3 {
+ hexio_get_int [hexio_read test.db 20 1]
+} 0 ;# Unused bytes per page is 0
+
+integrity_check corrupt6-1.4
+
+# Verify SerialTypeLen for first field of two records as we expect.
+# SerialTypeLen = (len*2+12) = 60*2+12 = 132
+do_test corrupt6-1.5.1 {
+ hexio_read test.db 1923 2
+} 8103 ;# First text field size is 81 03 == 131
+do_test corrupt6-1.5.2 {
+ hexio_read test.db 1987 2
+} 8103 ;# Second text field size is 81 03 == 131
+
+# Verify simple query results as expected.
+do_test corrupt6-1.6 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 0 {varint32 varint32} ]
+integrity_check corrupt6-1.7
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Increase SerialTypeLen by 2.
+do_test corrupt6-1.8.1 {
+ db close
+ hexio_write test.db 1923 8105
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Decrease SerialTypeLen by 2.
+do_test corrupt6-1.8.2 {
+ db close
+ hexio_write test.db 1923 8101
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Put value of record 1 / field 1 SerialTypeLen back.
+do_test corrupt6-1.8.3 {
+ db close
+ hexio_write test.db 1923 8103
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 0 {varint32 varint32} ]
+integrity_check corrupt6-1.8.4
+
+# Adjust value of record 2 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Increase SerialTypeLen by 2.
+do_test corrupt6-1.9.1 {
+ db close
+ hexio_write test.db 1987 8105
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 2 / field 2 SerialTypeLen and see if the
+# corruption is detected.
+# Decrease SerialTypeLen by 2.
+do_test corrupt6-1.9.2 {
+ db close
+ hexio_write test.db 1987 8101
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Put value of record 1 / field 2 SerialTypeLen back.
+do_test corrupt6-1.9.3 {
+ db close
+ hexio_write test.db 1987 8103
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 0 {varint32 varint32} ]
+integrity_check corrupt6-1.9.4
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF 7F (2 bytes)
+do_test corrupt6-1.10.1 {
+ db close
+ hexio_write test.db 1923 FF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF 7F (3 bytes)
+do_test corrupt6-1.10.2 {
+ db close
+ hexio_write test.db 1923 FFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF 7F (4 bytes)
+do_test corrupt6-1.10.3 {
+ db close
+ hexio_write test.db 1923 FFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF FF 7F (5 bytes)
+do_test corrupt6-1.10.4 {
+ db close
+ hexio_write test.db 1923 FFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF FF FF 7F (6 bytes, and overflows).
+do_test corrupt6-1.10.5 {
+ db close
+ hexio_write test.db 1923 FFFFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF FF FF FF 7F (7 bytes, and overflows).
+do_test corrupt6-1.10.6 {
+ db close
+ hexio_write test.db 1923 FFFFFFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF FF FF FF FF 7F (8 bytes, and overflows).
+do_test corrupt6-1.10.7 {
+ db close
+ hexio_write test.db 1923 FFFFFFFFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FF FF FF FF FF FF FF FF 7F (9 bytes, and overflows).
+do_test corrupt6-1.10.8 {
+ db close
+ hexio_write test.db 1923 FFFFFFFFFFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+# Adjust value of record 1 / field 1 SerialTypeLen and see if the
+# corruption is detected.
+# Set SerialTypeLen to FFFF FF FF FF FF FF FF FF 7F (10 bytes, and overflows).
+do_test corrupt6-1.10.9 {
+ db close
+ hexio_write test.db 1923 FFFFFFFFFFFFFFFFFF7F
+ sqlite3 db test.db
+ catchsql {
+ SELECT substr(x,1,8) FROM t1
+ }
+} [list 1 {database disk image is malformed}]
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt7.test b/third_party/sqlite/test/corrupt7.test
new file mode 100755
index 0000000..8b0b2fb
--- /dev/null
+++ b/third_party/sqlite/test/corrupt7.test
@@ -0,0 +1,102 @@
+# 2008 June 11
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file. It specifically focuses
+# on corrupt cell offsets in a btree page.
+#
+# $Id: corrupt7.test,v 1.4 2008/07/26 18:26:10 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Create a simple, small database.
+#
+do_test corrupt7-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(1);
+ INSERT INTO t1(x) VALUES(2);
+ INSERT INTO t1(x) SELECT x+2 FROM t1;
+ INSERT INTO t1(x) SELECT x+4 FROM t1;
+ INSERT INTO t1(x) SELECT x+8 FROM t1;
+ }
+ file size test.db
+} [expr {1024*2}]
+
+# Verify that the file format is as we expect. The page size
+# should be 1024 bytes.
+#
+do_test corrupt7-1.2 {
+ hexio_get_int [hexio_read test.db 16 2]
+} 1024 ;# The page size is 1024
+do_test corrupt7-1.3 {
+ hexio_get_int [hexio_read test.db 20 1]
+} 0 ;# Unused bytes per page is 0
+
+integrity_check corrupt7-1.4
+
+# Deliberately corrupt some of the cell offsets in the btree page
+# on page 2 of the database.
+#
+do_test corrupt7-2.1 {
+ db close
+ hexio_write test.db 1062 FF
+ sqlite3 db test.db
+ db eval {PRAGMA integrity_check(1)}
+} {{*** in database main ***
+Corruption detected in cell 15 on page 2}}
+do_test corrupt7-2.2 {
+ db close
+ hexio_write test.db 1062 04
+ sqlite3 db test.db
+ db eval {PRAGMA integrity_check(1)}
+} {{*** in database main ***
+Corruption detected in cell 15 on page 2}}
+
+do_test corrupt7-3.1 {
+ execsql {
+ DROP TABLE t1;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(100, 'one hundred');
+ INSERT INTO t1 VALUES(100000, 'one hundred thousand');
+ CREATE INDEX i1 ON t1(b);
+ }
+ db close
+
+ # Locate the 3rd cell in the index.
+ set cell_offset [hexio_get_int [hexio_read test.db [expr 1024*2 + 12] 2]]
+ incr cell_offset [expr 1024*2]
+ incr cell_offset 1
+
+ # This write corrupts the "header-size" field of the database record
+ # stored in the index cell. At one point this was causing sqlite to
+ # reference invalid memory.
+ hexio_write test.db $cell_offset FFFF7F
+
+ sqlite3 db test.db
+ catchsql {
+ SELECT b FROM t1 WHERE b > 'o' AND b < 'p';
+ }
+} {1 {database disk image is malformed}}
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt8.test b/third_party/sqlite/test/corrupt8.test
new file mode 100755
index 0000000..994f4aa
--- /dev/null
+++ b/third_party/sqlite/test/corrupt8.test
@@ -0,0 +1,99 @@
+# 2008 July 9
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file. It specifically focuses
+# on corrupt pointer map pages.
+#
+# $Id: corrupt8.test,v 1.2 2008/07/11 03:34:10 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas||!autovacuum {
+ finish_test
+ return
+}
+
+# Create a database to work with.
+#
+do_test corrupt8-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=1;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(1);
+ INSERT INTO t1(x) VALUES(2);
+ INSERT INTO t1(x) SELECT x+2 FROM t1;
+ INSERT INTO t1(x) SELECT x+4 FROM t1;
+ INSERT INTO t1(x) SELECT x+8 FROM t1;
+ INSERT INTO t1(x) SELECT x+16 FROM t1;
+ INSERT INTO t1(x) SELECT x+32 FROM t1;
+ INSERT INTO t1(x) SELECT x+64 FROM t1;
+ INSERT INTO t1(x) SELECT x+128 FROM t1;
+ INSERT INTO t1(x) SELECT x+256 FROM t1;
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 SELECT x, x*x FROM t1;
+ }
+ expr {[file size test.db]>1024*12}
+} {1}
+integrity_check corrupt8-1.2
+
+# Loop through each ptrmap entry. Corrupt the entry and make sure the
+# corruption is detected by the integrity_check.
+#
+for {set i 1024} {$i<2048} {incr i 5} {
+ set oldval [hexio_read test.db $i 1]
+ if {$oldval==0} break
+ hexio_write test.db $i 00
+ do_test corrupt8-2.$i.0 {
+ db close
+ sqlite3 db test.db
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+ } {1}
+ for {set k 1} {$k<=5} {incr k} {
+ if {$k==$oldval} continue
+ hexio_write test.db $i 0$k
+ do_test corrupt8-2.$i.$k {
+ db close
+ sqlite3 db test.db
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+ } {1}
+ }
+ hexio_write test.db $i 06
+ do_test corrupt8-2.$i.6 {
+ db close
+ sqlite3 db test.db
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+ } {1}
+ hexio_write test.db $i $oldval
+ if {$oldval>2} {
+ set i2 [expr {$i+1+$i%4}]
+ set oldval [hexio_read test.db $i2 1]
+ hexio_write test.db $i2 [format %02x [expr {($oldval+1)&0xff}]]
+ do_test corrupt8-2.$i.7 {
+ db close
+ sqlite3 db test.db
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+ } {1}
+ hexio_write test.db $i2 $oldval
+ }
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/corrupt9.test b/third_party/sqlite/test/corrupt9.test
new file mode 100755
index 0000000..ee7410d
--- /dev/null
+++ b/third_party/sqlite/test/corrupt9.test
@@ -0,0 +1,131 @@
+# 2008 July 9
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file. It specifically focuses
+# on corruption in the form of duplicate entries no the freelist.
+#
+# $Id: corrupt9.test,v 1.2 2008/07/16 12:25:32 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+# Return the offset to the first (trunk) page of the freelist. Return
+# zero of the freelist is empty.
+#
+proc freelist_trunk_offset {filename} {
+ if {[hexio_read $filename 36 4]==0} {return 0}
+ set pgno [hexio_get_int [hexio_read $filename 32 4]]
+ return [expr {($pgno-1)*[hexio_get_int [hexio_read $filename 16 2]]}]
+}
+
+# This procedure looks at the first trunk page of the freelist and
+# corrupts that page by overwriting up to N entries with duplicates
+# of the first entry.
+#
+proc corrupt_freelist {filename N} {
+ set offset [freelist_trunk_offset $filename]
+ if {$offset==0} {error "Freelist is empty"}
+ set cnt [hexio_get_int [hexio_read $filename [expr {$offset+4}] 4]]
+ set pgno [hexio_read $filename [expr {$offset+8}] 4]
+ for {set i 12} {$N>0 && $i<8+4*$cnt} {incr i 4; incr N -1} {
+ hexio_write $filename [expr {$offset+$i}] $pgno
+ }
+}
+
+# Create a database to work with. Make sure there are plenty of
+# entries on the freelist.
+#
+do_test corrupt9-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=NONE;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(1);
+ INSERT INTO t1(x) VALUES(2);
+ INSERT INTO t1(x) SELECT x+2 FROM t1;
+ INSERT INTO t1(x) SELECT x+4 FROM t1;
+ INSERT INTO t1(x) SELECT x+8 FROM t1;
+ INSERT INTO t1(x) SELECT x+16 FROM t1;
+ INSERT INTO t1(x) SELECT x+32 FROM t1;
+ INSERT INTO t1(x) SELECT x+64 FROM t1;
+ INSERT INTO t1(x) SELECT x+128 FROM t1;
+ INSERT INTO t1(x) SELECT x+256 FROM t1;
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 SELECT x, x*x FROM t1;
+ CREATE INDEX i1 ON t1(x);
+ CREATE INDEX i2 ON t2(b,a);
+ DROP INDEX i2;
+ }
+ expr {[file size test.db]>1024*24}
+} {1}
+integrity_check corrupt9-1.2
+
+# Corrupt the freelist by adding duplicate entries to the freelist.
+# Make sure the corruption is detected.
+#
+db close
+file copy -force test.db test.db-template
+
+corrupt_freelist test.db 1
+sqlite3 db test.db
+do_test corrupt9-2.1 {
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+} {1}
+do_test corrupt9-2.2 {
+ catchsql {
+ CREATE INDEX i2 ON t2(b,a);
+ REINDEX;
+ }
+} {1 {database disk image is malformed}}
+
+
+db close
+file copy -force test.db-template test.db
+corrupt_freelist test.db 2
+sqlite3 db test.db
+do_test corrupt9-3.1 {
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+} {1}
+do_test corrupt9-3.2 {
+ catchsql {
+ CREATE INDEX i2 ON t2(b,a);
+ REINDEX;
+ }
+} {1 {database disk image is malformed}}
+
+db close
+file copy -force test.db-template test.db
+corrupt_freelist test.db 3
+sqlite3 db test.db
+do_test corrupt9-4.1 {
+ set x [db eval {PRAGMA integrity_check}]
+ expr {$x!="ok"}
+} {1}
+do_test corrupt9-4.2 {
+ catchsql {
+ CREATE INDEX i2 ON t2(b,a);
+ REINDEX;
+ }
+} {1 {database disk image is malformed}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/corruptA.test b/third_party/sqlite/test/corruptA.test
new file mode 100755
index 0000000..fa3257f
--- /dev/null
+++ b/third_party/sqlite/test/corruptA.test
@@ -0,0 +1,72 @@
+# 2008 July 11
+#
+# 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.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file. It specifically focuses
+# on corrupt database headers.
+#
+# $Id: corruptA.test,v 1.1 2008/07/11 16:39:23 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Create a database to work with.
+#
+do_test corruptA-1.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(1);
+ }
+ expr {[file size test.db]>=1024}
+} {1}
+integrity_check corruptA-1.2
+
+# Corrupt the file header in various ways and make sure the corruption
+# is detected when opening the database file.
+#
+db close
+file copy -force test.db test.db-template
+
+do_test corruptA-2.1 {
+ file copy -force test.db-template test.db
+ hexio_write test.db 19 02 ;# the read format number
+ sqlite3 db test.db
+ catchsql {SELECT * FROM t1}
+} {1 {file is encrypted or is not a database}}
+
+do_test corruptA-2.2 {
+ db close
+ file copy -force test.db-template test.db
+ hexio_write test.db 21 41 ;# max embedded payload fraction
+ sqlite3 db test.db
+ catchsql {SELECT * FROM t1}
+} {1 {file is encrypted or is not a database}}
+
+do_test corruptA-2.3 {
+ db close
+ file copy -force test.db-template test.db
+ hexio_write test.db 22 1f ;# min embedded payload fraction
+ sqlite3 db test.db
+ catchsql {SELECT * FROM t1}
+} {1 {file is encrypted or is not a database}}
+
+do_test corruptA-2.4 {
+ db close
+ file copy -force test.db-template test.db
+ hexio_write test.db 23 21 ;# min leaf payload fraction
+ sqlite3 db test.db
+ catchsql {SELECT * FROM t1}
+} {1 {file is encrypted or is not a database}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/crash.test b/third_party/sqlite/test/crash.test
new file mode 100755
index 0000000..42f41a3
--- /dev/null
+++ b/third_party/sqlite/test/crash.test
@@ -0,0 +1,411 @@
+# 2001 September 15
+#
+# 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 file is testing the ability of the database to
+# uses its rollback journal to recover intact (no database corruption)
+# from a power failure during the middle of a COMMIT. The OS interface
+# modules are overloaded using the modified I/O routines found in test6.c.
+# These routines allow us to simulate the kind of file damage that
+# occurs after a power failure.
+#
+# $Id: crash.test,v 1.27 2008/01/08 15:18:52 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+set repeats 100
+#set repeats 10
+
+# The following procedure computes a "signature" for table "abc". If
+# abc changes in any way, the signature should change.
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
+}
+proc signature2 {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc2}]
+}
+
+#--------------------------------------------------------------------------
+# Simple crash test:
+#
+# crash-1.1: Create a database with a table with two rows.
+# crash-1.2: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
+# the first journal-sync.
+# crash-1.3: Ensure the database is in the same state as after crash-1.1.
+# crash-1.4: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
+# the first database-sync.
+# crash-1.5: Ensure the database is in the same state as after crash-1.1.
+# crash-1.6: Run a 'DELETE FROM abc WHERE a = 1' that crashes during
+# the second journal-sync.
+# crash-1.7: Ensure the database is in the same state as after crash-1.1.
+#
+# Tests 1.8 through 1.11 test for crashes on the third journal sync and
+# second database sync. Neither of these is required in such a small test
+# case, so these tests are just to verify that the test infrastructure
+# operates as expected.
+#
+do_test crash-1.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ }
+ set ::sig [signature]
+ expr 0
+} {0}
+for {set i 0} {$i<10} {incr i} {
+ set seed [expr {int(abs(rand()*10000))}]
+ do_test crash-1.2.$i {
+ crashsql -delay 1 -file test.db-journal -seed $seed {
+ DELETE FROM abc WHERE a = 1;
+ }
+ } {1 {child process exited abnormally}}
+ do_test crash-1.3.$i {
+ signature
+ } $::sig
+}
+do_test crash-1.4 {
+ crashsql -delay 1 -file test.db {
+ DELETE FROM abc WHERE a = 1;
+ }
+} {1 {child process exited abnormally}}
+do_test crash-1.5 {
+ signature
+} $::sig
+do_test crash-1.6 {
+ crashsql -delay 2 -file test.db-journal {
+ DELETE FROM abc WHERE a = 1;
+ }
+} {1 {child process exited abnormally}}
+do_test crash-1.7 {
+ catchsql {
+ SELECT * FROM abc;
+ }
+} {0 {1 2 3 4 5 6}}
+
+do_test crash-1.8 {
+ crashsql -delay 3 -file test.db-journal {
+ DELETE FROM abc WHERE a = 1;
+ }
+} {0 {}}
+do_test crash-1.9 {
+ catchsql {
+ SELECT * FROM abc;
+ }
+} {0 {4 5 6}}
+do_test crash-1.10 {
+ crashsql -delay 2 -file test.db {
+ DELETE FROM abc WHERE a = 4;
+ }
+} {0 {}}
+do_test crash-1.11 {
+ catchsql {
+ SELECT * FROM abc;
+ }
+} {0 {}}
+
+#--------------------------------------------------------------------------
+# The following tests test recovery when both the database file and the the
+# journal file contain corrupt data. This can happen after pages are
+# written to the database file before a transaction is committed due to
+# cache-pressure.
+#
+# crash-2.1: Insert 18 pages of data into the database.
+# crash-2.2: Check the database file size looks ok.
+# crash-2.3: Delete 15 or so pages (with a 10 page page-cache), then crash.
+# crash-2.4: Ensure the database is in the same state as after crash-2.1.
+#
+# Test cases crash-2.5 and crash-2.6 check that the database is OK if the
+# crash occurs during the main database file sync. But this isn't really
+# different from the crash-1.* cases.
+#
+do_test crash-2.1 {
+ execsql { BEGIN }
+ for {set n 0} {$n < 1000} {incr n} {
+ execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
+ }
+ execsql { COMMIT }
+ set ::sig [signature]
+ execsql { SELECT sum(a), sum(b), sum(c) from abc }
+} {499500 999000 1498500}
+do_test crash-2.2 {
+ expr ([file size test.db] / 1024)>16
+} {1}
+do_test crash-2.3 {
+ crashsql -delay 2 -file test.db-journal {
+ DELETE FROM abc WHERE a < 800;
+ }
+} {1 {child process exited abnormally}}
+do_test crash-2.4 {
+ signature
+} $sig
+do_test crash-2.5 {
+ crashsql -delay 1 -file test.db {
+ DELETE FROM abc WHERE a<800;
+ }
+} {1 {child process exited abnormally}}
+do_test crash-2.6 {
+ signature
+} $sig
+
+#--------------------------------------------------------------------------
+# The crash-3.* test cases are essentially the same test as test case
+# crash-2.*, but with a more complicated data set.
+#
+# The test is repeated a few times with different seeds for the random
+# number generator in the crashing executable. Because there is no way to
+# seed the random number generator directly, some SQL is added to the test
+# case to 'use up' a different quantity random numbers before the test SQL
+# is executed.
+#
+
+# Make sure the file is much bigger than the pager-cache (10 pages). This
+# ensures that cache-spills happen regularly.
+do_test crash-3.0 {
+ execsql {
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ expr ([file size test.db] / 1024) > 450
+} {1}
+for {set i 1} {$i < $repeats} {incr i} {
+ set sig [signature]
+ do_test crash-3.$i.1 {
+ set seed [expr {int(abs(rand()*10000))}]
+ crashsql -delay [expr $i%5 + 1] -file test.db-journal -seed $seed "
+ BEGIN;
+ SELECT random() FROM abc LIMIT $i;
+ INSERT INTO abc VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc WHERE random()%10!=0;
+ COMMIT;
+ "
+ } {1 {child process exited abnormally}}
+ do_test crash-3.$i.2 {
+ signature
+ } $sig
+}
+
+#--------------------------------------------------------------------------
+# The following test cases - crash-4.* - test the correct recovery of the
+# database when a crash occurs during a multi-file transaction.
+#
+# crash-4.1.*: Test recovery when crash occurs during sync() of the
+# main database journal file.
+# crash-4.2.*: Test recovery when crash occurs during sync() of an
+# attached database journal file.
+# crash-4.3.*: Test recovery when crash occurs during sync() of the master
+# journal file.
+#
+ifcapable attach {
+ do_test crash-4.0 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.default_cache_size = 10;
+ CREATE TABLE aux.abc2 AS SELECT 2*a as a, 2*b as b, 2*c as c FROM abc;
+ }
+ expr ([file size test2.db] / 1024) > 450
+ } {1}
+
+ set fin 0
+ for {set i 1} {$i<$repeats} {incr i} {
+ set seed [expr {int(abs(rand()*10000))}]
+ set sig [signature]
+ set sig2 [signature2]
+ do_test crash-4.1.$i.1 {
+ set c [crashsql -delay $i -file test.db-journal -seed $::seed "
+ ATTACH 'test2.db' AS aux;
+ BEGIN;
+ SELECT randstr($i,$i) FROM abc LIMIT $i;
+ INSERT INTO abc VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc WHERE random()%10!=0;
+ INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc2 WHERE random()%10!=0;
+ COMMIT;
+ "]
+ if { $c == {0 {}} } {
+ set ::fin 1
+ set c {1 {child process exited abnormally}}
+ }
+ set c
+ } {1 {child process exited abnormally}}
+ if {$::fin} break
+ do_test crash-4.1.$i.2 {
+ signature
+ } $sig
+ do_test crash-4.1.$i.3 {
+ signature2
+ } $sig2
+ }
+ set i 0
+ set fin 0
+ while {[incr i]} {
+ set seed [expr {int(abs(rand()*10000))}]
+ set sig [signature]
+ set sig2 [signature2]
+ set ::fin 0
+ do_test crash-4.2.$i.1 {
+ set c [crashsql -delay $i -file test2.db-journal -seed $::seed "
+ ATTACH 'test2.db' AS aux;
+ BEGIN;
+ SELECT randstr($i,$i) FROM abc LIMIT $i;
+ INSERT INTO abc VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc WHERE random()%10!=0;
+ INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc2 WHERE random()%10!=0;
+ COMMIT;
+ "]
+ if { $c == {0 {}} } {
+ set ::fin 1
+ set c {1 {child process exited abnormally}}
+ }
+ set c
+ } {1 {child process exited abnormally}}
+ if { $::fin } break
+ do_test crash-4.2.$i.2 {
+ signature
+ } $sig
+ do_test crash-4.2.$i.3 {
+ signature2
+ } $sig2
+ }
+ for {set i 1} {$i < 5} {incr i} {
+ set sig [signature]
+ set sig2 [signature2]
+ do_test crash-4.3.$i.1 {
+ crashsql -delay 1 -file test.db-mj* "
+ ATTACH 'test2.db' AS aux;
+ BEGIN;
+ SELECT random() FROM abc LIMIT $i;
+ INSERT INTO abc VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc WHERE random()%10!=0;
+ INSERT INTO abc2 VALUES(randstr(10,10), 0, 0);
+ DELETE FROM abc2 WHERE random()%10!=0;
+ COMMIT;
+ "
+ } {1 {child process exited abnormally}}
+ do_test crash-4.3.$i.2 {
+ signature
+ } $sig
+ do_test crash-4.3.$i.3 {
+ signature2
+ } $sig2
+ }
+}
+
+#--------------------------------------------------------------------------
+# The following test cases - crash-5.* - exposes a bug that existed in the
+# sqlite3pager_movepage() API used by auto-vacuum databases.
+# database when a crash occurs during a multi-file transaction. See comments
+# in test crash-5.3 for details.
+#
+db close
+file delete -force test.db
+sqlite3 db test.db
+do_test crash-5.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c); -- Root page 3
+ INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- Overflow page 4
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+} {}
+do_test crash-5.2 {
+ expr [file size test.db] / 1024
+} [expr [string match [execsql {pragma auto_vacuum}] 1] ? 11 : 10]
+set sig [signature]
+do_test crash-5.3 {
+# The SQL below is used to expose a bug that existed in
+# sqlite3pager_movepage() during development of the auto-vacuum feature. It
+# functions as follows:
+#
+# 1: Begin a transaction.
+# 2: Put page 4 on the free-list (was the overflow page for the row deleted).
+# 3: Write data to page 4 (it becomes the overflow page for the row inserted).
+# The old page 4 data has been written to the journal file, but the
+# journal file has not been sync()hronized.
+# 4: Create a table, which calls sqlite3pager_movepage() to move page 4
+# to the end of the database (page 12) to make room for the new root-page.
+# 5: Put pressure on the pager-cache. This results in page 4 being written
+# to the database file to make space in the cache to load a new page. The
+# bug was that page 4 was written to the database file before the journal
+# is sync()hronized.
+# 6: Commit. A crash occurs during the sync of the journal file.
+#
+# End result: Before the bug was fixed, data has been written to page 4 of the
+# database file and the journal file does not contain trustworthy rollback
+# data for this page.
+#
+ crashsql -delay 1 -file test.db-journal {
+ BEGIN; -- 1
+ DELETE FROM abc WHERE oid = 1; -- 2
+ INSERT INTO abc VALUES(randstr(1500,1500), 0, 0); -- 3
+ CREATE TABLE abc2(a, b, c); -- 4
+ SELECT * FROM abc; -- 5
+ COMMIT; -- 6
+ }
+} {1 {child process exited abnormally}}
+integrity_check crash-5.4
+do_test crash-5.5 {
+ signature
+} $sig
+
+#--------------------------------------------------------------------------
+# The following test cases - crash-6.* - test that a DROP TABLE operation
+# is correctly rolled back in the event of a crash while the database file
+# is being written. This is mainly to test that all pages are written to the
+# journal file before truncation in an auto-vacuum database.
+#
+do_test crash-6.1 {
+ crashsql -delay 1 -file test.db {
+ DROP TABLE abc;
+ }
+} {1 {child process exited abnormally}}
+do_test crash-6.2 {
+ signature
+} $sig
+
+#--------------------------------------------------------------------------
+# These test cases test the case where the master journal file name is
+# corrupted slightly so that the corruption has to be detected by the
+# checksum.
+do_test crash-7.1 {
+ crashsql -delay 1 -file test.db {
+ ATTACH 'test2.db' AS aux;
+ BEGIN;
+ INSERT INTO abc VALUES(randstr(1500,1500), 0, 0);
+ INSERT INTO abc2 VALUES(randstr(1500,1500), 0, 0);
+ COMMIT;
+ }
+
+ # Change the checksum value for the master journal name.
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ seek $f [expr [file size test.db-journal] - 12]
+ puts -nonewline $f "\00\00\00\00"
+ close $f
+} {}
+do_test crash-7.2 {
+ signature
+} $sig
+
+finish_test
diff --git a/third_party/sqlite/test/crash2.test b/third_party/sqlite/test/crash2.test
new file mode 100755
index 0000000..4320779
--- /dev/null
+++ b/third_party/sqlite/test/crash2.test
@@ -0,0 +1,132 @@
+# 2001 September 15
+#
+# 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 file is testing the ability of the database to
+# uses its rollback journal to recover intact (no database corruption)
+# from a power failure during the middle of a COMMIT. Even more
+# specifically, the tests in this file verify this functionality
+# for storage mediums with various sector sizes.
+#
+# $Id: crash2.test,v 1.5 2007/08/24 11:52:29 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+db close
+
+# This test is designed to check that the crash-test infrastructure
+# can create files that do not consist of an integer number of
+# simulated disk blocks (i.e. 3KB file using 2KB disk blocks).
+#
+do_test crash2-1.1 {
+ crashsql -delay 500 -file test.db -blocksize 2048 {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ BEGIN;
+ CREATE TABLE abc AS SELECT 1 AS a, 2 AS b, 3 AS c;
+ CREATE TABLE def AS SELECT 1 AS d, 2 AS e, 3 AS f;
+ COMMIT;
+ }
+ file size test.db
+} {3072}
+
+for {set ii 0} {$ii < 5} {incr ii} {
+
+ # Simple test using the database created above: Create a new
+ # table so that page 1 and page 4 are modified. Using a
+ # block-size of 2048 and page-size of 1024, this means
+ # pages 2 and 3 must also be saved in the journal to avoid
+ # risking corruption.
+ #
+ # The loop is so that this test can be run with a couple
+ # of different seeds for the random number generator.
+ #
+ do_test crash2-1.2.$ii {
+ crashsql -file test.db -blocksize 2048 [subst {
+ [string repeat {SELECT random();} $ii]
+ CREATE TABLE hij(h, i, j);
+ }]
+ sqlite3 db test.db
+ db eval {PRAGMA integrity_check}
+ } {ok}
+}
+
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
+}
+
+# Test case for crashing during journal sync with simulated
+# sector-size values from 1024 to 8192.
+#
+do_test crash2-2.0 {
+ execsql BEGIN
+ for {set n 0} {$n < 1000} {incr n} {
+ execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
+ }
+ execsql {
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ execsql COMMIT
+ expr ([file size test.db] / 1024) > 450
+} {1}
+for {set i 1} {$i < 30} {incr i} {
+ set sig [signature]
+ set sector [expr 1024 * 1<<($i%4)]
+ db close
+ do_test crash2-2.$i.1 {
+ crashsql -blocksize $sector -delay [expr $i%5 + 1] -file test.db-journal "
+ BEGIN;
+ SELECT random() FROM abc LIMIT $i;
+ INSERT INTO abc SELECT randstr(10,10), 0, 0 FROM abc WHERE random()%2==0;
+ DELETE FROM abc WHERE random()%2!=0;
+ COMMIT;
+ "
+ } {1 {child process exited abnormally}}
+ do_test crash2-2.$i.2 {
+ sqlite3 db test.db
+ signature
+ } $sig
+}
+
+
+# Test case for crashing during database sync with simulated
+# sector-size values from 1024 to 8192.
+#
+for {set i 1} {$i < 10} {incr i} {
+ set sig [signature]
+ set sector [expr 1024 * 1<<($i%4)]
+ db close
+ do_test crash2-3.$i.1 {
+ crashsql -blocksize $sector -file test.db "
+ BEGIN;
+ SELECT random() FROM abc LIMIT $i;
+ INSERT INTO abc SELECT randstr(10,10), 0, 0 FROM abc WHERE random()%2==0;
+ DELETE FROM abc WHERE random()%2!=0;
+ COMMIT;
+ "
+ } {1 {child process exited abnormally}}
+ do_test crash2-3.$i.2 {
+ sqlite3 db test.db
+ signature
+ } $sig
+}
+
+finish_test
diff --git a/third_party/sqlite/test/crash3.test b/third_party/sqlite/test/crash3.test
new file mode 100755
index 0000000..9b7a678
--- /dev/null
+++ b/third_party/sqlite/test/crash3.test
@@ -0,0 +1,190 @@
+# 2007 August 23
+#
+# 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 contains tests that verify that SQLite can correctly rollback
+# databases after crashes when using the special IO modes triggered
+# by device IOCAP flags.
+#
+# $Id: crash3.test,v 1.4 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+proc do_test2 {name tcl res1 res2} {
+ set script [subst -nocommands {
+ do_test $name {
+ set res1 {$res1}
+ set res2 {$res2}
+ set res [eval {$tcl}]
+ if {[set res] eq [set res1] || [set res] eq [set res2]} {
+ set res "{[set res1]} or {[set res2]}"
+ }
+ set res
+ } {{$res1} or {$res2}}
+ }]
+ uplevel $script
+}
+
+# This block tests crash-recovery when the IOCAP_ATOMIC flags is set.
+#
+# Each iteration of the following loop sets up the database to contain
+# the following schema and data:
+#
+# CREATE TABLE abc(a, b, c);
+# INSERT INTO abc VALUES(1, 2, 3);
+#
+# Then execute the SQL statement, scheduling a crash for part-way through
+# the first sync() of either the database file or the journal file (often
+# the journal file is not required - meaning no crash occurs).
+#
+# After the crash (or absence of a crash), open the database and
+# verify that:
+#
+# * The integrity check passes, and
+# * The contents of table abc is either {1 2 3} or the value specified
+# to the right of the SQL statement below.
+#
+# The procedure is repeated 10 times for each SQL statement. Five times
+# with the crash scheduled for midway through the first journal sync (if
+# any), and five times with the crash midway through the database sync.
+#
+set tn 1
+foreach {sql res2} [list \
+ {INSERT INTO abc VALUES(4, 5, 6)} {1 2 3 4 5 6} \
+ {DELETE FROM abc} {} \
+ {INSERT INTO abc SELECT * FROM abc} {1 2 3 1 2 3} \
+ {UPDATE abc SET a = 2} {2 2 3} \
+ {INSERT INTO abc VALUES(4, 5, randstr(1000,1000))} {n/a} \
+ {CREATE TABLE def(d, e, f)} {n/a} \
+] {
+ for {set ii 0} {$ii < 10} {incr ii} {
+
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ do_test crash3-1.$tn.1 {
+ execsql {
+ PRAGMA page_size = 1024;
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ COMMIT;
+ }
+ } {}
+ db close
+
+ set crashfile test.db
+ if {($ii%2)==0} { append crashfile -journal }
+ set rand "SELECT randstr($tn,$tn);"
+ do_test crash3-1.$tn.2 [subst {
+ crashsql -file $crashfile -char atomic {$rand $sql}
+ sqlite3 db test.db
+ execsql { PRAGMA integrity_check; }
+ }] {ok}
+
+ do_test2 crash3-1.$tn.3 {
+ execsql { SELECT * FROM abc }
+ } {1 2 3} $res2
+
+ incr tn
+ }
+}
+
+# This block tests both the IOCAP_SEQUENTIAL and IOCAP_SAFE_APPEND flags.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+do_test crash3-2.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ CREATE TABLE def(d PRIMARY KEY, e, f);
+ PRAGMA default_cache_size = 10;
+ INSERT INTO abc VALUES(randstr(10,1000),randstr(10,1000),randstr(10,1000));
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+ COMMIT;
+ }
+} {}
+
+set tn 1
+foreach {::crashfile ::delay ::char} {
+ test.db 1 sequential
+ test.db 1 safe_append
+ test.db-journal 1 sequential
+ test.db-journal 1 safe_append
+ test.db-journal 2 safe_append
+ test.db-journal 2 sequential
+ test.db-journal 3 sequential
+ test.db-journal 3 safe_append
+} {
+ for {set ii 0} {$ii < 100} {incr ii} {
+ set ::SQL [subst {
+ SELECT randstr($ii,$ii+10);
+ BEGIN;
+ DELETE FROM abc WHERE random()%5;
+ INSERT INTO abc
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000)
+ FROM abc
+ WHERE (random()%5)==0;
+ DELETE FROM def WHERE random()%5;
+ INSERT INTO def
+ SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000)
+ FROM def
+ WHERE (random()%5)==0;
+ COMMIT;
+ }]
+
+ do_test crash3-2.$tn.$ii {
+ crashsql -file $::crashfile -delay $::delay -char $::char $::SQL
+ db close
+ sqlite3 db test.db
+ execsql {PRAGMA integrity_check}
+ } {ok}
+ }
+ incr tn
+}
+
+# The following block tests an interaction between IOCAP_ATOMIC and
+# IOCAP_SEQUENTIAL. At one point, if both flags were set, small
+# journal files that contained only a single page, but were required
+# for some other reason (i.e. nTrunk) were not being written to
+# disk.
+#
+for {set ii 0} {$ii < 10} {incr ii} {
+ db close
+ file delete -force test.db test.db-journal
+ crashsql -file test.db -char {sequential atomic} {
+ CREATE TABLE abc(a, b, c);
+ }
+ sqlite3 db test.db
+ do_test crash3-3.$ii {
+ execsql {PRAGMA integrity_check}
+ } {ok}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/crash4.test b/third_party/sqlite/test/crash4.test
new file mode 100755
index 0000000..839299b
--- /dev/null
+++ b/third_party/sqlite/test/crash4.test
@@ -0,0 +1,102 @@
+# 2008 January 8
+#
+# 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 contains additional tests to verify that SQLite database
+# file survive a power loss or OS crash.
+#
+# $Id: crash4.test,v 1.3 2008/01/16 17:46:38 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+
+# A sequence of SQL commands:
+#
+set sql_cmd_list {
+ {CREATE TABLE a(id INTEGER, name CHAR(50))}
+ {INSERT INTO a(id,name) VALUES(1,'one')}
+ {INSERT INTO a(id,name) VALUES(2,'two')}
+ {INSERT INTO a(id,name) VALUES(3,'three')}
+ {INSERT INTO a(id,name) VALUES(4,'four')}
+ {INSERT INTO a(id,name) VALUES(5,'five')}
+ {INSERT INTO a(id,name) VALUES(6,'six')}
+ {INSERT INTO a(id,name) VALUES(7,'seven')}
+ {INSERT INTO a(id,name) VALUES(8,'eight')}
+ {INSERT INTO a(id,name) VALUES(9,'nine')}
+ {INSERT INTO a(id,name) VALUES(10,'ten')}
+ {UPDATE A SET name='new text for row 3' WHERE id=3}
+}
+
+# Assume that a database is created by evaluating the SQL statements
+# in $sql_cmd_list. Compute a set of checksums that capture the state
+# of the database after each statement. Also include a checksum for
+# the state of the database prior to any of these statements.
+#
+set crash4_cksum_set {}
+lappend crash4_cksum_set [allcksum db]
+foreach cmd $sql_cmd_list {
+ db eval $cmd
+ lappend crash4_cksum_set [allcksum db]
+}
+
+# Run the sequence of SQL statements shown above repeatedly.
+# Close and reopen the database right before the UPDATE statement.
+# On each repetition, introduce database corruption typical of
+# what might be seen in a power loss or OS crash.
+#
+# Slowly increase the delay before the crash, repeating the test
+# over and over. Stop testing when the entire sequence of SQL
+# statements runs to completing without hitting the crash.
+#
+for {set cnt 1; set fin 0} {!$fin} {incr cnt} {
+ db close
+ file delete -force test.db test.db-journal
+ do_test crash4-1.$cnt.1 {
+ set seed [expr {int(abs(rand()*10000))}]
+ set delay [expr {int($cnt/50)+1}]
+ set file [expr {($cnt&1)?"test.db":"test.db-journal"}]
+ set c [crashsql -delay $delay -file $file -seed $seed -tclbody {
+ db eval {CREATE TABLE a(id INTEGER, name CHAR(50))}
+ db eval {INSERT INTO a(id,name) VALUES(1,'one')}
+ db eval {INSERT INTO a(id,name) VALUES(2,'two')}
+ db eval {INSERT INTO a(id,name) VALUES(3,'three')}
+ db eval {INSERT INTO a(id,name) VALUES(4,'four')}
+ db eval {INSERT INTO a(id,name) VALUES(5,'five')}
+ db eval {INSERT INTO a(id,name) VALUES(6,'six')}
+ db eval {INSERT INTO a(id,name) VALUES(7,'seven')}
+ db eval {INSERT INTO a(id,name) VALUES(8,'eight')}
+ db eval {INSERT INTO a(id,name) VALUES(9,'nine')}
+ db eval {INSERT INTO a(id,name) VALUES(10,'ten')}
+ db close
+ sqlite3 db test.db
+ db eval {UPDATE A SET name='new text for row 3' WHERE id=3}
+ db close
+ } {}]
+ if {$c==[list 0 {}]} {
+ set ::fin 1
+ set c [list 1 {child process exited abnormally}]
+ }
+ set c
+ } {1 {child process exited abnormally}}
+ sqlite3 db test.db
+ integrity_check crash4-1.$cnt.2
+ do_test crash4-1.$cnt.3 {
+ set x [lsearch $::crash4_cksum_set [allcksum db]]
+ expr {$x>=0}
+ } {1}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/crash5.test b/third_party/sqlite/test/crash5.test
new file mode 100755
index 0000000..e8faa01
--- /dev/null
+++ b/third_party/sqlite/test/crash5.test
@@ -0,0 +1,110 @@
+
+# 2007 Aug 13
+#
+# 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 tests aspects of recovery from a malloc() failure
+# in a CREATE INDEX statement.
+#
+# $Id: crash5.test,v 1.3 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+ifcapable !memdebug||!crashtest||!memorymanage {
+ puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+db close
+
+for {set ii 0} {$ii < 10} {incr ii} {
+ for {set jj 50} {$jj < 100} {incr jj} {
+
+ # Set up the database so that it is an auto-vacuum database
+ # containing a single table (root page 3) with a single row.
+ # The row has an overflow page (page 4).
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ set c [string repeat 3 1500]
+ db eval {
+ pragma auto_vacuum = 1;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES('1111111111', '2222222222', $c);
+ }
+ db close
+
+ do_test crash5-$ii.$jj.1 {
+ crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \
+ [list set iFail $jj] {
+ sqlite3_crashparams 0 [file join [pwd] test.db-journal]
+
+ # Begin a transaction and evaluate a "CREATE INDEX" statement
+ # with the iFail'th malloc() set to fail. This operation will
+ # have to move the current contents of page 4 (the overflow
+ # page) to make room for the new root page. The bug is that
+ # if malloc() fails at a particular point in sqlite3PagerMovepage(),
+ # sqlite mistakenly thinks that the page being moved (page 4) has
+ # been safely synced into the journal. If the page is written
+ # to later in the transaction, it may be written out to the database
+ # before the relevant part of the journal has been synced.
+ #
+ db eval BEGIN
+ sqlite3_memdebug_fail $iFail -repeat 0
+ catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg
+ # puts "$n $msg ac=[sqlite3_get_autocommit db]"
+
+ # If the transaction is still active (it may not be if the malloc()
+ # failure occured in the OS layer), write to the database. Make sure
+ # page 4 is among those written.
+ #
+ if {![sqlite3_get_autocommit db]} {
+ db eval {
+ DELETE FROM t1; -- This will put page 4 on the free list.
+ INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
+ INSERT INTO t1 SELECT * FROM t1; -- 2
+ INSERT INTO t1 SELECT * FROM t1; -- 4
+ INSERT INTO t1 SELECT * FROM t1; -- 8
+ INSERT INTO t1 SELECT * FROM t1; -- 16
+ INSERT INTO t1 SELECT * FROM t1; -- 32
+ INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ }
+ }
+
+ # If the right malloc() failed during the 'CREATE INDEX' above and
+ # the transaction was not rolled back, then the sqlite cache now
+ # has a dirty page 4 that it incorrectly believes is already safely
+ # in the synced part of the journal file. When
+ # sqlite3_release_memory() is called sqlite tries to free memory
+ # by writing page 4 out to the db file. If it crashes later on,
+ # before syncing the journal... Corruption!
+ #
+ sqlite3_crashparams 1 [file join [pwd] test.db-journal]
+ sqlite3_release_memory 8092
+ }]] {}
+ expr 1
+ } {1}
+
+ sqlite3 db test.db
+ do_test crash5-$ii.$jj.2 {
+ db eval {pragma integrity_check}
+ } {ok}
+ do_test crash5-$ii.$jj.3 {
+ db eval {SELECT * FROM t1}
+ } [list 1111111111 2222222222 $::c]
+ db close
+ }
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/crash6.test b/third_party/sqlite/test/crash6.test
new file mode 100755
index 0000000..d5ee439
--- /dev/null
+++ b/third_party/sqlite/test/crash6.test
@@ -0,0 +1,118 @@
+# 2001 September 15
+#
+# 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 tests that rollback journals for databases that use a
+# page-size other than the default page-size can be rolled back Ok.
+#
+# $Id: crash6.test,v 1.2 2008/04/14 15:27:19 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+for {set ii 0} {$ii < 10} {incr ii} {
+ catch {db close}
+ file delete -force test.db test.db-journal
+ crashsql -delay 2 -file test.db {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=4096;
+ BEGIN;
+ CREATE TABLE abc AS SELECT 1 AS a, 2 AS b, 3 AS c;
+ COMMIT;
+ BEGIN;
+ CREATE TABLE def AS SELECT 1 AS d, 2 AS e, 3 AS f;
+ COMMIT;
+ }
+ sqlite3 db test.db
+ integrity_check crash6-1.$ii
+}
+
+for {set ii 0} {$ii < 10} {incr ii} {
+ catch {db close}
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=2048;
+ BEGIN;
+ CREATE TABLE abc AS SELECT 1 AS a, 2 AS b, 3 AS c;
+ COMMIT;
+ }
+ db close
+ crashsql -delay 1 -file test.db {
+ INSERT INTO abc VALUES(5, 6, 7);
+ }
+ sqlite3 db test.db
+ integrity_check crash6-2.$ii
+}
+
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
+}
+
+# Test case for crashing during database sync with page-size values
+# from 1024 to 8192.
+#
+for {set ii 0} {$ii < 30} {incr ii} {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+
+ set pagesize [expr 1024 << ($ii % 4)]
+ if {$pagesize>$::SQLITE_MAX_PAGE_SIZE} {
+ set pagesize $::SQLITE_MAX_PAGE_SIZE
+ }
+ do_test crash6-3.$ii.0 {
+ execsql "pragma page_size = $pagesize"
+ execsql "pragma page_size"
+ } $pagesize
+
+ do_test crash6-3.$ii.1 {
+
+ execsql BEGIN
+ execsql {CREATE TABLE abc(a, b, c)}
+ for {set n 0} {$n < 1000} {incr n} {
+ execsql "INSERT INTO abc VALUES($n, [expr 2*$n], [expr 3*$n])"
+ }
+ execsql {
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ execsql COMMIT
+ expr ([file size test.db] / 1024) > 450
+ } {1}
+
+ set sig [signature]
+ db close
+
+ do_test crash6-3.$ii.2 {
+ crashsql -file test.db "
+ BEGIN;
+ SELECT random() FROM abc LIMIT $ii;
+ INSERT INTO abc SELECT randstr(10,10), 0, 0 FROM abc WHERE random()%2==0;
+ DELETE FROM abc WHERE random()%2!=0;
+ COMMIT;
+ "
+ } {1 {child process exited abnormally}}
+
+ do_test crash6-3.$ii.3 {
+ sqlite3 db test.db
+ signature
+ } $sig
+}
+
+finish_test
diff --git a/third_party/sqlite/test/crash7.test b/third_party/sqlite/test/crash7.test
new file mode 100755
index 0000000..fc0596a
--- /dev/null
+++ b/third_party/sqlite/test/crash7.test
@@ -0,0 +1,82 @@
+# 2008 March 20
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: crash7.test,v 1.1 2008/04/03 14:36:26 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !crashtest {
+ finish_test
+ return
+}
+
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
+}
+
+foreach f [list test.db test.db-journal] {
+ for {set ii 1} {$ii < 64} {incr ii} {
+ db close
+ file delete test.db
+ sqlite3 db test.db
+
+ set from_size [expr 1024 << ($ii&3)]
+ set to_size [expr 1024 << (($ii>>2)&3)]
+
+ execsql "
+ PRAGMA page_size = $from_size;
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ INSERT INTO abc VALUES(randomblob(100), randomblob(200), randomblob(1000));
+ INSERT INTO abc
+ SELECT randomblob(1000), randomblob(200), randomblob(100)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc WHERE [expr $ii&16];
+ INSERT INTO abc
+ SELECT randomblob(25), randomblob(45), randomblob(9456)
+ FROM abc WHERE [expr $ii&32];
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc WHERE [expr $ii&8];
+ INSERT INTO abc
+ SELECT randomblob(25), randomblob(45), randomblob(9456)
+ FROM abc WHERE [expr $ii&4];
+ COMMIT;
+ "
+
+ set sig [signature]
+ db close
+
+ do_test crash7-1.$ii.crash {
+ crashsql -file $f "
+ PRAGMA page_size = $to_size;
+ VACUUM;
+ "
+ } {1 {child process exited abnormally}}
+
+ sqlite3 db test.db
+ integrity_check crash7-1.$ii.integrity
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/crashtest1.c b/third_party/sqlite/test/crashtest1.c
new file mode 100755
index 0000000..1f7035d
--- /dev/null
+++ b/third_party/sqlite/test/crashtest1.c
@@ -0,0 +1,96 @@
+/*
+** This program tests the ability of SQLite database to recover from a crash.
+** This program runs under Unix only, but the results are applicable to all
+** systems.
+**
+** The main process first constructs a test database, then starts creating
+** subprocesses that write to that database. Each subprocess is killed off,
+** without a chance to clean up its database connection, after a random
+** delay. This killing of the subprocesses simulates a crash or power
+** failure. The next subprocess to open the database should rollback
+** whatever operation was in process at the time of the simulated crash.
+**
+** If any problems are encountered, an error is reported and the test stops.
+** If no problems are seen after a large number of tests, we assume that
+** the rollback mechanism is working.
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include "sqlite.h"
+
+static void do_some_sql(int parent){
+ char *zErr;
+ int rc = SQLITE_OK;
+ sqlite *db;
+ int cnt = 0;
+ static char zBig[] =
+ "-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ if( access("./test.db-journal",0)==0 ){
+ /*printf("pid %d: journal exists. rollback will be required\n",getpid());*/ unlink("test.db-saved");
+ system("cp test.db test.db-saved");
+ unlink("test.db-journal-saved");
+ system("cp test.db-journal test.db-journal-saved");
+ }
+ db = sqlite_open("./test.db", 0, &zErr);
+ if( db==0 ){
+ printf("ERROR: %s\n", zErr);
+ if( strcmp(zErr,"database disk image is malformed")==0 ){
+ kill(parent, SIGKILL);
+ }
+ exit(1);
+ }
+ srand(getpid());
+ while( rc==SQLITE_OK ){
+ cnt++;
+ rc = sqlite_exec_printf(db,
+ "INSERT INTO t1 VALUES(%d,'%d%s')", 0, 0, &zErr,
+ rand(), rand(), zBig);
+ }
+ if( rc!=SQLITE_OK ){
+ printf("ERROR #%d: %s\n", rc, zErr);
+ if( rc==SQLITE_CORRUPT ){
+ kill(parent, SIGKILL);
+ }
+ }
+ printf("pid %d: cnt=%d\n", getpid(), cnt);
+}
+
+
+int main(int argc, char **argv){
+ int i;
+ sqlite *db;
+ char *zErr;
+ int status;
+ int parent = getpid();
+
+ unlink("test.db");
+ unlink("test.db-journal");
+ db = sqlite_open("test.db", 0, &zErr);
+ if( db==0 ){
+ printf("Cannot initialize: %s\n", zErr);
+ return 1;
+ }
+ sqlite_exec(db, "CREATE TABLE t1(a,b)", 0, 0, 0);
+ sqlite_close(db);
+ for(i=0; i<10000; i++){
+ int pid = fork();
+ if( pid==0 ){
+ sched_yield();
+ do_some_sql(parent);
+ return 0;
+ }
+ printf("test %d, pid=%d\n", i, pid);
+ usleep(rand()%10000 + 1000);
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ }
+ return 0;
+}
diff --git a/third_party/sqlite/test/createtab.test b/third_party/sqlite/test/createtab.test
new file mode 100755
index 0000000..3f036b7
--- /dev/null
+++ b/third_party/sqlite/test/createtab.test
@@ -0,0 +1,146 @@
+# 2007 May 02
+#
+# 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 file is testing that it is OK to create new tables
+# and indices while creating existing tables and indices.
+#
+# $Id: createtab.test,v 1.3 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable autovacuum {
+ set upperBound 2
+} else {
+ set upperBound 0
+}
+
+# Run these tests for all possible values of autovacuum.
+#
+for {set av 0} {$av<=$upperBound} {incr av} {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+
+ # Create a table that spans multiple pages. It is important
+ # that part of the database be in pages beyond the root page.
+ #
+ do_test createtab-$av.1 {
+ execsql "PRAGMA auto_vacuum=$av"
+ execsql {
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t1 VALUES(1, hex(randomblob(200)));
+ INSERT INTO t1 VALUES(2, hex(randomblob(200)));
+ INSERT INTO t1 VALUES(3, hex(randomblob(200)));
+ INSERT INTO t1 VALUES(4, hex(randomblob(200)));
+ SELECT count(*) FROM t1;
+ }
+ } {4}
+
+ set isUtf16 0
+ ifcapable utf16 {
+ set isUtf16 [expr {[execsql {PRAGMA encoding}] != "UTF-8"}]
+ }
+
+ do_test createtab-$av.2 {
+ file size test.db
+ } [expr {1024*(4+($av!=0)+(${isUtf16}*2))}]
+
+ # Start reading the table
+ #
+ do_test createtab-$av.3 {
+ set STMT [sqlite3_prepare db {SELECT x FROM t1} -1 TAIL]
+ sqlite3_step $STMT
+ } {SQLITE_ROW}
+ do_test createtab-$av.4 {
+ sqlite3_column_int $STMT 0
+ } {1}
+
+ # While still reading the table, create a new table.
+ #
+ do_test createtab-$av.5 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+ } {1 2}
+
+ # Continue reading the original table.
+ #
+ do_test createtab-$av.6 {
+ sqlite3_column_int $STMT 0
+ } {1}
+ do_test createtab-$av.7 {
+ sqlite3_step $STMT
+ } {SQLITE_ROW}
+ do_test createtab-$av.8 {
+ sqlite3_column_int $STMT 0
+ } {2}
+
+ # Do another cycle of creating a new database table while contining
+ # to read the original table.
+ #
+ do_test createtab-$av.11 {
+ execsql {
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES(4,5);
+ SELECT * FROM t3;
+ }
+ } {4 5}
+ do_test createtab-$av.12 {
+ sqlite3_column_int $STMT 0
+ } {2}
+ do_test createtab-$av.13 {
+ sqlite3_step $STMT
+ } {SQLITE_ROW}
+ do_test createtab-$av.14 {
+ sqlite3_column_int $STMT 0
+ } {3}
+
+ # One more cycle.
+ #
+ do_test createtab-$av.21 {
+ execsql {
+ CREATE TABLE t4(a,b);
+ INSERT INTO t4 VALUES('abc','xyz');
+ SELECT * FROM t4;
+ }
+ } {abc xyz}
+ do_test createtab-$av.22 {
+ sqlite3_column_int $STMT 0
+ } {3}
+ do_test createtab-$av.23 {
+ sqlite3_step $STMT
+ } {SQLITE_ROW}
+ do_test createtab-$av.24 {
+ sqlite3_column_int $STMT 0
+ } {4}
+
+ # Finish reading. Do an integrity check on the database.
+ #
+ do_test createtab-$av.30 {
+ sqlite3_step $STMT
+ } {SQLITE_DONE}
+ do_test createtab-$av.31 {
+ sqlite3_finalize $STMT
+ } {SQLITE_OK}
+ do_test createtab-$av.32 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1
+ }
+ } {t1 t2 t3 t4}
+ integrity_check createtab-$av.40
+
+}
+
+finish_test
diff --git a/third_party/sqlite/test/cse.test b/third_party/sqlite/test/cse.test
new file mode 100755
index 0000000..57cdef5
--- /dev/null
+++ b/third_party/sqlite/test/cse.test
@@ -0,0 +1,160 @@
+# 2008 April 1
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test cases designed to exercise and verify the logic for
+# factoring constant expressions out of loops and for
+# common subexpression eliminations.
+#
+# $Id: cse.test,v 1.6 2008/08/04 03:51:24 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test cse-1.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e, f);
+ INSERT INTO t1 VALUES(1,11,12,13,14,15);
+ INSERT INTO t1 VALUES(2,21,22,23,24,25);
+ }
+ execsql {
+ SELECT b, -b, ~b, NOT b, NOT NOT b, b-b, b+b, b*b, b/b, b FROM t1
+ }
+} {11 -11 -12 0 1 0 22 121 1 11 21 -21 -22 0 1 0 42 441 1 21}
+do_test cse-1.2 {
+ execsql {
+ SELECT b, b%b, b==b, b!=b, b<b, b<=b, b IS NULL, b NOT NULL, b FROM t1
+ }
+} {11 0 1 0 0 1 0 1 11 21 0 1 0 0 1 0 1 21}
+do_test cse-1.3 {
+ execsql {
+ SELECT b, abs(b), coalesce(b,-b,NOT b,c,NOT c), c, -c FROM t1;
+ }
+} {11 11 11 12 -12 21 21 21 22 -22}
+do_test cse-1.4 {
+ execsql {
+ SELECT CASE WHEN a==1 THEN b ELSE c END, b, c FROM t1
+ }
+} {11 11 12 22 21 22}
+do_test cse-1.5 {
+ execsql {
+ SELECT CASE a WHEN 1 THEN b WHEN 2 THEN c ELSE d END, b, c, d FROM t1
+ }
+} {11 11 12 13 22 21 22 23}
+do_test cse-1.6.1 {
+ execsql {
+ SELECT CASE b WHEN 11 THEN -b WHEN 21 THEN -c ELSE -d END, b, c, d FROM t1
+ }
+} {-11 11 12 13 -22 21 22 23}
+do_test cse-1.6.2 {
+ execsql {
+ SELECT CASE b+1 WHEN c THEN d WHEN e THEN f ELSE 999 END, b, c, d FROM t1
+ }
+} {13 11 12 13 23 21 22 23}
+do_test cse-1.6.3 {
+ execsql {
+ SELECT CASE WHEN b THEN d WHEN e THEN f ELSE 999 END, b, c, d FROM t1
+ }
+} {13 11 12 13 23 21 22 23}
+do_test cse-1.6.4 {
+ execsql {
+ SELECT b, c, d, CASE WHEN b THEN d WHEN e THEN f ELSE 999 END FROM t1
+ }
+} {11 12 13 13 21 22 23 23}
+do_test cse-1.6.5 {
+ execsql {
+ SELECT b, c, d, CASE WHEN 0 THEN d WHEN e THEN f ELSE 999 END FROM t1
+ }
+} {11 12 13 15 21 22 23 25}
+do_test cse-1.7 {
+ execsql {
+ SELECT a, -a, ~a, NOT a, NOT NOT a, a-a, a+a, a*a, a/a, a FROM t1
+ }
+} {1 -1 -2 0 1 0 2 1 1 1 2 -2 -3 0 1 0 4 4 1 2}
+do_test cse-1.8 {
+ execsql {
+ SELECT a, a%a, a==a, a!=a, a<a, a<=a, a IS NULL, a NOT NULL, a FROM t1
+ }
+} {1 0 1 0 0 1 0 1 1 2 0 1 0 0 1 0 1 2}
+do_test cse-1.9 {
+ execsql {
+ SELECT NOT b, ~b, NOT NOT b, b FROM t1
+ }
+} {0 -12 1 11 0 -22 1 21}
+do_test cse-1.10 {
+ execsql {
+ SELECT CAST(b AS integer), typeof(b), CAST(b AS text), typeof(b) FROM t1
+ }
+} {11 integer 11 integer 21 integer 21 integer}
+ifcapable compound {
+ do_test cse-1.11 {
+ execsql {
+ SELECT *,* FROM t1 WHERE a=2
+ UNION ALL
+ SELECT *,* FROM t1 WHERE a=1
+ }
+ } {2 21 22 23 24 25 2 21 22 23 24 25 1 11 12 13 14 15 1 11 12 13 14 15}
+ do_test cse-1.12 {
+ execsql {
+ SELECT coalesce(b,c,d,e), a, b, c, d, e FROM t1 WHERE a=2
+ UNION ALL
+ SELECT coalesce(e,d,c,b), e, d, c, b, a FROM t1 WHERE a=1
+ }
+ } {21 2 21 22 23 24 14 14 13 12 11 1}
+}
+do_test cse-1.13 {
+ execsql {
+ SELECT upper(b), typeof(b), b FROM t1
+ }
+} {11 integer 11 21 integer 21}
+do_test cse-1.14 {
+ execsql {
+ SELECT b, typeof(b), upper(b), typeof(b), b FROM t1
+ }
+} {11 integer 11 integer 11 21 integer 21 integer 21}
+
+# Overflow the column cache. Create queries involving more and more
+# columns until the cache overflows. Verify correct operation throughout.
+#
+do_test cse-2.1 {
+ execsql {
+ CREATE TABLE t2(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,
+ a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,
+ a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,
+ a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,
+ a40,a41,a42,a43,a44,a45,a46,a47,a48,a49);
+ INSERT INTO t2 VALUES(0,1,2,3,4,5,6,7,8,9,
+ 10,11,12,13,14,15,16,17,18,19,
+ 20,21,22,23,24,25,26,27,28,29,
+ 30,31,32,33,34,35,36,37,38,39,
+ 40,41,42,43,44,45,46,47,48,49);
+ SELECT * FROM t2;
+ }
+} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49}
+
+for {set i 1} {$i<100} {incr i} {
+ set n [expr {int(rand()*44)+5}]
+ set colset {}
+ set answer {}
+ for {set j 0} {$j<$n} {incr j} {
+ set r [expr {$j+int(rand()*5)}]
+ if {$r>49} {set r [expr {99-$r}]}
+ lappend colset a$j a$r
+ lappend answer $j $r
+ }
+ set sql "SELECT [join $colset ,] FROM t2"
+ do_test cse-2.2.$i {
+ # explain $::sql
+ execsql $::sql
+ } $answer
+}
+
+finish_test
diff --git a/third_party/sqlite/test/date.test b/third_party/sqlite/test/date.test
new file mode 100755
index 0000000..466fb65
--- /dev/null
+++ b/third_party/sqlite/test/date.test
@@ -0,0 +1,489 @@
+# 2003 October 31
+#
+# 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 file is testing date and time functions.
+#
+# $Id: date.test,v 1.31 2008/07/08 02:12:37 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this whole file if date and time functions are omitted
+# at compile-time
+#
+ifcapable {!datetime} {
+ finish_test
+ return
+}
+
+proc datetest {tnum expr result} {
+ do_test date-$tnum [subst {
+ execsql "SELECT coalesce($expr,'NULL')"
+ }] [list $result]
+}
+set tcl_precision 15
+datetest 1.1 julianday('2000-01-01') 2451544.5
+datetest 1.2 julianday('1970-01-01') 2440587.5
+datetest 1.3 julianday('1910-04-20') 2418781.5
+datetest 1.4 julianday('1986-02-09') 2446470.5
+datetest 1.5 julianday('12:00:00') 2451545.0
+datetest 1.6 {julianday('2000-01-01 12:00:00')} 2451545.0
+datetest 1.7 {julianday('2000-01-01 12:00')} 2451545.0
+datetest 1.8 julianday('bogus') NULL
+datetest 1.9 julianday('1999-12-31') 2451543.5
+datetest 1.10 julianday('1999-12-32') NULL
+datetest 1.11 julianday('1999-13-01') NULL
+datetest 1.12 julianday('2003-02-31') 2452701.5
+datetest 1.13 julianday('2003-03-03') 2452701.5
+datetest 1.14 julianday('+2000-01-01') NULL
+datetest 1.15 julianday('200-01-01') NULL
+datetest 1.16 julianday('2000-1-01') NULL
+datetest 1.17 julianday('2000-01-1') NULL
+datetest 1.18.1 {julianday('2000-01-01 12:00:00')} 2451545.0
+datetest 1.18.2 {julianday('2000-01-01T12:00:00')} 2451545.0
+datetest 1.18.3 {julianday('2000-01-01 T12:00:00')} 2451545.0
+datetest 1.18.4 {julianday('2000-01-01T 12:00:00')} 2451545.0
+datetest 1.18.4 {julianday('2000-01-01 T 12:00:00')} 2451545.0
+datetest 1.19 {julianday('2000-01-01 12:00:00.1')} 2451545.00000116
+datetest 1.20 {julianday('2000-01-01 12:00:00.01')} 2451545.00000012
+datetest 1.21 {julianday('2000-01-01 12:00:00.001')} 2451545.00000001
+datetest 1.22 {julianday('2000-01-01 12:00:00.')} NULL
+datetest 1.23 julianday(12345.6) 12345.6
+datetest 1.23b julianday('12345.6') 12345.6
+datetest 1.24 {julianday('2001-01-01 12:00:00 bogus')} NULL
+datetest 1.25 {julianday('2001-01-01 bogus')} NULL
+datetest 1.26 {julianday('2001-01-01 12:60:00')} NULL
+datetest 1.27 {julianday('2001-01-01 12:59:60')} NULL
+datetest 1.28 {julianday('2001-00-01')} NULL
+datetest 1.29 {julianday('2001-01-00')} NULL
+
+datetest 2.1 datetime(0,'unixepoch') {1970-01-01 00:00:00}
+datetest 2.1b datetime(0,'unixepoc') NULL
+datetest 2.1c datetime(0,'unixepochx') NULL
+datetest 2.1d datetime('2003-10-22','unixepoch') NULL
+datetest 2.2 datetime(946684800,'unixepoch') {2000-01-01 00:00:00}
+datetest 2.2b datetime('946684800','unixepoch') {2000-01-01 00:00:00}
+datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26
+datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27
+datetest 2.4a {date('2003-10-22','weekday 1')} 2003-10-27
+datetest 2.4b {date('2003-10-22','weekday 1x')} 2003-10-27
+datetest 2.4c {date('2003-10-22','weekday -1')} NULL
+datetest 2.4d {date('2003-10-22','weakday 1x')} NULL
+datetest 2.4e {date('2003-10-22','weekday ')} NULL
+datetest 2.5 {date('2003-10-22','weekday 2')} 2003-10-28
+datetest 2.6 {date('2003-10-22','weekday 3')} 2003-10-22
+datetest 2.7 {date('2003-10-22','weekday 4')} 2003-10-23
+datetest 2.8 {date('2003-10-22','weekday 5')} 2003-10-24
+datetest 2.9 {date('2003-10-22','weekday 6')} 2003-10-25
+datetest 2.10 {date('2003-10-22','weekday 7')} NULL
+datetest 2.11 {date('2003-10-22','weekday 5.5')} NULL
+datetest 2.12 {datetime('2003-10-22 12:34','weekday 0')} {2003-10-26 12:34:00}
+datetest 2.13 {datetime('2003-10-22 12:34','start of month')} \
+ {2003-10-01 00:00:00}
+datetest 2.14 {datetime('2003-10-22 12:34','start of year')} \
+ {2003-01-01 00:00:00}
+datetest 2.15 {datetime('2003-10-22 12:34','start of day')} \
+ {2003-10-22 00:00:00}
+datetest 2.15a {datetime('2003-10-22 12:34','start of')} NULL
+datetest 2.15b {datetime('2003-10-22 12:34','start of bogus')} NULL
+datetest 2.16 time('12:34:56.43') 12:34:56
+datetest 2.17 {datetime('2003-10-22 12:34','1 day')} {2003-10-23 12:34:00}
+datetest 2.18 {datetime('2003-10-22 12:34','+1 day')} {2003-10-23 12:34:00}
+datetest 2.19 {datetime('2003-10-22 12:34','+1.25 day')} {2003-10-23 18:34:00}
+datetest 2.20 {datetime('2003-10-22 12:34','-1.0 day')} {2003-10-21 12:34:00}
+datetest 2.21 {datetime('2003-10-22 12:34','1 month')} {2003-11-22 12:34:00}
+datetest 2.22 {datetime('2003-10-22 12:34','11 month')} {2004-09-22 12:34:00}
+datetest 2.23 {datetime('2003-10-22 12:34','-13 month')} {2002-09-22 12:34:00}
+datetest 2.24 {datetime('2003-10-22 12:34','1.5 months')} {2003-12-07 12:34:00}
+datetest 2.25 {datetime('2003-10-22 12:34','-5 years')} {1998-10-22 12:34:00}
+datetest 2.26 {datetime('2003-10-22 12:34','+10.5 minutes')} \
+ {2003-10-22 12:44:30}
+datetest 2.27 {datetime('2003-10-22 12:34','-1.25 hours')} \
+ {2003-10-22 11:19:00}
+datetest 2.28 {datetime('2003-10-22 12:34','11.25 seconds')} \
+ {2003-10-22 12:34:11}
+datetest 2.29 {datetime('2003-10-22 12:24','+5 bogus')} NULL
+datetest 2.30 {datetime('2003-10-22 12:24','+++')} NULL
+datetest 2.31 {datetime('2003-10-22 12:24','+12.3e4 femtoseconds')} NULL
+datetest 2.32 {datetime('2003-10-22 12:24','+12.3e4 uS')} NULL
+datetest 2.33 {datetime('2003-10-22 12:24','+1 abc')} NULL
+datetest 2.34 {datetime('2003-10-22 12:24','+1 abcd')} NULL
+datetest 2.35 {datetime('2003-10-22 12:24','+1 abcde')} NULL
+datetest 2.36 {datetime('2003-10-22 12:24','+1 abcdef')} NULL
+datetest 2.37 {datetime('2003-10-22 12:24','+1 abcdefg')} NULL
+datetest 2.38 {datetime('2003-10-22 12:24','+1 abcdefgh')} NULL
+datetest 2.39 {datetime('2003-10-22 12:24','+1 abcdefghi')} NULL
+set sqlite_current_time 1199243045
+datetest 2.40 {datetime()} {2008-01-02 03:04:05}
+set sqlite_current_time 0
+datetest 2.41 {datetime('2003-10-22 12:24','23 seconds')} {2003-10-22 12:24:23}
+datetest 2.42 {datetime('2003-10-22 12:24','345 second')} {2003-10-22 12:29:45}
+datetest 2.43 {datetime('2003-10-22 12:24','4 second')} {2003-10-22 12:24:04}
+datetest 2.44 {datetime('2003-10-22 12:24','56 second')} {2003-10-22 12:24:56}
+datetest 2.45 {datetime('2003-10-22 12:24','60 second')} {2003-10-22 12:25:00}
+datetest 2.46 {datetime('2003-10-22 12:24','70 second')} {2003-10-22 12:25:10}
+datetest 2.47 {datetime('2003-10-22 12:24','8.6 seconds')} {2003-10-22 12:24:08}
+datetest 2.48 {datetime('2003-10-22 12:24','9.4 second')} {2003-10-22 12:24:09}
+datetest 2.49 {datetime('2003-10-22 12:24','0000 second')} {2003-10-22 12:24:00}
+datetest 2.50 {datetime('2003-10-22 12:24','0001 second')} {2003-10-22 12:24:01}
+datetest 2.51 {datetime('2003-10-22 12:24','nonsense')} NULL
+
+datetest 3.1 {strftime('%d','2003-10-31 12:34:56.432')} 31
+datetest 3.2.1 {strftime('pre%fpost','2003-10-31 12:34:56.432')} pre56.432post
+datetest 3.2.2 {strftime('%f','2003-10-31 12:34:59.9999999')} 59.999
+datetest 3.3 {strftime('%H','2003-10-31 12:34:56.432')} 12
+datetest 3.4 {strftime('%j','2003-10-31 12:34:56.432')} 304
+datetest 3.5 {strftime('%J','2003-10-31 12:34:56.432')} 2452944.02426426
+datetest 3.6 {strftime('%m','2003-10-31 12:34:56.432')} 10
+datetest 3.7 {strftime('%M','2003-10-31 12:34:56.432')} 34
+datetest 3.8 {strftime('%s','2003-10-31 12:34:56.432')} 1067603696
+datetest 3.9 {strftime('%S','2003-10-31 12:34:56.432')} 56
+datetest 3.10 {strftime('%w','2003-10-31 12:34:56.432')} 5
+datetest 3.11.1 {strftime('%W','2003-10-31 12:34:56.432')} 43
+datetest 3.11.2 {strftime('%W','2004-01-01')} 00
+datetest 3.11.3 {strftime('%W','2004-01-02')} 00
+datetest 3.11.4 {strftime('%W','2004-01-03')} 00
+datetest 3.11.5 {strftime('abc%Wxyz','2004-01-04')} abc00xyz
+datetest 3.11.6 {strftime('%W','2004-01-05')} 01
+datetest 3.11.7 {strftime('%W','2004-01-06')} 01
+datetest 3.11.8 {strftime('%W','2004-01-07')} 01
+datetest 3.11.9 {strftime('%W','2004-01-08')} 01
+datetest 3.11.10 {strftime('%W','2004-01-09')} 01
+datetest 3.11.11 {strftime('%W','2004-07-18')} 28
+datetest 3.11.12 {strftime('%W','2004-12-31')} 52
+datetest 3.11.13 {strftime('%W','2007-12-31')} 53
+datetest 3.11.14 {strftime('%W','2007-01-01')} 01
+datetest 3.11.15 {strftime('%W %j',2454109.04140970)} {02 008}
+datetest 3.11.16 {strftime('%W %j',2454109.04140971)} {02 008}
+datetest 3.11.17 {strftime('%W %j',2454109.04140972)} {02 008}
+datetest 3.11.18 {strftime('%W %j',2454109.04140973)} {02 008}
+datetest 3.11.19 {strftime('%W %j',2454109.04140974)} {02 008}
+datetest 3.11.20 {strftime('%W %j',2454109.04140975)} {02 008}
+datetest 3.11.21 {strftime('%W %j',2454109.04140976)} {02 008}
+datetest 3.11.22 {strftime('%W %j',2454109.04140977)} {02 008}
+datetest 3.11.23 {strftime('%W %j',2454109.04140978)} {02 008}
+datetest 3.11.24 {strftime('%W %j',2454109.04140979)} {02 008}
+datetest 3.11.25 {strftime('%W %j',2454109.04140980)} {02 008}
+datetest 3.11.99 {strftime('%W %j','2454109.04140970')} {02 008}
+datetest 3.12 {strftime('%Y','2003-10-31 12:34:56.432')} 2003
+datetest 3.13 {strftime('%%','2003-10-31 12:34:56.432')} %
+datetest 3.14 {strftime('%_','2003-10-31 12:34:56.432')} NULL
+datetest 3.15 {strftime('%Y-%m-%d','2003-10-31')} 2003-10-31
+proc repeat {n txt} {
+ set x {}
+ while {$n>0} {
+ append x $txt
+ incr n -1
+ }
+ return $x
+}
+datetest 3.16 "strftime('[repeat 200 %Y]','2003-10-31')" [repeat 200 2003]
+datetest 3.17 "strftime('[repeat 200 abc%m123]','2003-10-31')" \
+ [repeat 200 abc10123]
+
+foreach c {a b c e g h i k l n o p q r t v x y z
+ A B C D E F G I K L N O P Q R T U V Z
+ 0 1 2 3 4 5 6 6 7 9 _} {
+ datetest 3.18.$c "strftime('%$c','2003-10-31')" NULL
+}
+
+# Ticket #2276. Make sure leading zeros are inserted where appropriate.
+#
+datetest 3.20 \
+ {strftime('%d/%f/%H/%W/%j/%m/%M/%S/%Y','0421-01-02 03:04:05.006')} \
+ 02/05.006/03/00/002/01/04/05/0421
+
+set sqlite_current_time 1157124367
+datetest 4.1 {date('now')} {2006-09-01}
+set sqlite_current_time 0
+
+datetest 5.1 {datetime('1994-04-16 14:00:00 +05:00')} {1994-04-16 09:00:00}
+datetest 5.2 {datetime('1994-04-16 14:00:00 -05:15')} {1994-04-16 19:15:00}
+datetest 5.3 {datetime('1994-04-16 05:00:00 +08:30')} {1994-04-15 20:30:00}
+datetest 5.4 {datetime('1994-04-16 14:00:00 -11:55')} {1994-04-17 01:55:00}
+datetest 5.5 {datetime('1994-04-16 14:00:00 -11:60')} NULL
+datetest 5.6 {datetime('1994-04-16 14:00:00 -11:55 ')} {1994-04-17 01:55:00}
+datetest 5.7 {datetime('1994-04-16 14:00:00 -11:55 x')} NULL
+datetest 5.8 {datetime('1994-04-16T14:00:00Z')} {1994-04-16 14:00:00}
+datetest 5.9 {datetime('1994-04-16 14:00:00z')} {1994-04-16 14:00:00}
+datetest 5.10 {datetime('1994-04-16 14:00:00 Z')} {1994-04-16 14:00:00}
+datetest 5.11 {datetime('1994-04-16 14:00:00z ')} {1994-04-16 14:00:00}
+datetest 5.12 {datetime('1994-04-16 14:00:00 z ')} {1994-04-16 14:00:00}
+datetest 5.13 {datetime('1994-04-16 14:00:00Zulu')} NULL
+datetest 5.14 {datetime('1994-04-16 14:00:00Z +05:00')} NULL
+datetest 5.15 {datetime('1994-04-16 14:00:00 +05:00 Z')} NULL
+
+# localtime->utc and utc->localtime conversions. These tests only work
+# if the localtime is in the US Eastern Time (the time in Charlotte, NC
+# and in New York.)
+#
+# On non-Vista Windows platform, '2006-03-31' is treated incorrectly as being
+# in DST giving a 4 hour offset instead of 5. In 2007, DST was extended to
+# start three weeks earlier (second Sunday in March) and end one week
+# later (first Sunday in November). Older Windows systems apply this
+# new rule incorrectly to dates prior to 2007.
+#
+# It might be argued that this is masking a problem on non-Vista Windows
+# platform. A ticket has already been opened for this issue
+# (http://www.sqlite.org/cvstrac/tktview?tn=2322). This is just to prevent
+# more confusion/reports of the issue.
+#
+
+# $tzoffset_old should be 5 if DST is working correctly.
+set tzoffset_old [db one {
+ SELECT CAST(24*(julianday('2006-03-31') -
+ julianday('2006-03-31','localtime'))+0.5
+ AS INT)
+}]
+
+# $tzoffset_new should be 4 if DST is working correctly.
+set tzoffset_new [db one {
+ SELECT CAST(24*(julianday('2007-03-31') -
+ julianday('2007-03-31','localtime'))+0.5
+ AS INT)
+}]
+
+# Warn about possibly broken Windows DST implementations.
+if {$::tcl_platform(platform)=="windows" && $tzoffset_new==4 && $tzoffset_old==4} {
+ puts "******************************************************************"
+ puts "N.B.: The DST support provided by your current O/S seems to be"
+ puts "suspect in that it is reporting incorrect DST values for dates"
+ puts "prior to 2007. This is the known case for most (all?) non-Vista"
+ puts "Windows versions. Please see ticket #2322 for more information."
+ puts "******************************************************************"
+}
+
+if {$tzoffset_new==4} {
+ datetest 6.1 {datetime('2000-10-29 05:59:00','localtime')}\
+ {2000-10-29 01:59:00}
+ datetest 6.1.1 {datetime('2006-10-29 05:59:00','localtime')}\
+ {2006-10-29 01:59:00}
+ datetest 6.1.2 {datetime('2007-11-04 05:59:00','localtime')}\
+ {2007-11-04 01:59:00}
+
+ # If the new and old DST rules seem to be working correctly...
+ if {$tzoffset_new==4 && $tzoffset_old==5} {
+ datetest 6.2 {datetime('2000-10-29 06:00:00','localtime')}\
+ {2000-10-29 01:00:00}
+ datetest 6.2.1 {datetime('2006-10-29 06:00:00','localtime')}\
+ {2006-10-29 01:00:00}
+ }
+ datetest 6.2.2 {datetime('2007-11-04 06:00:00','localtime')}\
+ {2007-11-04 01:00:00}
+
+ # If the new and old DST rules seem to be working correctly...
+ if {$tzoffset_new==4 && $tzoffset_old==5} {
+ datetest 6.3 {datetime('2000-04-02 06:59:00','localtime')}\
+ {2000-04-02 01:59:00}
+ datetest 6.3.1 {datetime('2006-04-02 06:59:00','localtime')}\
+ {2006-04-02 01:59:00}
+ }
+ datetest 6.3.2 {datetime('2007-03-11 07:00:00','localtime')}\
+ {2007-03-11 03:00:00}
+
+ datetest 6.4 {datetime('2000-04-02 07:00:00','localtime')}\
+ {2000-04-02 03:00:00}
+ datetest 6.4.1 {datetime('2006-04-02 07:00:00','localtime')}\
+ {2006-04-02 03:00:00}
+ datetest 6.4.2 {datetime('2007-03-11 07:00:00','localtime')}\
+ {2007-03-11 03:00:00}
+
+ datetest 6.5 {datetime('2000-10-29 01:59:00','utc')} {2000-10-29 05:59:00}
+ datetest 6.5.1 {datetime('2006-10-29 01:59:00','utc')} {2006-10-29 05:59:00}
+ datetest 6.5.2 {datetime('2007-11-04 01:59:00','utc')} {2007-11-04 05:59:00}
+
+ # If the new and old DST rules seem to be working correctly...
+ if {$tzoffset_new==4 && $tzoffset_old==5} {
+ datetest 6.6 {datetime('2000-10-29 02:00:00','utc')} {2000-10-29 07:00:00}
+ datetest 6.6.1 {datetime('2006-10-29 02:00:00','utc')} {2006-10-29 07:00:00}
+ }
+ datetest 6.6.2 {datetime('2007-11-04 02:00:00','utc')} {2007-11-04 07:00:00}
+
+ # If the new and old DST rules seem to be working correctly...
+ if {$tzoffset_new==4 && $tzoffset_old==5} {
+ datetest 6.7 {datetime('2000-04-02 01:59:00','utc')} {2000-04-02 06:59:00}
+ datetest 6.7.1 {datetime('2006-04-02 01:59:00','utc')} {2006-04-02 06:59:00}
+ }
+ datetest 6.7.2 {datetime('2007-03-11 01:59:00','utc')} {2007-03-11 06:59:00}
+
+ datetest 6.8 {datetime('2000-04-02 02:00:00','utc')} {2000-04-02 06:00:00}
+ datetest 6.8.1 {datetime('2006-04-02 02:00:00','utc')} {2006-04-02 06:00:00}
+ datetest 6.8.2 {datetime('2007-03-11 02:00:00','utc')} {2007-03-11 06:00:00}
+
+ datetest 6.10 {datetime('2000-01-01 12:00:00','localtime')} \
+ {2000-01-01 07:00:00}
+ datetest 6.11 {datetime('1969-01-01 12:00:00','localtime')} \
+ {1969-01-01 07:00:00}
+ datetest 6.12 {datetime('2039-01-01 12:00:00','localtime')} \
+ {2039-01-01 07:00:00}
+ datetest 6.13 {datetime('2000-07-01 12:00:00','localtime')} \
+ {2000-07-01 08:00:00}
+ datetest 6.14 {datetime('1969-07-01 12:00:00','localtime')} \
+ {1969-07-01 07:00:00}
+ datetest 6.15 {datetime('2039-07-01 12:00:00','localtime')} \
+ {2039-07-01 07:00:00}
+ set sqlite_current_time \
+ [db eval {SELECT strftime('%s','2000-07-01 12:34:56')}]
+ datetest 6.16 {datetime('now','localtime')} {2000-07-01 08:34:56}
+ datetest 6.17 {datetime('now','localtimex')} NULL
+ datetest 6.18 {datetime('now','localtim')} NULL
+ set sqlite_current_time 0
+}
+
+# These two are a bit of a scam. They are added to ensure that 100% of
+# the date.c file is covered by testing, even when the time-zone
+# is not -0400 (the condition for running of the block of tests above).
+#
+datetest 6.19 {datetime('2039-07-01 12:00:00','localtime',null)} NULL
+datetest 6.20 {datetime('2039-07-01 12:00:00','utc',null)} NULL
+
+# Date-time functions that contain NULL arguments return a NULL
+# result.
+#
+datetest 7.1 {datetime(null)} NULL
+datetest 7.2 {datetime('now',null)} NULL
+datetest 7.3 {datetime('now','localtime',null)} NULL
+datetest 7.4 {time(null)} NULL
+datetest 7.5 {time('now',null)} NULL
+datetest 7.6 {time('now','localtime',null)} NULL
+datetest 7.7 {date(null)} NULL
+datetest 7.8 {date('now',null)} NULL
+datetest 7.9 {date('now','localtime',null)} NULL
+datetest 7.10 {julianday(null)} NULL
+datetest 7.11 {julianday('now',null)} NULL
+datetest 7.12 {julianday('now','localtime',null)} NULL
+datetest 7.13 {strftime(null,'now')} NULL
+datetest 7.14 {strftime('%s',null)} NULL
+datetest 7.15 {strftime('%s','now',null)} NULL
+datetest 7.16 {strftime('%s','now','localtime',null)} NULL
+
+# Test modifiers when the date begins as a julian day number - to
+# make sure the HH:MM:SS is preserved. Ticket #551.
+#
+set sqlite_current_time [db eval {SELECT strftime('%s','2003-10-22 12:34:00')}]
+datetest 8.1 {datetime('now','weekday 0')} {2003-10-26 12:34:00}
+datetest 8.2 {datetime('now','weekday 1')} {2003-10-27 12:34:00}
+datetest 8.3 {datetime('now','weekday 2')} {2003-10-28 12:34:00}
+datetest 8.4 {datetime('now','weekday 3')} {2003-10-22 12:34:00}
+datetest 8.5 {datetime('now','start of month')} {2003-10-01 00:00:00}
+datetest 8.6 {datetime('now','start of year')} {2003-01-01 00:00:00}
+datetest 8.7 {datetime('now','start of day')} {2003-10-22 00:00:00}
+datetest 8.8 {datetime('now','1 day')} {2003-10-23 12:34:00}
+datetest 8.9 {datetime('now','+1 day')} {2003-10-23 12:34:00}
+datetest 8.10 {datetime('now','+1.25 day')} {2003-10-23 18:34:00}
+datetest 8.11 {datetime('now','-1.0 day')} {2003-10-21 12:34:00}
+datetest 8.12 {datetime('now','1 month')} {2003-11-22 12:34:00}
+datetest 8.13 {datetime('now','11 month')} {2004-09-22 12:34:00}
+datetest 8.14 {datetime('now','-13 month')} {2002-09-22 12:34:00}
+datetest 8.15 {datetime('now','1.5 months')} {2003-12-07 12:34:00}
+datetest 8.16 {datetime('now','-5 years')} {1998-10-22 12:34:00}
+datetest 8.17 {datetime('now','+10.5 minutes')} {2003-10-22 12:44:30}
+datetest 8.18 {datetime('now','-1.25 hours')} {2003-10-22 11:19:00}
+datetest 8.19 {datetime('now','11.25 seconds')} {2003-10-22 12:34:11}
+datetest 8.90 {datetime('now','abcdefghijklmnopqrstuvwyxzABCDEFGHIJLMNOP')} NULL
+set sqlite_current_time 0
+
+# Negative years work. Example: '-4713-11-26' is JD 1.5.
+#
+datetest 9.1 {julianday('-4713-11-24 12:00:00')} {0.0}
+datetest 9.2 {julianday(datetime(5))} {5.0}
+datetest 9.3 {julianday(datetime(10))} {10.0}
+datetest 9.4 {julianday(datetime(100))} {100.0}
+datetest 9.5 {julianday(datetime(1000))} {1000.0}
+datetest 9.6 {julianday(datetime(10000))} {10000.0}
+datetest 9.7 {julianday(datetime(100000))} {100000.0}
+
+# datetime() with just an HH:MM:SS correctly inserts the date 2000-01-01.
+#
+datetest 10.1 {datetime('01:02:03')} {2000-01-01 01:02:03}
+datetest 10.2 {date('01:02:03')} {2000-01-01}
+datetest 10.3 {strftime('%Y-%m-%d %H:%M','01:02:03')} {2000-01-01 01:02}
+
+# Test the new HH:MM:SS modifier
+#
+datetest 11.1 {datetime('2004-02-28 20:00:00', '-01:20:30')} \
+ {2004-02-28 18:39:30}
+datetest 11.2 {datetime('2004-02-28 20:00:00', '+12:30:00')} \
+ {2004-02-29 08:30:00}
+datetest 11.3 {datetime('2004-02-28 20:00:00', '+12:30')} \
+ {2004-02-29 08:30:00}
+datetest 11.4 {datetime('2004-02-28 20:00:00', '12:30')} \
+ {2004-02-29 08:30:00}
+datetest 11.5 {datetime('2004-02-28 20:00:00', '-12:00')} \
+ {2004-02-28 08:00:00}
+datetest 11.6 {datetime('2004-02-28 20:00:00', '-12:01')} \
+ {2004-02-28 07:59:00}
+datetest 11.7 {datetime('2004-02-28 20:00:00', '-11:59')} \
+ {2004-02-28 08:01:00}
+datetest 11.8 {datetime('2004-02-28 20:00:00', '11:59')} \
+ {2004-02-29 07:59:00}
+datetest 11.9 {datetime('2004-02-28 20:00:00', '12:01')} \
+ {2004-02-29 08:01:00}
+datetest 11.10 {datetime('2004-02-28 20:00:00', '12:60')} NULL
+
+# Ticket #1964
+datetest 12.1 {datetime('2005-09-01')} {2005-09-01 00:00:00}
+datetest 12.2 {datetime('2005-09-01','+0 hours')} {2005-09-01 00:00:00}
+
+# Ticket #1991
+do_test date-13.1 {
+ execsql {
+ SELECT strftime('%Y-%m-%d %H:%M:%f', julianday('2006-09-24T10:50:26.047'))
+ }
+} {{2006-09-24 10:50:26.047}}
+
+# Ticket #2153
+datetest 13.2 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 12:34:59.6')} \
+ {2007-01-01 12:34:59}
+datetest 13.3 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 12:34:59.6')} \
+ {2007-01-01 12:34:59.600}
+datetest 13.4 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 12:59:59.6')} \
+ {2007-01-01 12:59:59}
+datetest 13.5 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 12:59:59.6')} \
+ {2007-01-01 12:59:59.600}
+datetest 13.6 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 23:59:59.6')} \
+ {2007-01-01 23:59:59}
+datetest 13.7 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 23:59:59.6')} \
+ {2007-01-01 23:59:59.600}
+
+# Test for issues reported by BareFeet (list.sql at tandb.com.au)
+# on mailing list on 2008-06-12.
+#
+# Put a floating point number in the database so that we can manipulate
+# raw bits using the hexio interface.
+#
+do_test date-14.1 {
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1.1);
+ }
+ db close
+ hexio_write test.db 2040 4142ba32bffffff9
+ sqlite3 db test.db
+ db eval {SELECT * FROM t1}
+} {2454629.5}
+
+# Changing the least significant byte of the floating point value between
+# 00 and FF should always generate a time of either 23:59:59 or 00:00:00,
+# never 24:00:00
+#
+for {set i 0} {$i<=255} {incr i} {
+ db close
+ hexio_write test.db 2047 [format %02x $i]
+ sqlite3 db test.db
+ do_test date-14.2.$i {
+ set date [db one {SELECT datetime(x) FROM t1}]
+ expr {$date eq "2008-06-12 00:00:00" || $date eq "2008-06-11 23:59:59"}
+ } {1}
+}
+finish_test
diff --git a/third_party/sqlite/test/default.test b/third_party/sqlite/test/default.test
new file mode 100755
index 0000000..9a2ab1a
--- /dev/null
+++ b/third_party/sqlite/test/default.test
@@ -0,0 +1,52 @@
+# 2005 August 18
+#
+# 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 file is testing corner cases of the DEFAULT syntax
+# on table definitions.
+#
+# $Id: default.test,v 1.2 2005/08/20 03:03:04 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable bloblit {
+ do_test default-1.1 {
+ execsql {
+ CREATE TABLE t1(
+ a INTEGER,
+ b BLOB DEFAULT x'6869'
+ );
+ INSERT INTO t1(a) VALUES(1);
+ SELECT * from t1;
+ }
+ } {1 hi}
+}
+do_test default-1.2 {
+ execsql {
+ CREATE TABLE t2(
+ x INTEGER,
+ y INTEGER DEFAULT NULL
+ );
+ INSERT INTO t2(x) VALUES(1);
+ SELECT * FROM t2;
+ }
+} {1 {}}
+do_test default-1.3 {
+ catchsql {
+ CREATE TABLE t3(
+ x INTEGER,
+ y INTEGER DEFAULT (max(x,5))
+ )
+ }
+} {1 {default value of column [y] is not constant}}
+
+finish_test
diff --git a/third_party/sqlite/test/delete.test b/third_party/sqlite/test/delete.test
new file mode 100755
index 0000000..fc7e81d
--- /dev/null
+++ b/third_party/sqlite/test/delete.test
@@ -0,0 +1,320 @@
+# 2001 September 15
+#
+# 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 file is testing the DELETE FROM statement.
+#
+# $Id: delete.test,v 1.23 2008/04/19 20:53:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Try to delete from a non-existant table.
+#
+do_test delete-1.1 {
+ set v [catch {execsql {DELETE FROM test1}} msg]
+ lappend v $msg
+} {1 {no such table: test1}}
+
+# Try to delete from sqlite_master
+#
+do_test delete-2.1 {
+ set v [catch {execsql {DELETE FROM sqlite_master}} msg]
+ lappend v $msg
+} {1 {table sqlite_master may not be modified}}
+
+# Delete selected entries from a table with and without an index.
+#
+do_test delete-3.1.1 {
+ execsql {CREATE TABLE table1(f1 int, f2 int)}
+ execsql {INSERT INTO table1 VALUES(1,2)}
+ execsql {INSERT INTO table1 VALUES(2,4)}
+ execsql {INSERT INTO table1 VALUES(3,8)}
+ execsql {INSERT INTO table1 VALUES(4,16)}
+ execsql {SELECT * FROM table1 ORDER BY f1}
+} {1 2 2 4 3 8 4 16}
+do_test delete-3.1.2 {
+ execsql {DELETE FROM table1 WHERE f1=3}
+} {}
+do_test delete-3.1.3 {
+ execsql {SELECT * FROM table1 ORDER BY f1}
+} {1 2 2 4 4 16}
+do_test delete-3.1.4 {
+ execsql {CREATE INDEX index1 ON table1(f1)}
+ execsql {PRAGMA count_changes=on}
+ ifcapable explain {
+ execsql {EXPLAIN DELETE FROM table1 WHERE f1=3}
+ }
+ execsql {DELETE FROM 'table1' WHERE f1=3}
+} {0}
+do_test delete-3.1.5 {
+ execsql {SELECT * FROM table1 ORDER BY f1}
+} {1 2 2 4 4 16}
+do_test delete-3.1.6.1 {
+ execsql {DELETE FROM table1 WHERE f1=2}
+} {1}
+do_test delete-3.1.6.2 {
+ db changes
+} 1
+do_test delete-3.1.7 {
+ execsql {SELECT * FROM table1 ORDER BY f1}
+} {1 2 4 16}
+integrity_check delete-3.2
+
+
+# Semantic errors in the WHERE clause
+#
+do_test delete-4.1 {
+ execsql {CREATE TABLE table2(f1 int, f2 int)}
+ set v [catch {execsql {DELETE FROM table2 WHERE f3=5}} msg]
+ lappend v $msg
+} {1 {no such column: f3}}
+
+do_test delete-4.2 {
+ set v [catch {execsql {DELETE FROM table2 WHERE xyzzy(f1+4)}} msg]
+ lappend v $msg
+} {1 {no such function: xyzzy}}
+integrity_check delete-4.3
+
+# Lots of deletes
+#
+do_test delete-5.1.1 {
+ execsql {DELETE FROM table1}
+} {2}
+do_test delete-5.1.2 {
+ execsql {SELECT count(*) FROM table1}
+} {0}
+do_test delete-5.2.1 {
+ execsql {BEGIN TRANSACTION}
+ for {set i 1} {$i<=200} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {200}
+do_test delete-5.2.2 {
+ execsql {DELETE FROM table1}
+} {200}
+do_test delete-5.2.3 {
+ execsql {BEGIN TRANSACTION}
+ for {set i 1} {$i<=200} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {200}
+do_test delete-5.2.4 {
+ execsql {PRAGMA count_changes=off}
+ execsql {DELETE FROM table1}
+} {}
+do_test delete-5.2.5 {
+ execsql {SELECT count(*) FROM table1}
+} {0}
+do_test delete-5.2.6 {
+ execsql {BEGIN TRANSACTION}
+ for {set i 1} {$i<=200} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {200}
+do_test delete-5.3 {
+ for {set i 1} {$i<=200} {incr i 4} {
+ execsql "DELETE FROM table1 WHERE f1==$i"
+ }
+ execsql {SELECT count(*) FROM table1}
+} {150}
+do_test delete-5.4.1 {
+ execsql "DELETE FROM table1 WHERE f1>50"
+ db changes
+} [db one {SELECT count(*) FROM table1 WHERE f1>50}]
+do_test delete-5.4.2 {
+ execsql {SELECT count(*) FROM table1}
+} {37}
+do_test delete-5.5 {
+ for {set i 1} {$i<=70} {incr i 3} {
+ execsql "DELETE FROM table1 WHERE f1==$i"
+ }
+ execsql {SELECT f1 FROM table1 ORDER BY f1}
+} {2 3 6 8 11 12 14 15 18 20 23 24 26 27 30 32 35 36 38 39 42 44 47 48 50}
+do_test delete-5.6 {
+ for {set i 1} {$i<40} {incr i} {
+ execsql "DELETE FROM table1 WHERE f1==$i"
+ }
+ execsql {SELECT f1 FROM table1 ORDER BY f1}
+} {42 44 47 48 50}
+do_test delete-5.7 {
+ execsql "DELETE FROM table1 WHERE f1!=48"
+ execsql {SELECT f1 FROM table1 ORDER BY f1}
+} {48}
+integrity_check delete-5.8
+
+
+# Delete large quantities of data. We want to test the List overflow
+# mechanism in the vdbe.
+#
+do_test delete-6.1 {
+ execsql {BEGIN; DELETE FROM table1}
+ for {set i 1} {$i<=3000} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {DELETE FROM table2}
+ for {set i 1} {$i<=3000} {incr i} {
+ execsql "INSERT INTO table2 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {3000}
+do_test delete-6.2 {
+ execsql {SELECT count(*) FROM table2}
+} {3000}
+do_test delete-6.3 {
+ execsql {SELECT f1 FROM table1 WHERE f1<10 ORDER BY f1}
+} {1 2 3 4 5 6 7 8 9}
+do_test delete-6.4 {
+ execsql {SELECT f1 FROM table2 WHERE f1<10 ORDER BY f1}
+} {1 2 3 4 5 6 7 8 9}
+do_test delete-6.5.1 {
+ execsql {DELETE FROM table1 WHERE f1>7}
+ db changes
+} {2993}
+do_test delete-6.5.2 {
+ execsql {SELECT f1 FROM table1 ORDER BY f1}
+} {1 2 3 4 5 6 7}
+do_test delete-6.6 {
+ execsql {DELETE FROM table2 WHERE f1>7}
+ execsql {SELECT f1 FROM table2 ORDER BY f1}
+} {1 2 3 4 5 6 7}
+do_test delete-6.7 {
+ execsql {DELETE FROM table1}
+ execsql {SELECT f1 FROM table1}
+} {}
+do_test delete-6.8 {
+ execsql {INSERT INTO table1 VALUES(2,3)}
+ execsql {SELECT f1 FROM table1}
+} {2}
+do_test delete-6.9 {
+ execsql {DELETE FROM table2}
+ execsql {SELECT f1 FROM table2}
+} {}
+do_test delete-6.10 {
+ execsql {INSERT INTO table2 VALUES(2,3)}
+ execsql {SELECT f1 FROM table2}
+} {2}
+integrity_check delete-6.11
+
+do_test delete-7.1 {
+ execsql {
+ CREATE TABLE t3(a);
+ INSERT INTO t3 VALUES(1);
+ INSERT INTO t3 SELECT a+1 FROM t3;
+ INSERT INTO t3 SELECT a+2 FROM t3;
+ SELECT * FROM t3;
+ }
+} {1 2 3 4}
+ifcapable {trigger} {
+ do_test delete-7.2 {
+ execsql {
+ CREATE TABLE cnt(del);
+ INSERT INTO cnt VALUES(0);
+ CREATE TRIGGER r1 AFTER DELETE ON t3 FOR EACH ROW BEGIN
+ UPDATE cnt SET del=del+1;
+ END;
+ DELETE FROM t3 WHERE a<2;
+ SELECT * FROM t3;
+ }
+ } {2 3 4}
+ do_test delete-7.3 {
+ execsql {
+ SELECT * FROM cnt;
+ }
+ } {1}
+ do_test delete-7.4 {
+ execsql {
+ DELETE FROM t3;
+ SELECT * FROM t3;
+ }
+ } {}
+ do_test delete-7.5 {
+ execsql {
+ SELECT * FROM cnt;
+ }
+ } {4}
+ do_test delete-7.6 {
+ execsql {
+ INSERT INTO t3 VALUES(1);
+ INSERT INTO t3 SELECT a+1 FROM t3;
+ INSERT INTO t3 SELECT a+2 FROM t3;
+ CREATE TABLE t4 AS SELECT * FROM t3;
+ PRAGMA count_changes=ON;
+ DELETE FROM t3;
+ DELETE FROM t4;
+ }
+ } {4 4}
+} ;# endif trigger
+ifcapable {!trigger} {
+ execsql {DELETE FROM t3}
+}
+integrity_check delete-7.7
+
+# Make sure error messages are consistent when attempting to delete
+# from a read-only database. Ticket #304.
+#
+do_test delete-8.0 {
+ execsql {
+ PRAGMA count_changes=OFF;
+ INSERT INTO t3 VALUES(123);
+ SELECT * FROM t3;
+ }
+} {123}
+db close
+catch {file delete -force test.db-journal}
+catch {file attributes test.db -permissions 0444}
+catch {file attributes test.db -readonly 1}
+sqlite3 db test.db
+set ::DB [sqlite3_connection_pointer db]
+do_test delete-8.1 {
+ catchsql {
+ DELETE FROM t3;
+ }
+} {1 {attempt to write a readonly database}}
+do_test delete-8.2 {
+ execsql {SELECT * FROM t3}
+} {123}
+do_test delete-8.3 {
+ catchsql {
+ DELETE FROM t3 WHERE 1;
+ }
+} {1 {attempt to write a readonly database}}
+do_test delete-8.4 {
+ execsql {SELECT * FROM t3}
+} {123}
+
+# Update for v3: In v2 the DELETE statement would succeed because no
+# database writes actually occur. Version 3 refuses to open a transaction
+# on a read-only file, so the statement fails.
+do_test delete-8.5 {
+ catchsql {
+ DELETE FROM t3 WHERE a<100;
+ }
+# v2 result: {0 {}}
+} {1 {attempt to write a readonly database}}
+do_test delete-8.6 {
+ execsql {SELECT * FROM t3}
+} {123}
+integrity_check delete-8.7
+
+# Need to do the following for tcl 8.5 on mac. On that configuration, the
+# -readonly flag is taken so seriously that a subsequent [file delete -force]
+# (required before the next test file can be executed) will fail.
+#
+catch {file attributes test.db -readonly 0}
+
+finish_test
diff --git a/third_party/sqlite/test/delete2.test b/third_party/sqlite/test/delete2.test
new file mode 100755
index 0000000..1668a3a
--- /dev/null
+++ b/third_party/sqlite/test/delete2.test
@@ -0,0 +1,119 @@
+# 2003 September 6
+#
+# 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 a test to replicate the bug reported by
+# ticket #842.
+#
+# Ticket #842 was a database corruption problem caused by a DELETE that
+# removed an index entry by not the main table entry. To recreate the
+# problem do this:
+#
+# (1) Create a table with an index. Insert some data into that table.
+# (2) Start a query on the table but do not complete the query.
+# (3) Try to delete a single entry from the table.
+#
+# Step 3 will fail because there is still a read cursor on the table.
+# But the database is corrupted by the DELETE. It turns out that the
+# index entry was deleted first, before the table entry. And the index
+# delete worked. Thus an entry was deleted from the index but not from
+# the table.
+#
+# The solution to the problem was to detect that the table is locked
+# before the index entry is deleted.
+#
+# $Id: delete2.test,v 1.8 2008/07/08 15:59:52 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table that has an index.
+#
+do_test delete2-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TABLE q(s string, id string, constraint pk_q primary key(id));
+ BEGIN;
+ INSERT INTO q(s,id) VALUES('hello','id.1');
+ INSERT INTO q(s,id) VALUES('goodbye','id.2');
+ INSERT INTO q(s,id) VALUES('again','id.3');
+ END;
+ SELECT * FROM q;
+ }
+} {hello id.1 goodbye id.2 again id.3}
+do_test delete2-1.2 {
+ execsql {
+ SELECT * FROM q WHERE id='id.1';
+ }
+} {hello id.1}
+integrity_check delete2-1.3
+
+# Start a query on the table. The query should not use the index.
+# Do not complete the query, thus leaving the table locked.
+#
+do_test delete2-1.4 {
+ set STMT [sqlite3_prepare $DB {SELECT * FROM q} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+integrity_check delete2-1.5
+
+# Try to delete a row from the table while a read is in process.
+# As of 2006-08-16, this is allowed. (It used to fail with SQLITE_LOCKED.)
+#
+do_test delete2-1.6 {
+ catchsql {
+ DELETE FROM q WHERE rowid=1
+ }
+} {0 {}}
+integrity_check delete2-1.7
+do_test delete2-1.8 {
+ execsql {
+ SELECT * FROM q;
+ }
+} {goodbye id.2 again id.3}
+
+# Finalize the query, thus clearing the lock on the table. Then
+# retry the delete. The delete should work this time.
+#
+do_test delete2-1.9 {
+ sqlite3_finalize $STMT
+ catchsql {
+ DELETE FROM q WHERE rowid=1
+ }
+} {0 {}}
+integrity_check delete2-1.10
+do_test delete2-1.11 {
+ execsql {
+ SELECT * FROM q;
+ }
+} {goodbye id.2 again id.3}
+
+do_test delete2-2.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t2 VALUES(3, 4);
+ INSERT INTO t2 VALUES(5, 6);
+ }
+} {}
+do_test delete2-2.2 {
+ set res [list]
+ db eval {
+ SELECT CASE WHEN c = 5 THEN b ELSE NULL END AS b, c, d FROM t1, t2
+ } {
+ db eval {DELETE FROM t1}
+ lappend res $b $c $d
+ }
+ set res
+} {{} 3 4 {} 5 6}
+
+finish_test
diff --git a/third_party/sqlite/test/delete3.test b/third_party/sqlite/test/delete3.test
new file mode 100755
index 0000000..a31f6ec
--- /dev/null
+++ b/third_party/sqlite/test/delete3.test
@@ -0,0 +1,57 @@
+# 2005 August 24
+#
+# 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 a test of the DELETE command where a
+# large number of rows are deleted.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table that contains a large number of rows.
+#
+do_test delete3-1.1 {
+ execsql {
+ CREATE TABLE t1(x integer primary key);
+ BEGIN;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 SELECT x+2 FROM t1;
+ INSERT INTO t1 SELECT x+4 FROM t1;
+ INSERT INTO t1 SELECT x+8 FROM t1;
+ INSERT INTO t1 SELECT x+16 FROM t1;
+ INSERT INTO t1 SELECT x+32 FROM t1;
+ INSERT INTO t1 SELECT x+64 FROM t1;
+ INSERT INTO t1 SELECT x+128 FROM t1;
+ INSERT INTO t1 SELECT x+256 FROM t1;
+ INSERT INTO t1 SELECT x+512 FROM t1;
+ INSERT INTO t1 SELECT x+1024 FROM t1;
+ INSERT INTO t1 SELECT x+2048 FROM t1;
+ INSERT INTO t1 SELECT x+4096 FROM t1;
+ INSERT INTO t1 SELECT x+8192 FROM t1;
+ INSERT INTO t1 SELECT x+16384 FROM t1;
+ INSERT INTO t1 SELECT x+32768 FROM t1;
+ INSERT INTO t1 SELECT x+65536 FROM t1;
+ INSERT INTO t1 SELECT x+131072 FROM t1;
+ INSERT INTO t1 SELECT x+262144 FROM t1;
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+} {524288}
+do_test delete3-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE x%2==0;
+ SELECT count(*) FROM t1;
+ }
+} {262144}
+integrity_check delete3-1.3
+
+finish_test
diff --git a/third_party/sqlite/test/descidx1.test b/third_party/sqlite/test/descidx1.test
new file mode 100755
index 0000000..60cf969
--- /dev/null
+++ b/third_party/sqlite/test/descidx1.test
@@ -0,0 +1,359 @@
+# 2005 December 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is descending indices.
+#
+# $Id: descidx1.test,v 1.10 2008/03/19 00:21:31 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db eval {PRAGMA legacy_file_format=OFF}
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+
+# Verify that the file format starts as 4.
+#
+do_test descidx1-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ CREATE INDEX i1 ON t1(b ASC);
+ }
+ get_file_format
+} {4}
+do_test descidx1-1.2 {
+ execsql {
+ CREATE INDEX i2 ON t1(a DESC);
+ }
+ get_file_format
+} {4}
+
+# Put some information in the table and verify that the descending
+# index actually works.
+#
+do_test descidx1-2.1 {
+ execsql {
+ INSERT INTO t1 VALUES(1,1);
+ INSERT INTO t1 VALUES(2,2);
+ INSERT INTO t1 SELECT a+2, a+2 FROM t1;
+ INSERT INTO t1 SELECT a+4, a+4 FROM t1;
+ SELECT b FROM t1 WHERE a>3 AND a<7;
+ }
+} {6 5 4}
+do_test descidx1-2.2 {
+ execsql {
+ SELECT a FROM t1 WHERE b>3 AND b<7;
+ }
+} {4 5 6}
+do_test descidx1-2.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a>=3 AND a<7;
+ }
+} {6 5 4 3}
+do_test descidx1-2.4 {
+ execsql {
+ SELECT b FROM t1 WHERE a>3 AND a<=7;
+ }
+} {7 6 5 4}
+do_test descidx1-2.5 {
+ execsql {
+ SELECT b FROM t1 WHERE a>=3 AND a<=7;
+ }
+} {7 6 5 4 3}
+do_test descidx1-2.6 {
+ execsql {
+ SELECT a FROM t1 WHERE b>=3 AND b<=7;
+ }
+} {3 4 5 6 7}
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+# Test sorting using a descending index.
+#
+do_test descidx1-3.1 {
+ cksort {SELECT a FROM t1 ORDER BY a}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.2 {
+ cksort {SELECT a FROM t1 ORDER BY a ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.3 {
+ cksort {SELECT a FROM t1 ORDER BY a DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx1-3.4 {
+ cksort {SELECT b FROM t1 ORDER BY a}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.5 {
+ cksort {SELECT b FROM t1 ORDER BY a ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.6 {
+ cksort {SELECT b FROM t1 ORDER BY a DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx1-3.7 {
+ cksort {SELECT a FROM t1 ORDER BY b}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.8 {
+ cksort {SELECT a FROM t1 ORDER BY b ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.9 {
+ cksort {SELECT a FROM t1 ORDER BY b DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx1-3.10 {
+ cksort {SELECT b FROM t1 ORDER BY b}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.11 {
+ cksort {SELECT b FROM t1 ORDER BY b ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx1-3.12 {
+ cksort {SELECT b FROM t1 ORDER BY b DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+
+do_test descidx1-3.21 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a}
+} {4 5 6 7 nosort}
+do_test descidx1-3.22 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a ASC}
+} {4 5 6 7 nosort}
+do_test descidx1-3.23 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a DESC}
+} {7 6 5 4 nosort}
+do_test descidx1-3.24 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a}
+} {4 5 6 7 nosort}
+do_test descidx1-3.25 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a ASC}
+} {4 5 6 7 nosort}
+do_test descidx1-3.26 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a DESC}
+} {7 6 5 4 nosort}
+
+# Create a table with indices that are descending on some terms and
+# ascending on others.
+#
+ifcapable bloblit {
+ do_test descidx1-4.1 {
+ execsql {
+ CREATE TABLE t2(a INT, b TEXT, c BLOB, d REAL);
+ CREATE INDEX i3 ON t2(a ASC, b DESC, c ASC);
+ CREATE INDEX i4 ON t2(b DESC, a ASC, d DESC);
+ INSERT INTO t2 VALUES(1,'one',x'31',1.0);
+ INSERT INTO t2 VALUES(2,'two',x'3232',2.0);
+ INSERT INTO t2 VALUES(3,'three',x'333333',3.0);
+ INSERT INTO t2 VALUES(4,'four',x'34343434',4.0);
+ INSERT INTO t2 VALUES(5,'five',x'3535353535',5.0);
+ INSERT INTO t2 VALUES(6,'six',x'363636363636',6.0);
+ INSERT INTO t2 VALUES(2,'two',x'323232',2.1);
+ INSERT INTO t2 VALUES(2,'zwei',x'3232',2.2);
+ INSERT INTO t2 VALUES(2,NULL,NULL,2.3);
+ SELECT count(*) FROM t2;
+ }
+ } {9}
+ do_test descidx1-4.2 {
+ execsql {
+ SELECT d FROM t2 ORDER BY a;
+ }
+ } {1.0 2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0}
+ do_test descidx1-4.3 {
+ execsql {
+ SELECT d FROM t2 WHERE a>=2;
+ }
+ } {2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0}
+ do_test descidx1-4.4 {
+ execsql {
+ SELECT d FROM t2 WHERE a>2;
+ }
+ } {3.0 4.0 5.0 6.0}
+ do_test descidx1-4.5 {
+ execsql {
+ SELECT d FROM t2 WHERE a=2 AND b>'two';
+ }
+ } {2.2}
+ do_test descidx1-4.6 {
+ execsql {
+ SELECT d FROM t2 WHERE a=2 AND b>='two';
+ }
+ } {2.2 2.0 2.1}
+ do_test descidx1-4.7 {
+ execsql {
+ SELECT d FROM t2 WHERE a=2 AND b<'two';
+ }
+ } {}
+ do_test descidx1-4.8 {
+ execsql {
+ SELECT d FROM t2 WHERE a=2 AND b<='two';
+ }
+ } {2.0 2.1}
+}
+
+do_test descidx1-5.1 {
+ execsql {
+ CREATE TABLE t3(a,b,c,d);
+ CREATE INDEX t3i1 ON t3(a DESC, b ASC, c DESC, d ASC);
+ INSERT INTO t3 VALUES(0,0,0,0);
+ INSERT INTO t3 VALUES(0,0,0,1);
+ INSERT INTO t3 VALUES(0,0,1,0);
+ INSERT INTO t3 VALUES(0,0,1,1);
+ INSERT INTO t3 VALUES(0,1,0,0);
+ INSERT INTO t3 VALUES(0,1,0,1);
+ INSERT INTO t3 VALUES(0,1,1,0);
+ INSERT INTO t3 VALUES(0,1,1,1);
+ INSERT INTO t3 VALUES(1,0,0,0);
+ INSERT INTO t3 VALUES(1,0,0,1);
+ INSERT INTO t3 VALUES(1,0,1,0);
+ INSERT INTO t3 VALUES(1,0,1,1);
+ INSERT INTO t3 VALUES(1,1,0,0);
+ INSERT INTO t3 VALUES(1,1,0,1);
+ INSERT INTO t3 VALUES(1,1,1,0);
+ INSERT INTO t3 VALUES(1,1,1,1);
+ SELECT count(*) FROM t3;
+ }
+} {16}
+do_test descidx1-5.2 {
+ cksort {
+ SELECT a||b||c||d FROM t3 ORDER BY a,b,c,d;
+ }
+} {0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 sort}
+do_test descidx1-5.3 {
+ cksort {
+ SELECT a||b||c||d FROM t3 ORDER BY a DESC, b ASC, c DESC, d ASC;
+ }
+} {1010 1011 1000 1001 1110 1111 1100 1101 0010 0011 0000 0001 0110 0111 0100 0101 nosort}
+do_test descidx1-5.4 {
+ cksort {
+ SELECT a||b||c||d FROM t3 ORDER BY a ASC, b DESC, c ASC, d DESC;
+ }
+} {0101 0100 0111 0110 0001 0000 0011 0010 1101 1100 1111 1110 1001 1000 1011 1010 nosort}
+do_test descidx1-5.5 {
+ cksort {
+ SELECT a||b||c FROM t3 WHERE d=0 ORDER BY a DESC, b ASC, c DESC
+ }
+} {101 100 111 110 001 000 011 010 nosort}
+do_test descidx1-5.6 {
+ cksort {
+ SELECT a||b||c FROM t3 WHERE d=0 ORDER BY a ASC, b DESC, c ASC
+ }
+} {010 011 000 001 110 111 100 101 nosort}
+do_test descidx1-5.7 {
+ cksort {
+ SELECT a||b||c FROM t3 WHERE d=0 ORDER BY a ASC, b DESC, c DESC
+ }
+} {011 010 001 000 111 110 101 100 sort}
+do_test descidx1-5.8 {
+ cksort {
+ SELECT a||b||c FROM t3 WHERE d=0 ORDER BY a ASC, b ASC, c ASC
+ }
+} {000 001 010 011 100 101 110 111 sort}
+do_test descidx1-5.9 {
+ cksort {
+ SELECT a||b||c FROM t3 WHERE d=0 ORDER BY a DESC, b DESC, c ASC
+ }
+} {110 111 100 101 010 011 000 001 sort}
+
+# Test the legacy_file_format pragma here because we have access to
+# the get_file_format command.
+#
+ifcapable legacyformat {
+ do_test descidx1-6.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {PRAGMA legacy_file_format}
+ } {1}
+} else {
+ do_test descidx1-6.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {PRAGMA legacy_file_format}
+ } {0}
+}
+do_test descidx1-6.2 {
+ execsql {PRAGMA legacy_file_format=YES}
+ execsql {PRAGMA legacy_file_format}
+} {1}
+do_test descidx1-6.3 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ }
+ get_file_format
+} {1}
+ifcapable vacuum {
+ # Verify that the file format is preserved across a vacuum.
+ do_test descidx1-6.3.1 {
+ execsql {VACUUM}
+ get_file_format
+ } {1}
+}
+do_test descidx1-6.4 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {PRAGMA legacy_file_format=NO}
+ execsql {PRAGMA legacy_file_format}
+} {0}
+do_test descidx1-6.5 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ CREATE INDEX i1 ON t1(a ASC, b DESC, c ASC);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(1,1,0);
+ INSERT INTO t1 VALUES(1,2,1);
+ INSERT INTO t1 VALUES(1,3,4);
+ }
+ get_file_format
+} {4}
+ifcapable vacuum {
+ # Verify that the file format is preserved across a vacuum.
+ do_test descidx1-6.6 {
+ execsql {VACUUM}
+ get_file_format
+ } {4}
+ do_test descidx1-6.7 {
+ execsql {
+ PRAGMA legacy_file_format=ON;
+ VACUUM;
+ }
+ get_file_format
+ } {4}
+}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/descidx2.test b/third_party/sqlite/test/descidx2.test
new file mode 100755
index 0000000..69a88f1
--- /dev/null
+++ b/third_party/sqlite/test/descidx2.test
@@ -0,0 +1,179 @@
+# 2005 December 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is descending indices.
+#
+# $Id: descidx2.test,v 1.5 2008/03/19 00:21:31 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db eval {PRAGMA legacy_file_format=OFF}
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+
+# Verify that the file format starts as 4
+#
+do_test descidx2-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ CREATE INDEX i1 ON t1(b ASC);
+ }
+ get_file_format
+} {4}
+do_test descidx2-1.2 {
+ execsql {
+ CREATE INDEX i2 ON t1(a DESC);
+ }
+ get_file_format
+} {4}
+
+# Before adding any information to the database, set the file format
+# back to three. Then close and reopen the database. With the file
+# format set to three, SQLite should ignore the DESC argument on the
+# index.
+#
+do_test descidx2-2.0 {
+ set_file_format 3
+ db close
+ sqlite3 db test.db
+ get_file_format
+} {3}
+
+# Put some information in the table and verify that the DESC
+# on the index is ignored.
+#
+do_test descidx2-2.1 {
+ execsql {
+ INSERT INTO t1 VALUES(1,1);
+ INSERT INTO t1 VALUES(2,2);
+ INSERT INTO t1 SELECT a+2, a+2 FROM t1;
+ INSERT INTO t1 SELECT a+4, a+4 FROM t1;
+ SELECT b FROM t1 WHERE a>3 AND a<7;
+ }
+} {4 5 6}
+do_test descidx2-2.2 {
+ execsql {
+ SELECT a FROM t1 WHERE b>3 AND b<7;
+ }
+} {4 5 6}
+do_test descidx2-2.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a>=3 AND a<7;
+ }
+} {3 4 5 6}
+do_test descidx2-2.4 {
+ execsql {
+ SELECT b FROM t1 WHERE a>3 AND a<=7;
+ }
+} {4 5 6 7}
+do_test descidx2-2.5 {
+ execsql {
+ SELECT b FROM t1 WHERE a>=3 AND a<=7;
+ }
+} {3 4 5 6 7}
+do_test descidx2-2.6 {
+ execsql {
+ SELECT a FROM t1 WHERE b>=3 AND b<=7;
+ }
+} {3 4 5 6 7}
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+# Test sorting using a descending index.
+#
+do_test descidx2-3.1 {
+ cksort {SELECT a FROM t1 ORDER BY a}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.2 {
+ cksort {SELECT a FROM t1 ORDER BY a ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.3 {
+ cksort {SELECT a FROM t1 ORDER BY a DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx2-3.4 {
+ cksort {SELECT b FROM t1 ORDER BY a}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.5 {
+ cksort {SELECT b FROM t1 ORDER BY a ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.6 {
+ cksort {SELECT b FROM t1 ORDER BY a DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx2-3.7 {
+ cksort {SELECT a FROM t1 ORDER BY b}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.8 {
+ cksort {SELECT a FROM t1 ORDER BY b ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.9 {
+ cksort {SELECT a FROM t1 ORDER BY b DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+do_test descidx2-3.10 {
+ cksort {SELECT b FROM t1 ORDER BY b}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.11 {
+ cksort {SELECT b FROM t1 ORDER BY b ASC}
+} {1 2 3 4 5 6 7 8 nosort}
+do_test descidx2-3.12 {
+ cksort {SELECT b FROM t1 ORDER BY b DESC}
+} {8 7 6 5 4 3 2 1 nosort}
+
+do_test descidx2-3.21 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a}
+} {4 5 6 7 nosort}
+do_test descidx2-3.22 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a ASC}
+} {4 5 6 7 nosort}
+do_test descidx2-3.23 {
+ cksort {SELECT a FROM t1 WHERE a>3 AND a<8 ORDER BY a DESC}
+} {7 6 5 4 nosort}
+do_test descidx2-3.24 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a}
+} {4 5 6 7 nosort}
+do_test descidx2-3.25 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a ASC}
+} {4 5 6 7 nosort}
+do_test descidx2-3.26 {
+ cksort {SELECT b FROM t1 WHERE a>3 AND a<8 ORDER BY a DESC}
+} {7 6 5 4 nosort}
+
+finish_test
diff --git a/third_party/sqlite/test/descidx3.test b/third_party/sqlite/test/descidx3.test
new file mode 100755
index 0000000..5cfd1f6
--- /dev/null
+++ b/third_party/sqlite/test/descidx3.test
@@ -0,0 +1,149 @@
+# 2006 January 02
+#
+# 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 descending indices.
+#
+# $Id: descidx3.test,v 1.6 2008/03/19 00:21:31 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !bloblit {
+ finish_test
+ return
+}
+db eval {PRAGMA legacy_file_format=OFF}
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+# This procedure returns the value of the file-format in file 'test.db'.
+#
+proc get_file_format {{fname test.db}} {
+ return [hexio_get_int [hexio_read $fname 44 4]]
+}
+
+# Verify that the file format starts as 4.
+#
+do_test descidx3-1.1 {
+ execsql {
+ CREATE TABLE t1(i INTEGER PRIMARY KEY,a,b,c,d);
+ CREATE INDEX t1i1 ON t1(a DESC, b ASC, c DESC);
+ CREATE INDEX t1i2 ON t1(b DESC, c ASC, d DESC);
+ }
+ get_file_format
+} {4}
+
+# Put some information in the table and verify that the descending
+# index actually works.
+#
+do_test descidx3-2.1 {
+ execsql {
+ INSERT INTO t1 VALUES(1, NULL, NULL, NULL, NULL);
+ INSERT INTO t1 VALUES(2, 2, 2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3, 3, 3);
+ INSERT INTO t1 VALUES(4, 2.5, 2.5, 2.5, 2.5);
+ INSERT INTO t1 VALUES(5, -5, -5, -5, -5);
+ INSERT INTO t1 VALUES(6, 'six', 'six', 'six', 'six');
+ INSERT INTO t1 VALUES(7, x'77', x'77', x'77', x'77');
+ INSERT INTO t1 VALUES(8, 'eight', 'eight', 'eight', 'eight');
+ INSERT INTO t1 VALUES(9, x'7979', x'7979', x'7979', x'7979');
+ SELECT count(*) FROM t1;
+ }
+} 9
+do_test descidx3-2.2 {
+ execsql {
+ SELECT i FROM t1 ORDER BY a;
+ }
+} {1 5 2 4 3 8 6 7 9}
+do_test descidx3-2.3 {
+ execsql {
+ SELECT i FROM t1 ORDER BY a DESC;
+ }
+} {9 7 6 8 3 4 2 5 1}
+
+# The "natural" order for the index is decreasing
+do_test descidx3-2.4 {
+ execsql {
+ SELECT i FROM t1 WHERE a<=x'7979';
+ }
+} {9 7 6 8 3 4 2 5}
+do_test descidx3-2.5 {
+ execsql {
+ SELECT i FROM t1 WHERE a>-99;
+ }
+} {9 7 6 8 3 4 2 5}
+
+# Even when all values of t1.a are the same, sorting by A returns
+# the rows in reverse order because this the natural order of the
+# index.
+#
+do_test descidx3-3.1 {
+ execsql {
+ UPDATE t1 SET a=1;
+ SELECT i FROM t1 ORDER BY a;
+ }
+} {9 7 6 8 3 4 2 5 1}
+do_test descidx3-3.2 {
+ execsql {
+ SELECT i FROM t1 WHERE a=1 AND b>0 AND b<'zzz'
+ }
+} {2 4 3 8 6}
+do_test descidx3-3.3 {
+ execsql {
+ SELECT i FROM t1 WHERE b>0 AND b<'zzz'
+ }
+} {6 8 3 4 2}
+do_test descidx3-3.4 {
+ execsql {
+ SELECT i FROM t1 WHERE a=1 AND b>-9999 AND b<x'ffffffff'
+ }
+} {5 2 4 3 8 6 7 9}
+do_test descidx3-3.5 {
+ execsql {
+ SELECT i FROM t1 WHERE b>-9999 AND b<x'ffffffff'
+ }
+} {9 7 6 8 3 4 2 5}
+
+ifcapable subquery {
+ # If the subquery capability is not compiled in to the binary, then
+ # the IN(...) operator is not available. Hence these tests cannot be
+ # run.
+ do_test descidx3-4.1 {
+ execsql {
+ UPDATE t1 SET a=2 WHERE i<6;
+ SELECT i FROM t1 WHERE a IN (1,2) AND b>0 AND b<'zzz';
+ }
+ } {8 6 2 4 3}
+ do_test descidx3-4.2 {
+ execsql {
+ UPDATE t1 SET a=1;
+ SELECT i FROM t1 WHERE a IN (1,2) AND b>0 AND b<'zzz';
+ }
+ } {2 4 3 8 6}
+ do_test descidx3-4.3 {
+ execsql {
+ UPDATE t1 SET b=2;
+ SELECT i FROM t1 WHERE a IN (1,2) AND b>0 AND b<'zzz';
+ }
+ } {9 7 6 8 3 4 2 5 1}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/diskfull.test b/third_party/sqlite/test/diskfull.test
new file mode 100755
index 0000000..a71db91
--- /dev/null
+++ b/third_party/sqlite/test/diskfull.test
@@ -0,0 +1,115 @@
+# 2001 October 12
+#
+# 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 file is testing for correct handling of disk full
+# errors.
+#
+# $Id: diskfull.test,v 1.8 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set sqlite_io_error_persist 0
+set sqlite_io_error_hit 0
+set sqlite_io_error_pending 0
+do_test diskfull-1.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(randstr(1000,1000));
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ CREATE INDEX t1i1 ON t1(x);
+ CREATE TABLE t2 AS SELECT x AS a, x AS b FROM t1;
+ CREATE INDEX t2i1 ON t2(b);
+ }
+} {}
+set sqlite_diskfull_pending 0
+integrity_check diskfull-1.2
+do_test diskfull-1.3 {
+ set sqlite_diskfull_pending 1
+ catchsql {
+ INSERT INTO t1 SELECT * FROM t1;
+ }
+} {1 {database or disk is full}}
+set sqlite_diskfull_pending 0
+integrity_check diskfull-1.4
+do_test diskfull-1.5 {
+ set sqlite_diskfull_pending 1
+ catchsql {
+ DELETE FROM t1;
+ }
+} {1 {database or disk is full}}
+set sqlite_diskfull_pending 0
+set sqlite_io_error_hit 0
+integrity_check diskfull-1.6
+
+proc do_diskfull_test {prefix sql} {
+ set ::go 1
+ set ::sql $sql
+ set ::i 1
+ while {$::go} {
+ incr ::i
+ do_test ${prefix}.$::i.1 {
+ set ::sqlite_diskfull_pending $::i
+ set ::sqlite_diskfull 0
+ set r [catchsql $::sql]
+ if {!$::sqlite_diskfull} {
+ set r {1 {database or disk is full}}
+ set ::go 0
+ }
+ if {$r=="1 {disk I/O error}"} {
+ set r {1 {database or disk is full}}
+ }
+ set r
+ } {1 {database or disk is full}}
+ set ::sqlite_diskfull_pending 0
+ db close
+ sqlite3 db test.db
+ integrity_check ${prefix}.$::i.2
+ }
+}
+
+do_diskfull_test diskfull-2 VACUUM
+
+# db close
+# file delete -force test.db
+# file delete -force test.db-journal
+# sqlite3 db test.db
+#
+# do_test diskfull-3.1 {
+# execsql {
+# PRAGMA default_cache_size = 10;
+# CREATE TABLE t3(a, b, UNIQUE(a, b));
+# INSERT INTO t3 VALUES( randstr(100, 100), randstr(100, 100) );
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+# UPDATE t3
+# SET b = (SELECT a FROM t3 WHERE rowid = (SELECT max(rowid)-1 FROM t3))
+# WHERE rowid = (SELECT max(rowid) FROM t3);
+# PRAGMA cache_size;
+# }
+# } {10}
+#
+# do_diskfull_test diskfull-3.2 {
+# BEGIN;
+# INSERT INTO t3 VALUES( randstr(100, 100), randstr(100, 100) );
+# UPDATE t3 SET a = b;
+# COMMIT;
+# }
+
+finish_test
diff --git a/third_party/sqlite/test/distinctagg.test b/third_party/sqlite/test/distinctagg.test
new file mode 100755
index 0000000..b2191ea
--- /dev/null
+++ b/third_party/sqlite/test/distinctagg.test
@@ -0,0 +1,57 @@
+# 2005 September 11
+#
+# 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 the DISTINCT modifier on aggregate functions.
+#
+# $Id: distinctagg.test,v 1.2 2005/09/12 23:03:17 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test distinctagg-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(1,3,4);
+ INSERT INTO t1 VALUES(1,3,5);
+ SELECT count(distinct a),
+ count(distinct b),
+ count(distinct c),
+ count(all a) FROM t1;
+ }
+} {1 2 3 3}
+do_test distinctagg-1.2 {
+ execsql {
+ SELECT b, count(distinct c) FROM t1 GROUP BY b ORDER BY b
+ }
+} {2 1 3 2}
+do_test distinctagg-1.3 {
+ execsql {
+ INSERT INTO t1 SELECT a+1, b+3, c+5 FROM t1;
+ INSERT INTO t1 SELECT a+2, b+6, c+10 FROM t1;
+ INSERT INTO t1 SELECT a+4, b+12, c+20 FROM t1;
+ SELECT count(*), count(distinct a), count(distinct b) FROM t1
+ }
+} {24 8 16}
+do_test distinctagg-1.4 {
+ execsql {
+ SELECT a, count(distinct c) FROM t1 GROUP BY a ORDER BY a
+ }
+} {1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3}
+
+do_test distinctagg-2.1 {
+ catchsql {
+ SELECT count(distinct) FROM t1;
+ }
+} {1 {DISTINCT in aggregate must be followed by an expression}}
+
+finish_test
diff --git a/third_party/sqlite/test/enc.test b/third_party/sqlite/test/enc.test
new file mode 100755
index 0000000..5c24bbb
--- /dev/null
+++ b/third_party/sqlite/test/enc.test
@@ -0,0 +1,172 @@
+# 2002 May 24
+#
+# 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 file is testing the SQLite routines used for converting between the
+# various suported unicode encodings (UTF-8, UTF-16, UTF-16le and
+# UTF-16be).
+#
+# $Id: enc.test,v 1.7 2007/05/23 16:23:09 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this test if the build does not support multiple encodings.
+#
+ifcapable {!utf16} {
+ finish_test
+ return
+}
+
+proc do_bincmp_test {testname got expect} {
+ binary scan $expect \c* expectvals
+ binary scan $got \c* gotvals
+ do_test $testname [list set dummy $gotvals] $expectvals
+}
+
+# $utf16 is a UTF-16 encoded string. Swap each pair of bytes around
+# to change the byte-order of the string.
+proc swap_byte_order {utf16} {
+ binary scan $utf16 \c* ints
+
+ foreach {a b} $ints {
+ lappend ints2 $b
+ lappend ints2 $a
+ }
+
+ return [binary format \c* $ints2]
+}
+
+#
+# Test that the SQLite routines for converting between UTF encodings
+# produce the same results as their TCL counterparts.
+#
+# $testname is the prefix to be used for the test names.
+# $str is a string to use for testing (encoded in UTF-8, as normal for TCL).
+#
+# The test procedure is:
+# 1. Convert the string from UTF-8 to UTF-16le and check that the TCL and
+# SQLite routines produce the same results.
+#
+# 2. Convert the string from UTF-8 to UTF-16be and check that the TCL and
+# SQLite routines produce the same results.
+#
+# 3. Use the SQLite routines to convert the native machine order UTF-16
+# representation back to the original UTF-8. Check that the result
+# matches the original representation.
+#
+# 4. Add a byte-order mark to each of the UTF-16 representations and
+# check that the SQLite routines can convert them back to UTF-8. For
+# byte-order mark info, refer to section 3.10 of the unicode standard.
+#
+# 5. Take the byte-order marked UTF-16 strings from step 4 and ensure
+# that SQLite can convert them both to native byte order UTF-16
+# strings, sans BOM.
+#
+# Coverage:
+#
+# sqlite_utf8to16be (step 2)
+# sqlite_utf8to16le (step 1)
+# sqlite_utf16to8 (steps 3, 4)
+# sqlite_utf16to16le (step 5)
+# sqlite_utf16to16be (step 5)
+#
+proc test_conversion {testname str} {
+
+ # Step 1.
+ set utf16le_sqlite3 [test_translate $str UTF8 UTF16LE]
+ set utf16le_tcl [encoding convertto unicode $str]
+ append utf16le_tcl "\x00\x00"
+ if { $::tcl_platform(byteOrder)!="littleEndian" } {
+ set utf16le_tcl [swap_byte_order $utf16le_tcl]
+ }
+ do_bincmp_test $testname.1 $utf16le_sqlite3 $utf16le_tcl
+ set utf16le $utf16le_tcl
+
+ # Step 2.
+ set utf16be_sqlite3 [test_translate $str UTF8 UTF16BE]
+ set utf16be_tcl [encoding convertto unicode $str]
+ append utf16be_tcl "\x00\x00"
+ if { $::tcl_platform(byteOrder)=="littleEndian" } {
+ set utf16be_tcl [swap_byte_order $utf16be_tcl]
+ }
+ do_bincmp_test $testname.2 $utf16be_sqlite3 $utf16be_tcl
+ set utf16be $utf16be_tcl
+
+ # Step 3.
+ if { $::tcl_platform(byteOrder)=="littleEndian" } {
+ set utf16 $utf16le
+ } else {
+ set utf16 $utf16be
+ }
+ set utf8_sqlite3 [test_translate $utf16 UTF16 UTF8]
+ do_bincmp_test $testname.3 $utf8_sqlite3 [binarize $str]
+
+ # Step 4 (little endian).
+ append utf16le_bom "\xFF\xFE" $utf16le
+ set utf8_sqlite3 [test_translate $utf16le_bom UTF16 UTF8 1]
+ do_bincmp_test $testname.4.le $utf8_sqlite3 [binarize $str]
+
+ # Step 4 (big endian).
+ append utf16be_bom "\xFE\xFF" $utf16be
+ set utf8_sqlite3 [test_translate $utf16be_bom UTF16 UTF8]
+ do_bincmp_test $testname.4.be $utf8_sqlite3 [binarize $str]
+
+ # Step 5 (little endian to little endian).
+ set utf16_sqlite3 [test_translate $utf16le_bom UTF16LE UTF16LE]
+ do_bincmp_test $testname.5.le.le $utf16_sqlite3 $utf16le
+
+ # Step 5 (big endian to big endian).
+ set utf16_sqlite3 [test_translate $utf16be_bom UTF16 UTF16BE]
+ do_bincmp_test $testname.5.be.be $utf16_sqlite3 $utf16be
+
+ # Step 5 (big endian to little endian).
+ set utf16_sqlite3 [test_translate $utf16be_bom UTF16 UTF16LE]
+ do_bincmp_test $testname.5.be.le $utf16_sqlite3 $utf16le
+
+ # Step 5 (little endian to big endian).
+ set utf16_sqlite3 [test_translate $utf16le_bom UTF16 UTF16BE]
+ do_bincmp_test $testname.5.le.be $utf16_sqlite3 $utf16be
+}
+
+translate_selftest
+
+test_conversion enc-1 "hello world"
+test_conversion enc-2 "sqlite"
+test_conversion enc-3 ""
+test_conversion enc-X "\u0100"
+test_conversion enc-4 "\u1234"
+test_conversion enc-5 "\u4321abc"
+test_conversion enc-6 "\u4321\u1234"
+test_conversion enc-7 [string repeat "abcde\u00EF\u00EE\uFFFCabc" 100]
+test_conversion enc-8 [string repeat "\u007E\u007F\u0080\u0081" 100]
+test_conversion enc-9 [string repeat "\u07FE\u07FF\u0800\u0801\uFFF0" 100]
+test_conversion enc-10 [string repeat "\uE000" 100]
+
+proc test_collate {enc zLeft zRight} {
+ return [string compare $zLeft $zRight]
+}
+add_test_collate $::DB 0 0 1
+do_test enc-11.1 {
+ execsql {
+ CREATE TABLE ab(a COLLATE test_collate, b);
+ INSERT INTO ab VALUES(CAST (X'C388' AS TEXT), X'888800');
+ INSERT INTO ab VALUES(CAST (X'C0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808388' AS TEXT), X'888800');
+ CREATE INDEX ab_i ON ab(a, b);
+ }
+} {}
+do_test enc-11.2 {
+ set cp200 "\u00C8"
+ execsql {
+ SELECT count(*) FROM ab WHERE a = $::cp200;
+ }
+} {2}
+
+finish_test
diff --git a/third_party/sqlite/test/enc2.test b/third_party/sqlite/test/enc2.test
new file mode 100755
index 0000000..ce25bf3
--- /dev/null
+++ b/third_party/sqlite/test/enc2.test
@@ -0,0 +1,555 @@
+# 2002 May 24
+#
+# 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 file is testing the SQLite routines used for converting between the
+# various suported unicode encodings (UTF-8, UTF-16, UTF-16le and
+# UTF-16be).
+#
+# $Id: enc2.test,v 1.29 2007/10/09 08:29:32 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If UTF16 support is disabled, ignore the tests in this file
+#
+ifcapable {!utf16} {
+ finish_test
+ return
+}
+
+# The rough organisation of tests in this file is:
+#
+# enc2.1.*: Simple tests with a UTF-8 db.
+# enc2.2.*: Simple tests with a UTF-16LE db.
+# enc2.3.*: Simple tests with a UTF-16BE db.
+# enc2.4.*: Test that attached databases must have the same text encoding
+# as the main database.
+# enc2.5.*: Test the behaviour of the library when a collation sequence is
+# not available for the most desirable text encoding.
+# enc2.6.*: Similar test for user functions.
+# enc2.7.*: Test that the VerifyCookie opcode protects against assuming the
+# wrong text encoding for the database.
+# enc2.8.*: Test sqlite3_complete16()
+#
+
+db close
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str.
+proc utf8 {str} {
+ # If $str ends in two 0x00 0x00 bytes, knock these off before
+ # converting to UTF-8 using TCL.
+ binary scan $str \c* vals
+ if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+ set str [binary format \c* [lrange $vals 0 end-2]]
+ }
+
+ set r [encoding convertfrom unicode $str]
+ return $r
+}
+
+#
+# This proc contains all the tests in this file. It is run
+# three times. Each time the file 'test.db' contains a database
+# with the following contents:
+set dbcontents {
+ CREATE TABLE t1(a PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES('one', 'I', 1);
+}
+# This proc tests that we can open and manipulate the test.db
+# database, and that it is possible to retreive values in
+# various text encodings.
+#
+proc run_test_script {t enc} {
+
+# Open the database and pull out a (the) row.
+do_test $t.1 {
+ sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+ execsql {SELECT * FROM t1}
+} {one I 1}
+
+# Insert some data
+do_test $t.2 {
+ execsql {INSERT INTO t1 VALUES('two', 'II', 2);}
+ execsql {SELECT * FROM t1}
+} {one I 1 two II 2}
+
+# Insert some data
+do_test $t.3 {
+ execsql {
+ INSERT INTO t1 VALUES('three','III',3);
+ INSERT INTO t1 VALUES('four','IV',4);
+ INSERT INTO t1 VALUES('five','V',5);
+ }
+ execsql {SELECT * FROM t1}
+} {one I 1 two II 2 three III 3 four IV 4 five V 5}
+
+# Use the index
+do_test $t.4 {
+ execsql {
+ SELECT * FROM t1 WHERE a = 'one';
+ }
+} {one I 1}
+do_test $t.5 {
+ execsql {
+ SELECT * FROM t1 WHERE a = 'four';
+ }
+} {four IV 4}
+ifcapable subquery {
+ do_test $t.6 {
+ execsql {
+ SELECT * FROM t1 WHERE a IN ('one', 'two');
+ }
+ } {one I 1 two II 2}
+}
+
+# Now check that we can retrieve data in both UTF-16 and UTF-8
+do_test $t.7 {
+ set STMT [sqlite3_prepare $DB "SELECT a FROM t1 WHERE c>3;" -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_column_text $STMT 0
+} {four}
+
+do_test $t.8 {
+ sqlite3_step $STMT
+ utf8 [sqlite3_column_text16 $STMT 0]
+} {five}
+
+do_test $t.9 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+
+ifcapable vacuum {
+ execsql VACUUM
+}
+
+do_test $t.10 {
+ db eval {PRAGMA encoding}
+} $enc
+
+}
+
+# The three unicode encodings understood by SQLite.
+set encodings [list UTF-8 UTF-16le UTF-16be]
+
+set sqlite_os_trace 0
+set i 1
+foreach enc $encodings {
+ file delete -force test.db
+ sqlite3 db test.db
+ db eval "PRAGMA encoding = \"$enc\""
+ execsql $dbcontents
+ do_test enc2-$i.0.1 {
+ db eval {PRAGMA encoding}
+ } $enc
+ do_test enc2-$i.0.2 {
+ db eval {PRAGMA encoding=UTF8}
+ db eval {PRAGMA encoding}
+ } $enc
+ do_test enc2-$i.0.3 {
+ db eval {PRAGMA encoding=UTF16le}
+ db eval {PRAGMA encoding}
+ } $enc
+ do_test enc2-$i.0.4 {
+ db eval {PRAGMA encoding=UTF16be}
+ db eval {PRAGMA encoding}
+ } $enc
+
+ db close
+ run_test_script enc2-$i $enc
+ db close
+ incr i
+}
+
+# Test that it is an error to try to attach a database with a different
+# encoding to the main database.
+ifcapable attach {
+ do_test enc2-4.1 {
+ file delete -force test.db
+ sqlite3 db test.db
+ db eval "PRAGMA encoding = 'UTF-8'"
+ db eval "CREATE TABLE abc(a, b, c);"
+ } {}
+ do_test enc2-4.2 {
+ file delete -force test2.db
+ sqlite3 db2 test2.db
+ db2 eval "PRAGMA encoding = 'UTF-16'"
+ db2 eval "CREATE TABLE abc(a, b, c);"
+ } {}
+ do_test enc2-4.3 {
+ catchsql {
+ ATTACH 'test2.db' as aux;
+ }
+ } {1 {attached databases must use the same text encoding as main database}}
+ db2 close
+ db close
+}
+
+# The following tests - enc2-5.* - test that SQLite selects the correct
+# collation sequence when more than one is available.
+
+set ::values [list one two three four five]
+set ::test_collate_enc INVALID
+proc test_collate {enc lhs rhs} {
+ set ::test_collate_enc $enc
+ set l [lsearch -exact $::values $lhs]
+ set r [lsearch -exact $::values $rhs]
+ set res [expr $l - $r]
+ # puts "enc=$enc lhs=$lhs/$l rhs=$rhs/$r res=$res"
+ return $res
+}
+
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-5.0 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('one');
+ INSERT INTO t5 VALUES('two');
+ INSERT INTO t5 VALUES('five');
+ INSERT INTO t5 VALUES('three');
+ INSERT INTO t5 VALUES('four');
+ }
+} {}
+do_test enc2-5.1 {
+ add_test_collate $DB 1 1 1
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate;}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-8}
+do_test enc2-5.2 {
+ add_test_collate $DB 0 1 0
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16LE}
+do_test enc2-5.3 {
+ add_test_collate $DB 0 0 1
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16BE}
+
+db close
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+execsql {pragma encoding = 'UTF-16LE'}
+do_test enc2-5.4 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('one');
+ INSERT INTO t5 VALUES('two');
+ INSERT INTO t5 VALUES('five');
+ INSERT INTO t5 VALUES('three');
+ INSERT INTO t5 VALUES('four');
+ }
+} {}
+do_test enc2-5.5 {
+ add_test_collate $DB 1 1 1
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16LE}
+do_test enc2-5.6 {
+ add_test_collate $DB 1 0 1
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16BE}
+do_test enc2-5.7 {
+ add_test_collate $DB 1 0 0
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-8}
+
+db close
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+execsql {pragma encoding = 'UTF-16BE'}
+do_test enc2-5.8 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('one');
+ INSERT INTO t5 VALUES('two');
+ INSERT INTO t5 VALUES('five');
+ INSERT INTO t5 VALUES('three');
+ INSERT INTO t5 VALUES('four');
+ }
+} {}
+do_test enc2-5.9 {
+ add_test_collate $DB 1 1 1
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16BE}
+do_test enc2-5.10 {
+ add_test_collate $DB 1 1 0
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16LE}
+do_test enc2-5.11 {
+ add_test_collate $DB 1 0 0
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate}]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-8}
+
+# Also test that a UTF-16 collation factory works.
+do_test enc2-5-12 {
+ add_test_collate $DB 0 0 0
+ catchsql {
+ SELECT * FROM t5 ORDER BY 1 COLLATE test_collate
+ }
+} {1 {no such collation sequence: test_collate}}
+do_test enc2-5.13 {
+ add_test_collate_needed $DB
+ set res [execsql {SELECT * FROM t5 ORDER BY 1 COLLATE test_collate; }]
+ lappend res $::test_collate_enc
+} {one two three four five UTF-16BE}
+do_test enc2-5.14 {
+ set ::sqlite_last_needed_collation
+} test_collate
+
+db close
+file delete -force test.db
+
+do_test enc2-5.15 {
+ sqlite3 db test.db; set ::DB [sqlite3_connection_pointer db]
+ add_test_collate_needed $::DB
+ set ::sqlite_last_needed_collation
+} {}
+do_test enc2-5.16 {
+ execsql {CREATE TABLE t1(a varchar collate test_collate);}
+} {}
+do_test enc2-5.17 {
+ set ::sqlite_last_needed_collation
+} {test_collate}
+
+# The following tests - enc2-6.* - test that SQLite selects the correct
+# user function when more than one is available.
+
+proc test_function {enc arg} {
+ return "$enc $arg"
+}
+
+db close
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+execsql {pragma encoding = 'UTF-8'}
+do_test enc2-6.0 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('one');
+ }
+} {}
+do_test enc2-6.1 {
+ add_test_function $DB 1 1 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-8 sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.2 {
+ add_test_function $DB 0 1 0
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16LE sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.3 {
+ add_test_function $DB 0 0 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16BE sqlite}}
+
+db close
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+execsql {pragma encoding = 'UTF-16LE'}
+do_test enc2-6.3 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('sqlite');
+ }
+} {}
+do_test enc2-6.4 {
+ add_test_function $DB 1 1 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16LE sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.5 {
+ add_test_function $DB 0 1 0
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16LE sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.6 {
+ add_test_function $DB 0 0 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16BE sqlite}}
+
+db close
+file delete -force test.db
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+execsql {pragma encoding = 'UTF-16BE'}
+do_test enc2-6.7 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES('sqlite');
+ }
+} {}
+do_test enc2-6.8 {
+ add_test_function $DB 1 1 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16BE sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.9 {
+ add_test_function $DB 0 1 0
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16LE sqlite}}
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test enc2-6.10 {
+ add_test_function $DB 0 0 1
+ execsql {
+ SELECT test_function('sqlite')
+ }
+} {{UTF-16BE sqlite}}
+
+
+db close
+file delete -force test.db
+
+# The following tests - enc2-7.* - function as follows:
+#
+# 1: Open an empty database file assuming UTF-16 encoding.
+# 2: Open the same database with a different handle assuming UTF-8. Create
+# a table using this handle.
+# 3: Read the sqlite_master table from the first handle.
+# 4: Ensure the first handle recognises the database encoding is UTF-8.
+#
+do_test enc2-7.1 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-16';
+ SELECT * FROM sqlite_master;
+ }
+} {}
+do_test enc2-7.2 {
+ set enc [execsql {
+ PRAGMA encoding;
+ }]
+ string range $enc 0 end-2 ;# Chop off the "le" or "be"
+} {UTF-16}
+do_test enc2-7.3 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA encoding = 'UTF-8';
+ CREATE TABLE abc(a, b, c);
+ } db2
+} {}
+do_test enc2-7.4 {
+ execsql {
+ SELECT * FROM sqlite_master;
+ }
+} "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}"
+do_test enc2-7.5 {
+ execsql {
+ PRAGMA encoding;
+ }
+} {UTF-8}
+
+db close
+db2 close
+
+proc utf16 {utf8} {
+ set utf16 [encoding convertto unicode $utf8]
+ append utf16 "\x00\x00"
+ return $utf16
+}
+ifcapable {complete} {
+ do_test enc2-8.1 {
+ sqlite3_complete16 [utf16 "SELECT * FROM t1;"]
+ } {1}
+ do_test enc2-8.2 {
+ sqlite3_complete16 [utf16 "SELECT * FROM"]
+ } {0}
+}
+
+# Test that the encoding of an empty database may still be set after the
+# (empty) schema has been initialized.
+file delete -force test.db
+do_test enc2-9.1 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-8';
+ PRAGMA encoding;
+ }
+} {UTF-8}
+do_test enc2-9.2 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-16le';
+ PRAGMA encoding;
+ }
+} {UTF-16le}
+do_test enc2-9.3 {
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM sqlite_master;
+ PRAGMA encoding = 'UTF-8';
+ PRAGMA encoding;
+ }
+} {UTF-8}
+do_test enc2-9.4 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-16le';
+ CREATE TABLE abc(a, b, c);
+ PRAGMA encoding;
+ }
+} {UTF-16le}
+do_test enc2-9.5 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-8';
+ PRAGMA encoding;
+ }
+} {UTF-16le}
+
+# Ticket #1987.
+# Disallow encoding changes once the encoding has been set.
+#
+do_test enc2-10.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ db eval {
+ PRAGMA encoding=UTF16;
+ CREATE TABLE t1(a);
+ PRAGMA encoding=UTF8;
+ CREATE TABLE t2(b);
+ }
+ db close
+ sqlite3 db test.db
+ db eval {
+ SELECT name FROM sqlite_master
+ }
+} {t1 t2}
+
+finish_test
diff --git a/third_party/sqlite/test/enc3.test b/third_party/sqlite/test/enc3.test
new file mode 100755
index 0000000..44b2179
--- /dev/null
+++ b/third_party/sqlite/test/enc3.test
@@ -0,0 +1,107 @@
+# 2002 May 24
+#
+# 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 file is testing of the proper handling of conversions
+# to the native text representation.
+#
+# $Id: enc3.test,v 1.8 2008/01/22 01:48:09 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {utf16} {
+ do_test enc3-1.1 {
+ execsql {
+ PRAGMA encoding=utf16le;
+ PRAGMA encoding;
+ }
+ } {UTF-16le}
+}
+do_test enc3-1.2 {
+ execsql {
+ CREATE TABLE t1(x,y);
+ INSERT INTO t1 VALUES('abc''123',5);
+ SELECT * FROM t1
+ }
+} {abc'123 5}
+do_test enc3-1.3 {
+ execsql {
+ SELECT quote(x) || ' ' || quote(y) FROM t1
+ }
+} {{'abc''123' 5}}
+ifcapable {bloblit} {
+ do_test enc3-1.4 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(x'616263646566',NULL);
+ SELECT * FROM t1
+ }
+ } {abcdef {}}
+ do_test enc3-1.5 {
+ execsql {
+ SELECT quote(x) || ' ' || quote(y) FROM t1
+ }
+ } {{X'616263646566' NULL}}
+}
+ifcapable {bloblit && utf16} {
+ do_test enc3-2.1 {
+ execsql {
+ PRAGMA encoding
+ }
+ } {UTF-16le}
+ do_test enc3-2.2 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(x'61006200630064006500');
+ SELECT CAST(a AS text) FROM t2 WHERE a LIKE 'abc%';
+ }
+ } {abcde}
+ do_test enc3-2.3 {
+ execsql {
+ SELECT CAST(x'61006200630064006500' AS text);
+ }
+ } {abcde}
+ do_test enc3-2.4 {
+ execsql {
+ SELECT rowid FROM t2 WHERE a LIKE x'610062002500';
+ }
+ } {1}
+}
+
+# Try to attach a database with a different encoding.
+#
+ifcapable {utf16 && shared_cache} {
+ db close
+ file delete -force test8.db test8.db-journal
+ set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+ sqlite3 dbaux test8.db
+ sqlite3 db test.db
+ db eval {SELECT 1 FROM sqlite_master LIMIT 1}
+ do_test enc3-3.1 {
+ dbaux eval {
+ PRAGMA encoding='utf8';
+ CREATE TABLE t1(x);
+ PRAGMA encoding
+ }
+ } {UTF-8}
+ do_test enc3-3.2 {
+ catchsql {
+ ATTACH 'test.db' AS utf16;
+ SELECT 1 FROM utf16.sqlite_master LIMIT 1;
+ } dbaux
+ } {1 {attached databases must use the same text encoding as main database}}
+ dbaux close
+ file delete -force test8.db test8.db-journal
+ sqlite3_enable_shared_cache $::enable_shared_cache
+}
+
+finish_test
diff --git a/third_party/sqlite/test/eval.test b/third_party/sqlite/test/eval.test
new file mode 100755
index 0000000..0f51b22
--- /dev/null
+++ b/third_party/sqlite/test/eval.test
@@ -0,0 +1,71 @@
+# 2008 July 11
+#
+# 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.
+#
+# This file experiments with recursion using the "test_eval()" SQL function
+# in order to make sure that SQLite is reentrant.
+#
+# $Id: eval.test,v 1.1 2008/07/11 21:02:54 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table to work with.
+#
+do_test eval-1.1 {
+ execsql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 SELECT x+2 FROM t1;
+ INSERT INTO t1 SELECT x+4 FROM t1;
+ INSERT INTO t1 SELECT x+8 FROM t1;
+ INSERT INTO t1 SELECT x+16 FROM t1;
+ INSERT INTO t1 SELECT x+32 FROM t1;
+ INSERT INTO t1 SELECT x+64 FROM t1;
+ INSERT INTO t1 SELECT x+128 FROM t1;
+ INSERT INTO t1 SELECT x+256 FROM t1;
+ SELECT count(*), max(x) FROM t1;
+ }
+} {512 512}
+do_test eval-1.2 {
+ execsql {
+ SELECT x, test_eval('SELECT max(x) FROM t1 WHERE x<' || x) FROM t1 LIMIT 5
+ }
+} {1 {} 2 1 3 2 4 3 5 4}
+
+# Delete a row out from under a read cursor in the middle of
+# collecting the arguments for a single row in a result set.
+# Verify that subsequent rows come out as NULL.
+#
+do_test eval-2.1 {
+ execsql {
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 SELECT x, x+1 FROM t1 WHERE x<5;
+ SELECT x, test_eval('DELETE FROM t2 WHERE x='||x), y FROM t2;
+ }
+} {1 {} {} 2 {} {} 3 {} {} 4 {} {}}
+do_test eval-2.2 {
+ execsql {
+ SELECT * FROM t2
+ }
+} {}
+
+# Modify a row while it is being read.
+#
+do_test eval-3.1 {
+ execsql {
+ INSERT INTO t2 SELECT x, x+1 FROM t1 WHERE x<5;
+ SELECT x, test_eval('UPDATE t2 SET y=y+100 WHERE x='||x), y FROM t2;
+ }
+} {1 {} 102 2 {} 103 3 {} 104 4 {} 105}
+
+finish_test
diff --git a/third_party/sqlite/test/exclusive.test b/third_party/sqlite/test/exclusive.test
new file mode 100755
index 0000000..599a299
--- /dev/null
+++ b/third_party/sqlite/test/exclusive.test
@@ -0,0 +1,453 @@
+# 2007 March 24
+#
+# 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 these tests is exclusive access mode (i.e. the thing activated by
+# "PRAGMA locking_mode = EXCLUSIVE").
+#
+# $Id: exclusive.test,v 1.8 2008/04/17 14:16:42 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+ finish_test
+ return
+}
+
+file delete -force test2.db-journal
+file delete -force test2.db
+file delete -force test3.db-journal
+file delete -force test3.db
+file delete -force test4.db-journal
+file delete -force test4.db
+
+# The locking mode for the TEMP table is always "exclusive" for
+# on-disk tables and "normal" for in-memory tables.
+#
+if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
+ set temp_mode normal
+} else {
+ set temp_mode exclusive
+}
+
+#----------------------------------------------------------------------
+# Test cases exclusive-1.X test the PRAGMA logic.
+#
+do_test exclusive-1.0 {
+ execsql {
+ pragma locking_mode;
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ }
+} [list normal normal $temp_mode]
+do_test exclusive-1.1 {
+ execsql {
+ pragma locking_mode = exclusive;
+ }
+} {exclusive}
+do_test exclusive-1.2 {
+ execsql {
+ pragma locking_mode;
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ }
+} [list exclusive exclusive $temp_mode]
+do_test exclusive-1.3 {
+ execsql {
+ pragma locking_mode = normal;
+ }
+} {normal}
+do_test exclusive-1.4 {
+ execsql {
+ pragma locking_mode;
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ }
+} [list normal normal $temp_mode]
+do_test exclusive-1.5 {
+ execsql {
+ pragma locking_mode = invalid;
+ }
+} {normal}
+do_test exclusive-1.6 {
+ execsql {
+ pragma locking_mode;
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ }
+} [list normal normal $temp_mode]
+ifcapable attach {
+ do_test exclusive-1.7 {
+ execsql {
+ pragma locking_mode = exclusive;
+ ATTACH 'test2.db' as aux;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma aux.locking_mode;
+ }
+ } {exclusive exclusive}
+ do_test exclusive-1.8 {
+ execsql {
+ pragma main.locking_mode = normal;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ pragma aux.locking_mode;
+ }
+ } [list normal $temp_mode exclusive]
+ do_test exclusive-1.9 {
+ execsql {
+ pragma locking_mode;
+ }
+ } {exclusive}
+ do_test exclusive-1.10 {
+ execsql {
+ ATTACH 'test3.db' as aux2;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma aux.locking_mode;
+ pragma aux2.locking_mode;
+ }
+ } {normal exclusive exclusive}
+ do_test exclusive-1.11 {
+ execsql {
+ pragma aux.locking_mode = normal;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma aux.locking_mode;
+ pragma aux2.locking_mode;
+ }
+ } {normal normal exclusive}
+ do_test exclusive-1.12 {
+ execsql {
+ pragma locking_mode = normal;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ pragma aux.locking_mode;
+ pragma aux2.locking_mode;
+ }
+ } [list normal $temp_mode normal normal]
+ do_test exclusive-1.13 {
+ execsql {
+ ATTACH 'test4.db' as aux3;
+ }
+ execsql {
+ pragma main.locking_mode;
+ pragma temp.locking_mode;
+ pragma aux.locking_mode;
+ pragma aux2.locking_mode;
+ pragma aux3.locking_mode;
+ }
+ } [list normal $temp_mode normal normal normal]
+
+ do_test exclusive-1.99 {
+ execsql {
+ DETACH aux;
+ DETACH aux2;
+ DETACH aux3;
+ }
+ } {}
+}
+
+#----------------------------------------------------------------------
+# Test cases exclusive-2.X verify that connections in exclusive
+# locking_mode do not relinquish locks.
+#
+do_test exclusive-2.0 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ PRAGMA locking_mode = exclusive;
+ }
+} {exclusive}
+do_test exclusive-2.1 {
+ sqlite3 db2 test.db
+ execsql {
+ INSERT INTO abc VALUES(4, 5, 6);
+ SELECT * FROM abc;
+ } db2
+} {1 2 3 4 5 6}
+do_test exclusive-2.2 {
+ # This causes connection 'db' (in exclusive mode) to establish
+ # a shared-lock on the db. The other connection should now be
+ # locked out as a writer.
+ execsql {
+ SELECT * FROM abc;
+ } db
+} {1 2 3 4 5 6}
+do_test exclusive-2.4 {
+ execsql {
+ SELECT * FROM abc;
+ } db2
+} {1 2 3 4 5 6}
+do_test exclusive-2.5 {
+ catchsql {
+ INSERT INTO abc VALUES(7, 8, 9);
+ } db2
+} {1 {database is locked}}
+sqlite3_soft_heap_limit 0
+do_test exclusive-2.6 {
+ # Because connection 'db' only has a shared-lock, the other connection
+ # will be able to get a RESERVED, but will fail to upgrade to EXCLUSIVE.
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(7, 8, 9);
+ } db2
+ catchsql {
+ COMMIT
+ } db2
+} {1 {database is locked}}
+do_test exclusive-2.7 {
+ catchsql {
+ COMMIT
+ } db2
+} {1 {database is locked}}
+do_test exclusive-2.8 {
+ execsql {
+ ROLLBACK;
+ } db2
+} {}
+sqlite3_soft_heap_limit $soft_limit
+
+do_test exclusive-2.9 {
+ # Write the database to establish the exclusive lock with connection 'db.
+ execsql {
+ INSERT INTO abc VALUES(7, 8, 9);
+ } db
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {1 {database is locked}}
+do_test exclusive-2.10 {
+ # Changing the locking-mode does not release any locks.
+ execsql {
+ PRAGMA locking_mode = normal;
+ } db
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {1 {database is locked}}
+do_test exclusive-2.11 {
+ # After changing the locking mode, accessing the db releases locks.
+ execsql {
+ SELECT * FROM abc;
+ } db
+ execsql {
+ SELECT * FROM abc;
+ } db2
+} {1 2 3 4 5 6 7 8 9}
+db2 close
+
+#----------------------------------------------------------------------
+# Tests exclusive-3.X - test that a connection in exclusive mode
+# truncates instead of deletes the journal file when committing
+# a transaction.
+#
+proc filestate {fname} {
+ set exists 0
+ set content 0
+ if {[file exists $fname]} {
+ set exists 1
+ set hdr [hexio_read $fname 0 28]
+ set content \
+ [expr {$hdr!="00000000000000000000000000000000000000000000000000000000"}]
+ }
+ list $exists $content
+}
+do_test exclusive-3.0 {
+ filestate test.db-journal
+} {0 0}
+do_test exclusive-3.1 {
+ execsql {
+ PRAGMA locking_mode = exclusive;
+ BEGIN;
+ DELETE FROM abc;
+ }
+ filestate test.db-journal
+} {1 1}
+do_test exclusive-3.2 {
+ execsql {
+ COMMIT;
+ }
+ filestate test.db-journal
+} {1 0}
+do_test exclusive-3.3 {
+ execsql {
+ INSERT INTO abc VALUES('A', 'B', 'C');
+ SELECT * FROM abc;
+ }
+} {A B C}
+do_test exclusive-3.4 {
+ execsql {
+ BEGIN;
+ UPDATE abc SET a = 1, b = 2, c = 3;
+ ROLLBACK;
+ SELECT * FROM abc;
+ }
+} {A B C}
+do_test exclusive-3.5 {
+ filestate test.db-journal
+} {1 0}
+do_test exclusive-3.6 {
+ execsql {
+ PRAGMA locking_mode = normal;
+ SELECT * FROM abc;
+ }
+ filestate test.db-journal
+} {0 0}
+
+#----------------------------------------------------------------------
+# Tests exclusive-4.X - test that rollback works correctly when
+# in exclusive-access mode.
+#
+
+# The following procedure computes a "signature" for table "t3". If
+# T3 changes in any way, the signature should change.
+#
+# This is used to test ROLLBACK. We gather a signature for t3, then
+# make lots of changes to t3, then rollback and take another signature.
+# The two signatures should be the same.
+#
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(x) FROM t3}]
+}
+
+do_test exclusive-4.0 {
+ execsql { PRAGMA locking_mode = exclusive; }
+ execsql { PRAGMA default_cache_size = 10; }
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x TEXT);
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ COMMIT;
+ }
+ execsql {SELECT count(*) FROM t3;}
+} {32}
+
+set ::X [signature]
+do_test exclusive-4.1 {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ SELECT count(*) FROM t3;
+ ROLLBACK;
+ }
+ signature
+} $::X
+
+do_test exclusive-4.2 {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+} $::X
+
+do_test exclusive-4.3 {
+ execsql {
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0;
+ }
+} {}
+
+do_test exclusive-4.4 {
+ catch {set ::X [signature]}
+} {0}
+do_test exclusive-4.5 {
+ execsql {
+ PRAGMA locking_mode = NORMAL;
+ DROP TABLE t3;
+ DROP TABLE abc;
+ }
+} {normal}
+
+#----------------------------------------------------------------------
+# Tests exclusive-5.X - test that statement journals are truncated
+# instead of deleted when in exclusive access mode.
+#
+
+# Close and reopen the database so that the temp database is no
+# longer active.
+#
+db close
+sqlite db test.db
+
+
+do_test exclusive-5.0 {
+ execsql {
+ CREATE TABLE abc(a UNIQUE, b UNIQUE, c UNIQUE);
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc SELECT a+1, b+1, c+1 FROM abc;
+ }
+} {}
+do_test exclusive-5.1 {
+ # Three files are open: The db, journal and statement-journal.
+ set sqlite_open_file_count
+} {3}
+do_test exclusive-5.2 {
+ execsql {
+ COMMIT;
+ }
+ # One file open: the db.
+ set sqlite_open_file_count
+} {1}
+do_test exclusive-5.3 {
+ execsql {
+ PRAGMA locking_mode = exclusive;
+ BEGIN;
+ INSERT INTO abc VALUES(5, 6, 7);
+ }
+ # Two files open: the db and journal.
+ set sqlite_open_file_count
+} {2}
+do_test exclusive-5.4 {
+ execsql {
+ INSERT INTO abc SELECT a+10, b+10, c+10 FROM abc;
+ }
+ # Three files are open: The db, journal and statement-journal.
+ set sqlite_open_file_count
+} {3}
+do_test exclusive-5.5 {
+ execsql {
+ COMMIT;
+ }
+ # Three files are still open: The db, journal and statement-journal.
+ set sqlite_open_file_count
+} {3}
+do_test exclusive-5.6 {
+ execsql {
+ PRAGMA locking_mode = normal;
+ SELECT * FROM abc;
+ }
+} {normal 1 2 3 2 3 4 5 6 7 11 12 13 12 13 14 15 16 17}
+do_test exclusive-5.7 {
+ # Just the db open.
+ set sqlite_open_file_count
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/exclusive2.test b/third_party/sqlite/test/exclusive2.test
new file mode 100755
index 0000000..d0eeb19
--- /dev/null
+++ b/third_party/sqlite/test/exclusive2.test
@@ -0,0 +1,297 @@
+# 2007 March 24
+#
+# 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.
+#
+# $Id: exclusive2.test,v 1.8 2007/08/12 20:07:59 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+ finish_test
+ return
+}
+
+# This module does not work right if the cache spills at unexpected
+# moments. So disable the soft-heap-limit.
+#
+sqlite3_soft_heap_limit 0
+
+proc pagerChangeCounter {filename {new ""}} {
+ set fd [open $filename RDWR]
+ fconfigure $fd -translation binary -encoding binary
+ if {$new ne ""} {
+ seek $fd 24
+ set a [expr {($new&0xFF000000)>>24}]
+ set b [expr {($new&0x00FF0000)>>16}]
+ set c [expr {($new&0x0000FF00)>>8}]
+ set d [expr {($new&0x000000FF)}]
+ puts -nonewline $fd [binary format cccc $a $b $c $d]
+ flush $fd
+ }
+
+ seek $fd 24
+ foreach {a b c d} [list 0 0 0 0] {}
+ binary scan [read $fd 4] cccc a b c d
+ set ret [expr ($a&0x000000FF)<<24]
+ incr ret [expr ($b&0x000000FF)<<16]
+ incr ret [expr ($c&0x000000FF)<<8]
+ incr ret [expr ($d&0x000000FF)<<0]
+
+ close $fd
+ return $ret
+}
+
+proc readPagerChangeCounter {filename} {
+ set fd [open $filename RDONLY]
+ fconfigure $fd -translation binary -encoding binary
+
+ seek $fd 24
+ foreach {a b c d} [list 0 0 0 0] {}
+ binary scan [read $fd 4] cccc a b c d
+ set ret [expr ($a&0x000000FF)<<24]
+ incr ret [expr ($b&0x000000FF)<<16]
+ incr ret [expr ($c&0x000000FF)<<8]
+ incr ret [expr ($d&0x000000FF)<<0]
+
+ close $fd
+ return $ret
+}
+
+
+proc t1sig {{db db}} {
+ execsql {SELECT count(*), md5sum(a) FROM t1} $db
+}
+do_test exclusive2-1.0 {
+ readPagerChangeCounter test.db
+} {0}
+
+#-----------------------------------------------------------------------
+# The following tests - exclusive2-1.X - check that:
+#
+# 1-3: Build a database with connection 1, calculate a signature.
+# 4-9: Modify the database using a second connection in a way that
+# does not modify the freelist, then reset the pager change-counter
+# to the value it had before the modifications.
+# 8: Check that using the first connection, the database signature
+# is still the same. This is because it uses the in-memory cache.
+# It can't tell the db has changed because we reset the change-counter.
+# 9: Increment the change-counter.
+# 10: Ensure that the first connection now sees the updated database. It
+# sees the change-counter has been incremented and discards the
+# invalid in-memory cache.
+#
+# This will only work if the database cache is large enough to hold
+# the entire database. In the case of 1024 byte pages, this means
+# the cache size must be at least 17. Otherwise, some pages will be
+# loaded from the database file in step 8.
+#
+do_test exclusive2-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1(a) VALUES(randstr(10, 400));
+ INSERT INTO t1(a) VALUES(randstr(10, 400));
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+} {64}
+do_test exclusive2-1.2.1 {
+ # Make sure the pager cache is large enough to store the
+ # entire database.
+ set nPage [expr [file size test.db]/1024]
+ if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
+ execsql "PRAGMA cache_size = $nPage"
+ }
+ expr {[execsql {PRAGMA cache_size}] >= $nPage}
+} {1}
+do_test exclusive2-1.2 {
+ set ::sig [t1sig]
+ readPagerChangeCounter test.db
+} {1}
+do_test exclusive2-1.3 {
+ t1sig
+} $::sig
+do_test exclusive2-1.4 {
+ sqlite3 db2 test.db
+ t1sig db2
+} $::sig
+do_test exclusive2-1.5 {
+ execsql {
+ UPDATE t1 SET b=a, a=NULL;
+ } db2
+ expr {[t1sig db2] eq $::sig}
+} 0
+do_test exclusive2-1.6 {
+ readPagerChangeCounter test.db
+} {2}
+do_test exclusive2-1.7 {
+ pagerChangeCounter test.db 1
+} {1}
+do_test exclusive2-1.9 {
+ t1sig
+ expr {[t1sig] eq $::sig}
+} {1}
+do_test exclusive2-1.10 {
+ pagerChangeCounter test.db 2
+} {2}
+do_test exclusive2-1.11 {
+ expr {[t1sig] eq $::sig}
+} {0}
+
+#--------------------------------------------------------------------
+# These tests - exclusive2-2.X - are similar to exclusive2-1.X,
+# except that they are run with locking_mode=EXCLUSIVE.
+#
+# 1-3: Build a database with exclusive-access connection 1,
+# calculate a signature.
+# 4: Corrupt the database by writing 10000 bytes of garbage
+# starting at the beginning of page 2. Check that connection 1
+# still works. It should be accessing the in-memory cache.
+# 5-6: Modify the dataase change-counter. Connection 1 still works
+# entirely from in-memory cache, because it doesn't check the
+# change-counter.
+# 7-8 Set the locking-mode back to normal. After the db is unlocked,
+# SQLite detects the modified change-counter and discards the
+# in-memory cache. Then it finds the corruption caused in step 4....
+#
+# As above, this test is only applicable if the pager cache is
+# large enough to hold the entire database. With 1024 byte pages,
+# this means 19 pages. We also need to disable the soft-heap-limit
+# to prevent memory-induced cache spills.
+#
+do_test exclusive2-2.1 {
+ execsql {PRAGMA locking_mode = exclusive;}
+ execsql {
+ BEGIN;
+ DELETE FROM t1;
+ INSERT INTO t1(a) VALUES(randstr(10, 400));
+ INSERT INTO t1(a) VALUES(randstr(10, 400));
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+} {64}
+do_test exclusive2-2.2.1 {
+ # Make sure the pager cache is large enough to store the
+ # entire database.
+ set nPage [expr [file size test.db]/1024]
+ if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
+ execsql "PRAGMA cache_size = $nPage"
+ }
+ expr {[execsql {PRAGMA cache_size}] >= $nPage}
+} {1}
+do_test exclusive2-2.2 {
+ set ::sig [t1sig]
+ readPagerChangeCounter test.db
+} {3}
+do_test exclusive2-2.3 {
+ t1sig
+} $::sig
+
+do_test exclusive2-2.4 {
+ set fd [open test.db RDWR]
+ seek $fd 1024
+ puts -nonewline $fd [string repeat [binary format c 0] 10000]
+ flush $fd
+ close $fd
+ t1sig
+} $::sig
+
+do_test exclusive2-2.5 {
+ pagerChangeCounter test.db 5
+} {5}
+do_test exclusive2-2.6 {
+ t1sig
+} $::sig
+do_test exclusive2-2.7 {
+ execsql {PRAGMA locking_mode = normal}
+ t1sig
+} $::sig
+
+do_test exclusive2-2.8 {
+ set rc [catch {t1sig} msg]
+ list $rc $msg
+} {1 {database disk image is malformed}}
+
+#--------------------------------------------------------------------
+# These tests - exclusive2-3.X - verify that the pager change-counter
+# is only incremented by the first change when in exclusive access
+# mode. In normal mode, the change-counter is incremented once
+# per write-transaction.
+#
+
+db close
+db2 close
+file delete -force test.db
+file delete -force test.db-journal
+
+do_test exclusive2-3.0 {
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a UNIQUE);
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ COMMIT;
+ }
+ readPagerChangeCounter test.db
+} {1}
+do_test exclusive2-3.1 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {2}
+do_test exclusive2-3.2 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {3}
+do_test exclusive2-3.3 {
+ execsql {
+ PRAGMA locking_mode = exclusive;
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {4}
+do_test exclusive2-3.4 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {4}
+do_test exclusive2-3.5 {
+ execsql {
+ PRAGMA locking_mode = normal;
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {4}
+do_test exclusive2-3.6 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10, 400));
+ }
+ readPagerChangeCounter test.db
+} {5}
+sqlite3_soft_heap_limit $soft_limit
+
+finish_test
diff --git a/third_party/sqlite/test/exec.test b/third_party/sqlite/test/exec.test
new file mode 100755
index 0000000..dbcc626
--- /dev/null
+++ b/third_party/sqlite/test/exec.test
@@ -0,0 +1,37 @@
+# 2008 Jan 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests for the sqlite3_exec interface
+#
+# $Id: exec.test,v 1.1 2008/01/21 16:22:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test exec-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t1;
+ }
+} {1 2}
+do_test exec-1.2 {
+ sqlite3_exec db {/* comment */;;; SELECT * FROM t1; /* comment */}
+} {0 {a b 1 2}}
+do_test exec-1.3 {
+ sqlite3 db2 test.db
+ db2 eval {CREATE TABLE t2(x, y);}
+ db2 close
+ sqlite3_exec db {SELECT * FROM t1}
+} {0 {a b 1 2}}
+
+finish_test
diff --git a/third_party/sqlite/test/expr.test b/third_party/sqlite/test/expr.test
new file mode 100755
index 0000000..205af0d
--- /dev/null
+++ b/third_party/sqlite/test/expr.test
@@ -0,0 +1,747 @@
+# 2001 September 15
+#
+# 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 file is testing expressions.
+#
+# $Id: expr.test,v 1.64 2008/07/30 13:27:11 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table to work with.
+#
+execsql {CREATE TABLE test1(i1 int, i2 int, r1 real, r2 real, t1 text, t2 text)}
+execsql {INSERT INTO test1 VALUES(1,2,1.1,2.2,'hello','world')}
+proc test_expr {name settings expr result} {
+ do_test $name [format {
+ execsql {BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK;}
+ } $settings $expr] $result
+}
+
+test_expr expr-1.1 {i1=10, i2=20} {i1+i2} 30
+test_expr expr-1.2 {i1=10, i2=20} {i1-i2} -10
+test_expr expr-1.3 {i1=10, i2=20} {i1*i2} 200
+test_expr expr-1.4 {i1=10, i2=20} {i1/i2} 0
+test_expr expr-1.5 {i1=10, i2=20} {i2/i1} 2
+test_expr expr-1.6 {i1=10, i2=20} {i2<i1} 0
+test_expr expr-1.7 {i1=10, i2=20} {i2<=i1} 0
+test_expr expr-1.8 {i1=10, i2=20} {i2>i1} 1
+test_expr expr-1.9 {i1=10, i2=20} {i2>=i1} 1
+test_expr expr-1.10 {i1=10, i2=20} {i2!=i1} 1
+test_expr expr-1.11 {i1=10, i2=20} {i2=i1} 0
+test_expr expr-1.12 {i1=10, i2=20} {i2<>i1} 1
+test_expr expr-1.13 {i1=10, i2=20} {i2==i1} 0
+test_expr expr-1.14 {i1=20, i2=20} {i2<i1} 0
+test_expr expr-1.15 {i1=20, i2=20} {i2<=i1} 1
+test_expr expr-1.16 {i1=20, i2=20} {i2>i1} 0
+test_expr expr-1.17 {i1=20, i2=20} {i2>=i1} 1
+test_expr expr-1.18 {i1=20, i2=20} {i2!=i1} 0
+test_expr expr-1.19 {i1=20, i2=20} {i2=i1} 1
+test_expr expr-1.20 {i1=20, i2=20} {i2<>i1} 0
+test_expr expr-1.21 {i1=20, i2=20} {i2==i1} 1
+test_expr expr-1.22 {i1=1, i2=2, r1=3.0} {i1+i2*r1} {7.0}
+test_expr expr-1.23 {i1=1, i2=2, r1=3.0} {(i1+i2)*r1} {9.0}
+test_expr expr-1.24 {i1=1, i2=2} {min(i1,i2,i1+i2,i1-i2)} {-1}
+test_expr expr-1.25 {i1=1, i2=2} {max(i1,i2,i1+i2,i1-i2)} {3}
+test_expr expr-1.26 {i1=1, i2=2} {max(i1,i2,i1+i2,i1-i2)} {3}
+test_expr expr-1.27 {i1=1, i2=2} {i1==1 AND i2=2} {1}
+test_expr expr-1.28 {i1=1, i2=2} {i1=2 AND i2=1} {0}
+test_expr expr-1.29 {i1=1, i2=2} {i1=1 AND i2=1} {0}
+test_expr expr-1.30 {i1=1, i2=2} {i1=2 AND i2=2} {0}
+test_expr expr-1.31 {i1=1, i2=2} {i1==1 OR i2=2} {1}
+test_expr expr-1.32 {i1=1, i2=2} {i1=2 OR i2=1} {0}
+test_expr expr-1.33 {i1=1, i2=2} {i1=1 OR i2=1} {1}
+test_expr expr-1.34 {i1=1, i2=2} {i1=2 OR i2=2} {1}
+test_expr expr-1.35 {i1=1, i2=2} {i1-i2=-1} {1}
+test_expr expr-1.36 {i1=1, i2=0} {not i1} {0}
+test_expr expr-1.37 {i1=1, i2=0} {not i2} {1}
+test_expr expr-1.38 {i1=1} {-i1} {-1}
+test_expr expr-1.39 {i1=1} {+i1} {1}
+test_expr expr-1.40 {i1=1, i2=2} {+(i2+i1)} {3}
+test_expr expr-1.41 {i1=1, i2=2} {-(i2+i1)} {-3}
+test_expr expr-1.42 {i1=1, i2=2} {i1|i2} {3}
+test_expr expr-1.42b {i1=1, i2=2} {4|2} {6}
+test_expr expr-1.43 {i1=1, i2=2} {i1&i2} {0}
+test_expr expr-1.43b {i1=1, i2=2} {4&5} {4}
+test_expr expr-1.44 {i1=1} {~i1} {-2}
+test_expr expr-1.44b {i1=NULL} {~i1} {{}}
+test_expr expr-1.45 {i1=1, i2=3} {i1<<i2} {8}
+test_expr expr-1.46 {i1=32, i2=3} {i1>>i2} {4}
+test_expr expr-1.47 {i1=9999999999, i2=8888888888} {i1<i2} 0
+test_expr expr-1.48 {i1=9999999999, i2=8888888888} {i1=i2} 0
+test_expr expr-1.49 {i1=9999999999, i2=8888888888} {i1>i2} 1
+test_expr expr-1.50 {i1=99999999999, i2=99999999998} {i1<i2} 0
+test_expr expr-1.51 {i1=99999999999, i2=99999999998} {i1=i2} 0
+test_expr expr-1.52 {i1=99999999999, i2=99999999998} {i1>i2} 1
+test_expr expr-1.53 {i1=099999999999, i2=99999999999} {i1<i2} 0
+test_expr expr-1.54 {i1=099999999999, i2=99999999999} {i1=i2} 1
+test_expr expr-1.55 {i1=099999999999, i2=99999999999} {i1>i2} 0
+test_expr expr-1.56 {i1=25, i2=11} {i1%i2} 3
+test_expr expr-1.58 {i1=NULL, i2=1} {coalesce(i1+i2,99)} 99
+test_expr expr-1.59 {i1=1, i2=NULL} {coalesce(i1+i2,99)} 99
+test_expr expr-1.60 {i1=NULL, i2=NULL} {coalesce(i1+i2,99)} 99
+test_expr expr-1.61 {i1=NULL, i2=1} {coalesce(i1-i2,99)} 99
+test_expr expr-1.62 {i1=1, i2=NULL} {coalesce(i1-i2,99)} 99
+test_expr expr-1.63 {i1=NULL, i2=NULL} {coalesce(i1-i2,99)} 99
+test_expr expr-1.64 {i1=NULL, i2=1} {coalesce(i1*i2,99)} 99
+test_expr expr-1.65 {i1=1, i2=NULL} {coalesce(i1*i2,99)} 99
+test_expr expr-1.66 {i1=NULL, i2=NULL} {coalesce(i1*i2,99)} 99
+test_expr expr-1.67 {i1=NULL, i2=1} {coalesce(i1/i2,99)} 99
+test_expr expr-1.68 {i1=1, i2=NULL} {coalesce(i1/i2,99)} 99
+test_expr expr-1.69 {i1=NULL, i2=NULL} {coalesce(i1/i2,99)} 99
+test_expr expr-1.70 {i1=NULL, i2=1} {coalesce(i1<i2,99)} 99
+test_expr expr-1.71 {i1=1, i2=NULL} {coalesce(i1>i2,99)} 99
+test_expr expr-1.72 {i1=NULL, i2=NULL} {coalesce(i1<=i2,99)} 99
+test_expr expr-1.73 {i1=NULL, i2=1} {coalesce(i1>=i2,99)} 99
+test_expr expr-1.74 {i1=1, i2=NULL} {coalesce(i1!=i2,99)} 99
+test_expr expr-1.75 {i1=NULL, i2=NULL} {coalesce(i1==i2,99)} 99
+test_expr expr-1.76 {i1=NULL, i2=NULL} {coalesce(not i1,99)} 99
+test_expr expr-1.77 {i1=NULL, i2=NULL} {coalesce(-i1,99)} 99
+test_expr expr-1.78 {i1=NULL, i2=NULL} {coalesce(i1 IS NULL AND i2=5,99)} 99
+test_expr expr-1.79 {i1=NULL, i2=NULL} {coalesce(i1 IS NULL OR i2=5,99)} 1
+test_expr expr-1.80 {i1=NULL, i2=NULL} {coalesce(i1=5 AND i2 IS NULL,99)} 99
+test_expr expr-1.81 {i1=NULL, i2=NULL} {coalesce(i1=5 OR i2 IS NULL,99)} 1
+test_expr expr-1.82 {i1=NULL, i2=3} {coalesce(min(i1,i2,1),99)} 99
+test_expr expr-1.83 {i1=NULL, i2=3} {coalesce(max(i1,i2,1),99)} 99
+test_expr expr-1.84 {i1=3, i2=NULL} {coalesce(min(i1,i2,1),99)} 99
+test_expr expr-1.85 {i1=3, i2=NULL} {coalesce(max(i1,i2,1),99)} 99
+test_expr expr-1.86 {i1=3, i2=8} {5 between i1 and i2} 1
+test_expr expr-1.87 {i1=3, i2=8} {5 not between i1 and i2} 0
+test_expr expr-1.88 {i1=3, i2=8} {55 between i1 and i2} 0
+test_expr expr-1.89 {i1=3, i2=8} {55 not between i1 and i2} 1
+test_expr expr-1.90 {i1=3, i2=NULL} {5 between i1 and i2} {{}}
+test_expr expr-1.91 {i1=3, i2=NULL} {5 not between i1 and i2} {{}}
+test_expr expr-1.92 {i1=3, i2=NULL} {2 between i1 and i2} 0
+test_expr expr-1.93 {i1=3, i2=NULL} {2 not between i1 and i2} 1
+test_expr expr-1.94 {i1=NULL, i2=8} {2 between i1 and i2} {{}}
+test_expr expr-1.95 {i1=NULL, i2=8} {2 not between i1 and i2} {{}}
+test_expr expr-1.94 {i1=NULL, i2=8} {55 between i1 and i2} 0
+test_expr expr-1.95 {i1=NULL, i2=8} {55 not between i1 and i2} 1
+test_expr expr-1.96 {i1=NULL, i2=3} {coalesce(i1<<i2,99)} 99
+test_expr expr-1.97 {i1=32, i2=NULL} {coalesce(i1>>i2,99)} 99
+test_expr expr-1.98 {i1=NULL, i2=NULL} {coalesce(i1|i2,99)} 99
+test_expr expr-1.99 {i1=32, i2=NULL} {coalesce(i1&i2,99)} 99
+test_expr expr-1.100 {i1=1, i2=''} {i1=i2} 0
+test_expr expr-1.101 {i1=0, i2=''} {i1=i2} 0
+
+# Check for proper handling of 64-bit integer values.
+#
+test_expr expr-1.102 {i1=40, i2=1} {i2<<i1} 1099511627776
+
+test_expr expr-1.103 {i1=0} {(-2147483648.0 % -1)} 0.0
+test_expr expr-1.104 {i1=0} {(-9223372036854775808.0 % -1)} 0.0
+test_expr expr-1.105 {i1=0} {(-9223372036854775808.0 / -1)>1} 1
+
+test_expr expr-1.106 {i1=0} {(1<<63)/-1} -9223372036854775808
+test_expr expr-1.107 {i1=0} {(1<<63)%-1} 0
+test_expr expr-1.108 {i1=0} {1%0} {{}}
+test_expr expr-1.109 {i1=0} {1/0} {{}}
+test_expr expr-1.110 {i1=0} {-9223372036854775807/-1} 9223372036854775807
+
+test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57
+test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11
+test_expr expr-2.3 {r1=1.23, r2=2.34} {r1*r2} 2.8782
+set tcl_precision 15
+test_expr expr-2.4 {r1=1.23, r2=2.34} {r1/r2} 0.525641025641026
+test_expr expr-2.5 {r1=1.23, r2=2.34} {r2/r1} 1.90243902439024
+test_expr expr-2.6 {r1=1.23, r2=2.34} {r2<r1} 0
+test_expr expr-2.7 {r1=1.23, r2=2.34} {r2<=r1} 0
+test_expr expr-2.8 {r1=1.23, r2=2.34} {r2>r1} 1
+test_expr expr-2.9 {r1=1.23, r2=2.34} {r2>=r1} 1
+test_expr expr-2.10 {r1=1.23, r2=2.34} {r2!=r1} 1
+test_expr expr-2.11 {r1=1.23, r2=2.34} {r2=r1} 0
+test_expr expr-2.12 {r1=1.23, r2=2.34} {r2<>r1} 1
+test_expr expr-2.13 {r1=1.23, r2=2.34} {r2==r1} 0
+test_expr expr-2.14 {r1=2.34, r2=2.34} {r2<r1} 0
+test_expr expr-2.15 {r1=2.34, r2=2.34} {r2<=r1} 1
+test_expr expr-2.16 {r1=2.34, r2=2.34} {r2>r1} 0
+test_expr expr-2.17 {r1=2.34, r2=2.34} {r2>=r1} 1
+test_expr expr-2.18 {r1=2.34, r2=2.34} {r2!=r1} 0
+test_expr expr-2.19 {r1=2.34, r2=2.34} {r2=r1} 1
+test_expr expr-2.20 {r1=2.34, r2=2.34} {r2<>r1} 0
+test_expr expr-2.21 {r1=2.34, r2=2.34} {r2==r1} 1
+test_expr expr-2.22 {r1=1.23, r2=2.34} {min(r1,r2,r1+r2,r1-r2)} {-1.11}
+test_expr expr-2.23 {r1=1.23, r2=2.34} {max(r1,r2,r1+r2,r1-r2)} {3.57}
+test_expr expr-2.24 {r1=25.0, r2=11.0} {r1%r2} 3.0
+test_expr expr-2.25 {r1=1.23, r2=NULL} {coalesce(r1+r2,99.0)} 99.0
+test_expr expr-2.26 {r1=1e300, r2=1e300} {coalesce((r1*r2)*0.0,99.0)} 99.0
+test_expr expr-2.26b {r1=1e300, r2=-1e300} {coalesce((r1*r2)*0.0,99.0)} 99.0
+test_expr expr-2.27 {r1=1.1, r2=0.0} {r1/r2} {{}}
+test_expr expr-2.28 {r1=1.1, r2=0.0} {r1%r2} {{}}
+
+test_expr expr-3.1 {t1='abc', t2='xyz'} {t1<t2} 1
+test_expr expr-3.2 {t1='xyz', t2='abc'} {t1<t2} 0
+test_expr expr-3.3 {t1='abc', t2='abc'} {t1<t2} 0
+test_expr expr-3.4 {t1='abc', t2='xyz'} {t1<=t2} 1
+test_expr expr-3.5 {t1='xyz', t2='abc'} {t1<=t2} 0
+test_expr expr-3.6 {t1='abc', t2='abc'} {t1<=t2} 1
+test_expr expr-3.7 {t1='abc', t2='xyz'} {t1>t2} 0
+test_expr expr-3.8 {t1='xyz', t2='abc'} {t1>t2} 1
+test_expr expr-3.9 {t1='abc', t2='abc'} {t1>t2} 0
+test_expr expr-3.10 {t1='abc', t2='xyz'} {t1>=t2} 0
+test_expr expr-3.11 {t1='xyz', t2='abc'} {t1>=t2} 1
+test_expr expr-3.12 {t1='abc', t2='abc'} {t1>=t2} 1
+test_expr expr-3.13 {t1='abc', t2='xyz'} {t1=t2} 0
+test_expr expr-3.14 {t1='xyz', t2='abc'} {t1=t2} 0
+test_expr expr-3.15 {t1='abc', t2='abc'} {t1=t2} 1
+test_expr expr-3.16 {t1='abc', t2='xyz'} {t1==t2} 0
+test_expr expr-3.17 {t1='xyz', t2='abc'} {t1==t2} 0
+test_expr expr-3.18 {t1='abc', t2='abc'} {t1==t2} 1
+test_expr expr-3.19 {t1='abc', t2='xyz'} {t1<>t2} 1
+test_expr expr-3.20 {t1='xyz', t2='abc'} {t1<>t2} 1
+test_expr expr-3.21 {t1='abc', t2='abc'} {t1<>t2} 0
+test_expr expr-3.22 {t1='abc', t2='xyz'} {t1!=t2} 1
+test_expr expr-3.23 {t1='xyz', t2='abc'} {t1!=t2} 1
+test_expr expr-3.24 {t1='abc', t2='abc'} {t1!=t2} 0
+test_expr expr-3.25 {t1=NULL, t2='hi'} {t1 isnull} 1
+test_expr expr-3.25b {t1=NULL, t2='hi'} {t1 is null} 1
+test_expr expr-3.26 {t1=NULL, t2='hi'} {t2 isnull} 0
+test_expr expr-3.27 {t1=NULL, t2='hi'} {t1 notnull} 0
+test_expr expr-3.28 {t1=NULL, t2='hi'} {t2 notnull} 1
+test_expr expr-3.28b {t1=NULL, t2='hi'} {t2 is not null} 1
+test_expr expr-3.29 {t1='xyz', t2='abc'} {t1||t2} {xyzabc}
+test_expr expr-3.30 {t1=NULL, t2='abc'} {t1||t2} {{}}
+test_expr expr-3.31 {t1='xyz', t2=NULL} {t1||t2} {{}}
+test_expr expr-3.32 {t1='xyz', t2='abc'} {t1||' hi '||t2} {{xyz hi abc}}
+test_expr epxr-3.33 {t1='abc', t2=NULL} {coalesce(t1<t2,99)} 99
+test_expr epxr-3.34 {t1='abc', t2=NULL} {coalesce(t2<t1,99)} 99
+test_expr epxr-3.35 {t1='abc', t2=NULL} {coalesce(t1>t2,99)} 99
+test_expr epxr-3.36 {t1='abc', t2=NULL} {coalesce(t2>t1,99)} 99
+test_expr epxr-3.37 {t1='abc', t2=NULL} {coalesce(t1<=t2,99)} 99
+test_expr epxr-3.38 {t1='abc', t2=NULL} {coalesce(t2<=t1,99)} 99
+test_expr epxr-3.39 {t1='abc', t2=NULL} {coalesce(t1>=t2,99)} 99
+test_expr epxr-3.40 {t1='abc', t2=NULL} {coalesce(t2>=t1,99)} 99
+test_expr epxr-3.41 {t1='abc', t2=NULL} {coalesce(t1==t2,99)} 99
+test_expr epxr-3.42 {t1='abc', t2=NULL} {coalesce(t2==t1,99)} 99
+test_expr epxr-3.43 {t1='abc', t2=NULL} {coalesce(t1!=t2,99)} 99
+test_expr epxr-3.44 {t1='abc', t2=NULL} {coalesce(t2!=t1,99)} 99
+
+test_expr expr-4.1 {t1='abc', t2='Abc'} {t1<t2} 0
+test_expr expr-4.2 {t1='abc', t2='Abc'} {t1>t2} 1
+test_expr expr-4.3 {t1='abc', t2='Bbc'} {t1<t2} 0
+test_expr expr-4.4 {t1='abc', t2='Bbc'} {t1>t2} 1
+test_expr expr-4.5 {t1='0', t2='0.0'} {t1==t2} 0
+test_expr expr-4.6 {t1='0.000', t2='0.0'} {t1==t2} 0
+test_expr expr-4.7 {t1=' 0.000', t2=' 0.0'} {t1==t2} 0
+test_expr expr-4.8 {t1='0.0', t2='abc'} {t1<t2} 1
+test_expr expr-4.9 {t1='0.0', t2='abc'} {t1==t2} 0
+test_expr expr-4.10 {r1='0.0', r2='abc'} {r1>r2} 0
+test_expr expr-4.11 {r1='abc', r2='Abc'} {r1<r2} 0
+test_expr expr-4.12 {r1='abc', r2='Abc'} {r1>r2} 1
+test_expr expr-4.13 {r1='abc', r2='Bbc'} {r1<r2} 0
+test_expr expr-4.14 {r1='abc', r2='Bbc'} {r1>r2} 1
+test_expr expr-4.15 {r1='0', r2='0.0'} {r1==r2} 1
+test_expr expr-4.16 {r1='0.000', r2='0.0'} {r1==r2} 1
+test_expr expr-4.17 {r1=' 0.000', r2=' 0.0'} {r1==r2} 0
+test_expr expr-4.18 {r1='0.0', r2='abc'} {r1<r2} 1
+test_expr expr-4.19 {r1='0.0', r2='abc'} {r1==r2} 0
+test_expr expr-4.20 {r1='0.0', r2='abc'} {r1>r2} 0
+
+# CSL is true if LIKE is case sensitive and false if not.
+# NCSL is the opposite. Use these variables as the result
+# on operations where case makes a difference.
+set CSL $sqlite_options(casesensitivelike)
+set NCSL [expr {!$CSL}]
+
+test_expr expr-5.1 {t1='abc', t2='xyz'} {t1 LIKE t2} 0
+test_expr expr-5.2a {t1='abc', t2='abc'} {t1 LIKE t2} 1
+test_expr expr-5.2b {t1='abc', t2='ABC'} {t1 LIKE t2} $NCSL
+test_expr expr-5.3a {t1='abc', t2='a_c'} {t1 LIKE t2} 1
+test_expr expr-5.3b {t1='abc', t2='A_C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.4 {t1='abc', t2='abc_'} {t1 LIKE t2} 0
+test_expr expr-5.5a {t1='abc', t2='a%c'} {t1 LIKE t2} 1
+test_expr expr-5.5b {t1='abc', t2='A%C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.5c {t1='abdc', t2='a%c'} {t1 LIKE t2} 1
+test_expr expr-5.5d {t1='ac', t2='a%c'} {t1 LIKE t2} 1
+test_expr expr-5.5e {t1='ac', t2='A%C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.6a {t1='abxyzzyc', t2='a%c'} {t1 LIKE t2} 1
+test_expr expr-5.6b {t1='abxyzzyc', t2='A%C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.7a {t1='abxyzzy', t2='a%c'} {t1 LIKE t2} 0
+test_expr expr-5.7b {t1='abxyzzy', t2='A%C'} {t1 LIKE t2} 0
+test_expr expr-5.8a {t1='abxyzzycx', t2='a%c'} {t1 LIKE t2} 0
+test_expr expr-5.8b {t1='abxyzzycy', t2='a%cx'} {t1 LIKE t2} 0
+test_expr expr-5.8c {t1='abxyzzycx', t2='A%C'} {t1 LIKE t2} 0
+test_expr expr-5.8d {t1='abxyzzycy', t2='A%CX'} {t1 LIKE t2} 0
+test_expr expr-5.9a {t1='abc', t2='a%_c'} {t1 LIKE t2} 1
+test_expr expr-5.9b {t1='ac', t2='a%_c'} {t1 LIKE t2} 0
+test_expr expr-5.9c {t1='abc', t2='A%_C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.9d {t1='ac', t2='A%_C'} {t1 LIKE t2} 0
+test_expr expr-5.10a {t1='abxyzzyc', t2='a%_c'} {t1 LIKE t2} 1
+test_expr expr-5.10b {t1='abxyzzyc', t2='A%_C'} {t1 LIKE t2} $NCSL
+test_expr expr-5.11 {t1='abc', t2='xyz'} {t1 NOT LIKE t2} 1
+test_expr expr-5.12a {t1='abc', t2='abc'} {t1 NOT LIKE t2} 0
+test_expr expr-5.12b {t1='abc', t2='ABC'} {t1 NOT LIKE t2} $CSL
+test_expr expr-5.13 {t1='A'} {t1 LIKE 'A%_'} 0
+test_expr expr-5.14 {t1='AB'} {t1 LIKE 'A%b' ESCAPE 'b'} 0
+
+# The following tests only work on versions of TCL that support Unicode
+#
+if {"\u1234"!="u1234"} {
+ test_expr expr-5.13a "t1='a\u0080c', t2='a_c'" {t1 LIKE t2} 1
+ test_expr expr-5.13b "t1='a\u0080c', t2='A_C'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.14a "t1='a\u07FFc', t2='a_c'" {t1 LIKE t2} 1
+ test_expr expr-5.14b "t1='a\u07FFc', t2='A_C'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.15a "t1='a\u0800c', t2='a_c'" {t1 LIKE t2} 1
+ test_expr expr-5.15b "t1='a\u0800c', t2='A_C'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.16a "t1='a\uFFFFc', t2='a_c'" {t1 LIKE t2} 1
+ test_expr expr-5.16b "t1='a\uFFFFc', t2='A_C'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.17 "t1='a\u0080', t2='A__'" {t1 LIKE t2} 0
+ test_expr expr-5.18 "t1='a\u07FF', t2='A__'" {t1 LIKE t2} 0
+ test_expr expr-5.19 "t1='a\u0800', t2='A__'" {t1 LIKE t2} 0
+ test_expr expr-5.20 "t1='a\uFFFF', t2='A__'" {t1 LIKE t2} 0
+ test_expr expr-5.21a "t1='ax\uABCD', t2='a_\uABCD'" {t1 LIKE t2} 1
+ test_expr expr-5.21b "t1='ax\uABCD', t2='A_\uABCD'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.22a "t1='ax\u1234', t2='a%\u1234'" {t1 LIKE t2} 1
+ test_expr expr-5.22b "t1='ax\u1234', t2='A%\u1234'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.23a "t1='ax\uFEDC', t2='a_%'" {t1 LIKE t2} 1
+ test_expr expr-5.23b "t1='ax\uFEDC', t2='A_%'" {t1 LIKE t2} $NCSL
+ test_expr expr-5.24a "t1='ax\uFEDCy\uFEDC', t2='a%\uFEDC'" {t1 LIKE t2} 1
+ test_expr expr-5.24b "t1='ax\uFEDCy\uFEDC', t2='A%\uFEDC'" {t1 LIKE t2} $NCSL
+}
+
+test_expr expr-5.54 {t1='abc', t2=NULL} {t1 LIKE t2} {{}}
+test_expr expr-5.55 {t1='abc', t2=NULL} {t1 NOT LIKE t2} {{}}
+test_expr expr-5.56 {t1='abc', t2=NULL} {t2 LIKE t1} {{}}
+test_expr expr-5.57 {t1='abc', t2=NULL} {t2 NOT LIKE t1} {{}}
+
+# LIKE expressions that use ESCAPE characters.
+test_expr expr-5.58a {t1='abc', t2='a_c'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.58b {t1='abc', t2='A_C'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.59a {t1='a_c', t2='a7_c'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.59b {t1='a_c', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.60a {t1='abc', t2='a7_c'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.60b {t1='abc', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.61a {t1='a7Xc', t2='a7_c'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.61b {t1='a7Xc', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.62a {t1='abcde', t2='a%e'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.62b {t1='abcde', t2='A%E'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.63a {t1='abcde', t2='a7%e'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.63b {t1='abcde', t2='A7%E'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.64a {t1='a7cde', t2='a7%e'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.64b {t1='a7cde', t2='A7%E'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.65a {t1='a7cde', t2='a77%e'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.65b {t1='a7cde', t2='A77%E'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.66a {t1='abc7', t2='a%77'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.66b {t1='abc7', t2='A%77'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.67a {t1='abc_', t2='a%7_'} {t1 LIKE t2 ESCAPE '7'} 1
+test_expr expr-5.67b {t1='abc_', t2='A%7_'} {t1 LIKE t2 ESCAPE '7'} $NCSL
+test_expr expr-5.68a {t1='abc7', t2='a%7_'} {t1 LIKE t2 ESCAPE '7'} 0
+test_expr expr-5.68b {t1='abc7', t2='A%7_'} {t1 LIKE t2 ESCAPE '7'} 0
+
+# These are the same test as the block above, but using a multi-byte
+# character as the escape character.
+if {"\u1234"!="u1234"} {
+ test_expr expr-5.69a "t1='abc', t2='a_c'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.69b "t1='abc', t2='A_C'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.70a "t1='a_c', t2='a\u1234_c'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.70b "t1='a_c', t2='A\u1234_C'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.71a "t1='abc', t2='a\u1234_c'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.71b "t1='abc', t2='A\u1234_C'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.72a "t1='a\u1234Xc', t2='a\u1234_c'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.72b "t1='a\u1234Xc', t2='A\u1234_C'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.73a "t1='abcde', t2='a%e'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.73b "t1='abcde', t2='A%E'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.74a "t1='abcde', t2='a\u1234%e'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.74b "t1='abcde', t2='A\u1234%E'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.75a "t1='a\u1234cde', t2='a\u1234%e'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.75b "t1='a\u1234cde', t2='A\u1234%E'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.76a "t1='a\u1234cde', t2='a\u1234\u1234%e'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.76b "t1='a\u1234cde', t2='A\u1234\u1234%E'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.77a "t1='abc\u1234', t2='a%\u1234\u1234'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.77b "t1='abc\u1234', t2='A%\u1234\u1234'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.78a "t1='abc_', t2='a%\u1234_'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 1
+ test_expr expr-5.78b "t1='abc_', t2='A%\u1234_'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" $NCSL
+ test_expr expr-5.79a "t1='abc\u1234', t2='a%\u1234_'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+ test_expr expr-5.79b "t1='abc\u1234', t2='A%\u1234_'" \
+ "t1 LIKE t2 ESCAPE '\u1234'" 0
+}
+
+test_expr expr-6.1 {t1='abc', t2='xyz'} {t1 GLOB t2} 0
+test_expr expr-6.2 {t1='abc', t2='ABC'} {t1 GLOB t2} 0
+test_expr expr-6.3 {t1='abc', t2='A?C'} {t1 GLOB t2} 0
+test_expr expr-6.4 {t1='abc', t2='a?c'} {t1 GLOB t2} 1
+test_expr expr-6.5 {t1='abc', t2='abc?'} {t1 GLOB t2} 0
+test_expr expr-6.6 {t1='abc', t2='A*C'} {t1 GLOB t2} 0
+test_expr expr-6.7 {t1='abc', t2='a*c'} {t1 GLOB t2} 1
+test_expr expr-6.8 {t1='abxyzzyc', t2='a*c'} {t1 GLOB t2} 1
+test_expr expr-6.9 {t1='abxyzzy', t2='a*c'} {t1 GLOB t2} 0
+test_expr expr-6.10 {t1='abxyzzycx', t2='a*c'} {t1 GLOB t2} 0
+test_expr expr-6.11 {t1='abc', t2='xyz'} {t1 NOT GLOB t2} 1
+test_expr expr-6.12 {t1='abc', t2='abc'} {t1 NOT GLOB t2} 0
+test_expr expr-6.13 {t1='abc', t2='a[bx]c'} {t1 GLOB t2} 1
+test_expr expr-6.14 {t1='abc', t2='a[cx]c'} {t1 GLOB t2} 0
+test_expr expr-6.15 {t1='abc', t2='a[a-d]c'} {t1 GLOB t2} 1
+test_expr expr-6.16 {t1='abc', t2='a[^a-d]c'} {t1 GLOB t2} 0
+test_expr expr-6.17 {t1='abc', t2='a[A-Dc]c'} {t1 GLOB t2} 0
+test_expr expr-6.18 {t1='abc', t2='a[^A-Dc]c'} {t1 GLOB t2} 1
+test_expr expr-6.19 {t1='abc', t2='a[]b]c'} {t1 GLOB t2} 1
+test_expr expr-6.20 {t1='abc', t2='a[^]b]c'} {t1 GLOB t2} 0
+test_expr expr-6.21a {t1='abcdefg', t2='a*[de]g'} {t1 GLOB t2} 0
+test_expr expr-6.21b {t1='abcdefg', t2='a*[df]g'} {t1 GLOB t2} 1
+test_expr expr-6.21c {t1='abcdefg', t2='a*[d-h]g'} {t1 GLOB t2} 1
+test_expr expr-6.21d {t1='abcdefg', t2='a*[b-e]g'} {t1 GLOB t2} 0
+test_expr expr-6.22a {t1='abcdefg', t2='a*[^de]g'} {t1 GLOB t2} 1
+test_expr expr-6.22b {t1='abcdefg', t2='a*[^def]g'} {t1 GLOB t2} 0
+test_expr expr-6.23 {t1='abcdefg', t2='a*?g'} {t1 GLOB t2} 1
+test_expr expr-6.24 {t1='ac', t2='a*c'} {t1 GLOB t2} 1
+test_expr expr-6.25 {t1='ac', t2='a*?c'} {t1 GLOB t2} 0
+test_expr expr-6.26 {t1='a*c', t2='a[*]c'} {t1 GLOB t2} 1
+test_expr expr-6.27 {t1='a?c', t2='a[?]c'} {t1 GLOB t2} 1
+test_expr expr-6.28 {t1='a[c', t2='a[[]c'} {t1 GLOB t2} 1
+
+
+# These tests only work on versions of TCL that support Unicode
+#
+if {"\u1234"!="u1234"} {
+ test_expr expr-6.26 "t1='a\u0080c', t2='a?c'" {t1 GLOB t2} 1
+ test_expr expr-6.27 "t1='a\u07ffc', t2='a?c'" {t1 GLOB t2} 1
+ test_expr expr-6.28 "t1='a\u0800c', t2='a?c'" {t1 GLOB t2} 1
+ test_expr expr-6.29 "t1='a\uffffc', t2='a?c'" {t1 GLOB t2} 1
+ test_expr expr-6.30 "t1='a\u1234', t2='a?'" {t1 GLOB t2} 1
+ test_expr expr-6.31 "t1='a\u1234', t2='a??'" {t1 GLOB t2} 0
+ test_expr expr-6.32 "t1='ax\u1234', t2='a?\u1234'" {t1 GLOB t2} 1
+ test_expr expr-6.33 "t1='ax\u1234', t2='a*\u1234'" {t1 GLOB t2} 1
+ test_expr expr-6.34 "t1='ax\u1234y\u1234', t2='a*\u1234'" {t1 GLOB t2} 1
+ test_expr expr-6.35 "t1='a\u1234b', t2='a\[x\u1234y\]b'" {t1 GLOB t2} 1
+ test_expr expr-6.36 "t1='a\u1234b', t2='a\[\u1233-\u1235\]b'" {t1 GLOB t2} 1
+ test_expr expr-6.37 "t1='a\u1234b', t2='a\[\u1234-\u124f\]b'" {t1 GLOB t2} 1
+ test_expr expr-6.38 "t1='a\u1234b', t2='a\[\u1235-\u124f\]b'" {t1 GLOB t2} 0
+ test_expr expr-6.39 "t1='a\u1234b', t2='a\[a-\u1235\]b'" {t1 GLOB t2} 1
+ test_expr expr-6.40 "t1='a\u1234b', t2='a\[a-\u1234\]b'" {t1 GLOB t2} 1
+ test_expr expr-6.41 "t1='a\u1234b', t2='a\[a-\u1233\]b'" {t1 GLOB t2} 0
+}
+
+test_expr expr-6.51 {t1='ABC', t2='xyz'} {t1 GLOB t2} 0
+test_expr expr-6.52 {t1='ABC', t2='abc'} {t1 GLOB t2} 0
+test_expr expr-6.53 {t1='ABC', t2='a?c'} {t1 GLOB t2} 0
+test_expr expr-6.54 {t1='ABC', t2='A?C'} {t1 GLOB t2} 1
+test_expr expr-6.55 {t1='ABC', t2='abc?'} {t1 GLOB t2} 0
+test_expr expr-6.56 {t1='ABC', t2='a*c'} {t1 GLOB t2} 0
+test_expr expr-6.57 {t1='ABC', t2='A*C'} {t1 GLOB t2} 1
+test_expr expr-6.58 {t1='ABxyzzyC', t2='A*C'} {t1 GLOB t2} 1
+test_expr expr-6.59 {t1='ABxyzzy', t2='A*C'} {t1 GLOB t2} 0
+test_expr expr-6.60 {t1='ABxyzzyCx', t2='A*C'} {t1 GLOB t2} 0
+test_expr expr-6.61 {t1='ABC', t2='xyz'} {t1 NOT GLOB t2} 1
+test_expr expr-6.62 {t1='ABC', t2='ABC'} {t1 NOT GLOB t2} 0
+test_expr expr-6.63 {t1='ABC', t2='A[Bx]C'} {t1 GLOB t2} 1
+test_expr expr-6.64 {t1='ABC', t2='A[Cx]C'} {t1 GLOB t2} 0
+test_expr expr-6.65 {t1='ABC', t2='A[A-D]C'} {t1 GLOB t2} 1
+test_expr expr-6.66 {t1='ABC', t2='A[^A-D]C'} {t1 GLOB t2} 0
+test_expr expr-6.67 {t1='ABC', t2='A[a-dC]C'} {t1 GLOB t2} 0
+test_expr expr-6.68 {t1='ABC', t2='A[^a-dC]C'} {t1 GLOB t2} 1
+test_expr expr-6.69a {t1='ABC', t2='A[]B]C'} {t1 GLOB t2} 1
+test_expr expr-6.69b {t1='A]C', t2='A[]B]C'} {t1 GLOB t2} 1
+test_expr expr-6.70a {t1='ABC', t2='A[^]B]C'} {t1 GLOB t2} 0
+test_expr expr-6.70b {t1='AxC', t2='A[^]B]C'} {t1 GLOB t2} 1
+test_expr expr-6.70c {t1='A]C', t2='A[^]B]C'} {t1 GLOB t2} 0
+test_expr expr-6.71 {t1='ABCDEFG', t2='A*[DE]G'} {t1 GLOB t2} 0
+test_expr expr-6.72 {t1='ABCDEFG', t2='A*[^DE]G'} {t1 GLOB t2} 1
+test_expr expr-6.73 {t1='ABCDEFG', t2='A*?G'} {t1 GLOB t2} 1
+test_expr expr-6.74 {t1='AC', t2='A*C'} {t1 GLOB t2} 1
+test_expr expr-6.75 {t1='AC', t2='A*?C'} {t1 GLOB t2} 0
+
+test_expr expr-6.63 {t1=NULL, t2='a*?c'} {t1 GLOB t2} {{}}
+test_expr expr-6.64 {t1='ac', t2=NULL} {t1 GLOB t2} {{}}
+test_expr expr-6.65 {t1=NULL, t2='a*?c'} {t1 NOT GLOB t2} {{}}
+test_expr expr-6.66 {t1='ac', t2=NULL} {t1 NOT GLOB t2} {{}}
+
+# Check that the affinity of a CAST expression is calculated correctly.
+ifcapable cast {
+ test_expr expr-6.67 {t1='01', t2=1} {t1 = t2} 0
+ test_expr expr-6.68 {t1='1', t2=1} {t1 = t2} 1
+ test_expr expr-6.69 {t1='01', t2=1} {CAST(t1 AS INTEGER) = t2} 1
+}
+
+test_expr expr-case.1 {i1=1, i2=2} \
+ {CASE WHEN i1 = i2 THEN 'eq' ELSE 'ne' END} ne
+test_expr expr-case.2 {i1=2, i2=2} \
+ {CASE WHEN i1 = i2 THEN 'eq' ELSE 'ne' END} eq
+test_expr expr-case.3 {i1=NULL, i2=2} \
+ {CASE WHEN i1 = i2 THEN 'eq' ELSE 'ne' END} ne
+test_expr expr-case.4 {i1=2, i2=NULL} \
+ {CASE WHEN i1 = i2 THEN 'eq' ELSE 'ne' END} ne
+test_expr expr-case.5 {i1=2} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'error' END} two
+test_expr expr-case.6 {i1=1} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN NULL THEN 'two' ELSE 'error' END} one
+test_expr expr-case.7 {i1=2} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN NULL THEN 'two' ELSE 'error' END} error
+test_expr expr-case.8 {i1=3} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN NULL THEN 'two' ELSE 'error' END} error
+test_expr expr-case.9 {i1=3} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'error' END} error
+test_expr expr-case.10 {i1=3} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' END} {{}}
+test_expr expr-case.11 {i1=null} \
+ {CASE i1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 3 END} 3
+test_expr expr-case.12 {i1=1} \
+ {CASE i1 WHEN 1 THEN null WHEN 2 THEN 'two' ELSE 3 END} {{}}
+test_expr expr-case.13 {i1=7} \
+ { CASE WHEN i1 < 5 THEN 'low'
+ WHEN i1 < 10 THEN 'medium'
+ WHEN i1 < 15 THEN 'high' ELSE 'error' END} medium
+
+
+# The sqliteExprIfFalse and sqliteExprIfTrue routines are only
+# executed as part of a WHERE clause. Create a table suitable
+# for testing these functions.
+#
+execsql {DROP TABLE test1}
+execsql {CREATE TABLE test1(a int, b int);}
+for {set i 1} {$i<=20} {incr i} {
+ execsql "INSERT INTO test1 VALUES($i,[expr {1<<$i}])"
+}
+execsql "INSERT INTO test1 VALUES(NULL,0)"
+do_test expr-7.1 {
+ execsql {SELECT * FROM test1 ORDER BY a}
+} {{} 0 1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024 11 2048 12 4096 13 8192 14 16384 15 32768 16 65536 17 131072 18 262144 19 524288 20 1048576}
+
+proc test_expr2 {name expr result} {
+ do_test $name [format {
+ execsql {SELECT a FROM test1 WHERE %s ORDER BY a}
+ } $expr] $result
+}
+
+test_expr2 expr-7.2 {a<10 AND a>8} {9}
+test_expr2 expr-7.3 {a<=10 AND a>=8} {8 9 10}
+test_expr2 expr-7.4 {a>=8 AND a<=10} {8 9 10}
+test_expr2 expr-7.5 {a>=20 OR a<=1} {1 20}
+test_expr2 expr-7.6 {b!=4 AND a<=3} {1 3}
+test_expr2 expr-7.7 {b==8 OR b==16 OR b==32} {3 4 5}
+test_expr2 expr-7.8 {NOT b<>8 OR b==1024} {3 10}
+test_expr2 expr-7.9 {b LIKE '10%'} {10 20}
+test_expr2 expr-7.10 {b LIKE '_4'} {6}
+test_expr2 expr-7.11 {a GLOB '1?'} {10 11 12 13 14 15 16 17 18 19}
+test_expr2 expr-7.12 {b GLOB '1*4'} {10 14}
+test_expr2 expr-7.13 {b GLOB '*1[456]'} {4}
+test_expr2 expr-7.14 {a ISNULL} {{}}
+test_expr2 expr-7.15 {a NOTNULL AND a<3} {1 2}
+test_expr2 expr-7.16 {a AND a<3} {1 2}
+test_expr2 expr-7.17 {NOT a} {}
+test_expr2 expr-7.18 {a==11 OR (b>1000 AND b<2000)} {10 11}
+test_expr2 expr-7.19 {a<=1 OR a>=20} {1 20}
+test_expr2 expr-7.20 {a<1 OR a>20} {}
+test_expr2 expr-7.21 {a>19 OR a<1} {20}
+test_expr2 expr-7.22 {a!=1 OR a=100} \
+ {2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
+test_expr2 expr-7.23 {(a notnull AND a<4) OR a==8} {1 2 3 8}
+test_expr2 expr-7.24 {a LIKE '2_' OR a==8} {8 20}
+test_expr2 expr-7.25 {a GLOB '2?' OR a==8} {8 20}
+test_expr2 expr-7.26 {a isnull OR a=8} {{} 8}
+test_expr2 expr-7.27 {a notnull OR a=8} \
+ {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
+test_expr2 expr-7.28 {a<0 OR b=0} {{}}
+test_expr2 expr-7.29 {b=0 OR a<0} {{}}
+test_expr2 expr-7.30 {a<0 AND b=0} {}
+test_expr2 expr-7.31 {b=0 AND a<0} {}
+test_expr2 expr-7.32 {a IS NULL AND (a<0 OR b=0)} {{}}
+test_expr2 expr-7.33 {a IS NULL AND (b=0 OR a<0)} {{}}
+test_expr2 expr-7.34 {a IS NULL AND (a<0 AND b=0)} {}
+test_expr2 expr-7.35 {a IS NULL AND (b=0 AND a<0)} {}
+test_expr2 expr-7.32 {(a<0 OR b=0) AND a IS NULL} {{}}
+test_expr2 expr-7.33 {(b=0 OR a<0) AND a IS NULL} {{}}
+test_expr2 expr-7.34 {(a<0 AND b=0) AND a IS NULL} {}
+test_expr2 expr-7.35 {(b=0 AND a<0) AND a IS NULL} {}
+test_expr2 expr-7.36 {a<2 OR (a<0 OR b=0)} {{} 1}
+test_expr2 expr-7.37 {a<2 OR (b=0 OR a<0)} {{} 1}
+test_expr2 expr-7.38 {a<2 OR (a<0 AND b=0)} {1}
+test_expr2 expr-7.39 {a<2 OR (b=0 AND a<0)} {1}
+test_expr2 expr-7.40 {((a<2 OR a IS NULL) AND b<3) OR b>1e10} {{} 1}
+test_expr2 expr-7.41 {a BETWEEN -1 AND 1} {1}
+test_expr2 expr-7.42 {a NOT BETWEEN 2 AND 100} {1}
+test_expr2 expr-7.43 {(b+1234)||'this is a string that is at least 32 characters long' BETWEEN 1 AND 2} {}
+test_expr2 expr-7.44 {123||'xabcdefghijklmnopqrstuvwyxz01234567890'||a BETWEEN '123a' AND '123b'} {}
+test_expr2 expr-7.45 {((123||'xabcdefghijklmnopqrstuvwyxz01234567890'||a) BETWEEN '123a' AND '123b')<0} {}
+test_expr2 expr-7.46 {((123||'xabcdefghijklmnopqrstuvwyxz01234567890'||a) BETWEEN '123a' AND '123z')>0} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
+
+test_expr2 expr-7.50 {((a between 1 and 2 OR 0) AND 1) OR 0} {1 2}
+test_expr2 expr-7.51 {((a not between 3 and 100 OR 0) AND 1) OR 0} {1 2}
+
+ifcapable subquery {
+ test_expr2 expr-7.52 {((a in (1,2) OR 0) AND 1) OR 0} {1 2}
+ test_expr2 expr-7.53 \
+ {((a not in (3,4,5,6,7,8,9,10) OR 0) AND a<11) OR 0} {1 2}
+}
+test_expr2 expr-7.54 {((a>0 OR 0) AND a<3) OR 0} {1 2}
+ifcapable subquery {
+ test_expr2 expr-7.55 {((a in (1,2) OR 0) IS NULL AND 1) OR 0} {{}}
+ test_expr2 expr-7.56 \
+ {((a not in (3,4,5,6,7,8,9,10) IS NULL OR 0) AND 1) OR 0} {{}}
+}
+test_expr2 expr-7.57 {((a>0 IS NULL OR 0) AND 1) OR 0} {{}}
+
+test_expr2 expr-7.58 {(a||'')<='1'} {1}
+
+test_expr2 expr-7.59 {LIKE('10%',b)} {10 20}
+test_expr2 expr-7.60 {LIKE('_4',b)} {6}
+test_expr2 expr-7.61 {GLOB('1?',a)} {10 11 12 13 14 15 16 17 18 19}
+test_expr2 expr-7.62 {GLOB('1*4',b)} {10 14}
+test_expr2 expr-7.63 {GLOB('*1[456]',b)} {4}
+
+# Test the CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP expressions.
+#
+set sqlite_current_time 1157124849
+do_test expr-8.1 {
+ execsql {SELECT CURRENT_TIME}
+} {15:34:09}
+do_test expr-8.2 {
+ execsql {SELECT CURRENT_DATE}
+} {2006-09-01}
+do_test expr-8.3 {
+ execsql {SELECT CURRENT_TIMESTAMP}
+} {{2006-09-01 15:34:09}}
+ifcapable datetime {
+ do_test expr-8.4 {
+ execsql {SELECT CURRENT_TIME==time('now');}
+ } 1
+ do_test expr-8.5 {
+ execsql {SELECT CURRENT_DATE==date('now');}
+ } 1
+ do_test expr-8.6 {
+ execsql {SELECT CURRENT_TIMESTAMP==datetime('now');}
+ } 1
+}
+set sqlite_current_time 0
+
+do_test expr-9.1 {
+ execsql {SELECT round(-('-'||'123'))}
+} 123.0
+
+# Test an error message that can be generated by the LIKE expression
+do_test expr-10.1 {
+ catchsql {SELECT 'abc' LIKE 'abc' ESCAPE ''}
+} {1 {ESCAPE expression must be a single character}}
+do_test expr-10.2 {
+ catchsql {SELECT 'abc' LIKE 'abc' ESCAPE 'ab'}
+} {1 {ESCAPE expression must be a single character}}
+
+# If we specify an integer constant that is bigger than the largest
+# possible integer, code the integer as a real number.
+#
+do_test expr-11.1 {
+ execsql {SELECT typeof(9223372036854775807)}
+} {integer}
+do_test expr-11.2 {
+ execsql {SELECT typeof(00000009223372036854775807)}
+} {integer}
+do_test expr-11.3 {
+ execsql {SELECT typeof(+9223372036854775807)}
+} {integer}
+do_test expr-11.4 {
+ execsql {SELECT typeof(+000000009223372036854775807)}
+} {integer}
+do_test expr-11.5 {
+ execsql {SELECT typeof(9223372036854775808)}
+} {real}
+do_test expr-11.6 {
+ execsql {SELECT typeof(00000009223372036854775808)}
+} {real}
+do_test expr-11.7 {
+ execsql {SELECT typeof(+9223372036854775808)}
+} {real}
+do_test expr-11.8 {
+ execsql {SELECT typeof(+0000009223372036854775808)}
+} {real}
+do_test expr-11.11 {
+ execsql {SELECT typeof(-9223372036854775808)}
+} {integer}
+do_test expr-11.12 {
+ execsql {SELECT typeof(-00000009223372036854775808)}
+} {integer}
+do_test expr-11.13 {
+ execsql {SELECT typeof(-9223372036854775809)}
+} {real}
+do_test expr-11.14 {
+ execsql {SELECT typeof(-00000009223372036854775809)}
+} {real}
+
+# These two statements used to leak memory (because of missing %destructor
+# directives in parse.y).
+do_test expr-12.1 {
+ catchsql {
+ SELECT (CASE a>4 THEN 1 ELSE 0 END) FROM test1;
+ }
+} {1 {near "THEN": syntax error}}
+do_test expr-12.2 {
+ catchsql {
+ SELECT (CASE WHEN a>4 THEN 1 ELSE 0) FROM test1;
+ }
+} {1 {near ")": syntax error}}
+
+do_test expr-13.1 {
+ execsql {
+ SELECT 12345678901234567890;
+ }
+} {1.23456789012346e+19}
+
+# Implicit String->Integer conversion is used when possible.
+#
+do_test expr-13.2 {
+ execsql {
+ SELECT 0+'9223372036854775807'
+ }
+} {9223372036854775807}
+do_test expr-13.3 {
+ execsql {
+ SELECT '9223372036854775807'+0
+ }
+} {9223372036854775807}
+
+# If the value is too large, use String->Float conversion.
+#
+do_test expr-13.4 {
+ execsql {
+ SELECT 0+'9223372036854775808'
+ }
+} {9.22337203685478e+18}
+do_test expr-13.5 {
+ execsql {
+ SELECT '9223372036854775808'+0
+ }
+} {9.22337203685478e+18}
+
+# Use String->float conversion if the value is explicitly a floating
+# point value.
+#
+do_test expr-13.6 {
+ execsql {
+ SELECT 0+'9223372036854775807.0'
+ }
+} {9.22337203685478e+18}
+do_test expr-13.7 {
+ execsql {
+ SELECT '9223372036854775807.0'+0
+ }
+} {9.22337203685478e+18}
+
+
+finish_test
diff --git a/third_party/sqlite/test/filectrl.test b/third_party/sqlite/test/filectrl.test
new file mode 100755
index 0000000..eb0a0e4
--- /dev/null
+++ b/third_party/sqlite/test/filectrl.test
@@ -0,0 +1,31 @@
+# 2008 Jan 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.
+#
+#***********************************************************************
+#
+# $Id: filectrl.test,v 1.1 2008/01/22 14:50:17 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_test filectrl-1.1 {
+ file_control_test db
+} {}
+do_test filectrl-1.2 {
+ db eval {CREATE TEMP TABLE x(y);}
+ file_control_test db
+} {}
+do_test filectrl-1.3 {
+ db close
+ sqlite3 db :memory:
+ file_control_test db
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/filefmt.test b/third_party/sqlite/test/filefmt.test
new file mode 100755
index 0000000..dc4fe5b
--- /dev/null
+++ b/third_party/sqlite/test/filefmt.test
@@ -0,0 +1,115 @@
+# 2007 April 6
+#
+# 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.
+#
+# This file implements tests to verify database file format.
+#
+# $Id: filefmt.test,v 1.2 2007/04/06 21:42:22 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+file delete -force test.db test.db-journal
+
+# Database begins with valid 16-byte header string.
+#
+do_test filefmt-1.1 {
+ sqlite3 db test.db
+ db eval {CREATE TABLE t1(x)}
+ db close
+ hexio_read test.db 0 16
+} {53514C69746520666F726D6174203300}
+
+# If the 16-byte header is changed, the file will not open
+#
+do_test filefmt-1.2 {
+ hexio_write test.db 0 54
+ set x [catch {sqlite3 db test.db} err]
+ lappend x $err
+} {0 {}}
+do_test filefmt-1.3 {
+ catchsql {
+ SELECT count(*) FROM sqlite_master
+ }
+} {1 {file is encrypted or is not a database}}
+do_test filefmt-1.4 {
+ db close
+ hexio_write test.db 0 53
+ sqlite3 db test.db
+ catchsql {
+ SELECT count(*) FROM sqlite_master
+ }
+} {0 1}
+
+# The page-size is stored at offset 16
+#
+ifcapable pager_pragmas {
+ foreach pagesize {512 1024 2048 4096 8192 16384 32768} {
+ if {[info exists SQLITE_MAX_PAGE_SIZE]
+ && $pagesize>$SQLITE_MAX_PAGE_SIZE} continue
+ do_test filefmt-1.5.$pagesize.1 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ db eval "PRAGMA auto_vacuum=OFF"
+ db eval "PRAGMA page_size=$pagesize"
+ db eval {CREATE TABLE t1(x)}
+ file size test.db
+ } [expr $pagesize*2]
+ do_test filefmt-1.5.$pagesize.2 {
+ hexio_get_int [hexio_read test.db 16 2]
+ } $pagesize
+ }
+}
+
+# The page-size must be a power of 2
+#
+do_test filefmt-1.6 {
+ db close
+ hexio_write test.db 16 [hexio_render_int16 1025]
+ sqlite3 db test.db
+ catchsql {
+ SELECT count(*) FROM sqlite_master
+ }
+} {1 {file is encrypted or is not a database}}
+
+
+# The page-size must be at least 512 bytes
+#
+do_test filefmt-1.7 {
+ db close
+ hexio_write test.db 16 [hexio_render_int16 256]
+ sqlite3 db test.db
+ catchsql {
+ SELECT count(*) FROM sqlite_master
+ }
+} {1 {file is encrypted or is not a database}}
+
+# Usable space per page (page-size minus unused space per page)
+# must be at least 500 bytes
+#
+ifcapable pager_pragmas {
+ do_test filefmt-1.8 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ db eval {PRAGMA page_size=512; CREATE TABLE t1(x)}
+ db close
+ hexio_write test.db 20 10
+ sqlite3 db test.db
+ catchsql {
+ SELECT count(*) FROM sqlite_master
+ }
+ } {1 {file is encrypted or is not a database}}
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fkey1.test b/third_party/sqlite/test/fkey1.test
new file mode 100755
index 0000000..52b52d3
--- /dev/null
+++ b/third_party/sqlite/test/fkey1.test
@@ -0,0 +1,77 @@
+# 2001 September 15
+#
+# 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.
+#
+# This file implements tests for foreign keys.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!foreignkey} {
+ finish_test
+ return
+}
+
+# Create a table and some data to work with.
+#
+do_test fkey1-1.0 {
+ execsql {
+ CREATE TABLE t1(
+ a INTEGER PRIMARY KEY,
+ b INTEGER
+ REFERENCES t1 ON DELETE CASCADE
+ REFERENCES t2,
+ c TEXT,
+ FOREIGN KEY (b,c) REFERENCES t2(x,y) ON UPDATE CASCADE
+ );
+ }
+} {}
+do_test fkey1-1.1 {
+ execsql {
+ CREATE TABLE t2(
+ x INTEGER PRIMARY KEY,
+ y TEXT
+ );
+ }
+} {}
+do_test fkey1-1.2 {
+ execsql {
+ CREATE TABLE t3(
+ a INTEGER REFERENCES t2 ON INSERT RESTRICT,
+ b INTEGER REFERENCES t1,
+ FOREIGN KEY (a,b) REFERENCES t2(x,y)
+ );
+ }
+} {}
+
+do_test fkey1-2.1 {
+ execsql {
+ CREATE TABLE t4(a integer primary key);
+ CREATE TABLE t5(x references t4);
+ CREATE TABLE t6(x references t4);
+ CREATE TABLE t7(x references t4);
+ CREATE TABLE t8(x references t4);
+ CREATE TABLE t9(x references t4);
+ CREATE TABLE t10(x references t4);
+ DROP TABLE t7;
+ DROP TABLE t9;
+ DROP TABLE t5;
+ DROP TABLE t8;
+ DROP TABLE t6;
+ DROP TABLE t10;
+ }
+} {}
+
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/format4.test b/third_party/sqlite/test/format4.test
new file mode 100755
index 0000000..14d7947
--- /dev/null
+++ b/third_party/sqlite/test/format4.test
@@ -0,0 +1,65 @@
+# 2005 December 29
+#
+# 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.
+#
+# This file implements tests to verify that the new serial_type
+# values of 8 (integer 0) and 9 (integer 1) work correctly.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db eval {PRAGMA legacy_file_format=OFF}
+
+# The size of the database depends on whether or not autovacuum
+# is enabled.
+#
+ifcapable autovacuum {
+ if {[db one {PRAGMA auto_vacuum}]} {
+ set small 3072
+ set large 5120
+ } else {
+ set small 2048
+ set large 4096
+ }
+} else {
+ set small 2048
+ set large 4096
+}
+
+do_test format4-1.1 {
+ execsql {
+ CREATE TABLE t1(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
+ INSERT INTO t1 VALUES(0,0,0,0,0,0,0,0,0,0);
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ INSERT INTO t1 SELECT * FROM t1;
+ }
+ file size test.db
+} $small
+do_test format4-1.2 {
+ execsql {
+ UPDATE t1 SET x0=1, x1=1, x2=1, x3=1, x4=1, x5=1, x6=1, x7=1, x8=1, x9=1
+ }
+ file size test.db
+} $small
+do_test format4-1.3 {
+ execsql {
+ UPDATE t1 SET x0=2, x1=2, x2=2, x3=2, x4=2, x5=2, x6=2, x7=2, x8=2, x9=2
+ }
+ file size test.db
+} $large
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts.test b/third_party/sqlite/test/fts.test
new file mode 100755
index 0000000..031f547
--- /dev/null
+++ b/third_party/sqlite/test/fts.test
@@ -0,0 +1,61 @@
+#
+# 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 runs the fts tests.
+#
+# $Id$
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ fts.test
+ fts1.test
+ fts2.test
+}
+
+if {[sqlite3 -has-codec]} {
+ # lappend EXCLUDE \
+ # conflict.test
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/fts*.test]] {
+ set tail [file tail $testfile]
+ puts "test: $tail"
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ }
+}
+source $testdir/misuse.test
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/fts1.test b/third_party/sqlite/test/fts1.test
new file mode 100755
index 0000000..1ddb19f
--- /dev/null
+++ b/third_party/sqlite/test/fts1.test
@@ -0,0 +1,61 @@
+#
+# 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 runs the fts tests.
+#
+# $Id$
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ fts.test
+ fts1.test
+ fts2.test
+}
+
+if {[sqlite3 -has-codec]} {
+ # lappend EXCLUDE \
+ # conflict.test
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/fts1*.test]] {
+ set tail [file tail $testfile]
+ puts "test: $tail"
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ }
+}
+source $testdir/misuse.test
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/fts1a.test b/third_party/sqlite/test/fts1a.test
new file mode 100755
index 0000000..b63e79a
--- /dev/null
+++ b/third_party/sqlite/test/fts1a.test
@@ -0,0 +1,186 @@
+# 2006 September 9
+#
+# 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 testing the FTS1 module.
+#
+# $Id: fts1a.test,v 1.4 2006/09/28 19:43:32 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+ INSERT INTO t1(content) VALUES('one');
+ INSERT INTO t1(content) VALUES('two');
+ INSERT INTO t1(content) VALUES('one two');
+ INSERT INTO t1(content) VALUES('three');
+ INSERT INTO t1(content) VALUES('one three');
+ INSERT INTO t1(content) VALUES('two three');
+ INSERT INTO t1(content) VALUES('one two three');
+ INSERT INTO t1(content) VALUES('four');
+ INSERT INTO t1(content) VALUES('one four');
+ INSERT INTO t1(content) VALUES('two four');
+ INSERT INTO t1(content) VALUES('one two four');
+ INSERT INTO t1(content) VALUES('three four');
+ INSERT INTO t1(content) VALUES('one three four');
+ INSERT INTO t1(content) VALUES('two three four');
+ INSERT INTO t1(content) VALUES('one two three four');
+ INSERT INTO t1(content) VALUES('five');
+ INSERT INTO t1(content) VALUES('one five');
+ INSERT INTO t1(content) VALUES('two five');
+ INSERT INTO t1(content) VALUES('one two five');
+ INSERT INTO t1(content) VALUES('three five');
+ INSERT INTO t1(content) VALUES('one three five');
+ INSERT INTO t1(content) VALUES('two three five');
+ INSERT INTO t1(content) VALUES('one two three five');
+ INSERT INTO t1(content) VALUES('four five');
+ INSERT INTO t1(content) VALUES('one four five');
+ INSERT INTO t1(content) VALUES('two four five');
+ INSERT INTO t1(content) VALUES('one two four five');
+ INSERT INTO t1(content) VALUES('three four five');
+ INSERT INTO t1(content) VALUES('one three four five');
+ INSERT INTO t1(content) VALUES('two three four five');
+ INSERT INTO t1(content) VALUES('one two three four five');
+}
+
+do_test fts1a-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts1a-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two'}
+} {3 7 11 15 19 23 27 31}
+do_test fts1a-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one'}
+} {3 7 11 15 19 23 27 31}
+do_test fts1a-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two three'}
+} {7 15 23 31}
+do_test fts1a-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one three two'}
+} {7 15 23 31}
+do_test fts1a-1.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two three one'}
+} {7 15 23 31}
+do_test fts1a-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one three'}
+} {7 15 23 31}
+do_test fts1a-1.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three one two'}
+} {7 15 23 31}
+do_test fts1a-1.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three two one'}
+} {7 15 23 31}
+do_test fts1a-1.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two THREE'}
+} {7 15 23 31}
+do_test fts1a-1.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH ' ONE Two three '}
+} {7 15 23 31}
+
+do_test fts1a-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one"'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts1a-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two"'}
+} {3 7 11 15 19 23 27 31}
+do_test fts1a-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"two one"'}
+} {}
+do_test fts1a-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three"'}
+} {7 15 23 31}
+do_test fts1a-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two"'}
+} {}
+do_test fts1a-2.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three four"'}
+} {15 31}
+do_test fts1a-2.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two four"'}
+} {}
+do_test fts1a-2.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three five"'}
+} {21}
+do_test fts1a-2.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" five'}
+} {21 29}
+do_test fts1a-2.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three"'}
+} {21 29}
+do_test fts1a-2.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three" four'}
+} {29}
+do_test fts1a-2.12 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five four "one three"'}
+} {29}
+do_test fts1a-2.13 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" four five'}
+} {29}
+
+do_test fts1a-3.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts1a-3.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one -two'}
+} {1 5 9 13 17 21 25 29}
+do_test fts1a-3.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'}
+} {1 5 9 13 17 21 25 29}
+
+do_test fts1a-4.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one OR two'}
+} {1 2 3 5 6 7 9 10 11 13 14 15 17 18 19 21 22 23 25 26 27 29 30 31}
+do_test fts1a-4.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two" OR three'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts1a-4.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR "one two"'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts1a-4.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts1a-4.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR two one'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts1a-4.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three OR four'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+do_test fts1a-4.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two OR three OR four one'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+
+# Test the ability to handle NULL content
+#
+do_test fts1a-5.1 {
+ execsql {INSERT INTO t1(content) VALUES(NULL)}
+} {}
+do_test fts1a-5.2 {
+ set rowid [db last_insert_rowid]
+ execsql {SELECT content FROM t1 WHERE rowid=$rowid}
+} {{}}
+do_test fts1a-5.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH NULL}
+} {}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts1b.test b/third_party/sqlite/test/fts1b.test
new file mode 100755
index 0000000..2bbe1aa
--- /dev/null
+++ b/third_party/sqlite/test/fts1b.test
@@ -0,0 +1,147 @@
+# 2006 September 13
+#
+# 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 testing the FTS1 module.
+#
+# $Id: fts1b.test,v 1.4 2006/09/18 02:12:48 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Fill the full-text index "t1" with phrases in english, spanish,
+# and german. For the i-th row, fill in the names for the bits
+# that are set in the value of i. The least significant bit is
+# 1. For example, the value 5 is 101 in binary which will be
+# converted to "one three" in english.
+#
+proc fill_multilanguage_fulltext_t1 {} {
+ set english {one two three four five}
+ set spanish {un dos tres cuatro cinco}
+ set german {eine zwei drei vier funf}
+
+ for {set i 1} {$i<=31} {incr i} {
+ set cmd "INSERT INTO t1 VALUES"
+ set vset {}
+ foreach lang {english spanish german} {
+ set words {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend words [lindex [set $lang] $j]}
+ }
+ lappend vset "'$words'"
+ }
+ set sql "INSERT INTO t1(english,spanish,german) VALUES([join $vset ,])"
+ # puts $sql
+ db eval $sql
+ }
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(english,spanish,german);
+}
+fill_multilanguage_fulltext_t1
+
+do_test fts1b-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE english MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts1b-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE spanish MATCH 'one'}
+} {}
+do_test fts1b-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE german MATCH 'one'}
+} {}
+do_test fts1b-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts1b-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one dos drei'}
+} {7 15 23 31}
+do_test fts1b-1.6 {
+ execsql {SELECT english, spanish, german FROM t1 WHERE rowid=1}
+} {one un eine}
+do_test fts1b-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"one un"'}
+} {}
+
+do_test fts1b-2.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t2 USING fts1(from,to);
+ INSERT INTO t2([from],[to]) VALUES ('one two three', 'four five six');
+ SELECT [from], [to] FROM t2
+ }
+} {{one two three} {four five six}}
+
+
+# Compute an SQL string that contains the words one, two, three,... to
+# describe bits set in the value $i. Only the lower 5 bits are examined.
+#
+proc wordset {i} {
+ set x {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend x [lindex {one two three four five} $j]}
+ }
+ return '$x'
+}
+
+# Create a new FTS table with three columns:
+#
+# norm: words for the bits of rowid
+# plusone: words for the bits of rowid+1
+# invert: words for the bits of ~rowid
+#
+db eval {
+ CREATE VIRTUAL TABLE t4 USING fts1([norm],'plusone',"invert");
+}
+for {set i 1} {$i<=15} {incr i} {
+ set vset [list [wordset $i] [wordset [expr {$i+1}]] [wordset [expr {~$i}]]]
+ db eval "INSERT INTO t4(norm,plusone,invert) VALUES([join $vset ,]);"
+}
+
+do_test fts1b-4.1 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts1b-4.2 {
+ execsql {SELECT rowid FROM t4 WHERE norm MATCH 'one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts1b-4.3 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'one'}
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
+do_test fts1b-4.4 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:one'}
+} {2 4 6 8 10 12 14}
+do_test fts1b-4.5 {
+ execsql {SELECT rowid FROM t4 WHERE plusone MATCH 'one'}
+} {2 4 6 8 10 12 14}
+do_test fts1b-4.6 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one plusone:two'}
+} {1 5 9 13}
+do_test fts1b-4.7 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one two'}
+} {1 3 5 7 9 11 13 15}
+do_test fts1b-4.8 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:two norm:one'}
+} {1 5 9 13}
+do_test fts1b-4.9 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'two norm:one'}
+} {1 3 5 7 9 11 13 15}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts1c.test b/third_party/sqlite/test/fts1c.test
new file mode 100755
index 0000000..a124695
--- /dev/null
+++ b/third_party/sqlite/test/fts1c.test
@@ -0,0 +1,1213 @@
+# 2006 September 14
+#
+# 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 testing the FTS1 module.
+#
+# $Id: fts1c.test,v 1.11 2006/10/04 17:35:28 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Create a table of sample email data. The data comes from email
+# archives of Enron executives that was published as part of the
+# litigation against that company.
+#
+do_test fts1c-1.1 {
+ db eval {
+ CREATE VIRTUAL TABLE email USING fts1([from],[to],subject,body);
+ BEGIN TRANSACTION;
+INSERT INTO email([from],[to],subject,body) VALUES('savita.puthigai@enron.com', 'traders.eol@enron.com, traders.eol@enron.com', 'EnronOnline- Change to Autohedge', 'Effective Monday, October 22, 2001 the following changes will be made to the Autohedge functionality on EnronOnline.
+
+The volume on the hedge will now respect the minimum volume and volume increment settings on the parent product. See rules below:
+
+? If the transaction volume on the child is less than half of the parent''s minimum volume no hedge will occur.
+? If the transaction volume on the child is more than half the parent''s minimum volume but less than half the volume increment on the parent, the hedge will volume will be the parent''s minimum volume.
+? For all other volumes, the same rounding rules will apply based on the volume increment on the parent product.
+
+Please see example below:
+
+Parent''s Settings:
+Minimum: 5000
+Increment: 1000
+
+Volume on Autohedge transaction Volume Hedged
+1 - 2499 0
+2500 - 5499 5000
+5500 - 6499 6000');
+INSERT INTO email([from],[to],subject,body) VALUES('dana.davis@enron.com', 'laynie.east@enron.com, lisa.king@enron.com, lisa.best@enron.com,', 'Leaving Early', 'FYI:
+If it''s ok with everyone''s needs, I would like to leave @4pm. If you think
+you will need my assistance past the 4 o''clock hour just let me know; I''ll
+be more than willing to stay.');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'louise.kitchen@enron.com', '<<Concur Expense Document>> - CC02.06.02', 'The following expense report is ready for approval:
+
+Employee Name: Christopher F. Calger
+Status last changed by: Mollie E. Gustafson Ms
+Expense Report Name: CC02.06.02
+Report Total: $3,972.93
+Amount Due Employee: $3,972.93
+
+
+To approve this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('jeff.duff@enron.com', 'julie.johnson@enron.com', 'Work request', 'Julie,
+
+Could you print off the current work request report by 1:30 today?
+
+Gentlemen,
+
+I''d like to review this today at 1:30 in our office. Also, could you provide
+me with your activity reports so I can have Julie enter this information.
+
+JD');
+INSERT INTO email([from],[to],subject,body) VALUES('v.weldon@enron.com', 'gary.l.carrier@usa.dupont.com, scott.joyce@bankofamerica.com', 'Enron News', 'This could turn into something big....
+http://biz.yahoo.com/rf/010129/n29305829.html');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.haedicke@enron.com', 'paul.simons@enron.com', 'Re: First Polish Deal!', 'Congrats! Things seem to be building rapidly now on the Continent. Mark');
+INSERT INTO email([from],[to],subject,body) VALUES('e..carter@enron.com', 't..robinson@enron.com', 'FW: Producers Newsletter 9-24-2001', '
+The producer lumber pricing sheet.
+ -----Original Message-----
+From: Johnson, Jay
+Sent: Tuesday, October 16, 2001 3:42 PM
+To: Carter, Karen E.
+Subject: FW: Producers Newsletter 9-24-2001
+
+
+
+ -----Original Message-----
+From: Daigre, Sergai
+Sent: Friday, September 21, 2001 8:33 PM
+Subject: Producers Newsletter 9-24-2001
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('david.delainey@enron.com', 'kenneth.lay@enron.com', 'Greater Houston Partnership', 'Ken, in response to the letter from Mr Miguel San Juan, my suggestion would
+be to offer up the Falcon for their use; however, given the tight time frame
+and your recent visit with Mr. Fox that it would be difficult for either you
+or me to participate.
+
+I spoke to Max and he agrees with this approach.
+
+I hope this meets with your approval.
+
+Regards
+Delainey');
+INSERT INTO email([from],[to],subject,body) VALUES('lachandra.fenceroy@enron.com', 'lindy.donoho@enron.com', 'FW: Bus Applications Meeting Follow Up', 'Lindy,
+
+Here is the original memo we discussed earlier. Please provide any information that you may have.
+
+Your cooperation is greatly appreciated.
+
+Thanks,
+
+lachandra.fenceroy@enron.com
+713.853.3884
+877.498.3401 Pager
+
+ -----Original Message-----
+From: Bisbee, Joanne
+Sent: Wednesday, September 26, 2001 7:50 AM
+To: Fenceroy, LaChandra
+Subject: FW: Bus Applications Meeting Follow Up
+
+Lachandra, Please get with David Duff today and see what this is about. Who are our TW accounting business users?
+
+ -----Original Message-----
+From: Koh, Wendy
+Sent: Tuesday, September 25, 2001 2:41 PM
+To: Bisbee, Joanne
+Subject: Bus Applications Meeting Follow Up
+
+Lisa brought up a TW change effective Nov 1. It involves eliminating a turnback surcharge. I have no other information, but you might check with the business folks for any system changes required.
+
+Wendy');
+INSERT INTO email([from],[to],subject,body) VALUES('danny.mccarty@enron.com', 'fran.fagan@enron.com', 'RE: worksheets', 'Fran,
+ If Julie''s merit needs to be lump sum, just move it over to that column. Also, send me Eric Gadd''s sheets as well. Thanks.
+Dan
+
+ -----Original Message-----
+From: Fagan, Fran
+Sent: Thursday, December 20, 2001 11:10 AM
+To: McCarty, Danny
+Subject: worksheets
+
+As discussed, attached are your sheets for bonus and merit.
+
+Thanks,
+
+Fran Fagan
+Sr. HR Rep
+713.853.5219
+
+
+ << File: McCartyMerit.xls >> << File: mccartyBonusCommercial_UnP.xls >>
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('bert.meyers@enron.com', 'shift.dl-portland@enron.com', 'OCTOBER SCHEDULE', 'TEAM,
+
+PLEASE SEND ME ANY REQUESTS THAT YOU HAVE FOR OCTOBER. SO FAR I HAVE THEM FOR LEAF. I WOULD LIKE TO HAVE IT DONE BY THE 15TH OF THE MONTH. ANY QUESTIONS PLEASE GIVE ME A CALL.
+
+BERT');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'john.arnold@enron.com, bilal.bajwa@enron.com, john.griffith@enron.com,', 'TRV Notification: (NG - PROPT P/L - 09/27/2001)', 'The report named: NG - PROPT P/L <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=11&report_name=NG+-+PROPT+P/L&category_cd=5&category_name=FINANCIAL&toc_hide=1&sTV1=5&TV1Exp=Y&current_efct_date=09/27/2001>, published as of 09/27/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('patrice.mims@enron.com', 'calvin.eakins@enron.com', 'Re: Small business supply assistance', 'Hi Calvin
+
+
+I spoke with Rickey (boy, is he long-winded!!). Gave him the name of our
+credit guy, Russell Diamond.
+
+Thank for your help!');
+INSERT INTO email([from],[to],subject,body) VALUES('legal <.hall@enron.com>', 'stephanie.panus@enron.com', 'Termination update', 'City of Vernon and Salt River Project terminated their contracts. I will fax these notices to you.');
+INSERT INTO email([from],[to],subject,body) VALUES('d..steffes@enron.com', 'richard.shapiro@enron.com', 'EES / ENA Government Affairs Staffing & Outside Services', 'Rick --
+
+Here is the information on staffing and outside services. Call if you need anything else.
+
+Jim
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('gelliott@industrialinfo.com', 'pcopello@industrialinfo.com', 'ECAAR (Gavin), WSCC (Diablo Canyon), & NPCC (Seabrook)', 'Dear Power Outage Database Customer,
+Attached you will find an excel document. The outages contained within are forced or rescheduled outages. Your daily delivery will still contain these outages.
+In addition to the two excel documents, there is a dbf file that is formatted like your daily deliveries you receive nightly. This will enable you to load the data into your regular database. Any questions please let me know. Thanks.
+Greg Elliott
+IIR, Inc.
+713-783-5147 x 3481
+outages@industrialinfo.com
+THE INFORMATION CONTAINED IN THIS E-MAIL IS LEGALLY PRIVILEGED AND CONFIDENTIAL INFORMATION INTENDED ONLY FOR THE USE OF THE INDIVIDUAL OR ENTITY NAMED ABOVE. YOU ARE HEREBY NOTIFIED THAT ANY DISSEMINATION, DISTRIBUTION, OR COPY OF THIS E-MAIL TO UNAUTHORIZED ENTITIES IS STRICTLY PROHIBITED. IF YOU HAVE RECEIVED THIS
+E-MAIL IN ERROR, PLEASE DELETE IT.
+ - OUTAGE.dbf
+ - 111201R.xls
+ - 111201.xls ');
+INSERT INTO email([from],[to],subject,body) VALUES('enron.announcements@enron.com', 'all_ena_egm_eim@enron.com', 'EWS Brown Bag', 'MARK YOUR LUNCH CALENDARS NOW !
+
+You are invited to attend the EWS Brown Bag Lunch Series
+
+Featuring: RAY BOWEN, COO
+
+Topic: Enron Industrial Markets
+
+Thursday, March 15, 2001
+11:30 am - 12:30 pm
+EB 5 C2
+
+
+You bring your lunch, Limited Seating
+We provide drinks and dessert. RSVP x 3-9610');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.germany@enron.com', 'ingrid.immer@williams.com', 'Re: About St Pauls', 'Sounds good to me. I bet this is next to the Warick?? Hotel.
+
+
+
+
+"Immer, Ingrid" <Ingrid.Immer@Williams.com> on 12/21/2000 11:48:47 AM
+To: "''chris.germany@enron.com''" <chris.germany@enron.com>
+cc:
+Subject: About St Pauls
+
+
+
+
+ <<About St Pauls.url>>
+?
+?http://www.stpaulshouston.org/about.html
+
+Chris,
+
+I like the looks of this place.? What do you think about going here Christmas
+eve?? They have an 11:00 a.m. service and a candlelight service at 5:00 p.m.,
+among others.
+
+Let me know.?? ii
+
+ - About St Pauls.url
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('nas@cpuc.ca.gov', 'skatz@sempratrading.com, kmccrea@sablaw.com, thompson@wrightlaw.com,', 'Reply Brief filed July 31, 2000', ' - CPUC01-#76371-v1-Revised_Reply_Brief__Due_today_7_31_.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('gascontrol@aglresources.com', 'dscott4@enron.com, lcampbel@enron.com', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder
+As discussed in the Winter Operations Meeting on Sept.29,2000,
+E-Gas(Emergency Gas) will not be offered this winter as a service from AGLC.
+Marketers and Poolers can receive gas via Peaking and IBSS nominations(daisy
+chain) from other marketers up to the 6 p.m. Same Day 2 nomination cycle.
+');
+INSERT INTO email([from],[to],subject,body) VALUES('dutch.quigley@enron.com', 'rwolkwitz@powermerchants.com', '', '
+
+Here is a goody for you');
+INSERT INTO email([from],[to],subject,body) VALUES('ryan.o''rourke@enron.com', 'k..allen@enron.com, randy.bhatia@enron.com, frank.ermis@enron.com,', 'TRV Notification: (West VaR - 11/07/2001)', 'The report named: West VaR <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=36&report_name=West+VaR&category_cd=2&category_name=WEST&toc_hide=1&sTV1=2&TV1Exp=Y&current_efct_date=11/07/2001>, published as of 11/07/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('mjones7@txu.com', 'cstone1@txu.com, ggreen2@txu.com, timpowell@txu.com,', 'Enron / HPL Actuals for July 10, 2000', 'Teco Tap 10.000 / Enron ; 110.000 / HPL IFERC
+
+LS HPL LSK IC 30.000 / Enron
+');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.pereira@enron.com', 'kkw816@aol.com', 'soccer practice', 'Kathy-
+
+Is it safe to assume that practice is cancelled for tonight??
+
+Susan Pereira');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.whitt@enron.com', 'barry.tycholiz@enron.com', 'Huber Internal Memo', 'Please look at this. I didn''t know how deep to go with the desk. Do you think this works.
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('m..forney@enron.com', 'george.phillips@enron.com', '', 'George,
+Give me a call and we will further discuss opportunities on the 13st floor.
+
+Thanks,
+JMForney
+3-7160');
+INSERT INTO email([from],[to],subject,body) VALUES('brad.mckay@enron.com', 'angusmcka@aol.com', 'Re: (no subject)', 'not yet');
+INSERT INTO email([from],[to],subject,body) VALUES('adam.bayer@enron.com', 'jonathan.mckay@enron.com', 'FW: Curve Fetch File', 'Here is the curve fetch file sent to me. It has plenty of points in it. If you give me a list of which ones you need we may be able to construct a secondary worksheet to vlookup the values.
+
+adam
+35227
+
+
+ -----Original Message-----
+From: Royed, Jeff
+Sent: Tuesday, September 25, 2001 11:37 AM
+To: Bayer, Adam
+Subject: Curve Fetch File
+
+Let me know if it works. It may be required to have a certain version of Oracle for it to work properly.
+
+
+
+Jeff Royed
+Enron
+Energy Operations
+Phone: 713-853-5295');
+INSERT INTO email([from],[to],subject,body) VALUES('matt.smith@enron.com', 'yan.wang@enron.com', 'Report Formats', 'Yan,
+
+The merged reports look great. I believe the only orientation changes are to
+"unmerge" the following six reports:
+
+31 Keystone Receipts
+15 Questar Pipeline
+40 Rockies Production
+22 West_2
+23 West_3
+25 CIG_WIC
+
+The orientation of the individual reports should be correct. Thanks.
+
+Mat
+
+PS. Just a reminder to add the "*" by the title of calculated points.');
+INSERT INTO email([from],[to],subject,body) VALUES('michelle.lokay@enron.com', 'jimboman@bigfoot.com', 'Egyptian Festival', '---------------------- Forwarded by Michelle Lokay/ET&S/Enron on 09/07/2000
+10:08 AM ---------------------------
+
+
+"Karkour, Randa" <Randa.Karkour@COMPAQ.com> on 09/07/2000 09:01:04 AM
+To: "''Agheb (E-mail)" <Agheb@aol.com>, "Leila Mankarious (E-mail)"
+<Leila_Mankarious@mhhs.org>, "''Marymankarious (E-mail)"
+<marymankarious@aol.com>, "Michelle lokay (E-mail)" <mlokay@enron.com>, "Ramy
+Mankarious (E-mail)" <Mankarious@aol.com>
+cc:
+
+Subject: Egyptian Festival
+
+
+ <<Egyptian Festival.url>>
+
+ http://www.egyptianfestival.com/
+
+ - Egyptian Festival.url
+');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'sherry.dawson@enron.com', 'Urgent!!! --- New EAST books', 'This has to be done..................................
+
+Thanks
+---------------------- Forwarded by Errol McLaughlin/Corp/Enron on 12/20/2000
+08:39 AM ---------------------------
+
+
+
+ From: William Kelly @ ECT 12/20/2000 08:31 AM
+
+
+To: Kam Keiser/HOU/ECT@ECT, Darron C Giron/HOU/ECT@ECT, David
+Baumbach/HOU/ECT@ECT, Errol McLaughlin/Corp/Enron@ENRON
+cc: Kimat Singla/HOU/ECT@ECT, Kulvinder Fowler/NA/Enron@ENRON, Kyle R
+Lilly/HOU/ECT@ECT, Jeff Royed/Corp/Enron@ENRON, Alejandra
+Chavez/NA/Enron@ENRON, Crystal Hyde/HOU/ECT@ECT
+
+Subject: New EAST books
+
+We have new book names in TAGG for our intramonth portfolios and it is
+extremely important that any deal booked to the East is communicated quickly
+to someone on my team. I know it will take some time for the new names to
+sink in and I do not want us to miss any positions or P&L.
+
+Thanks for your help on this.
+
+New:
+Scott Neal : East Northeast
+Dick Jenkins: East Marketeast
+
+WK
+');
+INSERT INTO email([from],[to],subject,body) VALUES('david.forster@enron.com', 'eol.wide@enron.com', 'Change to Stack Manager', 'Effective immediately, there is a change to the Stack Manager which will
+affect any Inactive Child.
+
+An inactive Child with links to Parent products will not have their
+calculated prices updated until the Child product is Activated.
+
+When the Child Product is activated, the price will be recalculated and
+updated BEFORE it is displayed on the web.
+
+This means that if you are inputting a basis price on a Child product, you
+will not see the final, calculated price until you Activate the product, at
+which time the customer will also see it.
+
+If you have any questions, please contact the Help Desk on:
+
+Americas: 713 853 4357
+Europe: + 44 (0) 20 7783 7783
+Asia/Australia: +61 2 9229 2300
+
+Dave');
+INSERT INTO email([from],[to],subject,body) VALUES('vince.kaminski@enron.com', 'jhh1@email.msn.com', 'Re: Light reading - see pieces beginning on page 7', 'John,
+
+I saw it. Very interesting.
+
+Vince
+
+
+
+
+
+"John H Herbert" <jhh1@email.msn.com> on 07/28/2000 08:38:08 AM
+To: "Vince J Kaminski" <Vince_J_Kaminski@enron.com>
+cc:
+Subject: Light reading - see pieces beginning on page 7
+
+
+Cheers and have a nice weekend,
+
+
+JHHerbert
+
+
+
+
+ - gd000728.pdf
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('matthew.lenhart@enron.com', 'mmmarcantel@equiva.com', 'RE:', 'i will try to line up a pig for you ');
+INSERT INTO email([from],[to],subject,body) VALUES('jae.black@enron.com', 'claudette.harvey@enron.com, chaun.roberts@enron.com, judy.martinez@enron.com,', 'Disaster Recovery Equipment', 'As a reminder...there are several pieces of equipment that are set up on the 30th Floor, as well as on our floor, for the Disaster Recovery Team. PLEASE DO NOT TAKE, BORROW OR USE this equipment. Should you need to use another computer system, other than yours, or make conference calls please work with your Assistant to help find or set up equipment for you to use.
+
+Thanks for your understanding in this matter.
+
+T.Jae Black
+East Power Trading
+Assistant to Kevin Presto
+off. 713-853-5800
+fax 713-646-8272
+cell 713-539-4760');
+INSERT INTO email([from],[to],subject,body) VALUES('eric.bass@enron.com', 'dale.neuner@enron.com', '5 X 24', 'Dale,
+
+Have you heard anything more on the 5 X 24s? We would like to get this
+product out ASAP.
+
+
+Thanks,
+
+Eric');
+INSERT INTO email([from],[to],subject,body) VALUES('messenger@smartreminders.com', 'm..tholt@enron.com', '10% Coupon - PrintPal Printer Cartridges - 100% Guaranteed', '[IMAGE]
+[IMAGE][IMAGE][IMAGE]
+Dear SmartReminders Member,
+ [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+We respect your privacy and are a Certified Participant of the BBBOnLine
+ Privacy Program. To be removed from future offers,click here.
+SmartReminders.com is a permission based service. To unsubscribe click here . ');
+INSERT INTO email([from],[to],subject,body) VALUES('benjamin.rogers@enron.com', 'mark.bernstein@enron.com', '', 'The guy you are talking about left CIN under a "cloud of suspicion" sort of
+speak. He was the one who got into several bad deals and PPA''s in California
+for CIN, thus he left on a bad note. Let me know if you need more detail
+than that, I felt this was the type of info you were looking for. Thanks!
+Ben');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'michelle.cash@enron.com', 'Expense Report Receipts Not Received', 'Employee Name: Michelle Cash
+Report Name: Houston Cellular 8-11-01
+Report Date: 12/13/01
+Report ID: 594D37C9ED2111D5B452
+Submitted On: 12/13/01
+
+You are only allowed 2 reports with receipts outstanding. Your expense reports will not be paid until you meet this requirement.');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.mara@enron.com', 'ray.alvarez@enron.com, mark.palmer@enron.com, karen.denne@enron.com,', 'CAISO Emergency Motion -- to discontinue market-based rates for', 'FYI. the latest broadside against the generators.
+
+Sue Mara
+Enron Corp.
+Tel: (415) 782-7802
+Fax:(415) 782-7854
+----- Forwarded by Susan J Mara/NA/Enron on 06/08/2001 12:24 PM -----
+
+
+ "Milner, Marcie" <MMilner@coral-energy.com> 06/08/2001 11:13 AM To: "''smara@enron.com''" <smara@enron.com> cc: Subject: CAISO Emergency Motion
+
+
+Sue, did you see this emergency motion the CAISO filed today? Apparently
+they are requesting that FERC discontinue market-based rates immediately and
+grant refunds plus interest on the difference between cost-based rates and
+market revenues received back to May 2000. They are requesting the
+commission act within 14 days. Have you heard anything about what they are
+doing?
+
+Marcie
+
+http://www.caiso.com/docs/2001/06/08/200106081005526469.pdf
+');
+INSERT INTO email([from],[to],subject,body) VALUES('fletcher.sturm@enron.com', 'eloy.escobar@enron.com', 'Re: General Brinks Position Meeting', 'Eloy,
+
+Who is General Brinks?
+
+Fletch');
+INSERT INTO email([from],[to],subject,body) VALUES('nailia.dindarova@enron.com', 'richard.shapiro@enron.com', 'Documents for Mark Frevert (on EU developments and lessons from', 'Rick,
+
+Here are the documents that Peter has prepared for Mark Frevert.
+
+Nailia
+---------------------- Forwarded by Nailia Dindarova/LON/ECT on 25/06/2001
+16:36 ---------------------------
+
+
+Nailia Dindarova
+25/06/2001 15:36
+To: Michael Brown/Enron@EUEnronXGate
+cc: Ross Sankey/Enron@EUEnronXGate, Eric Shaw/ENRON@EUEnronXGate, Peter
+Styles/LON/ECT@ECT
+
+Subject: Documents for Mark Frevert (on EU developments and lessons from
+California)
+
+Michael,
+
+
+These are the documents that Peter promised to give to you for Mark Frevert.
+He has now handed them to him in person but asked me to transmit them
+electronically to you, as well as Eric and Ross.
+
+Nailia
+
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('peggy.a.kostial@accenture.com', 'dave.samuels@enron.com', 'EOL-Accenture Deal Sheet', 'Dave -
+
+Attached are our comments and suggested changes. Please call to review.
+
+On the time line for completion, we have four critical steps to complete:
+ Finalize market analysis to refine business case, specifically
+ projected revenue stream
+ Complete counterparty surveying, including targeting 3 CPs for letters
+ of intent
+ Review Enron asset base for potential reuse/ licensing
+ Contract negotiations
+
+Joe will come back to us with an updated time line, but it is my
+expectation that we are still on the same schedule (we just begun week
+three) with possibly a week or so slippage.....contract negotiations will
+probably be the critical path.
+
+We will send our cut at the actual time line here shortly. Thanks,
+
+Peggy
+
+(See attached file: accenture-dealpoints v2.doc)
+ - accenture-dealpoints v2.doc ');
+INSERT INTO email([from],[to],subject,body) VALUES('thomas.martin@enron.com', 'thomas.martin@enron.com', 'Re: Guadalupe Power Partners LP', '---------------------- Forwarded by Thomas A Martin/HOU/ECT on 03/20/2001
+03:49 PM ---------------------------
+
+
+Thomas A Martin
+10/11/2000 03:55 PM
+To: Patrick Wade/HOU/ECT@ECT
+cc:
+Subject: Re: Guadalupe Power Partners LP
+
+The deal is physically served at Oasis Waha or Oasis Katy and is priced at
+either HSC, Waha or Katytailgate GD at buyers option three days prior to
+NYMEX close.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('judy.townsend@enron.com', 'dan.junek@enron.com, chris.germany@enron.com', 'Columbia Distribution''s Capacity Available for Release - Sum', '---------------------- Forwarded by Judy Townsend/HOU/ECT on 03/09/2001 11:04
+AM ---------------------------
+
+
+agoddard@nisource.com on 03/08/2001 09:16:57 AM
+To: " - *Koch, Kent" <kkoch@nisource.com>, " -
+*Millar, Debra" <dmillar@nisource.com>, " - *Burke, Lynn"
+<lburke@nisource.com>
+cc: " - *Heckathorn, Tom" <theckathorn@nisource.com>
+Subject: Columbia Distribution''s Capacity Available for Release - Sum
+
+
+Attached is Columbia Distribution''s notice of capacity available for release
+for
+the summer of 2001 (Apr. 2001 through Oct. 2001).
+
+Please note that the deadline for bids is 3:00pm EST on March 20, 2001.
+
+If you have any questions, feel free to contact any of the representatives
+listed
+at the bottom of the attachment.
+
+Aaron Goddard
+
+
+
+
+ - 2001Summer.doc
+');
+INSERT INTO email([from],[to],subject,body) VALUES('rhonda.denton@enron.com', 'tim.belden@enron.com, dana.davis@enron.com, genia.fitzgerald@enron.com,', 'Split Rock Energy LLC', 'We have received the executed EEI contract from this CP dated 12/12/2000.
+Copies will be distributed to Legal and Credit.');
+INSERT INTO email([from],[to],subject,body) VALUES('kerrymcelroy@dwt.com', 'jack.speer@alcoa.com, crow@millernash.com, michaelearly@earthlink.net,', 'Oral Argument Request', ' - Oral Argument Request.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('mike.carson@enron.com', 'rlmichaelis@hormel.com', '', 'Did you come in town this wk end..... My new number at our house is :
+713-668-3712...... my cell # is 281-381-7332
+
+the kid');
+INSERT INTO email([from],[to],subject,body) VALUES('cooper.richey@enron.com', 'trycooper@hotmail.com', 'FW: Contact Info', '
+
+-----Original Message-----
+From: Punja, Karim
+Sent: Thursday, December 13, 2001 2:35 PM
+To: Richey, Cooper
+Subject: Contact Info
+
+
+Cooper,
+
+Its been a real pleasure working with you (even though it was for only a small amount of time)
+I hope we can stay in touch.
+
+Home# 234-0249
+email: kpunja@hotmail.com
+
+Take Care,
+
+Karim.
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('bjm30@earthlink.net', 'mcguinn.k@enron.com, mcguinn.ian@enron.com, mcguinn.stephen@enron.com,', 'email address change', 'Hello all.
+
+I haven''t talked to many of you via email recently but I do want to give you
+my new address for your email file:
+
+ bjm30@earthlink.net
+
+I hope all is well.
+
+Brian McGuinn');
+INSERT INTO email([from],[to],subject,body) VALUES('shelley.corman@enron.com', 'steve.hotte@enron.com', 'Flat Panels', 'Can you please advise what is going on with the flat panels that we had planned to distribute to our gas logistics team. It was in the budget and we had the okay, but now I''m hearing there is some hold-up & the units are stored on 44.
+
+Shelley');
+INSERT INTO email([from],[to],subject,body) VALUES('sara.davidson@enron.com', 'john.schwartzenburg@enron.com, scott.dieball@enron.com, recipients@enron.com,', '2001 Enron Law Conference (Distribution List 2)', ' Enron Law Conference
+
+San Antonio, Texas May 2-4, 2001 Westin Riverwalk
+
+ See attached memo for more details!!
+
+
+? Registration for the law conference this year will be handled through an
+Online RSVP Form on the Enron Law Conference Website at
+http://lawconference.corp.enron.com. The website is still under construction
+and will not be available until Thursday, March 15, 2001.
+
+? We will send you another e-mail to confirm when the Law Conference Website
+is operational.
+
+? Please complete the Online RSVP Form as soon as it is available and submit
+it no later than Friday, March 30th.
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('tori.kuykendall@enron.com', 'heath.b.taylor@accenture.com', 'Re:', 'hey - thats funny about john - he definitely remembers him - i''ll call pat
+and let him know - we are coming on saturday - i just havent had a chance to
+call you guys back -- looking forward to it -- i probably need the
+directions again though');
+INSERT INTO email([from],[to],subject,body) VALUES('darron.giron@enron.com', 'bryce.baxter@enron.com', 'Re: Feedback for Audrey Cook', 'Bryce,
+
+I''ll get it done today.
+
+DG 3-9573
+
+
+
+
+
+ From: Bryce Baxter 06/12/2000 07:15 PM
+
+
+To: Darron C Giron/HOU/ECT@ECT
+cc:
+Subject: Feedback for Audrey Cook
+
+You were identified as a reviewer for Audrey Cook. If possible, could you
+complete her feedback by end of business Wednesday? It will really help me
+in the PRC process to have your input. Thanks.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('casey.evans@enron.com', 'stephanie.sever@enron.com', 'Gas EOL ID', 'Stephanie,
+
+In conjunction with the recent movement of several power traders, they are changing the names of their gas books as well. The names of the new gas books and traders are as follows:
+
+PWR-NG-LT-SPP: Mike Carson
+PWR-NG-LT-SERC: Jeff King
+
+If you need to know their power desk to map their ID to their gas books, those desks are as follows:
+
+EPMI-LT-SPP: Mike Carson
+EPMI-LT-SERC: Jeff King
+
+I will be in training this afternoon, but will be back when class is over. Let me know if you have any questions.
+
+Thanks for your help!
+Casey');
+INSERT INTO email([from],[to],subject,body) VALUES('darrell.schoolcraft@enron.com', 'david.roensch@enron.com, kimberly.watson@enron.com, michelle.lokay@enron.com,', 'Postings', 'Please see the attached.
+
+
+ds
+
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('mcominsky@aol.com', 'cpatman@bracepatt.com, james_derrick@enron.com', 'Jurisprudence Luncheon', 'Carrin & Jim --
+
+It was an honor and a pleasure to meet both of you yesterday. I know we will
+have fun working together on this very special event.
+
+Jeff left the jurisprudence luncheon lists for me before he left on vacation.
+ I wasn''t sure whether he transmitted them to you as well. Would you please
+advise me if you would like them sent to you? I can email the MS Excel files
+or I can fax the hard copies to you. Please advise what is most convenient.
+
+I plan to be in town through the holidays and can be reached by phone, email,
+or cell phone at any time. My cell phone number is 713/705-4829.
+
+Thanks again for your interest in the ADL''s work. Martin.
+
+Martin B. Cominsky
+Director, Southwest Region
+Anti-Defamation League
+713/627-3490, ext. 122
+713/627-2011 (fax)
+MCominsky@aol.com');
+INSERT INTO email([from],[to],subject,body) VALUES('phillip.love@enron.com', 'todagost@utmb.edu, gbsonnta@utmb.edu', 'New President', 'I had a little bird put a word in my ear. Is there any possibility for Ben
+Raimer to be Bush''s secretary of HHS? Just curious about that infamous UTMB
+rumor mill. Hope things are well, happy holidays.
+PL');
+INSERT INTO email([from],[to],subject,body) VALUES('marie.heard@enron.com', 'ehamilton@fna.com', 'ISDA Master Agreement', 'Erin:
+
+Pursuant to your request, attached are the Schedule to the ISDA Master Agreement, together with Paragraph 13 to the ISDA Credit Support Annex. Please let me know if you need anything else. We look forward to hearing your comments.
+
+Marie
+
+Marie Heard
+Senior Legal Specialist
+Enron North America Corp.
+Phone: (713) 853-3907
+Fax: (713) 646-3490
+marie.heard@enron.com
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('andrea.ring@enron.com', 'beverly.beaty@enron.com', 'Re: Tennessee Buy - Louis Dreyfus', 'Beverly - once again thanks so much for your help on this.
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('karolyn.criado@enron.com', 'j..bonin@enron.com, felicia.case@enron.com, b..clapp@enron.com,', 'Price List week of Oct. 8-9, 2001', '
+Please contact me if you have any questions regarding last weeks prices.
+
+Thank you,
+Karolyn Criado
+3-9441
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.presto@enron.com', 'edward.baughman@enron.com, billy.braddock@enron.com', 'Associated', 'Please begin working on filling our Associated short position in 02. I would like to take this risk off the books.
+
+In addition, please find out what a buy-out of VEPCO would cost us. With Rogers transitioning to run our retail risk management, I would like to clean up our customer positions.
+
+We also need to continue to explore a JEA buy-out.
+
+Thanks.');
+INSERT INTO email([from],[to],subject,body) VALUES('stacy.dickson@enron.com', 'gregg.penman@enron.com', 'RE: Constellation TC 5-7-01', 'Gregg,
+
+I am at home with a sick baby. (Lots of fun!) I will call you about this
+tomorrow.
+
+Stacy');
+INSERT INTO email([from],[to],subject,body) VALUES('joe.quenet@enron.com', 'dfincher@utilicorp.com', '', 'hey big guy.....check this out.....
+
+ w ww.gorelieberman-2000.com/');
+INSERT INTO email([from],[to],subject,body) VALUES('k..allen@enron.com', 'jacqestc@aol.com', '', 'Jacques,
+
+I sent you a fax of Kevin Kolb''s comments on the release. The payoff on the note would be $36,248 ($36090(principal) + $158 (accrued interest)).
+This is assuming we wrap this up on Tuesday.
+
+Please email to confirm that their changes are ok so I can set up a meeting on Tuesday to reach closure.
+
+Phillip');
+INSERT INTO email([from],[to],subject,body) VALUES('kourtney.nelson@enron.com', 'mike.swerzbin@enron.com', 'Adjusted L/R Balance', 'Mike,
+
+I placed the adjusted L/R Balance on the Enronwest site. It is under the "Staff/Kourtney Nelson". There are two links:
+
+1) "Adj L_R" is the same data/format from the weekly strategy meeting.
+2) "New Gen 2001_2002" link has all of the supply side info that is used to calculate the L/R balance
+ -Please note the Data Flag column, a value of "3" indicates the project was cancelled, on hold, etc and is not included in the calc.
+
+Both of these sheets are interactive Excel spreadsheets and thus you can play around with the data as you please. Also, James Bruce is working to get his gen report on the web. That will help with your access to information on new gen.
+
+Please let me know if you have any questions or feedback,
+
+Kourtney
+
+
+
+Kourtney Nelson
+Fundamental Analysis
+Enron North America
+(503) 464-8280
+kourtney.nelson@enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('d..thomas@enron.com', 'naveed.ahmed@enron.com', 'FW: Current Enron TCC Portfolio', '
+
+-----Original Message-----
+From: Grace, Rebecca M.
+Sent: Monday, December 17, 2001 9:44 AM
+To: Thomas, Paul D.
+Cc: Cashion, Jim; Allen, Thresa A.; May, Tom
+Subject: RE: Current Enron TCC Portfolio
+
+
+Paul,
+
+I reviewed NY''s list. I agree with all of their contracts numbers and mw amounts.
+
+Call if you have any more questions.
+
+Rebecca
+
+
+
+ -----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:08 AM
+To: Grace, Rebecca M.
+Subject: FW: Current Enron TCC Portfolio
+
+ << File: enrontccs.xls >>
+Rebecca,
+Let me know if you see any differences.
+
+Paul
+X 3-0403
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:04 AM
+To: Ahmed, Naveed
+Subject: FW: Current Enron TCC Portfolio
+
+
+
+
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Thursday, December 13, 2001 10:01 AM
+To: Baughman, Edward D.
+Subject: Current Enron TCC Portfolio
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('stephanie.panus@enron.com', 'william.bradford@enron.com, debbie.brackett@enron.com,', 'Coastal Merchant Energy/El Paso Merchant Energy', 'Coastal Merchant Energy, L.P. merged with and into El Paso Merchant Energy,
+L.P., effective February 1, 2001, with the surviving entity being El Paso
+Merchant Energy, L.P. We currently have ISDA Master Agreements with both
+counterparties. Please see the attached memo regarding the existing Masters
+and let us know which agreement should be terminated.
+
+Thanks,
+Stephanie
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kam.keiser@enron.com', 'c..kenne@enron.com', 'RE: What about this too???', '
+
+ -----Original Message-----
+From: Kenne, Dawn C.
+Sent: Wednesday, February 06, 2002 11:50 AM
+To: Keiser, Kam
+Subject: What about this too???
+
+
+ << File: Netco Trader Matrix.xls >>
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.meyer@enron.com', 'joe.parks@enron.com', 'Centana', 'Talked to Chip. We do need Cash Committe approval given the netting feature of your deal, which means Batch Funding Request. Please update per my previous e-mail and forward.
+
+Thanks
+
+chris
+x31666');
+INSERT INTO email([from],[to],subject,body) VALUES('debra.perlingiere@enron.com', 'jworman@academyofhealth.com', '', 'Have a great weekend! Happy Fathers Day!
+
+
+Debra Perlingiere
+Enron North America Corp.
+1400 Smith Street, EB 3885
+Houston, Texas 77002
+dperlin@enron.com
+Phone 713-853-7658
+Fax 713-646-3490');
+INSERT INTO email([from],[to],subject,body) VALUES('outlook.team@enron.com', '', 'Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia &', 'CALENDAR ENTRY: APPOINTMENT
+
+Description:
+ Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia & Dir Rpts. - 4102
+
+Date: 1/5/2001
+Time: 9:00 AM - 10:00 AM (Central Standard Time)
+
+Chairperson: Outlook Migration Team
+
+Detailed Description:');
+INSERT INTO email([from],[to],subject,body) VALUES('diana.seifert@enron.com', 'mark.taylor@enron.com', 'Guest access Chile', 'Hello Mark,
+
+Justin Boyd told me that your can help me with questions regarding Chile.
+We got a request for guest access through MG.
+The company is called Escondida and is a subsidiary of BHP Australia.
+
+Please advise if I can set up a guest account or not.
+F.Y.I.: MG is planning to put a "in w/h Chile" contract for Copper on-line as
+soon as Enron has done the due diligence for this country.
+Thanks !
+
+
+Best regards
+
+Diana Seifert
+EOL PCG');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'mark.whitt@enron.com', '<<Concur Expense Document>> - 121001', 'The Approval status has changed on the following report:
+
+Status last changed by: Barry L. Tycholiz
+Expense Report Name: 121001
+Report Total: $198.98
+Amount Due Employee: $198.98
+Amount Approved: $198.98
+Amount Paid: $0.00
+Approval Status: Approved
+Payment Status: Pending
+
+
+To review this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.hyatt@enron.com', '', 'Technical Support', 'Outside the U.S., please refer to the list below:
+
+Australia:
+1800 678-515
+support@palm-au.com
+
+Canada:
+1905 305-6530
+support@palm.com
+
+New Zealand:
+0800 446-398
+support@palm-nz.com
+
+U.K.:
+0171 867 0108
+eurosupport@palm.3com.com
+
+Please refer to the Worldwide Customer Support card for a complete technical support contact list.');
+INSERT INTO email([from],[to],subject,body) VALUES('geoff.storey@enron.com', 'dutch.quigley@enron.com', 'RE:', 'duke contact?
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 10:14 AM
+To: Storey, Geoff
+Subject: RE:
+
+bp corp Albert LaMore 281-366-4962
+
+running the reports now
+
+
+ -----Original Message-----
+From: Storey, Geoff
+Sent: Wednesday, October 31, 2001 10:10 AM
+To: Quigley, Dutch
+Subject: RE:
+
+give me a contact over there too
+BP
+
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 9:42 AM
+To: Storey, Geoff
+Subject:
+
+Coral Jeff Whitnah 713-767-5374
+Relaint Steve McGinn 713-207-4000');
+INSERT INTO email([from],[to],subject,body) VALUES('pete.davis@enron.com', 'pete.davis@enron.com', 'Start Date: 4/22/01; HourAhead hour: 3; <CODESITE>', 'Start Date: 4/22/01; HourAhead hour: 3; No ancillary schedules awarded.
+Variances detected.
+Variances detected in Load schedule.
+
+ LOG MESSAGES:
+
+PARSING FILE -->> O:\Portland\WestDesk\California Scheduling\ISO Final
+Schedules\2001042203.txt
+
+---- Load Schedule ----
+$$$ Variance found in table tblLoads.
+ Details: (Hour: 3 / Preferred: 1.92 / Final: 1.89)
+ TRANS_TYPE: FINAL
+ LOAD_ID: PGE4
+ MKT_TYPE: 2
+ TRANS_DATE: 4/22/01
+ SC_ID: EPMI
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('john.postlethwaite@enron.com', 'john.zufferli@enron.com', 'Reference', 'John, hope things are going well up there for you. The big day is almost here for you and Jessica. I was wondering if I could use your name as a job reference if need be. I am just trying to get everything in order just in case something happens.
+
+John');
+INSERT INTO email([from],[to],subject,body) VALUES('jeffrey.shankman@enron.com', 'lschiffm@jonesday.com', 'Re:', 'I saw you called on the cell this a.m. Sorry I missed you. (I was in the
+shower). I have had a shitty week--I suspect my silence (not only to you,
+but others) after our phone call is a result of the week. I''m seeing Glen at
+11:15....talk to you');
+INSERT INTO email([from],[to],subject,body) VALUES('litebytz@enron.com', '', 'Lite Bytz RSVP', '
+This week''s Lite Bytz presentation will feature the following TOOLZ speaker:
+
+Richard McDougall
+Solaris 8
+Thursday, June 7, 2001
+
+If you have not already signed up, please RSVP via email to litebytz@enron.com by the end of the day Tuesday, June 5, 2001.
+
+*Remember: this is now a Brown Bag Event--so bring your lunch and we will provide cookies and drinks.
+
+Click below for more details.
+
+http://home.enron.com:84/messaging/litebytztoolzprint.jpg');
+ COMMIT;
+ }
+} {}
+
+###############################################################################
+# Everything above just builds an interesting test database. The actual
+# tests come after this comment.
+###############################################################################
+
+do_test fts1c-1.2 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark'
+ }
+} {6 17 25 38 40 42 73 74}
+do_test fts1c-1.3 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan'
+ }
+} {24 40}
+do_test fts1c-1.4 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark susan'
+ }
+} {40}
+do_test fts1c-1.5 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan mark'
+ }
+} {40}
+do_test fts1c-1.6 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '"mark susan"'
+ }
+} {}
+do_test fts1c-1.7 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark -susan'
+ }
+} {6 17 25 38 42 73 74}
+do_test fts1c-1.8 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '-mark susan'
+ }
+} {24}
+do_test fts1c-1.9 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark OR susan'
+ }
+} {6 17 24 25 38 40 42 73 74}
+
+# Some simple tests of the automatic "offsets(email)" column. In the sample
+# data set above, only one message, number 20, contains the words
+# "gas" and "reminder" in both body and subject.
+#
+do_test fts1c-2.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts1c-2.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 1 54 8}}
+do_test fts1c-2.3 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'body:gas reminder'
+ }
+} {20 {2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts1c-2.4 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE subject MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8}}
+do_test fts1c-2.5 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'gas reminder'
+ }
+} {20 {3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+
+# Document 32 contains 5 instances of the world "child". But only
+# 3 of them are paired with "product". Make sure only those instances
+# that match the phrase appear in the offsets(email) list.
+#
+do_test fts1c-3.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'child product' AND +rowid=32
+ }
+} {32 {3 0 94 5 3 0 114 5 3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7 3 1 493 7}}
+do_test fts1c-3.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH '"child product"'
+ }
+} {32 {3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7}}
+
+# Snippet generator tests
+#
+do_test fts1c-4.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {{Alert Posted 10:00 AM November 20,2000: E-<b>GAS</b> Request <b>Reminder</b>}}
+do_test fts1c-4.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'christmas candlelight'
+ }
+} {{<b>...</b> place.? What do you think about going here <b>Christmas</b>
+eve?? They have an 11:00 a.m. service and a <b>candlelight</b> service at 5:00 p.m.,
+among others. <b>...</b>}}
+
+do_test fts1c-4.3 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <b>Deal</b> <b>Sheet</b> <b>...</b> intent
+ Review Enron asset base for <b>potential</b> <b>reuse</b>/ licensing
+ Contract negotiations <b>...</b>}}
+do_test fts1c-4.4 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <<<Deal>>> <<<Sheet>>> intent
+ Review Enron asset base for <<<potential>>> <<<reuse>>>/ licensing
+ Contract negotiations }}
+do_test fts1c-4.5 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'first things'
+ }
+} {{Re: <<<First>>> Polish Deal! Congrats! <<<Things>>> seem to be building rapidly now on the }}
+do_test fts1c-4.6 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'chris is here'
+ }
+} {{<b>chris</b>.germany@enron.com <b>...</b> Sounds good to me. I bet this <b>is</b> next to the Warick?? Hotel. <b>...</b> place.? What do you think about going <b>here</b> Christmas
+eve?? They have an 11:00 a.m. <b>...</b>}}
+do_test fts1c-4.7 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH '"pursuant to"'
+ }
+} {{Erin:
+
+<b>Pursuant</b> <b>to</b> your request, attached are the Schedule to <b>...</b>}}
+do_test fts1c-4.8 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'ancillary load davis'
+ }
+} {{pete.<b>davis</b>@enron.com <b>...</b> Start Date: 4/22/01; HourAhead hour: 3; No <b>ancillary</b> schedules awarded.
+Variances detected.
+Variances detected in <b>Load</b> schedule.
+
+ LOG MESSAGES:
+
+PARSING <b>...</b>}}
+
+# Combinations of AND and OR operators:
+#
+do_test fts1c-5.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'questar enron OR com'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+do_test fts1c-5.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'enron OR com questar'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1d.test b/third_party/sqlite/test/fts1d.test
new file mode 100755
index 0000000..ea23034
--- /dev/null
+++ b/third_party/sqlite/test/fts1d.test
@@ -0,0 +1,65 @@
+# 2006 October 1
+#
+# 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 testing the FTS1 module, and in particular
+# the Porter stemmer.
+#
+# $Id: fts1d.test,v 1.1 2006/10/01 18:41:21 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+do_test fts1d-1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts1(content, tokenize porter);
+ INSERT INTO t1(rowid, content) VALUES(1, 'running and jumping');
+ SELECT rowid FROM t1 WHERE content MATCH 'run jump';
+ }
+} {1}
+do_test fts1d-1.2 {
+ execsql {
+ SELECT snippet(t1) FROM t1 WHERE t1 MATCH 'run jump';
+ }
+} {{<b>running</b> and <b>jumping</b>}}
+do_test fts1d-1.3 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(2, 'abcdefghijklmnopqrstuvwyxz');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts1d-1.4 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijXXXXqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts1d-1.5 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(3, 'The value is 123456789');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123789'
+ }
+} {3 {The value is <b>123456789</b>}}
+do_test fts1d-1.6 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123000000789'
+ }
+} {3 {The value is <b>123456789</b>}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts1e.test b/third_party/sqlite/test/fts1e.test
new file mode 100755
index 0000000..479cfac
--- /dev/null
+++ b/third_party/sqlite/test/fts1e.test
@@ -0,0 +1,85 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing deletions in the FTS1 module.
+#
+# $Id: fts1e.test,v 1.1 2006/10/19 23:28:35 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 30 INSERT and
+# DELETE statements, so that we'll test both the segmentMerge() merge
+# (over the first 16) and the termSelect() merge (over the level-1
+# segment and 14 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ DELETE FROM t1 WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ DELETE FROM t1 WHERE rowid = 22;
+}
+
+do_test fts1f-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {14}
+
+do_test fts1e-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {3 5 9 11 15 17 21}
+
+do_test fts1e-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 11 14 15 18}
+
+do_test fts1e-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {5 6 12 14 15 20 21}
+
+do_test fts1e-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {8 9 11 12 14 15}
+
+do_test fts1e-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {17 18 20 21}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1f.test b/third_party/sqlite/test/fts1f.test
new file mode 100755
index 0000000..19dea0a
--- /dev/null
+++ b/third_party/sqlite/test/fts1f.test
@@ -0,0 +1,90 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing updates in the FTS1 module.
+#
+# $Id: fts1f.test,v 1.2 2007/02/23 00:14:06 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 31 INSERT,
+# UPDATE, and DELETE statements, so that we'll test both the
+# segmentMerge() merge (over the first 16) and the termSelect() merge
+# (over the level-1 segment and 15 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ UPDATE t1 SET content = 'update one three' WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ UPDATE t1 SET content = 'update two five' WHERE rowid = 8;
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ UPDATE t1 SET content = 'update' WHERE rowid = 15;
+}
+
+do_test fts1f-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {16}
+
+do_test fts1f-2.0 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'update'}
+} {1 8 15}
+
+do_test fts1f-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 9 11 17 21}
+
+do_test fts1f-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 8 11 14 18 22}
+
+do_test fts1f-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {1 5 6 12 14 20 21 22}
+
+do_test fts1f-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {9 11 12 14}
+
+do_test fts1f-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {8 17 18 20 21 22}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1i.test b/third_party/sqlite/test/fts1i.test
new file mode 100755
index 0000000..803b93b
--- /dev/null
+++ b/third_party/sqlite/test/fts1i.test
@@ -0,0 +1,88 @@
+# 2007 January 17
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite fts1 library. The
+# focus here is testing handling of UPDATE when using UTF-16-encoded
+# databases.
+#
+# $Id: fts1i.test,v 1.2 2007/01/24 03:43:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+# NOTE(shess) Copied from capi3.test.
+proc utf16 {str {nt 1}} {
+ set r [encoding convertto unicode $str]
+ if {$nt} {
+ append r "\x00\x00"
+ }
+ return $r
+}
+
+db eval {
+ PRAGMA encoding = "UTF-16le";
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+}
+
+do_test fts1i-1.0 {
+ execsql {PRAGMA encoding}
+} {UTF-16le}
+
+do_test fts1i-1.1 {
+ execsql {INSERT INTO t1 (rowid, content) VALUES(1, 'one')}
+ execsql {SELECT content FROM t1 WHERE rowid = 1}
+} {one}
+
+do_test fts1i-1.2 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(2, 'two')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 2}
+} {two}
+
+do_test fts1i-1.3 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(3, 'three')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'trois' WHERE rowid = 3"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 3}
+} {trois}
+
+do_test fts1i-1.4 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(4, 'four')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 4}
+} {four}
+
+do_test fts1i-1.5 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(5, 'five')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'cinq' WHERE rowid = 5"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 5}
+} {cinq}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1j.test b/third_party/sqlite/test/fts1j.test
new file mode 100755
index 0000000..5ff0d0e
--- /dev/null
+++ b/third_party/sqlite/test/fts1j.test
@@ -0,0 +1,89 @@
+# 2007 February 6
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. This
+# tests creating fts1 tables in an attached database.
+#
+# $Id: fts1j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Clean up anything left over from a previous pass.
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts1(content);
+ INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
+}
+
+db2 eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# This has always worked because the t1_* tables used by fts1 will be
+# the defaults.
+do_test fts1j-1.1 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+# Make certain we're detached if there was an error.
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this appears to work fine, but the t2_* tables used
+# by fts1 will be created in database 'main' instead of database
+# 'two'. It appears to work fine because the tables end up being the
+# defaults, but obviously is badly broken if you hope to use things
+# other than in the exact same ATTACH setup.
+do_test fts1j-1.2 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ CREATE VIRTUAL TABLE two.t2 USING fts1(content);
+ INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this broke because the fts1 code attempted to create
+# t3_* tables in database 'main', but they already existed. Normally
+# this wouldn't happen without t3 itself existing, in which case the
+# fts1 code would never be called in the first place.
+do_test fts1j-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+
+ CREATE VIRTUAL TABLE two.t3 USING fts1(content);
+ INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
+
+ DETACH DATABASE two;
+ } db2
+} {2}
+catch {db eval {DETACH DATABASE two}}
+
+catch {db2 close}
+file delete -force test2.db
+
+finish_test
diff --git a/third_party/sqlite/test/fts1k.test b/third_party/sqlite/test/fts1k.test
new file mode 100755
index 0000000..35b94d2
--- /dev/null
+++ b/third_party/sqlite/test/fts1k.test
@@ -0,0 +1,69 @@
+# 2007 March 28
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing isspace/isalnum/tolower problems with the
+# FTS1 module. Unfortunately, this code isn't a really principled set
+# of tests, because it is impossible to know where new uses of these
+# functions might appear.
+#
+# $Id: fts1k.test,v 1.2 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Tests that startsWith() (calls isspace, tolower, isalnum) can handle
+# hi-bit chars. parseSpec() also calls isalnum here.
+do_test fts1k-1.1 {
+ execsql "CREATE VIRTUAL TABLE t1 USING fts1(content, \x80)"
+} {}
+
+# Additionally tests isspace() call in getToken(), and isalnum() call
+# in tokenListToIdList().
+do_test fts1k-1.2 {
+ catch {
+ execsql "CREATE VIRTUAL TABLE t2 USING fts1(content, tokenize \x80)"
+ }
+ sqlite3_errmsg $DB
+} "unknown tokenizer: \x80"
+
+# Additionally test final isalnum() in startsWith().
+do_test fts1k-1.3 {
+ execsql "CREATE VIRTUAL TABLE t3 USING fts1(content, tokenize\x80)"
+} {}
+
+# The snippet-generation code has calls to isspace() which are sort of
+# hard to get to. It finds convenient breakpoints by starting ~40
+# chars before and after the matched term, and scanning ~10 chars
+# around that position for isspace() characters. The long word with
+# embedded hi-bit chars causes one of these isspace() calls to be
+# exercised. The version with a couple extra spaces should cause the
+# other isspace() call to be exercised. [Both cases have been tested
+# in the debugger, but I'm hoping to continue to catch it if simple
+# constant changes change things slightly.
+#
+# The trailing and leading hi-bit chars help with code which tests for
+# isspace() to coalesce multiple spaces.
+
+set word "\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80"
+set phrase1 "$word $word $word target $word $word $word"
+set phrase2 "$word $word $word target $word $word $word"
+
+db eval {CREATE VIRTUAL TABLE t4 USING fts1(content)}
+db eval "INSERT INTO t4 (content) VALUES ('$phrase1')"
+db eval "INSERT INTO t4 (content) VALUES ('$phrase2')"
+
+do_test fts1k-1.4 {
+ execsql {SELECT rowid, length(snippet(t4)) FROM t4 WHERE t4 MATCH 'target'}
+} {1 111 2 117}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1l.test b/third_party/sqlite/test/fts1l.test
new file mode 100755
index 0000000..924be33
--- /dev/null
+++ b/third_party/sqlite/test/fts1l.test
@@ -0,0 +1,65 @@
+# 2007 April 9
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. fts1
+# DELETE handling assumed all fields were non-null. This was not
+# the intention at all.
+#
+# $Id: fts1l.test,v 1.1 2007/04/09 20:45:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(col_a, col_b);
+
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(1, 'testing', 'testing');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(2, 'only a', null);
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(3, null, 'only b');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(4, null, null);
+}
+
+do_test fts1m-1.0 {
+ execsql {
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {2 2 4}
+
+do_test fts1m-1.1 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 1;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {1 1 3}
+
+do_test fts1m-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 2;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 1 2}
+
+do_test fts1m-1.3 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 3;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 1}
+
+do_test fts1m-1.4 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 4;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 0}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1m.test b/third_party/sqlite/test/fts1m.test
new file mode 100755
index 0000000..c2f8f91
--- /dev/null
+++ b/third_party/sqlite/test/fts1m.test
@@ -0,0 +1,50 @@
+# 2007 July 27
+#
+# 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 testing the FTS1 module, specifically snippet
+# generation. Extracted from fts2o.test.
+#
+# $Id: fts1m.test,v 1.1 2007/07/25 00:25:20 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is not defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+#---------------------------------------------------------------------
+# These tests, fts1m-1.*, test that ticket #2429 is fixed.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two');
+}
+do_test fts1m-1.1 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE c MATCH 'four';
+ }
+} {1 {one <b>four</b> two}}
+do_test fts1m-1.2 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE b MATCH 'four';
+ }
+} {1 {one <b>four</b>}}
+do_test fts1m-1.3 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE a MATCH 'four';
+ }
+} {1 {one three <b>four</b>}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1n.test b/third_party/sqlite/test/fts1n.test
new file mode 100755
index 0000000..2f102b4
--- /dev/null
+++ b/third_party/sqlite/test/fts1n.test
@@ -0,0 +1,45 @@
+# 2007 July 24
+#
+# 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 testing the FTS1 module for errors in the handling
+# of SQLITE_SCHEMA.
+#
+# $Id: fts1n.test,v 1.1 2007/07/25 00:38:06 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is not defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+do_test fts1m-1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts1(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one two');
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two}}
+
+# This test was crashing at one point.
+#
+do_test fts1m-1.2 {
+ execsql {
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ CREATE TABLE t3(a, b, c);
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two} {one three four} {one four} {one two}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1o.test b/third_party/sqlite/test/fts1o.test
new file mode 100755
index 0000000..92666c6
--- /dev/null
+++ b/third_party/sqlite/test/fts1o.test
@@ -0,0 +1,138 @@
+# 2007 July 24
+#
+# 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 testing the FTS1 module rename functionality. Mostly
+# copied from fts2o.test.
+#
+# $Id: fts1o.test,v 1.2 2007/08/30 20:01:33 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is not defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two');
+}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts1 table.
+#
+do_test fts1o-1.1 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {t1 t1_content t1_term}
+do_test fts1o-1.2 {
+ execsql { ALTER TABLE t1 RENAME to fts_t1; }
+} {}
+do_test fts1o-1.3 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts1o-1.4 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_term}
+
+# See what happens when renaming the fts1 table fails.
+#
+do_test fts1o-2.1 {
+ catchsql {
+ CREATE TABLE t1_term(a, b, c);
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+do_test fts1o-2.2 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts1o-2.3 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_term t1_term}
+
+# See what happens when renaming the fts1 table fails inside a transaction.
+#
+do_test fts1o-3.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two');
+ }
+} {}
+do_test fts1o-3.2 {
+ catchsql {
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+# NOTE(shess) rowid AS rowid to defeat caching. Otherwise, this
+# seg-faults, I suspect that there's something up with a stale
+# virtual-table reference, but I'm not quite sure how it happens here
+# but not for fts2o.test.
+do_test fts1o-3.3 {
+ execsql { SELECT rowid AS rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts1o-3.4 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_term t1_term}
+do_test fts1o-3.5 {
+ execsql COMMIT
+ execsql {SELECT a FROM fts_t1}
+} {{one three four} {one two three}}
+do_test fts1o-3.6 {
+ execsql { SELECT a, b, c FROM fts_t1 WHERE c MATCH 'four'; }
+} {{one three four} {one four} {one four two}}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts1 table in an attached
+# database.
+#
+file delete -force test2.db test2.db-journal
+
+do_test fts1o-4.1 {
+ execsql {
+ DROP TABLE t1_term;
+ ALTER TABLE fts_t1 RENAME to t1;
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one four two} {one two three} {one four} {one two}}
+
+do_test fts1o-4.2 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE VIRTUAL TABLE aux.t1 USING fts1(a, b, c);
+ INSERT INTO aux.t1(a, b, c) VALUES(
+ 'neung song sahm', 'neung see', 'neung see song'
+ );
+ }
+} {}
+
+do_test fts1o-4.3 {
+ execsql { SELECT a, b, c FROM aux.t1 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts1o-4.4 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one four two} {one two three} {one four} {one two}}
+
+do_test fts1o-4.5 {
+ execsql { ALTER TABLE aux.t1 RENAME TO t2 }
+} {}
+
+do_test fts1o-4.6 {
+ execsql { SELECT a, b, c FROM t2 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts1o-4.7 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one four two} {one two three} {one four} {one two}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts1porter.test b/third_party/sqlite/test/fts1porter.test
new file mode 100755
index 0000000..0ca87a01
--- /dev/null
+++ b/third_party/sqlite/test/fts1porter.test
@@ -0,0 +1,23590 @@
+# 2006 October 1
+#
+# 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 testing the FTS1 module, and in particular
+# the Porter stemmer.
+#
+# $Id: fts1porter.test,v 1.5 2006/10/03 19:37:37 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Test data for the Porter stemmer. The first word of each line
+# is the input. The second word is the desired output.
+#
+# This test data is taken from http://www.tartarus.org/martin/PorterStemmer/
+# There is no claim of copyright made on that page, but you should
+# probably contact the author (Martin Porter - the inventor of the
+# Porter Stemmer algorithm) if you want to use this test data in a
+# commerical product of some kind. The stemmer code in FTS1 is a
+# complete rewrite from scratch based on the algorithm specification
+# and does not contain any code under copyright.
+#
+set porter_test_data {
+ a a
+ aaron aaron
+ abaissiez abaissiez
+ abandon abandon
+ abandoned abandon
+ abase abas
+ abash abash
+ abate abat
+ abated abat
+ abatement abat
+ abatements abat
+ abates abat
+ abbess abbess
+ abbey abbei
+ abbeys abbei
+ abbominable abbomin
+ abbot abbot
+ abbots abbot
+ abbreviated abbrevi
+ abed ab
+ abel abel
+ aberga aberga
+ abergavenny abergavenni
+ abet abet
+ abetting abet
+ abhominable abhomin
+ abhor abhor
+ abhorr abhorr
+ abhorred abhor
+ abhorring abhor
+ abhors abhor
+ abhorson abhorson
+ abide abid
+ abides abid
+ abilities abil
+ ability abil
+ abject abject
+ abjectly abjectli
+ abjects abject
+ abjur abjur
+ abjure abjur
+ able abl
+ abler abler
+ aboard aboard
+ abode abod
+ aboded abod
+ abodements abod
+ aboding abod
+ abominable abomin
+ abominably abomin
+ abominations abomin
+ abortive abort
+ abortives abort
+ abound abound
+ abounding abound
+ about about
+ above abov
+ abr abr
+ abraham abraham
+ abram abram
+ abreast abreast
+ abridg abridg
+ abridge abridg
+ abridged abridg
+ abridgment abridg
+ abroach abroach
+ abroad abroad
+ abrogate abrog
+ abrook abrook
+ abrupt abrupt
+ abruption abrupt
+ abruptly abruptli
+ absence absenc
+ absent absent
+ absey absei
+ absolute absolut
+ absolutely absolut
+ absolv absolv
+ absolver absolv
+ abstains abstain
+ abstemious abstemi
+ abstinence abstin
+ abstract abstract
+ absurd absurd
+ absyrtus absyrtu
+ abundance abund
+ abundant abund
+ abundantly abundantli
+ abus abu
+ abuse abus
+ abused abus
+ abuser abus
+ abuses abus
+ abusing abus
+ abutting abut
+ aby abi
+ abysm abysm
+ ac ac
+ academe academ
+ academes academ
+ accent accent
+ accents accent
+ accept accept
+ acceptable accept
+ acceptance accept
+ accepted accept
+ accepts accept
+ access access
+ accessary accessari
+ accessible access
+ accidence accid
+ accident accid
+ accidental accident
+ accidentally accident
+ accidents accid
+ accite accit
+ accited accit
+ accites accit
+ acclamations acclam
+ accommodate accommod
+ accommodated accommod
+ accommodation accommod
+ accommodations accommod
+ accommodo accommodo
+ accompanied accompani
+ accompany accompani
+ accompanying accompani
+ accomplices accomplic
+ accomplish accomplish
+ accomplished accomplish
+ accomplishing accomplish
+ accomplishment accomplish
+ accompt accompt
+ accord accord
+ accordant accord
+ accorded accord
+ accordeth accordeth
+ according accord
+ accordingly accordingli
+ accords accord
+ accost accost
+ accosted accost
+ account account
+ accountant account
+ accounted account
+ accounts account
+ accoutred accoutr
+ accoutrement accoutr
+ accoutrements accoutr
+ accrue accru
+ accumulate accumul
+ accumulated accumul
+ accumulation accumul
+ accurs accur
+ accursed accurs
+ accurst accurst
+ accus accu
+ accusation accus
+ accusations accus
+ accusative accus
+ accusativo accusativo
+ accuse accus
+ accused accus
+ accuser accus
+ accusers accus
+ accuses accus
+ accuseth accuseth
+ accusing accus
+ accustom accustom
+ accustomed accustom
+ ace ac
+ acerb acerb
+ ache ach
+ acheron acheron
+ aches ach
+ achiev achiev
+ achieve achiev
+ achieved achiev
+ achievement achiev
+ achievements achiev
+ achiever achiev
+ achieves achiev
+ achieving achiev
+ achilles achil
+ aching ach
+ achitophel achitophel
+ acknowledg acknowledg
+ acknowledge acknowledg
+ acknowledged acknowledg
+ acknowledgment acknowledg
+ acknown acknown
+ acold acold
+ aconitum aconitum
+ acordo acordo
+ acorn acorn
+ acquaint acquaint
+ acquaintance acquaint
+ acquainted acquaint
+ acquaints acquaint
+ acquir acquir
+ acquire acquir
+ acquisition acquisit
+ acquit acquit
+ acquittance acquitt
+ acquittances acquitt
+ acquitted acquit
+ acre acr
+ acres acr
+ across across
+ act act
+ actaeon actaeon
+ acted act
+ acting act
+ action action
+ actions action
+ actium actium
+ active activ
+ actively activ
+ activity activ
+ actor actor
+ actors actor
+ acts act
+ actual actual
+ acture actur
+ acute acut
+ acutely acut
+ ad ad
+ adage adag
+ adallas adalla
+ adam adam
+ adamant adam
+ add add
+ added ad
+ adder adder
+ adders adder
+ addeth addeth
+ addict addict
+ addicted addict
+ addiction addict
+ adding ad
+ addition addit
+ additions addit
+ addle addl
+ address address
+ addressing address
+ addrest addrest
+ adds add
+ adhere adher
+ adheres adher
+ adieu adieu
+ adieus adieu
+ adjacent adjac
+ adjoin adjoin
+ adjoining adjoin
+ adjourn adjourn
+ adjudg adjudg
+ adjudged adjudg
+ adjunct adjunct
+ administer administ
+ administration administr
+ admir admir
+ admirable admir
+ admiral admir
+ admiration admir
+ admire admir
+ admired admir
+ admirer admir
+ admiring admir
+ admiringly admiringli
+ admission admiss
+ admit admit
+ admits admit
+ admittance admitt
+ admitted admit
+ admitting admit
+ admonish admonish
+ admonishing admonish
+ admonishment admonish
+ admonishments admonish
+ admonition admonit
+ ado ado
+ adonis adoni
+ adopt adopt
+ adopted adopt
+ adoptedly adoptedli
+ adoption adopt
+ adoptious adopti
+ adopts adopt
+ ador ador
+ adoration ador
+ adorations ador
+ adore ador
+ adorer ador
+ adores ador
+ adorest adorest
+ adoreth adoreth
+ adoring ador
+ adorn adorn
+ adorned adorn
+ adornings adorn
+ adornment adorn
+ adorns adorn
+ adown adown
+ adramadio adramadio
+ adrian adrian
+ adriana adriana
+ adriano adriano
+ adriatic adriat
+ adsum adsum
+ adulation adul
+ adulterate adulter
+ adulterates adulter
+ adulterers adulter
+ adulteress adulteress
+ adulteries adulteri
+ adulterous adulter
+ adultery adulteri
+ adultress adultress
+ advanc advanc
+ advance advanc
+ advanced advanc
+ advancement advanc
+ advancements advanc
+ advances advanc
+ advancing advanc
+ advantage advantag
+ advantageable advantag
+ advantaged advantag
+ advantageous advantag
+ advantages advantag
+ advantaging advantag
+ advent advent
+ adventur adventur
+ adventure adventur
+ adventures adventur
+ adventuring adventur
+ adventurous adventur
+ adventurously adventur
+ adversaries adversari
+ adversary adversari
+ adverse advers
+ adversely advers
+ adversities advers
+ adversity advers
+ advertis adverti
+ advertise advertis
+ advertised advertis
+ advertisement advertis
+ advertising advertis
+ advice advic
+ advis advi
+ advise advis
+ advised advis
+ advisedly advisedli
+ advises advis
+ advisings advis
+ advocate advoc
+ advocation advoc
+ aeacida aeacida
+ aeacides aeacid
+ aedile aedil
+ aediles aedil
+ aegeon aegeon
+ aegion aegion
+ aegles aegl
+ aemelia aemelia
+ aemilia aemilia
+ aemilius aemiliu
+ aeneas aenea
+ aeolus aeolu
+ aer aer
+ aerial aerial
+ aery aeri
+ aesculapius aesculapiu
+ aeson aeson
+ aesop aesop
+ aetna aetna
+ afar afar
+ afear afear
+ afeard afeard
+ affability affabl
+ affable affabl
+ affair affair
+ affaire affair
+ affairs affair
+ affect affect
+ affectation affect
+ affectations affect
+ affected affect
+ affectedly affectedli
+ affecteth affecteth
+ affecting affect
+ affection affect
+ affectionate affection
+ affectionately affection
+ affections affect
+ affects affect
+ affeer affeer
+ affianc affianc
+ affiance affianc
+ affianced affianc
+ affied affi
+ affin affin
+ affined affin
+ affinity affin
+ affirm affirm
+ affirmation affirm
+ affirmatives affirm
+ afflict afflict
+ afflicted afflict
+ affliction afflict
+ afflictions afflict
+ afflicts afflict
+ afford afford
+ affordeth affordeth
+ affords afford
+ affray affrai
+ affright affright
+ affrighted affright
+ affrights affright
+ affront affront
+ affronted affront
+ affy affi
+ afield afield
+ afire afir
+ afloat afloat
+ afoot afoot
+ afore afor
+ aforehand aforehand
+ aforesaid aforesaid
+ afraid afraid
+ afresh afresh
+ afric afric
+ africa africa
+ african african
+ afront afront
+ after after
+ afternoon afternoon
+ afterward afterward
+ afterwards afterward
+ ag ag
+ again again
+ against against
+ agamemmon agamemmon
+ agamemnon agamemnon
+ agate agat
+ agaz agaz
+ age ag
+ aged ag
+ agenor agenor
+ agent agent
+ agents agent
+ ages ag
+ aggravate aggrav
+ aggrief aggrief
+ agile agil
+ agincourt agincourt
+ agitation agit
+ aglet aglet
+ agnize agniz
+ ago ago
+ agone agon
+ agony agoni
+ agree agre
+ agreed agre
+ agreeing agre
+ agreement agreement
+ agrees agre
+ agrippa agrippa
+ aground aground
+ ague agu
+ aguecheek aguecheek
+ agued agu
+ agueface aguefac
+ agues agu
+ ah ah
+ aha aha
+ ahungry ahungri
+ ai ai
+ aialvolio aialvolio
+ aiaria aiaria
+ aid aid
+ aidance aidanc
+ aidant aidant
+ aided aid
+ aiding aid
+ aidless aidless
+ aids aid
+ ail ail
+ aim aim
+ aimed aim
+ aimest aimest
+ aiming aim
+ aims aim
+ ainsi ainsi
+ aio aio
+ air air
+ aired air
+ airless airless
+ airs air
+ airy airi
+ ajax ajax
+ akilling akil
+ al al
+ alabaster alabast
+ alack alack
+ alacrity alacr
+ alarbus alarbu
+ alarm alarm
+ alarms alarm
+ alarum alarum
+ alarums alarum
+ alas ala
+ alb alb
+ alban alban
+ albans alban
+ albany albani
+ albeit albeit
+ albion albion
+ alchemist alchemist
+ alchemy alchemi
+ alcibiades alcibiad
+ alcides alcid
+ alder alder
+ alderman alderman
+ aldermen aldermen
+ ale al
+ alecto alecto
+ alehouse alehous
+ alehouses alehous
+ alencon alencon
+ alengon alengon
+ aleppo aleppo
+ ales al
+ alewife alewif
+ alexander alexand
+ alexanders alexand
+ alexandria alexandria
+ alexandrian alexandrian
+ alexas alexa
+ alias alia
+ alice alic
+ alien alien
+ aliena aliena
+ alight alight
+ alighted alight
+ alights alight
+ aliis alii
+ alike alik
+ alisander alisand
+ alive aliv
+ all all
+ alla alla
+ allay allai
+ allayed allai
+ allaying allai
+ allayment allay
+ allayments allay
+ allays allai
+ allegation alleg
+ allegations alleg
+ allege alleg
+ alleged alleg
+ allegiance allegi
+ allegiant allegi
+ alley allei
+ alleys allei
+ allhallowmas allhallowma
+ alliance allianc
+ allicholy allicholi
+ allied alli
+ allies alli
+ alligant allig
+ alligator allig
+ allons allon
+ allot allot
+ allots allot
+ allotted allot
+ allottery allotteri
+ allow allow
+ allowance allow
+ allowed allow
+ allowing allow
+ allows allow
+ allur allur
+ allure allur
+ allurement allur
+ alluring allur
+ allusion allus
+ ally alli
+ allycholly allycholli
+ almain almain
+ almanac almanac
+ almanack almanack
+ almanacs almanac
+ almighty almighti
+ almond almond
+ almost almost
+ alms alm
+ almsman almsman
+ aloes alo
+ aloft aloft
+ alone alon
+ along along
+ alonso alonso
+ aloof aloof
+ aloud aloud
+ alphabet alphabet
+ alphabetical alphabet
+ alphonso alphonso
+ alps alp
+ already alreadi
+ also also
+ alt alt
+ altar altar
+ altars altar
+ alter alter
+ alteration alter
+ altered alter
+ alters alter
+ althaea althaea
+ although although
+ altitude altitud
+ altogether altogeth
+ alton alton
+ alway alwai
+ always alwai
+ am am
+ amaimon amaimon
+ amain amain
+ amaking amak
+ amamon amamon
+ amaz amaz
+ amaze amaz
+ amazed amaz
+ amazedly amazedli
+ amazedness amazed
+ amazement amaz
+ amazes amaz
+ amazeth amazeth
+ amazing amaz
+ amazon amazon
+ amazonian amazonian
+ amazons amazon
+ ambassador ambassador
+ ambassadors ambassador
+ amber amber
+ ambiguides ambiguid
+ ambiguities ambigu
+ ambiguous ambigu
+ ambition ambit
+ ambitions ambit
+ ambitious ambiti
+ ambitiously ambiti
+ amble ambl
+ ambled ambl
+ ambles ambl
+ ambling ambl
+ ambo ambo
+ ambuscadoes ambuscado
+ ambush ambush
+ amen amen
+ amend amend
+ amended amend
+ amendment amend
+ amends amend
+ amerce amerc
+ america america
+ ames am
+ amiable amiabl
+ amid amid
+ amidst amidst
+ amiens amien
+ amis ami
+ amiss amiss
+ amities amiti
+ amity amiti
+ amnipotent amnipot
+ among among
+ amongst amongst
+ amorous amor
+ amorously amor
+ amort amort
+ amount amount
+ amounts amount
+ amour amour
+ amphimacus amphimacu
+ ample ampl
+ ampler ampler
+ amplest amplest
+ amplified amplifi
+ amplify amplifi
+ amply ampli
+ ampthill ampthil
+ amurath amurath
+ amyntas amynta
+ an an
+ anatomiz anatomiz
+ anatomize anatom
+ anatomy anatomi
+ ancestor ancestor
+ ancestors ancestor
+ ancestry ancestri
+ anchises anchis
+ anchor anchor
+ anchorage anchorag
+ anchored anchor
+ anchoring anchor
+ anchors anchor
+ anchovies anchovi
+ ancient ancient
+ ancientry ancientri
+ ancients ancient
+ ancus ancu
+ and and
+ andirons andiron
+ andpholus andpholu
+ andren andren
+ andrew andrew
+ andromache andromach
+ andronici andronici
+ andronicus andronicu
+ anew anew
+ ang ang
+ angel angel
+ angelica angelica
+ angelical angel
+ angelo angelo
+ angels angel
+ anger anger
+ angerly angerli
+ angers anger
+ anges ang
+ angiers angier
+ angl angl
+ anglais anglai
+ angle angl
+ angler angler
+ angleterre angleterr
+ angliae anglia
+ angling angl
+ anglish anglish
+ angrily angrili
+ angry angri
+ anguish anguish
+ angus angu
+ animal anim
+ animals anim
+ animis animi
+ anjou anjou
+ ankle ankl
+ anna anna
+ annals annal
+ anne ann
+ annex annex
+ annexed annex
+ annexions annexion
+ annexment annex
+ annothanize annothan
+ announces announc
+ annoy annoi
+ annoyance annoy
+ annoying annoi
+ annual annual
+ anoint anoint
+ anointed anoint
+ anon anon
+ another anoth
+ anselmo anselmo
+ answer answer
+ answerable answer
+ answered answer
+ answerest answerest
+ answering answer
+ answers answer
+ ant ant
+ ante ant
+ antenor antenor
+ antenorides antenorid
+ anteroom anteroom
+ anthem anthem
+ anthems anthem
+ anthony anthoni
+ anthropophagi anthropophagi
+ anthropophaginian anthropophaginian
+ antiates antiat
+ antic antic
+ anticipate anticip
+ anticipates anticip
+ anticipatest anticipatest
+ anticipating anticip
+ anticipation anticip
+ antick antick
+ anticly anticli
+ antics antic
+ antidote antidot
+ antidotes antidot
+ antigonus antigonu
+ antiopa antiopa
+ antipathy antipathi
+ antipholus antipholu
+ antipholuses antipholus
+ antipodes antipod
+ antiquary antiquari
+ antique antiqu
+ antiquity antiqu
+ antium antium
+ antoniad antoniad
+ antonio antonio
+ antonius antoniu
+ antony antoni
+ antres antr
+ anvil anvil
+ any ani
+ anybody anybodi
+ anyone anyon
+ anything anyth
+ anywhere anywher
+ ap ap
+ apace apac
+ apart apart
+ apartment apart
+ apartments apart
+ ape ap
+ apemantus apemantu
+ apennines apennin
+ apes ap
+ apiece apiec
+ apish apish
+ apollinem apollinem
+ apollo apollo
+ apollodorus apollodoru
+ apology apolog
+ apoplex apoplex
+ apoplexy apoplexi
+ apostle apostl
+ apostles apostl
+ apostrophas apostropha
+ apoth apoth
+ apothecary apothecari
+ appal appal
+ appall appal
+ appalled appal
+ appals appal
+ apparel apparel
+ apparell apparel
+ apparelled apparel
+ apparent appar
+ apparently appar
+ apparition apparit
+ apparitions apparit
+ appeach appeach
+ appeal appeal
+ appeals appeal
+ appear appear
+ appearance appear
+ appeared appear
+ appeareth appeareth
+ appearing appear
+ appears appear
+ appeas appea
+ appease appeas
+ appeased appeas
+ appelant appel
+ appele appel
+ appelee appele
+ appeles appel
+ appelez appelez
+ appellant appel
+ appellants appel
+ appelons appelon
+ appendix appendix
+ apperil apperil
+ appertain appertain
+ appertaining appertain
+ appertainings appertain
+ appertains appertain
+ appertinent appertin
+ appertinents appertin
+ appetite appetit
+ appetites appetit
+ applaud applaud
+ applauded applaud
+ applauding applaud
+ applause applaus
+ applauses applaus
+ apple appl
+ apples appl
+ appletart appletart
+ appliance applianc
+ appliances applianc
+ applications applic
+ applied appli
+ applies appli
+ apply appli
+ applying appli
+ appoint appoint
+ appointed appoint
+ appointment appoint
+ appointments appoint
+ appoints appoint
+ apprehend apprehend
+ apprehended apprehend
+ apprehends apprehend
+ apprehension apprehens
+ apprehensions apprehens
+ apprehensive apprehens
+ apprendre apprendr
+ apprenne apprenn
+ apprenticehood apprenticehood
+ appris appri
+ approach approach
+ approachers approach
+ approaches approach
+ approacheth approacheth
+ approaching approach
+ approbation approb
+ approof approof
+ appropriation appropri
+ approv approv
+ approve approv
+ approved approv
+ approvers approv
+ approves approv
+ appurtenance appurten
+ appurtenances appurten
+ apricocks apricock
+ april april
+ apron apron
+ aprons apron
+ apt apt
+ apter apter
+ aptest aptest
+ aptly aptli
+ aptness apt
+ aqua aqua
+ aquilon aquilon
+ aquitaine aquitain
+ arabia arabia
+ arabian arabian
+ araise arais
+ arbitrate arbitr
+ arbitrating arbitr
+ arbitrator arbitr
+ arbitrement arbitr
+ arbors arbor
+ arbour arbour
+ arc arc
+ arch arch
+ archbishop archbishop
+ archbishopric archbishopr
+ archdeacon archdeacon
+ arched arch
+ archelaus archelau
+ archer archer
+ archers archer
+ archery archeri
+ archibald archibald
+ archidamus archidamu
+ architect architect
+ arcu arcu
+ arde ard
+ arden arden
+ ardent ardent
+ ardour ardour
+ are ar
+ argal argal
+ argier argier
+ argo argo
+ argosies argosi
+ argosy argosi
+ argu argu
+ argue argu
+ argued argu
+ argues argu
+ arguing argu
+ argument argument
+ arguments argument
+ argus argu
+ ariachne ariachn
+ ariadne ariadn
+ ariel ariel
+ aries ari
+ aright aright
+ arinado arinado
+ arinies arini
+ arion arion
+ arise aris
+ arises aris
+ ariseth ariseth
+ arising aris
+ aristode aristod
+ aristotle aristotl
+ arithmetic arithmet
+ arithmetician arithmetician
+ ark ark
+ arm arm
+ arma arma
+ armado armado
+ armadoes armado
+ armagnac armagnac
+ arme arm
+ armed arm
+ armenia armenia
+ armies armi
+ armigero armigero
+ arming arm
+ armipotent armipot
+ armor armor
+ armour armour
+ armourer armour
+ armourers armour
+ armours armour
+ armoury armouri
+ arms arm
+ army armi
+ arn arn
+ aroint aroint
+ arose aros
+ arouse arous
+ aroused arous
+ arragon arragon
+ arraign arraign
+ arraigned arraign
+ arraigning arraign
+ arraignment arraign
+ arrant arrant
+ arras arra
+ array arrai
+ arrearages arrearag
+ arrest arrest
+ arrested arrest
+ arrests arrest
+ arriv arriv
+ arrival arriv
+ arrivance arriv
+ arrive arriv
+ arrived arriv
+ arrives arriv
+ arriving arriv
+ arrogance arrog
+ arrogancy arrog
+ arrogant arrog
+ arrow arrow
+ arrows arrow
+ art art
+ artemidorus artemidoru
+ arteries arteri
+ arthur arthur
+ article articl
+ articles articl
+ articulate articul
+ artificer artific
+ artificial artifici
+ artillery artilleri
+ artire artir
+ artist artist
+ artists artist
+ artless artless
+ artois artoi
+ arts art
+ artus artu
+ arviragus arviragu
+ as as
+ asaph asaph
+ ascanius ascaniu
+ ascend ascend
+ ascended ascend
+ ascendeth ascendeth
+ ascends ascend
+ ascension ascens
+ ascent ascent
+ ascribe ascrib
+ ascribes ascrib
+ ash ash
+ asham asham
+ ashamed asham
+ asher asher
+ ashes ash
+ ashford ashford
+ ashore ashor
+ ashouting ashout
+ ashy ashi
+ asia asia
+ aside asid
+ ask ask
+ askance askanc
+ asked ask
+ asker asker
+ asketh asketh
+ asking ask
+ asks ask
+ aslant aslant
+ asleep asleep
+ asmath asmath
+ asp asp
+ aspect aspect
+ aspects aspect
+ aspen aspen
+ aspersion aspers
+ aspic aspic
+ aspicious aspici
+ aspics aspic
+ aspir aspir
+ aspiration aspir
+ aspire aspir
+ aspiring aspir
+ asquint asquint
+ ass ass
+ assail assail
+ assailable assail
+ assailant assail
+ assailants assail
+ assailed assail
+ assaileth assaileth
+ assailing assail
+ assails assail
+ assassination assassin
+ assault assault
+ assaulted assault
+ assaults assault
+ assay assai
+ assaying assai
+ assays assai
+ assemblance assembl
+ assemble assembl
+ assembled assembl
+ assemblies assembl
+ assembly assembl
+ assent assent
+ asses ass
+ assez assez
+ assign assign
+ assigned assign
+ assigns assign
+ assinico assinico
+ assist assist
+ assistance assist
+ assistances assist
+ assistant assist
+ assistants assist
+ assisted assist
+ assisting assist
+ associate associ
+ associated associ
+ associates associ
+ assuage assuag
+ assubjugate assubjug
+ assum assum
+ assume assum
+ assumes assum
+ assumption assumpt
+ assur assur
+ assurance assur
+ assure assur
+ assured assur
+ assuredly assuredli
+ assures assur
+ assyrian assyrian
+ astonish astonish
+ astonished astonish
+ astraea astraea
+ astray astrai
+ astrea astrea
+ astronomer astronom
+ astronomers astronom
+ astronomical astronom
+ astronomy astronomi
+ asunder asund
+ at at
+ atalanta atalanta
+ ate at
+ ates at
+ athenian athenian
+ athenians athenian
+ athens athen
+ athol athol
+ athversary athversari
+ athwart athwart
+ atlas atla
+ atomies atomi
+ atomy atomi
+ atone aton
+ atonement aton
+ atonements aton
+ atropos atropo
+ attach attach
+ attached attach
+ attachment attach
+ attain attain
+ attainder attaind
+ attains attain
+ attaint attaint
+ attainted attaint
+ attainture attaintur
+ attempt attempt
+ attemptable attempt
+ attempted attempt
+ attempting attempt
+ attempts attempt
+ attend attend
+ attendance attend
+ attendant attend
+ attendants attend
+ attended attend
+ attendents attend
+ attendeth attendeth
+ attending attend
+ attends attend
+ attent attent
+ attention attent
+ attentive attent
+ attentivenes attentiven
+ attest attest
+ attested attest
+ attir attir
+ attire attir
+ attired attir
+ attires attir
+ attorney attornei
+ attorneyed attornei
+ attorneys attornei
+ attorneyship attorneyship
+ attract attract
+ attraction attract
+ attractive attract
+ attracts attract
+ attribute attribut
+ attributed attribut
+ attributes attribut
+ attribution attribut
+ attributive attribut
+ atwain atwain
+ au au
+ aubrey aubrei
+ auburn auburn
+ aucun aucun
+ audacious audaci
+ audaciously audaci
+ audacity audac
+ audible audibl
+ audience audienc
+ audis audi
+ audit audit
+ auditor auditor
+ auditors auditor
+ auditory auditori
+ audre audr
+ audrey audrei
+ aufidius aufidiu
+ aufidiuses aufidius
+ auger auger
+ aught aught
+ augment augment
+ augmentation augment
+ augmented augment
+ augmenting augment
+ augurer augur
+ augurers augur
+ augures augur
+ auguring augur
+ augurs augur
+ augury auguri
+ august august
+ augustus augustu
+ auld auld
+ aumerle aumerl
+ aunchient aunchient
+ aunt aunt
+ aunts aunt
+ auricular auricular
+ aurora aurora
+ auspicious auspici
+ aussi aussi
+ austere auster
+ austerely auster
+ austereness auster
+ austerity auster
+ austria austria
+ aut aut
+ authentic authent
+ author author
+ authorities author
+ authority author
+ authorized author
+ authorizing author
+ authors author
+ autolycus autolycu
+ autre autr
+ autumn autumn
+ auvergne auvergn
+ avail avail
+ avails avail
+ avarice avaric
+ avaricious avarici
+ avaunt avaunt
+ ave av
+ aveng aveng
+ avenge aveng
+ avenged aveng
+ averring aver
+ avert avert
+ aves av
+ avez avez
+ avis avi
+ avoid avoid
+ avoided avoid
+ avoiding avoid
+ avoids avoid
+ avoirdupois avoirdupoi
+ avouch avouch
+ avouched avouch
+ avouches avouch
+ avouchment avouch
+ avow avow
+ aw aw
+ await await
+ awaits await
+ awak awak
+ awake awak
+ awaked awak
+ awaken awaken
+ awakened awaken
+ awakens awaken
+ awakes awak
+ awaking awak
+ award award
+ awards award
+ awasy awasi
+ away awai
+ awe aw
+ aweary aweari
+ aweless aweless
+ awful aw
+ awhile awhil
+ awkward awkward
+ awl awl
+ awooing awoo
+ awork awork
+ awry awri
+ axe ax
+ axle axl
+ axletree axletre
+ ay ay
+ aye ay
+ ayez ayez
+ ayli ayli
+ azur azur
+ azure azur
+ b b
+ ba ba
+ baa baa
+ babbl babbl
+ babble babbl
+ babbling babbl
+ babe babe
+ babes babe
+ babies babi
+ baboon baboon
+ baboons baboon
+ baby babi
+ babylon babylon
+ bacare bacar
+ bacchanals bacchan
+ bacchus bacchu
+ bach bach
+ bachelor bachelor
+ bachelors bachelor
+ back back
+ backbite backbit
+ backbitten backbitten
+ backing back
+ backs back
+ backward backward
+ backwardly backwardli
+ backwards backward
+ bacon bacon
+ bacons bacon
+ bad bad
+ bade bade
+ badge badg
+ badged badg
+ badges badg
+ badly badli
+ badness bad
+ baes bae
+ baffl baffl
+ baffle baffl
+ baffled baffl
+ bag bag
+ baggage baggag
+ bagot bagot
+ bagpipe bagpip
+ bags bag
+ bail bail
+ bailiff bailiff
+ baillez baillez
+ baily baili
+ baisant baisant
+ baisees baise
+ baiser baiser
+ bait bait
+ baited bait
+ baiting bait
+ baitings bait
+ baits bait
+ bajazet bajazet
+ bak bak
+ bake bake
+ baked bake
+ baker baker
+ bakers baker
+ bakes bake
+ baking bake
+ bal bal
+ balanc balanc
+ balance balanc
+ balcony balconi
+ bald bald
+ baldrick baldrick
+ bale bale
+ baleful bale
+ balk balk
+ ball ball
+ ballad ballad
+ ballads ballad
+ ballast ballast
+ ballasting ballast
+ ballet ballet
+ ballow ballow
+ balls ball
+ balm balm
+ balms balm
+ balmy balmi
+ balsam balsam
+ balsamum balsamum
+ balth balth
+ balthasar balthasar
+ balthazar balthazar
+ bames bame
+ ban ban
+ banbury banburi
+ band band
+ bandied bandi
+ banding band
+ bandit bandit
+ banditti banditti
+ banditto banditto
+ bands band
+ bandy bandi
+ bandying bandi
+ bane bane
+ banes bane
+ bang bang
+ bangor bangor
+ banish banish
+ banished banish
+ banishers banish
+ banishment banish
+ banister banist
+ bank bank
+ bankrout bankrout
+ bankrupt bankrupt
+ bankrupts bankrupt
+ banks bank
+ banner banner
+ bannerets banneret
+ banners banner
+ banning ban
+ banns bann
+ banquet banquet
+ banqueted banquet
+ banqueting banquet
+ banquets banquet
+ banquo banquo
+ bans ban
+ baptism baptism
+ baptista baptista
+ baptiz baptiz
+ bar bar
+ barbarian barbarian
+ barbarians barbarian
+ barbarism barbar
+ barbarous barbar
+ barbary barbari
+ barbason barbason
+ barbed barb
+ barber barber
+ barbermonger barbermong
+ bard bard
+ bardolph bardolph
+ bards bard
+ bare bare
+ bared bare
+ barefac barefac
+ barefaced barefac
+ barefoot barefoot
+ bareheaded barehead
+ barely bare
+ bareness bare
+ barful bar
+ bargain bargain
+ bargains bargain
+ barge barg
+ bargulus bargulu
+ baring bare
+ bark bark
+ barking bark
+ barkloughly barkloughli
+ barks bark
+ barky barki
+ barley barlei
+ barm barm
+ barn barn
+ barnacles barnacl
+ barnardine barnardin
+ barne barn
+ barnes barn
+ barnet barnet
+ barns barn
+ baron baron
+ barons baron
+ barony baroni
+ barr barr
+ barrabas barraba
+ barrel barrel
+ barrels barrel
+ barren barren
+ barrenly barrenli
+ barrenness barren
+ barricado barricado
+ barricadoes barricado
+ barrow barrow
+ bars bar
+ barson barson
+ barter barter
+ bartholomew bartholomew
+ bas ba
+ basan basan
+ base base
+ baseless baseless
+ basely base
+ baseness base
+ baser baser
+ bases base
+ basest basest
+ bashful bash
+ bashfulness bash
+ basilisco basilisco
+ basilisk basilisk
+ basilisks basilisk
+ basimecu basimecu
+ basin basin
+ basingstoke basingstok
+ basins basin
+ basis basi
+ bask bask
+ basket basket
+ baskets basket
+ bass bass
+ bassanio bassanio
+ basset basset
+ bassianus bassianu
+ basta basta
+ bastard bastard
+ bastardizing bastard
+ bastardly bastardli
+ bastards bastard
+ bastardy bastardi
+ basted bast
+ bastes bast
+ bastinado bastinado
+ basting bast
+ bat bat
+ batailles batail
+ batch batch
+ bate bate
+ bated bate
+ bates bate
+ bath bath
+ bathe bath
+ bathed bath
+ bathing bath
+ baths bath
+ bating bate
+ batler batler
+ bats bat
+ batt batt
+ battalia battalia
+ battalions battalion
+ batten batten
+ batter batter
+ battering batter
+ batters batter
+ battery batteri
+ battle battl
+ battled battl
+ battlefield battlefield
+ battlements battlement
+ battles battl
+ batty batti
+ bauble baubl
+ baubles baubl
+ baubling baubl
+ baulk baulk
+ bavin bavin
+ bawcock bawcock
+ bawd bawd
+ bawdry bawdri
+ bawds bawd
+ bawdy bawdi
+ bawl bawl
+ bawling bawl
+ bay bai
+ baying bai
+ baynard baynard
+ bayonne bayonn
+ bays bai
+ be be
+ beach beach
+ beached beach
+ beachy beachi
+ beacon beacon
+ bead bead
+ beaded bead
+ beadle beadl
+ beadles beadl
+ beads bead
+ beadsmen beadsmen
+ beagle beagl
+ beagles beagl
+ beak beak
+ beaks beak
+ beam beam
+ beamed beam
+ beams beam
+ bean bean
+ beans bean
+ bear bear
+ beard beard
+ bearded beard
+ beardless beardless
+ beards beard
+ bearer bearer
+ bearers bearer
+ bearest bearest
+ beareth beareth
+ bearing bear
+ bears bear
+ beast beast
+ beastliest beastliest
+ beastliness beastli
+ beastly beastli
+ beasts beast
+ beat beat
+ beated beat
+ beaten beaten
+ beating beat
+ beatrice beatric
+ beats beat
+ beau beau
+ beaufort beaufort
+ beaumond beaumond
+ beaumont beaumont
+ beauteous beauteou
+ beautied beauti
+ beauties beauti
+ beautified beautifi
+ beautiful beauti
+ beautify beautifi
+ beauty beauti
+ beaver beaver
+ beavers beaver
+ became becam
+ because becaus
+ bechanc bechanc
+ bechance bechanc
+ bechanced bechanc
+ beck beck
+ beckon beckon
+ beckons beckon
+ becks beck
+ becom becom
+ become becom
+ becomed becom
+ becomes becom
+ becoming becom
+ becomings becom
+ bed bed
+ bedabbled bedabbl
+ bedash bedash
+ bedaub bedaub
+ bedazzled bedazzl
+ bedchamber bedchamb
+ bedclothes bedcloth
+ bedded bed
+ bedeck bedeck
+ bedecking bedeck
+ bedew bedew
+ bedfellow bedfellow
+ bedfellows bedfellow
+ bedford bedford
+ bedlam bedlam
+ bedrench bedrench
+ bedrid bedrid
+ beds bed
+ bedtime bedtim
+ bedward bedward
+ bee bee
+ beef beef
+ beefs beef
+ beehives beehiv
+ been been
+ beer beer
+ bees bee
+ beest beest
+ beetle beetl
+ beetles beetl
+ beeves beev
+ befall befal
+ befallen befallen
+ befalls befal
+ befell befel
+ befits befit
+ befitted befit
+ befitting befit
+ befor befor
+ before befor
+ beforehand beforehand
+ befortune befortun
+ befriend befriend
+ befriended befriend
+ befriends befriend
+ beg beg
+ began began
+ beget beget
+ begets beget
+ begetting beget
+ begg begg
+ beggar beggar
+ beggared beggar
+ beggarly beggarli
+ beggarman beggarman
+ beggars beggar
+ beggary beggari
+ begging beg
+ begin begin
+ beginners beginn
+ beginning begin
+ beginnings begin
+ begins begin
+ begnawn begnawn
+ begone begon
+ begot begot
+ begotten begotten
+ begrimed begrim
+ begs beg
+ beguil beguil
+ beguile beguil
+ beguiled beguil
+ beguiles beguil
+ beguiling beguil
+ begun begun
+ behalf behalf
+ behalfs behalf
+ behav behav
+ behaved behav
+ behavedst behavedst
+ behavior behavior
+ behaviors behavior
+ behaviour behaviour
+ behaviours behaviour
+ behead behead
+ beheaded behead
+ beheld beheld
+ behest behest
+ behests behest
+ behind behind
+ behold behold
+ beholder behold
+ beholders behold
+ beholdest beholdest
+ beholding behold
+ beholds behold
+ behoof behoof
+ behooffull behoofful
+ behooves behoov
+ behove behov
+ behoves behov
+ behowls behowl
+ being be
+ bel bel
+ belarius belariu
+ belch belch
+ belching belch
+ beldam beldam
+ beldame beldam
+ beldams beldam
+ belee bele
+ belgia belgia
+ belie beli
+ belied beli
+ belief belief
+ beliest beliest
+ believ believ
+ believe believ
+ believed believ
+ believes believ
+ believest believest
+ believing believ
+ belike belik
+ bell bell
+ bellario bellario
+ belle bell
+ bellied belli
+ bellies belli
+ bellman bellman
+ bellona bellona
+ bellow bellow
+ bellowed bellow
+ bellowing bellow
+ bellows bellow
+ bells bell
+ belly belli
+ bellyful belly
+ belman belman
+ belmont belmont
+ belock belock
+ belong belong
+ belonging belong
+ belongings belong
+ belongs belong
+ belov belov
+ beloved belov
+ beloving belov
+ below below
+ belt belt
+ belzebub belzebub
+ bemadding bemad
+ bemet bemet
+ bemete bemet
+ bemoan bemoan
+ bemoaned bemoan
+ bemock bemock
+ bemoil bemoil
+ bemonster bemonst
+ ben ben
+ bench bench
+ bencher bencher
+ benches bench
+ bend bend
+ bended bend
+ bending bend
+ bends bend
+ bene bene
+ beneath beneath
+ benedicite benedicit
+ benedick benedick
+ benediction benedict
+ benedictus benedictu
+ benefactors benefactor
+ benefice benefic
+ beneficial benefici
+ benefit benefit
+ benefited benefit
+ benefits benefit
+ benetted benet
+ benevolence benevol
+ benevolences benevol
+ benied beni
+ benison benison
+ bennet bennet
+ bent bent
+ bentii bentii
+ bentivolii bentivolii
+ bents bent
+ benumbed benumb
+ benvolio benvolio
+ bepaint bepaint
+ bepray beprai
+ bequeath bequeath
+ bequeathed bequeath
+ bequeathing bequeath
+ bequest bequest
+ ber ber
+ berard berard
+ berattle berattl
+ beray berai
+ bere bere
+ bereave bereav
+ bereaved bereav
+ bereaves bereav
+ bereft bereft
+ bergamo bergamo
+ bergomask bergomask
+ berhym berhym
+ berhyme berhym
+ berkeley berkelei
+ bermoothes bermooth
+ bernardo bernardo
+ berod berod
+ berowne berown
+ berri berri
+ berries berri
+ berrord berrord
+ berry berri
+ bertram bertram
+ berwick berwick
+ bescreen bescreen
+ beseech beseech
+ beseeched beseech
+ beseechers beseech
+ beseeching beseech
+ beseek beseek
+ beseem beseem
+ beseemeth beseemeth
+ beseeming beseem
+ beseems beseem
+ beset beset
+ beshrew beshrew
+ beside besid
+ besides besid
+ besieg besieg
+ besiege besieg
+ besieged besieg
+ beslubber beslubb
+ besmear besmear
+ besmeared besmear
+ besmirch besmirch
+ besom besom
+ besort besort
+ besotted besot
+ bespake bespak
+ bespeak bespeak
+ bespice bespic
+ bespoke bespok
+ bespotted bespot
+ bess bess
+ bessy bessi
+ best best
+ bestained bestain
+ bested best
+ bestial bestial
+ bestir bestir
+ bestirr bestirr
+ bestow bestow
+ bestowed bestow
+ bestowing bestow
+ bestows bestow
+ bestraught bestraught
+ bestrew bestrew
+ bestrid bestrid
+ bestride bestrid
+ bestrides bestrid
+ bet bet
+ betake betak
+ beteem beteem
+ bethink bethink
+ bethought bethought
+ bethrothed bethroth
+ bethump bethump
+ betid betid
+ betide betid
+ betideth betideth
+ betime betim
+ betimes betim
+ betoken betoken
+ betook betook
+ betossed betoss
+ betray betrai
+ betrayed betrai
+ betraying betrai
+ betrays betrai
+ betrims betrim
+ betroth betroth
+ betrothed betroth
+ betroths betroth
+ bett bett
+ betted bet
+ better better
+ bettered better
+ bettering better
+ betters better
+ betting bet
+ bettre bettr
+ between between
+ betwixt betwixt
+ bevel bevel
+ beverage beverag
+ bevis bevi
+ bevy bevi
+ bewail bewail
+ bewailed bewail
+ bewailing bewail
+ bewails bewail
+ beware bewar
+ bewasted bewast
+ beweep beweep
+ bewept bewept
+ bewet bewet
+ bewhored bewhor
+ bewitch bewitch
+ bewitched bewitch
+ bewitchment bewitch
+ bewray bewrai
+ beyond beyond
+ bezonian bezonian
+ bezonians bezonian
+ bianca bianca
+ bianco bianco
+ bias bia
+ bibble bibbl
+ bickerings bicker
+ bid bid
+ bidden bidden
+ bidding bid
+ biddings bid
+ biddy biddi
+ bide bide
+ bides bide
+ biding bide
+ bids bid
+ bien bien
+ bier bier
+ bifold bifold
+ big big
+ bigamy bigami
+ biggen biggen
+ bigger bigger
+ bigness big
+ bigot bigot
+ bilberry bilberri
+ bilbo bilbo
+ bilboes bilbo
+ bilbow bilbow
+ bill bill
+ billeted billet
+ billets billet
+ billiards billiard
+ billing bill
+ billow billow
+ billows billow
+ bills bill
+ bin bin
+ bind bind
+ bindeth bindeth
+ binding bind
+ binds bind
+ biondello biondello
+ birch birch
+ bird bird
+ birding bird
+ birdlime birdlim
+ birds bird
+ birnam birnam
+ birth birth
+ birthday birthdai
+ birthdom birthdom
+ birthplace birthplac
+ birthright birthright
+ birthrights birthright
+ births birth
+ bis bi
+ biscuit biscuit
+ bishop bishop
+ bishops bishop
+ bisson bisson
+ bit bit
+ bitch bitch
+ bite bite
+ biter biter
+ bites bite
+ biting bite
+ bits bit
+ bitt bitt
+ bitten bitten
+ bitter bitter
+ bitterest bitterest
+ bitterly bitterli
+ bitterness bitter
+ blab blab
+ blabb blabb
+ blabbing blab
+ blabs blab
+ black black
+ blackamoor blackamoor
+ blackamoors blackamoor
+ blackberries blackberri
+ blackberry blackberri
+ blacker blacker
+ blackest blackest
+ blackfriars blackfriar
+ blackheath blackheath
+ blackmere blackmer
+ blackness black
+ blacks black
+ bladder bladder
+ bladders bladder
+ blade blade
+ bladed blade
+ blades blade
+ blains blain
+ blam blam
+ blame blame
+ blamed blame
+ blameful blame
+ blameless blameless
+ blames blame
+ blanc blanc
+ blanca blanca
+ blanch blanch
+ blank blank
+ blanket blanket
+ blanks blank
+ blaspheme blasphem
+ blaspheming blasphem
+ blasphemous blasphem
+ blasphemy blasphemi
+ blast blast
+ blasted blast
+ blasting blast
+ blastments blastment
+ blasts blast
+ blaz blaz
+ blaze blaze
+ blazes blaze
+ blazing blaze
+ blazon blazon
+ blazoned blazon
+ blazoning blazon
+ bleach bleach
+ bleaching bleach
+ bleak bleak
+ blear blear
+ bleared blear
+ bleat bleat
+ bleated bleat
+ bleats bleat
+ bled bled
+ bleed bleed
+ bleedest bleedest
+ bleedeth bleedeth
+ bleeding bleed
+ bleeds bleed
+ blemish blemish
+ blemishes blemish
+ blench blench
+ blenches blench
+ blend blend
+ blended blend
+ blent blent
+ bless bless
+ blessed bless
+ blessedly blessedli
+ blessedness blessed
+ blesses bless
+ blesseth blesseth
+ blessing bless
+ blessings bless
+ blest blest
+ blew blew
+ blind blind
+ blinded blind
+ blindfold blindfold
+ blinding blind
+ blindly blindli
+ blindness blind
+ blinds blind
+ blink blink
+ blinking blink
+ bliss bliss
+ blist blist
+ blister blister
+ blisters blister
+ blithe blith
+ blithild blithild
+ bloat bloat
+ block block
+ blockish blockish
+ blocks block
+ blois bloi
+ blood blood
+ blooded blood
+ bloodhound bloodhound
+ bloodied bloodi
+ bloodier bloodier
+ bloodiest bloodiest
+ bloodily bloodili
+ bloodless bloodless
+ bloods blood
+ bloodshed bloodsh
+ bloodshedding bloodshed
+ bloodstained bloodstain
+ bloody bloodi
+ bloom bloom
+ blooms bloom
+ blossom blossom
+ blossoming blossom
+ blossoms blossom
+ blot blot
+ blots blot
+ blotted blot
+ blotting blot
+ blount blount
+ blow blow
+ blowed blow
+ blowers blower
+ blowest blowest
+ blowing blow
+ blown blown
+ blows blow
+ blowse blows
+ blubb blubb
+ blubber blubber
+ blubbering blubber
+ blue blue
+ bluecaps bluecap
+ bluest bluest
+ blunt blunt
+ blunted blunt
+ blunter blunter
+ bluntest bluntest
+ blunting blunt
+ bluntly bluntli
+ bluntness blunt
+ blunts blunt
+ blur blur
+ blurr blurr
+ blurs blur
+ blush blush
+ blushes blush
+ blushest blushest
+ blushing blush
+ blust blust
+ bluster bluster
+ blusterer bluster
+ blusters bluster
+ bo bo
+ boar boar
+ board board
+ boarded board
+ boarding board
+ boards board
+ boarish boarish
+ boars boar
+ boast boast
+ boasted boast
+ boastful boast
+ boasting boast
+ boasts boast
+ boat boat
+ boats boat
+ boatswain boatswain
+ bob bob
+ bobb bobb
+ boblibindo boblibindo
+ bobtail bobtail
+ bocchus bocchu
+ bode bode
+ boded bode
+ bodements bodement
+ bodes bode
+ bodg bodg
+ bodied bodi
+ bodies bodi
+ bodiless bodiless
+ bodily bodili
+ boding bode
+ bodkin bodkin
+ body bodi
+ bodykins bodykin
+ bog bog
+ boggle boggl
+ boggler boggler
+ bogs bog
+ bohemia bohemia
+ bohemian bohemian
+ bohun bohun
+ boil boil
+ boiling boil
+ boils boil
+ boist boist
+ boisterous boister
+ boisterously boister
+ boitier boitier
+ bold bold
+ bolden bolden
+ bolder bolder
+ boldest boldest
+ boldly boldli
+ boldness bold
+ bolds bold
+ bolingbroke bolingbrok
+ bolster bolster
+ bolt bolt
+ bolted bolt
+ bolter bolter
+ bolters bolter
+ bolting bolt
+ bolts bolt
+ bombard bombard
+ bombards bombard
+ bombast bombast
+ bon bon
+ bona bona
+ bond bond
+ bondage bondag
+ bonded bond
+ bondmaid bondmaid
+ bondman bondman
+ bondmen bondmen
+ bonds bond
+ bondslave bondslav
+ bone bone
+ boneless boneless
+ bones bone
+ bonfire bonfir
+ bonfires bonfir
+ bonjour bonjour
+ bonne bonn
+ bonnet bonnet
+ bonneted bonnet
+ bonny bonni
+ bonos bono
+ bonto bonto
+ bonville bonvil
+ bood bood
+ book book
+ bookish bookish
+ books book
+ boon boon
+ boor boor
+ boorish boorish
+ boors boor
+ boot boot
+ booted boot
+ booties booti
+ bootless bootless
+ boots boot
+ booty booti
+ bor bor
+ bora bora
+ borachio borachio
+ bordeaux bordeaux
+ border border
+ bordered border
+ borderers border
+ borders border
+ bore bore
+ boreas borea
+ bores bore
+ boring bore
+ born born
+ borne born
+ borough borough
+ boroughs borough
+ borrow borrow
+ borrowed borrow
+ borrower borrow
+ borrowing borrow
+ borrows borrow
+ bosko bosko
+ boskos bosko
+ bosky boski
+ bosom bosom
+ bosoms bosom
+ boson boson
+ boss boss
+ bosworth bosworth
+ botch botch
+ botcher botcher
+ botches botch
+ botchy botchi
+ both both
+ bots bot
+ bottle bottl
+ bottled bottl
+ bottles bottl
+ bottom bottom
+ bottomless bottomless
+ bottoms bottom
+ bouciqualt bouciqualt
+ bouge boug
+ bough bough
+ boughs bough
+ bought bought
+ bounce bounc
+ bouncing bounc
+ bound bound
+ bounded bound
+ bounden bounden
+ boundeth boundeth
+ bounding bound
+ boundless boundless
+ bounds bound
+ bounteous bounteou
+ bounteously bounteous
+ bounties bounti
+ bountiful bounti
+ bountifully bountifulli
+ bounty bounti
+ bourbier bourbier
+ bourbon bourbon
+ bourchier bourchier
+ bourdeaux bourdeaux
+ bourn bourn
+ bout bout
+ bouts bout
+ bove bove
+ bow bow
+ bowcase bowcas
+ bowed bow
+ bowels bowel
+ bower bower
+ bowing bow
+ bowl bowl
+ bowler bowler
+ bowling bowl
+ bowls bowl
+ bows bow
+ bowsprit bowsprit
+ bowstring bowstr
+ box box
+ boxes box
+ boy boi
+ boyet boyet
+ boyish boyish
+ boys boi
+ brabant brabant
+ brabantio brabantio
+ brabble brabbl
+ brabbler brabbler
+ brac brac
+ brace brace
+ bracelet bracelet
+ bracelets bracelet
+ brach brach
+ bracy braci
+ brag brag
+ bragg bragg
+ braggardism braggard
+ braggards braggard
+ braggart braggart
+ braggarts braggart
+ bragged brag
+ bragging brag
+ bragless bragless
+ brags brag
+ braid braid
+ braided braid
+ brain brain
+ brained brain
+ brainford brainford
+ brainish brainish
+ brainless brainless
+ brains brain
+ brainsick brainsick
+ brainsickly brainsickli
+ brake brake
+ brakenbury brakenburi
+ brakes brake
+ brambles brambl
+ bran bran
+ branch branch
+ branches branch
+ branchless branchless
+ brand brand
+ branded brand
+ brandish brandish
+ brandon brandon
+ brands brand
+ bras bra
+ brass brass
+ brassy brassi
+ brat brat
+ brats brat
+ brav brav
+ brave brave
+ braved brave
+ bravely brave
+ braver braver
+ bravery braveri
+ braves brave
+ bravest bravest
+ braving brave
+ brawl brawl
+ brawler brawler
+ brawling brawl
+ brawls brawl
+ brawn brawn
+ brawns brawn
+ bray brai
+ braying brai
+ braz braz
+ brazen brazen
+ brazier brazier
+ breach breach
+ breaches breach
+ bread bread
+ breadth breadth
+ break break
+ breaker breaker
+ breakfast breakfast
+ breaking break
+ breaks break
+ breast breast
+ breasted breast
+ breasting breast
+ breastplate breastplat
+ breasts breast
+ breath breath
+ breathe breath
+ breathed breath
+ breather breather
+ breathers breather
+ breathes breath
+ breathest breathest
+ breathing breath
+ breathless breathless
+ breaths breath
+ brecknock brecknock
+ bred bred
+ breech breech
+ breeches breech
+ breeching breech
+ breed breed
+ breeder breeder
+ breeders breeder
+ breeding breed
+ breeds breed
+ breese brees
+ breeze breez
+ breff breff
+ bretagne bretagn
+ brethen brethen
+ bretheren bretheren
+ brethren brethren
+ brevis brevi
+ brevity breviti
+ brew brew
+ brewage brewag
+ brewer brewer
+ brewers brewer
+ brewing brew
+ brews brew
+ briareus briareu
+ briars briar
+ brib brib
+ bribe bribe
+ briber briber
+ bribes bribe
+ brick brick
+ bricklayer bricklay
+ bricks brick
+ bridal bridal
+ bride bride
+ bridegroom bridegroom
+ bridegrooms bridegroom
+ brides bride
+ bridge bridg
+ bridgenorth bridgenorth
+ bridges bridg
+ bridget bridget
+ bridle bridl
+ bridled bridl
+ brief brief
+ briefer briefer
+ briefest briefest
+ briefly briefli
+ briefness brief
+ brier brier
+ briers brier
+ brigandine brigandin
+ bright bright
+ brighten brighten
+ brightest brightest
+ brightly brightli
+ brightness bright
+ brim brim
+ brimful brim
+ brims brim
+ brimstone brimston
+ brinded brind
+ brine brine
+ bring bring
+ bringer bringer
+ bringeth bringeth
+ bringing bring
+ bringings bring
+ brings bring
+ brinish brinish
+ brink brink
+ brisk brisk
+ brisky briski
+ bristle bristl
+ bristled bristl
+ bristly bristli
+ bristol bristol
+ bristow bristow
+ britain britain
+ britaine britain
+ britaines britain
+ british british
+ briton briton
+ britons briton
+ brittany brittani
+ brittle brittl
+ broach broach
+ broached broach
+ broad broad
+ broader broader
+ broadsides broadsid
+ brocas broca
+ brock brock
+ brogues brogu
+ broil broil
+ broiling broil
+ broils broil
+ broke broke
+ broken broken
+ brokenly brokenli
+ broker broker
+ brokers broker
+ brokes broke
+ broking broke
+ brooch brooch
+ brooches brooch
+ brood brood
+ brooded brood
+ brooding brood
+ brook brook
+ brooks brook
+ broom broom
+ broomstaff broomstaff
+ broth broth
+ brothel brothel
+ brother brother
+ brotherhood brotherhood
+ brotherhoods brotherhood
+ brotherly brotherli
+ brothers brother
+ broths broth
+ brought brought
+ brow brow
+ brown brown
+ browner browner
+ brownist brownist
+ browny browni
+ brows brow
+ browse brows
+ browsing brows
+ bruis brui
+ bruise bruis
+ bruised bruis
+ bruises bruis
+ bruising bruis
+ bruit bruit
+ bruited bruit
+ brundusium brundusium
+ brunt brunt
+ brush brush
+ brushes brush
+ brute brute
+ brutish brutish
+ brutus brutu
+ bubble bubbl
+ bubbles bubbl
+ bubbling bubbl
+ bubukles bubukl
+ buck buck
+ bucket bucket
+ buckets bucket
+ bucking buck
+ buckingham buckingham
+ buckle buckl
+ buckled buckl
+ buckler buckler
+ bucklers buckler
+ bucklersbury bucklersburi
+ buckles buckl
+ buckram buckram
+ bucks buck
+ bud bud
+ budded bud
+ budding bud
+ budge budg
+ budger budger
+ budget budget
+ buds bud
+ buff buff
+ buffet buffet
+ buffeting buffet
+ buffets buffet
+ bug bug
+ bugbear bugbear
+ bugle bugl
+ bugs bug
+ build build
+ builded build
+ buildeth buildeth
+ building build
+ buildings build
+ builds build
+ built built
+ bulk bulk
+ bulks bulk
+ bull bull
+ bullcalf bullcalf
+ bullen bullen
+ bullens bullen
+ bullet bullet
+ bullets bullet
+ bullocks bullock
+ bulls bull
+ bully bulli
+ bulmer bulmer
+ bulwark bulwark
+ bulwarks bulwark
+ bum bum
+ bumbast bumbast
+ bump bump
+ bumper bumper
+ bums bum
+ bunch bunch
+ bunches bunch
+ bundle bundl
+ bung bung
+ bunghole bunghol
+ bungle bungl
+ bunting bunt
+ buoy buoi
+ bur bur
+ burbolt burbolt
+ burd burd
+ burden burden
+ burdened burden
+ burdening burden
+ burdenous burden
+ burdens burden
+ burgh burgh
+ burgher burgher
+ burghers burgher
+ burglary burglari
+ burgomasters burgomast
+ burgonet burgonet
+ burgundy burgundi
+ burial burial
+ buried buri
+ burier burier
+ buriest buriest
+ burly burli
+ burn burn
+ burned burn
+ burnet burnet
+ burneth burneth
+ burning burn
+ burnish burnish
+ burns burn
+ burnt burnt
+ burr burr
+ burrows burrow
+ burs bur
+ burst burst
+ bursting burst
+ bursts burst
+ burthen burthen
+ burthens burthen
+ burton burton
+ bury buri
+ burying buri
+ bush bush
+ bushels bushel
+ bushes bush
+ bushy bushi
+ busied busi
+ busily busili
+ busines busin
+ business busi
+ businesses busi
+ buskin buskin
+ busky buski
+ buss buss
+ busses buss
+ bussing buss
+ bustle bustl
+ bustling bustl
+ busy busi
+ but but
+ butcheed butche
+ butcher butcher
+ butchered butcher
+ butcheries butcheri
+ butcherly butcherli
+ butchers butcher
+ butchery butcheri
+ butler butler
+ butt butt
+ butter butter
+ buttered butter
+ butterflies butterfli
+ butterfly butterfli
+ butterwoman butterwoman
+ buttery butteri
+ buttock buttock
+ buttocks buttock
+ button button
+ buttonhole buttonhol
+ buttons button
+ buttress buttress
+ buttry buttri
+ butts butt
+ buxom buxom
+ buy bui
+ buyer buyer
+ buying bui
+ buys bui
+ buzz buzz
+ buzzard buzzard
+ buzzards buzzard
+ buzzers buzzer
+ buzzing buzz
+ by by
+ bye bye
+ byzantium byzantium
+ c c
+ ca ca
+ cabbage cabbag
+ cabileros cabilero
+ cabin cabin
+ cabins cabin
+ cable cabl
+ cables cabl
+ cackling cackl
+ cacodemon cacodemon
+ caddis caddi
+ caddisses caddiss
+ cade cade
+ cadence cadenc
+ cadent cadent
+ cades cade
+ cadmus cadmu
+ caduceus caduceu
+ cadwal cadwal
+ cadwallader cadwallad
+ caelius caeliu
+ caelo caelo
+ caesar caesar
+ caesarion caesarion
+ caesars caesar
+ cage cage
+ caged cage
+ cagion cagion
+ cain cain
+ caithness caith
+ caitiff caitiff
+ caitiffs caitiff
+ caius caiu
+ cak cak
+ cake cake
+ cakes cake
+ calaber calab
+ calais calai
+ calamities calam
+ calamity calam
+ calchas calcha
+ calculate calcul
+ calen calen
+ calendar calendar
+ calendars calendar
+ calf calf
+ caliban caliban
+ calibans caliban
+ calipolis calipoli
+ cality caliti
+ caliver caliv
+ call call
+ callat callat
+ called call
+ callet callet
+ calling call
+ calls call
+ calm calm
+ calmest calmest
+ calmly calmli
+ calmness calm
+ calms calm
+ calpurnia calpurnia
+ calumniate calumni
+ calumniating calumni
+ calumnious calumni
+ calumny calumni
+ calve calv
+ calved calv
+ calves calv
+ calveskins calveskin
+ calydon calydon
+ cam cam
+ cambio cambio
+ cambria cambria
+ cambric cambric
+ cambrics cambric
+ cambridge cambridg
+ cambyses cambys
+ came came
+ camel camel
+ camelot camelot
+ camels camel
+ camest camest
+ camillo camillo
+ camlet camlet
+ camomile camomil
+ camp camp
+ campeius campeiu
+ camping camp
+ camps camp
+ can can
+ canakin canakin
+ canaries canari
+ canary canari
+ cancel cancel
+ cancell cancel
+ cancelled cancel
+ cancelling cancel
+ cancels cancel
+ cancer cancer
+ candidatus candidatu
+ candied candi
+ candle candl
+ candles candl
+ candlesticks candlestick
+ candy candi
+ canidius canidiu
+ cank cank
+ canker canker
+ cankerblossom cankerblossom
+ cankers canker
+ cannibally cannib
+ cannibals cannib
+ cannon cannon
+ cannoneer cannon
+ cannons cannon
+ cannot cannot
+ canon canon
+ canoniz canoniz
+ canonize canon
+ canonized canon
+ canons canon
+ canopied canopi
+ canopies canopi
+ canopy canopi
+ canst canst
+ canstick canstick
+ canterbury canterburi
+ cantle cantl
+ cantons canton
+ canus canu
+ canvas canva
+ canvass canvass
+ canzonet canzonet
+ cap cap
+ capability capabl
+ capable capabl
+ capacities capac
+ capacity capac
+ caparison caparison
+ capdv capdv
+ cape cape
+ capel capel
+ capels capel
+ caper caper
+ capers caper
+ capet capet
+ caphis caphi
+ capilet capilet
+ capitaine capitain
+ capital capit
+ capite capit
+ capitol capitol
+ capitulate capitul
+ capocchia capocchia
+ capon capon
+ capons capon
+ capp capp
+ cappadocia cappadocia
+ capriccio capriccio
+ capricious caprici
+ caps cap
+ capt capt
+ captain captain
+ captains captain
+ captainship captainship
+ captious captiou
+ captivate captiv
+ captivated captiv
+ captivates captiv
+ captive captiv
+ captives captiv
+ captivity captiv
+ captum captum
+ capucius capuciu
+ capulet capulet
+ capulets capulet
+ car car
+ carack carack
+ caracks carack
+ carat carat
+ caraways carawai
+ carbonado carbonado
+ carbuncle carbuncl
+ carbuncled carbuncl
+ carbuncles carbuncl
+ carcanet carcanet
+ carcase carcas
+ carcases carcas
+ carcass carcass
+ carcasses carcass
+ card card
+ cardecue cardecu
+ carded card
+ carders carder
+ cardinal cardin
+ cardinally cardin
+ cardinals cardin
+ cardmaker cardmak
+ cards card
+ carduus carduu
+ care care
+ cared care
+ career career
+ careers career
+ careful care
+ carefully carefulli
+ careless careless
+ carelessly carelessli
+ carelessness careless
+ cares care
+ caret caret
+ cargo cargo
+ carl carl
+ carlisle carlisl
+ carlot carlot
+ carman carman
+ carmen carmen
+ carnal carnal
+ carnally carnal
+ carnarvonshire carnarvonshir
+ carnation carnat
+ carnations carnat
+ carol carol
+ carous carou
+ carouse carous
+ caroused carous
+ carouses carous
+ carousing carous
+ carp carp
+ carpenter carpent
+ carper carper
+ carpet carpet
+ carpets carpet
+ carping carp
+ carriage carriag
+ carriages carriag
+ carried carri
+ carrier carrier
+ carriers carrier
+ carries carri
+ carrion carrion
+ carrions carrion
+ carry carri
+ carrying carri
+ cars car
+ cart cart
+ carters carter
+ carthage carthag
+ carts cart
+ carv carv
+ carve carv
+ carved carv
+ carver carver
+ carves carv
+ carving carv
+ cas ca
+ casa casa
+ casaer casaer
+ casca casca
+ case case
+ casement casement
+ casements casement
+ cases case
+ cash cash
+ cashier cashier
+ casing case
+ cask cask
+ casket casket
+ casketed casket
+ caskets casket
+ casque casqu
+ casques casqu
+ cassado cassado
+ cassandra cassandra
+ cassibelan cassibelan
+ cassio cassio
+ cassius cassiu
+ cassocks cassock
+ cast cast
+ castalion castalion
+ castaway castawai
+ castaways castawai
+ casted cast
+ caster caster
+ castigate castig
+ castigation castig
+ castile castil
+ castiliano castiliano
+ casting cast
+ castle castl
+ castles castl
+ casts cast
+ casual casual
+ casually casual
+ casualties casualti
+ casualty casualti
+ cat cat
+ cataian cataian
+ catalogue catalogu
+ cataplasm cataplasm
+ cataracts cataract
+ catarrhs catarrh
+ catastrophe catastroph
+ catch catch
+ catcher catcher
+ catches catch
+ catching catch
+ cate cate
+ catechising catechis
+ catechism catech
+ catechize catech
+ cater cater
+ caterpillars caterpillar
+ caters cater
+ caterwauling caterwaul
+ cates cate
+ catesby catesbi
+ cathedral cathedr
+ catlike catlik
+ catling catl
+ catlings catl
+ cato cato
+ cats cat
+ cattle cattl
+ caucasus caucasu
+ caudle caudl
+ cauf cauf
+ caught caught
+ cauldron cauldron
+ caus cau
+ cause caus
+ caused caus
+ causeless causeless
+ causer causer
+ causes caus
+ causest causest
+ causeth causeth
+ cautel cautel
+ cautelous cautel
+ cautels cautel
+ cauterizing cauter
+ caution caution
+ cautions caution
+ cavaleiro cavaleiro
+ cavalery cavaleri
+ cavaliers cavali
+ cave cave
+ cavern cavern
+ caverns cavern
+ caves cave
+ caveto caveto
+ caviary caviari
+ cavil cavil
+ cavilling cavil
+ cawdor cawdor
+ cawdron cawdron
+ cawing caw
+ ce ce
+ ceas cea
+ cease ceas
+ ceases ceas
+ ceaseth ceaseth
+ cedar cedar
+ cedars cedar
+ cedius cediu
+ celebrate celebr
+ celebrated celebr
+ celebrates celebr
+ celebration celebr
+ celerity celer
+ celestial celesti
+ celia celia
+ cell cell
+ cellar cellar
+ cellarage cellarag
+ celsa celsa
+ cement cement
+ censer censer
+ censor censor
+ censorinus censorinu
+ censur censur
+ censure censur
+ censured censur
+ censurers censur
+ censures censur
+ censuring censur
+ centaur centaur
+ centaurs centaur
+ centre centr
+ cents cent
+ centuries centuri
+ centurion centurion
+ centurions centurion
+ century centuri
+ cerberus cerberu
+ cerecloth cerecloth
+ cerements cerement
+ ceremonial ceremoni
+ ceremonies ceremoni
+ ceremonious ceremoni
+ ceremoniously ceremoni
+ ceremony ceremoni
+ ceres cere
+ cerns cern
+ certain certain
+ certainer certain
+ certainly certainli
+ certainties certainti
+ certainty certainti
+ certes cert
+ certificate certif
+ certified certifi
+ certifies certifi
+ certify certifi
+ ces ce
+ cesario cesario
+ cess cess
+ cesse cess
+ cestern cestern
+ cetera cetera
+ cette cett
+ chaces chace
+ chaf chaf
+ chafe chafe
+ chafed chafe
+ chafes chafe
+ chaff chaff
+ chaffless chaffless
+ chafing chafe
+ chain chain
+ chains chain
+ chair chair
+ chairs chair
+ chalic chalic
+ chalice chalic
+ chalices chalic
+ chalk chalk
+ chalks chalk
+ chalky chalki
+ challeng challeng
+ challenge challeng
+ challenged challeng
+ challenger challeng
+ challengers challeng
+ challenges challeng
+ cham cham
+ chamber chamber
+ chamberers chamber
+ chamberlain chamberlain
+ chamberlains chamberlain
+ chambermaid chambermaid
+ chambermaids chambermaid
+ chambers chamber
+ chameleon chameleon
+ champ champ
+ champagne champagn
+ champain champain
+ champains champain
+ champion champion
+ champions champion
+ chanc chanc
+ chance chanc
+ chanced chanc
+ chancellor chancellor
+ chances chanc
+ chandler chandler
+ chang chang
+ change chang
+ changeable changeabl
+ changed chang
+ changeful chang
+ changeling changel
+ changelings changel
+ changer changer
+ changes chang
+ changest changest
+ changing chang
+ channel channel
+ channels channel
+ chanson chanson
+ chant chant
+ chanticleer chanticl
+ chanting chant
+ chantries chantri
+ chantry chantri
+ chants chant
+ chaos chao
+ chap chap
+ chape chape
+ chapel chapel
+ chapeless chapeless
+ chapels chapel
+ chaplain chaplain
+ chaplains chaplain
+ chapless chapless
+ chaplet chaplet
+ chapmen chapmen
+ chaps chap
+ chapter chapter
+ character charact
+ charactered charact
+ characterless characterless
+ characters charact
+ charactery characteri
+ characts charact
+ charbon charbon
+ chare chare
+ chares chare
+ charg charg
+ charge charg
+ charged charg
+ chargeful charg
+ charges charg
+ chargeth chargeth
+ charging charg
+ chariest chariest
+ chariness chari
+ charing chare
+ chariot chariot
+ chariots chariot
+ charitable charit
+ charitably charit
+ charities chariti
+ charity chariti
+ charlemain charlemain
+ charles charl
+ charm charm
+ charmed charm
+ charmer charmer
+ charmeth charmeth
+ charmian charmian
+ charming charm
+ charmingly charmingli
+ charms charm
+ charneco charneco
+ charnel charnel
+ charolois charoloi
+ charon charon
+ charter charter
+ charters charter
+ chartreux chartreux
+ chary chari
+ charybdis charybdi
+ chas cha
+ chase chase
+ chased chase
+ chaser chaser
+ chaseth chaseth
+ chasing chase
+ chaste chast
+ chastely chast
+ chastis chasti
+ chastise chastis
+ chastised chastis
+ chastisement chastis
+ chastity chastiti
+ chat chat
+ chatham chatham
+ chatillon chatillon
+ chats chat
+ chatt chatt
+ chattels chattel
+ chatter chatter
+ chattering chatter
+ chattles chattl
+ chaud chaud
+ chaunted chaunt
+ chaw chaw
+ chawdron chawdron
+ che che
+ cheap cheap
+ cheapen cheapen
+ cheaper cheaper
+ cheapest cheapest
+ cheaply cheapli
+ cheapside cheapsid
+ cheat cheat
+ cheated cheat
+ cheater cheater
+ cheaters cheater
+ cheating cheat
+ cheats cheat
+ check check
+ checked check
+ checker checker
+ checking check
+ checks check
+ cheek cheek
+ cheeks cheek
+ cheer cheer
+ cheered cheer
+ cheerer cheerer
+ cheerful cheer
+ cheerfully cheerfulli
+ cheering cheer
+ cheerless cheerless
+ cheerly cheerli
+ cheers cheer
+ cheese chees
+ chequer chequer
+ cher cher
+ cherish cherish
+ cherished cherish
+ cherisher cherish
+ cherishes cherish
+ cherishing cherish
+ cherries cherri
+ cherry cherri
+ cherrypit cherrypit
+ chertsey chertsei
+ cherub cherub
+ cherubims cherubim
+ cherubin cherubin
+ cherubins cherubin
+ cheshu cheshu
+ chess chess
+ chest chest
+ chester chester
+ chestnut chestnut
+ chestnuts chestnut
+ chests chest
+ chetas cheta
+ chev chev
+ cheval cheval
+ chevalier chevali
+ chevaliers chevali
+ cheveril cheveril
+ chew chew
+ chewed chew
+ chewet chewet
+ chewing chew
+ chez chez
+ chi chi
+ chick chick
+ chicken chicken
+ chickens chicken
+ chicurmurco chicurmurco
+ chid chid
+ chidden chidden
+ chide chide
+ chiders chider
+ chides chide
+ chiding chide
+ chief chief
+ chiefest chiefest
+ chiefly chiefli
+ chien chien
+ child child
+ childed child
+ childeric childer
+ childhood childhood
+ childhoods childhood
+ childing child
+ childish childish
+ childishness childish
+ childlike childlik
+ childness child
+ children children
+ chill chill
+ chilling chill
+ chime chime
+ chimes chime
+ chimney chimnei
+ chimneypiece chimneypiec
+ chimneys chimnei
+ chimurcho chimurcho
+ chin chin
+ china china
+ chine chine
+ chines chine
+ chink chink
+ chinks chink
+ chins chin
+ chipp chipp
+ chipper chipper
+ chips chip
+ chiron chiron
+ chirping chirp
+ chirrah chirrah
+ chirurgeonly chirurgeonli
+ chisel chisel
+ chitopher chitoph
+ chivalrous chivalr
+ chivalry chivalri
+ choice choic
+ choicely choic
+ choicest choicest
+ choir choir
+ choirs choir
+ chok chok
+ choke choke
+ choked choke
+ chokes choke
+ choking choke
+ choler choler
+ choleric choler
+ cholers choler
+ chollors chollor
+ choose choos
+ chooser chooser
+ chooses choos
+ chooseth chooseth
+ choosing choos
+ chop chop
+ chopine chopin
+ choplogic choplog
+ chopp chopp
+ chopped chop
+ chopping chop
+ choppy choppi
+ chops chop
+ chopt chopt
+ chor chor
+ choristers chorist
+ chorus choru
+ chose chose
+ chosen chosen
+ chough chough
+ choughs chough
+ chrish chrish
+ christ christ
+ christen christen
+ christendom christendom
+ christendoms christendom
+ christening christen
+ christenings christen
+ christian christian
+ christianlike christianlik
+ christians christian
+ christmas christma
+ christom christom
+ christopher christoph
+ christophero christophero
+ chronicle chronicl
+ chronicled chronicl
+ chronicler chronicl
+ chroniclers chronicl
+ chronicles chronicl
+ chrysolite chrysolit
+ chuck chuck
+ chucks chuck
+ chud chud
+ chuffs chuff
+ church church
+ churches church
+ churchman churchman
+ churchmen churchmen
+ churchyard churchyard
+ churchyards churchyard
+ churl churl
+ churlish churlish
+ churlishly churlishli
+ churls churl
+ churn churn
+ chus chu
+ cicatrice cicatric
+ cicatrices cicatric
+ cicely cice
+ cicero cicero
+ ciceter cicet
+ ciel ciel
+ ciitzens ciitzen
+ cilicia cilicia
+ cimber cimber
+ cimmerian cimmerian
+ cinable cinabl
+ cincture cinctur
+ cinders cinder
+ cine cine
+ cinna cinna
+ cinque cinqu
+ cipher cipher
+ ciphers cipher
+ circa circa
+ circe circ
+ circle circl
+ circled circl
+ circlets circlet
+ circling circl
+ circuit circuit
+ circum circum
+ circumcised circumcis
+ circumference circumfer
+ circummur circummur
+ circumscrib circumscrib
+ circumscribed circumscrib
+ circumscription circumscript
+ circumspect circumspect
+ circumstance circumst
+ circumstanced circumstanc
+ circumstances circumst
+ circumstantial circumstanti
+ circumvent circumv
+ circumvention circumvent
+ cistern cistern
+ citadel citadel
+ cital cital
+ cite cite
+ cited cite
+ cites cite
+ cities citi
+ citing cite
+ citizen citizen
+ citizens citizen
+ cittern cittern
+ city citi
+ civet civet
+ civil civil
+ civility civil
+ civilly civilli
+ clack clack
+ clad clad
+ claim claim
+ claiming claim
+ claims claim
+ clamb clamb
+ clamber clamber
+ clammer clammer
+ clamor clamor
+ clamorous clamor
+ clamors clamor
+ clamour clamour
+ clamours clamour
+ clang clang
+ clangor clangor
+ clap clap
+ clapp clapp
+ clapped clap
+ clapper clapper
+ clapping clap
+ claps clap
+ clare clare
+ clarence clarenc
+ claret claret
+ claribel claribel
+ clasp clasp
+ clasps clasp
+ clatter clatter
+ claud claud
+ claudio claudio
+ claudius claudiu
+ clause claus
+ claw claw
+ clawed claw
+ clawing claw
+ claws claw
+ clay clai
+ clays clai
+ clean clean
+ cleanliest cleanliest
+ cleanly cleanli
+ cleans clean
+ cleanse cleans
+ cleansing cleans
+ clear clear
+ clearer clearer
+ clearest clearest
+ clearly clearli
+ clearness clear
+ clears clear
+ cleave cleav
+ cleaving cleav
+ clef clef
+ cleft cleft
+ cleitus cleitu
+ clemency clemenc
+ clement clement
+ cleomenes cleomen
+ cleopatpa cleopatpa
+ cleopatra cleopatra
+ clepeth clepeth
+ clept clept
+ clerestories clerestori
+ clergy clergi
+ clergyman clergyman
+ clergymen clergymen
+ clerk clerk
+ clerkly clerkli
+ clerks clerk
+ clew clew
+ client client
+ clients client
+ cliff cliff
+ clifford clifford
+ cliffords clifford
+ cliffs cliff
+ clifton clifton
+ climate climat
+ climature climatur
+ climb climb
+ climbed climb
+ climber climber
+ climbeth climbeth
+ climbing climb
+ climbs climb
+ clime clime
+ cling cling
+ clink clink
+ clinking clink
+ clinquant clinquant
+ clip clip
+ clipp clipp
+ clipper clipper
+ clippeth clippeth
+ clipping clip
+ clipt clipt
+ clitus clitu
+ clo clo
+ cloak cloak
+ cloakbag cloakbag
+ cloaks cloak
+ clock clock
+ clocks clock
+ clod clod
+ cloddy cloddi
+ clodpole clodpol
+ clog clog
+ clogging clog
+ clogs clog
+ cloister cloister
+ cloistress cloistress
+ cloquence cloquenc
+ clos clo
+ close close
+ closed close
+ closely close
+ closeness close
+ closer closer
+ closes close
+ closest closest
+ closet closet
+ closing close
+ closure closur
+ cloten cloten
+ clotens cloten
+ cloth cloth
+ clothair clothair
+ clotharius clothariu
+ clothe cloth
+ clothes cloth
+ clothier clothier
+ clothiers clothier
+ clothing cloth
+ cloths cloth
+ clotpoles clotpol
+ clotpoll clotpol
+ cloud cloud
+ clouded cloud
+ cloudiness cloudi
+ clouds cloud
+ cloudy cloudi
+ clout clout
+ clouted clout
+ clouts clout
+ cloven cloven
+ clover clover
+ cloves clove
+ clovest clovest
+ clowder clowder
+ clown clown
+ clownish clownish
+ clowns clown
+ cloy cloi
+ cloyed cloi
+ cloying cloi
+ cloyless cloyless
+ cloyment cloyment
+ cloys cloi
+ club club
+ clubs club
+ cluck cluck
+ clung clung
+ clust clust
+ clusters cluster
+ clutch clutch
+ clyster clyster
+ cneius cneiu
+ cnemies cnemi
+ co co
+ coach coach
+ coaches coach
+ coachmakers coachmak
+ coact coact
+ coactive coactiv
+ coagulate coagul
+ coal coal
+ coals coal
+ coarse coars
+ coarsely coars
+ coast coast
+ coasting coast
+ coasts coast
+ coat coat
+ coated coat
+ coats coat
+ cobble cobbl
+ cobbled cobbl
+ cobbler cobbler
+ cobham cobham
+ cobloaf cobloaf
+ cobweb cobweb
+ cobwebs cobweb
+ cock cock
+ cockatrice cockatric
+ cockatrices cockatric
+ cockle cockl
+ cockled cockl
+ cockney cocknei
+ cockpit cockpit
+ cocks cock
+ cocksure cocksur
+ coctus coctu
+ cocytus cocytu
+ cod cod
+ codding cod
+ codling codl
+ codpiece codpiec
+ codpieces codpiec
+ cods cod
+ coelestibus coelestibu
+ coesar coesar
+ coeur coeur
+ coffer coffer
+ coffers coffer
+ coffin coffin
+ coffins coffin
+ cog cog
+ cogging cog
+ cogitation cogit
+ cogitations cogit
+ cognition cognit
+ cognizance cogniz
+ cogscomb cogscomb
+ cohabitants cohabit
+ coher coher
+ cohere coher
+ coherence coher
+ coherent coher
+ cohorts cohort
+ coif coif
+ coign coign
+ coil coil
+ coin coin
+ coinage coinag
+ coiner coiner
+ coining coin
+ coins coin
+ col col
+ colbrand colbrand
+ colchos colcho
+ cold cold
+ colder colder
+ coldest coldest
+ coldly coldli
+ coldness cold
+ coldspur coldspur
+ colebrook colebrook
+ colic colic
+ collar collar
+ collars collar
+ collateral collater
+ colleagued colleagu
+ collect collect
+ collected collect
+ collection collect
+ college colleg
+ colleges colleg
+ collied colli
+ collier collier
+ colliers collier
+ collop collop
+ collusion collus
+ colme colm
+ colmekill colmekil
+ coloquintida coloquintida
+ color color
+ colors color
+ colossus colossu
+ colour colour
+ colourable colour
+ coloured colour
+ colouring colour
+ colours colour
+ colt colt
+ colted colt
+ colts colt
+ columbine columbin
+ columbines columbin
+ colville colvil
+ com com
+ comagene comagen
+ comart comart
+ comb comb
+ combat combat
+ combatant combat
+ combatants combat
+ combated combat
+ combating combat
+ combin combin
+ combinate combin
+ combination combin
+ combine combin
+ combined combin
+ combless combless
+ combustion combust
+ come come
+ comedian comedian
+ comedians comedian
+ comedy comedi
+ comeliness comeli
+ comely come
+ comer comer
+ comers comer
+ comes come
+ comest comest
+ comet comet
+ cometh cometh
+ comets comet
+ comfect comfect
+ comfit comfit
+ comfits comfit
+ comfort comfort
+ comfortable comfort
+ comforted comfort
+ comforter comfort
+ comforting comfort
+ comfortless comfortless
+ comforts comfort
+ comic comic
+ comical comic
+ coming come
+ comings come
+ cominius cominiu
+ comma comma
+ command command
+ commande command
+ commanded command
+ commander command
+ commanders command
+ commanding command
+ commandment command
+ commandments command
+ commands command
+ comme comm
+ commenc commenc
+ commence commenc
+ commenced commenc
+ commencement commenc
+ commences commenc
+ commencing commenc
+ commend commend
+ commendable commend
+ commendation commend
+ commendations commend
+ commended commend
+ commending commend
+ commends commend
+ comment comment
+ commentaries commentari
+ commenting comment
+ comments comment
+ commerce commerc
+ commingled commingl
+ commiseration commiser
+ commission commiss
+ commissioners commission
+ commissions commiss
+ commit commit
+ commits commit
+ committ committ
+ committed commit
+ committing commit
+ commix commix
+ commixed commix
+ commixtion commixt
+ commixture commixtur
+ commodious commodi
+ commodities commod
+ commodity commod
+ common common
+ commonalty commonalti
+ commoner common
+ commoners common
+ commonly commonli
+ commons common
+ commonweal commonw
+ commonwealth commonwealth
+ commotion commot
+ commotions commot
+ commune commun
+ communicat communicat
+ communicate commun
+ communication commun
+ communities commun
+ community commun
+ comonty comonti
+ compact compact
+ companies compani
+ companion companion
+ companions companion
+ companionship companionship
+ company compani
+ compar compar
+ comparative compar
+ compare compar
+ compared compar
+ comparing compar
+ comparison comparison
+ comparisons comparison
+ compartner compartn
+ compass compass
+ compasses compass
+ compassing compass
+ compassion compass
+ compassionate compassion
+ compeers compeer
+ compel compel
+ compell compel
+ compelled compel
+ compelling compel
+ compels compel
+ compensation compens
+ competence compet
+ competency compet
+ competent compet
+ competitor competitor
+ competitors competitor
+ compil compil
+ compile compil
+ compiled compil
+ complain complain
+ complainer complain
+ complainest complainest
+ complaining complain
+ complainings complain
+ complains complain
+ complaint complaint
+ complaints complaint
+ complement complement
+ complements complement
+ complete complet
+ complexion complexion
+ complexioned complexion
+ complexions complexion
+ complices complic
+ complies compli
+ compliment compliment
+ complimental compliment
+ compliments compliment
+ complot complot
+ complots complot
+ complotted complot
+ comply compli
+ compos compo
+ compose compos
+ composed compos
+ composition composit
+ compost compost
+ composture compostur
+ composure composur
+ compound compound
+ compounded compound
+ compounds compound
+ comprehend comprehend
+ comprehended comprehend
+ comprehends comprehend
+ compremises compremis
+ compris compri
+ comprising compris
+ compromis compromi
+ compromise compromis
+ compt compt
+ comptible comptibl
+ comptrollers comptrol
+ compulsatory compulsatori
+ compulsion compuls
+ compulsive compuls
+ compunctious compuncti
+ computation comput
+ comrade comrad
+ comrades comrad
+ comutual comutu
+ con con
+ concave concav
+ concavities concav
+ conceal conceal
+ concealed conceal
+ concealing conceal
+ concealment conceal
+ concealments conceal
+ conceals conceal
+ conceit conceit
+ conceited conceit
+ conceitless conceitless
+ conceits conceit
+ conceiv conceiv
+ conceive conceiv
+ conceived conceiv
+ conceives conceiv
+ conceiving conceiv
+ conception concept
+ conceptions concept
+ conceptious concepti
+ concern concern
+ concernancy concern
+ concerneth concerneth
+ concerning concern
+ concernings concern
+ concerns concern
+ conclave conclav
+ conclud conclud
+ conclude conclud
+ concluded conclud
+ concludes conclud
+ concluding conclud
+ conclusion conclus
+ conclusions conclus
+ concolinel concolinel
+ concord concord
+ concubine concubin
+ concupiscible concupisc
+ concupy concupi
+ concur concur
+ concurring concur
+ concurs concur
+ condemn condemn
+ condemnation condemn
+ condemned condemn
+ condemning condemn
+ condemns condemn
+ condescend condescend
+ condign condign
+ condition condit
+ conditionally condition
+ conditions condit
+ condole condol
+ condolement condol
+ condoling condol
+ conduce conduc
+ conduct conduct
+ conducted conduct
+ conducting conduct
+ conductor conductor
+ conduit conduit
+ conduits conduit
+ conected conect
+ coney conei
+ confection confect
+ confectionary confectionari
+ confections confect
+ confederacy confederaci
+ confederate confeder
+ confederates confeder
+ confer confer
+ conference confer
+ conferr conferr
+ conferring confer
+ confess confess
+ confessed confess
+ confesses confess
+ confesseth confesseth
+ confessing confess
+ confession confess
+ confessions confess
+ confessor confessor
+ confidence confid
+ confident confid
+ confidently confid
+ confin confin
+ confine confin
+ confined confin
+ confineless confineless
+ confiners confin
+ confines confin
+ confining confin
+ confirm confirm
+ confirmation confirm
+ confirmations confirm
+ confirmed confirm
+ confirmer confirm
+ confirmers confirm
+ confirming confirm
+ confirmities confirm
+ confirms confirm
+ confiscate confisc
+ confiscated confisc
+ confiscation confisc
+ confixed confix
+ conflict conflict
+ conflicting conflict
+ conflicts conflict
+ confluence confluenc
+ conflux conflux
+ conform conform
+ conformable conform
+ confound confound
+ confounded confound
+ confounding confound
+ confounds confound
+ confront confront
+ confronted confront
+ confus confu
+ confused confus
+ confusedly confusedli
+ confusion confus
+ confusions confus
+ confutation confut
+ confutes confut
+ congeal congeal
+ congealed congeal
+ congealment congeal
+ congee conge
+ conger conger
+ congest congest
+ congied congi
+ congratulate congratul
+ congreeing congre
+ congreeted congreet
+ congregate congreg
+ congregated congreg
+ congregation congreg
+ congregations congreg
+ congruent congruent
+ congruing congru
+ conies coni
+ conjectural conjectur
+ conjecture conjectur
+ conjectures conjectur
+ conjoin conjoin
+ conjoined conjoin
+ conjoins conjoin
+ conjointly conjointli
+ conjunct conjunct
+ conjunction conjunct
+ conjunctive conjunct
+ conjur conjur
+ conjuration conjur
+ conjurations conjur
+ conjure conjur
+ conjured conjur
+ conjurer conjur
+ conjurers conjur
+ conjures conjur
+ conjuring conjur
+ conjuro conjuro
+ conn conn
+ connected connect
+ connive conniv
+ conqu conqu
+ conquer conquer
+ conquered conquer
+ conquering conquer
+ conqueror conqueror
+ conquerors conqueror
+ conquers conquer
+ conquest conquest
+ conquests conquest
+ conquring conqur
+ conrade conrad
+ cons con
+ consanguineous consanguin
+ consanguinity consanguin
+ conscienc conscienc
+ conscience conscienc
+ consciences conscienc
+ conscionable conscion
+ consecrate consecr
+ consecrated consecr
+ consecrations consecr
+ consent consent
+ consented consent
+ consenting consent
+ consents consent
+ consequence consequ
+ consequences consequ
+ consequently consequ
+ conserve conserv
+ conserved conserv
+ conserves conserv
+ consider consid
+ considerance consider
+ considerate consider
+ consideration consider
+ considerations consider
+ considered consid
+ considering consid
+ considerings consid
+ considers consid
+ consign consign
+ consigning consign
+ consist consist
+ consisteth consisteth
+ consisting consist
+ consistory consistori
+ consists consist
+ consolate consol
+ consolation consol
+ consonancy conson
+ consonant conson
+ consort consort
+ consorted consort
+ consortest consortest
+ conspectuities conspectu
+ conspir conspir
+ conspiracy conspiraci
+ conspirant conspir
+ conspirator conspir
+ conspirators conspir
+ conspire conspir
+ conspired conspir
+ conspirers conspir
+ conspires conspir
+ conspiring conspir
+ constable constabl
+ constables constabl
+ constance constanc
+ constancies constanc
+ constancy constanc
+ constant constant
+ constantine constantin
+ constantinople constantinopl
+ constantly constantli
+ constellation constel
+ constitution constitut
+ constrain constrain
+ constrained constrain
+ constraineth constraineth
+ constrains constrain
+ constraint constraint
+ constring constr
+ construction construct
+ construe constru
+ consul consul
+ consuls consul
+ consulship consulship
+ consulships consulship
+ consult consult
+ consulting consult
+ consults consult
+ consum consum
+ consume consum
+ consumed consum
+ consumes consum
+ consuming consum
+ consummate consumm
+ consummation consumm
+ consumption consumpt
+ consumptions consumpt
+ contagion contagion
+ contagious contagi
+ contain contain
+ containing contain
+ contains contain
+ contaminate contamin
+ contaminated contamin
+ contemn contemn
+ contemned contemn
+ contemning contemn
+ contemns contemn
+ contemplate contempl
+ contemplation contempl
+ contemplative contempl
+ contempt contempt
+ contemptible contempt
+ contempts contempt
+ contemptuous contemptu
+ contemptuously contemptu
+ contend contend
+ contended contend
+ contending contend
+ contendon contendon
+ content content
+ contenta contenta
+ contented content
+ contenteth contenteth
+ contention content
+ contentious contenti
+ contentless contentless
+ contento contento
+ contents content
+ contest contest
+ contestation contest
+ continence contin
+ continency contin
+ continent contin
+ continents contin
+ continu continu
+ continual continu
+ continually continu
+ continuance continu
+ continuantly continuantli
+ continuate continu
+ continue continu
+ continued continu
+ continuer continu
+ continues continu
+ continuing continu
+ contract contract
+ contracted contract
+ contracting contract
+ contraction contract
+ contradict contradict
+ contradicted contradict
+ contradiction contradict
+ contradicts contradict
+ contraries contrari
+ contrarieties contrarieti
+ contrariety contrarieti
+ contrarious contrari
+ contrariously contrari
+ contrary contrari
+ contre contr
+ contribution contribut
+ contributors contributor
+ contrite contrit
+ contriv contriv
+ contrive contriv
+ contrived contriv
+ contriver contriv
+ contrives contriv
+ contriving contriv
+ control control
+ controll control
+ controller control
+ controlling control
+ controlment control
+ controls control
+ controversy controversi
+ contumelious contumeli
+ contumeliously contumeli
+ contumely contum
+ contusions contus
+ convenience conveni
+ conveniences conveni
+ conveniency conveni
+ convenient conveni
+ conveniently conveni
+ convented convent
+ conventicles conventicl
+ convents convent
+ convers conver
+ conversant convers
+ conversation convers
+ conversations convers
+ converse convers
+ conversed convers
+ converses convers
+ conversing convers
+ conversion convers
+ convert convert
+ converted convert
+ convertest convertest
+ converting convert
+ convertite convertit
+ convertites convertit
+ converts convert
+ convey convei
+ conveyance convey
+ conveyances convey
+ conveyers convey
+ conveying convei
+ convict convict
+ convicted convict
+ convince convinc
+ convinced convinc
+ convinces convinc
+ convive conviv
+ convocation convoc
+ convoy convoi
+ convulsions convuls
+ cony coni
+ cook cook
+ cookery cookeri
+ cooks cook
+ cool cool
+ cooled cool
+ cooling cool
+ cools cool
+ coop coop
+ coops coop
+ cop cop
+ copatain copatain
+ cope cope
+ cophetua cophetua
+ copied copi
+ copies copi
+ copious copiou
+ copper copper
+ copperspur copperspur
+ coppice coppic
+ copulation copul
+ copulatives copul
+ copy copi
+ cor cor
+ coragio coragio
+ coral coral
+ coram coram
+ corambus corambu
+ coranto coranto
+ corantos coranto
+ corbo corbo
+ cord cord
+ corded cord
+ cordelia cordelia
+ cordial cordial
+ cordis cordi
+ cords cord
+ core core
+ corin corin
+ corinth corinth
+ corinthian corinthian
+ coriolanus coriolanu
+ corioli corioli
+ cork cork
+ corky corki
+ cormorant cormor
+ corn corn
+ cornelia cornelia
+ cornelius corneliu
+ corner corner
+ corners corner
+ cornerstone cornerston
+ cornets cornet
+ cornish cornish
+ corns corn
+ cornuto cornuto
+ cornwall cornwal
+ corollary corollari
+ coronal coron
+ coronation coron
+ coronet coronet
+ coronets coronet
+ corporal corpor
+ corporals corpor
+ corporate corpor
+ corpse corps
+ corpulent corpul
+ correct correct
+ corrected correct
+ correcting correct
+ correction correct
+ correctioner correction
+ corrects correct
+ correspondence correspond
+ correspondent correspond
+ corresponding correspond
+ corresponsive correspons
+ corrigible corrig
+ corrival corriv
+ corrivals corriv
+ corroborate corrobor
+ corrosive corros
+ corrupt corrupt
+ corrupted corrupt
+ corrupter corrupt
+ corrupters corrupt
+ corruptible corrupt
+ corruptibly corrupt
+ corrupting corrupt
+ corruption corrupt
+ corruptly corruptli
+ corrupts corrupt
+ corse cors
+ corses cors
+ corslet corslet
+ cosmo cosmo
+ cost cost
+ costard costard
+ costermongers costermong
+ costlier costlier
+ costly costli
+ costs cost
+ cot cot
+ cote cote
+ coted cote
+ cotsall cotsal
+ cotsole cotsol
+ cotswold cotswold
+ cottage cottag
+ cottages cottag
+ cotus cotu
+ couch couch
+ couched couch
+ couching couch
+ couchings couch
+ coude coud
+ cough cough
+ coughing cough
+ could could
+ couldst couldst
+ coulter coulter
+ council council
+ councillor councillor
+ councils council
+ counsel counsel
+ counsell counsel
+ counsellor counsellor
+ counsellors counsellor
+ counselor counselor
+ counselors counselor
+ counsels counsel
+ count count
+ counted count
+ countenanc countenanc
+ countenance counten
+ countenances counten
+ counter counter
+ counterchange counterchang
+ countercheck countercheck
+ counterfeit counterfeit
+ counterfeited counterfeit
+ counterfeiting counterfeit
+ counterfeitly counterfeitli
+ counterfeits counterfeit
+ countermand countermand
+ countermands countermand
+ countermines countermin
+ counterpart counterpart
+ counterpoints counterpoint
+ counterpois counterpoi
+ counterpoise counterpois
+ counters counter
+ countervail countervail
+ countess countess
+ countesses countess
+ counties counti
+ counting count
+ countless countless
+ countries countri
+ countrv countrv
+ country countri
+ countryman countryman
+ countrymen countrymen
+ counts count
+ county counti
+ couper couper
+ couple coupl
+ coupled coupl
+ couplement couplement
+ couples coupl
+ couplet couplet
+ couplets couplet
+ cour cour
+ courage courag
+ courageous courag
+ courageously courag
+ courages courag
+ courier courier
+ couriers courier
+ couronne couronn
+ cours cour
+ course cours
+ coursed cours
+ courser courser
+ coursers courser
+ courses cours
+ coursing cours
+ court court
+ courted court
+ courteous courteou
+ courteously courteous
+ courtesan courtesan
+ courtesies courtesi
+ courtesy courtesi
+ courtezan courtezan
+ courtezans courtezan
+ courtier courtier
+ courtiers courtier
+ courtlike courtlik
+ courtly courtli
+ courtney courtnei
+ courts court
+ courtship courtship
+ cousin cousin
+ cousins cousin
+ couterfeit couterfeit
+ coutume coutum
+ covenant coven
+ covenants coven
+ covent covent
+ coventry coventri
+ cover cover
+ covered cover
+ covering cover
+ coverlet coverlet
+ covers cover
+ covert covert
+ covertly covertli
+ coverture covertur
+ covet covet
+ coveted covet
+ coveting covet
+ covetings covet
+ covetous covet
+ covetously covet
+ covetousness covet
+ covets covet
+ cow cow
+ coward coward
+ cowarded coward
+ cowardice cowardic
+ cowardly cowardli
+ cowards coward
+ cowardship cowardship
+ cowish cowish
+ cowl cowl
+ cowslip cowslip
+ cowslips cowslip
+ cox cox
+ coxcomb coxcomb
+ coxcombs coxcomb
+ coy coi
+ coystrill coystril
+ coz coz
+ cozen cozen
+ cozenage cozenag
+ cozened cozen
+ cozener cozen
+ cozeners cozen
+ cozening cozen
+ coziers cozier
+ crab crab
+ crabbed crab
+ crabs crab
+ crack crack
+ cracked crack
+ cracker cracker
+ crackers cracker
+ cracking crack
+ cracks crack
+ cradle cradl
+ cradled cradl
+ cradles cradl
+ craft craft
+ crafted craft
+ craftied crafti
+ craftier craftier
+ craftily craftili
+ crafts craft
+ craftsmen craftsmen
+ crafty crafti
+ cram cram
+ cramm cramm
+ cramp cramp
+ cramps cramp
+ crams cram
+ cranking crank
+ cranks crank
+ cranmer cranmer
+ crannied cranni
+ crannies cranni
+ cranny cranni
+ crants crant
+ crare crare
+ crash crash
+ crassus crassu
+ crav crav
+ crave crave
+ craved crave
+ craven craven
+ cravens craven
+ craves crave
+ craveth craveth
+ craving crave
+ crawl crawl
+ crawling crawl
+ crawls crawl
+ craz craz
+ crazed craze
+ crazy crazi
+ creaking creak
+ cream cream
+ create creat
+ created creat
+ creates creat
+ creating creat
+ creation creation
+ creator creator
+ creature creatur
+ creatures creatur
+ credence credenc
+ credent credent
+ credible credibl
+ credit credit
+ creditor creditor
+ creditors creditor
+ credo credo
+ credulity credul
+ credulous credul
+ creed creed
+ creek creek
+ creeks creek
+ creep creep
+ creeping creep
+ creeps creep
+ crept crept
+ crescent crescent
+ crescive cresciv
+ cressets cresset
+ cressid cressid
+ cressida cressida
+ cressids cressid
+ cressy cressi
+ crest crest
+ crested crest
+ crestfall crestfal
+ crestless crestless
+ crests crest
+ cretan cretan
+ crete crete
+ crevice crevic
+ crew crew
+ crews crew
+ crib crib
+ cribb cribb
+ cribs crib
+ cricket cricket
+ crickets cricket
+ cried cri
+ criedst criedst
+ crier crier
+ cries cri
+ criest criest
+ crieth crieth
+ crime crime
+ crimeful crime
+ crimeless crimeless
+ crimes crime
+ criminal crimin
+ crimson crimson
+ cringe cring
+ cripple crippl
+ crisp crisp
+ crisped crisp
+ crispian crispian
+ crispianus crispianu
+ crispin crispin
+ critic critic
+ critical critic
+ critics critic
+ croak croak
+ croaking croak
+ croaks croak
+ crocodile crocodil
+ cromer cromer
+ cromwell cromwel
+ crone crone
+ crook crook
+ crookback crookback
+ crooked crook
+ crooking crook
+ crop crop
+ cropp cropp
+ crosby crosbi
+ cross cross
+ crossed cross
+ crosses cross
+ crossest crossest
+ crossing cross
+ crossings cross
+ crossly crossli
+ crossness cross
+ crost crost
+ crotchets crotchet
+ crouch crouch
+ crouching crouch
+ crow crow
+ crowd crowd
+ crowded crowd
+ crowding crowd
+ crowds crowd
+ crowflowers crowflow
+ crowing crow
+ crowkeeper crowkeep
+ crown crown
+ crowned crown
+ crowner crowner
+ crownet crownet
+ crownets crownet
+ crowning crown
+ crowns crown
+ crows crow
+ crudy crudi
+ cruel cruel
+ cruell cruell
+ crueller crueller
+ cruelly cruelli
+ cruels cruel
+ cruelty cruelti
+ crum crum
+ crumble crumbl
+ crumbs crumb
+ crupper crupper
+ crusadoes crusado
+ crush crush
+ crushed crush
+ crushest crushest
+ crushing crush
+ crust crust
+ crusts crust
+ crusty crusti
+ crutch crutch
+ crutches crutch
+ cry cry
+ crying cry
+ crystal crystal
+ crystalline crystallin
+ crystals crystal
+ cub cub
+ cubbert cubbert
+ cubiculo cubiculo
+ cubit cubit
+ cubs cub
+ cuckold cuckold
+ cuckoldly cuckoldli
+ cuckolds cuckold
+ cuckoo cuckoo
+ cucullus cucullu
+ cudgel cudgel
+ cudgeled cudgel
+ cudgell cudgel
+ cudgelling cudgel
+ cudgels cudgel
+ cue cue
+ cues cue
+ cuff cuff
+ cuffs cuff
+ cuique cuiqu
+ cull cull
+ culling cull
+ cullion cullion
+ cullionly cullionli
+ cullions cullion
+ culpable culpabl
+ culverin culverin
+ cum cum
+ cumber cumber
+ cumberland cumberland
+ cunning cun
+ cunningly cunningli
+ cunnings cun
+ cuore cuor
+ cup cup
+ cupbearer cupbear
+ cupboarding cupboard
+ cupid cupid
+ cupids cupid
+ cuppele cuppel
+ cups cup
+ cur cur
+ curan curan
+ curate curat
+ curb curb
+ curbed curb
+ curbing curb
+ curbs curb
+ curd curd
+ curdied curdi
+ curds curd
+ cure cure
+ cured cure
+ cureless cureless
+ curer curer
+ cures cure
+ curfew curfew
+ curing cure
+ curio curio
+ curiosity curios
+ curious curiou
+ curiously curious
+ curl curl
+ curled curl
+ curling curl
+ curls curl
+ currance curranc
+ currants currant
+ current current
+ currents current
+ currish currish
+ curry curri
+ curs cur
+ curse curs
+ cursed curs
+ curses curs
+ cursies cursi
+ cursing curs
+ cursorary cursorari
+ curst curst
+ curster curster
+ curstest curstest
+ curstness curst
+ cursy cursi
+ curtail curtail
+ curtain curtain
+ curtains curtain
+ curtal curtal
+ curtis curti
+ curtle curtl
+ curtsied curtsi
+ curtsies curtsi
+ curtsy curtsi
+ curvet curvet
+ curvets curvet
+ cushes cush
+ cushion cushion
+ cushions cushion
+ custalorum custalorum
+ custard custard
+ custody custodi
+ custom custom
+ customary customari
+ customed custom
+ customer custom
+ customers custom
+ customs custom
+ custure custur
+ cut cut
+ cutler cutler
+ cutpurse cutpurs
+ cutpurses cutpurs
+ cuts cut
+ cutter cutter
+ cutting cut
+ cuttle cuttl
+ cxsar cxsar
+ cyclops cyclop
+ cydnus cydnu
+ cygnet cygnet
+ cygnets cygnet
+ cym cym
+ cymbals cymbal
+ cymbeline cymbelin
+ cyme cyme
+ cynic cynic
+ cynthia cynthia
+ cypress cypress
+ cypriot cypriot
+ cyprus cypru
+ cyrus cyru
+ cytherea cytherea
+ d d
+ dabbled dabbl
+ dace dace
+ dad dad
+ daedalus daedalu
+ daemon daemon
+ daff daff
+ daffed daf
+ daffest daffest
+ daffodils daffodil
+ dagger dagger
+ daggers dagger
+ dagonet dagonet
+ daily daili
+ daintier daintier
+ dainties dainti
+ daintiest daintiest
+ daintily daintili
+ daintiness dainti
+ daintry daintri
+ dainty dainti
+ daisied daisi
+ daisies daisi
+ daisy daisi
+ dale dale
+ dalliance dallianc
+ dallied dalli
+ dallies dalli
+ dally dalli
+ dallying dalli
+ dalmatians dalmatian
+ dam dam
+ damage damag
+ damascus damascu
+ damask damask
+ damasked damask
+ dame dame
+ dames dame
+ damm damm
+ damn damn
+ damnable damnabl
+ damnably damnabl
+ damnation damnat
+ damned damn
+ damns damn
+ damoiselle damoisel
+ damon damon
+ damosella damosella
+ damp damp
+ dams dam
+ damsel damsel
+ damsons damson
+ dan dan
+ danc danc
+ dance danc
+ dancer dancer
+ dances danc
+ dancing danc
+ dandle dandl
+ dandy dandi
+ dane dane
+ dang dang
+ danger danger
+ dangerous danger
+ dangerously danger
+ dangers danger
+ dangling dangl
+ daniel daniel
+ danish danish
+ dank dank
+ dankish dankish
+ danskers dansker
+ daphne daphn
+ dappled dappl
+ dapples dappl
+ dar dar
+ dardan dardan
+ dardanian dardanian
+ dardanius dardaniu
+ dare dare
+ dared dare
+ dareful dare
+ dares dare
+ darest darest
+ daring dare
+ darius dariu
+ dark dark
+ darken darken
+ darkening darken
+ darkens darken
+ darker darker
+ darkest darkest
+ darkling darkl
+ darkly darkli
+ darkness dark
+ darling darl
+ darlings darl
+ darnel darnel
+ darraign darraign
+ dart dart
+ darted dart
+ darter darter
+ dartford dartford
+ darting dart
+ darts dart
+ dash dash
+ dashes dash
+ dashing dash
+ dastard dastard
+ dastards dastard
+ dat dat
+ datchet datchet
+ date date
+ dated date
+ dateless dateless
+ dates date
+ daub daub
+ daughter daughter
+ daughters daughter
+ daunt daunt
+ daunted daunt
+ dauntless dauntless
+ dauphin dauphin
+ daventry daventri
+ davy davi
+ daw daw
+ dawn dawn
+ dawning dawn
+ daws daw
+ day dai
+ daylight daylight
+ days dai
+ dazzle dazzl
+ dazzled dazzl
+ dazzling dazzl
+ de de
+ dead dead
+ deadly deadli
+ deaf deaf
+ deafing deaf
+ deafness deaf
+ deafs deaf
+ deal deal
+ dealer dealer
+ dealers dealer
+ dealest dealest
+ dealing deal
+ dealings deal
+ deals deal
+ dealt dealt
+ dean dean
+ deanery deaneri
+ dear dear
+ dearer dearer
+ dearest dearest
+ dearly dearli
+ dearness dear
+ dears dear
+ dearth dearth
+ dearths dearth
+ death death
+ deathbed deathb
+ deathful death
+ deaths death
+ deathsman deathsman
+ deathsmen deathsmen
+ debarred debar
+ debase debas
+ debate debat
+ debated debat
+ debatement debat
+ debateth debateth
+ debating debat
+ debauch debauch
+ debile debil
+ debility debil
+ debitor debitor
+ debonair debonair
+ deborah deborah
+ debosh debosh
+ debt debt
+ debted debt
+ debtor debtor
+ debtors debtor
+ debts debt
+ debuty debuti
+ decay decai
+ decayed decai
+ decayer decay
+ decaying decai
+ decays decai
+ deceas decea
+ decease deceas
+ deceased deceas
+ deceit deceit
+ deceitful deceit
+ deceits deceit
+ deceiv deceiv
+ deceivable deceiv
+ deceive deceiv
+ deceived deceiv
+ deceiver deceiv
+ deceivers deceiv
+ deceives deceiv
+ deceivest deceivest
+ deceiveth deceiveth
+ deceiving deceiv
+ december decemb
+ decent decent
+ deceptious decepti
+ decerns decern
+ decide decid
+ decides decid
+ decimation decim
+ decipher deciph
+ deciphers deciph
+ decision decis
+ decius deciu
+ deck deck
+ decking deck
+ decks deck
+ deckt deckt
+ declare declar
+ declares declar
+ declension declens
+ declensions declens
+ declin declin
+ decline declin
+ declined declin
+ declines declin
+ declining declin
+ decoct decoct
+ decorum decorum
+ decreas decrea
+ decrease decreas
+ decreasing decreas
+ decree decre
+ decreed decre
+ decrees decre
+ decrepit decrepit
+ dedicate dedic
+ dedicated dedic
+ dedicates dedic
+ dedication dedic
+ deed deed
+ deedless deedless
+ deeds deed
+ deem deem
+ deemed deem
+ deep deep
+ deeper deeper
+ deepest deepest
+ deeply deepli
+ deeps deep
+ deepvow deepvow
+ deer deer
+ deesse deess
+ defac defac
+ deface defac
+ defaced defac
+ defacer defac
+ defacers defac
+ defacing defac
+ defam defam
+ default default
+ defeat defeat
+ defeated defeat
+ defeats defeat
+ defeatures defeatur
+ defect defect
+ defective defect
+ defects defect
+ defence defenc
+ defences defenc
+ defend defend
+ defendant defend
+ defended defend
+ defender defend
+ defenders defend
+ defending defend
+ defends defend
+ defense defens
+ defensible defens
+ defensive defens
+ defer defer
+ deferr deferr
+ defiance defianc
+ deficient defici
+ defied defi
+ defies defi
+ defil defil
+ defile defil
+ defiler defil
+ defiles defil
+ defiling defil
+ define defin
+ definement defin
+ definite definit
+ definitive definit
+ definitively definit
+ deflow deflow
+ deflower deflow
+ deflowered deflow
+ deform deform
+ deformed deform
+ deformities deform
+ deformity deform
+ deftly deftli
+ defunct defunct
+ defunction defunct
+ defuse defus
+ defy defi
+ defying defi
+ degenerate degener
+ degraded degrad
+ degree degre
+ degrees degre
+ deified deifi
+ deifying deifi
+ deign deign
+ deigned deign
+ deiphobus deiphobu
+ deities deiti
+ deity deiti
+ deja deja
+ deject deject
+ dejected deject
+ delabreth delabreth
+ delay delai
+ delayed delai
+ delaying delai
+ delays delai
+ delectable delect
+ deliberate deliber
+ delicate delic
+ delicates delic
+ delicious delici
+ deliciousness delici
+ delight delight
+ delighted delight
+ delightful delight
+ delights delight
+ delinquents delinqu
+ deliv deliv
+ deliver deliv
+ deliverance deliver
+ delivered deliv
+ delivering deliv
+ delivers deliv
+ delivery deliveri
+ delphos delpho
+ deluded delud
+ deluding delud
+ deluge delug
+ delve delv
+ delver delver
+ delves delv
+ demand demand
+ demanded demand
+ demanding demand
+ demands demand
+ demean demean
+ demeanor demeanor
+ demeanour demeanour
+ demerits demerit
+ demesnes demesn
+ demetrius demetriu
+ demi demi
+ demigod demigod
+ demise demis
+ demoiselles demoisel
+ demon demon
+ demonstrable demonstr
+ demonstrate demonstr
+ demonstrated demonstr
+ demonstrating demonstr
+ demonstration demonstr
+ demonstrative demonstr
+ demure demur
+ demurely demur
+ demuring demur
+ den den
+ denay denai
+ deni deni
+ denial denial
+ denials denial
+ denied deni
+ denier denier
+ denies deni
+ deniest deniest
+ denis deni
+ denmark denmark
+ dennis denni
+ denny denni
+ denote denot
+ denoted denot
+ denotement denot
+ denounc denounc
+ denounce denounc
+ denouncing denounc
+ dens den
+ denunciation denunci
+ deny deni
+ denying deni
+ deo deo
+ depart depart
+ departed depart
+ departest departest
+ departing depart
+ departure departur
+ depeche depech
+ depend depend
+ dependant depend
+ dependants depend
+ depended depend
+ dependence depend
+ dependences depend
+ dependency depend
+ dependent depend
+ dependents depend
+ depender depend
+ depending depend
+ depends depend
+ deplore deplor
+ deploring deplor
+ depopulate depopul
+ depos depo
+ depose depos
+ deposed depos
+ deposing depos
+ depositaries depositari
+ deprav deprav
+ depravation deprav
+ deprave deprav
+ depraved deprav
+ depraves deprav
+ depress depress
+ depriv depriv
+ deprive depriv
+ depth depth
+ depths depth
+ deputation deput
+ depute deput
+ deputed deput
+ deputies deputi
+ deputing deput
+ deputy deputi
+ deracinate deracin
+ derby derbi
+ dercetas derceta
+ dere dere
+ derides derid
+ derision deris
+ deriv deriv
+ derivation deriv
+ derivative deriv
+ derive deriv
+ derived deriv
+ derives deriv
+ derogate derog
+ derogately derog
+ derogation derog
+ des de
+ desartless desartless
+ descant descant
+ descend descend
+ descended descend
+ descending descend
+ descends descend
+ descension descens
+ descent descent
+ descents descent
+ describe describ
+ described describ
+ describes describ
+ descried descri
+ description descript
+ descriptions descript
+ descry descri
+ desdemon desdemon
+ desdemona desdemona
+ desert desert
+ deserts desert
+ deserv deserv
+ deserve deserv
+ deserved deserv
+ deservedly deservedli
+ deserver deserv
+ deservers deserv
+ deserves deserv
+ deservest deservest
+ deserving deserv
+ deservings deserv
+ design design
+ designment design
+ designments design
+ designs design
+ desir desir
+ desire desir
+ desired desir
+ desirers desir
+ desires desir
+ desirest desirest
+ desiring desir
+ desirous desir
+ desist desist
+ desk desk
+ desolate desol
+ desolation desol
+ desp desp
+ despair despair
+ despairing despair
+ despairs despair
+ despatch despatch
+ desperate desper
+ desperately desper
+ desperation desper
+ despis despi
+ despise despis
+ despised despis
+ despiser despis
+ despiseth despiseth
+ despising despis
+ despite despit
+ despiteful despit
+ despoiled despoil
+ dest dest
+ destin destin
+ destined destin
+ destinies destini
+ destiny destini
+ destitute destitut
+ destroy destroi
+ destroyed destroi
+ destroyer destroy
+ destroyers destroy
+ destroying destroi
+ destroys destroi
+ destruction destruct
+ destructions destruct
+ det det
+ detain detain
+ detains detain
+ detect detect
+ detected detect
+ detecting detect
+ detection detect
+ detector detector
+ detects detect
+ detention detent
+ determin determin
+ determinate determin
+ determination determin
+ determinations determin
+ determine determin
+ determined determin
+ determines determin
+ detest detest
+ detestable detest
+ detested detest
+ detesting detest
+ detests detest
+ detract detract
+ detraction detract
+ detractions detract
+ deucalion deucalion
+ deuce deuc
+ deum deum
+ deux deux
+ devant devant
+ devesting devest
+ device devic
+ devices devic
+ devil devil
+ devilish devilish
+ devils devil
+ devis devi
+ devise devis
+ devised devis
+ devises devis
+ devising devis
+ devoid devoid
+ devonshire devonshir
+ devote devot
+ devoted devot
+ devotion devot
+ devour devour
+ devoured devour
+ devourers devour
+ devouring devour
+ devours devour
+ devout devout
+ devoutly devoutli
+ dew dew
+ dewberries dewberri
+ dewdrops dewdrop
+ dewlap dewlap
+ dewlapp dewlapp
+ dews dew
+ dewy dewi
+ dexter dexter
+ dexteriously dexteri
+ dexterity dexter
+ di di
+ diable diabl
+ diablo diablo
+ diadem diadem
+ dial dial
+ dialect dialect
+ dialogue dialogu
+ dialogued dialogu
+ dials dial
+ diameter diamet
+ diamond diamond
+ diamonds diamond
+ dian dian
+ diana diana
+ diaper diaper
+ dibble dibbl
+ dic dic
+ dice dice
+ dicers dicer
+ dich dich
+ dick dick
+ dickens dicken
+ dickon dickon
+ dicky dicki
+ dictator dictat
+ diction diction
+ dictynna dictynna
+ did did
+ diddle diddl
+ didest didest
+ dido dido
+ didst didst
+ die die
+ died di
+ diedst diedst
+ dies di
+ diest diest
+ diet diet
+ dieted diet
+ dieter dieter
+ dieu dieu
+ diff diff
+ differ differ
+ difference differ
+ differences differ
+ differency differ
+ different differ
+ differing differ
+ differs differ
+ difficile difficil
+ difficult difficult
+ difficulties difficulti
+ difficulty difficulti
+ diffidence diffid
+ diffidences diffid
+ diffus diffu
+ diffused diffus
+ diffusest diffusest
+ dig dig
+ digest digest
+ digested digest
+ digestion digest
+ digestions digest
+ digg digg
+ digging dig
+ dighton dighton
+ dignified dignifi
+ dignifies dignifi
+ dignify dignifi
+ dignities digniti
+ dignity digniti
+ digress digress
+ digressing digress
+ digression digress
+ digs dig
+ digt digt
+ dilate dilat
+ dilated dilat
+ dilations dilat
+ dilatory dilatori
+ dild dild
+ dildos dildo
+ dilemma dilemma
+ dilemmas dilemma
+ diligence dilig
+ diligent dilig
+ diluculo diluculo
+ dim dim
+ dimension dimens
+ dimensions dimens
+ diminish diminish
+ diminishing diminish
+ diminution diminut
+ diminutive diminut
+ diminutives diminut
+ dimm dimm
+ dimmed dim
+ dimming dim
+ dimpled dimpl
+ dimples dimpl
+ dims dim
+ din din
+ dine dine
+ dined dine
+ diner diner
+ dines dine
+ ding ding
+ dining dine
+ dinner dinner
+ dinners dinner
+ dinnertime dinnertim
+ dint dint
+ diomed diom
+ diomede diomed
+ diomedes diomed
+ dion dion
+ dip dip
+ dipp dipp
+ dipping dip
+ dips dip
+ dir dir
+ dire dire
+ direct direct
+ directed direct
+ directing direct
+ direction direct
+ directions direct
+ directitude directitud
+ directive direct
+ directly directli
+ directs direct
+ direful dire
+ direness dire
+ direst direst
+ dirge dirg
+ dirges dirg
+ dirt dirt
+ dirty dirti
+ dis di
+ disability disabl
+ disable disabl
+ disabled disabl
+ disabling disabl
+ disadvantage disadvantag
+ disagree disagre
+ disallow disallow
+ disanimates disanim
+ disannul disannul
+ disannuls disannul
+ disappointed disappoint
+ disarm disarm
+ disarmed disarm
+ disarmeth disarmeth
+ disarms disarm
+ disaster disast
+ disasters disast
+ disastrous disastr
+ disbench disbench
+ disbranch disbranch
+ disburdened disburden
+ disburs disbur
+ disburse disburs
+ disbursed disburs
+ discandy discandi
+ discandying discandi
+ discard discard
+ discarded discard
+ discase discas
+ discased discas
+ discern discern
+ discerner discern
+ discerning discern
+ discernings discern
+ discerns discern
+ discharg discharg
+ discharge discharg
+ discharged discharg
+ discharging discharg
+ discipled discipl
+ disciples discipl
+ disciplin disciplin
+ discipline disciplin
+ disciplined disciplin
+ disciplines disciplin
+ disclaim disclaim
+ disclaiming disclaim
+ disclaims disclaim
+ disclos disclo
+ disclose disclos
+ disclosed disclos
+ discloses disclos
+ discolour discolour
+ discoloured discolour
+ discolours discolour
+ discomfit discomfit
+ discomfited discomfit
+ discomfiture discomfitur
+ discomfort discomfort
+ discomfortable discomfort
+ discommend discommend
+ disconsolate disconsol
+ discontent discont
+ discontented discont
+ discontentedly discontentedli
+ discontenting discont
+ discontents discont
+ discontinue discontinu
+ discontinued discontinu
+ discord discord
+ discordant discord
+ discords discord
+ discourse discours
+ discoursed discours
+ discourser discours
+ discourses discours
+ discoursive discours
+ discourtesy discourtesi
+ discov discov
+ discover discov
+ discovered discov
+ discoverers discover
+ discoveries discoveri
+ discovering discov
+ discovers discov
+ discovery discoveri
+ discredit discredit
+ discredited discredit
+ discredits discredit
+ discreet discreet
+ discreetly discreetli
+ discretion discret
+ discretions discret
+ discuss discuss
+ disdain disdain
+ disdained disdain
+ disdaineth disdaineth
+ disdainful disdain
+ disdainfully disdainfulli
+ disdaining disdain
+ disdains disdain
+ disdnguish disdnguish
+ diseas disea
+ disease diseas
+ diseased diseas
+ diseases diseas
+ disedg disedg
+ disembark disembark
+ disfigure disfigur
+ disfigured disfigur
+ disfurnish disfurnish
+ disgorge disgorg
+ disgrac disgrac
+ disgrace disgrac
+ disgraced disgrac
+ disgraceful disgrac
+ disgraces disgrac
+ disgracing disgrac
+ disgracious disgraci
+ disguis disgui
+ disguise disguis
+ disguised disguis
+ disguiser disguis
+ disguises disguis
+ disguising disguis
+ dish dish
+ dishabited dishabit
+ dishclout dishclout
+ dishearten dishearten
+ disheartens dishearten
+ dishes dish
+ dishonest dishonest
+ dishonestly dishonestli
+ dishonesty dishonesti
+ dishonor dishonor
+ dishonorable dishonor
+ dishonors dishonor
+ dishonour dishonour
+ dishonourable dishonour
+ dishonoured dishonour
+ dishonours dishonour
+ disinherit disinherit
+ disinherited disinherit
+ disjoin disjoin
+ disjoining disjoin
+ disjoins disjoin
+ disjoint disjoint
+ disjunction disjunct
+ dislik dislik
+ dislike dislik
+ disliken disliken
+ dislikes dislik
+ dislimns dislimn
+ dislocate disloc
+ dislodg dislodg
+ disloyal disloy
+ disloyalty disloyalti
+ dismal dismal
+ dismantle dismantl
+ dismantled dismantl
+ dismask dismask
+ dismay dismai
+ dismayed dismai
+ dismemb dismemb
+ dismember dismemb
+ dismes dism
+ dismiss dismiss
+ dismissed dismiss
+ dismissing dismiss
+ dismission dismiss
+ dismount dismount
+ dismounted dismount
+ disnatur disnatur
+ disobedience disobedi
+ disobedient disobedi
+ disobey disobei
+ disobeys disobei
+ disorb disorb
+ disorder disord
+ disordered disord
+ disorderly disorderli
+ disorders disord
+ disparage disparag
+ disparagement disparag
+ disparagements disparag
+ dispark dispark
+ dispatch dispatch
+ dispensation dispens
+ dispense dispens
+ dispenses dispens
+ dispers disper
+ disperse dispers
+ dispersed dispers
+ dispersedly dispersedli
+ dispersing dispers
+ dispiteous dispit
+ displac displac
+ displace displac
+ displaced displac
+ displant displant
+ displanting displant
+ display displai
+ displayed displai
+ displeas displea
+ displease displeas
+ displeased displeas
+ displeasing displeas
+ displeasure displeasur
+ displeasures displeasur
+ disponge dispong
+ disport disport
+ disports disport
+ dispos dispo
+ dispose dispos
+ disposed dispos
+ disposer dispos
+ disposing dispos
+ disposition disposit
+ dispositions disposit
+ dispossess dispossess
+ dispossessing dispossess
+ disprais disprai
+ dispraise disprais
+ dispraising disprais
+ dispraisingly dispraisingli
+ dispropertied disproperti
+ disproportion disproport
+ disproportioned disproport
+ disprov disprov
+ disprove disprov
+ disproved disprov
+ dispursed dispurs
+ disputable disput
+ disputation disput
+ disputations disput
+ dispute disput
+ disputed disput
+ disputes disput
+ disputing disput
+ disquantity disquant
+ disquiet disquiet
+ disquietly disquietli
+ disrelish disrelish
+ disrobe disrob
+ disseat disseat
+ dissemble dissembl
+ dissembled dissembl
+ dissembler dissembl
+ dissemblers dissembl
+ dissembling dissembl
+ dissembly dissembl
+ dissension dissens
+ dissensions dissens
+ dissentious dissenti
+ dissever dissev
+ dissipation dissip
+ dissolute dissolut
+ dissolutely dissolut
+ dissolution dissolut
+ dissolutions dissolut
+ dissolv dissolv
+ dissolve dissolv
+ dissolved dissolv
+ dissolves dissolv
+ dissuade dissuad
+ dissuaded dissuad
+ distaff distaff
+ distaffs distaff
+ distain distain
+ distains distain
+ distance distanc
+ distant distant
+ distaste distast
+ distasted distast
+ distasteful distast
+ distemp distemp
+ distemper distemp
+ distemperature distemperatur
+ distemperatures distemperatur
+ distempered distemp
+ distempering distemp
+ distil distil
+ distill distil
+ distillation distil
+ distilled distil
+ distills distil
+ distilment distil
+ distinct distinct
+ distinction distinct
+ distinctly distinctli
+ distingue distingu
+ distinguish distinguish
+ distinguishes distinguish
+ distinguishment distinguish
+ distract distract
+ distracted distract
+ distractedly distractedli
+ distraction distract
+ distractions distract
+ distracts distract
+ distrain distrain
+ distraught distraught
+ distress distress
+ distressed distress
+ distresses distress
+ distressful distress
+ distribute distribut
+ distributed distribut
+ distribution distribut
+ distrust distrust
+ distrustful distrust
+ disturb disturb
+ disturbed disturb
+ disturbers disturb
+ disturbing disturb
+ disunite disunit
+ disvalued disvalu
+ disvouch disvouch
+ dit dit
+ ditch ditch
+ ditchers ditcher
+ ditches ditch
+ dites dite
+ ditties ditti
+ ditty ditti
+ diurnal diurnal
+ div div
+ dive dive
+ diver diver
+ divers diver
+ diversely divers
+ diversity divers
+ divert divert
+ diverted divert
+ diverts divert
+ dives dive
+ divest divest
+ dividable divid
+ dividant divid
+ divide divid
+ divided divid
+ divides divid
+ divideth divideth
+ divin divin
+ divination divin
+ divine divin
+ divinely divin
+ divineness divin
+ diviner divin
+ divines divin
+ divinest divinest
+ divining divin
+ divinity divin
+ division divis
+ divisions divis
+ divorc divorc
+ divorce divorc
+ divorced divorc
+ divorcement divorc
+ divorcing divorc
+ divulg divulg
+ divulge divulg
+ divulged divulg
+ divulging divulg
+ dizy dizi
+ dizzy dizzi
+ do do
+ doating doat
+ dobbin dobbin
+ dock dock
+ docks dock
+ doct doct
+ doctor doctor
+ doctors doctor
+ doctrine doctrin
+ document document
+ dodge dodg
+ doe doe
+ doer doer
+ doers doer
+ does doe
+ doest doest
+ doff doff
+ dog dog
+ dogberry dogberri
+ dogfish dogfish
+ dogg dogg
+ dogged dog
+ dogs dog
+ doigts doigt
+ doing do
+ doings do
+ doit doit
+ doits doit
+ dolabella dolabella
+ dole dole
+ doleful dole
+ doll doll
+ dollar dollar
+ dollars dollar
+ dolor dolor
+ dolorous dolor
+ dolour dolour
+ dolours dolour
+ dolphin dolphin
+ dolt dolt
+ dolts dolt
+ domestic domest
+ domestics domest
+ dominance domin
+ dominations domin
+ dominator domin
+ domine domin
+ domineer domin
+ domineering domin
+ dominical domin
+ dominion dominion
+ dominions dominion
+ domitius domitiu
+ dommelton dommelton
+ don don
+ donalbain donalbain
+ donation donat
+ donc donc
+ doncaster doncast
+ done done
+ dong dong
+ donn donn
+ donne donn
+ donner donner
+ donnerai donnerai
+ doom doom
+ doomsday doomsdai
+ door door
+ doorkeeper doorkeep
+ doors door
+ dorcas dorca
+ doreus doreu
+ doricles doricl
+ dormouse dormous
+ dorothy dorothi
+ dorset dorset
+ dorsetshire dorsetshir
+ dost dost
+ dotage dotag
+ dotant dotant
+ dotard dotard
+ dotards dotard
+ dote dote
+ doted dote
+ doters doter
+ dotes dote
+ doteth doteth
+ doth doth
+ doting dote
+ double doubl
+ doubled doubl
+ doubleness doubl
+ doubler doubler
+ doublet doublet
+ doublets doublet
+ doubling doubl
+ doubly doubli
+ doubt doubt
+ doubted doubt
+ doubtful doubt
+ doubtfully doubtfulli
+ doubting doubt
+ doubtless doubtless
+ doubts doubt
+ doug doug
+ dough dough
+ doughty doughti
+ doughy doughi
+ douglas dougla
+ dout dout
+ doute dout
+ douts dout
+ dove dove
+ dovehouse dovehous
+ dover dover
+ doves dove
+ dow dow
+ dowager dowag
+ dowdy dowdi
+ dower dower
+ dowerless dowerless
+ dowers dower
+ dowlas dowla
+ dowle dowl
+ down down
+ downfall downfal
+ downright downright
+ downs down
+ downstairs downstair
+ downtrod downtrod
+ downward downward
+ downwards downward
+ downy downi
+ dowries dowri
+ dowry dowri
+ dowsabel dowsabel
+ doxy doxi
+ dozed doze
+ dozen dozen
+ dozens dozen
+ dozy dozi
+ drab drab
+ drabbing drab
+ drabs drab
+ drachma drachma
+ drachmas drachma
+ draff draff
+ drag drag
+ dragg dragg
+ dragged drag
+ dragging drag
+ dragon dragon
+ dragonish dragonish
+ dragons dragon
+ drain drain
+ drained drain
+ drains drain
+ drake drake
+ dram dram
+ dramatis dramati
+ drank drank
+ draught draught
+ draughts draught
+ drave drave
+ draw draw
+ drawbridge drawbridg
+ drawer drawer
+ drawers drawer
+ draweth draweth
+ drawing draw
+ drawling drawl
+ drawn drawn
+ draws draw
+ drayman drayman
+ draymen draymen
+ dread dread
+ dreaded dread
+ dreadful dread
+ dreadfully dreadfulli
+ dreading dread
+ dreads dread
+ dream dream
+ dreamer dreamer
+ dreamers dreamer
+ dreaming dream
+ dreams dream
+ dreamt dreamt
+ drearning drearn
+ dreary dreari
+ dreg dreg
+ dregs dreg
+ drench drench
+ drenched drench
+ dress dress
+ dressed dress
+ dresser dresser
+ dressing dress
+ dressings dress
+ drest drest
+ drew drew
+ dribbling dribbl
+ dried dri
+ drier drier
+ dries dri
+ drift drift
+ drily drili
+ drink drink
+ drinketh drinketh
+ drinking drink
+ drinkings drink
+ drinks drink
+ driv driv
+ drive drive
+ drivelling drivel
+ driven driven
+ drives drive
+ driveth driveth
+ driving drive
+ drizzle drizzl
+ drizzled drizzl
+ drizzles drizzl
+ droit droit
+ drollery drolleri
+ dromio dromio
+ dromios dromio
+ drone drone
+ drones drone
+ droop droop
+ droopeth droopeth
+ drooping droop
+ droops droop
+ drop drop
+ dropheir dropheir
+ droplets droplet
+ dropp dropp
+ dropper dropper
+ droppeth droppeth
+ dropping drop
+ droppings drop
+ drops drop
+ dropsied dropsi
+ dropsies dropsi
+ dropsy dropsi
+ dropt dropt
+ dross dross
+ drossy drossi
+ drought drought
+ drove drove
+ droven droven
+ drovier drovier
+ drown drown
+ drowned drown
+ drowning drown
+ drowns drown
+ drows drow
+ drowse drows
+ drowsily drowsili
+ drowsiness drowsi
+ drowsy drowsi
+ drudge drudg
+ drudgery drudgeri
+ drudges drudg
+ drug drug
+ drugg drugg
+ drugs drug
+ drum drum
+ drumble drumbl
+ drummer drummer
+ drumming drum
+ drums drum
+ drunk drunk
+ drunkard drunkard
+ drunkards drunkard
+ drunken drunken
+ drunkenly drunkenli
+ drunkenness drunken
+ dry dry
+ dryness dryness
+ dst dst
+ du du
+ dub dub
+ dubb dubb
+ ducat ducat
+ ducats ducat
+ ducdame ducdam
+ duchess duchess
+ duchies duchi
+ duchy duchi
+ duck duck
+ ducking duck
+ ducks duck
+ dudgeon dudgeon
+ due due
+ duellist duellist
+ duello duello
+ duer duer
+ dues due
+ duff duff
+ dug dug
+ dugs dug
+ duke duke
+ dukedom dukedom
+ dukedoms dukedom
+ dukes duke
+ dulcet dulcet
+ dulche dulch
+ dull dull
+ dullard dullard
+ duller duller
+ dullest dullest
+ dulling dull
+ dullness dull
+ dulls dull
+ dully dulli
+ dulness dul
+ duly duli
+ dumain dumain
+ dumb dumb
+ dumbe dumb
+ dumbly dumbl
+ dumbness dumb
+ dump dump
+ dumps dump
+ dun dun
+ duncan duncan
+ dung dung
+ dungeon dungeon
+ dungeons dungeon
+ dunghill dunghil
+ dunghills dunghil
+ dungy dungi
+ dunnest dunnest
+ dunsinane dunsinan
+ dunsmore dunsmor
+ dunstable dunstabl
+ dupp dupp
+ durance duranc
+ during dure
+ durst durst
+ dusky duski
+ dust dust
+ dusted dust
+ dusty dusti
+ dutch dutch
+ dutchman dutchman
+ duteous duteou
+ duties duti
+ dutiful duti
+ duty duti
+ dwarf dwarf
+ dwarfish dwarfish
+ dwell dwell
+ dwellers dweller
+ dwelling dwell
+ dwells dwell
+ dwelt dwelt
+ dwindle dwindl
+ dy dy
+ dye dye
+ dyed dy
+ dyer dyer
+ dying dy
+ e e
+ each each
+ eager eager
+ eagerly eagerli
+ eagerness eager
+ eagle eagl
+ eagles eagl
+ eaning ean
+ eanlings eanl
+ ear ear
+ earing ear
+ earl earl
+ earldom earldom
+ earlier earlier
+ earliest earliest
+ earliness earli
+ earls earl
+ early earli
+ earn earn
+ earned earn
+ earnest earnest
+ earnestly earnestli
+ earnestness earnest
+ earns earn
+ ears ear
+ earth earth
+ earthen earthen
+ earthlier earthlier
+ earthly earthli
+ earthquake earthquak
+ earthquakes earthquak
+ earthy earthi
+ eas ea
+ ease eas
+ eased eas
+ easeful eas
+ eases eas
+ easier easier
+ easiest easiest
+ easiliest easiliest
+ easily easili
+ easiness easi
+ easing eas
+ east east
+ eastcheap eastcheap
+ easter easter
+ eastern eastern
+ eastward eastward
+ easy easi
+ eat eat
+ eaten eaten
+ eater eater
+ eaters eater
+ eating eat
+ eats eat
+ eaux eaux
+ eaves eav
+ ebb ebb
+ ebbing eb
+ ebbs ebb
+ ebon ebon
+ ebony eboni
+ ebrew ebrew
+ ecce ecc
+ echapper echapp
+ echo echo
+ echoes echo
+ eclips eclip
+ eclipse eclips
+ eclipses eclips
+ ecolier ecoli
+ ecoutez ecoutez
+ ecstacy ecstaci
+ ecstasies ecstasi
+ ecstasy ecstasi
+ ecus ecu
+ eden eden
+ edg edg
+ edgar edgar
+ edge edg
+ edged edg
+ edgeless edgeless
+ edges edg
+ edict edict
+ edicts edict
+ edifice edific
+ edifices edific
+ edified edifi
+ edifies edifi
+ edition edit
+ edm edm
+ edmund edmund
+ edmunds edmund
+ edmundsbury edmundsburi
+ educate educ
+ educated educ
+ education educ
+ edward edward
+ eel eel
+ eels eel
+ effect effect
+ effected effect
+ effectless effectless
+ effects effect
+ effectual effectu
+ effectually effectu
+ effeminate effemin
+ effigies effigi
+ effus effu
+ effuse effus
+ effusion effus
+ eftest eftest
+ egal egal
+ egally egal
+ eget eget
+ egeus egeu
+ egg egg
+ eggs egg
+ eggshell eggshel
+ eglamour eglamour
+ eglantine eglantin
+ egma egma
+ ego ego
+ egregious egregi
+ egregiously egregi
+ egress egress
+ egypt egypt
+ egyptian egyptian
+ egyptians egyptian
+ eie eie
+ eight eight
+ eighteen eighteen
+ eighth eighth
+ eightpenny eightpenni
+ eighty eighti
+ eisel eisel
+ either either
+ eject eject
+ eke ek
+ el el
+ elbe elb
+ elbow elbow
+ elbows elbow
+ eld eld
+ elder elder
+ elders elder
+ eldest eldest
+ eleanor eleanor
+ elect elect
+ elected elect
+ election elect
+ elegancy eleg
+ elegies elegi
+ element element
+ elements element
+ elephant eleph
+ elephants eleph
+ elevated elev
+ eleven eleven
+ eleventh eleventh
+ elf elf
+ elflocks elflock
+ eliads eliad
+ elinor elinor
+ elizabeth elizabeth
+ ell ell
+ elle ell
+ ellen ellen
+ elm elm
+ eloquence eloqu
+ eloquent eloqu
+ else els
+ elsewhere elsewher
+ elsinore elsinor
+ eltham eltham
+ elves elv
+ elvish elvish
+ ely eli
+ elysium elysium
+ em em
+ emballing embal
+ embalm embalm
+ embalms embalm
+ embark embark
+ embarked embark
+ embarquements embarqu
+ embassade embassad
+ embassage embassag
+ embassies embassi
+ embassy embassi
+ embattailed embattail
+ embattl embattl
+ embattle embattl
+ embay embai
+ embellished embellish
+ embers ember
+ emblaze emblaz
+ emblem emblem
+ emblems emblem
+ embodied embodi
+ embold embold
+ emboldens embolden
+ emboss emboss
+ embossed emboss
+ embounded embound
+ embowel embowel
+ embowell embowel
+ embrac embrac
+ embrace embrac
+ embraced embrac
+ embracement embrac
+ embracements embrac
+ embraces embrac
+ embracing embrac
+ embrasures embrasur
+ embroider embroid
+ embroidery embroideri
+ emhracing emhrac
+ emilia emilia
+ eminence emin
+ eminent emin
+ eminently emin
+ emmanuel emmanuel
+ emnity emniti
+ empale empal
+ emperal emper
+ emperess emperess
+ emperial emperi
+ emperor emperor
+ empery emperi
+ emphasis emphasi
+ empire empir
+ empirics empir
+ empiricutic empiricut
+ empleached empleach
+ employ emploi
+ employed emploi
+ employer employ
+ employment employ
+ employments employ
+ empoison empoison
+ empress empress
+ emptied empti
+ emptier emptier
+ empties empti
+ emptiness empti
+ empty empti
+ emptying empti
+ emulate emul
+ emulation emul
+ emulations emul
+ emulator emul
+ emulous emul
+ en en
+ enact enact
+ enacted enact
+ enacts enact
+ enactures enactur
+ enamell enamel
+ enamelled enamel
+ enamour enamour
+ enamoured enamour
+ enanmour enanmour
+ encamp encamp
+ encamped encamp
+ encave encav
+ enceladus enceladu
+ enchaf enchaf
+ enchafed enchaf
+ enchant enchant
+ enchanted enchant
+ enchanting enchant
+ enchantingly enchantingli
+ enchantment enchant
+ enchantress enchantress
+ enchants enchant
+ enchas encha
+ encircle encircl
+ encircled encircl
+ enclos enclo
+ enclose enclos
+ enclosed enclos
+ encloses enclos
+ encloseth encloseth
+ enclosing enclos
+ enclouded encloud
+ encompass encompass
+ encompassed encompass
+ encompasseth encompasseth
+ encompassment encompass
+ encore encor
+ encorporal encorpor
+ encount encount
+ encounter encount
+ encountered encount
+ encounters encount
+ encourage encourag
+ encouraged encourag
+ encouragement encourag
+ encrimsoned encrimson
+ encroaching encroach
+ encumb encumb
+ end end
+ endamage endamag
+ endamagement endamag
+ endanger endang
+ endart endart
+ endear endear
+ endeared endear
+ endeavour endeavour
+ endeavours endeavour
+ ended end
+ ender ender
+ ending end
+ endings end
+ endite endit
+ endless endless
+ endow endow
+ endowed endow
+ endowments endow
+ endows endow
+ ends end
+ endu endu
+ endue endu
+ endur endur
+ endurance endur
+ endure endur
+ endured endur
+ endures endur
+ enduring endur
+ endymion endymion
+ eneas enea
+ enemies enemi
+ enemy enemi
+ enernies enerni
+ enew enew
+ enfeebled enfeebl
+ enfeebles enfeebl
+ enfeoff enfeoff
+ enfetter enfett
+ enfoldings enfold
+ enforc enforc
+ enforce enforc
+ enforced enforc
+ enforcedly enforcedli
+ enforcement enforc
+ enforces enforc
+ enforcest enforcest
+ enfranched enfranch
+ enfranchis enfranchi
+ enfranchise enfranchis
+ enfranchised enfranchis
+ enfranchisement enfranchis
+ enfreed enfre
+ enfreedoming enfreedom
+ engag engag
+ engage engag
+ engaged engag
+ engagements engag
+ engaging engag
+ engaol engaol
+ engend engend
+ engender engend
+ engenders engend
+ engilds engild
+ engine engin
+ engineer engin
+ enginer engin
+ engines engin
+ engirt engirt
+ england england
+ english english
+ englishman englishman
+ englishmen englishmen
+ engluts englut
+ englutted englut
+ engraffed engraf
+ engraft engraft
+ engrafted engraft
+ engrav engrav
+ engrave engrav
+ engross engross
+ engrossed engross
+ engrossest engrossest
+ engrossing engross
+ engrossments engross
+ enguard enguard
+ enigma enigma
+ enigmatical enigmat
+ enjoin enjoin
+ enjoined enjoin
+ enjoy enjoi
+ enjoyed enjoi
+ enjoyer enjoy
+ enjoying enjoi
+ enjoys enjoi
+ enkindle enkindl
+ enkindled enkindl
+ enlard enlard
+ enlarg enlarg
+ enlarge enlarg
+ enlarged enlarg
+ enlargement enlarg
+ enlargeth enlargeth
+ enlighten enlighten
+ enlink enlink
+ enmesh enmesh
+ enmities enmiti
+ enmity enmiti
+ ennoble ennobl
+ ennobled ennobl
+ enobarb enobarb
+ enobarbus enobarbu
+ enon enon
+ enormity enorm
+ enormous enorm
+ enough enough
+ enow enow
+ enpatron enpatron
+ enpierced enpierc
+ enquir enquir
+ enquire enquir
+ enquired enquir
+ enrag enrag
+ enrage enrag
+ enraged enrag
+ enrages enrag
+ enrank enrank
+ enrapt enrapt
+ enrich enrich
+ enriched enrich
+ enriches enrich
+ enridged enridg
+ enrings enr
+ enrob enrob
+ enrobe enrob
+ enroll enrol
+ enrolled enrol
+ enrooted enroot
+ enrounded enround
+ enschedul enschedul
+ ensconce ensconc
+ ensconcing ensconc
+ enseamed enseam
+ ensear ensear
+ enseigne enseign
+ enseignez enseignez
+ ensemble ensembl
+ enshelter enshelt
+ enshielded enshield
+ enshrines enshrin
+ ensign ensign
+ ensigns ensign
+ enskied enski
+ ensman ensman
+ ensnare ensnar
+ ensnared ensnar
+ ensnareth ensnareth
+ ensteep ensteep
+ ensu ensu
+ ensue ensu
+ ensued ensu
+ ensues ensu
+ ensuing ensu
+ enswathed enswath
+ ent ent
+ entail entail
+ entame entam
+ entangled entangl
+ entangles entangl
+ entendre entendr
+ enter enter
+ entered enter
+ entering enter
+ enterprise enterpris
+ enterprises enterpris
+ enters enter
+ entertain entertain
+ entertained entertain
+ entertainer entertain
+ entertaining entertain
+ entertainment entertain
+ entertainments entertain
+ enthrall enthral
+ enthralled enthral
+ enthron enthron
+ enthroned enthron
+ entice entic
+ enticements entic
+ enticing entic
+ entire entir
+ entirely entir
+ entitle entitl
+ entitled entitl
+ entitling entitl
+ entomb entomb
+ entombed entomb
+ entrails entrail
+ entrance entranc
+ entrances entranc
+ entrap entrap
+ entrapp entrapp
+ entre entr
+ entreat entreat
+ entreated entreat
+ entreaties entreati
+ entreating entreat
+ entreatments entreat
+ entreats entreat
+ entreaty entreati
+ entrench entrench
+ entry entri
+ entwist entwist
+ envelop envelop
+ envenom envenom
+ envenomed envenom
+ envenoms envenom
+ envied envi
+ envies envi
+ envious enviou
+ enviously envious
+ environ environ
+ environed environ
+ envoy envoi
+ envy envi
+ envying envi
+ enwheel enwheel
+ enwombed enwomb
+ enwraps enwrap
+ ephesian ephesian
+ ephesians ephesian
+ ephesus ephesu
+ epicure epicur
+ epicurean epicurean
+ epicures epicur
+ epicurism epicur
+ epicurus epicuru
+ epidamnum epidamnum
+ epidaurus epidauru
+ epigram epigram
+ epilepsy epilepsi
+ epileptic epilept
+ epilogue epilogu
+ epilogues epilogu
+ epistles epistl
+ epistrophus epistrophu
+ epitaph epitaph
+ epitaphs epitaph
+ epithet epithet
+ epitheton epitheton
+ epithets epithet
+ epitome epitom
+ equal equal
+ equalities equal
+ equality equal
+ equall equal
+ equally equal
+ equalness equal
+ equals equal
+ equinoctial equinocti
+ equinox equinox
+ equipage equipag
+ equity equiti
+ equivocal equivoc
+ equivocate equivoc
+ equivocates equivoc
+ equivocation equivoc
+ equivocator equivoc
+ er er
+ erbear erbear
+ erbearing erbear
+ erbears erbear
+ erbeat erbeat
+ erblows erblow
+ erboard erboard
+ erborne erborn
+ ercame ercam
+ ercast ercast
+ ercharg ercharg
+ ercharged ercharg
+ ercharging ercharg
+ ercles ercl
+ ercome ercom
+ ercover ercov
+ ercrows ercrow
+ erdoing erdo
+ ere er
+ erebus erebu
+ erect erect
+ erected erect
+ erecting erect
+ erection erect
+ erects erect
+ erewhile erewhil
+ erflourish erflourish
+ erflow erflow
+ erflowing erflow
+ erflows erflow
+ erfraught erfraught
+ erga erga
+ ergalled ergal
+ erglanced erglanc
+ ergo ergo
+ ergone ergon
+ ergrow ergrow
+ ergrown ergrown
+ ergrowth ergrowth
+ erhang erhang
+ erhanging erhang
+ erhasty erhasti
+ erhear erhear
+ erheard erheard
+ eringoes eringo
+ erjoy erjoi
+ erleap erleap
+ erleaps erleap
+ erleavens erleaven
+ erlook erlook
+ erlooking erlook
+ ermaster ermast
+ ermengare ermengar
+ ermount ermount
+ ern ern
+ ernight ernight
+ eros ero
+ erpaid erpaid
+ erparted erpart
+ erpast erpast
+ erpays erpai
+ erpeer erpeer
+ erperch erperch
+ erpicturing erpictur
+ erpingham erpingham
+ erposting erpost
+ erpow erpow
+ erpress erpress
+ erpressed erpress
+ err err
+ errand errand
+ errands errand
+ errant errant
+ errate errat
+ erraught erraught
+ erreaches erreach
+ erred er
+ errest errest
+ erring er
+ erroneous erron
+ error error
+ errors error
+ errs err
+ errule errul
+ errun errun
+ erset erset
+ ershade ershad
+ ershades ershad
+ ershine ershin
+ ershot ershot
+ ersized ersiz
+ erskip erskip
+ erslips erslip
+ erspreads erspread
+ erst erst
+ erstare erstar
+ erstep erstep
+ erstunk erstunk
+ ersway erswai
+ ersways erswai
+ erswell erswel
+ erta erta
+ ertake ertak
+ erteemed erteem
+ erthrow erthrow
+ erthrown erthrown
+ erthrows erthrow
+ ertook ertook
+ ertop ertop
+ ertopping ertop
+ ertrip ertrip
+ erturn erturn
+ erudition erudit
+ eruption erupt
+ eruptions erupt
+ ervalues ervalu
+ erwalk erwalk
+ erwatch erwatch
+ erween erween
+ erweens erween
+ erweigh erweigh
+ erweighs erweigh
+ erwhelm erwhelm
+ erwhelmed erwhelm
+ erworn erworn
+ es es
+ escalus escalu
+ escap escap
+ escape escap
+ escaped escap
+ escapes escap
+ eschew eschew
+ escoted escot
+ esill esil
+ especial especi
+ especially especi
+ esperance esper
+ espials espial
+ espied espi
+ espies espi
+ espous espou
+ espouse espous
+ espy espi
+ esquire esquir
+ esquires esquir
+ essay essai
+ essays essai
+ essence essenc
+ essential essenti
+ essentially essenti
+ esses ess
+ essex essex
+ est est
+ establish establish
+ established establish
+ estate estat
+ estates estat
+ esteem esteem
+ esteemed esteem
+ esteemeth esteemeth
+ esteeming esteem
+ esteems esteem
+ estimable estim
+ estimate estim
+ estimation estim
+ estimations estim
+ estime estim
+ estranged estrang
+ estridge estridg
+ estridges estridg
+ et et
+ etc etc
+ etceteras etcetera
+ ete et
+ eternal etern
+ eternally etern
+ eterne etern
+ eternity etern
+ eterniz eterniz
+ etes et
+ ethiop ethiop
+ ethiope ethiop
+ ethiopes ethiop
+ ethiopian ethiopian
+ etna etna
+ eton eton
+ etre etr
+ eunuch eunuch
+ eunuchs eunuch
+ euphrates euphrat
+ euphronius euphroniu
+ euriphile euriphil
+ europa europa
+ europe europ
+ ev ev
+ evade evad
+ evades evad
+ evans evan
+ evasion evas
+ evasions evas
+ eve ev
+ even even
+ evening even
+ evenly evenli
+ event event
+ eventful event
+ events event
+ ever ever
+ everlasting everlast
+ everlastingly everlastingli
+ evermore evermor
+ every everi
+ everyone everyon
+ everything everyth
+ everywhere everywher
+ evidence evid
+ evidences evid
+ evident evid
+ evil evil
+ evilly evilli
+ evils evil
+ evitate evit
+ ewe ew
+ ewer ewer
+ ewers ewer
+ ewes ew
+ exact exact
+ exacted exact
+ exactest exactest
+ exacting exact
+ exaction exact
+ exactions exact
+ exactly exactli
+ exacts exact
+ exalt exalt
+ exalted exalt
+ examin examin
+ examination examin
+ examinations examin
+ examine examin
+ examined examin
+ examines examin
+ exampl exampl
+ example exampl
+ exampled exampl
+ examples exampl
+ exasperate exasper
+ exasperates exasper
+ exceed exce
+ exceeded exceed
+ exceedeth exceedeth
+ exceeding exceed
+ exceedingly exceedingli
+ exceeds exce
+ excel excel
+ excelled excel
+ excellence excel
+ excellencies excel
+ excellency excel
+ excellent excel
+ excellently excel
+ excelling excel
+ excels excel
+ except except
+ excepted except
+ excepting except
+ exception except
+ exceptions except
+ exceptless exceptless
+ excess excess
+ excessive excess
+ exchang exchang
+ exchange exchang
+ exchanged exchang
+ exchequer exchequ
+ exchequers exchequ
+ excite excit
+ excited excit
+ excitements excit
+ excites excit
+ exclaim exclaim
+ exclaims exclaim
+ exclamation exclam
+ exclamations exclam
+ excludes exclud
+ excommunicate excommun
+ excommunication excommun
+ excrement excrement
+ excrements excrement
+ excursion excurs
+ excursions excurs
+ excus excu
+ excusable excus
+ excuse excus
+ excused excus
+ excuses excus
+ excusez excusez
+ excusing excus
+ execrable execr
+ execrations execr
+ execute execut
+ executed execut
+ executing execut
+ execution execut
+ executioner execution
+ executioners execution
+ executor executor
+ executors executor
+ exempt exempt
+ exempted exempt
+ exequies exequi
+ exercise exercis
+ exercises exercis
+ exeter exet
+ exeunt exeunt
+ exhal exhal
+ exhalation exhal
+ exhalations exhal
+ exhale exhal
+ exhales exhal
+ exhaust exhaust
+ exhibit exhibit
+ exhibiters exhibit
+ exhibition exhibit
+ exhort exhort
+ exhortation exhort
+ exigent exig
+ exil exil
+ exile exil
+ exiled exil
+ exion exion
+ exist exist
+ exists exist
+ exit exit
+ exits exit
+ exorciser exorcis
+ exorcisms exorc
+ exorcist exorcist
+ expect expect
+ expectance expect
+ expectancy expect
+ expectation expect
+ expectations expect
+ expected expect
+ expecters expect
+ expecting expect
+ expects expect
+ expedience expedi
+ expedient expedi
+ expediently expedi
+ expedition expedit
+ expeditious expediti
+ expel expel
+ expell expel
+ expelling expel
+ expels expel
+ expend expend
+ expense expens
+ expenses expens
+ experienc experienc
+ experience experi
+ experiences experi
+ experiment experi
+ experimental experiment
+ experiments experi
+ expert expert
+ expertness expert
+ expiate expiat
+ expiation expiat
+ expir expir
+ expiration expir
+ expire expir
+ expired expir
+ expires expir
+ expiring expir
+ explication explic
+ exploit exploit
+ exploits exploit
+ expos expo
+ expose expos
+ exposing expos
+ exposition exposit
+ expositor expositor
+ expostulate expostul
+ expostulation expostul
+ exposture expostur
+ exposure exposur
+ expound expound
+ expounded expound
+ express express
+ expressed express
+ expresseth expresseth
+ expressing express
+ expressive express
+ expressly expressli
+ expressure expressur
+ expuls expul
+ expulsion expuls
+ exquisite exquisit
+ exsufflicate exsuffl
+ extant extant
+ extemporal extempor
+ extemporally extempor
+ extempore extempor
+ extend extend
+ extended extend
+ extends extend
+ extent extent
+ extenuate extenu
+ extenuated extenu
+ extenuates extenu
+ extenuation extenu
+ exterior exterior
+ exteriorly exteriorli
+ exteriors exterior
+ extermin extermin
+ extern extern
+ external extern
+ extinct extinct
+ extincted extinct
+ extincture extinctur
+ extinguish extinguish
+ extirp extirp
+ extirpate extirp
+ extirped extirp
+ extol extol
+ extoll extol
+ extolment extol
+ exton exton
+ extort extort
+ extorted extort
+ extortion extort
+ extortions extort
+ extra extra
+ extract extract
+ extracted extract
+ extracting extract
+ extraordinarily extraordinarili
+ extraordinary extraordinari
+ extraught extraught
+ extravagancy extravag
+ extravagant extravag
+ extreme extrem
+ extremely extrem
+ extremes extrem
+ extremest extremest
+ extremities extrem
+ extremity extrem
+ exuent exuent
+ exult exult
+ exultation exult
+ ey ey
+ eyas eya
+ eyases eyas
+ eye ey
+ eyeball eyebal
+ eyeballs eyebal
+ eyebrow eyebrow
+ eyebrows eyebrow
+ eyed ei
+ eyeless eyeless
+ eyelid eyelid
+ eyelids eyelid
+ eyes ey
+ eyesight eyesight
+ eyestrings eyestr
+ eying ei
+ eyne eyn
+ eyrie eyri
+ fa fa
+ fabian fabian
+ fable fabl
+ fables fabl
+ fabric fabric
+ fabulous fabul
+ fac fac
+ face face
+ faced face
+ facere facer
+ faces face
+ faciant faciant
+ facile facil
+ facility facil
+ facinerious facineri
+ facing face
+ facit facit
+ fact fact
+ faction faction
+ factionary factionari
+ factions faction
+ factious factiou
+ factor factor
+ factors factor
+ faculties faculti
+ faculty faculti
+ fade fade
+ faded fade
+ fadeth fadeth
+ fadge fadg
+ fading fade
+ fadings fade
+ fadom fadom
+ fadoms fadom
+ fagot fagot
+ fagots fagot
+ fail fail
+ failing fail
+ fails fail
+ fain fain
+ faint faint
+ fainted faint
+ fainter fainter
+ fainting faint
+ faintly faintli
+ faintness faint
+ faints faint
+ fair fair
+ fairer fairer
+ fairest fairest
+ fairies fairi
+ fairing fair
+ fairings fair
+ fairly fairli
+ fairness fair
+ fairs fair
+ fairwell fairwel
+ fairy fairi
+ fais fai
+ fait fait
+ faites fait
+ faith faith
+ faithful faith
+ faithfull faithful
+ faithfully faithfulli
+ faithless faithless
+ faiths faith
+ faitors faitor
+ fal fal
+ falchion falchion
+ falcon falcon
+ falconbridge falconbridg
+ falconer falcon
+ falconers falcon
+ fall fall
+ fallacy fallaci
+ fallen fallen
+ falleth falleth
+ falliable falliabl
+ fallible fallibl
+ falling fall
+ fallow fallow
+ fallows fallow
+ falls fall
+ fally falli
+ falorous falor
+ false fals
+ falsehood falsehood
+ falsely fals
+ falseness fals
+ falser falser
+ falsify falsifi
+ falsing fals
+ falstaff falstaff
+ falstaffs falstaff
+ falter falter
+ fam fam
+ fame fame
+ famed fame
+ familiar familiar
+ familiarity familiar
+ familiarly familiarli
+ familiars familiar
+ family famili
+ famine famin
+ famish famish
+ famished famish
+ famous famou
+ famoused famous
+ famously famous
+ fan fan
+ fanatical fanat
+ fancies fanci
+ fancy fanci
+ fane fane
+ fanes fane
+ fang fang
+ fangled fangl
+ fangless fangless
+ fangs fang
+ fann fann
+ fanning fan
+ fans fan
+ fantasied fantasi
+ fantasies fantasi
+ fantastic fantast
+ fantastical fantast
+ fantastically fantast
+ fantasticoes fantastico
+ fantasy fantasi
+ fap fap
+ far far
+ farborough farborough
+ farced farc
+ fardel fardel
+ fardels fardel
+ fare fare
+ fares fare
+ farewell farewel
+ farewells farewel
+ fariner farin
+ faring fare
+ farm farm
+ farmer farmer
+ farmhouse farmhous
+ farms farm
+ farre farr
+ farrow farrow
+ farther farther
+ farthest farthest
+ farthing farth
+ farthingale farthingal
+ farthingales farthingal
+ farthings farth
+ fartuous fartuou
+ fas fa
+ fashion fashion
+ fashionable fashion
+ fashioning fashion
+ fashions fashion
+ fast fast
+ fasted fast
+ fasten fasten
+ fastened fasten
+ faster faster
+ fastest fastest
+ fasting fast
+ fastly fastli
+ fastolfe fastolf
+ fasts fast
+ fat fat
+ fatal fatal
+ fatally fatal
+ fate fate
+ fated fate
+ fates fate
+ father father
+ fathered father
+ fatherless fatherless
+ fatherly fatherli
+ fathers father
+ fathom fathom
+ fathomless fathomless
+ fathoms fathom
+ fatigate fatig
+ fatness fat
+ fats fat
+ fatted fat
+ fatter fatter
+ fattest fattest
+ fatting fat
+ fatuus fatuu
+ fauconbridge fauconbridg
+ faulconbridge faulconbridg
+ fault fault
+ faultiness faulti
+ faultless faultless
+ faults fault
+ faulty faulti
+ fausse fauss
+ fauste faust
+ faustuses faustus
+ faut faut
+ favor favor
+ favorable favor
+ favorably favor
+ favors favor
+ favour favour
+ favourable favour
+ favoured favour
+ favouredly favouredli
+ favourer favour
+ favourers favour
+ favouring favour
+ favourite favourit
+ favourites favourit
+ favours favour
+ favout favout
+ fawn fawn
+ fawneth fawneth
+ fawning fawn
+ fawns fawn
+ fay fai
+ fe fe
+ fealty fealti
+ fear fear
+ feared fear
+ fearest fearest
+ fearful fear
+ fearfull fearful
+ fearfully fearfulli
+ fearfulness fear
+ fearing fear
+ fearless fearless
+ fears fear
+ feast feast
+ feasted feast
+ feasting feast
+ feasts feast
+ feat feat
+ feated feat
+ feater feater
+ feather feather
+ feathered feather
+ feathers feather
+ featly featli
+ feats feat
+ featur featur
+ feature featur
+ featured featur
+ featureless featureless
+ features featur
+ february februari
+ fecks feck
+ fed fed
+ fedary fedari
+ federary federari
+ fee fee
+ feeble feebl
+ feebled feebl
+ feebleness feebl
+ feebling feebl
+ feebly feebli
+ feed feed
+ feeder feeder
+ feeders feeder
+ feedeth feedeth
+ feeding feed
+ feeds feed
+ feel feel
+ feeler feeler
+ feeling feel
+ feelingly feelingli
+ feels feel
+ fees fee
+ feet feet
+ fehemently fehement
+ feign feign
+ feigned feign
+ feigning feign
+ feil feil
+ feith feith
+ felicitate felicit
+ felicity felic
+ fell fell
+ fellest fellest
+ fellies felli
+ fellow fellow
+ fellowly fellowli
+ fellows fellow
+ fellowship fellowship
+ fellowships fellowship
+ fells fell
+ felon felon
+ felonious feloni
+ felony feloni
+ felt felt
+ female femal
+ females femal
+ feminine feminin
+ fen fen
+ fenc fenc
+ fence fenc
+ fencer fencer
+ fencing fenc
+ fends fend
+ fennel fennel
+ fenny fenni
+ fens fen
+ fenton fenton
+ fer fer
+ ferdinand ferdinand
+ fere fere
+ fernseed fernse
+ ferrara ferrara
+ ferrers ferrer
+ ferret ferret
+ ferry ferri
+ ferryman ferryman
+ fertile fertil
+ fertility fertil
+ fervency fervenc
+ fervour fervour
+ fery feri
+ fest fest
+ feste fest
+ fester fester
+ festinate festin
+ festinately festin
+ festival festiv
+ festivals festiv
+ fet fet
+ fetch fetch
+ fetches fetch
+ fetching fetch
+ fetlock fetlock
+ fetlocks fetlock
+ fett fett
+ fetter fetter
+ fettering fetter
+ fetters fetter
+ fettle fettl
+ feu feu
+ feud feud
+ fever fever
+ feverous fever
+ fevers fever
+ few few
+ fewer fewer
+ fewest fewest
+ fewness few
+ fickle fickl
+ fickleness fickl
+ fico fico
+ fiction fiction
+ fiddle fiddl
+ fiddler fiddler
+ fiddlestick fiddlestick
+ fidele fidel
+ fidelicet fidelicet
+ fidelity fidel
+ fidius fidiu
+ fie fie
+ field field
+ fielded field
+ fields field
+ fiend fiend
+ fiends fiend
+ fierce fierc
+ fiercely fierc
+ fierceness fierc
+ fiery fieri
+ fife fife
+ fifes fife
+ fifteen fifteen
+ fifteens fifteen
+ fifteenth fifteenth
+ fifth fifth
+ fifty fifti
+ fiftyfold fiftyfold
+ fig fig
+ fight fight
+ fighter fighter
+ fightest fightest
+ fighteth fighteth
+ fighting fight
+ fights fight
+ figo figo
+ figs fig
+ figur figur
+ figure figur
+ figured figur
+ figures figur
+ figuring figur
+ fike fike
+ fil fil
+ filberts filbert
+ filch filch
+ filches filch
+ filching filch
+ file file
+ filed file
+ files file
+ filial filial
+ filius filiu
+ fill fill
+ filled fill
+ fillet fillet
+ filling fill
+ fillip fillip
+ fills fill
+ filly filli
+ film film
+ fils fil
+ filth filth
+ filths filth
+ filthy filthi
+ fin fin
+ finally final
+ finch finch
+ find find
+ finder finder
+ findeth findeth
+ finding find
+ findings find
+ finds find
+ fine fine
+ fineless fineless
+ finely fine
+ finem finem
+ fineness fine
+ finer finer
+ fines fine
+ finest finest
+ fing fing
+ finger finger
+ fingering finger
+ fingers finger
+ fingre fingr
+ fingres fingr
+ finical finic
+ finish finish
+ finished finish
+ finisher finish
+ finless finless
+ finn finn
+ fins fin
+ finsbury finsburi
+ fir fir
+ firago firago
+ fire fire
+ firebrand firebrand
+ firebrands firebrand
+ fired fire
+ fires fire
+ firework firework
+ fireworks firework
+ firing fire
+ firk firk
+ firm firm
+ firmament firmament
+ firmly firmli
+ firmness firm
+ first first
+ firstlings firstl
+ fish fish
+ fisher fisher
+ fishermen fishermen
+ fishers fisher
+ fishes fish
+ fishified fishifi
+ fishmonger fishmong
+ fishpond fishpond
+ fisnomy fisnomi
+ fist fist
+ fisting fist
+ fists fist
+ fistula fistula
+ fit fit
+ fitchew fitchew
+ fitful fit
+ fitly fitli
+ fitment fitment
+ fitness fit
+ fits fit
+ fitted fit
+ fitter fitter
+ fittest fittest
+ fitteth fitteth
+ fitting fit
+ fitzwater fitzwat
+ five five
+ fivepence fivep
+ fives five
+ fix fix
+ fixed fix
+ fixes fix
+ fixeth fixeth
+ fixing fix
+ fixture fixtur
+ fl fl
+ flag flag
+ flagging flag
+ flagon flagon
+ flagons flagon
+ flags flag
+ flail flail
+ flakes flake
+ flaky flaki
+ flam flam
+ flame flame
+ flamen flamen
+ flamens flamen
+ flames flame
+ flaming flame
+ flaminius flaminiu
+ flanders flander
+ flannel flannel
+ flap flap
+ flaring flare
+ flash flash
+ flashes flash
+ flashing flash
+ flask flask
+ flat flat
+ flatly flatli
+ flatness flat
+ flats flat
+ flatt flatt
+ flatter flatter
+ flattered flatter
+ flatterer flatter
+ flatterers flatter
+ flatterest flatterest
+ flatteries flatteri
+ flattering flatter
+ flatters flatter
+ flattery flatteri
+ flaunts flaunt
+ flavio flavio
+ flavius flaviu
+ flaw flaw
+ flaws flaw
+ flax flax
+ flaxen flaxen
+ flay flai
+ flaying flai
+ flea flea
+ fleance fleanc
+ fleas flea
+ flecked fleck
+ fled fled
+ fledge fledg
+ flee flee
+ fleec fleec
+ fleece fleec
+ fleeces fleec
+ fleer fleer
+ fleering fleer
+ fleers fleer
+ fleet fleet
+ fleeter fleeter
+ fleeting fleet
+ fleming fleme
+ flemish flemish
+ flesh flesh
+ fleshes flesh
+ fleshly fleshli
+ fleshment fleshment
+ fleshmonger fleshmong
+ flew flew
+ flexible flexibl
+ flexure flexur
+ flibbertigibbet flibbertigibbet
+ flickering flicker
+ flidge flidg
+ fliers flier
+ flies fli
+ flieth flieth
+ flight flight
+ flights flight
+ flighty flighti
+ flinch flinch
+ fling fling
+ flint flint
+ flints flint
+ flinty flinti
+ flirt flirt
+ float float
+ floated float
+ floating float
+ flock flock
+ flocks flock
+ flood flood
+ floodgates floodgat
+ floods flood
+ floor floor
+ flora flora
+ florence florenc
+ florentine florentin
+ florentines florentin
+ florentius florentiu
+ florizel florizel
+ flote flote
+ floulish floulish
+ flour flour
+ flourish flourish
+ flourishes flourish
+ flourisheth flourisheth
+ flourishing flourish
+ flout flout
+ flouted flout
+ flouting flout
+ flouts flout
+ flow flow
+ flowed flow
+ flower flower
+ flowerets floweret
+ flowers flower
+ flowing flow
+ flown flown
+ flows flow
+ fluellen fluellen
+ fluent fluent
+ flung flung
+ flush flush
+ flushing flush
+ fluster fluster
+ flute flute
+ flutes flute
+ flutter flutter
+ flux flux
+ fluxive fluxiv
+ fly fly
+ flying fly
+ fo fo
+ foal foal
+ foals foal
+ foam foam
+ foamed foam
+ foaming foam
+ foams foam
+ foamy foami
+ fob fob
+ focative foc
+ fodder fodder
+ foe foe
+ foeman foeman
+ foemen foemen
+ foes foe
+ fog fog
+ foggy foggi
+ fogs fog
+ foh foh
+ foi foi
+ foil foil
+ foiled foil
+ foils foil
+ foin foin
+ foining foin
+ foins foin
+ fois foi
+ foison foison
+ foisons foison
+ foist foist
+ foix foix
+ fold fold
+ folded fold
+ folds fold
+ folio folio
+ folk folk
+ folks folk
+ follies folli
+ follow follow
+ followed follow
+ follower follow
+ followers follow
+ followest followest
+ following follow
+ follows follow
+ folly folli
+ fond fond
+ fonder fonder
+ fondly fondli
+ fondness fond
+ font font
+ fontibell fontibel
+ food food
+ fool fool
+ fooleries fooleri
+ foolery fooleri
+ foolhardy foolhardi
+ fooling fool
+ foolish foolish
+ foolishly foolishli
+ foolishness foolish
+ fools fool
+ foot foot
+ football footbal
+ footboy footboi
+ footboys footboi
+ footed foot
+ footfall footfal
+ footing foot
+ footman footman
+ footmen footmen
+ footpath footpath
+ footsteps footstep
+ footstool footstool
+ fopp fopp
+ fopped fop
+ foppery fopperi
+ foppish foppish
+ fops fop
+ for for
+ forage forag
+ foragers forag
+ forbade forbad
+ forbear forbear
+ forbearance forbear
+ forbears forbear
+ forbid forbid
+ forbidden forbidden
+ forbiddenly forbiddenli
+ forbids forbid
+ forbod forbod
+ forborne forborn
+ forc forc
+ force forc
+ forced forc
+ forceful forc
+ forceless forceless
+ forces forc
+ forcible forcibl
+ forcibly forcibl
+ forcing forc
+ ford ford
+ fordid fordid
+ fordo fordo
+ fordoes fordo
+ fordone fordon
+ fore fore
+ forecast forecast
+ forefather forefath
+ forefathers forefath
+ forefinger forefing
+ forego forego
+ foregone foregon
+ forehand forehand
+ forehead forehead
+ foreheads forehead
+ forehorse forehors
+ foreign foreign
+ foreigner foreign
+ foreigners foreign
+ foreknowing foreknow
+ foreknowledge foreknowledg
+ foremost foremost
+ forenamed forenam
+ forenoon forenoon
+ forerun forerun
+ forerunner forerunn
+ forerunning forerun
+ foreruns forerun
+ foresaid foresaid
+ foresaw foresaw
+ foresay foresai
+ foresee forese
+ foreseeing forese
+ foresees forese
+ foreshow foreshow
+ foreskirt foreskirt
+ forespent foresp
+ forest forest
+ forestall forestal
+ forestalled forestal
+ forester forest
+ foresters forest
+ forests forest
+ foretell foretel
+ foretelling foretel
+ foretells foretel
+ forethink forethink
+ forethought forethought
+ foretold foretold
+ forever forev
+ foreward foreward
+ forewarn forewarn
+ forewarned forewarn
+ forewarning forewarn
+ forfeit forfeit
+ forfeited forfeit
+ forfeiters forfeit
+ forfeiting forfeit
+ forfeits forfeit
+ forfeiture forfeitur
+ forfeitures forfeitur
+ forfend forfend
+ forfended forfend
+ forg forg
+ forgave forgav
+ forge forg
+ forged forg
+ forgeries forgeri
+ forgery forgeri
+ forges forg
+ forget forget
+ forgetful forget
+ forgetfulness forget
+ forgetive forget
+ forgets forget
+ forgetting forget
+ forgive forgiv
+ forgiven forgiven
+ forgiveness forgiv
+ forgo forgo
+ forgoing forgo
+ forgone forgon
+ forgot forgot
+ forgotten forgotten
+ fork fork
+ forked fork
+ forks fork
+ forlorn forlorn
+ form form
+ formal formal
+ formally formal
+ formed form
+ former former
+ formerly formerli
+ formless formless
+ forms form
+ fornication fornic
+ fornications fornic
+ fornicatress fornicatress
+ forres forr
+ forrest forrest
+ forsake forsak
+ forsaken forsaken
+ forsaketh forsaketh
+ forslow forslow
+ forsook forsook
+ forsooth forsooth
+ forspent forspent
+ forspoke forspok
+ forswear forswear
+ forswearing forswear
+ forswore forswor
+ forsworn forsworn
+ fort fort
+ forted fort
+ forth forth
+ forthcoming forthcom
+ forthlight forthlight
+ forthright forthright
+ forthwith forthwith
+ fortification fortif
+ fortifications fortif
+ fortified fortifi
+ fortifies fortifi
+ fortify fortifi
+ fortinbras fortinbra
+ fortitude fortitud
+ fortnight fortnight
+ fortress fortress
+ fortresses fortress
+ forts fort
+ fortun fortun
+ fortuna fortuna
+ fortunate fortun
+ fortunately fortun
+ fortune fortun
+ fortuned fortun
+ fortunes fortun
+ fortward fortward
+ forty forti
+ forum forum
+ forward forward
+ forwarding forward
+ forwardness forward
+ forwards forward
+ forwearied forweari
+ fosset fosset
+ fost fost
+ foster foster
+ fostered foster
+ fought fought
+ foughten foughten
+ foul foul
+ fouler fouler
+ foulest foulest
+ foully foulli
+ foulness foul
+ found found
+ foundation foundat
+ foundations foundat
+ founded found
+ founder founder
+ fount fount
+ fountain fountain
+ fountains fountain
+ founts fount
+ four four
+ fourscore fourscor
+ fourteen fourteen
+ fourth fourth
+ foutra foutra
+ fowl fowl
+ fowler fowler
+ fowling fowl
+ fowls fowl
+ fox fox
+ foxes fox
+ foxship foxship
+ fracted fract
+ fraction fraction
+ fractions fraction
+ fragile fragil
+ fragment fragment
+ fragments fragment
+ fragrant fragrant
+ frail frail
+ frailer frailer
+ frailties frailti
+ frailty frailti
+ fram fram
+ frame frame
+ framed frame
+ frames frame
+ frampold frampold
+ fran fran
+ francais francai
+ france franc
+ frances franc
+ franchise franchis
+ franchised franchis
+ franchisement franchis
+ franchises franchis
+ franciae francia
+ francis franci
+ francisca francisca
+ franciscan franciscan
+ francisco francisco
+ frank frank
+ franker franker
+ frankfort frankfort
+ franklin franklin
+ franklins franklin
+ frankly frankli
+ frankness frank
+ frantic frantic
+ franticly franticli
+ frateretto frateretto
+ fratrum fratrum
+ fraud fraud
+ fraudful fraud
+ fraught fraught
+ fraughtage fraughtag
+ fraughting fraught
+ fray frai
+ frays frai
+ freckl freckl
+ freckled freckl
+ freckles freckl
+ frederick frederick
+ free free
+ freed freed
+ freedom freedom
+ freedoms freedom
+ freehearted freeheart
+ freelier freelier
+ freely freeli
+ freeman freeman
+ freemen freemen
+ freeness freeness
+ freer freer
+ frees free
+ freestone freeston
+ freetown freetown
+ freeze freez
+ freezes freez
+ freezing freez
+ freezings freez
+ french french
+ frenchman frenchman
+ frenchmen frenchmen
+ frenchwoman frenchwoman
+ frenzy frenzi
+ frequent frequent
+ frequents frequent
+ fresh fresh
+ fresher fresher
+ freshes fresh
+ freshest freshest
+ freshly freshli
+ freshness fresh
+ fret fret
+ fretful fret
+ frets fret
+ fretted fret
+ fretten fretten
+ fretting fret
+ friar friar
+ friars friar
+ friday fridai
+ fridays fridai
+ friend friend
+ friended friend
+ friending friend
+ friendless friendless
+ friendliness friendli
+ friendly friendli
+ friends friend
+ friendship friendship
+ friendships friendship
+ frieze friez
+ fright fright
+ frighted fright
+ frightened frighten
+ frightful fright
+ frighting fright
+ frights fright
+ fringe fring
+ fringed fring
+ frippery fripperi
+ frisk frisk
+ fritters fritter
+ frivolous frivol
+ fro fro
+ frock frock
+ frog frog
+ frogmore frogmor
+ froissart froissart
+ frolic frolic
+ from from
+ front front
+ fronted front
+ frontier frontier
+ frontiers frontier
+ fronting front
+ frontlet frontlet
+ fronts front
+ frost frost
+ frosts frost
+ frosty frosti
+ froth froth
+ froward froward
+ frown frown
+ frowning frown
+ frowningly frowningli
+ frowns frown
+ froze froze
+ frozen frozen
+ fructify fructifi
+ frugal frugal
+ fruit fruit
+ fruiterer fruiter
+ fruitful fruit
+ fruitfully fruitfulli
+ fruitfulness fruit
+ fruition fruition
+ fruitless fruitless
+ fruits fruit
+ frush frush
+ frustrate frustrat
+ frutify frutifi
+ fry fry
+ fubb fubb
+ fuel fuel
+ fugitive fugit
+ fulfil fulfil
+ fulfill fulfil
+ fulfilling fulfil
+ fulfils fulfil
+ full full
+ fullam fullam
+ fuller fuller
+ fullers fuller
+ fullest fullest
+ fullness full
+ fully fulli
+ fulness ful
+ fulsome fulsom
+ fulvia fulvia
+ fum fum
+ fumble fumbl
+ fumbles fumbl
+ fumblest fumblest
+ fumbling fumbl
+ fume fume
+ fumes fume
+ fuming fume
+ fumiter fumit
+ fumitory fumitori
+ fun fun
+ function function
+ functions function
+ fundamental fundament
+ funeral funer
+ funerals funer
+ fur fur
+ furbish furbish
+ furies furi
+ furious furiou
+ furlongs furlong
+ furnace furnac
+ furnaces furnac
+ furnish furnish
+ furnished furnish
+ furnishings furnish
+ furniture furnitur
+ furnival furniv
+ furor furor
+ furr furr
+ furrow furrow
+ furrowed furrow
+ furrows furrow
+ furth furth
+ further further
+ furtherance further
+ furtherer further
+ furthermore furthermor
+ furthest furthest
+ fury furi
+ furze furz
+ furzes furz
+ fust fust
+ fustian fustian
+ fustilarian fustilarian
+ fusty fusti
+ fut fut
+ future futur
+ futurity futur
+ g g
+ gabble gabbl
+ gaberdine gaberdin
+ gabriel gabriel
+ gad gad
+ gadding gad
+ gads gad
+ gadshill gadshil
+ gag gag
+ gage gage
+ gaged gage
+ gagg gagg
+ gaging gage
+ gagne gagn
+ gain gain
+ gained gain
+ gainer gainer
+ gaingiving gaingiv
+ gains gain
+ gainsaid gainsaid
+ gainsay gainsai
+ gainsaying gainsai
+ gainsays gainsai
+ gainst gainst
+ gait gait
+ gaited gait
+ galathe galath
+ gale gale
+ galen galen
+ gales gale
+ gall gall
+ gallant gallant
+ gallantly gallantli
+ gallantry gallantri
+ gallants gallant
+ galled gall
+ gallery galleri
+ galley gallei
+ galleys gallei
+ gallia gallia
+ gallian gallian
+ galliard galliard
+ galliasses galliass
+ gallimaufry gallimaufri
+ galling gall
+ gallons gallon
+ gallop gallop
+ galloping gallop
+ gallops gallop
+ gallow gallow
+ galloway gallowai
+ gallowglasses gallowglass
+ gallows gallow
+ gallowses gallows
+ galls gall
+ gallus gallu
+ gam gam
+ gambol gambol
+ gambold gambold
+ gambols gambol
+ gamboys gamboi
+ game game
+ gamers gamer
+ games game
+ gamesome gamesom
+ gamester gamest
+ gaming game
+ gammon gammon
+ gamut gamut
+ gan gan
+ gangren gangren
+ ganymede ganymed
+ gaol gaol
+ gaoler gaoler
+ gaolers gaoler
+ gaols gaol
+ gap gap
+ gape gape
+ gapes gape
+ gaping gape
+ gar gar
+ garb garb
+ garbage garbag
+ garboils garboil
+ garcon garcon
+ gard gard
+ garde gard
+ garden garden
+ gardener garden
+ gardeners garden
+ gardens garden
+ gardez gardez
+ gardiner gardin
+ gardon gardon
+ gargantua gargantua
+ gargrave gargrav
+ garish garish
+ garland garland
+ garlands garland
+ garlic garlic
+ garment garment
+ garments garment
+ garmet garmet
+ garner garner
+ garners garner
+ garnish garnish
+ garnished garnish
+ garret garret
+ garrison garrison
+ garrisons garrison
+ gart gart
+ garter garter
+ garterd garterd
+ gartering garter
+ garters garter
+ gascony gasconi
+ gash gash
+ gashes gash
+ gaskins gaskin
+ gasp gasp
+ gasping gasp
+ gasted gast
+ gastness gast
+ gat gat
+ gate gate
+ gated gate
+ gates gate
+ gath gath
+ gather gather
+ gathered gather
+ gathering gather
+ gathers gather
+ gatories gatori
+ gatory gatori
+ gaud gaud
+ gaudeo gaudeo
+ gaudy gaudi
+ gauge gaug
+ gaul gaul
+ gaultree gaultre
+ gaunt gaunt
+ gauntlet gauntlet
+ gauntlets gauntlet
+ gav gav
+ gave gave
+ gavest gavest
+ gawded gawd
+ gawds gawd
+ gawsey gawsei
+ gay gai
+ gayness gay
+ gaz gaz
+ gaze gaze
+ gazed gaze
+ gazer gazer
+ gazers gazer
+ gazes gaze
+ gazeth gazeth
+ gazing gaze
+ gear gear
+ geck geck
+ geese gees
+ geffrey geffrei
+ geld geld
+ gelded geld
+ gelding geld
+ gelida gelida
+ gelidus gelidu
+ gelt gelt
+ gem gem
+ geminy gemini
+ gems gem
+ gen gen
+ gender gender
+ genders gender
+ general gener
+ generally gener
+ generals gener
+ generation gener
+ generations gener
+ generative gener
+ generosity generos
+ generous gener
+ genitive genit
+ genitivo genitivo
+ genius geniu
+ gennets gennet
+ genoa genoa
+ genoux genoux
+ gens gen
+ gent gent
+ gentilhomme gentilhomm
+ gentility gentil
+ gentle gentl
+ gentlefolks gentlefolk
+ gentleman gentleman
+ gentlemanlike gentlemanlik
+ gentlemen gentlemen
+ gentleness gentl
+ gentler gentler
+ gentles gentl
+ gentlest gentlest
+ gentlewoman gentlewoman
+ gentlewomen gentlewomen
+ gently gentli
+ gentry gentri
+ george georg
+ gerard gerard
+ germaines germain
+ germains germain
+ german german
+ germane german
+ germans german
+ germany germani
+ gertrude gertrud
+ gest gest
+ gests gest
+ gesture gestur
+ gestures gestur
+ get get
+ getrude getrud
+ gets get
+ getter getter
+ getting get
+ ghastly ghastli
+ ghost ghost
+ ghosted ghost
+ ghostly ghostli
+ ghosts ghost
+ gi gi
+ giant giant
+ giantess giantess
+ giantlike giantlik
+ giants giant
+ gib gib
+ gibber gibber
+ gibbet gibbet
+ gibbets gibbet
+ gibe gibe
+ giber giber
+ gibes gibe
+ gibing gibe
+ gibingly gibingli
+ giddily giddili
+ giddiness giddi
+ giddy giddi
+ gift gift
+ gifts gift
+ gig gig
+ giglets giglet
+ giglot giglot
+ gilbert gilbert
+ gild gild
+ gilded gild
+ gilding gild
+ gilliams gilliam
+ gillian gillian
+ gills gill
+ gillyvors gillyvor
+ gilt gilt
+ gimmal gimmal
+ gimmers gimmer
+ gin gin
+ ging ging
+ ginger ginger
+ gingerbread gingerbread
+ gingerly gingerli
+ ginn ginn
+ gins gin
+ gioucestershire gioucestershir
+ gipes gipe
+ gipsies gipsi
+ gipsy gipsi
+ gird gird
+ girded gird
+ girdle girdl
+ girdled girdl
+ girdles girdl
+ girdling girdl
+ girl girl
+ girls girl
+ girt girt
+ girth girth
+ gis gi
+ giv giv
+ give give
+ given given
+ giver giver
+ givers giver
+ gives give
+ givest givest
+ giveth giveth
+ giving give
+ givings give
+ glad glad
+ gladded glad
+ gladding glad
+ gladly gladli
+ gladness glad
+ glamis glami
+ glanc glanc
+ glance glanc
+ glanced glanc
+ glances glanc
+ glancing glanc
+ glanders glander
+ glansdale glansdal
+ glare glare
+ glares glare
+ glass glass
+ glasses glass
+ glassy glassi
+ glaz glaz
+ glazed glaze
+ gleams gleam
+ glean glean
+ gleaned glean
+ gleaning glean
+ gleeful gleeful
+ gleek gleek
+ gleeking gleek
+ gleeks gleek
+ glend glend
+ glendower glendow
+ glib glib
+ glide glide
+ glided glide
+ glides glide
+ glideth glideth
+ gliding glide
+ glimmer glimmer
+ glimmering glimmer
+ glimmers glimmer
+ glimpse glimps
+ glimpses glimps
+ glist glist
+ glistening glisten
+ glister glister
+ glistering glister
+ glisters glister
+ glitt glitt
+ glittering glitter
+ globe globe
+ globes globe
+ glooming gloom
+ gloomy gloomi
+ glories glori
+ glorified glorifi
+ glorify glorifi
+ glorious gloriou
+ gloriously glorious
+ glory glori
+ glose glose
+ gloss gloss
+ glosses gloss
+ glou glou
+ glouceste gloucest
+ gloucester gloucest
+ gloucestershire gloucestershir
+ glove glove
+ glover glover
+ gloves glove
+ glow glow
+ glowed glow
+ glowing glow
+ glowworm glowworm
+ gloz gloz
+ gloze gloze
+ glozes gloze
+ glu glu
+ glue glue
+ glued glu
+ glues glue
+ glut glut
+ glutt glutt
+ glutted glut
+ glutton glutton
+ gluttoning glutton
+ gluttony gluttoni
+ gnarled gnarl
+ gnarling gnarl
+ gnat gnat
+ gnats gnat
+ gnaw gnaw
+ gnawing gnaw
+ gnawn gnawn
+ gnaws gnaw
+ go go
+ goad goad
+ goaded goad
+ goads goad
+ goal goal
+ goat goat
+ goatish goatish
+ goats goat
+ gobbets gobbet
+ gobbo gobbo
+ goblet goblet
+ goblets goblet
+ goblin goblin
+ goblins goblin
+ god god
+ godded god
+ godden godden
+ goddess goddess
+ goddesses goddess
+ goddild goddild
+ godfather godfath
+ godfathers godfath
+ godhead godhead
+ godlike godlik
+ godliness godli
+ godly godli
+ godmother godmoth
+ gods god
+ godson godson
+ goer goer
+ goers goer
+ goes goe
+ goest goest
+ goeth goeth
+ goffe goff
+ gogs gog
+ going go
+ gold gold
+ golden golden
+ goldenly goldenli
+ goldsmith goldsmith
+ goldsmiths goldsmith
+ golgotha golgotha
+ goliases golias
+ goliath goliath
+ gon gon
+ gondola gondola
+ gondolier gondoli
+ gone gone
+ goneril goneril
+ gong gong
+ gonzago gonzago
+ gonzalo gonzalo
+ good good
+ goodfellow goodfellow
+ goodlier goodlier
+ goodliest goodliest
+ goodly goodli
+ goodman goodman
+ goodness good
+ goodnight goodnight
+ goodrig goodrig
+ goods good
+ goodwife goodwif
+ goodwill goodwil
+ goodwin goodwin
+ goodwins goodwin
+ goodyear goodyear
+ goodyears goodyear
+ goose goos
+ gooseberry gooseberri
+ goosequills goosequil
+ goot goot
+ gor gor
+ gorbellied gorbelli
+ gorboduc gorboduc
+ gordian gordian
+ gore gore
+ gored gore
+ gorg gorg
+ gorge gorg
+ gorgeous gorgeou
+ gorget gorget
+ gorging gorg
+ gorgon gorgon
+ gormandize gormand
+ gormandizing gormand
+ gory gori
+ gosling gosl
+ gospel gospel
+ gospels gospel
+ goss goss
+ gossamer gossam
+ gossip gossip
+ gossiping gossip
+ gossiplike gossiplik
+ gossips gossip
+ got got
+ goth goth
+ goths goth
+ gotten gotten
+ gourd gourd
+ gout gout
+ gouts gout
+ gouty gouti
+ govern govern
+ governance govern
+ governed govern
+ governess gover
+ government govern
+ governor governor
+ governors governor
+ governs govern
+ gower gower
+ gown gown
+ gowns gown
+ grac grac
+ grace grace
+ graced grace
+ graceful grace
+ gracefully gracefulli
+ graceless graceless
+ graces grace
+ gracing grace
+ gracious graciou
+ graciously gracious
+ gradation gradat
+ graff graff
+ graffing graf
+ graft graft
+ grafted graft
+ grafters grafter
+ grain grain
+ grained grain
+ grains grain
+ gramercies gramerci
+ gramercy gramerci
+ grammar grammar
+ grand grand
+ grandam grandam
+ grandame grandam
+ grandchild grandchild
+ grande grand
+ grandeur grandeur
+ grandfather grandfath
+ grandjurors grandjuror
+ grandmother grandmoth
+ grandpre grandpr
+ grandsir grandsir
+ grandsire grandsir
+ grandsires grandsir
+ grange grang
+ grant grant
+ granted grant
+ granting grant
+ grants grant
+ grape grape
+ grapes grape
+ grapple grappl
+ grapples grappl
+ grappling grappl
+ grasp grasp
+ grasped grasp
+ grasps grasp
+ grass grass
+ grasshoppers grasshopp
+ grassy grassi
+ grate grate
+ grated grate
+ grateful grate
+ grates grate
+ gratiano gratiano
+ gratify gratifi
+ gratii gratii
+ gratillity gratil
+ grating grate
+ gratis grati
+ gratitude gratitud
+ gratulate gratul
+ grav grav
+ grave grave
+ gravediggers gravedigg
+ gravel gravel
+ graveless graveless
+ gravell gravel
+ gravely grave
+ graven graven
+ graveness grave
+ graver graver
+ graves grave
+ gravest gravest
+ gravestone graveston
+ gravities graviti
+ gravity graviti
+ gravy gravi
+ gray grai
+ graymalkin graymalkin
+ graz graz
+ graze graze
+ grazed graze
+ grazing graze
+ grease greas
+ greases greas
+ greasily greasili
+ greasy greasi
+ great great
+ greater greater
+ greatest greatest
+ greatly greatli
+ greatness great
+ grecian grecian
+ grecians grecian
+ gree gree
+ greece greec
+ greed greed
+ greedily greedili
+ greediness greedi
+ greedy greedi
+ greeing gree
+ greek greek
+ greekish greekish
+ greeks greek
+ green green
+ greener greener
+ greenly greenli
+ greens green
+ greensleeves greensleev
+ greenwich greenwich
+ greenwood greenwood
+ greet greet
+ greeted greet
+ greeting greet
+ greetings greet
+ greets greet
+ greg greg
+ gregory gregori
+ gremio gremio
+ grew grew
+ grey grei
+ greybeard greybeard
+ greybeards greybeard
+ greyhound greyhound
+ greyhounds greyhound
+ grief grief
+ griefs grief
+ griev griev
+ grievance grievanc
+ grievances grievanc
+ grieve griev
+ grieved griev
+ grieves griev
+ grievest grievest
+ grieving griev
+ grievingly grievingli
+ grievous grievou
+ grievously grievous
+ griffin griffin
+ griffith griffith
+ grim grim
+ grime grime
+ grimly grimli
+ grin grin
+ grind grind
+ grinding grind
+ grindstone grindston
+ grinning grin
+ grip grip
+ gripe gripe
+ gripes gripe
+ griping gripe
+ grise grise
+ grisly grisli
+ grissel grissel
+ grize grize
+ grizzle grizzl
+ grizzled grizzl
+ groan groan
+ groaning groan
+ groans groan
+ groat groat
+ groats groat
+ groin groin
+ groom groom
+ grooms groom
+ grop grop
+ groping grope
+ gros gro
+ gross gross
+ grosser grosser
+ grossly grossli
+ grossness gross
+ ground ground
+ grounded ground
+ groundlings groundl
+ grounds ground
+ grove grove
+ grovel grovel
+ grovelling grovel
+ groves grove
+ grow grow
+ groweth groweth
+ growing grow
+ grown grown
+ grows grow
+ growth growth
+ grub grub
+ grubb grubb
+ grubs grub
+ grudge grudg
+ grudged grudg
+ grudges grudg
+ grudging grudg
+ gruel gruel
+ grumble grumbl
+ grumblest grumblest
+ grumbling grumbl
+ grumblings grumbl
+ grumio grumio
+ grund grund
+ grunt grunt
+ gualtier gualtier
+ guard guard
+ guardage guardag
+ guardant guardant
+ guarded guard
+ guardian guardian
+ guardians guardian
+ guards guard
+ guardsman guardsman
+ gud gud
+ gudgeon gudgeon
+ guerdon guerdon
+ guerra guerra
+ guess guess
+ guesses guess
+ guessingly guessingli
+ guest guest
+ guests guest
+ guiana guiana
+ guichard guichard
+ guide guid
+ guided guid
+ guider guider
+ guiderius guideriu
+ guides guid
+ guiding guid
+ guidon guidon
+ guienne guienn
+ guil guil
+ guildenstern guildenstern
+ guilders guilder
+ guildford guildford
+ guildhall guildhal
+ guile guil
+ guiled guil
+ guileful guil
+ guilfords guilford
+ guilt guilt
+ guiltian guiltian
+ guiltier guiltier
+ guiltily guiltili
+ guiltiness guilti
+ guiltless guiltless
+ guilts guilt
+ guilty guilti
+ guinea guinea
+ guinever guinev
+ guise guis
+ gul gul
+ gules gule
+ gulf gulf
+ gulfs gulf
+ gull gull
+ gulls gull
+ gum gum
+ gumm gumm
+ gums gum
+ gun gun
+ gunner gunner
+ gunpowder gunpowd
+ guns gun
+ gurnet gurnet
+ gurney gurnei
+ gust gust
+ gusts gust
+ gusty gusti
+ guts gut
+ gutter gutter
+ guy gui
+ guynes guyn
+ guysors guysor
+ gypsy gypsi
+ gyve gyve
+ gyved gyve
+ gyves gyve
+ h h
+ ha ha
+ haberdasher haberdash
+ habiliment habili
+ habiliments habili
+ habit habit
+ habitation habit
+ habited habit
+ habits habit
+ habitude habitud
+ hack hack
+ hacket hacket
+ hackney hacknei
+ hacks hack
+ had had
+ hadst hadst
+ haec haec
+ haeres haer
+ hag hag
+ hagar hagar
+ haggard haggard
+ haggards haggard
+ haggish haggish
+ haggled haggl
+ hags hag
+ hail hail
+ hailed hail
+ hailstone hailston
+ hailstones hailston
+ hair hair
+ hairless hairless
+ hairs hair
+ hairy hairi
+ hal hal
+ halberd halberd
+ halberds halberd
+ halcyon halcyon
+ hale hale
+ haled hale
+ hales hale
+ half half
+ halfcan halfcan
+ halfpence halfpenc
+ halfpenny halfpenni
+ halfpennyworth halfpennyworth
+ halfway halfwai
+ halidom halidom
+ hall hall
+ halloa halloa
+ halloing hallo
+ hallond hallond
+ halloo halloo
+ hallooing halloo
+ hallow hallow
+ hallowed hallow
+ hallowmas hallowma
+ hallown hallown
+ hals hal
+ halt halt
+ halter halter
+ halters halter
+ halting halt
+ halts halt
+ halves halv
+ ham ham
+ hames hame
+ hamlet hamlet
+ hammer hammer
+ hammered hammer
+ hammering hammer
+ hammers hammer
+ hamper hamper
+ hampton hampton
+ hams ham
+ hamstring hamstr
+ hand hand
+ handed hand
+ handful hand
+ handicraft handicraft
+ handicraftsmen handicraftsmen
+ handing hand
+ handiwork handiwork
+ handkercher handkerch
+ handkerchers handkerch
+ handkerchief handkerchief
+ handle handl
+ handled handl
+ handles handl
+ handless handless
+ handlest handlest
+ handling handl
+ handmaid handmaid
+ handmaids handmaid
+ hands hand
+ handsaw handsaw
+ handsome handsom
+ handsomely handsom
+ handsomeness handsom
+ handwriting handwrit
+ handy handi
+ hang hang
+ hanged hang
+ hangers hanger
+ hangeth hangeth
+ hanging hang
+ hangings hang
+ hangman hangman
+ hangmen hangmen
+ hangs hang
+ hannibal hannib
+ hap hap
+ hapless hapless
+ haply hapli
+ happ happ
+ happen happen
+ happened happen
+ happier happier
+ happies happi
+ happiest happiest
+ happily happili
+ happiness happi
+ happy happi
+ haps hap
+ harbinger harbing
+ harbingers harbing
+ harbor harbor
+ harbour harbour
+ harbourage harbourag
+ harbouring harbour
+ harbours harbour
+ harcourt harcourt
+ hard hard
+ harder harder
+ hardest hardest
+ hardiest hardiest
+ hardiment hardiment
+ hardiness hardi
+ hardly hardli
+ hardness hard
+ hardocks hardock
+ hardy hardi
+ hare hare
+ harelip harelip
+ hares hare
+ harfleur harfleur
+ hark hark
+ harlot harlot
+ harlotry harlotri
+ harlots harlot
+ harm harm
+ harmed harm
+ harmful harm
+ harming harm
+ harmless harmless
+ harmonious harmoni
+ harmony harmoni
+ harms harm
+ harness har
+ harp harp
+ harper harper
+ harpier harpier
+ harping harp
+ harpy harpi
+ harried harri
+ harrow harrow
+ harrows harrow
+ harry harri
+ harsh harsh
+ harshly harshli
+ harshness harsh
+ hart hart
+ harts hart
+ harum harum
+ harvest harvest
+ has ha
+ hast hast
+ haste hast
+ hasted hast
+ hasten hasten
+ hastes hast
+ hastily hastili
+ hasting hast
+ hastings hast
+ hasty hasti
+ hat hat
+ hatch hatch
+ hatches hatch
+ hatchet hatchet
+ hatching hatch
+ hatchment hatchment
+ hate hate
+ hated hate
+ hateful hate
+ hater hater
+ haters hater
+ hates hate
+ hateth hateth
+ hatfield hatfield
+ hath hath
+ hating hate
+ hatred hatr
+ hats hat
+ haud haud
+ hauf hauf
+ haught haught
+ haughtiness haughti
+ haughty haughti
+ haunch haunch
+ haunches haunch
+ haunt haunt
+ haunted haunt
+ haunting haunt
+ haunts haunt
+ hautboy hautboi
+ hautboys hautboi
+ have have
+ haven haven
+ havens haven
+ haver haver
+ having have
+ havings have
+ havior havior
+ haviour haviour
+ havoc havoc
+ hawk hawk
+ hawking hawk
+ hawks hawk
+ hawthorn hawthorn
+ hawthorns hawthorn
+ hay hai
+ hazard hazard
+ hazarded hazard
+ hazards hazard
+ hazel hazel
+ hazelnut hazelnut
+ he he
+ head head
+ headborough headborough
+ headed head
+ headier headier
+ heading head
+ headland headland
+ headless headless
+ headlong headlong
+ heads head
+ headsman headsman
+ headstrong headstrong
+ heady headi
+ heal heal
+ healed heal
+ healing heal
+ heals heal
+ health health
+ healthful health
+ healths health
+ healthsome healthsom
+ healthy healthi
+ heap heap
+ heaping heap
+ heaps heap
+ hear hear
+ heard heard
+ hearer hearer
+ hearers hearer
+ hearest hearest
+ heareth heareth
+ hearing hear
+ hearings hear
+ heark heark
+ hearken hearken
+ hearkens hearken
+ hears hear
+ hearsay hearsai
+ hearse hears
+ hearsed hears
+ hearst hearst
+ heart heart
+ heartache heartach
+ heartbreak heartbreak
+ heartbreaking heartbreak
+ hearted heart
+ hearten hearten
+ hearth hearth
+ hearths hearth
+ heartily heartili
+ heartiness hearti
+ heartless heartless
+ heartlings heartl
+ heartly heartli
+ hearts heart
+ heartsick heartsick
+ heartstrings heartstr
+ hearty hearti
+ heat heat
+ heated heat
+ heath heath
+ heathen heathen
+ heathenish heathenish
+ heating heat
+ heats heat
+ heauties heauti
+ heav heav
+ heave heav
+ heaved heav
+ heaven heaven
+ heavenly heavenli
+ heavens heaven
+ heaves heav
+ heavier heavier
+ heaviest heaviest
+ heavily heavili
+ heaviness heavi
+ heaving heav
+ heavings heav
+ heavy heavi
+ hebona hebona
+ hebrew hebrew
+ hecate hecat
+ hectic hectic
+ hector hector
+ hectors hector
+ hecuba hecuba
+ hedg hedg
+ hedge hedg
+ hedgehog hedgehog
+ hedgehogs hedgehog
+ hedges hedg
+ heed heed
+ heeded heed
+ heedful heed
+ heedfull heedful
+ heedfully heedfulli
+ heedless heedless
+ heel heel
+ heels heel
+ hefted heft
+ hefts heft
+ heifer heifer
+ heifers heifer
+ heigh heigh
+ height height
+ heighten heighten
+ heinous heinou
+ heinously heinous
+ heir heir
+ heiress heiress
+ heirless heirless
+ heirs heir
+ held held
+ helen helen
+ helena helena
+ helenus helenu
+ helias helia
+ helicons helicon
+ hell hell
+ hellespont hellespont
+ hellfire hellfir
+ hellish hellish
+ helm helm
+ helmed helm
+ helmet helmet
+ helmets helmet
+ helms helm
+ help help
+ helper helper
+ helpers helper
+ helpful help
+ helping help
+ helpless helpless
+ helps help
+ helter helter
+ hem hem
+ heme heme
+ hemlock hemlock
+ hemm hemm
+ hemp hemp
+ hempen hempen
+ hems hem
+ hen hen
+ hence henc
+ henceforth henceforth
+ henceforward henceforward
+ henchman henchman
+ henri henri
+ henricus henricu
+ henry henri
+ hens hen
+ hent hent
+ henton henton
+ her her
+ herald herald
+ heraldry heraldri
+ heralds herald
+ herb herb
+ herbert herbert
+ herblets herblet
+ herbs herb
+ herculean herculean
+ hercules hercul
+ herd herd
+ herds herd
+ herdsman herdsman
+ herdsmen herdsmen
+ here here
+ hereabout hereabout
+ hereabouts hereabout
+ hereafter hereaft
+ hereby herebi
+ hereditary hereditari
+ hereford hereford
+ herefordshire herefordshir
+ herein herein
+ hereof hereof
+ heresies heresi
+ heresy heresi
+ heretic heret
+ heretics heret
+ hereto hereto
+ hereupon hereupon
+ heritage heritag
+ heritier heriti
+ hermes herm
+ hermia hermia
+ hermione hermion
+ hermit hermit
+ hermitage hermitag
+ hermits hermit
+ herne hern
+ hero hero
+ herod herod
+ herods herod
+ heroes hero
+ heroic heroic
+ heroical heroic
+ herring her
+ herrings her
+ hers her
+ herself herself
+ hesperides hesperid
+ hesperus hesperu
+ hest hest
+ hests hest
+ heure heur
+ heureux heureux
+ hew hew
+ hewgh hewgh
+ hewing hew
+ hewn hewn
+ hews hew
+ hey hei
+ heyday heydai
+ hibocrates hibocr
+ hic hic
+ hiccups hiccup
+ hick hick
+ hid hid
+ hidden hidden
+ hide hide
+ hideous hideou
+ hideously hideous
+ hideousness hideous
+ hides hide
+ hidest hidest
+ hiding hide
+ hie hie
+ hied hi
+ hiems hiem
+ hies hi
+ hig hig
+ high high
+ higher higher
+ highest highest
+ highly highli
+ highmost highmost
+ highness high
+ hight hight
+ highway highwai
+ highways highwai
+ hilding hild
+ hildings hild
+ hill hill
+ hillo hillo
+ hilloa hilloa
+ hills hill
+ hilt hilt
+ hilts hilt
+ hily hili
+ him him
+ himself himself
+ hinc hinc
+ hinckley hincklei
+ hind hind
+ hinder hinder
+ hindered hinder
+ hinders hinder
+ hindmost hindmost
+ hinds hind
+ hing hing
+ hinge hing
+ hinges hing
+ hint hint
+ hip hip
+ hipp hipp
+ hipparchus hipparchu
+ hippolyta hippolyta
+ hips hip
+ hir hir
+ hire hire
+ hired hire
+ hiren hiren
+ hirtius hirtiu
+ his hi
+ hisperia hisperia
+ hiss hiss
+ hisses hiss
+ hissing hiss
+ hist hist
+ historical histor
+ history histori
+ hit hit
+ hither hither
+ hitherto hitherto
+ hitherward hitherward
+ hitherwards hitherward
+ hits hit
+ hitting hit
+ hive hive
+ hives hive
+ hizzing hizz
+ ho ho
+ hoa hoa
+ hoar hoar
+ hoard hoard
+ hoarded hoard
+ hoarding hoard
+ hoars hoar
+ hoarse hoars
+ hoary hoari
+ hob hob
+ hobbididence hobbidid
+ hobby hobbi
+ hobbyhorse hobbyhors
+ hobgoblin hobgoblin
+ hobnails hobnail
+ hoc hoc
+ hod hod
+ hodge hodg
+ hog hog
+ hogs hog
+ hogshead hogshead
+ hogsheads hogshead
+ hois hoi
+ hoise hois
+ hoist hoist
+ hoisted hoist
+ hoists hoist
+ holborn holborn
+ hold hold
+ holden holden
+ holder holder
+ holdeth holdeth
+ holdfast holdfast
+ holding hold
+ holds hold
+ hole hole
+ holes hole
+ holidam holidam
+ holidame holidam
+ holiday holidai
+ holidays holidai
+ holier holier
+ holiest holiest
+ holily holili
+ holiness holi
+ holla holla
+ holland holland
+ hollander holland
+ hollanders holland
+ holloa holloa
+ holloaing holloa
+ hollow hollow
+ hollowly hollowli
+ hollowness hollow
+ holly holli
+ holmedon holmedon
+ holofernes holofern
+ holp holp
+ holy holi
+ homage homag
+ homager homag
+ home home
+ homely home
+ homes home
+ homespuns homespun
+ homeward homeward
+ homewards homeward
+ homicide homicid
+ homicides homicid
+ homily homili
+ hominem hominem
+ hommes homm
+ homo homo
+ honest honest
+ honester honest
+ honestest honestest
+ honestly honestli
+ honesty honesti
+ honey honei
+ honeycomb honeycomb
+ honeying honei
+ honeyless honeyless
+ honeysuckle honeysuckl
+ honeysuckles honeysuckl
+ honi honi
+ honneur honneur
+ honor honor
+ honorable honor
+ honorably honor
+ honorato honorato
+ honors honor
+ honour honour
+ honourable honour
+ honourably honour
+ honoured honour
+ honourest honourest
+ honourible honour
+ honouring honour
+ honours honour
+ hoo hoo
+ hood hood
+ hooded hood
+ hoodman hoodman
+ hoods hood
+ hoodwink hoodwink
+ hoof hoof
+ hoofs hoof
+ hook hook
+ hooking hook
+ hooks hook
+ hoop hoop
+ hoops hoop
+ hoot hoot
+ hooted hoot
+ hooting hoot
+ hoots hoot
+ hop hop
+ hope hope
+ hopeful hope
+ hopeless hopeless
+ hopes hope
+ hopest hopest
+ hoping hope
+ hopkins hopkin
+ hoppedance hopped
+ hor hor
+ horace horac
+ horatio horatio
+ horizon horizon
+ horn horn
+ hornbook hornbook
+ horned horn
+ horner horner
+ horning horn
+ hornpipes hornpip
+ horns horn
+ horologe horolog
+ horrible horribl
+ horribly horribl
+ horrid horrid
+ horrider horrid
+ horridly horridli
+ horror horror
+ horrors horror
+ hors hor
+ horse hors
+ horseback horseback
+ horsed hors
+ horsehairs horsehair
+ horseman horseman
+ horsemanship horsemanship
+ horsemen horsemen
+ horses hors
+ horseway horsewai
+ horsing hors
+ hortensio hortensio
+ hortensius hortensiu
+ horum horum
+ hose hose
+ hospitable hospit
+ hospital hospit
+ hospitality hospit
+ host host
+ hostage hostag
+ hostages hostag
+ hostess hostess
+ hostile hostil
+ hostility hostil
+ hostilius hostiliu
+ hosts host
+ hot hot
+ hotly hotli
+ hotspur hotspur
+ hotter hotter
+ hottest hottest
+ hound hound
+ hounds hound
+ hour hour
+ hourly hourli
+ hours hour
+ hous hou
+ house hous
+ household household
+ householder household
+ householders household
+ households household
+ housekeeper housekeep
+ housekeepers housekeep
+ housekeeping housekeep
+ houseless houseless
+ houses hous
+ housewife housewif
+ housewifery housewiferi
+ housewives housew
+ hovel hovel
+ hover hover
+ hovered hover
+ hovering hover
+ hovers hover
+ how how
+ howbeit howbeit
+ howe how
+ howeer howeer
+ however howev
+ howl howl
+ howled howl
+ howlet howlet
+ howling howl
+ howls howl
+ howsoe howso
+ howsoever howsoev
+ howsome howsom
+ hoxes hox
+ hoy hoi
+ hoyday hoydai
+ hubert hubert
+ huddled huddl
+ huddling huddl
+ hue hue
+ hued hu
+ hues hue
+ hug hug
+ huge huge
+ hugely huge
+ hugeness huge
+ hugg hugg
+ hugger hugger
+ hugh hugh
+ hugs hug
+ hujus huju
+ hulk hulk
+ hulks hulk
+ hull hull
+ hulling hull
+ hullo hullo
+ hum hum
+ human human
+ humane human
+ humanely human
+ humanity human
+ humble humbl
+ humbled humbl
+ humbleness humbl
+ humbler humbler
+ humbles humbl
+ humblest humblest
+ humbling humbl
+ humbly humbl
+ hume hume
+ humh humh
+ humidity humid
+ humility humil
+ humming hum
+ humor humor
+ humorous humor
+ humors humor
+ humour humour
+ humourists humourist
+ humours humour
+ humphrey humphrei
+ humphry humphri
+ hums hum
+ hundred hundr
+ hundreds hundr
+ hundredth hundredth
+ hung hung
+ hungarian hungarian
+ hungary hungari
+ hunger hunger
+ hungerford hungerford
+ hungerly hungerli
+ hungry hungri
+ hunt hunt
+ hunted hunt
+ hunter hunter
+ hunters hunter
+ hunteth hunteth
+ hunting hunt
+ huntington huntington
+ huntress huntress
+ hunts hunt
+ huntsman huntsman
+ huntsmen huntsmen
+ hurdle hurdl
+ hurl hurl
+ hurling hurl
+ hurls hurl
+ hurly hurli
+ hurlyburly hurlyburli
+ hurricano hurricano
+ hurricanoes hurricano
+ hurried hurri
+ hurries hurri
+ hurry hurri
+ hurt hurt
+ hurting hurt
+ hurtled hurtl
+ hurtless hurtless
+ hurtling hurtl
+ hurts hurt
+ husband husband
+ husbanded husband
+ husbandless husbandless
+ husbandry husbandri
+ husbands husband
+ hush hush
+ hushes hush
+ husht husht
+ husks husk
+ huswife huswif
+ huswifes huswif
+ hutch hutch
+ hybla hybla
+ hydra hydra
+ hyen hyen
+ hymen hymen
+ hymenaeus hymenaeu
+ hymn hymn
+ hymns hymn
+ hyperboles hyperbol
+ hyperbolical hyperbol
+ hyperion hyperion
+ hypocrisy hypocrisi
+ hypocrite hypocrit
+ hypocrites hypocrit
+ hyrcan hyrcan
+ hyrcania hyrcania
+ hyrcanian hyrcanian
+ hyssop hyssop
+ hysterica hysterica
+ i i
+ iachimo iachimo
+ iaculis iaculi
+ iago iago
+ iament iament
+ ibat ibat
+ icarus icaru
+ ice ic
+ iceland iceland
+ ici ici
+ icicle icicl
+ icicles icicl
+ icy ici
+ idea idea
+ ideas idea
+ idem idem
+ iden iden
+ ides id
+ idiot idiot
+ idiots idiot
+ idle idl
+ idleness idl
+ idles idl
+ idly idli
+ idol idol
+ idolatrous idolatr
+ idolatry idolatri
+ ield ield
+ if if
+ ifs if
+ ignis igni
+ ignoble ignobl
+ ignobly ignobl
+ ignominious ignomini
+ ignominy ignomini
+ ignomy ignomi
+ ignorance ignor
+ ignorant ignor
+ ii ii
+ iii iii
+ iiii iiii
+ il il
+ ilbow ilbow
+ ild ild
+ ilion ilion
+ ilium ilium
+ ill ill
+ illegitimate illegitim
+ illiterate illiter
+ illness ill
+ illo illo
+ ills ill
+ illume illum
+ illumin illumin
+ illuminate illumin
+ illumineth illumineth
+ illusion illus
+ illusions illus
+ illustrate illustr
+ illustrated illustr
+ illustrious illustri
+ illyria illyria
+ illyrian illyrian
+ ils il
+ im im
+ image imag
+ imagery imageri
+ images imag
+ imagin imagin
+ imaginary imaginari
+ imagination imagin
+ imaginations imagin
+ imagine imagin
+ imagining imagin
+ imaginings imagin
+ imbar imbar
+ imbecility imbecil
+ imbrue imbru
+ imitari imitari
+ imitate imit
+ imitated imit
+ imitation imit
+ imitations imit
+ immaculate immacul
+ immanity imman
+ immask immask
+ immaterial immateri
+ immediacy immediaci
+ immediate immedi
+ immediately immedi
+ imminence immin
+ imminent immin
+ immoderate immoder
+ immoderately immoder
+ immodest immodest
+ immoment immoment
+ immortal immort
+ immortaliz immortaliz
+ immortally immort
+ immur immur
+ immured immur
+ immures immur
+ imogen imogen
+ imp imp
+ impaint impaint
+ impair impair
+ impairing impair
+ impale impal
+ impaled impal
+ impanelled impanel
+ impart impart
+ imparted impart
+ impartial imparti
+ impartment impart
+ imparts impart
+ impasted impast
+ impatience impati
+ impatient impati
+ impatiently impati
+ impawn impawn
+ impeach impeach
+ impeached impeach
+ impeachment impeach
+ impeachments impeach
+ impedes imped
+ impediment impedi
+ impediments impedi
+ impenetrable impenetr
+ imperator imper
+ imperceiverant imperceiver
+ imperfect imperfect
+ imperfection imperfect
+ imperfections imperfect
+ imperfectly imperfectli
+ imperial imperi
+ imperious imperi
+ imperiously imperi
+ impertinency impertin
+ impertinent impertin
+ impeticos impetico
+ impetuosity impetuos
+ impetuous impetu
+ impieties impieti
+ impiety impieti
+ impious impiou
+ implacable implac
+ implements implement
+ implies impli
+ implor implor
+ implorators implor
+ implore implor
+ implored implor
+ imploring implor
+ impon impon
+ import import
+ importance import
+ importancy import
+ important import
+ importantly importantli
+ imported import
+ importeth importeth
+ importing import
+ importless importless
+ imports import
+ importun importun
+ importunacy importunaci
+ importunate importun
+ importune importun
+ importunes importun
+ importunity importun
+ impos impo
+ impose impos
+ imposed impos
+ imposition imposit
+ impositions imposit
+ impossibilities imposs
+ impossibility imposs
+ impossible imposs
+ imposthume imposthum
+ impostor impostor
+ impostors impostor
+ impotence impot
+ impotent impot
+ impounded impound
+ impregnable impregn
+ imprese impres
+ impress impress
+ impressed impress
+ impressest impressest
+ impression impress
+ impressure impressur
+ imprimendum imprimendum
+ imprimis imprimi
+ imprint imprint
+ imprinted imprint
+ imprison imprison
+ imprisoned imprison
+ imprisoning imprison
+ imprisonment imprison
+ improbable improb
+ improper improp
+ improve improv
+ improvident improvid
+ impudence impud
+ impudency impud
+ impudent impud
+ impudently impud
+ impudique impudiqu
+ impugn impugn
+ impugns impugn
+ impure impur
+ imputation imput
+ impute imput
+ in in
+ inaccessible inaccess
+ inaidable inaid
+ inaudible inaud
+ inauspicious inauspici
+ incaged incag
+ incantations incant
+ incapable incap
+ incardinate incardin
+ incarnadine incarnadin
+ incarnate incarn
+ incarnation incarn
+ incens incen
+ incense incens
+ incensed incens
+ incensement incens
+ incenses incens
+ incensing incens
+ incertain incertain
+ incertainties incertainti
+ incertainty incertainti
+ incessant incess
+ incessantly incessantli
+ incest incest
+ incestuous incestu
+ inch inch
+ incharitable incharit
+ inches inch
+ incidency incid
+ incident incid
+ incision incis
+ incite incit
+ incites incit
+ incivil incivil
+ incivility incivil
+ inclin inclin
+ inclinable inclin
+ inclination inclin
+ incline inclin
+ inclined inclin
+ inclines inclin
+ inclining inclin
+ inclips inclip
+ include includ
+ included includ
+ includes includ
+ inclusive inclus
+ incomparable incompar
+ incomprehensible incomprehens
+ inconsiderate inconsider
+ inconstancy inconst
+ inconstant inconst
+ incontinency incontin
+ incontinent incontin
+ incontinently incontin
+ inconvenience inconveni
+ inconveniences inconveni
+ inconvenient inconveni
+ incony inconi
+ incorporate incorpor
+ incorps incorp
+ incorrect incorrect
+ increas increa
+ increase increas
+ increases increas
+ increaseth increaseth
+ increasing increas
+ incredible incred
+ incredulous incredul
+ incur incur
+ incurable incur
+ incurr incurr
+ incurred incur
+ incursions incurs
+ ind ind
+ inde ind
+ indebted indebt
+ indeed inde
+ indent indent
+ indented indent
+ indenture indentur
+ indentures indentur
+ index index
+ indexes index
+ india india
+ indian indian
+ indict indict
+ indicted indict
+ indictment indict
+ indies indi
+ indifferency indiffer
+ indifferent indiffer
+ indifferently indiffer
+ indigent indig
+ indigest indigest
+ indigested indigest
+ indign indign
+ indignation indign
+ indignations indign
+ indigne indign
+ indignities indign
+ indignity indign
+ indirect indirect
+ indirection indirect
+ indirections indirect
+ indirectly indirectli
+ indiscreet indiscreet
+ indiscretion indiscret
+ indispos indispo
+ indisposition indisposit
+ indissoluble indissolubl
+ indistinct indistinct
+ indistinguish indistinguish
+ indistinguishable indistinguish
+ indited indit
+ individable individ
+ indrench indrench
+ indu indu
+ indubitate indubit
+ induc induc
+ induce induc
+ induced induc
+ inducement induc
+ induction induct
+ inductions induct
+ indue indu
+ indued indu
+ indues indu
+ indulgence indulg
+ indulgences indulg
+ indulgent indulg
+ indurance indur
+ industrious industri
+ industriously industri
+ industry industri
+ inequality inequ
+ inestimable inestim
+ inevitable inevit
+ inexecrable inexecr
+ inexorable inexor
+ inexplicable inexplic
+ infallible infal
+ infallibly infal
+ infamonize infamon
+ infamous infam
+ infamy infami
+ infancy infanc
+ infant infant
+ infants infant
+ infect infect
+ infected infect
+ infecting infect
+ infection infect
+ infections infect
+ infectious infecti
+ infectiously infecti
+ infects infect
+ infer infer
+ inference infer
+ inferior inferior
+ inferiors inferior
+ infernal infern
+ inferr inferr
+ inferreth inferreth
+ inferring infer
+ infest infest
+ infidel infidel
+ infidels infidel
+ infinite infinit
+ infinitely infinit
+ infinitive infinit
+ infirm infirm
+ infirmities infirm
+ infirmity infirm
+ infixed infix
+ infixing infix
+ inflam inflam
+ inflame inflam
+ inflaming inflam
+ inflammation inflamm
+ inflict inflict
+ infliction inflict
+ influence influenc
+ influences influenc
+ infold infold
+ inform inform
+ informal inform
+ information inform
+ informations inform
+ informed inform
+ informer inform
+ informs inform
+ infortunate infortun
+ infring infr
+ infringe infring
+ infringed infring
+ infus infu
+ infuse infus
+ infused infus
+ infusing infus
+ infusion infus
+ ingener ingen
+ ingenious ingeni
+ ingeniously ingeni
+ inglorious inglori
+ ingots ingot
+ ingraffed ingraf
+ ingraft ingraft
+ ingrate ingrat
+ ingrated ingrat
+ ingrateful ingrat
+ ingratitude ingratitud
+ ingratitudes ingratitud
+ ingredient ingredi
+ ingredients ingredi
+ ingross ingross
+ inhabit inhabit
+ inhabitable inhabit
+ inhabitants inhabit
+ inhabited inhabit
+ inhabits inhabit
+ inhearse inhears
+ inhearsed inhears
+ inherent inher
+ inherit inherit
+ inheritance inherit
+ inherited inherit
+ inheriting inherit
+ inheritor inheritor
+ inheritors inheritor
+ inheritrix inheritrix
+ inherits inherit
+ inhibited inhibit
+ inhibition inhibit
+ inhoop inhoop
+ inhuman inhuman
+ iniquities iniqu
+ iniquity iniqu
+ initiate initi
+ injointed injoint
+ injunction injunct
+ injunctions injunct
+ injur injur
+ injure injur
+ injurer injur
+ injuries injuri
+ injurious injuri
+ injury injuri
+ injustice injustic
+ ink ink
+ inkhorn inkhorn
+ inkle inkl
+ inkles inkl
+ inkling inkl
+ inky inki
+ inlaid inlaid
+ inland inland
+ inlay inlai
+ inly inli
+ inmost inmost
+ inn inn
+ inner inner
+ innkeeper innkeep
+ innocence innoc
+ innocency innoc
+ innocent innoc
+ innocents innoc
+ innovation innov
+ innovator innov
+ inns inn
+ innumerable innumer
+ inoculate inocul
+ inordinate inordin
+ inprimis inprimi
+ inquir inquir
+ inquire inquir
+ inquiry inquiri
+ inquisition inquisit
+ inquisitive inquisit
+ inroads inroad
+ insane insan
+ insanie insani
+ insatiate insati
+ insconce insconc
+ inscrib inscrib
+ inscription inscript
+ inscriptions inscript
+ inscroll inscrol
+ inscrutable inscrut
+ insculp insculp
+ insculpture insculptur
+ insensible insens
+ inseparable insepar
+ inseparate insepar
+ insert insert
+ inserted insert
+ inset inset
+ inshell inshel
+ inshipp inshipp
+ inside insid
+ insinewed insinew
+ insinuate insinu
+ insinuateth insinuateth
+ insinuating insinu
+ insinuation insinu
+ insisted insist
+ insisting insist
+ insisture insistur
+ insociable insoci
+ insolence insol
+ insolent insol
+ insomuch insomuch
+ inspir inspir
+ inspiration inspir
+ inspirations inspir
+ inspire inspir
+ inspired inspir
+ install instal
+ installed instal
+ instalment instal
+ instance instanc
+ instances instanc
+ instant instant
+ instantly instantli
+ instate instat
+ instead instead
+ insteeped insteep
+ instigate instig
+ instigated instig
+ instigation instig
+ instigations instig
+ instigator instig
+ instinct instinct
+ instinctively instinct
+ institute institut
+ institutions institut
+ instruct instruct
+ instructed instruct
+ instruction instruct
+ instructions instruct
+ instructs instruct
+ instrument instrument
+ instrumental instrument
+ instruments instrument
+ insubstantial insubstanti
+ insufficience insuffici
+ insufficiency insuffici
+ insult insult
+ insulted insult
+ insulting insult
+ insultment insult
+ insults insult
+ insupportable insupport
+ insuppressive insuppress
+ insurrection insurrect
+ insurrections insurrect
+ int int
+ integer integ
+ integritas integrita
+ integrity integr
+ intellect intellect
+ intellects intellect
+ intellectual intellectu
+ intelligence intellig
+ intelligencer intelligenc
+ intelligencing intelligenc
+ intelligent intellig
+ intelligis intelligi
+ intelligo intelligo
+ intemperance intemper
+ intemperate intemper
+ intend intend
+ intended intend
+ intendeth intendeth
+ intending intend
+ intendment intend
+ intends intend
+ intenible inten
+ intent intent
+ intention intent
+ intentively intent
+ intents intent
+ inter inter
+ intercept intercept
+ intercepted intercept
+ intercepter intercept
+ interception intercept
+ intercepts intercept
+ intercession intercess
+ intercessors intercessor
+ interchained interchain
+ interchang interchang
+ interchange interchang
+ interchangeably interchang
+ interchangement interchang
+ interchanging interchang
+ interdiction interdict
+ interest interest
+ interim interim
+ interims interim
+ interior interior
+ interjections interject
+ interjoin interjoin
+ interlude interlud
+ intermingle intermingl
+ intermission intermiss
+ intermissive intermiss
+ intermit intermit
+ intermix intermix
+ intermixed intermix
+ interpose interpos
+ interposer interpos
+ interposes interpos
+ interpret interpret
+ interpretation interpret
+ interpreted interpret
+ interpreter interpret
+ interpreters interpret
+ interprets interpret
+ interr interr
+ interred inter
+ interrogatories interrogatori
+ interrupt interrupt
+ interrupted interrupt
+ interrupter interrupt
+ interruptest interruptest
+ interruption interrupt
+ interrupts interrupt
+ intertissued intertissu
+ intervallums intervallum
+ interview interview
+ intestate intest
+ intestine intestin
+ intil intil
+ intimate intim
+ intimation intim
+ intitled intitl
+ intituled intitul
+ into into
+ intolerable intoler
+ intoxicates intox
+ intreasured intreasur
+ intreat intreat
+ intrench intrench
+ intrenchant intrench
+ intricate intric
+ intrinse intrins
+ intrinsicate intrins
+ intrude intrud
+ intruder intrud
+ intruding intrud
+ intrusion intrus
+ inundation inund
+ inure inur
+ inurn inurn
+ invade invad
+ invades invad
+ invasion invas
+ invasive invas
+ invectively invect
+ invectives invect
+ inveigled inveigl
+ invent invent
+ invented invent
+ invention invent
+ inventions invent
+ inventor inventor
+ inventorially inventori
+ inventoried inventori
+ inventors inventor
+ inventory inventori
+ inverness inver
+ invert invert
+ invest invest
+ invested invest
+ investing invest
+ investments invest
+ inveterate inveter
+ invincible invinc
+ inviolable inviol
+ invised invis
+ invisible invis
+ invitation invit
+ invite invit
+ invited invit
+ invites invit
+ inviting invit
+ invitis inviti
+ invocate invoc
+ invocation invoc
+ invoke invok
+ invoked invok
+ invulnerable invulner
+ inward inward
+ inwardly inwardli
+ inwardness inward
+ inwards inward
+ ionia ionia
+ ionian ionian
+ ipse ips
+ ipswich ipswich
+ ira ira
+ irae ira
+ iras ira
+ ire ir
+ ireful ir
+ ireland ireland
+ iris iri
+ irish irish
+ irishman irishman
+ irishmen irishmen
+ irks irk
+ irksome irksom
+ iron iron
+ irons iron
+ irreconcil irreconcil
+ irrecoverable irrecover
+ irregular irregular
+ irregulous irregul
+ irreligious irreligi
+ irremovable irremov
+ irreparable irrepar
+ irresolute irresolut
+ irrevocable irrevoc
+ is is
+ isabel isabel
+ isabella isabella
+ isbel isbel
+ isbels isbel
+ iscariot iscariot
+ ise is
+ ish ish
+ isidore isidor
+ isis isi
+ island island
+ islander island
+ islanders island
+ islands island
+ isle isl
+ isles isl
+ israel israel
+ issu issu
+ issue issu
+ issued issu
+ issueless issueless
+ issues issu
+ issuing issu
+ ist ist
+ ista ista
+ it it
+ italian italian
+ italy itali
+ itch itch
+ itches itch
+ itching itch
+ item item
+ items item
+ iteration iter
+ ithaca ithaca
+ its it
+ itself itself
+ itshall itshal
+ iv iv
+ ivory ivori
+ ivy ivi
+ iwis iwi
+ ix ix
+ j j
+ jacet jacet
+ jack jack
+ jackanapes jackanap
+ jacks jack
+ jacksauce jacksauc
+ jackslave jackslav
+ jacob jacob
+ jade jade
+ jaded jade
+ jades jade
+ jail jail
+ jakes jake
+ jamany jamani
+ james jame
+ jamy jami
+ jane jane
+ jangled jangl
+ jangling jangl
+ january januari
+ janus janu
+ japhet japhet
+ jaquenetta jaquenetta
+ jaques jaqu
+ jar jar
+ jarring jar
+ jars jar
+ jarteer jarteer
+ jasons jason
+ jaunce jaunc
+ jauncing jaunc
+ jaundice jaundic
+ jaundies jaundi
+ jaw jaw
+ jawbone jawbon
+ jaws jaw
+ jay jai
+ jays jai
+ jc jc
+ je je
+ jealous jealou
+ jealousies jealousi
+ jealousy jealousi
+ jeer jeer
+ jeering jeer
+ jelly jelli
+ jenny jenni
+ jeopardy jeopardi
+ jephtha jephtha
+ jephthah jephthah
+ jerkin jerkin
+ jerkins jerkin
+ jerks jerk
+ jeronimy jeronimi
+ jerusalem jerusalem
+ jeshu jeshu
+ jesses jess
+ jessica jessica
+ jest jest
+ jested jest
+ jester jester
+ jesters jester
+ jesting jest
+ jests jest
+ jesu jesu
+ jesus jesu
+ jet jet
+ jets jet
+ jew jew
+ jewel jewel
+ jeweller jewel
+ jewels jewel
+ jewess jewess
+ jewish jewish
+ jewry jewri
+ jews jew
+ jezebel jezebel
+ jig jig
+ jigging jig
+ jill jill
+ jills jill
+ jingling jingl
+ joan joan
+ job job
+ jockey jockei
+ jocund jocund
+ jog jog
+ jogging jog
+ john john
+ johns john
+ join join
+ joinder joinder
+ joined join
+ joiner joiner
+ joineth joineth
+ joins join
+ joint joint
+ jointed joint
+ jointing joint
+ jointly jointli
+ jointress jointress
+ joints joint
+ jointure jointur
+ jollity jolliti
+ jolly jolli
+ jolt jolt
+ joltheads jolthead
+ jordan jordan
+ joseph joseph
+ joshua joshua
+ jot jot
+ jour jour
+ jourdain jourdain
+ journal journal
+ journey journei
+ journeying journei
+ journeyman journeyman
+ journeymen journeymen
+ journeys journei
+ jove jove
+ jovem jovem
+ jovial jovial
+ jowl jowl
+ jowls jowl
+ joy joi
+ joyed joi
+ joyful joy
+ joyfully joyfulli
+ joyless joyless
+ joyous joyou
+ joys joi
+ juan juan
+ jud jud
+ judas juda
+ judases judas
+ jude jude
+ judg judg
+ judge judg
+ judged judg
+ judgement judgement
+ judges judg
+ judgest judgest
+ judging judg
+ judgment judgment
+ judgments judgment
+ judicious judici
+ jug jug
+ juggle juggl
+ juggled juggl
+ juggler juggler
+ jugglers juggler
+ juggling juggl
+ jugs jug
+ juice juic
+ juiced juic
+ jul jul
+ jule jule
+ julia julia
+ juliet juliet
+ julietta julietta
+ julio julio
+ julius juliu
+ july juli
+ jump jump
+ jumpeth jumpeth
+ jumping jump
+ jumps jump
+ june june
+ junes june
+ junior junior
+ junius juniu
+ junkets junket
+ juno juno
+ jupiter jupit
+ jure jure
+ jurement jurement
+ jurisdiction jurisdict
+ juror juror
+ jurors juror
+ jury juri
+ jurymen jurymen
+ just just
+ justeius justeiu
+ justest justest
+ justice justic
+ justicer justic
+ justicers justic
+ justices justic
+ justification justif
+ justified justifi
+ justify justifi
+ justle justl
+ justled justl
+ justles justl
+ justling justl
+ justly justli
+ justness just
+ justs just
+ jutting jut
+ jutty jutti
+ juvenal juven
+ kam kam
+ kate kate
+ kated kate
+ kates kate
+ katharine katharin
+ katherina katherina
+ katherine katherin
+ kecksies kecksi
+ keech keech
+ keel keel
+ keels keel
+ keen keen
+ keenness keen
+ keep keep
+ keepdown keepdown
+ keeper keeper
+ keepers keeper
+ keepest keepest
+ keeping keep
+ keeps keep
+ keiser keiser
+ ken ken
+ kendal kendal
+ kennel kennel
+ kent kent
+ kentish kentish
+ kentishman kentishman
+ kentishmen kentishmen
+ kept kept
+ kerchief kerchief
+ kerely kere
+ kern kern
+ kernal kernal
+ kernel kernel
+ kernels kernel
+ kerns kern
+ kersey kersei
+ kettle kettl
+ kettledrum kettledrum
+ kettledrums kettledrum
+ key kei
+ keys kei
+ kibe kibe
+ kibes kibe
+ kick kick
+ kicked kick
+ kickshaws kickshaw
+ kickshawses kickshaws
+ kicky kicki
+ kid kid
+ kidney kidnei
+ kikely kike
+ kildare kildar
+ kill kill
+ killed kill
+ killer killer
+ killeth killeth
+ killing kill
+ killingworth killingworth
+ kills kill
+ kiln kiln
+ kimbolton kimbolton
+ kin kin
+ kind kind
+ kinder kinder
+ kindest kindest
+ kindle kindl
+ kindled kindl
+ kindless kindless
+ kindlier kindlier
+ kindling kindl
+ kindly kindli
+ kindness kind
+ kindnesses kind
+ kindred kindr
+ kindreds kindr
+ kinds kind
+ kine kine
+ king king
+ kingdom kingdom
+ kingdoms kingdom
+ kingly kingli
+ kings king
+ kinred kinr
+ kins kin
+ kinsman kinsman
+ kinsmen kinsmen
+ kinswoman kinswoman
+ kirtle kirtl
+ kirtles kirtl
+ kiss kiss
+ kissed kiss
+ kisses kiss
+ kissing kiss
+ kitchen kitchen
+ kitchens kitchen
+ kite kite
+ kites kite
+ kitten kitten
+ kj kj
+ kl kl
+ klll klll
+ knack knack
+ knacks knack
+ knapp knapp
+ knav knav
+ knave knave
+ knaveries knaveri
+ knavery knaveri
+ knaves knave
+ knavish knavish
+ knead knead
+ kneaded knead
+ kneading knead
+ knee knee
+ kneel kneel
+ kneeling kneel
+ kneels kneel
+ knees knee
+ knell knell
+ knew knew
+ knewest knewest
+ knife knife
+ knight knight
+ knighted knight
+ knighthood knighthood
+ knighthoods knighthood
+ knightly knightli
+ knights knight
+ knit knit
+ knits knit
+ knitters knitter
+ knitteth knitteth
+ knives knive
+ knobs knob
+ knock knock
+ knocking knock
+ knocks knock
+ knog knog
+ knoll knoll
+ knot knot
+ knots knot
+ knotted knot
+ knotty knotti
+ know know
+ knower knower
+ knowest knowest
+ knowing know
+ knowingly knowingli
+ knowings know
+ knowledge knowledg
+ known known
+ knows know
+ l l
+ la la
+ laban laban
+ label label
+ labell label
+ labienus labienu
+ labio labio
+ labor labor
+ laboring labor
+ labors labor
+ labour labour
+ laboured labour
+ labourer labour
+ labourers labour
+ labouring labour
+ labours labour
+ laboursome laboursom
+ labras labra
+ labyrinth labyrinth
+ lac lac
+ lace lace
+ laced lace
+ lacedaemon lacedaemon
+ laces lace
+ lacies laci
+ lack lack
+ lackbeard lackbeard
+ lacked lack
+ lackey lackei
+ lackeying lackei
+ lackeys lackei
+ lacking lack
+ lacks lack
+ lad lad
+ ladder ladder
+ ladders ladder
+ lade lade
+ laden laden
+ ladies ladi
+ lading lade
+ lads lad
+ lady ladi
+ ladybird ladybird
+ ladyship ladyship
+ ladyships ladyship
+ laer laer
+ laertes laert
+ lafeu lafeu
+ lag lag
+ lagging lag
+ laid laid
+ lain lain
+ laissez laissez
+ lake lake
+ lakes lake
+ lakin lakin
+ lam lam
+ lamb lamb
+ lambert lambert
+ lambkin lambkin
+ lambkins lambkin
+ lambs lamb
+ lame lame
+ lamely lame
+ lameness lame
+ lament lament
+ lamentable lament
+ lamentably lament
+ lamentation lament
+ lamentations lament
+ lamented lament
+ lamenting lament
+ lamentings lament
+ laments lament
+ lames lame
+ laming lame
+ lammas lamma
+ lammastide lammastid
+ lamound lamound
+ lamp lamp
+ lampass lampass
+ lamps lamp
+ lanc lanc
+ lancaster lancast
+ lance lanc
+ lances lanc
+ lanceth lanceth
+ lanch lanch
+ land land
+ landed land
+ landing land
+ landless landless
+ landlord landlord
+ landmen landmen
+ lands land
+ lane lane
+ lanes lane
+ langage langag
+ langley langlei
+ langton langton
+ language languag
+ languageless languageless
+ languages languag
+ langues langu
+ languish languish
+ languished languish
+ languishes languish
+ languishing languish
+ languishings languish
+ languishment languish
+ languor languor
+ lank lank
+ lantern lantern
+ lanterns lantern
+ lanthorn lanthorn
+ lap lap
+ lapis lapi
+ lapland lapland
+ lapp lapp
+ laps lap
+ lapse laps
+ lapsed laps
+ lapsing laps
+ lapwing lapw
+ laquais laquai
+ larded lard
+ larder larder
+ larding lard
+ lards lard
+ large larg
+ largely larg
+ largeness larg
+ larger larger
+ largess largess
+ largest largest
+ lark lark
+ larks lark
+ larron larron
+ lartius lartiu
+ larum larum
+ larums larum
+ las la
+ lascivious lascivi
+ lash lash
+ lass lass
+ lasses lass
+ last last
+ lasted last
+ lasting last
+ lastly lastli
+ lasts last
+ latch latch
+ latches latch
+ late late
+ lated late
+ lately late
+ later later
+ latest latest
+ lath lath
+ latin latin
+ latten latten
+ latter latter
+ lattice lattic
+ laud laud
+ laudable laudabl
+ laudis laudi
+ laugh laugh
+ laughable laughabl
+ laughed laugh
+ laugher laugher
+ laughest laughest
+ laughing laugh
+ laughs laugh
+ laughter laughter
+ launce launc
+ launcelot launcelot
+ launces launc
+ launch launch
+ laund laund
+ laundress laundress
+ laundry laundri
+ laur laur
+ laura laura
+ laurel laurel
+ laurels laurel
+ laurence laurenc
+ laus lau
+ lavache lavach
+ lave lave
+ lavee lave
+ lavender lavend
+ lavina lavina
+ lavinia lavinia
+ lavish lavish
+ lavishly lavishli
+ lavolt lavolt
+ lavoltas lavolta
+ law law
+ lawful law
+ lawfully lawfulli
+ lawless lawless
+ lawlessly lawlessli
+ lawn lawn
+ lawns lawn
+ lawrence lawrenc
+ laws law
+ lawyer lawyer
+ lawyers lawyer
+ lay lai
+ layer layer
+ layest layest
+ laying lai
+ lays lai
+ lazar lazar
+ lazars lazar
+ lazarus lazaru
+ lazy lazi
+ lc lc
+ ld ld
+ ldst ldst
+ le le
+ lead lead
+ leaden leaden
+ leader leader
+ leaders leader
+ leadest leadest
+ leading lead
+ leads lead
+ leaf leaf
+ leagu leagu
+ league leagu
+ leagued leagu
+ leaguer leaguer
+ leagues leagu
+ leah leah
+ leak leak
+ leaky leaki
+ lean lean
+ leander leander
+ leaner leaner
+ leaning lean
+ leanness lean
+ leans lean
+ leap leap
+ leaped leap
+ leaping leap
+ leaps leap
+ leapt leapt
+ lear lear
+ learn learn
+ learned learn
+ learnedly learnedli
+ learning learn
+ learnings learn
+ learns learn
+ learnt learnt
+ leas lea
+ lease leas
+ leases leas
+ leash leash
+ leasing leas
+ least least
+ leather leather
+ leathern leathern
+ leav leav
+ leave leav
+ leaven leaven
+ leavening leaven
+ leaver leaver
+ leaves leav
+ leaving leav
+ leavy leavi
+ lecher lecher
+ lecherous lecher
+ lechers lecher
+ lechery lecheri
+ lecon lecon
+ lecture lectur
+ lectures lectur
+ led led
+ leda leda
+ leech leech
+ leeches leech
+ leek leek
+ leeks leek
+ leer leer
+ leers leer
+ lees lee
+ leese lees
+ leet leet
+ leets leet
+ left left
+ leg leg
+ legacies legaci
+ legacy legaci
+ legate legat
+ legatine legatin
+ lege lege
+ legerity leger
+ leges lege
+ legg legg
+ legion legion
+ legions legion
+ legitimate legitim
+ legitimation legitim
+ legs leg
+ leicester leicest
+ leicestershire leicestershir
+ leiger leiger
+ leigers leiger
+ leisure leisur
+ leisurely leisur
+ leisures leisur
+ leman leman
+ lemon lemon
+ lena lena
+ lend lend
+ lender lender
+ lending lend
+ lendings lend
+ lends lend
+ length length
+ lengthen lengthen
+ lengthens lengthen
+ lengths length
+ lenity leniti
+ lennox lennox
+ lent lent
+ lenten lenten
+ lentus lentu
+ leo leo
+ leon leon
+ leonardo leonardo
+ leonati leonati
+ leonato leonato
+ leonatus leonatu
+ leontes leont
+ leopard leopard
+ leopards leopard
+ leper leper
+ leperous leper
+ lepidus lepidu
+ leprosy leprosi
+ lequel lequel
+ lers ler
+ les le
+ less less
+ lessen lessen
+ lessens lessen
+ lesser lesser
+ lesson lesson
+ lessoned lesson
+ lessons lesson
+ lest lest
+ lestrake lestrak
+ let let
+ lethargied lethargi
+ lethargies lethargi
+ lethargy lethargi
+ lethe leth
+ lets let
+ lett lett
+ letter letter
+ letters letter
+ letting let
+ lettuce lettuc
+ leur leur
+ leve leve
+ level level
+ levell level
+ levelled level
+ levels level
+ leven leven
+ levers lever
+ leviathan leviathan
+ leviathans leviathan
+ levied levi
+ levies levi
+ levity leviti
+ levy levi
+ levying levi
+ lewd lewd
+ lewdly lewdli
+ lewdness lewd
+ lewdsters lewdster
+ lewis lewi
+ liable liabl
+ liar liar
+ liars liar
+ libbard libbard
+ libelling libel
+ libels libel
+ liberal liber
+ liberality liber
+ liberte libert
+ liberties liberti
+ libertine libertin
+ libertines libertin
+ liberty liberti
+ library librari
+ libya libya
+ licence licenc
+ licens licen
+ license licens
+ licentious licenti
+ lichas licha
+ licio licio
+ lick lick
+ licked lick
+ licker licker
+ lictors lictor
+ lid lid
+ lids lid
+ lie lie
+ lied li
+ lief lief
+ liefest liefest
+ liege lieg
+ liegeman liegeman
+ liegemen liegemen
+ lien lien
+ lies li
+ liest liest
+ lieth lieth
+ lieu lieu
+ lieutenant lieuten
+ lieutenantry lieutenantri
+ lieutenants lieuten
+ lieve liev
+ life life
+ lifeblood lifeblood
+ lifeless lifeless
+ lifelings lifel
+ lift lift
+ lifted lift
+ lifter lifter
+ lifteth lifteth
+ lifting lift
+ lifts lift
+ lig lig
+ ligarius ligariu
+ liggens liggen
+ light light
+ lighted light
+ lighten lighten
+ lightens lighten
+ lighter lighter
+ lightest lightest
+ lightly lightli
+ lightness light
+ lightning lightn
+ lightnings lightn
+ lights light
+ lik lik
+ like like
+ liked like
+ likeliest likeliest
+ likelihood likelihood
+ likelihoods likelihood
+ likely like
+ likeness like
+ liker liker
+ likes like
+ likest likest
+ likewise likewis
+ liking like
+ likings like
+ lilies lili
+ lily lili
+ lim lim
+ limander limand
+ limb limb
+ limbeck limbeck
+ limbecks limbeck
+ limber limber
+ limbo limbo
+ limbs limb
+ lime lime
+ limed lime
+ limehouse limehous
+ limekilns limekiln
+ limit limit
+ limitation limit
+ limited limit
+ limits limit
+ limn limn
+ limp limp
+ limping limp
+ limps limp
+ lin lin
+ lincoln lincoln
+ lincolnshire lincolnshir
+ line line
+ lineal lineal
+ lineally lineal
+ lineament lineament
+ lineaments lineament
+ lined line
+ linen linen
+ linens linen
+ lines line
+ ling ling
+ lingare lingar
+ linger linger
+ lingered linger
+ lingers linger
+ linguist linguist
+ lining line
+ link link
+ links link
+ linsey linsei
+ linstock linstock
+ linta linta
+ lion lion
+ lionel lionel
+ lioness lioness
+ lions lion
+ lip lip
+ lipp lipp
+ lips lip
+ lipsbury lipsburi
+ liquid liquid
+ liquor liquor
+ liquorish liquorish
+ liquors liquor
+ lirra lirra
+ lisbon lisbon
+ lisp lisp
+ lisping lisp
+ list list
+ listen listen
+ listening listen
+ lists list
+ literatured literatur
+ lither lither
+ litter litter
+ little littl
+ littlest littlest
+ liv liv
+ live live
+ lived live
+ livelier liveli
+ livelihood livelihood
+ livelong livelong
+ lively live
+ liver liver
+ liveries liveri
+ livers liver
+ livery liveri
+ lives live
+ livest livest
+ liveth liveth
+ livia livia
+ living live
+ livings live
+ lizard lizard
+ lizards lizard
+ ll ll
+ lll lll
+ llous llou
+ lnd lnd
+ lo lo
+ loa loa
+ loach loach
+ load load
+ loaden loaden
+ loading load
+ loads load
+ loaf loaf
+ loam loam
+ loan loan
+ loath loath
+ loathe loath
+ loathed loath
+ loather loather
+ loathes loath
+ loathing loath
+ loathly loathli
+ loathness loath
+ loathsome loathsom
+ loathsomeness loathsom
+ loathsomest loathsomest
+ loaves loav
+ lob lob
+ lobbies lobbi
+ lobby lobbi
+ local local
+ lochaber lochab
+ lock lock
+ locked lock
+ locking lock
+ lockram lockram
+ locks lock
+ locusts locust
+ lode lode
+ lodg lodg
+ lodge lodg
+ lodged lodg
+ lodgers lodger
+ lodges lodg
+ lodging lodg
+ lodgings lodg
+ lodovico lodovico
+ lodowick lodowick
+ lofty lofti
+ log log
+ logger logger
+ loggerhead loggerhead
+ loggerheads loggerhead
+ loggets logget
+ logic logic
+ logs log
+ loins loin
+ loiter loiter
+ loiterer loiter
+ loiterers loiter
+ loitering loiter
+ lolling loll
+ lolls loll
+ lombardy lombardi
+ london london
+ londoners london
+ lone lone
+ loneliness loneli
+ lonely lone
+ long long
+ longaville longavil
+ longboat longboat
+ longed long
+ longer longer
+ longest longest
+ longeth longeth
+ longing long
+ longings long
+ longly longli
+ longs long
+ longtail longtail
+ loo loo
+ loof loof
+ look look
+ looked look
+ looker looker
+ lookers looker
+ lookest lookest
+ looking look
+ looks look
+ loon loon
+ loop loop
+ loos loo
+ loose loos
+ loosed loos
+ loosely loos
+ loosen loosen
+ loosing loos
+ lop lop
+ lopp lopp
+ loquitur loquitur
+ lord lord
+ lorded lord
+ lording lord
+ lordings lord
+ lordliness lordli
+ lordly lordli
+ lords lord
+ lordship lordship
+ lordships lordship
+ lorenzo lorenzo
+ lorn lorn
+ lorraine lorrain
+ lorship lorship
+ los lo
+ lose lose
+ loser loser
+ losers loser
+ loses lose
+ losest losest
+ loseth loseth
+ losing lose
+ loss loss
+ losses loss
+ lost lost
+ lot lot
+ lots lot
+ lott lott
+ lottery lotteri
+ loud loud
+ louder louder
+ loudly loudli
+ lour lour
+ loureth loureth
+ louring lour
+ louse lous
+ louses lous
+ lousy lousi
+ lout lout
+ louted lout
+ louts lout
+ louvre louvr
+ lov lov
+ love love
+ loved love
+ lovedst lovedst
+ lovel lovel
+ lovelier loveli
+ loveliness loveli
+ lovell lovel
+ lovely love
+ lover lover
+ lovered lover
+ lovers lover
+ loves love
+ lovest lovest
+ loveth loveth
+ loving love
+ lovingly lovingli
+ low low
+ lowe low
+ lower lower
+ lowest lowest
+ lowing low
+ lowliness lowli
+ lowly lowli
+ lown lown
+ lowness low
+ loyal loyal
+ loyally loyal
+ loyalties loyalti
+ loyalty loyalti
+ lozel lozel
+ lt lt
+ lubber lubber
+ lubberly lubberli
+ luc luc
+ luccicos luccico
+ luce luce
+ lucentio lucentio
+ luces luce
+ lucetta lucetta
+ luciana luciana
+ lucianus lucianu
+ lucifer lucif
+ lucifier lucifi
+ lucilius luciliu
+ lucina lucina
+ lucio lucio
+ lucius luciu
+ luck luck
+ luckier luckier
+ luckiest luckiest
+ luckily luckili
+ luckless luckless
+ lucky lucki
+ lucre lucr
+ lucrece lucrec
+ lucretia lucretia
+ lucullius luculliu
+ lucullus lucullu
+ lucy luci
+ lud lud
+ ludlow ludlow
+ lug lug
+ lugg lugg
+ luggage luggag
+ luke luke
+ lukewarm lukewarm
+ lull lull
+ lulla lulla
+ lullaby lullabi
+ lulls lull
+ lumbert lumbert
+ lump lump
+ lumpish lumpish
+ luna luna
+ lunacies lunaci
+ lunacy lunaci
+ lunatic lunat
+ lunatics lunat
+ lunes lune
+ lungs lung
+ lupercal luperc
+ lurch lurch
+ lure lure
+ lurk lurk
+ lurketh lurketh
+ lurking lurk
+ lurks lurk
+ luscious lusciou
+ lush lush
+ lust lust
+ lusted lust
+ luster luster
+ lustful lust
+ lustier lustier
+ lustiest lustiest
+ lustig lustig
+ lustihood lustihood
+ lustily lustili
+ lustre lustr
+ lustrous lustrou
+ lusts lust
+ lusty lusti
+ lute lute
+ lutes lute
+ lutestring lutestr
+ lutheran lutheran
+ luxurious luxuri
+ luxuriously luxuri
+ luxury luxuri
+ ly ly
+ lycaonia lycaonia
+ lycurguses lycurgus
+ lydia lydia
+ lye lye
+ lyen lyen
+ lying ly
+ lym lym
+ lymoges lymog
+ lynn lynn
+ lysander lysand
+ m m
+ ma ma
+ maan maan
+ mab mab
+ macbeth macbeth
+ maccabaeus maccabaeu
+ macdonwald macdonwald
+ macduff macduff
+ mace mace
+ macedon macedon
+ maces mace
+ machiavel machiavel
+ machination machin
+ machinations machin
+ machine machin
+ mack mack
+ macmorris macmorri
+ maculate macul
+ maculation macul
+ mad mad
+ madam madam
+ madame madam
+ madams madam
+ madcap madcap
+ madded mad
+ madding mad
+ made made
+ madeira madeira
+ madly madli
+ madman madman
+ madmen madmen
+ madness mad
+ madonna madonna
+ madrigals madrig
+ mads mad
+ maecenas maecena
+ maggot maggot
+ maggots maggot
+ magic magic
+ magical magic
+ magician magician
+ magistrate magistr
+ magistrates magistr
+ magnanimity magnanim
+ magnanimous magnanim
+ magni magni
+ magnifi magnifi
+ magnificence magnific
+ magnificent magnific
+ magnifico magnifico
+ magnificoes magnifico
+ magnus magnu
+ mahomet mahomet
+ mahu mahu
+ maid maid
+ maiden maiden
+ maidenhead maidenhead
+ maidenheads maidenhead
+ maidenhood maidenhood
+ maidenhoods maidenhood
+ maidenliest maidenliest
+ maidenly maidenli
+ maidens maiden
+ maidhood maidhood
+ maids maid
+ mail mail
+ mailed mail
+ mails mail
+ maim maim
+ maimed maim
+ maims maim
+ main main
+ maincourse maincours
+ maine main
+ mainly mainli
+ mainmast mainmast
+ mains main
+ maintain maintain
+ maintained maintain
+ maintains maintain
+ maintenance mainten
+ mais mai
+ maison maison
+ majestas majesta
+ majestee majeste
+ majestic majest
+ majestical majest
+ majestically majest
+ majesties majesti
+ majesty majesti
+ major major
+ majority major
+ mak mak
+ make make
+ makeless makeless
+ maker maker
+ makers maker
+ makes make
+ makest makest
+ maketh maketh
+ making make
+ makings make
+ mal mal
+ mala mala
+ maladies maladi
+ malady maladi
+ malapert malapert
+ malcolm malcolm
+ malcontent malcont
+ malcontents malcont
+ male male
+ maledictions maledict
+ malefactions malefact
+ malefactor malefactor
+ malefactors malefactor
+ males male
+ malevolence malevol
+ malevolent malevol
+ malhecho malhecho
+ malice malic
+ malicious malici
+ maliciously malici
+ malign malign
+ malignancy malign
+ malignant malign
+ malignantly malignantli
+ malkin malkin
+ mall mall
+ mallard mallard
+ mallet mallet
+ mallows mallow
+ malmsey malmsei
+ malt malt
+ maltworms maltworm
+ malvolio malvolio
+ mamillius mamilliu
+ mammering mammer
+ mammet mammet
+ mammets mammet
+ mammock mammock
+ man man
+ manacle manacl
+ manacles manacl
+ manage manag
+ managed manag
+ manager manag
+ managing manag
+ manakin manakin
+ manchus manchu
+ mandate mandat
+ mandragora mandragora
+ mandrake mandrak
+ mandrakes mandrak
+ mane mane
+ manent manent
+ manes mane
+ manet manet
+ manfully manfulli
+ mangle mangl
+ mangled mangl
+ mangles mangl
+ mangling mangl
+ mangy mangi
+ manhood manhood
+ manhoods manhood
+ manifest manifest
+ manifested manifest
+ manifests manifest
+ manifold manifold
+ manifoldly manifoldli
+ manka manka
+ mankind mankind
+ manlike manlik
+ manly manli
+ mann mann
+ manna manna
+ manner manner
+ mannerly mannerli
+ manners manner
+ manningtree manningtre
+ mannish mannish
+ manor manor
+ manors manor
+ mans man
+ mansion mansion
+ mansionry mansionri
+ mansions mansion
+ manslaughter manslaught
+ mantle mantl
+ mantled mantl
+ mantles mantl
+ mantua mantua
+ mantuan mantuan
+ manual manual
+ manure manur
+ manured manur
+ manus manu
+ many mani
+ map map
+ mapp mapp
+ maps map
+ mar mar
+ marble marbl
+ marbled marbl
+ marcade marcad
+ marcellus marcellu
+ march march
+ marches march
+ marcheth marcheth
+ marching march
+ marchioness marchio
+ marchpane marchpan
+ marcians marcian
+ marcius marciu
+ marcus marcu
+ mardian mardian
+ mare mare
+ mares mare
+ marg marg
+ margarelon margarelon
+ margaret margaret
+ marge marg
+ margent margent
+ margery margeri
+ maria maria
+ marian marian
+ mariana mariana
+ maries mari
+ marigold marigold
+ mariner marin
+ mariners marin
+ maritime maritim
+ marjoram marjoram
+ mark mark
+ marked mark
+ market market
+ marketable market
+ marketplace marketplac
+ markets market
+ marking mark
+ markman markman
+ marks mark
+ marl marl
+ marle marl
+ marmoset marmoset
+ marquess marquess
+ marquis marqui
+ marr marr
+ marriage marriag
+ marriages marriag
+ married marri
+ marries marri
+ marring mar
+ marrow marrow
+ marrowless marrowless
+ marrows marrow
+ marry marri
+ marrying marri
+ mars mar
+ marseilles marseil
+ marsh marsh
+ marshal marshal
+ marshalsea marshalsea
+ marshalship marshalship
+ mart mart
+ marted mart
+ martem martem
+ martext martext
+ martial martial
+ martin martin
+ martino martino
+ martius martiu
+ martlemas martlema
+ martlet martlet
+ marts mart
+ martyr martyr
+ martyrs martyr
+ marullus marullu
+ marv marv
+ marvel marvel
+ marvell marvel
+ marvellous marvel
+ marvellously marvel
+ marvels marvel
+ mary mari
+ mas ma
+ masculine masculin
+ masham masham
+ mask mask
+ masked mask
+ masker masker
+ maskers masker
+ masking mask
+ masks mask
+ mason mason
+ masonry masonri
+ masons mason
+ masque masqu
+ masquers masquer
+ masques masqu
+ masquing masqu
+ mass mass
+ massacre massacr
+ massacres massacr
+ masses mass
+ massy massi
+ mast mast
+ mastcr mastcr
+ master master
+ masterdom masterdom
+ masterest masterest
+ masterless masterless
+ masterly masterli
+ masterpiece masterpiec
+ masters master
+ mastership mastership
+ mastic mastic
+ mastiff mastiff
+ mastiffs mastiff
+ masts mast
+ match match
+ matches match
+ matcheth matcheth
+ matching match
+ matchless matchless
+ mate mate
+ mated mate
+ mater mater
+ material materi
+ mates mate
+ mathematics mathemat
+ matin matin
+ matron matron
+ matrons matron
+ matter matter
+ matters matter
+ matthew matthew
+ mattock mattock
+ mattress mattress
+ mature matur
+ maturity matur
+ maud maud
+ maudlin maudlin
+ maugre maugr
+ maul maul
+ maund maund
+ mauri mauri
+ mauritania mauritania
+ mauvais mauvai
+ maw maw
+ maws maw
+ maxim maxim
+ may mai
+ mayday maydai
+ mayest mayest
+ mayor mayor
+ maypole maypol
+ mayst mayst
+ maz maz
+ maze maze
+ mazed maze
+ mazes maze
+ mazzard mazzard
+ me me
+ meacock meacock
+ mead mead
+ meadow meadow
+ meadows meadow
+ meads mead
+ meagre meagr
+ meal meal
+ meals meal
+ mealy meali
+ mean mean
+ meanders meander
+ meaner meaner
+ meanest meanest
+ meaneth meaneth
+ meaning mean
+ meanings mean
+ meanly meanli
+ means mean
+ meant meant
+ meantime meantim
+ meanwhile meanwhil
+ measles measl
+ measur measur
+ measurable measur
+ measure measur
+ measured measur
+ measureless measureless
+ measures measur
+ measuring measur
+ meat meat
+ meats meat
+ mechanic mechan
+ mechanical mechan
+ mechanicals mechan
+ mechanics mechan
+ mechante mechant
+ med med
+ medal medal
+ meddle meddl
+ meddler meddler
+ meddling meddl
+ mede mede
+ medea medea
+ media media
+ mediation mediat
+ mediators mediat
+ medice medic
+ medicinal medicin
+ medicine medicin
+ medicines medicin
+ meditate medit
+ meditates medit
+ meditating medit
+ meditation medit
+ meditations medit
+ mediterranean mediterranean
+ mediterraneum mediterraneum
+ medlar medlar
+ medlars medlar
+ meed meed
+ meeds meed
+ meek meek
+ meekly meekli
+ meekness meek
+ meet meet
+ meeter meeter
+ meetest meetest
+ meeting meet
+ meetings meet
+ meetly meetli
+ meetness meet
+ meets meet
+ meg meg
+ mehercle mehercl
+ meilleur meilleur
+ meiny meini
+ meisen meisen
+ melancholies melancholi
+ melancholy melancholi
+ melford melford
+ mell mell
+ mellifluous melliflu
+ mellow mellow
+ mellowing mellow
+ melodious melodi
+ melody melodi
+ melt melt
+ melted melt
+ melteth melteth
+ melting melt
+ melts melt
+ melun melun
+ member member
+ members member
+ memento memento
+ memorable memor
+ memorandums memorandum
+ memorial memori
+ memorials memori
+ memories memori
+ memoriz memoriz
+ memorize memor
+ memory memori
+ memphis memphi
+ men men
+ menac menac
+ menace menac
+ menaces menac
+ menaphon menaphon
+ menas mena
+ mend mend
+ mended mend
+ mender mender
+ mending mend
+ mends mend
+ menecrates menecr
+ menelaus menelau
+ menenius meneniu
+ mental mental
+ menteith menteith
+ mention mention
+ mentis menti
+ menton menton
+ mephostophilus mephostophilu
+ mer mer
+ mercatante mercatant
+ mercatio mercatio
+ mercenaries mercenari
+ mercenary mercenari
+ mercer mercer
+ merchandise merchandis
+ merchandized merchand
+ merchant merchant
+ merchants merchant
+ mercies merci
+ merciful merci
+ mercifully mercifulli
+ merciless merciless
+ mercurial mercuri
+ mercuries mercuri
+ mercury mercuri
+ mercutio mercutio
+ mercy merci
+ mere mere
+ mered mere
+ merely mere
+ merest merest
+ meridian meridian
+ merit merit
+ merited merit
+ meritorious meritori
+ merits merit
+ merlin merlin
+ mermaid mermaid
+ mermaids mermaid
+ merops merop
+ merrier merrier
+ merriest merriest
+ merrily merrili
+ merriman merriman
+ merriment merriment
+ merriments merriment
+ merriness merri
+ merry merri
+ mervailous mervail
+ mes me
+ mesh mesh
+ meshes mesh
+ mesopotamia mesopotamia
+ mess mess
+ message messag
+ messages messag
+ messala messala
+ messaline messalin
+ messenger messeng
+ messengers messeng
+ messes mess
+ messina messina
+ met met
+ metal metal
+ metals metal
+ metamorphis metamorphi
+ metamorphoses metamorphos
+ metaphor metaphor
+ metaphysical metaphys
+ metaphysics metaphys
+ mete mete
+ metellus metellu
+ meteor meteor
+ meteors meteor
+ meteyard meteyard
+ metheglin metheglin
+ metheglins metheglin
+ methink methink
+ methinks methink
+ method method
+ methods method
+ methought methought
+ methoughts methought
+ metre metr
+ metres metr
+ metropolis metropoli
+ mette mett
+ mettle mettl
+ mettled mettl
+ meus meu
+ mew mew
+ mewed mew
+ mewling mewl
+ mexico mexico
+ mi mi
+ mice mice
+ michael michael
+ michaelmas michaelma
+ micher micher
+ miching mich
+ mickle mickl
+ microcosm microcosm
+ mid mid
+ midas mida
+ middest middest
+ middle middl
+ middleham middleham
+ midnight midnight
+ midriff midriff
+ midst midst
+ midsummer midsumm
+ midway midwai
+ midwife midwif
+ midwives midwiv
+ mienne mienn
+ might might
+ mightful might
+ mightier mightier
+ mightiest mightiest
+ mightily mightili
+ mightiness mighti
+ mightst mightst
+ mighty mighti
+ milan milan
+ milch milch
+ mild mild
+ milder milder
+ mildest mildest
+ mildew mildew
+ mildews mildew
+ mildly mildli
+ mildness mild
+ mile mile
+ miles mile
+ milford milford
+ militarist militarist
+ military militari
+ milk milk
+ milking milk
+ milkmaid milkmaid
+ milks milk
+ milksops milksop
+ milky milki
+ mill mill
+ mille mill
+ miller miller
+ milliner millin
+ million million
+ millioned million
+ millions million
+ mills mill
+ millstones millston
+ milo milo
+ mimic mimic
+ minc minc
+ mince minc
+ minces minc
+ mincing minc
+ mind mind
+ minded mind
+ minding mind
+ mindless mindless
+ minds mind
+ mine mine
+ mineral miner
+ minerals miner
+ minerva minerva
+ mines mine
+ mingle mingl
+ mingled mingl
+ mingling mingl
+ minikin minikin
+ minim minim
+ minime minim
+ minimo minimo
+ minimus minimu
+ mining mine
+ minion minion
+ minions minion
+ minist minist
+ minister minist
+ ministers minist
+ ministration ministr
+ minnow minnow
+ minnows minnow
+ minola minola
+ minority minor
+ minos mino
+ minotaurs minotaur
+ minstrel minstrel
+ minstrels minstrel
+ minstrelsy minstrelsi
+ mint mint
+ mints mint
+ minute minut
+ minutely minut
+ minutes minut
+ minx minx
+ mio mio
+ mir mir
+ mirable mirabl
+ miracle miracl
+ miracles miracl
+ miraculous miracul
+ miranda miranda
+ mire mire
+ mirror mirror
+ mirrors mirror
+ mirth mirth
+ mirthful mirth
+ miry miri
+ mis mi
+ misadventur misadventur
+ misadventure misadventur
+ misanthropos misanthropo
+ misapplied misappli
+ misbecame misbecam
+ misbecom misbecom
+ misbecome misbecom
+ misbegot misbegot
+ misbegotten misbegotten
+ misbeliever misbeliev
+ misbelieving misbeliev
+ misbhav misbhav
+ miscall miscal
+ miscalled miscal
+ miscarried miscarri
+ miscarries miscarri
+ miscarry miscarri
+ miscarrying miscarri
+ mischance mischanc
+ mischances mischanc
+ mischief mischief
+ mischiefs mischief
+ mischievous mischiev
+ misconceived misconceiv
+ misconst misconst
+ misconster misconst
+ misconstruction misconstruct
+ misconstrued misconstru
+ misconstrues misconstru
+ miscreant miscreant
+ miscreate miscreat
+ misdeed misde
+ misdeeds misde
+ misdemean misdemean
+ misdemeanours misdemeanour
+ misdoubt misdoubt
+ misdoubteth misdoubteth
+ misdoubts misdoubt
+ misenum misenum
+ miser miser
+ miserable miser
+ miserably miser
+ misericorde misericord
+ miseries miseri
+ misers miser
+ misery miseri
+ misfortune misfortun
+ misfortunes misfortun
+ misgive misgiv
+ misgives misgiv
+ misgiving misgiv
+ misgoverned misgovern
+ misgovernment misgovern
+ misgraffed misgraf
+ misguide misguid
+ mishap mishap
+ mishaps mishap
+ misheard misheard
+ misinterpret misinterpret
+ mislead mislead
+ misleader mislead
+ misleaders mislead
+ misleading mislead
+ misled misl
+ mislike mislik
+ misord misord
+ misplac misplac
+ misplaced misplac
+ misplaces misplac
+ mispris mispri
+ misprised mispris
+ misprision mispris
+ misprizing mispriz
+ misproud misproud
+ misquote misquot
+ misreport misreport
+ miss miss
+ missed miss
+ misses miss
+ misshap misshap
+ misshapen misshapen
+ missheathed missheath
+ missing miss
+ missingly missingli
+ missions mission
+ missive missiv
+ missives missiv
+ misspoke misspok
+ mist mist
+ mista mista
+ mistak mistak
+ mistake mistak
+ mistaken mistaken
+ mistakes mistak
+ mistaketh mistaketh
+ mistaking mistak
+ mistakings mistak
+ mistemp mistemp
+ mistempered mistemp
+ misterm misterm
+ mistful mist
+ misthink misthink
+ misthought misthought
+ mistletoe mistleto
+ mistook mistook
+ mistreadings mistread
+ mistress mistress
+ mistresses mistress
+ mistresss mistresss
+ mistriship mistriship
+ mistrust mistrust
+ mistrusted mistrust
+ mistrustful mistrust
+ mistrusting mistrust
+ mists mist
+ misty misti
+ misus misu
+ misuse misus
+ misused misus
+ misuses misus
+ mites mite
+ mithridates mithrid
+ mitigate mitig
+ mitigation mitig
+ mix mix
+ mixed mix
+ mixture mixtur
+ mixtures mixtur
+ mm mm
+ mnd mnd
+ moan moan
+ moans moan
+ moat moat
+ moated moat
+ mobled mobl
+ mock mock
+ mockable mockabl
+ mocker mocker
+ mockeries mockeri
+ mockers mocker
+ mockery mockeri
+ mocking mock
+ mocks mock
+ mockvater mockvat
+ mockwater mockwat
+ model model
+ modena modena
+ moderate moder
+ moderately moder
+ moderation moder
+ modern modern
+ modest modest
+ modesties modesti
+ modestly modestli
+ modesty modesti
+ modicums modicum
+ modo modo
+ module modul
+ moe moe
+ moi moi
+ moiety moieti
+ moist moist
+ moisten moisten
+ moisture moistur
+ moldwarp moldwarp
+ mole mole
+ molehill molehil
+ moles mole
+ molest molest
+ molestation molest
+ mollification mollif
+ mollis molli
+ molten molten
+ molto molto
+ mome mome
+ moment moment
+ momentary momentari
+ moming mome
+ mon mon
+ monachum monachum
+ monarch monarch
+ monarchies monarchi
+ monarchize monarch
+ monarcho monarcho
+ monarchs monarch
+ monarchy monarchi
+ monast monast
+ monastery monasteri
+ monastic monast
+ monday mondai
+ monde mond
+ money monei
+ moneys monei
+ mong mong
+ monger monger
+ mongers monger
+ monging mong
+ mongrel mongrel
+ mongrels mongrel
+ mongst mongst
+ monk monk
+ monkey monkei
+ monkeys monkei
+ monks monk
+ monmouth monmouth
+ monopoly monopoli
+ mons mon
+ monsieur monsieur
+ monsieurs monsieur
+ monster monster
+ monsters monster
+ monstrous monstrou
+ monstrously monstrous
+ monstrousness monstrous
+ monstruosity monstruos
+ montacute montacut
+ montage montag
+ montague montagu
+ montagues montagu
+ montano montano
+ montant montant
+ montez montez
+ montferrat montferrat
+ montgomery montgomeri
+ month month
+ monthly monthli
+ months month
+ montjoy montjoi
+ monument monument
+ monumental monument
+ monuments monument
+ mood mood
+ moods mood
+ moody moodi
+ moon moon
+ moonbeams moonbeam
+ moonish moonish
+ moonlight moonlight
+ moons moon
+ moonshine moonshin
+ moonshines moonshin
+ moor moor
+ moorfields moorfield
+ moors moor
+ moorship moorship
+ mop mop
+ mope mope
+ moping mope
+ mopping mop
+ mopsa mopsa
+ moral moral
+ moraler moral
+ morality moral
+ moralize moral
+ mordake mordak
+ more more
+ moreover moreov
+ mores more
+ morgan morgan
+ mori mori
+ morisco morisco
+ morn morn
+ morning morn
+ mornings morn
+ morocco morocco
+ morris morri
+ morrow morrow
+ morrows morrow
+ morsel morsel
+ morsels morsel
+ mort mort
+ mortal mortal
+ mortality mortal
+ mortally mortal
+ mortals mortal
+ mortar mortar
+ mortgaged mortgag
+ mortified mortifi
+ mortifying mortifi
+ mortimer mortim
+ mortimers mortim
+ mortis morti
+ mortise mortis
+ morton morton
+ mose mose
+ moss moss
+ mossgrown mossgrown
+ most most
+ mote mote
+ moth moth
+ mother mother
+ mothers mother
+ moths moth
+ motion motion
+ motionless motionless
+ motions motion
+ motive motiv
+ motives motiv
+ motley motlei
+ mots mot
+ mought mought
+ mould mould
+ moulded mould
+ mouldeth mouldeth
+ moulds mould
+ mouldy mouldi
+ moult moult
+ moulten moulten
+ mounch mounch
+ mounseur mounseur
+ mounsieur mounsieur
+ mount mount
+ mountain mountain
+ mountaineer mountain
+ mountaineers mountain
+ mountainous mountain
+ mountains mountain
+ mountant mountant
+ mountanto mountanto
+ mountebank mountebank
+ mountebanks mountebank
+ mounted mount
+ mounteth mounteth
+ mounting mount
+ mounts mount
+ mourn mourn
+ mourned mourn
+ mourner mourner
+ mourners mourner
+ mournful mourn
+ mournfully mournfulli
+ mourning mourn
+ mourningly mourningli
+ mournings mourn
+ mourns mourn
+ mous mou
+ mouse mous
+ mousetrap mousetrap
+ mousing mous
+ mouth mouth
+ mouthed mouth
+ mouths mouth
+ mov mov
+ movables movabl
+ move move
+ moveable moveabl
+ moveables moveabl
+ moved move
+ mover mover
+ movers mover
+ moves move
+ moveth moveth
+ moving move
+ movingly movingli
+ movousus movousu
+ mow mow
+ mowbray mowbrai
+ mower mower
+ mowing mow
+ mows mow
+ moy moi
+ moys moi
+ moyses moys
+ mrs mr
+ much much
+ muck muck
+ mud mud
+ mudded mud
+ muddied muddi
+ muddy muddi
+ muffins muffin
+ muffl muffl
+ muffle muffl
+ muffled muffl
+ muffler muffler
+ muffling muffl
+ mugger mugger
+ mugs mug
+ mulberries mulberri
+ mulberry mulberri
+ mule mule
+ mules mule
+ muleteers mulet
+ mulier mulier
+ mulieres mulier
+ muliteus muliteu
+ mull mull
+ mulmutius mulmutiu
+ multiplied multipli
+ multiply multipli
+ multiplying multipli
+ multipotent multipot
+ multitude multitud
+ multitudes multitud
+ multitudinous multitudin
+ mum mum
+ mumble mumbl
+ mumbling mumbl
+ mummers mummer
+ mummy mummi
+ mun mun
+ munch munch
+ muniments muniment
+ munition munit
+ murd murd
+ murder murder
+ murdered murder
+ murderer murder
+ murderers murder
+ murdering murder
+ murderous murder
+ murders murder
+ mure mure
+ murk murk
+ murkiest murkiest
+ murky murki
+ murmur murmur
+ murmurers murmur
+ murmuring murmur
+ murrain murrain
+ murray murrai
+ murrion murrion
+ murther murther
+ murtherer murther
+ murtherers murther
+ murthering murther
+ murtherous murther
+ murthers murther
+ mus mu
+ muscadel muscadel
+ muscovites muscovit
+ muscovits muscovit
+ muscovy muscovi
+ muse muse
+ muses muse
+ mush mush
+ mushrooms mushroom
+ music music
+ musical music
+ musician musician
+ musicians musician
+ musics music
+ musing muse
+ musings muse
+ musk musk
+ musket musket
+ muskets musket
+ muskos musko
+ muss muss
+ mussel mussel
+ mussels mussel
+ must must
+ mustachio mustachio
+ mustard mustard
+ mustardseed mustardse
+ muster muster
+ mustering muster
+ musters muster
+ musty musti
+ mutability mutabl
+ mutable mutabl
+ mutation mutat
+ mutations mutat
+ mute mute
+ mutes mute
+ mutest mutest
+ mutine mutin
+ mutineer mutin
+ mutineers mutin
+ mutines mutin
+ mutinies mutini
+ mutinous mutin
+ mutiny mutini
+ mutius mutiu
+ mutter mutter
+ muttered mutter
+ mutton mutton
+ muttons mutton
+ mutual mutual
+ mutualities mutual
+ mutually mutual
+ muzzl muzzl
+ muzzle muzzl
+ muzzled muzzl
+ mv mv
+ mww mww
+ my my
+ mynheers mynheer
+ myrmidon myrmidon
+ myrmidons myrmidon
+ myrtle myrtl
+ myself myself
+ myst myst
+ mysteries mysteri
+ mystery mysteri
+ n n
+ nag nag
+ nage nage
+ nags nag
+ naiads naiad
+ nail nail
+ nails nail
+ nak nak
+ naked nake
+ nakedness naked
+ nal nal
+ nam nam
+ name name
+ named name
+ nameless nameless
+ namely name
+ names name
+ namest namest
+ naming name
+ nan nan
+ nance nanc
+ nap nap
+ nape nape
+ napes nape
+ napkin napkin
+ napkins napkin
+ naples napl
+ napless napless
+ napping nap
+ naps nap
+ narbon narbon
+ narcissus narcissu
+ narines narin
+ narrow narrow
+ narrowly narrowli
+ naso naso
+ nasty nasti
+ nathaniel nathaniel
+ natifs natif
+ nation nation
+ nations nation
+ native nativ
+ nativity nativ
+ natur natur
+ natural natur
+ naturalize natur
+ naturally natur
+ nature natur
+ natured natur
+ natures natur
+ natus natu
+ naught naught
+ naughtily naughtili
+ naughty naughti
+ navarre navarr
+ nave nave
+ navel navel
+ navigation navig
+ navy navi
+ nay nai
+ nayward nayward
+ nayword nayword
+ nazarite nazarit
+ ne ne
+ neaf neaf
+ neamnoins neamnoin
+ neanmoins neanmoin
+ neapolitan neapolitan
+ neapolitans neapolitan
+ near near
+ nearer nearer
+ nearest nearest
+ nearly nearli
+ nearness near
+ neat neat
+ neatly neatli
+ neb neb
+ nebour nebour
+ nebuchadnezzar nebuchadnezzar
+ nec nec
+ necessaries necessari
+ necessarily necessarili
+ necessary necessari
+ necessitied necess
+ necessities necess
+ necessity necess
+ neck neck
+ necklace necklac
+ necks neck
+ nectar nectar
+ ned ned
+ nedar nedar
+ need need
+ needed need
+ needer needer
+ needful need
+ needfull needful
+ needing need
+ needle needl
+ needles needl
+ needless needless
+ needly needli
+ needs need
+ needy needi
+ neer neer
+ neeze neez
+ nefas nefa
+ negation negat
+ negative neg
+ negatives neg
+ neglect neglect
+ neglected neglect
+ neglecting neglect
+ neglectingly neglectingli
+ neglection neglect
+ negligence neglig
+ negligent neglig
+ negotiate negoti
+ negotiations negoti
+ negro negro
+ neigh neigh
+ neighbors neighbor
+ neighbour neighbour
+ neighbourhood neighbourhood
+ neighbouring neighbour
+ neighbourly neighbourli
+ neighbours neighbour
+ neighing neigh
+ neighs neigh
+ neither neither
+ nell nell
+ nemean nemean
+ nemesis nemesi
+ neoptolemus neoptolemu
+ nephew nephew
+ nephews nephew
+ neptune neptun
+ ner ner
+ nereides nereid
+ nerissa nerissa
+ nero nero
+ neroes nero
+ ners ner
+ nerve nerv
+ nerves nerv
+ nervii nervii
+ nervy nervi
+ nessus nessu
+ nest nest
+ nestor nestor
+ nests nest
+ net net
+ nether nether
+ netherlands netherland
+ nets net
+ nettle nettl
+ nettled nettl
+ nettles nettl
+ neuter neuter
+ neutral neutral
+ nev nev
+ never never
+ nevil nevil
+ nevils nevil
+ new new
+ newborn newborn
+ newer newer
+ newest newest
+ newgate newgat
+ newly newli
+ newness new
+ news new
+ newsmongers newsmong
+ newt newt
+ newts newt
+ next next
+ nibbling nibbl
+ nicanor nicanor
+ nice nice
+ nicely nice
+ niceness nice
+ nicer nicer
+ nicety niceti
+ nicholas nichola
+ nick nick
+ nickname nicknam
+ nicks nick
+ niece niec
+ nieces niec
+ niggard niggard
+ niggarding niggard
+ niggardly niggardli
+ nigh nigh
+ night night
+ nightcap nightcap
+ nightcaps nightcap
+ nighted night
+ nightgown nightgown
+ nightingale nightingal
+ nightingales nightingal
+ nightly nightli
+ nightmare nightmar
+ nights night
+ nightwork nightwork
+ nihil nihil
+ nile nile
+ nill nill
+ nilus nilu
+ nimble nimbl
+ nimbleness nimbl
+ nimbler nimbler
+ nimbly nimbl
+ nine nine
+ nineteen nineteen
+ ning ning
+ ningly ningli
+ ninny ninni
+ ninth ninth
+ ninus ninu
+ niobe niob
+ niobes niob
+ nip nip
+ nipp nipp
+ nipping nip
+ nipple nippl
+ nips nip
+ nit nit
+ nly nly
+ nnight nnight
+ nnights nnight
+ no no
+ noah noah
+ nob nob
+ nobility nobil
+ nobis nobi
+ noble nobl
+ nobleman nobleman
+ noblemen noblemen
+ nobleness nobl
+ nobler nobler
+ nobles nobl
+ noblesse nobless
+ noblest noblest
+ nobly nobli
+ nobody nobodi
+ noces noce
+ nod nod
+ nodded nod
+ nodding nod
+ noddle noddl
+ noddles noddl
+ noddy noddi
+ nods nod
+ noes noe
+ nointed noint
+ nois noi
+ noise nois
+ noiseless noiseless
+ noisemaker noisemak
+ noises nois
+ noisome noisom
+ nole nole
+ nominate nomin
+ nominated nomin
+ nomination nomin
+ nominativo nominativo
+ non non
+ nonage nonag
+ nonce nonc
+ none none
+ nonino nonino
+ nonny nonni
+ nonpareil nonpareil
+ nonsuits nonsuit
+ nony noni
+ nook nook
+ nooks nook
+ noon noon
+ noonday noondai
+ noontide noontid
+ nor nor
+ norbery norberi
+ norfolk norfolk
+ norman norman
+ normandy normandi
+ normans norman
+ north north
+ northampton northampton
+ northamptonshire northamptonshir
+ northerly northerli
+ northern northern
+ northgate northgat
+ northumberland northumberland
+ northumberlands northumberland
+ northward northward
+ norway norwai
+ norways norwai
+ norwegian norwegian
+ norweyan norweyan
+ nos no
+ nose nose
+ nosegays nosegai
+ noseless noseless
+ noses nose
+ noster noster
+ nostra nostra
+ nostril nostril
+ nostrils nostril
+ not not
+ notable notabl
+ notably notabl
+ notary notari
+ notch notch
+ note note
+ notebook notebook
+ noted note
+ notedly notedli
+ notes note
+ notest notest
+ noteworthy noteworthi
+ nothing noth
+ nothings noth
+ notice notic
+ notify notifi
+ noting note
+ notion notion
+ notorious notori
+ notoriously notori
+ notre notr
+ notwithstanding notwithstand
+ nought nought
+ noun noun
+ nouns noun
+ nourish nourish
+ nourished nourish
+ nourisher nourish
+ nourishes nourish
+ nourisheth nourisheth
+ nourishing nourish
+ nourishment nourish
+ nous nou
+ novel novel
+ novelties novelti
+ novelty novelti
+ noverbs noverb
+ novi novi
+ novice novic
+ novices novic
+ novum novum
+ now now
+ nowhere nowher
+ noyance noyanc
+ ns ns
+ nt nt
+ nubibus nubibu
+ numa numa
+ numb numb
+ number number
+ numbered number
+ numbering number
+ numberless numberless
+ numbers number
+ numbness numb
+ nun nun
+ nuncio nuncio
+ nuncle nuncl
+ nunnery nunneri
+ nuns nun
+ nuntius nuntiu
+ nuptial nuptial
+ nurs nur
+ nurse nurs
+ nursed nurs
+ nurser nurser
+ nursery nurseri
+ nurses nurs
+ nurseth nurseth
+ nursh nursh
+ nursing nurs
+ nurtur nurtur
+ nurture nurtur
+ nut nut
+ nuthook nuthook
+ nutmeg nutmeg
+ nutmegs nutmeg
+ nutriment nutriment
+ nuts nut
+ nutshell nutshel
+ ny ny
+ nym nym
+ nymph nymph
+ nymphs nymph
+ o o
+ oak oak
+ oaken oaken
+ oaks oak
+ oared oar
+ oars oar
+ oatcake oatcak
+ oaten oaten
+ oath oath
+ oathable oathabl
+ oaths oath
+ oats oat
+ ob ob
+ obduracy obduraci
+ obdurate obdur
+ obedience obedi
+ obedient obedi
+ obeisance obeis
+ oberon oberon
+ obey obei
+ obeyed obei
+ obeying obei
+ obeys obei
+ obidicut obidicut
+ object object
+ objected object
+ objections object
+ objects object
+ oblation oblat
+ oblations oblat
+ obligation oblig
+ obligations oblig
+ obliged oblig
+ oblique obliqu
+ oblivion oblivion
+ oblivious oblivi
+ obloquy obloqui
+ obscene obscen
+ obscenely obscen
+ obscur obscur
+ obscure obscur
+ obscured obscur
+ obscurely obscur
+ obscures obscur
+ obscuring obscur
+ obscurity obscur
+ obsequies obsequi
+ obsequious obsequi
+ obsequiously obsequi
+ observ observ
+ observance observ
+ observances observ
+ observancy observ
+ observant observ
+ observants observ
+ observation observ
+ observe observ
+ observed observ
+ observer observ
+ observers observ
+ observing observ
+ observingly observingli
+ obsque obsqu
+ obstacle obstacl
+ obstacles obstacl
+ obstinacy obstinaci
+ obstinate obstin
+ obstinately obstin
+ obstruct obstruct
+ obstruction obstruct
+ obstructions obstruct
+ obtain obtain
+ obtained obtain
+ obtaining obtain
+ occasion occas
+ occasions occas
+ occident occid
+ occidental occident
+ occulted occult
+ occupat occupat
+ occupation occup
+ occupations occup
+ occupied occupi
+ occupies occupi
+ occupy occupi
+ occurrence occurr
+ occurrences occurr
+ occurrents occurr
+ ocean ocean
+ oceans ocean
+ octavia octavia
+ octavius octaviu
+ ocular ocular
+ od od
+ odd odd
+ oddest oddest
+ oddly oddli
+ odds odd
+ ode od
+ odes od
+ odious odiou
+ odoriferous odorifer
+ odorous odor
+ odour odour
+ odours odour
+ ods od
+ oeillades oeillad
+ oes oe
+ oeuvres oeuvr
+ of of
+ ofephesus ofephesu
+ off off
+ offal offal
+ offence offenc
+ offenceful offenc
+ offences offenc
+ offend offend
+ offended offend
+ offendendo offendendo
+ offender offend
+ offenders offend
+ offendeth offendeth
+ offending offend
+ offendress offendress
+ offends offend
+ offense offens
+ offenseless offenseless
+ offenses offens
+ offensive offens
+ offer offer
+ offered offer
+ offering offer
+ offerings offer
+ offers offer
+ offert offert
+ offic offic
+ office offic
+ officed offic
+ officer offic
+ officers offic
+ offices offic
+ official offici
+ officious offici
+ offspring offspr
+ oft oft
+ often often
+ oftener often
+ oftentimes oftentim
+ oh oh
+ oil oil
+ oils oil
+ oily oili
+ old old
+ oldcastle oldcastl
+ olden olden
+ older older
+ oldest oldest
+ oldness old
+ olive oliv
+ oliver oliv
+ olivers oliv
+ olives oliv
+ olivia olivia
+ olympian olympian
+ olympus olympu
+ oman oman
+ omans oman
+ omen omen
+ ominous omin
+ omission omiss
+ omit omit
+ omittance omitt
+ omitted omit
+ omitting omit
+ omne omn
+ omnes omn
+ omnipotent omnipot
+ on on
+ once onc
+ one on
+ ones on
+ oneyers oney
+ ongles ongl
+ onion onion
+ onions onion
+ only onli
+ onset onset
+ onward onward
+ onwards onward
+ oo oo
+ ooze ooz
+ oozes ooz
+ oozy oozi
+ op op
+ opal opal
+ ope op
+ open open
+ opener open
+ opening open
+ openly openli
+ openness open
+ opens open
+ operant oper
+ operate oper
+ operation oper
+ operations oper
+ operative oper
+ opes op
+ oph oph
+ ophelia ophelia
+ opinion opinion
+ opinions opinion
+ opportune opportun
+ opportunities opportun
+ opportunity opportun
+ oppos oppo
+ oppose oppos
+ opposed oppos
+ opposeless opposeless
+ opposer oppos
+ opposers oppos
+ opposes oppos
+ opposing oppos
+ opposite opposit
+ opposites opposit
+ opposition opposit
+ oppositions opposit
+ oppress oppress
+ oppressed oppress
+ oppresses oppress
+ oppresseth oppresseth
+ oppressing oppress
+ oppression oppress
+ oppressor oppressor
+ opprest opprest
+ opprobriously opprobri
+ oppugnancy oppugn
+ opulency opul
+ opulent opul
+ or or
+ oracle oracl
+ oracles oracl
+ orange orang
+ oration orat
+ orator orat
+ orators orat
+ oratory oratori
+ orb orb
+ orbed orb
+ orbs orb
+ orchard orchard
+ orchards orchard
+ ord ord
+ ordain ordain
+ ordained ordain
+ ordaining ordain
+ order order
+ ordered order
+ ordering order
+ orderless orderless
+ orderly orderli
+ orders order
+ ordinance ordin
+ ordinant ordin
+ ordinaries ordinari
+ ordinary ordinari
+ ordnance ordnanc
+ ords ord
+ ordure ordur
+ ore or
+ organ organ
+ organs organ
+ orgillous orgil
+ orient orient
+ orifex orifex
+ origin origin
+ original origin
+ orisons orison
+ ork ork
+ orlando orlando
+ orld orld
+ orleans orlean
+ ornament ornament
+ ornaments ornament
+ orodes orod
+ orphan orphan
+ orphans orphan
+ orpheus orpheu
+ orsino orsino
+ ort ort
+ orthography orthographi
+ orts ort
+ oscorbidulchos oscorbidulcho
+ osier osier
+ osiers osier
+ osprey osprei
+ osr osr
+ osric osric
+ ossa ossa
+ ost ost
+ ostent ostent
+ ostentare ostentar
+ ostentation ostent
+ ostents ostent
+ ostler ostler
+ ostlers ostler
+ ostrich ostrich
+ osw osw
+ oswald oswald
+ othello othello
+ other other
+ othergates otherg
+ others other
+ otherwhere otherwher
+ otherwhiles otherwhil
+ otherwise otherwis
+ otter otter
+ ottoman ottoman
+ ottomites ottomit
+ oublie oubli
+ ouches ouch
+ ought ought
+ oui oui
+ ounce ounc
+ ounces ounc
+ ouphes ouph
+ our our
+ ours our
+ ourself ourself
+ ourselves ourselv
+ ousel ousel
+ out out
+ outbids outbid
+ outbrave outbrav
+ outbraves outbrav
+ outbreak outbreak
+ outcast outcast
+ outcries outcri
+ outcry outcri
+ outdar outdar
+ outdare outdar
+ outdares outdar
+ outdone outdon
+ outfac outfac
+ outface outfac
+ outfaced outfac
+ outfacing outfac
+ outfly outfli
+ outfrown outfrown
+ outgo outgo
+ outgoes outgo
+ outgrown outgrown
+ outjest outjest
+ outlaw outlaw
+ outlawry outlawri
+ outlaws outlaw
+ outliv outliv
+ outlive outliv
+ outlives outliv
+ outliving outliv
+ outlook outlook
+ outlustres outlustr
+ outpriz outpriz
+ outrage outrag
+ outrageous outrag
+ outrages outrag
+ outran outran
+ outright outright
+ outroar outroar
+ outrun outrun
+ outrunning outrun
+ outruns outrun
+ outscold outscold
+ outscorn outscorn
+ outsell outsel
+ outsells outsel
+ outside outsid
+ outsides outsid
+ outspeaks outspeak
+ outsport outsport
+ outstare outstar
+ outstay outstai
+ outstood outstood
+ outstretch outstretch
+ outstretched outstretch
+ outstrike outstrik
+ outstrip outstrip
+ outstripped outstrip
+ outswear outswear
+ outvenoms outvenom
+ outward outward
+ outwardly outwardli
+ outwards outward
+ outwear outwear
+ outweighs outweigh
+ outwent outwent
+ outworn outworn
+ outworths outworth
+ oven oven
+ over over
+ overawe overaw
+ overbear overbear
+ overblown overblown
+ overboard overboard
+ overbold overbold
+ overborne overborn
+ overbulk overbulk
+ overbuys overbui
+ overcame overcam
+ overcast overcast
+ overcharg overcharg
+ overcharged overcharg
+ overcome overcom
+ overcomes overcom
+ overdone overdon
+ overearnest overearnest
+ overfar overfar
+ overflow overflow
+ overflown overflown
+ overglance overgl
+ overgo overgo
+ overgone overgon
+ overgorg overgorg
+ overgrown overgrown
+ overhead overhead
+ overhear overhear
+ overheard overheard
+ overhold overhold
+ overjoyed overjoi
+ overkind overkind
+ overland overland
+ overleather overleath
+ overlive overl
+ overlook overlook
+ overlooking overlook
+ overlooks overlook
+ overmaster overmast
+ overmounting overmount
+ overmuch overmuch
+ overpass overpass
+ overpeer overp
+ overpeering overp
+ overplus overplu
+ overrul overrul
+ overrun overrun
+ overscutch overscutch
+ overset overset
+ overshades overshad
+ overshine overshin
+ overshines overshin
+ overshot overshot
+ oversights oversight
+ overspread overspread
+ overstain overstain
+ overswear overswear
+ overt overt
+ overta overta
+ overtake overtak
+ overtaketh overtaketh
+ overthrow overthrow
+ overthrown overthrown
+ overthrows overthrow
+ overtook overtook
+ overtopp overtopp
+ overture overtur
+ overturn overturn
+ overwatch overwatch
+ overween overween
+ overweening overween
+ overweigh overweigh
+ overwhelm overwhelm
+ overwhelming overwhelm
+ overworn overworn
+ ovid ovid
+ ovidius ovidiu
+ ow ow
+ owe ow
+ owed ow
+ owedst owedst
+ owen owen
+ owes ow
+ owest owest
+ oweth oweth
+ owing ow
+ owl owl
+ owls owl
+ own own
+ owner owner
+ owners owner
+ owning own
+ owns own
+ owy owi
+ ox ox
+ oxen oxen
+ oxford oxford
+ oxfordshire oxfordshir
+ oxlips oxlip
+ oyes oy
+ oyster oyster
+ p p
+ pabble pabbl
+ pabylon pabylon
+ pac pac
+ pace pace
+ paced pace
+ paces pace
+ pacified pacifi
+ pacify pacifi
+ pacing pace
+ pack pack
+ packet packet
+ packets packet
+ packhorses packhors
+ packing pack
+ packings pack
+ packs pack
+ packthread packthread
+ pacorus pacoru
+ paction paction
+ pad pad
+ paddle paddl
+ paddling paddl
+ paddock paddock
+ padua padua
+ pagan pagan
+ pagans pagan
+ page page
+ pageant pageant
+ pageants pageant
+ pages page
+ pah pah
+ paid paid
+ pail pail
+ pailfuls pail
+ pails pail
+ pain pain
+ pained pain
+ painful pain
+ painfully painfulli
+ pains pain
+ paint paint
+ painted paint
+ painter painter
+ painting paint
+ paintings paint
+ paints paint
+ pair pair
+ paired pair
+ pairs pair
+ pajock pajock
+ pal pal
+ palabras palabra
+ palace palac
+ palaces palac
+ palamedes palamed
+ palate palat
+ palates palat
+ palatine palatin
+ palating palat
+ pale pale
+ paled pale
+ paleness pale
+ paler paler
+ pales pale
+ palestine palestin
+ palfrey palfrei
+ palfreys palfrei
+ palisadoes palisado
+ pall pall
+ pallabris pallabri
+ pallas palla
+ pallets pallet
+ palm palm
+ palmer palmer
+ palmers palmer
+ palms palm
+ palmy palmi
+ palpable palpabl
+ palsied palsi
+ palsies palsi
+ palsy palsi
+ palt palt
+ palter palter
+ paltry paltri
+ paly pali
+ pamp pamp
+ pamper pamper
+ pamphlets pamphlet
+ pan pan
+ pancackes pancack
+ pancake pancak
+ pancakes pancak
+ pandar pandar
+ pandars pandar
+ pandarus pandaru
+ pander pander
+ panderly panderli
+ panders pander
+ pandulph pandulph
+ panel panel
+ pang pang
+ panging pang
+ pangs pang
+ pannier pannier
+ pannonians pannonian
+ pansa pansa
+ pansies pansi
+ pant pant
+ pantaloon pantaloon
+ panted pant
+ pantheon pantheon
+ panther panther
+ panthino panthino
+ panting pant
+ pantingly pantingli
+ pantler pantler
+ pantry pantri
+ pants pant
+ pap pap
+ papal papal
+ paper paper
+ papers paper
+ paphlagonia paphlagonia
+ paphos papho
+ papist papist
+ paps pap
+ par par
+ parable parabl
+ paracelsus paracelsu
+ paradise paradis
+ paradox paradox
+ paradoxes paradox
+ paragon paragon
+ paragons paragon
+ parallel parallel
+ parallels parallel
+ paramour paramour
+ paramours paramour
+ parapets parapet
+ paraquito paraquito
+ parasite parasit
+ parasites parasit
+ parca parca
+ parcel parcel
+ parcell parcel
+ parcels parcel
+ parch parch
+ parched parch
+ parching parch
+ parchment parchment
+ pard pard
+ pardon pardon
+ pardona pardona
+ pardoned pardon
+ pardoner pardon
+ pardoning pardon
+ pardonne pardonn
+ pardonner pardonn
+ pardonnez pardonnez
+ pardons pardon
+ pare pare
+ pared pare
+ parel parel
+ parent parent
+ parentage parentag
+ parents parent
+ parfect parfect
+ paring pare
+ parings pare
+ paris pari
+ parish parish
+ parishioners parishion
+ parisians parisian
+ paritors paritor
+ park park
+ parks park
+ parle parl
+ parler parler
+ parles parl
+ parley parlei
+ parlez parlez
+ parliament parliament
+ parlors parlor
+ parlour parlour
+ parlous parlou
+ parmacity parmac
+ parolles parol
+ parricide parricid
+ parricides parricid
+ parrot parrot
+ parrots parrot
+ parsley parslei
+ parson parson
+ part part
+ partake partak
+ partaken partaken
+ partaker partak
+ partakers partak
+ parted part
+ parthia parthia
+ parthian parthian
+ parthians parthian
+ parti parti
+ partial partial
+ partialize partial
+ partially partial
+ participate particip
+ participation particip
+ particle particl
+ particular particular
+ particularities particular
+ particularize particular
+ particularly particularli
+ particulars particular
+ parties parti
+ parting part
+ partisan partisan
+ partisans partisan
+ partition partit
+ partizan partizan
+ partlet partlet
+ partly partli
+ partner partner
+ partners partner
+ partridge partridg
+ parts part
+ party parti
+ pas pa
+ pash pash
+ pashed pash
+ pashful pash
+ pass pass
+ passable passabl
+ passado passado
+ passage passag
+ passages passag
+ passant passant
+ passed pass
+ passenger passeng
+ passengers passeng
+ passes pass
+ passeth passeth
+ passing pass
+ passio passio
+ passion passion
+ passionate passion
+ passioning passion
+ passions passion
+ passive passiv
+ passport passport
+ passy passi
+ past past
+ paste past
+ pasterns pastern
+ pasties pasti
+ pastime pastim
+ pastimes pastim
+ pastoral pastor
+ pastorals pastor
+ pastors pastor
+ pastry pastri
+ pasture pastur
+ pastures pastur
+ pasty pasti
+ pat pat
+ patay patai
+ patch patch
+ patchery patcheri
+ patches patch
+ pate pate
+ pated pate
+ patent patent
+ patents patent
+ paternal patern
+ pates pate
+ path path
+ pathetical pathet
+ paths path
+ pathway pathwai
+ pathways pathwai
+ patience patienc
+ patient patient
+ patiently patient
+ patients patient
+ patines patin
+ patrician patrician
+ patricians patrician
+ patrick patrick
+ patrimony patrimoni
+ patroclus patroclu
+ patron patron
+ patronage patronag
+ patroness patro
+ patrons patron
+ patrum patrum
+ patter patter
+ pattern pattern
+ patterns pattern
+ pattle pattl
+ pauca pauca
+ paucas pauca
+ paul paul
+ paulina paulina
+ paunch paunch
+ paunches paunch
+ pause paus
+ pauser pauser
+ pauses paus
+ pausingly pausingli
+ pauvres pauvr
+ pav pav
+ paved pave
+ pavement pavement
+ pavilion pavilion
+ pavilions pavilion
+ pavin pavin
+ paw paw
+ pawn pawn
+ pawns pawn
+ paws paw
+ pax pax
+ pay pai
+ payest payest
+ paying pai
+ payment payment
+ payments payment
+ pays pai
+ paysan paysan
+ paysans paysan
+ pe pe
+ peace peac
+ peaceable peaceabl
+ peaceably peaceabl
+ peaceful peac
+ peacemakers peacemak
+ peaces peac
+ peach peach
+ peaches peach
+ peacock peacock
+ peacocks peacock
+ peak peak
+ peaking peak
+ peal peal
+ peals peal
+ pear pear
+ peard peard
+ pearl pearl
+ pearls pearl
+ pears pear
+ peas pea
+ peasant peasant
+ peasantry peasantri
+ peasants peasant
+ peascod peascod
+ pease peas
+ peaseblossom peaseblossom
+ peat peat
+ peaten peaten
+ peating peat
+ pebble pebbl
+ pebbled pebbl
+ pebbles pebbl
+ peck peck
+ pecks peck
+ peculiar peculiar
+ pecus pecu
+ pedant pedant
+ pedantical pedant
+ pedascule pedascul
+ pede pede
+ pedestal pedest
+ pedigree pedigre
+ pedlar pedlar
+ pedlars pedlar
+ pedro pedro
+ peds ped
+ peel peel
+ peep peep
+ peeped peep
+ peeping peep
+ peeps peep
+ peer peer
+ peereth peereth
+ peering peer
+ peerless peerless
+ peers peer
+ peesel peesel
+ peevish peevish
+ peevishly peevishli
+ peflur peflur
+ peg peg
+ pegasus pegasu
+ pegs peg
+ peise peis
+ peised peis
+ peize peiz
+ pelf pelf
+ pelican pelican
+ pelion pelion
+ pell pell
+ pella pella
+ pelleted pellet
+ peloponnesus peloponnesu
+ pelt pelt
+ pelting pelt
+ pembroke pembrok
+ pen pen
+ penalties penalti
+ penalty penalti
+ penance penanc
+ pence penc
+ pencil pencil
+ pencill pencil
+ pencils pencil
+ pendant pendant
+ pendent pendent
+ pendragon pendragon
+ pendulous pendul
+ penelope penelop
+ penetrable penetr
+ penetrate penetr
+ penetrative penetr
+ penitence penit
+ penitent penit
+ penitential penitenti
+ penitently penit
+ penitents penit
+ penker penker
+ penknife penknif
+ penn penn
+ penned pen
+ penning pen
+ pennons pennon
+ penny penni
+ pennyworth pennyworth
+ pennyworths pennyworth
+ pens pen
+ pense pens
+ pension pension
+ pensioners pension
+ pensive pensiv
+ pensived pensiv
+ pensively pensiv
+ pent pent
+ pentecost pentecost
+ penthesilea penthesilea
+ penthouse penthous
+ penurious penuri
+ penury penuri
+ peopl peopl
+ people peopl
+ peopled peopl
+ peoples peopl
+ pepin pepin
+ pepper pepper
+ peppercorn peppercorn
+ peppered pepper
+ per per
+ peradventure peradventur
+ peradventures peradventur
+ perceiv perceiv
+ perceive perceiv
+ perceived perceiv
+ perceives perceiv
+ perceiveth perceiveth
+ perch perch
+ perchance perchanc
+ percies perci
+ percussion percuss
+ percy perci
+ perdie perdi
+ perdita perdita
+ perdition perdit
+ perdonato perdonato
+ perdu perdu
+ perdurable perdur
+ perdurably perdur
+ perdy perdi
+ pere pere
+ peregrinate peregrin
+ peremptorily peremptorili
+ peremptory peremptori
+ perfect perfect
+ perfected perfect
+ perfecter perfect
+ perfectest perfectest
+ perfection perfect
+ perfections perfect
+ perfectly perfectli
+ perfectness perfect
+ perfidious perfidi
+ perfidiously perfidi
+ perforce perforc
+ perform perform
+ performance perform
+ performances perform
+ performed perform
+ performer perform
+ performers perform
+ performing perform
+ performs perform
+ perfum perfum
+ perfume perfum
+ perfumed perfum
+ perfumer perfum
+ perfumes perfum
+ perge perg
+ perhaps perhap
+ periapts periapt
+ perigort perigort
+ perigouna perigouna
+ peril peril
+ perilous peril
+ perils peril
+ period period
+ periods period
+ perish perish
+ perished perish
+ perishest perishest
+ perisheth perisheth
+ perishing perish
+ periwig periwig
+ perjur perjur
+ perjure perjur
+ perjured perjur
+ perjuries perjuri
+ perjury perjuri
+ perk perk
+ perkes perk
+ permafoy permafoi
+ permanent perman
+ permission permiss
+ permissive permiss
+ permit permit
+ permitted permit
+ pernicious pernici
+ perniciously pernici
+ peroration peror
+ perpend perpend
+ perpendicular perpendicular
+ perpendicularly perpendicularli
+ perpetual perpetu
+ perpetually perpetu
+ perpetuity perpetu
+ perplex perplex
+ perplexed perplex
+ perplexity perplex
+ pers per
+ persecuted persecut
+ persecutions persecut
+ persecutor persecutor
+ perseus perseu
+ persever persev
+ perseverance persever
+ persevers persev
+ persia persia
+ persian persian
+ persist persist
+ persisted persist
+ persistency persist
+ persistive persist
+ persists persist
+ person person
+ personae persona
+ personage personag
+ personages personag
+ personal person
+ personally person
+ personate person
+ personated person
+ personates person
+ personating person
+ persons person
+ perspective perspect
+ perspectively perspect
+ perspectives perspect
+ perspicuous perspicu
+ persuade persuad
+ persuaded persuad
+ persuades persuad
+ persuading persuad
+ persuasion persuas
+ persuasions persuas
+ pert pert
+ pertain pertain
+ pertaining pertain
+ pertains pertain
+ pertaunt pertaunt
+ pertinent pertin
+ pertly pertli
+ perturb perturb
+ perturbation perturb
+ perturbations perturb
+ perturbed perturb
+ perus peru
+ perusal perus
+ peruse perus
+ perused perus
+ perusing perus
+ perverse pervers
+ perversely pervers
+ perverseness pervers
+ pervert pervert
+ perverted pervert
+ peseech peseech
+ pest pest
+ pester pester
+ pestiferous pestifer
+ pestilence pestil
+ pestilent pestil
+ pet pet
+ petar petar
+ peter peter
+ petit petit
+ petition petit
+ petitionary petitionari
+ petitioner petition
+ petitioners petition
+ petitions petit
+ peto peto
+ petrarch petrarch
+ petruchio petruchio
+ petter petter
+ petticoat petticoat
+ petticoats petticoat
+ pettiness petti
+ pettish pettish
+ pettitoes pettito
+ petty petti
+ peu peu
+ pew pew
+ pewter pewter
+ pewterer pewter
+ phaethon phaethon
+ phaeton phaeton
+ phantasime phantasim
+ phantasimes phantasim
+ phantasma phantasma
+ pharamond pharamond
+ pharaoh pharaoh
+ pharsalia pharsalia
+ pheasant pheasant
+ pheazar pheazar
+ phebe phebe
+ phebes phebe
+ pheebus pheebu
+ pheeze pheez
+ phibbus phibbu
+ philadelphos philadelpho
+ philario philario
+ philarmonus philarmonu
+ philemon philemon
+ philip philip
+ philippan philippan
+ philippe philipp
+ philippi philippi
+ phillida phillida
+ philo philo
+ philomel philomel
+ philomela philomela
+ philosopher philosoph
+ philosophers philosoph
+ philosophical philosoph
+ philosophy philosophi
+ philostrate philostr
+ philotus philotu
+ phlegmatic phlegmat
+ phoebe phoeb
+ phoebus phoebu
+ phoenicia phoenicia
+ phoenicians phoenician
+ phoenix phoenix
+ phorbus phorbu
+ photinus photinu
+ phrase phrase
+ phraseless phraseless
+ phrases phrase
+ phrygia phrygia
+ phrygian phrygian
+ phrynia phrynia
+ physic physic
+ physical physic
+ physician physician
+ physicians physician
+ physics physic
+ pia pia
+ pibble pibbl
+ pible pibl
+ picardy picardi
+ pick pick
+ pickaxe pickax
+ pickaxes pickax
+ pickbone pickbon
+ picked pick
+ pickers picker
+ picking pick
+ pickle pickl
+ picklock picklock
+ pickpurse pickpurs
+ picks pick
+ pickt pickt
+ pickthanks pickthank
+ pictur pictur
+ picture pictur
+ pictured pictur
+ pictures pictur
+ pid pid
+ pie pie
+ piec piec
+ piece piec
+ pieces piec
+ piecing piec
+ pied pi
+ piedness pied
+ pier pier
+ pierc pierc
+ pierce pierc
+ pierced pierc
+ pierces pierc
+ pierceth pierceth
+ piercing pierc
+ piercy pierci
+ piers pier
+ pies pi
+ piety pieti
+ pig pig
+ pigeon pigeon
+ pigeons pigeon
+ pight pight
+ pigmy pigmi
+ pigrogromitus pigrogromitu
+ pike pike
+ pikes pike
+ pil pil
+ pilate pilat
+ pilates pilat
+ pilchers pilcher
+ pile pile
+ piles pile
+ pilf pilf
+ pilfering pilfer
+ pilgrim pilgrim
+ pilgrimage pilgrimag
+ pilgrims pilgrim
+ pill pill
+ pillage pillag
+ pillagers pillag
+ pillar pillar
+ pillars pillar
+ pillicock pillicock
+ pillory pillori
+ pillow pillow
+ pillows pillow
+ pills pill
+ pilot pilot
+ pilots pilot
+ pimpernell pimpernel
+ pin pin
+ pinch pinch
+ pinched pinch
+ pinches pinch
+ pinching pinch
+ pindarus pindaru
+ pine pine
+ pined pine
+ pines pine
+ pinfold pinfold
+ pining pine
+ pinion pinion
+ pink pink
+ pinn pinn
+ pinnace pinnac
+ pins pin
+ pinse pins
+ pint pint
+ pintpot pintpot
+ pioned pion
+ pioneers pioneer
+ pioner pioner
+ pioners pioner
+ pious piou
+ pip pip
+ pipe pipe
+ piper piper
+ pipers piper
+ pipes pipe
+ piping pipe
+ pippin pippin
+ pippins pippin
+ pirate pirat
+ pirates pirat
+ pisa pisa
+ pisanio pisanio
+ pish pish
+ pismires pismir
+ piss piss
+ pissing piss
+ pistol pistol
+ pistols pistol
+ pit pit
+ pitch pitch
+ pitched pitch
+ pitcher pitcher
+ pitchers pitcher
+ pitchy pitchi
+ piteous piteou
+ piteously piteous
+ pitfall pitfal
+ pith pith
+ pithless pithless
+ pithy pithi
+ pitie piti
+ pitied piti
+ pities piti
+ pitiful piti
+ pitifully pitifulli
+ pitiless pitiless
+ pits pit
+ pittance pittanc
+ pittie pitti
+ pittikins pittikin
+ pity piti
+ pitying piti
+ pius piu
+ plac plac
+ place place
+ placed place
+ placentio placentio
+ places place
+ placeth placeth
+ placid placid
+ placing place
+ plack plack
+ placket placket
+ plackets placket
+ plagu plagu
+ plague plagu
+ plagued plagu
+ plagues plagu
+ plaguing plagu
+ plaguy plagui
+ plain plain
+ plainer plainer
+ plainest plainest
+ plaining plain
+ plainings plain
+ plainly plainli
+ plainness plain
+ plains plain
+ plainsong plainsong
+ plaintful plaint
+ plaintiff plaintiff
+ plaintiffs plaintiff
+ plaints plaint
+ planched planch
+ planet planet
+ planetary planetari
+ planets planet
+ planks plank
+ plant plant
+ plantage plantag
+ plantagenet plantagenet
+ plantagenets plantagenet
+ plantain plantain
+ plantation plantat
+ planted plant
+ planteth planteth
+ plants plant
+ plash plash
+ plashy plashi
+ plast plast
+ plaster plaster
+ plasterer plaster
+ plat plat
+ plate plate
+ plated plate
+ plates plate
+ platform platform
+ platforms platform
+ plats plat
+ platted plat
+ plausible plausibl
+ plausive plausiv
+ plautus plautu
+ play plai
+ played plai
+ player player
+ players player
+ playeth playeth
+ playfellow playfellow
+ playfellows playfellow
+ playhouse playhous
+ playing plai
+ plays plai
+ plea plea
+ pleach pleach
+ pleached pleach
+ plead plead
+ pleaded plead
+ pleader pleader
+ pleaders pleader
+ pleading plead
+ pleads plead
+ pleas plea
+ pleasance pleasanc
+ pleasant pleasant
+ pleasantly pleasantli
+ please pleas
+ pleased pleas
+ pleaser pleaser
+ pleasers pleaser
+ pleases pleas
+ pleasest pleasest
+ pleaseth pleaseth
+ pleasing pleas
+ pleasure pleasur
+ pleasures pleasur
+ plebeians plebeian
+ plebeii plebeii
+ plebs pleb
+ pledge pledg
+ pledges pledg
+ pleines plein
+ plenitude plenitud
+ plenteous plenteou
+ plenteously plenteous
+ plenties plenti
+ plentiful plenti
+ plentifully plentifulli
+ plenty plenti
+ pless pless
+ plessed pless
+ plessing pless
+ pliant pliant
+ plied pli
+ plies pli
+ plight plight
+ plighted plight
+ plighter plighter
+ plod plod
+ plodded plod
+ plodders plodder
+ plodding plod
+ plods plod
+ plood plood
+ ploody ploodi
+ plot plot
+ plots plot
+ plotted plot
+ plotter plotter
+ plough plough
+ ploughed plough
+ ploughman ploughman
+ ploughmen ploughmen
+ plow plow
+ plows plow
+ pluck pluck
+ plucked pluck
+ plucker plucker
+ plucking pluck
+ plucks pluck
+ plue plue
+ plum plum
+ plume plume
+ plumed plume
+ plumes plume
+ plummet plummet
+ plump plump
+ plumpy plumpi
+ plums plum
+ plung plung
+ plunge plung
+ plunged plung
+ plural plural
+ plurisy plurisi
+ plus plu
+ pluto pluto
+ plutus plutu
+ ply ply
+ po po
+ pocket pocket
+ pocketing pocket
+ pockets pocket
+ pocky pocki
+ pody podi
+ poem poem
+ poesy poesi
+ poet poet
+ poetical poetic
+ poetry poetri
+ poets poet
+ poictiers poictier
+ poinards poinard
+ poins poin
+ point point
+ pointblank pointblank
+ pointed point
+ pointing point
+ points point
+ pois poi
+ poise pois
+ poising pois
+ poison poison
+ poisoned poison
+ poisoner poison
+ poisoning poison
+ poisonous poison
+ poisons poison
+ poke poke
+ poking poke
+ pol pol
+ polack polack
+ polacks polack
+ poland poland
+ pold pold
+ pole pole
+ poleaxe poleax
+ polecat polecat
+ polecats polecat
+ polemon polemon
+ poles pole
+ poli poli
+ policies polici
+ policy polici
+ polish polish
+ polished polish
+ politic polit
+ politician politician
+ politicians politician
+ politicly politicli
+ polixenes polixen
+ poll poll
+ polluted pollut
+ pollution pollut
+ polonius poloniu
+ poltroons poltroon
+ polusion polus
+ polydamus polydamu
+ polydore polydor
+ polyxena polyxena
+ pomander pomand
+ pomegranate pomegran
+ pomewater pomewat
+ pomfret pomfret
+ pomgarnet pomgarnet
+ pommel pommel
+ pomp pomp
+ pompeius pompeiu
+ pompey pompei
+ pompion pompion
+ pompous pompou
+ pomps pomp
+ pond pond
+ ponder ponder
+ ponderous ponder
+ ponds pond
+ poniard poniard
+ poniards poniard
+ pont pont
+ pontic pontic
+ pontifical pontif
+ ponton ponton
+ pooh pooh
+ pool pool
+ poole pool
+ poop poop
+ poor poor
+ poorer poorer
+ poorest poorest
+ poorly poorli
+ pop pop
+ pope pope
+ popedom popedom
+ popilius popiliu
+ popingay popingai
+ popish popish
+ popp popp
+ poppy poppi
+ pops pop
+ popular popular
+ popularity popular
+ populous popul
+ porch porch
+ porches porch
+ pore pore
+ poring pore
+ pork pork
+ porn porn
+ porpentine porpentin
+ porridge porridg
+ porringer porring
+ port port
+ portable portabl
+ portage portag
+ portal portal
+ portance portanc
+ portcullis portculli
+ portend portend
+ portends portend
+ portent portent
+ portentous portent
+ portents portent
+ porter porter
+ porters porter
+ portia portia
+ portion portion
+ portly portli
+ portotartarossa portotartarossa
+ portrait portrait
+ portraiture portraitur
+ ports port
+ portugal portug
+ pose pose
+ posied posi
+ posies posi
+ position posit
+ positive posit
+ positively posit
+ posse poss
+ possess possess
+ possessed possess
+ possesses possess
+ possesseth possesseth
+ possessing possess
+ possession possess
+ possessions possess
+ possessor possessor
+ posset posset
+ possets posset
+ possibilities possibl
+ possibility possibl
+ possible possibl
+ possibly possibl
+ possitable possit
+ post post
+ poste post
+ posted post
+ posterior posterior
+ posteriors posterior
+ posterity poster
+ postern postern
+ posterns postern
+ posters poster
+ posthorse posthors
+ posthorses posthors
+ posthumus posthumu
+ posting post
+ postmaster postmast
+ posts post
+ postscript postscript
+ posture postur
+ postures postur
+ posy posi
+ pot pot
+ potable potabl
+ potations potat
+ potato potato
+ potatoes potato
+ potch potch
+ potency potenc
+ potent potent
+ potentates potent
+ potential potenti
+ potently potent
+ potents potent
+ pothecary pothecari
+ pother pother
+ potion potion
+ potions potion
+ potpan potpan
+ pots pot
+ potter potter
+ potting pot
+ pottle pottl
+ pouch pouch
+ poulter poulter
+ poultice poultic
+ poultney poultnei
+ pouncet pouncet
+ pound pound
+ pounds pound
+ pour pour
+ pourest pourest
+ pouring pour
+ pourquoi pourquoi
+ pours pour
+ pout pout
+ poverty poverti
+ pow pow
+ powd powd
+ powder powder
+ power power
+ powerful power
+ powerfully powerfulli
+ powerless powerless
+ powers power
+ pox pox
+ poys poi
+ poysam poysam
+ prabbles prabbl
+ practic practic
+ practice practic
+ practiced practic
+ practicer practic
+ practices practic
+ practicing practic
+ practis practi
+ practisants practis
+ practise practis
+ practiser practis
+ practisers practis
+ practises practis
+ practising practis
+ praeclarissimus praeclarissimu
+ praemunire praemunir
+ praetor praetor
+ praetors praetor
+ pragging prag
+ prague pragu
+ prain prain
+ prains prain
+ prais prai
+ praise prais
+ praised prais
+ praises prais
+ praisest praisest
+ praiseworthy praiseworthi
+ praising prais
+ prancing pranc
+ prank prank
+ pranks prank
+ prat prat
+ prate prate
+ prated prate
+ prater prater
+ prating prate
+ prattle prattl
+ prattler prattler
+ prattling prattl
+ prave prave
+ prawls prawl
+ prawns prawn
+ pray prai
+ prayer prayer
+ prayers prayer
+ praying prai
+ prays prai
+ pre pre
+ preach preach
+ preached preach
+ preachers preacher
+ preaches preach
+ preaching preach
+ preachment preachment
+ pread pread
+ preambulate preambul
+ precedence preced
+ precedent preced
+ preceding preced
+ precept precept
+ preceptial precepti
+ precepts precept
+ precinct precinct
+ precious preciou
+ preciously precious
+ precipice precipic
+ precipitating precipit
+ precipitation precipit
+ precise precis
+ precisely precis
+ preciseness precis
+ precisian precisian
+ precor precor
+ precurse precurs
+ precursors precursor
+ predeceased predeceas
+ predecessor predecessor
+ predecessors predecessor
+ predestinate predestin
+ predicament predica
+ predict predict
+ prediction predict
+ predictions predict
+ predominance predomin
+ predominant predomin
+ predominate predomin
+ preeches preech
+ preeminence preemin
+ preface prefac
+ prefer prefer
+ preferment prefer
+ preferments prefer
+ preferr preferr
+ preferreth preferreth
+ preferring prefer
+ prefers prefer
+ prefiguring prefigur
+ prefix prefix
+ prefixed prefix
+ preformed preform
+ pregnancy pregnanc
+ pregnant pregnant
+ pregnantly pregnantli
+ prejudicates prejud
+ prejudice prejudic
+ prejudicial prejudici
+ prelate prelat
+ premeditated premedit
+ premeditation premedit
+ premised premis
+ premises premis
+ prenez prenez
+ prenominate prenomin
+ prentice prentic
+ prentices prentic
+ preordinance preordin
+ prepar prepar
+ preparation prepar
+ preparations prepar
+ prepare prepar
+ prepared prepar
+ preparedly preparedli
+ prepares prepar
+ preparing prepar
+ prepost prepost
+ preposterous preposter
+ preposterously preposter
+ prerogatifes prerogatif
+ prerogative prerog
+ prerogatived prerogativ
+ presage presag
+ presagers presag
+ presages presag
+ presageth presageth
+ presaging presag
+ prescience prescienc
+ prescribe prescrib
+ prescript prescript
+ prescription prescript
+ prescriptions prescript
+ prescripts prescript
+ presence presenc
+ presences presenc
+ present present
+ presentation present
+ presented present
+ presenter present
+ presenters present
+ presenteth presenteth
+ presenting present
+ presently present
+ presentment present
+ presents present
+ preserv preserv
+ preservation preserv
+ preservative preserv
+ preserve preserv
+ preserved preserv
+ preserver preserv
+ preservers preserv
+ preserving preserv
+ president presid
+ press press
+ pressed press
+ presser presser
+ presses press
+ pressing press
+ pressure pressur
+ pressures pressur
+ prest prest
+ prester prester
+ presume presum
+ presumes presum
+ presuming presum
+ presumption presumpt
+ presumptuous presumptu
+ presuppos presuppo
+ pret pret
+ pretence pretenc
+ pretences pretenc
+ pretend pretend
+ pretended pretend
+ pretending pretend
+ pretense pretens
+ pretext pretext
+ pretia pretia
+ prettier prettier
+ prettiest prettiest
+ prettily prettili
+ prettiness pretti
+ pretty pretti
+ prevail prevail
+ prevailed prevail
+ prevaileth prevaileth
+ prevailing prevail
+ prevailment prevail
+ prevails prevail
+ prevent prevent
+ prevented prevent
+ prevention prevent
+ preventions prevent
+ prevents prevent
+ prey prei
+ preyful prey
+ preys prei
+ priam priam
+ priami priami
+ priamus priamu
+ pribbles pribbl
+ price price
+ prick prick
+ pricked prick
+ pricket pricket
+ pricking prick
+ pricks prick
+ pricksong pricksong
+ pride pride
+ prides pride
+ pridge pridg
+ prie prie
+ pried pri
+ prief prief
+ pries pri
+ priest priest
+ priesthood priesthood
+ priests priest
+ prig prig
+ primal primal
+ prime prime
+ primer primer
+ primero primero
+ primest primest
+ primitive primit
+ primo primo
+ primogenity primogen
+ primrose primros
+ primroses primros
+ primy primi
+ prince princ
+ princely princ
+ princes princ
+ princess princess
+ principal princip
+ principalities princip
+ principality princip
+ principle principl
+ principles principl
+ princox princox
+ prings pring
+ print print
+ printed print
+ printing print
+ printless printless
+ prints print
+ prioress prioress
+ priories priori
+ priority prioriti
+ priory priori
+ priscian priscian
+ prison prison
+ prisoner prison
+ prisoners prison
+ prisonment prison
+ prisonnier prisonni
+ prisons prison
+ pristine pristin
+ prithe prith
+ prithee prithe
+ privacy privaci
+ private privat
+ privately privat
+ privates privat
+ privilage privilag
+ privileg privileg
+ privilege privileg
+ privileged privileg
+ privileges privileg
+ privilegio privilegio
+ privily privili
+ privity priviti
+ privy privi
+ priz priz
+ prize prize
+ prized prize
+ prizer prizer
+ prizes prize
+ prizest prizest
+ prizing prize
+ pro pro
+ probable probabl
+ probal probal
+ probation probat
+ proceed proce
+ proceeded proceed
+ proceeders proceed
+ proceeding proceed
+ proceedings proceed
+ proceeds proce
+ process process
+ procession process
+ proclaim proclaim
+ proclaimed proclaim
+ proclaimeth proclaimeth
+ proclaims proclaim
+ proclamation proclam
+ proclamations proclam
+ proconsul proconsul
+ procrastinate procrastin
+ procreant procreant
+ procreants procreant
+ procreation procreat
+ procrus procru
+ proculeius proculeiu
+ procur procur
+ procurator procur
+ procure procur
+ procured procur
+ procures procur
+ procuring procur
+ prodigal prodig
+ prodigality prodig
+ prodigally prodig
+ prodigals prodig
+ prodigies prodigi
+ prodigious prodigi
+ prodigiously prodigi
+ prodigy prodigi
+ proditor proditor
+ produc produc
+ produce produc
+ produced produc
+ produces produc
+ producing produc
+ proface profac
+ profan profan
+ profanation profan
+ profane profan
+ profaned profan
+ profanely profan
+ profaneness profan
+ profaners profan
+ profaning profan
+ profess profess
+ professed profess
+ professes profess
+ profession profess
+ professions profess
+ professors professor
+ proffer proffer
+ proffered proffer
+ profferer proffer
+ proffers proffer
+ proficient profici
+ profit profit
+ profitable profit
+ profitably profit
+ profited profit
+ profiting profit
+ profitless profitless
+ profits profit
+ profound profound
+ profoundest profoundest
+ profoundly profoundli
+ progenitors progenitor
+ progeny progeni
+ progne progn
+ prognosticate prognost
+ prognostication prognost
+ progress progress
+ progression progress
+ prohibit prohibit
+ prohibition prohibit
+ project project
+ projection project
+ projects project
+ prolixious prolixi
+ prolixity prolix
+ prologue prologu
+ prologues prologu
+ prolong prolong
+ prolongs prolong
+ promethean promethean
+ prometheus prometheu
+ promis promi
+ promise promis
+ promised promis
+ promises promis
+ promiseth promiseth
+ promising promis
+ promontory promontori
+ promotion promot
+ promotions promot
+ prompt prompt
+ prompted prompt
+ promptement promptement
+ prompter prompter
+ prompting prompt
+ prompts prompt
+ prompture promptur
+ promulgate promulg
+ prone prone
+ prononcer prononc
+ prononcez prononcez
+ pronoun pronoun
+ pronounc pronounc
+ pronounce pronounc
+ pronounced pronounc
+ pronouncing pronounc
+ pronouns pronoun
+ proof proof
+ proofs proof
+ prop prop
+ propagate propag
+ propagation propag
+ propend propend
+ propension propens
+ proper proper
+ properer proper
+ properly properli
+ propertied properti
+ properties properti
+ property properti
+ prophecies propheci
+ prophecy propheci
+ prophesied prophesi
+ prophesier prophesi
+ prophesy prophesi
+ prophesying prophesi
+ prophet prophet
+ prophetess prophetess
+ prophetic prophet
+ prophetically prophet
+ prophets prophet
+ propinquity propinqu
+ propontic propont
+ proportion proport
+ proportionable proportion
+ proportions proport
+ propos propo
+ propose propos
+ proposed propos
+ proposer propos
+ proposes propos
+ proposing propos
+ proposition proposit
+ propositions proposit
+ propounded propound
+ propp propp
+ propre propr
+ propriety proprieti
+ props prop
+ propugnation propugn
+ prorogue prorogu
+ prorogued prorogu
+ proscription proscript
+ proscriptions proscript
+ prose prose
+ prosecute prosecut
+ prosecution prosecut
+ proselytes proselyt
+ proserpina proserpina
+ prosp prosp
+ prospect prospect
+ prosper prosper
+ prosperity prosper
+ prospero prospero
+ prosperous prosper
+ prosperously prosper
+ prospers prosper
+ prostitute prostitut
+ prostrate prostrat
+ protect protect
+ protected protect
+ protection protect
+ protector protector
+ protectors protector
+ protectorship protectorship
+ protectress protectress
+ protects protect
+ protest protest
+ protestation protest
+ protestations protest
+ protested protest
+ protester protest
+ protesting protest
+ protests protest
+ proteus proteu
+ protheus protheu
+ protract protract
+ protractive protract
+ proud proud
+ prouder prouder
+ proudest proudest
+ proudlier proudlier
+ proudly proudli
+ prouds proud
+ prov prov
+ provand provand
+ prove prove
+ proved prove
+ provender provend
+ proverb proverb
+ proverbs proverb
+ proves prove
+ proveth proveth
+ provide provid
+ provided provid
+ providence provid
+ provident provid
+ providently provid
+ provider provid
+ provides provid
+ province provinc
+ provinces provinc
+ provincial provinci
+ proving prove
+ provision provis
+ proviso proviso
+ provocation provoc
+ provok provok
+ provoke provok
+ provoked provok
+ provoker provok
+ provokes provok
+ provoketh provoketh
+ provoking provok
+ provost provost
+ prowess prowess
+ prudence prudenc
+ prudent prudent
+ prun prun
+ prune prune
+ prunes prune
+ pruning prune
+ pry pry
+ prying pry
+ psalm psalm
+ psalmist psalmist
+ psalms psalm
+ psalteries psalteri
+ ptolemies ptolemi
+ ptolemy ptolemi
+ public public
+ publican publican
+ publication public
+ publicly publicli
+ publicola publicola
+ publish publish
+ published publish
+ publisher publish
+ publishing publish
+ publius publiu
+ pucelle pucel
+ puck puck
+ pudder pudder
+ pudding pud
+ puddings pud
+ puddle puddl
+ puddled puddl
+ pudency pudenc
+ pueritia pueritia
+ puff puff
+ puffing puf
+ puffs puff
+ pugging pug
+ puis pui
+ puissance puissanc
+ puissant puissant
+ puke puke
+ puking puke
+ pulcher pulcher
+ puling pule
+ pull pull
+ puller puller
+ pullet pullet
+ pulling pull
+ pulls pull
+ pulpit pulpit
+ pulpiter pulpit
+ pulpits pulpit
+ pulse puls
+ pulsidge pulsidg
+ pump pump
+ pumpion pumpion
+ pumps pump
+ pun pun
+ punched punch
+ punish punish
+ punished punish
+ punishes punish
+ punishment punish
+ punishments punish
+ punk punk
+ punto punto
+ puny puni
+ pupil pupil
+ pupils pupil
+ puppet puppet
+ puppets puppet
+ puppies puppi
+ puppy puppi
+ pur pur
+ purblind purblind
+ purchas purcha
+ purchase purchas
+ purchased purchas
+ purchases purchas
+ purchaseth purchaseth
+ purchasing purchas
+ pure pure
+ purely pure
+ purer purer
+ purest purest
+ purg purg
+ purgation purgat
+ purgative purg
+ purgatory purgatori
+ purge purg
+ purged purg
+ purgers purger
+ purging purg
+ purifies purifi
+ purifying purifi
+ puritan puritan
+ purity puriti
+ purlieus purlieu
+ purple purpl
+ purpled purpl
+ purples purpl
+ purport purport
+ purpos purpo
+ purpose purpos
+ purposed purpos
+ purposely purpos
+ purposes purpos
+ purposeth purposeth
+ purposing purpos
+ purr purr
+ purs pur
+ purse purs
+ pursents pursent
+ purses purs
+ pursu pursu
+ pursue pursu
+ pursued pursu
+ pursuers pursuer
+ pursues pursu
+ pursuest pursuest
+ pursueth pursueth
+ pursuing pursu
+ pursuit pursuit
+ pursuivant pursuiv
+ pursuivants pursuiv
+ pursy pursi
+ purus puru
+ purveyor purveyor
+ push push
+ pushes push
+ pusillanimity pusillanim
+ put put
+ putrefy putrefi
+ putrified putrifi
+ puts put
+ putter putter
+ putting put
+ puttock puttock
+ puzzel puzzel
+ puzzle puzzl
+ puzzled puzzl
+ puzzles puzzl
+ py py
+ pygmalion pygmalion
+ pygmies pygmi
+ pygmy pygmi
+ pyramid pyramid
+ pyramides pyramid
+ pyramids pyramid
+ pyramis pyrami
+ pyramises pyramis
+ pyramus pyramu
+ pyrenean pyrenean
+ pyrrhus pyrrhu
+ pythagoras pythagora
+ qu qu
+ quadrangle quadrangl
+ quae quae
+ quaff quaff
+ quaffing quaf
+ quagmire quagmir
+ quail quail
+ quailing quail
+ quails quail
+ quaint quaint
+ quaintly quaintli
+ quak quak
+ quake quak
+ quakes quak
+ qualification qualif
+ qualified qualifi
+ qualifies qualifi
+ qualify qualifi
+ qualifying qualifi
+ qualite qualit
+ qualities qualiti
+ quality qualiti
+ qualm qualm
+ qualmish qualmish
+ quam quam
+ quand quand
+ quando quando
+ quantities quantiti
+ quantity quantiti
+ quare quar
+ quarrel quarrel
+ quarrell quarrel
+ quarreller quarrel
+ quarrelling quarrel
+ quarrelous quarrel
+ quarrels quarrel
+ quarrelsome quarrelsom
+ quarries quarri
+ quarry quarri
+ quart quart
+ quarter quarter
+ quartered quarter
+ quartering quarter
+ quarters quarter
+ quarts quart
+ quasi quasi
+ quat quat
+ quatch quatch
+ quay quai
+ que que
+ quean quean
+ queas quea
+ queasiness queasi
+ queasy queasi
+ queen queen
+ queens queen
+ quell quell
+ queller queller
+ quench quench
+ quenched quench
+ quenching quench
+ quenchless quenchless
+ quern quern
+ quest quest
+ questant questant
+ question question
+ questionable question
+ questioned question
+ questioning question
+ questionless questionless
+ questions question
+ questrists questrist
+ quests quest
+ queubus queubu
+ qui qui
+ quick quick
+ quicken quicken
+ quickens quicken
+ quicker quicker
+ quicklier quicklier
+ quickly quickli
+ quickness quick
+ quicksand quicksand
+ quicksands quicksand
+ quicksilverr quicksilverr
+ quid quid
+ quiddities quidditi
+ quiddits quiddit
+ quier quier
+ quiet quiet
+ quieter quieter
+ quietly quietli
+ quietness quiet
+ quietus quietu
+ quill quill
+ quillets quillet
+ quills quill
+ quilt quilt
+ quinapalus quinapalu
+ quince quinc
+ quinces quinc
+ quintain quintain
+ quintessence quintess
+ quintus quintu
+ quip quip
+ quips quip
+ quire quir
+ quiring quir
+ quirk quirk
+ quirks quirk
+ quis qui
+ quit quit
+ quite quit
+ quits quit
+ quittance quittanc
+ quitted quit
+ quitting quit
+ quiver quiver
+ quivering quiver
+ quivers quiver
+ quo quo
+ quod quod
+ quoifs quoif
+ quoint quoint
+ quoit quoit
+ quoits quoit
+ quondam quondam
+ quoniam quoniam
+ quote quot
+ quoted quot
+ quotes quot
+ quoth quoth
+ quotidian quotidian
+ r r
+ rabbit rabbit
+ rabble rabbl
+ rabblement rabblement
+ race race
+ rack rack
+ rackers racker
+ racket racket
+ rackets racket
+ racking rack
+ racks rack
+ radiance radianc
+ radiant radiant
+ radish radish
+ rafe rafe
+ raft raft
+ rag rag
+ rage rage
+ rages rage
+ rageth rageth
+ ragg ragg
+ ragged rag
+ raggedness ragged
+ raging rage
+ ragozine ragozin
+ rags rag
+ rah rah
+ rail rail
+ railed rail
+ railer railer
+ railest railest
+ raileth raileth
+ railing rail
+ rails rail
+ raiment raiment
+ rain rain
+ rainbow rainbow
+ raineth raineth
+ raining rain
+ rainold rainold
+ rains rain
+ rainy raini
+ rais rai
+ raise rais
+ raised rais
+ raises rais
+ raising rais
+ raisins raisin
+ rak rak
+ rake rake
+ rakers raker
+ rakes rake
+ ral ral
+ rald rald
+ ralph ralph
+ ram ram
+ rambures rambur
+ ramm ramm
+ rampallian rampallian
+ rampant rampant
+ ramping ramp
+ rampir rampir
+ ramps ramp
+ rams ram
+ ramsey ramsei
+ ramston ramston
+ ran ran
+ rance ranc
+ rancorous rancor
+ rancors rancor
+ rancour rancour
+ random random
+ rang rang
+ range rang
+ ranged rang
+ rangers ranger
+ ranges rang
+ ranging rang
+ rank rank
+ ranker ranker
+ rankest rankest
+ ranking rank
+ rankle rankl
+ rankly rankli
+ rankness rank
+ ranks rank
+ ransack ransack
+ ransacking ransack
+ ransom ransom
+ ransomed ransom
+ ransoming ransom
+ ransomless ransomless
+ ransoms ransom
+ rant rant
+ ranting rant
+ rap rap
+ rape rape
+ rapes rape
+ rapier rapier
+ rapiers rapier
+ rapine rapin
+ raps rap
+ rapt rapt
+ rapture raptur
+ raptures raptur
+ rar rar
+ rare rare
+ rarely rare
+ rareness rare
+ rarer rarer
+ rarest rarest
+ rarities rariti
+ rarity rariti
+ rascal rascal
+ rascalliest rascalliest
+ rascally rascal
+ rascals rascal
+ rased rase
+ rash rash
+ rasher rasher
+ rashly rashli
+ rashness rash
+ rat rat
+ ratcatcher ratcatch
+ ratcliff ratcliff
+ rate rate
+ rated rate
+ rately rate
+ rates rate
+ rather rather
+ ratherest ratherest
+ ratified ratifi
+ ratifiers ratifi
+ ratify ratifi
+ rating rate
+ rational ration
+ ratolorum ratolorum
+ rats rat
+ ratsbane ratsban
+ rattle rattl
+ rattles rattl
+ rattling rattl
+ rature ratur
+ raught raught
+ rav rav
+ rave rave
+ ravel ravel
+ raven raven
+ ravening raven
+ ravenous raven
+ ravens raven
+ ravenspurgh ravenspurgh
+ raves rave
+ ravin ravin
+ raving rave
+ ravish ravish
+ ravished ravish
+ ravisher ravish
+ ravishing ravish
+ ravishments ravish
+ raw raw
+ rawer rawer
+ rawly rawli
+ rawness raw
+ ray rai
+ rayed rai
+ rays rai
+ raz raz
+ raze raze
+ razed raze
+ razes raze
+ razeth razeth
+ razing raze
+ razor razor
+ razorable razor
+ razors razor
+ razure razur
+ re re
+ reach reach
+ reaches reach
+ reacheth reacheth
+ reaching reach
+ read read
+ reader reader
+ readiest readiest
+ readily readili
+ readiness readi
+ reading read
+ readins readin
+ reads read
+ ready readi
+ real real
+ really realli
+ realm realm
+ realms realm
+ reap reap
+ reapers reaper
+ reaping reap
+ reaps reap
+ rear rear
+ rears rear
+ rearward rearward
+ reason reason
+ reasonable reason
+ reasonably reason
+ reasoned reason
+ reasoning reason
+ reasonless reasonless
+ reasons reason
+ reave reav
+ rebate rebat
+ rebato rebato
+ rebeck rebeck
+ rebel rebel
+ rebell rebel
+ rebelling rebel
+ rebellion rebellion
+ rebellious rebelli
+ rebels rebel
+ rebound rebound
+ rebuk rebuk
+ rebuke rebuk
+ rebukeable rebuk
+ rebuked rebuk
+ rebukes rebuk
+ rebus rebu
+ recall recal
+ recant recant
+ recantation recant
+ recanter recant
+ recanting recant
+ receipt receipt
+ receipts receipt
+ receiv receiv
+ receive receiv
+ received receiv
+ receiver receiv
+ receives receiv
+ receivest receivest
+ receiveth receiveth
+ receiving receiv
+ receptacle receptacl
+ rechate rechat
+ reciprocal reciproc
+ reciprocally reciproc
+ recite recit
+ recited recit
+ reciterai reciterai
+ reck reck
+ recking reck
+ reckless reckless
+ reckon reckon
+ reckoned reckon
+ reckoning reckon
+ reckonings reckon
+ recks reck
+ reclaim reclaim
+ reclaims reclaim
+ reclusive reclus
+ recognizance recogniz
+ recognizances recogniz
+ recoil recoil
+ recoiling recoil
+ recollected recollect
+ recomforted recomfort
+ recomforture recomfortur
+ recommend recommend
+ recommended recommend
+ recommends recommend
+ recompens recompen
+ recompense recompens
+ reconcil reconcil
+ reconcile reconcil
+ reconciled reconcil
+ reconcilement reconcil
+ reconciler reconcil
+ reconciles reconcil
+ reconciliation reconcili
+ record record
+ recordation record
+ recorded record
+ recorder record
+ recorders record
+ records record
+ recount recount
+ recounted recount
+ recounting recount
+ recountments recount
+ recounts recount
+ recourse recours
+ recov recov
+ recover recov
+ recoverable recover
+ recovered recov
+ recoveries recoveri
+ recovers recov
+ recovery recoveri
+ recreant recreant
+ recreants recreant
+ recreate recreat
+ recreation recreat
+ rectify rectifi
+ rector rector
+ rectorship rectorship
+ recure recur
+ recured recur
+ red red
+ redbreast redbreast
+ redder redder
+ reddest reddest
+ rede rede
+ redeem redeem
+ redeemed redeem
+ redeemer redeem
+ redeeming redeem
+ redeems redeem
+ redeliver redeliv
+ redemption redempt
+ redime redim
+ redness red
+ redoubled redoubl
+ redoubted redoubt
+ redound redound
+ redress redress
+ redressed redress
+ redresses redress
+ reduce reduc
+ reechy reechi
+ reed reed
+ reeds reed
+ reek reek
+ reeking reek
+ reeks reek
+ reeky reeki
+ reel reel
+ reeleth reeleth
+ reeling reel
+ reels reel
+ refell refel
+ refer refer
+ reference refer
+ referr referr
+ referred refer
+ refigured refigur
+ refin refin
+ refined refin
+ reflect reflect
+ reflecting reflect
+ reflection reflect
+ reflex reflex
+ reform reform
+ reformation reform
+ reformed reform
+ refractory refractori
+ refrain refrain
+ refresh refresh
+ refreshing refresh
+ reft reft
+ refts reft
+ refuge refug
+ refus refu
+ refusal refus
+ refuse refus
+ refused refus
+ refusest refusest
+ refusing refus
+ reg reg
+ regal regal
+ regalia regalia
+ regan regan
+ regard regard
+ regardance regard
+ regarded regard
+ regardfully regardfulli
+ regarding regard
+ regards regard
+ regenerate regener
+ regent regent
+ regentship regentship
+ regia regia
+ regiment regiment
+ regiments regiment
+ regina regina
+ region region
+ regions region
+ regist regist
+ register regist
+ registers regist
+ regreet regreet
+ regreets regreet
+ regress regress
+ reguerdon reguerdon
+ regular regular
+ rehears rehear
+ rehearsal rehears
+ rehearse rehears
+ reign reign
+ reigned reign
+ reignier reignier
+ reigning reign
+ reigns reign
+ rein rein
+ reinforc reinforc
+ reinforce reinforc
+ reinforcement reinforc
+ reins rein
+ reiterate reiter
+ reject reject
+ rejected reject
+ rejoic rejoic
+ rejoice rejoic
+ rejoices rejoic
+ rejoiceth rejoiceth
+ rejoicing rejoic
+ rejoicingly rejoicingli
+ rejoindure rejoindur
+ rejourn rejourn
+ rel rel
+ relapse relaps
+ relate relat
+ relates relat
+ relation relat
+ relations relat
+ relative rel
+ releas relea
+ release releas
+ released releas
+ releasing releas
+ relent relent
+ relenting relent
+ relents relent
+ reliances relianc
+ relics relic
+ relief relief
+ reliev reliev
+ relieve reliev
+ relieved reliev
+ relieves reliev
+ relieving reliev
+ religion religion
+ religions religion
+ religious religi
+ religiously religi
+ relinquish relinquish
+ reliques reliqu
+ reliquit reliquit
+ relish relish
+ relume relum
+ rely reli
+ relying reli
+ remain remain
+ remainder remaind
+ remainders remaind
+ remained remain
+ remaineth remaineth
+ remaining remain
+ remains remain
+ remark remark
+ remarkable remark
+ remediate remedi
+ remedied remedi
+ remedies remedi
+ remedy remedi
+ rememb rememb
+ remember rememb
+ remembered rememb
+ remembers rememb
+ remembrance remembr
+ remembrancer remembranc
+ remembrances remembr
+ remercimens remercimen
+ remiss remiss
+ remission remiss
+ remissness remiss
+ remit remit
+ remnant remnant
+ remnants remnant
+ remonstrance remonstr
+ remorse remors
+ remorseful remors
+ remorseless remorseless
+ remote remot
+ remotion remot
+ remov remov
+ remove remov
+ removed remov
+ removedness removed
+ remover remov
+ removes remov
+ removing remov
+ remunerate remuner
+ remuneration remuner
+ rence renc
+ rend rend
+ render render
+ rendered render
+ renders render
+ rendezvous rendezv
+ renegado renegado
+ renege reneg
+ reneges reneg
+ renew renew
+ renewed renew
+ renewest renewest
+ renounce renounc
+ renouncement renounc
+ renouncing renounc
+ renowmed renowm
+ renown renown
+ renowned renown
+ rent rent
+ rents rent
+ repaid repaid
+ repair repair
+ repaired repair
+ repairing repair
+ repairs repair
+ repass repass
+ repast repast
+ repasture repastur
+ repay repai
+ repaying repai
+ repays repai
+ repeal repeal
+ repealing repeal
+ repeals repeal
+ repeat repeat
+ repeated repeat
+ repeating repeat
+ repeats repeat
+ repel repel
+ repent repent
+ repentance repent
+ repentant repent
+ repented repent
+ repenting repent
+ repents repent
+ repetition repetit
+ repetitions repetit
+ repin repin
+ repine repin
+ repining repin
+ replant replant
+ replenish replenish
+ replenished replenish
+ replete replet
+ replication replic
+ replied repli
+ replies repli
+ repliest repliest
+ reply repli
+ replying repli
+ report report
+ reported report
+ reporter report
+ reportest reportest
+ reporting report
+ reportingly reportingli
+ reports report
+ reposal repos
+ repose repos
+ reposeth reposeth
+ reposing repos
+ repossess repossess
+ reprehend reprehend
+ reprehended reprehend
+ reprehending reprehend
+ represent repres
+ representing repres
+ reprieve repriev
+ reprieves repriev
+ reprisal repris
+ reproach reproach
+ reproaches reproach
+ reproachful reproach
+ reproachfully reproachfulli
+ reprobate reprob
+ reprobation reprob
+ reproof reproof
+ reprov reprov
+ reprove reprov
+ reproveable reprov
+ reproves reprov
+ reproving reprov
+ repugn repugn
+ repugnancy repugn
+ repugnant repugn
+ repulse repuls
+ repulsed repuls
+ repurchas repurcha
+ repured repur
+ reputation reput
+ repute reput
+ reputed reput
+ reputeless reputeless
+ reputes reput
+ reputing reput
+ request request
+ requested request
+ requesting request
+ requests request
+ requiem requiem
+ requir requir
+ require requir
+ required requir
+ requires requir
+ requireth requireth
+ requiring requir
+ requisite requisit
+ requisites requisit
+ requit requit
+ requital requit
+ requite requit
+ requited requit
+ requites requit
+ rer rer
+ rere rere
+ rers rer
+ rescu rescu
+ rescue rescu
+ rescued rescu
+ rescues rescu
+ rescuing rescu
+ resemblance resembl
+ resemble resembl
+ resembled resembl
+ resembles resembl
+ resembleth resembleth
+ resembling resembl
+ reserv reserv
+ reservation reserv
+ reserve reserv
+ reserved reserv
+ reserves reserv
+ reside resid
+ residence resid
+ resident resid
+ resides resid
+ residing resid
+ residue residu
+ resign resign
+ resignation resign
+ resist resist
+ resistance resist
+ resisted resist
+ resisting resist
+ resists resist
+ resolute resolut
+ resolutely resolut
+ resolutes resolut
+ resolution resolut
+ resolv resolv
+ resolve resolv
+ resolved resolv
+ resolvedly resolvedli
+ resolves resolv
+ resolveth resolveth
+ resort resort
+ resorted resort
+ resounding resound
+ resounds resound
+ respeaking respeak
+ respect respect
+ respected respect
+ respecting respect
+ respective respect
+ respectively respect
+ respects respect
+ respice respic
+ respite respit
+ respites respit
+ responsive respons
+ respose respos
+ ress ress
+ rest rest
+ rested rest
+ resteth resteth
+ restful rest
+ resting rest
+ restitution restitut
+ restless restless
+ restor restor
+ restoration restor
+ restorative restor
+ restore restor
+ restored restor
+ restores restor
+ restoring restor
+ restrain restrain
+ restrained restrain
+ restraining restrain
+ restrains restrain
+ restraint restraint
+ rests rest
+ resty resti
+ resum resum
+ resume resum
+ resumes resum
+ resurrections resurrect
+ retail retail
+ retails retail
+ retain retain
+ retainers retain
+ retaining retain
+ retell retel
+ retention retent
+ retentive retent
+ retinue retinu
+ retir retir
+ retire retir
+ retired retir
+ retirement retir
+ retires retir
+ retiring retir
+ retold retold
+ retort retort
+ retorts retort
+ retourne retourn
+ retract retract
+ retreat retreat
+ retrograde retrograd
+ rets ret
+ return return
+ returned return
+ returnest returnest
+ returneth returneth
+ returning return
+ returns return
+ revania revania
+ reveal reveal
+ reveals reveal
+ revel revel
+ reveler revel
+ revell revel
+ reveller revel
+ revellers revel
+ revelling revel
+ revelry revelri
+ revels revel
+ reveng reveng
+ revenge reveng
+ revenged reveng
+ revengeful reveng
+ revengement reveng
+ revenger reveng
+ revengers reveng
+ revenges reveng
+ revenging reveng
+ revengingly revengingli
+ revenue revenu
+ revenues revenu
+ reverb reverb
+ reverberate reverber
+ reverbs reverb
+ reverenc reverenc
+ reverence rever
+ reverend reverend
+ reverent rever
+ reverently rever
+ revers rever
+ reverse revers
+ reversion revers
+ reverted revert
+ review review
+ reviewest reviewest
+ revil revil
+ revile revil
+ revisits revisit
+ reviv reviv
+ revive reviv
+ revives reviv
+ reviving reviv
+ revok revok
+ revoke revok
+ revokement revok
+ revolt revolt
+ revolted revolt
+ revolting revolt
+ revolts revolt
+ revolution revolut
+ revolutions revolut
+ revolve revolv
+ revolving revolv
+ reward reward
+ rewarded reward
+ rewarder reward
+ rewarding reward
+ rewards reward
+ reword reword
+ reworded reword
+ rex rex
+ rey rei
+ reynaldo reynaldo
+ rford rford
+ rful rful
+ rfull rfull
+ rhapsody rhapsodi
+ rheims rheim
+ rhenish rhenish
+ rhesus rhesu
+ rhetoric rhetor
+ rheum rheum
+ rheumatic rheumat
+ rheums rheum
+ rheumy rheumi
+ rhinoceros rhinocero
+ rhodes rhode
+ rhodope rhodop
+ rhubarb rhubarb
+ rhym rhym
+ rhyme rhyme
+ rhymers rhymer
+ rhymes rhyme
+ rhyming rhyme
+ rialto rialto
+ rib rib
+ ribald ribald
+ riband riband
+ ribands riband
+ ribaudred ribaudr
+ ribb ribb
+ ribbed rib
+ ribbon ribbon
+ ribbons ribbon
+ ribs rib
+ rice rice
+ rich rich
+ richard richard
+ richer richer
+ riches rich
+ richest richest
+ richly richli
+ richmond richmond
+ richmonds richmond
+ rid rid
+ riddance riddanc
+ ridden ridden
+ riddle riddl
+ riddles riddl
+ riddling riddl
+ ride ride
+ rider rider
+ riders rider
+ rides ride
+ ridest ridest
+ rideth rideth
+ ridge ridg
+ ridges ridg
+ ridiculous ridicul
+ riding ride
+ rids rid
+ rien rien
+ ries ri
+ rifle rifl
+ rift rift
+ rifted rift
+ rig rig
+ rigg rigg
+ riggish riggish
+ right right
+ righteous righteou
+ righteously righteous
+ rightful right
+ rightfully rightfulli
+ rightly rightli
+ rights right
+ rigol rigol
+ rigorous rigor
+ rigorously rigor
+ rigour rigour
+ ril ril
+ rim rim
+ rin rin
+ rinaldo rinaldo
+ rind rind
+ ring ring
+ ringing ring
+ ringleader ringlead
+ ringlets ringlet
+ rings ring
+ ringwood ringwood
+ riot riot
+ rioter rioter
+ rioting riot
+ riotous riotou
+ riots riot
+ rip rip
+ ripe ripe
+ ripely ripe
+ ripen ripen
+ ripened ripen
+ ripeness ripe
+ ripening ripen
+ ripens ripen
+ riper riper
+ ripest ripest
+ riping ripe
+ ripp ripp
+ ripping rip
+ rise rise
+ risen risen
+ rises rise
+ riseth riseth
+ rish rish
+ rising rise
+ rite rite
+ rites rite
+ rivage rivag
+ rival rival
+ rivality rival
+ rivall rival
+ rivals rival
+ rive rive
+ rived rive
+ rivelled rivel
+ river river
+ rivers river
+ rivet rivet
+ riveted rivet
+ rivets rivet
+ rivo rivo
+ rj rj
+ rless rless
+ road road
+ roads road
+ roam roam
+ roaming roam
+ roan roan
+ roar roar
+ roared roar
+ roarers roarer
+ roaring roar
+ roars roar
+ roast roast
+ roasted roast
+ rob rob
+ roba roba
+ robas roba
+ robb robb
+ robbed rob
+ robber robber
+ robbers robber
+ robbery robberi
+ robbing rob
+ robe robe
+ robed robe
+ robert robert
+ robes robe
+ robin robin
+ robs rob
+ robustious robusti
+ rochester rochest
+ rochford rochford
+ rock rock
+ rocks rock
+ rocky rocki
+ rod rod
+ rode rode
+ roderigo roderigo
+ rods rod
+ roe roe
+ roes roe
+ roger roger
+ rogero rogero
+ rogue rogu
+ roguery rogueri
+ rogues rogu
+ roguish roguish
+ roi roi
+ roisting roist
+ roll roll
+ rolled roll
+ rolling roll
+ rolls roll
+ rom rom
+ romage romag
+ roman roman
+ romano romano
+ romanos romano
+ romans roman
+ rome rome
+ romeo romeo
+ romish romish
+ rondure rondur
+ ronyon ronyon
+ rood rood
+ roof roof
+ roofs roof
+ rook rook
+ rooks rook
+ rooky rooki
+ room room
+ rooms room
+ root root
+ rooted root
+ rootedly rootedli
+ rooteth rooteth
+ rooting root
+ roots root
+ rope rope
+ ropery roperi
+ ropes rope
+ roping rope
+ ros ro
+ rosalind rosalind
+ rosalinda rosalinda
+ rosalinde rosalind
+ rosaline rosalin
+ roscius rosciu
+ rose rose
+ rosed rose
+ rosemary rosemari
+ rosencrantz rosencrantz
+ roses rose
+ ross ross
+ rosy rosi
+ rot rot
+ rote rote
+ roted rote
+ rother rother
+ rotherham rotherham
+ rots rot
+ rotted rot
+ rotten rotten
+ rottenness rotten
+ rotting rot
+ rotundity rotund
+ rouen rouen
+ rough rough
+ rougher rougher
+ roughest roughest
+ roughly roughli
+ roughness rough
+ round round
+ rounded round
+ roundel roundel
+ rounder rounder
+ roundest roundest
+ rounding round
+ roundly roundli
+ rounds round
+ roundure roundur
+ rous rou
+ rouse rous
+ roused rous
+ rousillon rousillon
+ rously rousli
+ roussi roussi
+ rout rout
+ routed rout
+ routs rout
+ rove rove
+ rover rover
+ row row
+ rowel rowel
+ rowland rowland
+ rowlands rowland
+ roy roi
+ royal royal
+ royalize royal
+ royally royal
+ royalties royalti
+ royalty royalti
+ roynish roynish
+ rs rs
+ rt rt
+ rub rub
+ rubb rubb
+ rubbing rub
+ rubbish rubbish
+ rubies rubi
+ rubious rubiou
+ rubs rub
+ ruby rubi
+ rud rud
+ rudand rudand
+ rudder rudder
+ ruddiness ruddi
+ ruddock ruddock
+ ruddy ruddi
+ rude rude
+ rudely rude
+ rudeness rude
+ ruder ruder
+ rudesby rudesbi
+ rudest rudest
+ rudiments rudiment
+ rue rue
+ rued ru
+ ruff ruff
+ ruffian ruffian
+ ruffians ruffian
+ ruffle ruffl
+ ruffling ruffl
+ ruffs ruff
+ rug rug
+ rugby rugbi
+ rugemount rugemount
+ rugged rug
+ ruin ruin
+ ruinate ruinat
+ ruined ruin
+ ruining ruin
+ ruinous ruinou
+ ruins ruin
+ rul rul
+ rule rule
+ ruled rule
+ ruler ruler
+ rulers ruler
+ rules rule
+ ruling rule
+ rumble rumbl
+ ruminaies ruminai
+ ruminat ruminat
+ ruminate rumin
+ ruminated rumin
+ ruminates rumin
+ rumination rumin
+ rumor rumor
+ rumour rumour
+ rumourer rumour
+ rumours rumour
+ rump rump
+ run run
+ runagate runag
+ runagates runag
+ runaway runawai
+ runaways runawai
+ rung rung
+ runn runn
+ runner runner
+ runners runner
+ running run
+ runs run
+ rupture ruptur
+ ruptures ruptur
+ rural rural
+ rush rush
+ rushes rush
+ rushing rush
+ rushling rushl
+ rushy rushi
+ russet russet
+ russia russia
+ russian russian
+ russians russian
+ rust rust
+ rusted rust
+ rustic rustic
+ rustically rustic
+ rustics rustic
+ rustle rustl
+ rustling rustl
+ rusts rust
+ rusty rusti
+ rut rut
+ ruth ruth
+ ruthful ruth
+ ruthless ruthless
+ rutland rutland
+ ruttish ruttish
+ ry ry
+ rye rye
+ rything ryth
+ s s
+ sa sa
+ saba saba
+ sabbath sabbath
+ sable sabl
+ sables sabl
+ sack sack
+ sackbuts sackbut
+ sackcloth sackcloth
+ sacked sack
+ sackerson sackerson
+ sacks sack
+ sacrament sacrament
+ sacred sacr
+ sacrific sacrif
+ sacrifice sacrific
+ sacrificers sacrific
+ sacrifices sacrific
+ sacrificial sacrifici
+ sacrificing sacrif
+ sacrilegious sacrilegi
+ sacring sacr
+ sad sad
+ sadder sadder
+ saddest saddest
+ saddle saddl
+ saddler saddler
+ saddles saddl
+ sadly sadli
+ sadness sad
+ saf saf
+ safe safe
+ safeguard safeguard
+ safely safe
+ safer safer
+ safest safest
+ safeties safeti
+ safety safeti
+ saffron saffron
+ sag sag
+ sage sage
+ sagittary sagittari
+ said said
+ saidst saidst
+ sail sail
+ sailing sail
+ sailmaker sailmak
+ sailor sailor
+ sailors sailor
+ sails sail
+ sain sain
+ saint saint
+ sainted saint
+ saintlike saintlik
+ saints saint
+ saith saith
+ sake sake
+ sakes sake
+ sala sala
+ salad salad
+ salamander salamand
+ salary salari
+ sale sale
+ salerio salerio
+ salicam salicam
+ salique saliqu
+ salisbury salisburi
+ sall sall
+ sallet sallet
+ sallets sallet
+ sallies salli
+ sallow sallow
+ sally salli
+ salmon salmon
+ salmons salmon
+ salt salt
+ salter salter
+ saltiers saltier
+ saltness salt
+ saltpetre saltpetr
+ salutation salut
+ salutations salut
+ salute salut
+ saluted salut
+ salutes salut
+ saluteth saluteth
+ salv salv
+ salvation salvat
+ salve salv
+ salving salv
+ same same
+ samingo samingo
+ samp samp
+ sampire sampir
+ sample sampl
+ sampler sampler
+ sampson sampson
+ samson samson
+ samsons samson
+ sancta sancta
+ sanctified sanctifi
+ sanctifies sanctifi
+ sanctify sanctifi
+ sanctimonies sanctimoni
+ sanctimonious sanctimoni
+ sanctimony sanctimoni
+ sanctities sanctiti
+ sanctity sanctiti
+ sanctuarize sanctuar
+ sanctuary sanctuari
+ sand sand
+ sandal sandal
+ sandbag sandbag
+ sanded sand
+ sands sand
+ sandy sandi
+ sandys sandi
+ sang sang
+ sanguine sanguin
+ sanguis sangui
+ sanity saniti
+ sans san
+ santrailles santrail
+ sap sap
+ sapient sapient
+ sapit sapit
+ sapless sapless
+ sapling sapl
+ sapphire sapphir
+ sapphires sapphir
+ saracens saracen
+ sarcenet sarcenet
+ sard sard
+ sardians sardian
+ sardinia sardinia
+ sardis sardi
+ sarum sarum
+ sat sat
+ satan satan
+ satchel satchel
+ sate sate
+ sated sate
+ satiate satiat
+ satiety satieti
+ satin satin
+ satire satir
+ satirical satir
+ satis sati
+ satisfaction satisfact
+ satisfied satisfi
+ satisfies satisfi
+ satisfy satisfi
+ satisfying satisfi
+ saturday saturdai
+ saturdays saturdai
+ saturn saturn
+ saturnine saturnin
+ saturninus saturninu
+ satyr satyr
+ satyrs satyr
+ sauc sauc
+ sauce sauc
+ sauced sauc
+ saucers saucer
+ sauces sauc
+ saucily saucili
+ sauciness sauci
+ saucy sauci
+ sauf sauf
+ saunder saunder
+ sav sav
+ savage savag
+ savagely savag
+ savageness savag
+ savagery savageri
+ savages savag
+ save save
+ saved save
+ saves save
+ saving save
+ saviour saviour
+ savory savori
+ savour savour
+ savouring savour
+ savours savour
+ savoury savouri
+ savoy savoi
+ saw saw
+ sawed saw
+ sawest sawest
+ sawn sawn
+ sawpit sawpit
+ saws saw
+ sawyer sawyer
+ saxons saxon
+ saxony saxoni
+ saxton saxton
+ say sai
+ sayest sayest
+ saying sai
+ sayings sai
+ says sai
+ sayst sayst
+ sblood sblood
+ sc sc
+ scab scab
+ scabbard scabbard
+ scabs scab
+ scaffold scaffold
+ scaffoldage scaffoldag
+ scal scal
+ scald scald
+ scalded scald
+ scalding scald
+ scale scale
+ scaled scale
+ scales scale
+ scaling scale
+ scall scall
+ scalp scalp
+ scalps scalp
+ scaly scali
+ scamble scambl
+ scambling scambl
+ scamels scamel
+ scan scan
+ scandal scandal
+ scandaliz scandaliz
+ scandalous scandal
+ scandy scandi
+ scann scann
+ scant scant
+ scanted scant
+ scanter scanter
+ scanting scant
+ scantling scantl
+ scants scant
+ scap scap
+ scape scape
+ scaped scape
+ scapes scape
+ scapeth scapeth
+ scar scar
+ scarce scarc
+ scarcely scarc
+ scarcity scarciti
+ scare scare
+ scarecrow scarecrow
+ scarecrows scarecrow
+ scarf scarf
+ scarfed scarf
+ scarfs scarf
+ scaring scare
+ scarlet scarlet
+ scarr scarr
+ scarre scarr
+ scars scar
+ scarus scaru
+ scath scath
+ scathe scath
+ scathful scath
+ scatt scatt
+ scatter scatter
+ scattered scatter
+ scattering scatter
+ scatters scatter
+ scelera scelera
+ scelerisque scelerisqu
+ scene scene
+ scenes scene
+ scent scent
+ scented scent
+ scept scept
+ scepter scepter
+ sceptre sceptr
+ sceptred sceptr
+ sceptres sceptr
+ schedule schedul
+ schedules schedul
+ scholar scholar
+ scholarly scholarli
+ scholars scholar
+ school school
+ schoolboy schoolboi
+ schoolboys schoolboi
+ schoolfellows schoolfellow
+ schooling school
+ schoolmaster schoolmast
+ schoolmasters schoolmast
+ schools school
+ sciatica sciatica
+ sciaticas sciatica
+ science scienc
+ sciences scienc
+ scimitar scimitar
+ scion scion
+ scions scion
+ scissors scissor
+ scoff scoff
+ scoffer scoffer
+ scoffing scof
+ scoffs scoff
+ scoggin scoggin
+ scold scold
+ scolding scold
+ scolds scold
+ sconce sconc
+ scone scone
+ scope scope
+ scopes scope
+ scorch scorch
+ scorched scorch
+ score score
+ scored score
+ scores score
+ scoring score
+ scorn scorn
+ scorned scorn
+ scornful scorn
+ scornfully scornfulli
+ scorning scorn
+ scorns scorn
+ scorpion scorpion
+ scorpions scorpion
+ scot scot
+ scotch scotch
+ scotches scotch
+ scotland scotland
+ scots scot
+ scottish scottish
+ scoundrels scoundrel
+ scour scour
+ scoured scour
+ scourg scourg
+ scourge scourg
+ scouring scour
+ scout scout
+ scouts scout
+ scowl scowl
+ scrap scrap
+ scrape scrape
+ scraping scrape
+ scraps scrap
+ scratch scratch
+ scratches scratch
+ scratching scratch
+ scream scream
+ screams scream
+ screech screech
+ screeching screech
+ screen screen
+ screens screen
+ screw screw
+ screws screw
+ scribbl scribbl
+ scribbled scribbl
+ scribe scribe
+ scribes scribe
+ scrimers scrimer
+ scrip scrip
+ scrippage scrippag
+ scripture scriptur
+ scriptures scriptur
+ scrivener scriven
+ scroll scroll
+ scrolls scroll
+ scroop scroop
+ scrowl scrowl
+ scroyles scroyl
+ scrubbed scrub
+ scruple scrupl
+ scruples scrupl
+ scrupulous scrupul
+ scuffles scuffl
+ scuffling scuffl
+ scullion scullion
+ sculls scull
+ scum scum
+ scurril scurril
+ scurrility scurril
+ scurrilous scurril
+ scurvy scurvi
+ scuse scuse
+ scut scut
+ scutcheon scutcheon
+ scutcheons scutcheon
+ scylla scylla
+ scythe scyth
+ scythed scyth
+ scythia scythia
+ scythian scythian
+ sdeath sdeath
+ se se
+ sea sea
+ seacoal seacoal
+ seafaring seafar
+ seal seal
+ sealed seal
+ sealing seal
+ seals seal
+ seam seam
+ seamen seamen
+ seamy seami
+ seaport seaport
+ sear sear
+ searce searc
+ search search
+ searchers searcher
+ searches search
+ searcheth searcheth
+ searching search
+ seared sear
+ seas sea
+ seasick seasick
+ seaside seasid
+ season season
+ seasoned season
+ seasons season
+ seat seat
+ seated seat
+ seats seat
+ sebastian sebastian
+ second second
+ secondarily secondarili
+ secondary secondari
+ seconded second
+ seconds second
+ secrecy secreci
+ secret secret
+ secretaries secretari
+ secretary secretari
+ secretly secretli
+ secrets secret
+ sect sect
+ sectary sectari
+ sects sect
+ secundo secundo
+ secure secur
+ securely secur
+ securing secur
+ security secur
+ sedg sedg
+ sedge sedg
+ sedges sedg
+ sedgy sedgi
+ sedition sedit
+ seditious sediti
+ seduc seduc
+ seduce seduc
+ seduced seduc
+ seducer seduc
+ seducing seduc
+ see see
+ seed seed
+ seeded seed
+ seedness seed
+ seeds seed
+ seedsman seedsman
+ seein seein
+ seeing see
+ seek seek
+ seeking seek
+ seeks seek
+ seel seel
+ seeling seel
+ seely seeli
+ seem seem
+ seemed seem
+ seemers seemer
+ seemest seemest
+ seemeth seemeth
+ seeming seem
+ seemingly seemingli
+ seemly seemli
+ seems seem
+ seen seen
+ seer seer
+ sees see
+ seese sees
+ seest seest
+ seethe seeth
+ seethes seeth
+ seething seeth
+ seeting seet
+ segregation segreg
+ seigneur seigneur
+ seigneurs seigneur
+ seiz seiz
+ seize seiz
+ seized seiz
+ seizes seiz
+ seizeth seizeth
+ seizing seiz
+ seizure seizur
+ seld seld
+ seldom seldom
+ select select
+ seleucus seleucu
+ self self
+ selfsame selfsam
+ sell sell
+ seller seller
+ selling sell
+ sells sell
+ selves selv
+ semblable semblabl
+ semblably semblabl
+ semblance semblanc
+ semblances semblanc
+ semblative sembl
+ semi semi
+ semicircle semicircl
+ semiramis semirami
+ semper semper
+ sempronius semproniu
+ senate senat
+ senator senat
+ senators senat
+ send send
+ sender sender
+ sendeth sendeth
+ sending send
+ sends send
+ seneca seneca
+ senior senior
+ seniory seniori
+ senis seni
+ sennet sennet
+ senoys senoi
+ sense sens
+ senseless senseless
+ senses sens
+ sensible sensibl
+ sensibly sensibl
+ sensual sensual
+ sensuality sensual
+ sent sent
+ sentenc sentenc
+ sentence sentenc
+ sentences sentenc
+ sententious sententi
+ sentinel sentinel
+ sentinels sentinel
+ separable separ
+ separate separ
+ separated separ
+ separates separ
+ separation separ
+ septentrion septentrion
+ sepulchre sepulchr
+ sepulchres sepulchr
+ sepulchring sepulchr
+ sequel sequel
+ sequence sequenc
+ sequent sequent
+ sequest sequest
+ sequester sequest
+ sequestration sequestr
+ sere sere
+ serenis sereni
+ serge serg
+ sergeant sergeant
+ serious seriou
+ seriously serious
+ sermon sermon
+ sermons sermon
+ serpent serpent
+ serpentine serpentin
+ serpents serpent
+ serpigo serpigo
+ serv serv
+ servant servant
+ servanted servant
+ servants servant
+ serve serv
+ served serv
+ server server
+ serves serv
+ serveth serveth
+ service servic
+ serviceable servic
+ services servic
+ servile servil
+ servility servil
+ servilius serviliu
+ serving serv
+ servingman servingman
+ servingmen servingmen
+ serviteur serviteur
+ servitor servitor
+ servitors servitor
+ servitude servitud
+ sessa sessa
+ session session
+ sessions session
+ sestos sesto
+ set set
+ setebos setebo
+ sets set
+ setter setter
+ setting set
+ settle settl
+ settled settl
+ settlest settlest
+ settling settl
+ sev sev
+ seven seven
+ sevenfold sevenfold
+ sevennight sevennight
+ seventeen seventeen
+ seventh seventh
+ seventy seventi
+ sever sever
+ several sever
+ severally sever
+ severals sever
+ severe sever
+ severed sever
+ severely sever
+ severest severest
+ severing sever
+ severity sever
+ severn severn
+ severs sever
+ sew sew
+ seward seward
+ sewer sewer
+ sewing sew
+ sex sex
+ sexes sex
+ sexton sexton
+ sextus sextu
+ seymour seymour
+ seyton seyton
+ sfoot sfoot
+ sh sh
+ shackle shackl
+ shackles shackl
+ shade shade
+ shades shade
+ shadow shadow
+ shadowed shadow
+ shadowing shadow
+ shadows shadow
+ shadowy shadowi
+ shady shadi
+ shafalus shafalu
+ shaft shaft
+ shafts shaft
+ shag shag
+ shak shak
+ shake shake
+ shaked shake
+ shaken shaken
+ shakes shake
+ shaking shake
+ shales shale
+ shall shall
+ shallenge shalleng
+ shallow shallow
+ shallowest shallowest
+ shallowly shallowli
+ shallows shallow
+ shalt shalt
+ sham sham
+ shambles shambl
+ shame shame
+ shamed shame
+ shameful shame
+ shamefully shamefulli
+ shameless shameless
+ shames shame
+ shamest shamest
+ shaming shame
+ shank shank
+ shanks shank
+ shap shap
+ shape shape
+ shaped shape
+ shapeless shapeless
+ shapen shapen
+ shapes shape
+ shaping shape
+ shar shar
+ shard shard
+ sharded shard
+ shards shard
+ share share
+ shared share
+ sharers sharer
+ shares share
+ sharing share
+ shark shark
+ sharp sharp
+ sharpen sharpen
+ sharpened sharpen
+ sharpens sharpen
+ sharper sharper
+ sharpest sharpest
+ sharply sharpli
+ sharpness sharp
+ sharps sharp
+ shatter shatter
+ shav shav
+ shave shave
+ shaven shaven
+ shaw shaw
+ she she
+ sheaf sheaf
+ sheal sheal
+ shear shear
+ shearers shearer
+ shearing shear
+ shearman shearman
+ shears shear
+ sheath sheath
+ sheathe sheath
+ sheathed sheath
+ sheathes sheath
+ sheathing sheath
+ sheaved sheav
+ sheaves sheav
+ shed shed
+ shedding shed
+ sheds shed
+ sheen sheen
+ sheep sheep
+ sheepcote sheepcot
+ sheepcotes sheepcot
+ sheeps sheep
+ sheepskins sheepskin
+ sheer sheer
+ sheet sheet
+ sheeted sheet
+ sheets sheet
+ sheffield sheffield
+ shelf shelf
+ shell shell
+ shells shell
+ shelt shelt
+ shelter shelter
+ shelters shelter
+ shelves shelv
+ shelving shelv
+ shelvy shelvi
+ shent shent
+ shepherd shepherd
+ shepherdes shepherd
+ shepherdess shepherdess
+ shepherdesses shepherdess
+ shepherds shepherd
+ sher sher
+ sheriff sheriff
+ sherris sherri
+ shes she
+ sheweth sheweth
+ shield shield
+ shielded shield
+ shields shield
+ shift shift
+ shifted shift
+ shifting shift
+ shifts shift
+ shilling shill
+ shillings shill
+ shin shin
+ shine shine
+ shines shine
+ shineth shineth
+ shining shine
+ shins shin
+ shiny shini
+ ship ship
+ shipboard shipboard
+ shipman shipman
+ shipmaster shipmast
+ shipmen shipmen
+ shipp shipp
+ shipped ship
+ shipping ship
+ ships ship
+ shipt shipt
+ shipwreck shipwreck
+ shipwrecking shipwreck
+ shipwright shipwright
+ shipwrights shipwright
+ shire shire
+ shirley shirlei
+ shirt shirt
+ shirts shirt
+ shive shive
+ shiver shiver
+ shivering shiver
+ shivers shiver
+ shoal shoal
+ shoals shoal
+ shock shock
+ shocks shock
+ shod shod
+ shoe shoe
+ shoeing shoe
+ shoemaker shoemak
+ shoes shoe
+ shog shog
+ shone shone
+ shook shook
+ shoon shoon
+ shoot shoot
+ shooter shooter
+ shootie shooti
+ shooting shoot
+ shoots shoot
+ shop shop
+ shops shop
+ shore shore
+ shores shore
+ shorn shorn
+ short short
+ shortcake shortcak
+ shorten shorten
+ shortened shorten
+ shortens shorten
+ shorter shorter
+ shortly shortli
+ shortness short
+ shot shot
+ shotten shotten
+ shoughs shough
+ should should
+ shoulder shoulder
+ shouldering shoulder
+ shoulders shoulder
+ shouldst shouldst
+ shout shout
+ shouted shout
+ shouting shout
+ shouts shout
+ shov shov
+ shove shove
+ shovel shovel
+ shovels shovel
+ show show
+ showed show
+ shower shower
+ showers shower
+ showest showest
+ showing show
+ shown shown
+ shows show
+ shreds shred
+ shrew shrew
+ shrewd shrewd
+ shrewdly shrewdli
+ shrewdness shrewd
+ shrewish shrewish
+ shrewishly shrewishli
+ shrewishness shrewish
+ shrews shrew
+ shrewsbury shrewsburi
+ shriek shriek
+ shrieking shriek
+ shrieks shriek
+ shrieve shriev
+ shrift shrift
+ shrill shrill
+ shriller shriller
+ shrills shrill
+ shrilly shrilli
+ shrimp shrimp
+ shrine shrine
+ shrink shrink
+ shrinking shrink
+ shrinks shrink
+ shriv shriv
+ shrive shrive
+ shriver shriver
+ shrives shrive
+ shriving shrive
+ shroud shroud
+ shrouded shroud
+ shrouding shroud
+ shrouds shroud
+ shrove shrove
+ shrow shrow
+ shrows shrow
+ shrub shrub
+ shrubs shrub
+ shrug shrug
+ shrugs shrug
+ shrunk shrunk
+ shudd shudd
+ shudders shudder
+ shuffl shuffl
+ shuffle shuffl
+ shuffled shuffl
+ shuffling shuffl
+ shun shun
+ shunless shunless
+ shunn shunn
+ shunned shun
+ shunning shun
+ shuns shun
+ shut shut
+ shuts shut
+ shuttle shuttl
+ shy shy
+ shylock shylock
+ si si
+ sibyl sibyl
+ sibylla sibylla
+ sibyls sibyl
+ sicil sicil
+ sicilia sicilia
+ sicilian sicilian
+ sicilius siciliu
+ sicils sicil
+ sicily sicili
+ sicinius siciniu
+ sick sick
+ sicken sicken
+ sickens sicken
+ sicker sicker
+ sickle sickl
+ sicklemen sicklemen
+ sicklied sickli
+ sickliness sickli
+ sickly sickli
+ sickness sick
+ sicles sicl
+ sicyon sicyon
+ side side
+ sided side
+ sides side
+ siege sieg
+ sieges sieg
+ sienna sienna
+ sies si
+ sieve siev
+ sift sift
+ sifted sift
+ sigeia sigeia
+ sigh sigh
+ sighed sigh
+ sighing sigh
+ sighs sigh
+ sight sight
+ sighted sight
+ sightless sightless
+ sightly sightli
+ sights sight
+ sign sign
+ signal signal
+ signet signet
+ signieur signieur
+ significant signific
+ significants signific
+ signified signifi
+ signifies signifi
+ signify signifi
+ signifying signifi
+ signior signior
+ signiories signiori
+ signiors signior
+ signiory signiori
+ signor signor
+ signories signori
+ signs sign
+ signum signum
+ silenc silenc
+ silence silenc
+ silenced silenc
+ silencing silenc
+ silent silent
+ silently silent
+ silius siliu
+ silk silk
+ silken silken
+ silkman silkman
+ silks silk
+ silliest silliest
+ silliness silli
+ silling sill
+ silly silli
+ silva silva
+ silver silver
+ silvered silver
+ silverly silverli
+ silvia silvia
+ silvius silviu
+ sima sima
+ simile simil
+ similes simil
+ simois simoi
+ simon simon
+ simony simoni
+ simp simp
+ simpcox simpcox
+ simple simpl
+ simpleness simpl
+ simpler simpler
+ simples simpl
+ simplicity simplic
+ simply simpli
+ simular simular
+ simulation simul
+ sin sin
+ since sinc
+ sincere sincer
+ sincerely sincer
+ sincerity sincer
+ sinel sinel
+ sinew sinew
+ sinewed sinew
+ sinews sinew
+ sinewy sinewi
+ sinful sin
+ sinfully sinfulli
+ sing sing
+ singe sing
+ singeing sing
+ singer singer
+ singes sing
+ singeth singeth
+ singing sing
+ single singl
+ singled singl
+ singleness singl
+ singly singli
+ sings sing
+ singular singular
+ singulariter singularit
+ singularities singular
+ singularity singular
+ singuled singul
+ sinister sinist
+ sink sink
+ sinking sink
+ sinks sink
+ sinn sinn
+ sinner sinner
+ sinners sinner
+ sinning sin
+ sinon sinon
+ sins sin
+ sip sip
+ sipping sip
+ sir sir
+ sire sire
+ siren siren
+ sirrah sirrah
+ sirs sir
+ sist sist
+ sister sister
+ sisterhood sisterhood
+ sisterly sisterli
+ sisters sister
+ sit sit
+ sith sith
+ sithence sithenc
+ sits sit
+ sitting sit
+ situate situat
+ situation situat
+ situations situat
+ siward siward
+ six six
+ sixpence sixpenc
+ sixpences sixpenc
+ sixpenny sixpenni
+ sixteen sixteen
+ sixth sixth
+ sixty sixti
+ siz siz
+ size size
+ sizes size
+ sizzle sizzl
+ skains skain
+ skamble skambl
+ skein skein
+ skelter skelter
+ skies ski
+ skilful skil
+ skilfully skilfulli
+ skill skill
+ skilless skilless
+ skillet skillet
+ skillful skill
+ skills skill
+ skim skim
+ skimble skimbl
+ skin skin
+ skinker skinker
+ skinny skinni
+ skins skin
+ skip skip
+ skipp skipp
+ skipper skipper
+ skipping skip
+ skirmish skirmish
+ skirmishes skirmish
+ skirr skirr
+ skirted skirt
+ skirts skirt
+ skittish skittish
+ skulking skulk
+ skull skull
+ skulls skull
+ sky sky
+ skyey skyei
+ skyish skyish
+ slab slab
+ slack slack
+ slackly slackli
+ slackness slack
+ slain slain
+ slake slake
+ sland sland
+ slander slander
+ slandered slander
+ slanderer slander
+ slanderers slander
+ slandering slander
+ slanderous slander
+ slanders slander
+ slash slash
+ slaught slaught
+ slaughter slaughter
+ slaughtered slaughter
+ slaughterer slaughter
+ slaughterman slaughterman
+ slaughtermen slaughtermen
+ slaughterous slaughter
+ slaughters slaughter
+ slave slave
+ slaver slaver
+ slavery slaveri
+ slaves slave
+ slavish slavish
+ slay slai
+ slayeth slayeth
+ slaying slai
+ slays slai
+ sleave sleav
+ sledded sled
+ sleek sleek
+ sleekly sleekli
+ sleep sleep
+ sleeper sleeper
+ sleepers sleeper
+ sleepest sleepest
+ sleeping sleep
+ sleeps sleep
+ sleepy sleepi
+ sleeve sleev
+ sleeves sleev
+ sleid sleid
+ sleided sleid
+ sleight sleight
+ sleights sleight
+ slender slender
+ slenderer slender
+ slenderly slenderli
+ slept slept
+ slew slew
+ slewest slewest
+ slice slice
+ slid slid
+ slide slide
+ slides slide
+ sliding slide
+ slight slight
+ slighted slight
+ slightest slightest
+ slightly slightli
+ slightness slight
+ slights slight
+ slily slili
+ slime slime
+ slimy slimi
+ slings sling
+ slink slink
+ slip slip
+ slipp slipp
+ slipper slipper
+ slippers slipper
+ slippery slipperi
+ slips slip
+ slish slish
+ slit slit
+ sliver sliver
+ slobb slobb
+ slomber slomber
+ slop slop
+ slope slope
+ slops slop
+ sloth sloth
+ slothful sloth
+ slough slough
+ slovenly slovenli
+ slovenry slovenri
+ slow slow
+ slower slower
+ slowly slowli
+ slowness slow
+ slubber slubber
+ slug slug
+ sluggard sluggard
+ sluggardiz sluggardiz
+ sluggish sluggish
+ sluic sluic
+ slumb slumb
+ slumber slumber
+ slumbers slumber
+ slumbery slumberi
+ slunk slunk
+ slut slut
+ sluts slut
+ sluttery slutteri
+ sluttish sluttish
+ sluttishness sluttish
+ sly sly
+ slys sly
+ smack smack
+ smacking smack
+ smacks smack
+ small small
+ smaller smaller
+ smallest smallest
+ smallness small
+ smalus smalu
+ smart smart
+ smarting smart
+ smartly smartli
+ smatch smatch
+ smatter smatter
+ smear smear
+ smell smell
+ smelling smell
+ smells smell
+ smelt smelt
+ smil smil
+ smile smile
+ smiled smile
+ smiles smile
+ smilest smilest
+ smilets smilet
+ smiling smile
+ smilingly smilingli
+ smirch smirch
+ smirched smirch
+ smit smit
+ smite smite
+ smites smite
+ smith smith
+ smithfield smithfield
+ smock smock
+ smocks smock
+ smok smok
+ smoke smoke
+ smoked smoke
+ smokes smoke
+ smoking smoke
+ smoky smoki
+ smooth smooth
+ smoothed smooth
+ smoothing smooth
+ smoothly smoothli
+ smoothness smooth
+ smooths smooth
+ smote smote
+ smoth smoth
+ smother smother
+ smothered smother
+ smothering smother
+ smug smug
+ smulkin smulkin
+ smutch smutch
+ snaffle snaffl
+ snail snail
+ snails snail
+ snake snake
+ snakes snake
+ snaky snaki
+ snap snap
+ snapp snapp
+ snapper snapper
+ snar snar
+ snare snare
+ snares snare
+ snarl snarl
+ snarleth snarleth
+ snarling snarl
+ snatch snatch
+ snatchers snatcher
+ snatches snatch
+ snatching snatch
+ sneak sneak
+ sneaking sneak
+ sneap sneap
+ sneaping sneap
+ sneck sneck
+ snip snip
+ snipe snipe
+ snipt snipt
+ snore snore
+ snores snore
+ snoring snore
+ snorting snort
+ snout snout
+ snow snow
+ snowballs snowbal
+ snowed snow
+ snowy snowi
+ snuff snuff
+ snuffs snuff
+ snug snug
+ so so
+ soak soak
+ soaking soak
+ soaks soak
+ soar soar
+ soaring soar
+ soars soar
+ sob sob
+ sobbing sob
+ sober sober
+ soberly soberli
+ sobriety sobrieti
+ sobs sob
+ sociable sociabl
+ societies societi
+ society societi
+ socks sock
+ socrates socrat
+ sod sod
+ sodden sodden
+ soe soe
+ soever soever
+ soft soft
+ soften soften
+ softens soften
+ softer softer
+ softest softest
+ softly softli
+ softness soft
+ soil soil
+ soiled soil
+ soilure soilur
+ soit soit
+ sojourn sojourn
+ sol sol
+ sola sola
+ solace solac
+ solanio solanio
+ sold sold
+ soldat soldat
+ solder solder
+ soldest soldest
+ soldier soldier
+ soldiers soldier
+ soldiership soldiership
+ sole sole
+ solely sole
+ solem solem
+ solemn solemn
+ solemness solem
+ solemnities solemn
+ solemnity solemn
+ solemniz solemniz
+ solemnize solemn
+ solemnized solemn
+ solemnly solemnli
+ soles sole
+ solicit solicit
+ solicitation solicit
+ solicited solicit
+ soliciting solicit
+ solicitings solicit
+ solicitor solicitor
+ solicits solicit
+ solid solid
+ solidares solidar
+ solidity solid
+ solinus solinu
+ solitary solitari
+ solomon solomon
+ solon solon
+ solum solum
+ solus solu
+ solyman solyman
+ some some
+ somebody somebodi
+ someone someon
+ somerset somerset
+ somerville somervil
+ something someth
+ sometime sometim
+ sometimes sometim
+ somever somev
+ somewhat somewhat
+ somewhere somewher
+ somewhither somewhith
+ somme somm
+ son son
+ sonance sonanc
+ song song
+ songs song
+ sonnet sonnet
+ sonneting sonnet
+ sonnets sonnet
+ sons son
+ sont sont
+ sonties sonti
+ soon soon
+ sooner sooner
+ soonest soonest
+ sooth sooth
+ soothe sooth
+ soothers soother
+ soothing sooth
+ soothsay soothsai
+ soothsayer soothsay
+ sooty sooti
+ sop sop
+ sophister sophist
+ sophisticated sophist
+ sophy sophi
+ sops sop
+ sorcerer sorcer
+ sorcerers sorcer
+ sorceress sorceress
+ sorceries sorceri
+ sorcery sorceri
+ sore sore
+ sorel sorel
+ sorely sore
+ sorer sorer
+ sores sore
+ sorrier sorrier
+ sorriest sorriest
+ sorrow sorrow
+ sorrowed sorrow
+ sorrowest sorrowest
+ sorrowful sorrow
+ sorrowing sorrow
+ sorrows sorrow
+ sorry sorri
+ sort sort
+ sortance sortanc
+ sorted sort
+ sorting sort
+ sorts sort
+ sossius sossiu
+ sot sot
+ soto soto
+ sots sot
+ sottish sottish
+ soud soud
+ sought sought
+ soul soul
+ sould sould
+ soulless soulless
+ souls soul
+ sound sound
+ sounded sound
+ sounder sounder
+ soundest soundest
+ sounding sound
+ soundless soundless
+ soundly soundli
+ soundness sound
+ soundpost soundpost
+ sounds sound
+ sour sour
+ source sourc
+ sources sourc
+ sourest sourest
+ sourly sourli
+ sours sour
+ sous sou
+ souse sous
+ south south
+ southam southam
+ southampton southampton
+ southerly southerli
+ southern southern
+ southward southward
+ southwark southwark
+ southwell southwel
+ souviendrai souviendrai
+ sov sov
+ sovereign sovereign
+ sovereignest sovereignest
+ sovereignly sovereignli
+ sovereignty sovereignti
+ sovereignvours sovereignvour
+ sow sow
+ sowing sow
+ sowl sowl
+ sowter sowter
+ space space
+ spaces space
+ spacious spaciou
+ spade spade
+ spades spade
+ spain spain
+ spak spak
+ spake spake
+ spakest spakest
+ span span
+ spangle spangl
+ spangled spangl
+ spaniard spaniard
+ spaniel spaniel
+ spaniels spaniel
+ spanish spanish
+ spann spann
+ spans span
+ spar spar
+ spare spare
+ spares spare
+ sparing spare
+ sparingly sparingli
+ spark spark
+ sparkle sparkl
+ sparkles sparkl
+ sparkling sparkl
+ sparks spark
+ sparrow sparrow
+ sparrows sparrow
+ sparta sparta
+ spartan spartan
+ spavin spavin
+ spavins spavin
+ spawn spawn
+ speak speak
+ speaker speaker
+ speakers speaker
+ speakest speakest
+ speaketh speaketh
+ speaking speak
+ speaks speak
+ spear spear
+ speargrass speargrass
+ spears spear
+ special special
+ specialities special
+ specially special
+ specialties specialti
+ specialty specialti
+ specify specifi
+ speciously specious
+ spectacle spectacl
+ spectacled spectacl
+ spectacles spectacl
+ spectators spectat
+ spectatorship spectatorship
+ speculation specul
+ speculations specul
+ speculative specul
+ sped sped
+ speech speech
+ speeches speech
+ speechless speechless
+ speed speed
+ speeded speed
+ speedier speedier
+ speediest speediest
+ speedily speedili
+ speediness speedi
+ speeding speed
+ speeds speed
+ speedy speedi
+ speens speen
+ spell spell
+ spelling spell
+ spells spell
+ spelt spelt
+ spencer spencer
+ spend spend
+ spendest spendest
+ spending spend
+ spends spend
+ spendthrift spendthrift
+ spent spent
+ sperato sperato
+ sperm sperm
+ spero spero
+ sperr sperr
+ spher spher
+ sphere sphere
+ sphered sphere
+ spheres sphere
+ spherical spheric
+ sphery spheri
+ sphinx sphinx
+ spice spice
+ spiced spice
+ spicery spiceri
+ spices spice
+ spider spider
+ spiders spider
+ spied spi
+ spies spi
+ spieth spieth
+ spightfully spightfulli
+ spigot spigot
+ spill spill
+ spilling spill
+ spills spill
+ spilt spilt
+ spilth spilth
+ spin spin
+ spinii spinii
+ spinners spinner
+ spinster spinster
+ spinsters spinster
+ spire spire
+ spirit spirit
+ spirited spirit
+ spiritless spiritless
+ spirits spirit
+ spiritual spiritu
+ spiritualty spiritualti
+ spirt spirt
+ spit spit
+ spital spital
+ spite spite
+ spited spite
+ spiteful spite
+ spites spite
+ spits spit
+ spitted spit
+ spitting spit
+ splay splai
+ spleen spleen
+ spleenful spleen
+ spleens spleen
+ spleeny spleeni
+ splendour splendour
+ splenitive splenit
+ splinter splinter
+ splinters splinter
+ split split
+ splits split
+ splitted split
+ splitting split
+ spoil spoil
+ spoils spoil
+ spok spok
+ spoke spoke
+ spoken spoken
+ spokes spoke
+ spokesman spokesman
+ sponge spong
+ spongy spongi
+ spoon spoon
+ spoons spoon
+ sport sport
+ sportful sport
+ sporting sport
+ sportive sportiv
+ sports sport
+ spot spot
+ spotless spotless
+ spots spot
+ spotted spot
+ spousal spousal
+ spouse spous
+ spout spout
+ spouting spout
+ spouts spout
+ sprag sprag
+ sprang sprang
+ sprat sprat
+ sprawl sprawl
+ spray sprai
+ sprays sprai
+ spread spread
+ spreading spread
+ spreads spread
+ sprighted spright
+ sprightful spright
+ sprightly sprightli
+ sprigs sprig
+ spring spring
+ springe spring
+ springes spring
+ springeth springeth
+ springhalt springhalt
+ springing spring
+ springs spring
+ springtime springtim
+ sprinkle sprinkl
+ sprinkles sprinkl
+ sprite sprite
+ sprited sprite
+ spritely sprite
+ sprites sprite
+ spriting sprite
+ sprout sprout
+ spruce spruce
+ sprung sprung
+ spun spun
+ spur spur
+ spurio spurio
+ spurn spurn
+ spurns spurn
+ spurr spurr
+ spurrer spurrer
+ spurring spur
+ spurs spur
+ spy spy
+ spying spy
+ squabble squabbl
+ squadron squadron
+ squadrons squadron
+ squand squand
+ squar squar
+ square squar
+ squarer squarer
+ squares squar
+ squash squash
+ squeak squeak
+ squeaking squeak
+ squeal squeal
+ squealing squeal
+ squeezes squeez
+ squeezing squeez
+ squele squel
+ squier squier
+ squints squint
+ squiny squini
+ squire squir
+ squires squir
+ squirrel squirrel
+ st st
+ stab stab
+ stabb stabb
+ stabbed stab
+ stabbing stab
+ stable stabl
+ stableness stabl
+ stables stabl
+ stablish stablish
+ stablishment stablish
+ stabs stab
+ stacks stack
+ staff staff
+ stafford stafford
+ staffords stafford
+ staffordshire staffordshir
+ stag stag
+ stage stage
+ stages stage
+ stagger stagger
+ staggering stagger
+ staggers stagger
+ stags stag
+ staid staid
+ staider staider
+ stain stain
+ stained stain
+ staines stain
+ staineth staineth
+ staining stain
+ stainless stainless
+ stains stain
+ stair stair
+ stairs stair
+ stake stake
+ stakes stake
+ stale stale
+ staled stale
+ stalk stalk
+ stalking stalk
+ stalks stalk
+ stall stall
+ stalling stall
+ stalls stall
+ stamford stamford
+ stammer stammer
+ stamp stamp
+ stamped stamp
+ stamps stamp
+ stanch stanch
+ stanchless stanchless
+ stand stand
+ standard standard
+ standards standard
+ stander stander
+ standers stander
+ standest standest
+ standeth standeth
+ standing stand
+ stands stand
+ staniel staniel
+ stanley stanlei
+ stanze stanz
+ stanzo stanzo
+ stanzos stanzo
+ staple stapl
+ staples stapl
+ star star
+ stare stare
+ stared stare
+ stares stare
+ staring stare
+ starings stare
+ stark stark
+ starkly starkli
+ starlight starlight
+ starling starl
+ starr starr
+ starry starri
+ stars star
+ start start
+ started start
+ starting start
+ startingly startingli
+ startle startl
+ startles startl
+ starts start
+ starv starv
+ starve starv
+ starved starv
+ starvelackey starvelackei
+ starveling starvel
+ starveth starveth
+ starving starv
+ state state
+ statelier stateli
+ stately state
+ states state
+ statesman statesman
+ statesmen statesmen
+ statilius statiliu
+ station station
+ statist statist
+ statists statist
+ statue statu
+ statues statu
+ stature statur
+ statures statur
+ statute statut
+ statutes statut
+ stave stave
+ staves stave
+ stay stai
+ stayed stai
+ stayest stayest
+ staying stai
+ stays stai
+ stead stead
+ steaded stead
+ steadfast steadfast
+ steadier steadier
+ steads stead
+ steal steal
+ stealer stealer
+ stealers stealer
+ stealing steal
+ steals steal
+ stealth stealth
+ stealthy stealthi
+ steed steed
+ steeds steed
+ steel steel
+ steeled steel
+ steely steeli
+ steep steep
+ steeped steep
+ steeple steepl
+ steeples steepl
+ steeps steep
+ steepy steepi
+ steer steer
+ steerage steerag
+ steering steer
+ steers steer
+ stelled stell
+ stem stem
+ stemming stem
+ stench stench
+ step step
+ stepdame stepdam
+ stephano stephano
+ stephen stephen
+ stepmothers stepmoth
+ stepp stepp
+ stepping step
+ steps step
+ sterile steril
+ sterility steril
+ sterling sterl
+ stern stern
+ sternage sternag
+ sterner sterner
+ sternest sternest
+ sternness stern
+ steterat steterat
+ stew stew
+ steward steward
+ stewards steward
+ stewardship stewardship
+ stewed stew
+ stews stew
+ stick stick
+ sticking stick
+ stickler stickler
+ sticks stick
+ stiff stiff
+ stiffen stiffen
+ stiffly stiffli
+ stifle stifl
+ stifled stifl
+ stifles stifl
+ stigmatic stigmat
+ stigmatical stigmat
+ stile stile
+ still still
+ stiller stiller
+ stillest stillest
+ stillness still
+ stilly stilli
+ sting sting
+ stinging sting
+ stingless stingless
+ stings sting
+ stink stink
+ stinking stink
+ stinkingly stinkingli
+ stinks stink
+ stint stint
+ stinted stint
+ stints stint
+ stir stir
+ stirr stirr
+ stirred stir
+ stirrer stirrer
+ stirrers stirrer
+ stirreth stirreth
+ stirring stir
+ stirrup stirrup
+ stirrups stirrup
+ stirs stir
+ stitchery stitcheri
+ stitches stitch
+ stithied stithi
+ stithy stithi
+ stoccadoes stoccado
+ stoccata stoccata
+ stock stock
+ stockfish stockfish
+ stocking stock
+ stockings stock
+ stockish stockish
+ stocks stock
+ stog stog
+ stogs stog
+ stoics stoic
+ stokesly stokesli
+ stol stol
+ stole stole
+ stolen stolen
+ stolest stolest
+ stomach stomach
+ stomachers stomach
+ stomaching stomach
+ stomachs stomach
+ ston ston
+ stone stone
+ stonecutter stonecutt
+ stones stone
+ stonish stonish
+ stony stoni
+ stood stood
+ stool stool
+ stools stool
+ stoop stoop
+ stooping stoop
+ stoops stoop
+ stop stop
+ stope stope
+ stopp stopp
+ stopped stop
+ stopping stop
+ stops stop
+ stor stor
+ store store
+ storehouse storehous
+ storehouses storehous
+ stores store
+ stories stori
+ storm storm
+ stormed storm
+ storming storm
+ storms storm
+ stormy stormi
+ story stori
+ stoup stoup
+ stoups stoup
+ stout stout
+ stouter stouter
+ stoutly stoutli
+ stoutness stout
+ stover stover
+ stow stow
+ stowage stowag
+ stowed stow
+ strachy strachi
+ stragglers straggler
+ straggling straggl
+ straight straight
+ straightest straightest
+ straightway straightwai
+ strain strain
+ strained strain
+ straining strain
+ strains strain
+ strait strait
+ straited strait
+ straiter straiter
+ straitly straitli
+ straitness strait
+ straits strait
+ strand strand
+ strange strang
+ strangely strang
+ strangeness strang
+ stranger stranger
+ strangers stranger
+ strangest strangest
+ strangle strangl
+ strangled strangl
+ strangler strangler
+ strangles strangl
+ strangling strangl
+ strappado strappado
+ straps strap
+ stratagem stratagem
+ stratagems stratagem
+ stratford stratford
+ strato strato
+ straw straw
+ strawberries strawberri
+ strawberry strawberri
+ straws straw
+ strawy strawi
+ stray strai
+ straying strai
+ strays strai
+ streak streak
+ streaks streak
+ stream stream
+ streamers streamer
+ streaming stream
+ streams stream
+ streching strech
+ street street
+ streets street
+ strength strength
+ strengthen strengthen
+ strengthened strengthen
+ strengthless strengthless
+ strengths strength
+ stretch stretch
+ stretched stretch
+ stretches stretch
+ stretching stretch
+ strew strew
+ strewing strew
+ strewings strew
+ strewments strewment
+ stricken stricken
+ strict strict
+ stricter stricter
+ strictest strictest
+ strictly strictli
+ stricture strictur
+ stride stride
+ strides stride
+ striding stride
+ strife strife
+ strifes strife
+ strik strik
+ strike strike
+ strikers striker
+ strikes strike
+ strikest strikest
+ striking strike
+ string string
+ stringless stringless
+ strings string
+ strip strip
+ stripes stripe
+ stripling stripl
+ striplings stripl
+ stripp stripp
+ stripping strip
+ striv striv
+ strive strive
+ strives strive
+ striving strive
+ strok strok
+ stroke stroke
+ strokes stroke
+ strond strond
+ stronds strond
+ strong strong
+ stronger stronger
+ strongest strongest
+ strongly strongli
+ strooke strook
+ strossers strosser
+ strove strove
+ strown strown
+ stroy stroi
+ struck struck
+ strucken strucken
+ struggle struggl
+ struggles struggl
+ struggling struggl
+ strumpet strumpet
+ strumpeted strumpet
+ strumpets strumpet
+ strung strung
+ strut strut
+ struts strut
+ strutted strut
+ strutting strut
+ stubble stubbl
+ stubborn stubborn
+ stubbornest stubbornest
+ stubbornly stubbornli
+ stubbornness stubborn
+ stuck stuck
+ studded stud
+ student student
+ students student
+ studied studi
+ studies studi
+ studious studiou
+ studiously studious
+ studs stud
+ study studi
+ studying studi
+ stuff stuff
+ stuffing stuf
+ stuffs stuff
+ stumble stumbl
+ stumbled stumbl
+ stumblest stumblest
+ stumbling stumbl
+ stump stump
+ stumps stump
+ stung stung
+ stupefy stupefi
+ stupid stupid
+ stupified stupifi
+ stuprum stuprum
+ sturdy sturdi
+ sty sty
+ styga styga
+ stygian stygian
+ styl styl
+ style style
+ styx styx
+ su su
+ sub sub
+ subcontracted subcontract
+ subdu subdu
+ subdue subdu
+ subdued subdu
+ subduements subduement
+ subdues subdu
+ subduing subdu
+ subject subject
+ subjected subject
+ subjection subject
+ subjects subject
+ submerg submerg
+ submission submiss
+ submissive submiss
+ submit submit
+ submits submit
+ submitting submit
+ suborn suborn
+ subornation suborn
+ suborned suborn
+ subscrib subscrib
+ subscribe subscrib
+ subscribed subscrib
+ subscribes subscrib
+ subscription subscript
+ subsequent subsequ
+ subsidies subsidi
+ subsidy subsidi
+ subsist subsist
+ subsisting subsist
+ substance substanc
+ substances substanc
+ substantial substanti
+ substitute substitut
+ substituted substitut
+ substitutes substitut
+ substitution substitut
+ subtile subtil
+ subtilly subtilli
+ subtle subtl
+ subtleties subtleti
+ subtlety subtleti
+ subtly subtli
+ subtractors subtractor
+ suburbs suburb
+ subversion subvers
+ subverts subvert
+ succedant succed
+ succeed succe
+ succeeded succeed
+ succeeders succeed
+ succeeding succeed
+ succeeds succe
+ success success
+ successantly successantli
+ successes success
+ successful success
+ successfully successfulli
+ succession success
+ successive success
+ successively success
+ successor successor
+ successors successor
+ succour succour
+ succours succour
+ such such
+ suck suck
+ sucker sucker
+ suckers sucker
+ sucking suck
+ suckle suckl
+ sucks suck
+ sudden sudden
+ suddenly suddenli
+ sue sue
+ sued su
+ suerly suerli
+ sues sue
+ sueth sueth
+ suff suff
+ suffer suffer
+ sufferance suffer
+ sufferances suffer
+ suffered suffer
+ suffering suffer
+ suffers suffer
+ suffic suffic
+ suffice suffic
+ sufficed suffic
+ suffices suffic
+ sufficeth sufficeth
+ sufficiency suffici
+ sufficient suffici
+ sufficiently suffici
+ sufficing suffic
+ sufficit sufficit
+ suffigance suffig
+ suffocate suffoc
+ suffocating suffoc
+ suffocation suffoc
+ suffolk suffolk
+ suffrage suffrag
+ suffrages suffrag
+ sug sug
+ sugar sugar
+ sugarsop sugarsop
+ suggest suggest
+ suggested suggest
+ suggesting suggest
+ suggestion suggest
+ suggestions suggest
+ suggests suggest
+ suis sui
+ suit suit
+ suitable suitabl
+ suited suit
+ suiting suit
+ suitor suitor
+ suitors suitor
+ suits suit
+ suivez suivez
+ sullen sullen
+ sullens sullen
+ sullied sulli
+ sullies sulli
+ sully sulli
+ sulph sulph
+ sulpherous sulpher
+ sulphur sulphur
+ sulphurous sulphur
+ sultan sultan
+ sultry sultri
+ sum sum
+ sumless sumless
+ summ summ
+ summa summa
+ summary summari
+ summer summer
+ summers summer
+ summit summit
+ summon summon
+ summoners summon
+ summons summon
+ sumpter sumpter
+ sumptuous sumptuou
+ sumptuously sumptuous
+ sums sum
+ sun sun
+ sunbeams sunbeam
+ sunburning sunburn
+ sunburnt sunburnt
+ sund sund
+ sunday sundai
+ sundays sundai
+ sunder sunder
+ sunders sunder
+ sundry sundri
+ sung sung
+ sunk sunk
+ sunken sunken
+ sunny sunni
+ sunrising sunris
+ suns sun
+ sunset sunset
+ sunshine sunshin
+ sup sup
+ super super
+ superficial superfici
+ superficially superfici
+ superfluity superflu
+ superfluous superflu
+ superfluously superflu
+ superflux superflux
+ superior superior
+ supernal supern
+ supernatural supernatur
+ superpraise superprais
+ superscript superscript
+ superscription superscript
+ superserviceable superservic
+ superstition superstit
+ superstitious superstiti
+ superstitiously superstiti
+ supersubtle supersubtl
+ supervise supervis
+ supervisor supervisor
+ supp supp
+ supper supper
+ suppers supper
+ suppertime suppertim
+ supping sup
+ supplant supplant
+ supple suppl
+ suppler suppler
+ suppliance supplianc
+ suppliant suppliant
+ suppliants suppliant
+ supplicant supplic
+ supplication supplic
+ supplications supplic
+ supplie suppli
+ supplied suppli
+ supplies suppli
+ suppliest suppliest
+ supply suppli
+ supplyant supplyant
+ supplying suppli
+ supplyment supplyment
+ support support
+ supportable support
+ supportance support
+ supported support
+ supporter support
+ supporters support
+ supporting support
+ supportor supportor
+ suppos suppo
+ supposal suppos
+ suppose suppos
+ supposed suppos
+ supposes suppos
+ supposest supposest
+ supposing suppos
+ supposition supposit
+ suppress suppress
+ suppressed suppress
+ suppresseth suppresseth
+ supremacy supremaci
+ supreme suprem
+ sups sup
+ sur sur
+ surance suranc
+ surcease surceas
+ surd surd
+ sure sure
+ surecard surecard
+ surely sure
+ surer surer
+ surest surest
+ sureties sureti
+ surety sureti
+ surfeit surfeit
+ surfeited surfeit
+ surfeiter surfeit
+ surfeiting surfeit
+ surfeits surfeit
+ surge surg
+ surgeon surgeon
+ surgeons surgeon
+ surgere surger
+ surgery surgeri
+ surges surg
+ surly surli
+ surmis surmi
+ surmise surmis
+ surmised surmis
+ surmises surmis
+ surmount surmount
+ surmounted surmount
+ surmounts surmount
+ surnam surnam
+ surname surnam
+ surnamed surnam
+ surpasseth surpasseth
+ surpassing surpass
+ surplice surplic
+ surplus surplu
+ surpris surpri
+ surprise surpris
+ surprised surpris
+ surrender surrend
+ surrey surrei
+ surreys surrei
+ survey survei
+ surveyest surveyest
+ surveying survei
+ surveyor surveyor
+ surveyors surveyor
+ surveys survei
+ survive surviv
+ survives surviv
+ survivor survivor
+ susan susan
+ suspect suspect
+ suspected suspect
+ suspecting suspect
+ suspects suspect
+ suspend suspend
+ suspense suspens
+ suspicion suspicion
+ suspicions suspicion
+ suspicious suspici
+ suspiration suspir
+ suspire suspir
+ sust sust
+ sustain sustain
+ sustaining sustain
+ sutler sutler
+ sutton sutton
+ suum suum
+ swabber swabber
+ swaddling swaddl
+ swag swag
+ swagg swagg
+ swagger swagger
+ swaggerer swagger
+ swaggerers swagger
+ swaggering swagger
+ swain swain
+ swains swain
+ swallow swallow
+ swallowed swallow
+ swallowing swallow
+ swallows swallow
+ swam swam
+ swan swan
+ swans swan
+ sward sward
+ sware sware
+ swarm swarm
+ swarming swarm
+ swart swart
+ swarth swarth
+ swarths swarth
+ swarthy swarthi
+ swashers swasher
+ swashing swash
+ swath swath
+ swathing swath
+ swathling swathl
+ sway swai
+ swaying swai
+ sways swai
+ swear swear
+ swearer swearer
+ swearers swearer
+ swearest swearest
+ swearing swear
+ swearings swear
+ swears swear
+ sweat sweat
+ sweaten sweaten
+ sweating sweat
+ sweats sweat
+ sweaty sweati
+ sweep sweep
+ sweepers sweeper
+ sweeps sweep
+ sweet sweet
+ sweeten sweeten
+ sweetens sweeten
+ sweeter sweeter
+ sweetest sweetest
+ sweetheart sweetheart
+ sweeting sweet
+ sweetly sweetli
+ sweetmeats sweetmeat
+ sweetness sweet
+ sweets sweet
+ swell swell
+ swelling swell
+ swellings swell
+ swells swell
+ swelter swelter
+ sweno sweno
+ swept swept
+ swerve swerv
+ swerver swerver
+ swerving swerv
+ swift swift
+ swifter swifter
+ swiftest swiftest
+ swiftly swiftli
+ swiftness swift
+ swill swill
+ swills swill
+ swim swim
+ swimmer swimmer
+ swimmers swimmer
+ swimming swim
+ swims swim
+ swine swine
+ swineherds swineherd
+ swing swing
+ swinge swing
+ swinish swinish
+ swinstead swinstead
+ switches switch
+ swits swit
+ switzers switzer
+ swol swol
+ swoll swoll
+ swoln swoln
+ swoon swoon
+ swooned swoon
+ swooning swoon
+ swoons swoon
+ swoop swoop
+ swoopstake swoopstak
+ swor swor
+ sword sword
+ sworder sworder
+ swords sword
+ swore swore
+ sworn sworn
+ swounded swound
+ swounds swound
+ swum swum
+ swung swung
+ sy sy
+ sycamore sycamor
+ sycorax sycorax
+ sylla sylla
+ syllable syllabl
+ syllables syllabl
+ syllogism syllog
+ symbols symbol
+ sympathise sympathis
+ sympathiz sympathiz
+ sympathize sympath
+ sympathized sympath
+ sympathy sympathi
+ synagogue synagogu
+ synod synod
+ synods synod
+ syracuse syracus
+ syracusian syracusian
+ syracusians syracusian
+ syria syria
+ syrups syrup
+ t t
+ ta ta
+ taber taber
+ table tabl
+ tabled tabl
+ tables tabl
+ tablet tablet
+ tabor tabor
+ taborer tabor
+ tabors tabor
+ tabourines tabourin
+ taciturnity taciturn
+ tack tack
+ tackle tackl
+ tackled tackl
+ tackles tackl
+ tackling tackl
+ tacklings tackl
+ taddle taddl
+ tadpole tadpol
+ taffeta taffeta
+ taffety taffeti
+ tag tag
+ tagrag tagrag
+ tah tah
+ tail tail
+ tailor tailor
+ tailors tailor
+ tails tail
+ taint taint
+ tainted taint
+ tainting taint
+ taints taint
+ tainture taintur
+ tak tak
+ take take
+ taken taken
+ taker taker
+ takes take
+ takest takest
+ taketh taketh
+ taking take
+ tal tal
+ talbot talbot
+ talbotites talbotit
+ talbots talbot
+ tale tale
+ talent talent
+ talents talent
+ taleporter taleport
+ tales tale
+ talk talk
+ talked talk
+ talker talker
+ talkers talker
+ talkest talkest
+ talking talk
+ talks talk
+ tall tall
+ taller taller
+ tallest tallest
+ tallies talli
+ tallow tallow
+ tally talli
+ talons talon
+ tam tam
+ tambourines tambourin
+ tame tame
+ tamed tame
+ tamely tame
+ tameness tame
+ tamer tamer
+ tames tame
+ taming tame
+ tamora tamora
+ tamworth tamworth
+ tan tan
+ tang tang
+ tangle tangl
+ tangled tangl
+ tank tank
+ tanlings tanl
+ tann tann
+ tanned tan
+ tanner tanner
+ tanquam tanquam
+ tanta tanta
+ tantaene tantaen
+ tap tap
+ tape tape
+ taper taper
+ tapers taper
+ tapestries tapestri
+ tapestry tapestri
+ taphouse taphous
+ tapp tapp
+ tapster tapster
+ tapsters tapster
+ tar tar
+ tardied tardi
+ tardily tardili
+ tardiness tardi
+ tardy tardi
+ tarentum tarentum
+ targe targ
+ targes targ
+ target target
+ targets target
+ tarpeian tarpeian
+ tarquin tarquin
+ tarquins tarquin
+ tarr tarr
+ tarre tarr
+ tarriance tarrianc
+ tarried tarri
+ tarries tarri
+ tarry tarri
+ tarrying tarri
+ tart tart
+ tartar tartar
+ tartars tartar
+ tartly tartli
+ tartness tart
+ task task
+ tasker tasker
+ tasking task
+ tasks task
+ tassel tassel
+ taste tast
+ tasted tast
+ tastes tast
+ tasting tast
+ tatt tatt
+ tatter tatter
+ tattered tatter
+ tatters tatter
+ tattle tattl
+ tattling tattl
+ tattlings tattl
+ taught taught
+ taunt taunt
+ taunted taunt
+ taunting taunt
+ tauntingly tauntingli
+ taunts taunt
+ taurus tauru
+ tavern tavern
+ taverns tavern
+ tavy tavi
+ tawdry tawdri
+ tawny tawni
+ tax tax
+ taxation taxat
+ taxations taxat
+ taxes tax
+ taxing tax
+ tc tc
+ te te
+ teach teach
+ teacher teacher
+ teachers teacher
+ teaches teach
+ teachest teachest
+ teacheth teacheth
+ teaching teach
+ team team
+ tear tear
+ tearful tear
+ tearing tear
+ tears tear
+ tearsheet tearsheet
+ teat teat
+ tedious tediou
+ tediously tedious
+ tediousness tedious
+ teem teem
+ teeming teem
+ teems teem
+ teen teen
+ teeth teeth
+ teipsum teipsum
+ telamon telamon
+ telamonius telamoniu
+ tell tell
+ teller teller
+ telling tell
+ tells tell
+ tellus tellu
+ temp temp
+ temper temper
+ temperality temper
+ temperance temper
+ temperate temper
+ temperately temper
+ tempers temper
+ tempest tempest
+ tempests tempest
+ tempestuous tempestu
+ temple templ
+ temples templ
+ temporal tempor
+ temporary temporari
+ temporiz temporiz
+ temporize tempor
+ temporizer tempor
+ temps temp
+ tempt tempt
+ temptation temptat
+ temptations temptat
+ tempted tempt
+ tempter tempter
+ tempters tempter
+ tempteth tempteth
+ tempting tempt
+ tempts tempt
+ ten ten
+ tenable tenabl
+ tenant tenant
+ tenantius tenantiu
+ tenantless tenantless
+ tenants tenant
+ tench tench
+ tend tend
+ tendance tendanc
+ tended tend
+ tender tender
+ tendered tender
+ tenderly tenderli
+ tenderness tender
+ tenders tender
+ tending tend
+ tends tend
+ tenedos tenedo
+ tenement tenement
+ tenements tenement
+ tenfold tenfold
+ tennis tenni
+ tenour tenour
+ tenours tenour
+ tens ten
+ tent tent
+ tented tent
+ tenth tenth
+ tenths tenth
+ tents tent
+ tenure tenur
+ tenures tenur
+ tercel tercel
+ tereus tereu
+ term term
+ termagant termag
+ termed term
+ terminations termin
+ termless termless
+ terms term
+ terra terra
+ terrace terrac
+ terram terram
+ terras terra
+ terre terr
+ terrene terren
+ terrestrial terrestri
+ terrible terribl
+ terribly terribl
+ territories territori
+ territory territori
+ terror terror
+ terrors terror
+ tertian tertian
+ tertio tertio
+ test test
+ testament testament
+ tested test
+ tester tester
+ testern testern
+ testify testifi
+ testimonied testimoni
+ testimonies testimoni
+ testimony testimoni
+ testiness testi
+ testril testril
+ testy testi
+ tetchy tetchi
+ tether tether
+ tetter tetter
+ tevil tevil
+ tewksbury tewksburi
+ text text
+ tgv tgv
+ th th
+ thaes thae
+ thames thame
+ than than
+ thane thane
+ thanes thane
+ thank thank
+ thanked thank
+ thankful thank
+ thankfully thankfulli
+ thankfulness thank
+ thanking thank
+ thankings thank
+ thankless thankless
+ thanks thank
+ thanksgiving thanksgiv
+ thasos thaso
+ that that
+ thatch thatch
+ thaw thaw
+ thawing thaw
+ thaws thaw
+ the the
+ theatre theatr
+ theban theban
+ thebes thebe
+ thee thee
+ theft theft
+ thefts theft
+ thein thein
+ their their
+ theirs their
+ theise theis
+ them them
+ theme theme
+ themes theme
+ themselves themselv
+ then then
+ thence thenc
+ thenceforth thenceforth
+ theoric theoric
+ there there
+ thereabout thereabout
+ thereabouts thereabout
+ thereafter thereaft
+ thereat thereat
+ thereby therebi
+ therefore therefor
+ therein therein
+ thereof thereof
+ thereon thereon
+ thereto thereto
+ thereunto thereunto
+ thereupon thereupon
+ therewith therewith
+ therewithal therewith
+ thersites thersit
+ these these
+ theseus theseu
+ thessalian thessalian
+ thessaly thessali
+ thetis theti
+ thews thew
+ they thei
+ thick thick
+ thicken thicken
+ thickens thicken
+ thicker thicker
+ thickest thickest
+ thicket thicket
+ thickskin thickskin
+ thief thief
+ thievery thieveri
+ thieves thiev
+ thievish thievish
+ thigh thigh
+ thighs thigh
+ thimble thimbl
+ thimbles thimbl
+ thin thin
+ thine thine
+ thing thing
+ things thing
+ think think
+ thinkest thinkest
+ thinking think
+ thinkings think
+ thinks think
+ thinkst thinkst
+ thinly thinli
+ third third
+ thirdly thirdli
+ thirds third
+ thirst thirst
+ thirsting thirst
+ thirsts thirst
+ thirsty thirsti
+ thirteen thirteen
+ thirties thirti
+ thirtieth thirtieth
+ thirty thirti
+ this thi
+ thisby thisbi
+ thisne thisn
+ thistle thistl
+ thistles thistl
+ thither thither
+ thitherward thitherward
+ thoas thoa
+ thomas thoma
+ thorn thorn
+ thorns thorn
+ thorny thorni
+ thorough thorough
+ thoroughly thoroughli
+ those those
+ thou thou
+ though though
+ thought thought
+ thoughtful thought
+ thoughts thought
+ thousand thousand
+ thousands thousand
+ thracian thracian
+ thraldom thraldom
+ thrall thrall
+ thralled thrall
+ thralls thrall
+ thrash thrash
+ thrasonical thrason
+ thread thread
+ threadbare threadbar
+ threaden threaden
+ threading thread
+ threat threat
+ threaten threaten
+ threatening threaten
+ threatens threaten
+ threatest threatest
+ threats threat
+ three three
+ threefold threefold
+ threepence threepenc
+ threepile threepil
+ threes three
+ threescore threescor
+ thresher thresher
+ threshold threshold
+ threw threw
+ thrice thrice
+ thrift thrift
+ thriftless thriftless
+ thrifts thrift
+ thrifty thrifti
+ thrill thrill
+ thrilling thrill
+ thrills thrill
+ thrive thrive
+ thrived thrive
+ thrivers thriver
+ thrives thrive
+ thriving thrive
+ throat throat
+ throats throat
+ throbbing throb
+ throbs throb
+ throca throca
+ throe throe
+ throes throe
+ thromuldo thromuldo
+ thron thron
+ throne throne
+ throned throne
+ thrones throne
+ throng throng
+ thronging throng
+ throngs throng
+ throstle throstl
+ throttle throttl
+ through through
+ throughfare throughfar
+ throughfares throughfar
+ throughly throughli
+ throughout throughout
+ throw throw
+ thrower thrower
+ throwest throwest
+ throwing throw
+ thrown thrown
+ throws throw
+ thrum thrum
+ thrumm thrumm
+ thrush thrush
+ thrust thrust
+ thrusteth thrusteth
+ thrusting thrust
+ thrusts thrust
+ thumb thumb
+ thumbs thumb
+ thump thump
+ thund thund
+ thunder thunder
+ thunderbolt thunderbolt
+ thunderbolts thunderbolt
+ thunderer thunder
+ thunders thunder
+ thunderstone thunderston
+ thunderstroke thunderstrok
+ thurio thurio
+ thursday thursdai
+ thus thu
+ thwack thwack
+ thwart thwart
+ thwarted thwart
+ thwarting thwart
+ thwartings thwart
+ thy thy
+ thyme thyme
+ thymus thymu
+ thyreus thyreu
+ thyself thyself
+ ti ti
+ tib tib
+ tiber tiber
+ tiberio tiberio
+ tibey tibei
+ ticed tice
+ tick tick
+ tickl tickl
+ tickle tickl
+ tickled tickl
+ tickles tickl
+ tickling tickl
+ ticklish ticklish
+ tiddle tiddl
+ tide tide
+ tides tide
+ tidings tide
+ tidy tidi
+ tie tie
+ tied ti
+ ties ti
+ tiff tiff
+ tiger tiger
+ tigers tiger
+ tight tight
+ tightly tightli
+ tike tike
+ til til
+ tile tile
+ till till
+ tillage tillag
+ tilly tilli
+ tilt tilt
+ tilter tilter
+ tilth tilth
+ tilting tilt
+ tilts tilt
+ tiltyard tiltyard
+ tim tim
+ timandra timandra
+ timber timber
+ time time
+ timeless timeless
+ timelier timeli
+ timely time
+ times time
+ timon timon
+ timor timor
+ timorous timor
+ timorously timor
+ tinct tinct
+ tincture tinctur
+ tinctures tinctur
+ tinder tinder
+ tingling tingl
+ tinker tinker
+ tinkers tinker
+ tinsel tinsel
+ tiny tini
+ tip tip
+ tipp tipp
+ tippling tippl
+ tips tip
+ tipsy tipsi
+ tiptoe tipto
+ tir tir
+ tire tire
+ tired tire
+ tires tire
+ tirest tirest
+ tiring tire
+ tirra tirra
+ tirrits tirrit
+ tis ti
+ tish tish
+ tisick tisick
+ tissue tissu
+ titan titan
+ titania titania
+ tithe tith
+ tithed tith
+ tithing tith
+ titinius titiniu
+ title titl
+ titled titl
+ titleless titleless
+ titles titl
+ tittle tittl
+ tittles tittl
+ titular titular
+ titus titu
+ tn tn
+ to to
+ toad toad
+ toads toad
+ toadstool toadstool
+ toast toast
+ toasted toast
+ toasting toast
+ toasts toast
+ toaze toaz
+ toby tobi
+ tock tock
+ tod tod
+ today todai
+ todpole todpol
+ tods tod
+ toe toe
+ toes toe
+ tofore tofor
+ toge toge
+ toged toge
+ together togeth
+ toil toil
+ toiled toil
+ toiling toil
+ toils toil
+ token token
+ tokens token
+ told told
+ toledo toledo
+ tolerable toler
+ toll toll
+ tolling toll
+ tom tom
+ tomb tomb
+ tombe tomb
+ tombed tomb
+ tombless tombless
+ tomboys tomboi
+ tombs tomb
+ tomorrow tomorrow
+ tomyris tomyri
+ ton ton
+ tongs tong
+ tongu tongu
+ tongue tongu
+ tongued tongu
+ tongueless tongueless
+ tongues tongu
+ tonight tonight
+ too too
+ took took
+ tool tool
+ tools tool
+ tooth tooth
+ toothache toothach
+ toothpick toothpick
+ toothpicker toothpick
+ top top
+ topas topa
+ topful top
+ topgallant topgal
+ topless topless
+ topmast topmast
+ topp topp
+ topping top
+ topple toppl
+ topples toppl
+ tops top
+ topsail topsail
+ topsy topsi
+ torch torch
+ torchbearer torchbear
+ torchbearers torchbear
+ torcher torcher
+ torches torch
+ torchlight torchlight
+ tore tore
+ torment torment
+ tormenta tormenta
+ tormente torment
+ tormented torment
+ tormenting torment
+ tormentors tormentor
+ torments torment
+ torn torn
+ torrent torrent
+ tortive tortiv
+ tortoise tortois
+ tortur tortur
+ torture tortur
+ tortured tortur
+ torturer tortur
+ torturers tortur
+ tortures tortur
+ torturest torturest
+ torturing tortur
+ toryne toryn
+ toss toss
+ tossed toss
+ tosseth tosseth
+ tossing toss
+ tot tot
+ total total
+ totally total
+ tott tott
+ tottered totter
+ totters totter
+ tou tou
+ touch touch
+ touched touch
+ touches touch
+ toucheth toucheth
+ touching touch
+ touchstone touchston
+ tough tough
+ tougher tougher
+ toughness tough
+ touraine tourain
+ tournaments tournament
+ tours tour
+ tous tou
+ tout tout
+ touze touz
+ tow tow
+ toward toward
+ towardly towardli
+ towards toward
+ tower tower
+ towering tower
+ towers tower
+ town town
+ towns town
+ township township
+ townsman townsman
+ townsmen townsmen
+ towton towton
+ toy toi
+ toys toi
+ trace trace
+ traces trace
+ track track
+ tract tract
+ tractable tractabl
+ trade trade
+ traded trade
+ traders trader
+ trades trade
+ tradesman tradesman
+ tradesmen tradesmen
+ trading trade
+ tradition tradit
+ traditional tradit
+ traduc traduc
+ traduced traduc
+ traducement traduc
+ traffic traffic
+ traffickers traffick
+ traffics traffic
+ tragedian tragedian
+ tragedians tragedian
+ tragedies tragedi
+ tragedy tragedi
+ tragic tragic
+ tragical tragic
+ trail trail
+ train train
+ trained train
+ training train
+ trains train
+ trait trait
+ traitor traitor
+ traitorly traitorli
+ traitorous traitor
+ traitorously traitor
+ traitors traitor
+ traitress traitress
+ traject traject
+ trammel trammel
+ trample trampl
+ trampled trampl
+ trampling trampl
+ tranc tranc
+ trance tranc
+ tranio tranio
+ tranquil tranquil
+ tranquillity tranquil
+ transcendence transcend
+ transcends transcend
+ transferred transfer
+ transfigur transfigur
+ transfix transfix
+ transform transform
+ transformation transform
+ transformations transform
+ transformed transform
+ transgress transgress
+ transgresses transgress
+ transgressing transgress
+ transgression transgress
+ translate translat
+ translated translat
+ translates translat
+ translation translat
+ transmigrates transmigr
+ transmutation transmut
+ transparent transpar
+ transport transport
+ transportance transport
+ transported transport
+ transporting transport
+ transports transport
+ transpose transpos
+ transshape transshap
+ trap trap
+ trapp trapp
+ trappings trap
+ traps trap
+ trash trash
+ travail travail
+ travails travail
+ travel travel
+ traveler travel
+ traveling travel
+ travell travel
+ travelled travel
+ traveller travel
+ travellers travel
+ travellest travellest
+ travelling travel
+ travels travel
+ travers traver
+ traverse travers
+ tray trai
+ treacherous treacher
+ treacherously treacher
+ treachers treacher
+ treachery treacheri
+ tread tread
+ treading tread
+ treads tread
+ treason treason
+ treasonable treason
+ treasonous treason
+ treasons treason
+ treasure treasur
+ treasurer treasur
+ treasures treasur
+ treasuries treasuri
+ treasury treasuri
+ treat treat
+ treaties treati
+ treatise treatis
+ treats treat
+ treaty treati
+ treble trebl
+ trebled trebl
+ trebles trebl
+ trebonius treboniu
+ tree tree
+ trees tree
+ tremble trembl
+ trembled trembl
+ trembles trembl
+ tremblest tremblest
+ trembling trembl
+ tremblingly tremblingli
+ tremor tremor
+ trempling trempl
+ trench trench
+ trenchant trenchant
+ trenched trench
+ trencher trencher
+ trenchering trencher
+ trencherman trencherman
+ trenchers trencher
+ trenches trench
+ trenching trench
+ trent trent
+ tres tre
+ trespass trespass
+ trespasses trespass
+ tressel tressel
+ tresses tress
+ treys trei
+ trial trial
+ trials trial
+ trib trib
+ tribe tribe
+ tribes tribe
+ tribulation tribul
+ tribunal tribun
+ tribune tribun
+ tribunes tribun
+ tributaries tributari
+ tributary tributari
+ tribute tribut
+ tributes tribut
+ trice trice
+ trick trick
+ tricking trick
+ trickling trickl
+ tricks trick
+ tricksy tricksi
+ trident trident
+ tried tri
+ trier trier
+ trifle trifl
+ trifled trifl
+ trifler trifler
+ trifles trifl
+ trifling trifl
+ trigon trigon
+ trill trill
+ trim trim
+ trimly trimli
+ trimm trimm
+ trimmed trim
+ trimming trim
+ trims trim
+ trinculo trinculo
+ trinculos trinculo
+ trinkets trinket
+ trip trip
+ tripartite tripartit
+ tripe tripe
+ triple tripl
+ triplex triplex
+ tripoli tripoli
+ tripolis tripoli
+ tripp tripp
+ tripping trip
+ trippingly trippingli
+ trips trip
+ tristful trist
+ triton triton
+ triumph triumph
+ triumphant triumphant
+ triumphantly triumphantli
+ triumpher triumpher
+ triumphers triumpher
+ triumphing triumph
+ triumphs triumph
+ triumvir triumvir
+ triumvirate triumvir
+ triumvirs triumvir
+ triumviry triumviri
+ trivial trivial
+ troat troat
+ trod trod
+ trodden trodden
+ troiant troiant
+ troien troien
+ troilus troilu
+ troiluses troilus
+ trojan trojan
+ trojans trojan
+ troll troll
+ tromperies tromperi
+ trompet trompet
+ troop troop
+ trooping troop
+ troops troop
+ trop trop
+ trophies trophi
+ trophy trophi
+ tropically tropic
+ trot trot
+ troth troth
+ trothed troth
+ troths troth
+ trots trot
+ trotting trot
+ trouble troubl
+ troubled troubl
+ troubler troubler
+ troubles troubl
+ troublesome troublesom
+ troublest troublest
+ troublous troublou
+ trough trough
+ trout trout
+ trouts trout
+ trovato trovato
+ trow trow
+ trowel trowel
+ trowest trowest
+ troy troi
+ troyan troyan
+ troyans troyan
+ truant truant
+ truce truce
+ truckle truckl
+ trudge trudg
+ true true
+ trueborn trueborn
+ truepenny truepenni
+ truer truer
+ truest truest
+ truie truie
+ trull trull
+ trulls trull
+ truly truli
+ trump trump
+ trumpery trumperi
+ trumpet trumpet
+ trumpeter trumpet
+ trumpeters trumpet
+ trumpets trumpet
+ truncheon truncheon
+ truncheoners truncheon
+ trundle trundl
+ trunk trunk
+ trunks trunk
+ trust trust
+ trusted trust
+ truster truster
+ trusters truster
+ trusting trust
+ trusts trust
+ trusty trusti
+ truth truth
+ truths truth
+ try try
+ ts ts
+ tu tu
+ tuae tuae
+ tub tub
+ tubal tubal
+ tubs tub
+ tuck tuck
+ tucket tucket
+ tuesday tuesdai
+ tuft tuft
+ tufts tuft
+ tug tug
+ tugg tugg
+ tugging tug
+ tuition tuition
+ tullus tullu
+ tully tulli
+ tumble tumbl
+ tumbled tumbl
+ tumbler tumbler
+ tumbling tumbl
+ tumult tumult
+ tumultuous tumultu
+ tun tun
+ tune tune
+ tuneable tuneabl
+ tuned tune
+ tuners tuner
+ tunes tune
+ tunis tuni
+ tuns tun
+ tupping tup
+ turban turban
+ turbans turban
+ turbulence turbul
+ turbulent turbul
+ turd turd
+ turf turf
+ turfy turfi
+ turk turk
+ turkey turkei
+ turkeys turkei
+ turkish turkish
+ turks turk
+ turlygod turlygod
+ turmoil turmoil
+ turmoiled turmoil
+ turn turn
+ turnbull turnbul
+ turncoat turncoat
+ turncoats turncoat
+ turned turn
+ turneth turneth
+ turning turn
+ turnips turnip
+ turns turn
+ turph turph
+ turpitude turpitud
+ turquoise turquois
+ turret turret
+ turrets turret
+ turtle turtl
+ turtles turtl
+ turvy turvi
+ tuscan tuscan
+ tush tush
+ tut tut
+ tutor tutor
+ tutored tutor
+ tutors tutor
+ tutto tutto
+ twain twain
+ twang twang
+ twangling twangl
+ twas twa
+ tway twai
+ tweaks tweak
+ tween tween
+ twelfth twelfth
+ twelve twelv
+ twelvemonth twelvemonth
+ twentieth twentieth
+ twenty twenti
+ twere twere
+ twice twice
+ twig twig
+ twiggen twiggen
+ twigs twig
+ twilight twilight
+ twill twill
+ twilled twill
+ twin twin
+ twine twine
+ twink twink
+ twinkle twinkl
+ twinkled twinkl
+ twinkling twinkl
+ twinn twinn
+ twins twin
+ twire twire
+ twist twist
+ twisted twist
+ twit twit
+ twits twit
+ twitting twit
+ twixt twixt
+ two two
+ twofold twofold
+ twopence twopenc
+ twopences twopenc
+ twos two
+ twould twould
+ tyb tyb
+ tybalt tybalt
+ tybalts tybalt
+ tyburn tyburn
+ tying ty
+ tyke tyke
+ tymbria tymbria
+ type type
+ types type
+ typhon typhon
+ tyrannical tyrann
+ tyrannically tyrann
+ tyrannize tyrann
+ tyrannous tyrann
+ tyranny tyranni
+ tyrant tyrant
+ tyrants tyrant
+ tyrian tyrian
+ tyrrel tyrrel
+ u u
+ ubique ubiqu
+ udders udder
+ udge udg
+ uds ud
+ uglier uglier
+ ugliest ugliest
+ ugly ugli
+ ulcer ulcer
+ ulcerous ulcer
+ ulysses ulyss
+ um um
+ umber umber
+ umbra umbra
+ umbrage umbrag
+ umfrevile umfrevil
+ umpire umpir
+ umpires umpir
+ un un
+ unable unabl
+ unaccommodated unaccommod
+ unaccompanied unaccompani
+ unaccustom unaccustom
+ unaching unach
+ unacquainted unacquaint
+ unactive unact
+ unadvis unadvi
+ unadvised unadvis
+ unadvisedly unadvisedli
+ unagreeable unagre
+ unanel unanel
+ unanswer unansw
+ unappeas unappea
+ unapproved unapprov
+ unapt unapt
+ unaptness unapt
+ unarm unarm
+ unarmed unarm
+ unarms unarm
+ unassail unassail
+ unassailable unassail
+ unattainted unattaint
+ unattempted unattempt
+ unattended unattend
+ unauspicious unauspici
+ unauthorized unauthor
+ unavoided unavoid
+ unawares unawar
+ unback unback
+ unbak unbak
+ unbanded unband
+ unbar unbar
+ unbarb unbarb
+ unbashful unbash
+ unbated unbat
+ unbatter unbatt
+ unbecoming unbecom
+ unbefitting unbefit
+ unbegot unbegot
+ unbegotten unbegotten
+ unbelieved unbeliev
+ unbend unbend
+ unbent unbent
+ unbewail unbewail
+ unbid unbid
+ unbidden unbidden
+ unbind unbind
+ unbinds unbind
+ unbitted unbit
+ unbless unbless
+ unblest unblest
+ unbloodied unbloodi
+ unblown unblown
+ unbodied unbodi
+ unbolt unbolt
+ unbolted unbolt
+ unbonneted unbonnet
+ unbookish unbookish
+ unborn unborn
+ unbosom unbosom
+ unbound unbound
+ unbounded unbound
+ unbow unbow
+ unbowed unbow
+ unbrac unbrac
+ unbraced unbrac
+ unbraided unbraid
+ unbreathed unbreath
+ unbred unbr
+ unbreech unbreech
+ unbridled unbridl
+ unbroke unbrok
+ unbruis unbrui
+ unbruised unbruis
+ unbuckle unbuckl
+ unbuckles unbuckl
+ unbuckling unbuckl
+ unbuild unbuild
+ unburden unburden
+ unburdens unburden
+ unburied unburi
+ unburnt unburnt
+ unburthen unburthen
+ unbutton unbutton
+ unbuttoning unbutton
+ uncapable uncap
+ uncape uncap
+ uncase uncas
+ uncasing uncas
+ uncaught uncaught
+ uncertain uncertain
+ uncertainty uncertainti
+ unchain unchain
+ unchanging unchang
+ uncharge uncharg
+ uncharged uncharg
+ uncharitably uncharit
+ unchary unchari
+ unchaste unchast
+ uncheck uncheck
+ unchilded unchild
+ uncivil uncivil
+ unclaim unclaim
+ unclasp unclasp
+ uncle uncl
+ unclean unclean
+ uncleanliness uncleanli
+ uncleanly uncleanli
+ uncleanness unclean
+ uncles uncl
+ unclew unclew
+ unclog unclog
+ uncoined uncoin
+ uncolted uncolt
+ uncomeliness uncomeli
+ uncomfortable uncomfort
+ uncompassionate uncompassion
+ uncomprehensive uncomprehens
+ unconfinable unconfin
+ unconfirm unconfirm
+ unconfirmed unconfirm
+ unconquer unconqu
+ unconquered unconqu
+ unconsidered unconsid
+ unconstant unconst
+ unconstrain unconstrain
+ unconstrained unconstrain
+ uncontemn uncontemn
+ uncontroll uncontrol
+ uncorrected uncorrect
+ uncounted uncount
+ uncouple uncoupl
+ uncourteous uncourt
+ uncouth uncouth
+ uncover uncov
+ uncovered uncov
+ uncropped uncrop
+ uncross uncross
+ uncrown uncrown
+ unction unction
+ unctuous unctuou
+ uncuckolded uncuckold
+ uncurable uncur
+ uncurbable uncurb
+ uncurbed uncurb
+ uncurls uncurl
+ uncurrent uncurr
+ uncurse uncurs
+ undaunted undaunt
+ undeaf undeaf
+ undeck undeck
+ undeeded undeed
+ under under
+ underbearing underbear
+ underborne underborn
+ undercrest undercrest
+ underfoot underfoot
+ undergo undergo
+ undergoes undergo
+ undergoing undergo
+ undergone undergon
+ underground underground
+ underhand underhand
+ underlings underl
+ undermine undermin
+ underminers undermin
+ underneath underneath
+ underprizing underpr
+ underprop underprop
+ understand understand
+ understandeth understandeth
+ understanding understand
+ understandings understand
+ understands understand
+ understood understood
+ underta underta
+ undertake undertak
+ undertakeing undertak
+ undertaker undertak
+ undertakes undertak
+ undertaking undertak
+ undertakings undertak
+ undertook undertook
+ undervalu undervalu
+ undervalued undervalu
+ underwent underw
+ underwrit underwrit
+ underwrite underwrit
+ undescried undescri
+ undeserved undeserv
+ undeserver undeserv
+ undeservers undeserv
+ undeserving undeserv
+ undetermin undetermin
+ undid undid
+ undinted undint
+ undiscernible undiscern
+ undiscover undiscov
+ undishonoured undishonour
+ undispos undispo
+ undistinguishable undistinguish
+ undistinguished undistinguish
+ undividable undivid
+ undivided undivid
+ undivulged undivulg
+ undo undo
+ undoes undo
+ undoing undo
+ undone undon
+ undoubted undoubt
+ undoubtedly undoubtedli
+ undream undream
+ undress undress
+ undressed undress
+ undrown undrown
+ unduteous undut
+ undutiful unduti
+ une un
+ uneared unear
+ unearned unearn
+ unearthly unearthli
+ uneasines uneasin
+ uneasy uneasi
+ uneath uneath
+ uneducated uneduc
+ uneffectual uneffectu
+ unelected unelect
+ unequal unequ
+ uneven uneven
+ unexamin unexamin
+ unexecuted unexecut
+ unexpected unexpect
+ unexperienc unexperienc
+ unexperient unexperi
+ unexpressive unexpress
+ unfair unfair
+ unfaithful unfaith
+ unfallible unfal
+ unfam unfam
+ unfashionable unfashion
+ unfasten unfasten
+ unfather unfath
+ unfathered unfath
+ unfed unf
+ unfeed unfe
+ unfeeling unfeel
+ unfeigned unfeign
+ unfeignedly unfeignedli
+ unfellowed unfellow
+ unfelt unfelt
+ unfenced unfenc
+ unfilial unfili
+ unfill unfil
+ unfinish unfinish
+ unfirm unfirm
+ unfit unfit
+ unfitness unfit
+ unfix unfix
+ unfledg unfledg
+ unfold unfold
+ unfolded unfold
+ unfoldeth unfoldeth
+ unfolding unfold
+ unfolds unfold
+ unfool unfool
+ unforc unforc
+ unforced unforc
+ unforfeited unforfeit
+ unfortified unfortifi
+ unfortunate unfortun
+ unfought unfought
+ unfrequented unfrequ
+ unfriended unfriend
+ unfurnish unfurnish
+ ungain ungain
+ ungalled ungal
+ ungart ungart
+ ungarter ungart
+ ungenitur ungenitur
+ ungentle ungentl
+ ungentleness ungentl
+ ungently ungent
+ ungird ungird
+ ungodly ungodli
+ ungor ungor
+ ungot ungot
+ ungotten ungotten
+ ungovern ungovern
+ ungracious ungraci
+ ungrateful ungrat
+ ungravely ungrav
+ ungrown ungrown
+ unguarded unguard
+ unguem unguem
+ unguided unguid
+ unhack unhack
+ unhair unhair
+ unhallow unhallow
+ unhallowed unhallow
+ unhand unhand
+ unhandled unhandl
+ unhandsome unhandsom
+ unhang unhang
+ unhappied unhappi
+ unhappily unhappili
+ unhappiness unhappi
+ unhappy unhappi
+ unhardened unharden
+ unharm unharm
+ unhatch unhatch
+ unheard unheard
+ unhearts unheart
+ unheedful unheed
+ unheedfully unheedfulli
+ unheedy unheedi
+ unhelpful unhelp
+ unhidden unhidden
+ unholy unholi
+ unhop unhop
+ unhopefullest unhopefullest
+ unhorse unhors
+ unhospitable unhospit
+ unhous unhou
+ unhoused unhous
+ unhurtful unhurt
+ unicorn unicorn
+ unicorns unicorn
+ unimproved unimprov
+ uninhabitable uninhabit
+ uninhabited uninhabit
+ unintelligent unintellig
+ union union
+ unions union
+ unite unit
+ united unit
+ unity uniti
+ universal univers
+ universe univers
+ universities univers
+ university univers
+ unjointed unjoint
+ unjust unjust
+ unjustice unjustic
+ unjustly unjustli
+ unkennel unkennel
+ unkept unkept
+ unkind unkind
+ unkindest unkindest
+ unkindly unkindli
+ unkindness unkind
+ unking unk
+ unkinglike unkinglik
+ unkiss unkiss
+ unknit unknit
+ unknowing unknow
+ unknown unknown
+ unlace unlac
+ unlaid unlaid
+ unlawful unlaw
+ unlawfully unlawfulli
+ unlearn unlearn
+ unlearned unlearn
+ unless unless
+ unlesson unlesson
+ unletter unlett
+ unlettered unlett
+ unlick unlick
+ unlike unlik
+ unlikely unlik
+ unlimited unlimit
+ unlineal unlin
+ unlink unlink
+ unload unload
+ unloaded unload
+ unloading unload
+ unloads unload
+ unlock unlock
+ unlocks unlock
+ unlook unlook
+ unlooked unlook
+ unloos unloo
+ unloose unloos
+ unlov unlov
+ unloving unlov
+ unluckily unluckili
+ unlucky unlucki
+ unmade unmad
+ unmake unmak
+ unmanly unmanli
+ unmann unmann
+ unmanner unmann
+ unmannerd unmannerd
+ unmannerly unmannerli
+ unmarried unmarri
+ unmask unmask
+ unmasked unmask
+ unmasking unmask
+ unmasks unmask
+ unmast unmast
+ unmatch unmatch
+ unmatchable unmatch
+ unmatched unmatch
+ unmeasurable unmeasur
+ unmeet unmeet
+ unmellowed unmellow
+ unmerciful unmerci
+ unmeritable unmerit
+ unmeriting unmerit
+ unminded unmind
+ unmindfull unmindful
+ unmingled unmingl
+ unmitigable unmitig
+ unmitigated unmitig
+ unmix unmix
+ unmoan unmoan
+ unmov unmov
+ unmoved unmov
+ unmoving unmov
+ unmuffles unmuffl
+ unmuffling unmuffl
+ unmusical unmus
+ unmuzzle unmuzzl
+ unmuzzled unmuzzl
+ unnatural unnatur
+ unnaturally unnatur
+ unnaturalness unnatur
+ unnecessarily unnecessarili
+ unnecessary unnecessari
+ unneighbourly unneighbourli
+ unnerved unnerv
+ unnoble unnobl
+ unnoted unnot
+ unnumb unnumb
+ unnumber unnumb
+ unowed unow
+ unpack unpack
+ unpaid unpaid
+ unparagon unparagon
+ unparallel unparallel
+ unpartial unparti
+ unpath unpath
+ unpaved unpav
+ unpay unpai
+ unpeaceable unpeac
+ unpeg unpeg
+ unpeople unpeopl
+ unpeopled unpeopl
+ unperfect unperfect
+ unperfectness unperfect
+ unpick unpick
+ unpin unpin
+ unpink unpink
+ unpitied unpiti
+ unpitifully unpitifulli
+ unplagu unplagu
+ unplausive unplaus
+ unpleas unplea
+ unpleasant unpleas
+ unpleasing unpleas
+ unpolicied unpolici
+ unpolish unpolish
+ unpolished unpolish
+ unpolluted unpollut
+ unpossess unpossess
+ unpossessing unpossess
+ unpossible unposs
+ unpractis unpracti
+ unpregnant unpregn
+ unpremeditated unpremedit
+ unprepar unprepar
+ unprepared unprepar
+ unpress unpress
+ unprevailing unprevail
+ unprevented unprev
+ unpriz unpriz
+ unprizable unpriz
+ unprofitable unprofit
+ unprofited unprofit
+ unproper unprop
+ unproperly unproperli
+ unproportion unproport
+ unprovide unprovid
+ unprovided unprovid
+ unprovident unprovid
+ unprovokes unprovok
+ unprun unprun
+ unpruned unprun
+ unpublish unpublish
+ unpurged unpurg
+ unpurpos unpurpo
+ unqualitied unqual
+ unqueen unqueen
+ unquestion unquest
+ unquestionable unquestion
+ unquiet unquiet
+ unquietly unquietli
+ unquietness unquiet
+ unraised unrais
+ unrak unrak
+ unread unread
+ unready unreadi
+ unreal unreal
+ unreasonable unreason
+ unreasonably unreason
+ unreclaimed unreclaim
+ unreconciled unreconcil
+ unreconciliable unreconcili
+ unrecounted unrecount
+ unrecuring unrecur
+ unregarded unregard
+ unregist unregist
+ unrelenting unrel
+ unremovable unremov
+ unremovably unremov
+ unreprievable unrepriev
+ unresolv unresolv
+ unrespected unrespect
+ unrespective unrespect
+ unrest unrest
+ unrestor unrestor
+ unrestrained unrestrain
+ unreveng unreveng
+ unreverend unreverend
+ unreverent unrever
+ unrevers unrev
+ unrewarded unreward
+ unrighteous unright
+ unrightful unright
+ unripe unrip
+ unripp unripp
+ unrivall unrival
+ unroll unrol
+ unroof unroof
+ unroosted unroost
+ unroot unroot
+ unrough unrough
+ unruly unruli
+ unsafe unsaf
+ unsaluted unsalut
+ unsanctified unsanctifi
+ unsatisfied unsatisfi
+ unsavoury unsavouri
+ unsay unsai
+ unscalable unscal
+ unscann unscann
+ unscarr unscarr
+ unschool unschool
+ unscorch unscorch
+ unscour unscour
+ unscratch unscratch
+ unseal unseal
+ unseam unseam
+ unsearch unsearch
+ unseason unseason
+ unseasonable unseason
+ unseasonably unseason
+ unseasoned unseason
+ unseconded unsecond
+ unsecret unsecret
+ unseduc unseduc
+ unseeing unse
+ unseeming unseem
+ unseemly unseemli
+ unseen unseen
+ unseminar unseminar
+ unseparable unsepar
+ unserviceable unservic
+ unset unset
+ unsettle unsettl
+ unsettled unsettl
+ unsever unsev
+ unsex unsex
+ unshak unshak
+ unshaked unshak
+ unshaken unshaken
+ unshaped unshap
+ unshapes unshap
+ unsheath unsheath
+ unsheathe unsheath
+ unshorn unshorn
+ unshout unshout
+ unshown unshown
+ unshrinking unshrink
+ unshrubb unshrubb
+ unshunn unshunn
+ unshunnable unshunn
+ unsifted unsift
+ unsightly unsightli
+ unsinew unsinew
+ unsisting unsist
+ unskilful unskil
+ unskilfully unskilfulli
+ unskillful unskil
+ unslipping unslip
+ unsmirched unsmirch
+ unsoil unsoil
+ unsolicited unsolicit
+ unsorted unsort
+ unsought unsought
+ unsound unsound
+ unsounded unsound
+ unspeak unspeak
+ unspeakable unspeak
+ unspeaking unspeak
+ unsphere unspher
+ unspoke unspok
+ unspoken unspoken
+ unspotted unspot
+ unsquar unsquar
+ unstable unstabl
+ unstaid unstaid
+ unstain unstain
+ unstained unstain
+ unstanched unstanch
+ unstate unstat
+ unsteadfast unsteadfast
+ unstooping unstoop
+ unstringed unstring
+ unstuff unstuff
+ unsubstantial unsubstanti
+ unsuitable unsuit
+ unsuiting unsuit
+ unsullied unsulli
+ unsunn unsunn
+ unsur unsur
+ unsure unsur
+ unsuspected unsuspect
+ unsway unswai
+ unswayable unsway
+ unswayed unswai
+ unswear unswear
+ unswept unswept
+ unsworn unsworn
+ untainted untaint
+ untalk untalk
+ untangle untangl
+ untangled untangl
+ untasted untast
+ untaught untaught
+ untempering untemp
+ untender untend
+ untent untent
+ untented untent
+ unthankful unthank
+ unthankfulness unthank
+ unthink unthink
+ unthought unthought
+ unthread unthread
+ unthrift unthrift
+ unthrifts unthrift
+ unthrifty unthrifti
+ untie unti
+ untied unti
+ until until
+ untimber untimb
+ untimely untim
+ untir untir
+ untirable untir
+ untired untir
+ untitled untitl
+ unto unto
+ untold untold
+ untouch untouch
+ untoward untoward
+ untowardly untowardli
+ untraded untrad
+ untrain untrain
+ untrained untrain
+ untread untread
+ untreasur untreasur
+ untried untri
+ untrimmed untrim
+ untrod untrod
+ untrodden untrodden
+ untroubled untroubl
+ untrue untru
+ untrussing untruss
+ untruth untruth
+ untruths untruth
+ untucked untuck
+ untun untun
+ untune untun
+ untuneable untun
+ untutor untutor
+ untutored untutor
+ untwine untwin
+ unurg unurg
+ unus unu
+ unused unus
+ unusual unusu
+ unvalued unvalu
+ unvanquish unvanquish
+ unvarnish unvarnish
+ unveil unveil
+ unveiling unveil
+ unvenerable unvener
+ unvex unvex
+ unviolated unviol
+ unvirtuous unvirtu
+ unvisited unvisit
+ unvulnerable unvulner
+ unwares unwar
+ unwarily unwarili
+ unwash unwash
+ unwatch unwatch
+ unwearied unweari
+ unwed unw
+ unwedgeable unwedg
+ unweeded unweed
+ unweighed unweigh
+ unweighing unweigh
+ unwelcome unwelcom
+ unwept unwept
+ unwhipp unwhipp
+ unwholesome unwholesom
+ unwieldy unwieldi
+ unwilling unwil
+ unwillingly unwillingli
+ unwillingness unwilling
+ unwind unwind
+ unwiped unwip
+ unwise unwis
+ unwisely unwis
+ unwish unwish
+ unwished unwish
+ unwitted unwit
+ unwittingly unwittingli
+ unwonted unwont
+ unwooed unwoo
+ unworthier unworthi
+ unworthiest unworthiest
+ unworthily unworthili
+ unworthiness unworthi
+ unworthy unworthi
+ unwrung unwrung
+ unyok unyok
+ unyoke unyok
+ up up
+ upbraid upbraid
+ upbraided upbraid
+ upbraidings upbraid
+ upbraids upbraid
+ uphoarded uphoard
+ uphold uphold
+ upholdeth upholdeth
+ upholding uphold
+ upholds uphold
+ uplift uplift
+ uplifted uplift
+ upmost upmost
+ upon upon
+ upper upper
+ uprear uprear
+ upreared uprear
+ upright upright
+ uprighteously upright
+ uprightness upright
+ uprise upris
+ uprising upris
+ uproar uproar
+ uproars uproar
+ uprous uprou
+ upshoot upshoot
+ upshot upshot
+ upside upsid
+ upspring upspr
+ upstairs upstair
+ upstart upstart
+ upturned upturn
+ upward upward
+ upwards upward
+ urchin urchin
+ urchinfield urchinfield
+ urchins urchin
+ urg urg
+ urge urg
+ urged urg
+ urgent urgent
+ urges urg
+ urgest urgest
+ urging urg
+ urinal urin
+ urinals urin
+ urine urin
+ urn urn
+ urns urn
+ urs ur
+ ursa ursa
+ ursley urslei
+ ursula ursula
+ urswick urswick
+ us us
+ usage usag
+ usance usanc
+ usances usanc
+ use us
+ used us
+ useful us
+ useless useless
+ user user
+ uses us
+ usest usest
+ useth useth
+ usher usher
+ ushered usher
+ ushering usher
+ ushers usher
+ using us
+ usual usual
+ usually usual
+ usurer usur
+ usurers usur
+ usuries usuri
+ usuring usur
+ usurp usurp
+ usurpation usurp
+ usurped usurp
+ usurper usurp
+ usurpers usurp
+ usurping usurp
+ usurpingly usurpingli
+ usurps usurp
+ usury usuri
+ ut ut
+ utensil utensil
+ utensils utensil
+ utility util
+ utmost utmost
+ utt utt
+ utter utter
+ utterance utter
+ uttered utter
+ uttereth uttereth
+ uttering utter
+ utterly utterli
+ uttermost uttermost
+ utters utter
+ uy uy
+ v v
+ va va
+ vacancy vacanc
+ vacant vacant
+ vacation vacat
+ vade vade
+ vagabond vagabond
+ vagabonds vagabond
+ vagram vagram
+ vagrom vagrom
+ vail vail
+ vailed vail
+ vailing vail
+ vaillant vaillant
+ vain vain
+ vainer vainer
+ vainglory vainglori
+ vainly vainli
+ vainness vain
+ vais vai
+ valanc valanc
+ valance valanc
+ vale vale
+ valence valenc
+ valentine valentin
+ valentinus valentinu
+ valentio valentio
+ valeria valeria
+ valerius valeriu
+ vales vale
+ valiant valiant
+ valiantly valiantli
+ valiantness valiant
+ validity valid
+ vallant vallant
+ valley vallei
+ valleys vallei
+ vally valli
+ valor valor
+ valorous valor
+ valorously valor
+ valour valour
+ valu valu
+ valuation valuat
+ value valu
+ valued valu
+ valueless valueless
+ values valu
+ valuing valu
+ vane vane
+ vanish vanish
+ vanished vanish
+ vanishes vanish
+ vanishest vanishest
+ vanishing vanish
+ vanities vaniti
+ vanity vaniti
+ vanquish vanquish
+ vanquished vanquish
+ vanquisher vanquish
+ vanquishest vanquishest
+ vanquisheth vanquisheth
+ vant vant
+ vantage vantag
+ vantages vantag
+ vantbrace vantbrac
+ vapians vapian
+ vapor vapor
+ vaporous vapor
+ vapour vapour
+ vapours vapour
+ vara vara
+ variable variabl
+ variance varianc
+ variation variat
+ variations variat
+ varied vari
+ variest variest
+ variety varieti
+ varld varld
+ varlet varlet
+ varletry varletri
+ varlets varlet
+ varletto varletto
+ varnish varnish
+ varrius varriu
+ varro varro
+ vary vari
+ varying vari
+ vassal vassal
+ vassalage vassalag
+ vassals vassal
+ vast vast
+ vastidity vastid
+ vasty vasti
+ vat vat
+ vater vater
+ vaudemont vaudemont
+ vaughan vaughan
+ vault vault
+ vaultages vaultag
+ vaulted vault
+ vaulting vault
+ vaults vault
+ vaulty vaulti
+ vaumond vaumond
+ vaunt vaunt
+ vaunted vaunt
+ vaunter vaunter
+ vaunting vaunt
+ vauntingly vauntingli
+ vaunts vaunt
+ vauvado vauvado
+ vaux vaux
+ vaward vaward
+ ve ve
+ veal veal
+ vede vede
+ vehemence vehem
+ vehemency vehem
+ vehement vehement
+ vehor vehor
+ veil veil
+ veiled veil
+ veiling veil
+ vein vein
+ veins vein
+ vell vell
+ velure velur
+ velutus velutu
+ velvet velvet
+ vendible vendibl
+ venerable vener
+ venereal vener
+ venetia venetia
+ venetian venetian
+ venetians venetian
+ veneys venei
+ venge veng
+ vengeance vengeanc
+ vengeances vengeanc
+ vengeful veng
+ veni veni
+ venial venial
+ venice venic
+ venison venison
+ venit venit
+ venom venom
+ venomous venom
+ venomously venom
+ vent vent
+ ventages ventag
+ vented vent
+ ventidius ventidiu
+ ventricle ventricl
+ vents vent
+ ventur ventur
+ venture ventur
+ ventured ventur
+ ventures ventur
+ venturing ventur
+ venturous ventur
+ venue venu
+ venus venu
+ venuto venuto
+ ver ver
+ verb verb
+ verba verba
+ verbal verbal
+ verbatim verbatim
+ verbosity verbos
+ verdict verdict
+ verdun verdun
+ verdure verdur
+ vere vere
+ verefore verefor
+ verg verg
+ verge verg
+ vergers verger
+ verges verg
+ verier verier
+ veriest veriest
+ verified verifi
+ verify verifi
+ verily verili
+ veritable verit
+ verite verit
+ verities veriti
+ verity veriti
+ vermilion vermilion
+ vermin vermin
+ vernon vernon
+ verona verona
+ veronesa veronesa
+ versal versal
+ verse vers
+ verses vers
+ versing vers
+ vert vert
+ very veri
+ vesper vesper
+ vessel vessel
+ vessels vessel
+ vestal vestal
+ vestments vestment
+ vesture vestur
+ vetch vetch
+ vetches vetch
+ veux veux
+ vex vex
+ vexation vexat
+ vexations vexat
+ vexed vex
+ vexes vex
+ vexest vexest
+ vexeth vexeth
+ vexing vex
+ vi vi
+ via via
+ vial vial
+ vials vial
+ viand viand
+ viands viand
+ vic vic
+ vicar vicar
+ vice vice
+ vicegerent viceger
+ vicentio vicentio
+ viceroy viceroi
+ viceroys viceroi
+ vices vice
+ vici vici
+ vicious viciou
+ viciousness vicious
+ vict vict
+ victims victim
+ victor victor
+ victoress victoress
+ victories victori
+ victorious victori
+ victors victor
+ victory victori
+ victual victual
+ victuall victual
+ victuals victual
+ videlicet videlicet
+ video video
+ vides vide
+ videsne videsn
+ vidi vidi
+ vie vie
+ vied vi
+ vienna vienna
+ view view
+ viewest viewest
+ vieweth vieweth
+ viewing view
+ viewless viewless
+ views view
+ vigil vigil
+ vigilance vigil
+ vigilant vigil
+ vigitant vigit
+ vigour vigour
+ vii vii
+ viii viii
+ vile vile
+ vilely vile
+ vileness vile
+ viler viler
+ vilest vilest
+ vill vill
+ village villag
+ villager villag
+ villagery villageri
+ villages villag
+ villain villain
+ villainies villaini
+ villainous villain
+ villainously villain
+ villains villain
+ villainy villaini
+ villanies villani
+ villanous villan
+ villany villani
+ villiago villiago
+ villian villian
+ villianda villianda
+ villians villian
+ vinaigre vinaigr
+ vincentio vincentio
+ vincere vincer
+ vindicative vindic
+ vine vine
+ vinegar vinegar
+ vines vine
+ vineyard vineyard
+ vineyards vineyard
+ vint vint
+ vintner vintner
+ viol viol
+ viola viola
+ violate violat
+ violated violat
+ violates violat
+ violation violat
+ violator violat
+ violence violenc
+ violent violent
+ violenta violenta
+ violenteth violenteth
+ violently violent
+ violet violet
+ violets violet
+ viper viper
+ viperous viper
+ vipers viper
+ vir vir
+ virgilia virgilia
+ virgin virgin
+ virginal virgin
+ virginalling virginal
+ virginity virgin
+ virginius virginiu
+ virgins virgin
+ virgo virgo
+ virtue virtu
+ virtues virtu
+ virtuous virtuou
+ virtuously virtuous
+ visag visag
+ visage visag
+ visages visag
+ visard visard
+ viscount viscount
+ visible visibl
+ visibly visibl
+ vision vision
+ visions vision
+ visit visit
+ visitation visit
+ visitations visit
+ visited visit
+ visiting visit
+ visitings visit
+ visitor visitor
+ visitors visitor
+ visits visit
+ visor visor
+ vita vita
+ vitae vita
+ vital vital
+ vitement vitement
+ vitruvio vitruvio
+ vitx vitx
+ viva viva
+ vivant vivant
+ vive vive
+ vixen vixen
+ viz viz
+ vizaments vizament
+ vizard vizard
+ vizarded vizard
+ vizards vizard
+ vizor vizor
+ vlouting vlout
+ vocation vocat
+ vocativo vocativo
+ vocatur vocatur
+ voce voce
+ voic voic
+ voice voic
+ voices voic
+ void void
+ voided void
+ voiding void
+ voke voke
+ volable volabl
+ volant volant
+ volivorco volivorco
+ volley vollei
+ volquessen volquessen
+ volsce volsc
+ volsces volsc
+ volscian volscian
+ volscians volscian
+ volt volt
+ voltemand voltemand
+ volubility volubl
+ voluble volubl
+ volume volum
+ volumes volum
+ volumnia volumnia
+ volumnius volumniu
+ voluntaries voluntari
+ voluntary voluntari
+ voluptuously voluptu
+ voluptuousness voluptu
+ vomissement vomiss
+ vomit vomit
+ vomits vomit
+ vor vor
+ vore vore
+ vortnight vortnight
+ vot vot
+ votaries votari
+ votarist votarist
+ votarists votarist
+ votary votari
+ votre votr
+ vouch vouch
+ voucher voucher
+ vouchers voucher
+ vouches vouch
+ vouching vouch
+ vouchsaf vouchsaf
+ vouchsafe vouchsaf
+ vouchsafed vouchsaf
+ vouchsafes vouchsaf
+ vouchsafing vouchsaf
+ voudrais voudrai
+ vour vour
+ vous vou
+ voutsafe voutsaf
+ vow vow
+ vowed vow
+ vowel vowel
+ vowels vowel
+ vowing vow
+ vows vow
+ vox vox
+ voyage voyag
+ voyages voyag
+ vraiment vraiment
+ vulcan vulcan
+ vulgar vulgar
+ vulgarly vulgarli
+ vulgars vulgar
+ vulgo vulgo
+ vulnerable vulner
+ vulture vultur
+ vultures vultur
+ vurther vurther
+ w w
+ wad wad
+ waddled waddl
+ wade wade
+ waded wade
+ wafer wafer
+ waft waft
+ waftage waftag
+ wafting waft
+ wafts waft
+ wag wag
+ wage wage
+ wager wager
+ wagers wager
+ wages wage
+ wagging wag
+ waggish waggish
+ waggling waggl
+ waggon waggon
+ waggoner waggon
+ wagon wagon
+ wagoner wagon
+ wags wag
+ wagtail wagtail
+ wail wail
+ wailful wail
+ wailing wail
+ wails wail
+ wain wain
+ wainropes wainrop
+ wainscot wainscot
+ waist waist
+ wait wait
+ waited wait
+ waiter waiter
+ waiteth waiteth
+ waiting wait
+ waits wait
+ wak wak
+ wake wake
+ waked wake
+ wakefield wakefield
+ waken waken
+ wakened waken
+ wakes wake
+ wakest wakest
+ waking wake
+ wales wale
+ walk walk
+ walked walk
+ walking walk
+ walks walk
+ wall wall
+ walled wall
+ wallet wallet
+ wallets wallet
+ wallon wallon
+ walloon walloon
+ wallow wallow
+ walls wall
+ walnut walnut
+ walter walter
+ wan wan
+ wand wand
+ wander wander
+ wanderer wander
+ wanderers wander
+ wandering wander
+ wanders wander
+ wands wand
+ wane wane
+ waned wane
+ wanes wane
+ waning wane
+ wann wann
+ want want
+ wanted want
+ wanteth wanteth
+ wanting want
+ wanton wanton
+ wantonly wantonli
+ wantonness wanton
+ wantons wanton
+ wants want
+ wappen wappen
+ war war
+ warble warbl
+ warbling warbl
+ ward ward
+ warded ward
+ warden warden
+ warder warder
+ warders warder
+ wardrobe wardrob
+ wardrop wardrop
+ wards ward
+ ware ware
+ wares ware
+ warily warili
+ warkworth warkworth
+ warlike warlik
+ warm warm
+ warmed warm
+ warmer warmer
+ warming warm
+ warms warm
+ warmth warmth
+ warn warn
+ warned warn
+ warning warn
+ warnings warn
+ warns warn
+ warp warp
+ warped warp
+ warr warr
+ warrant warrant
+ warranted warrant
+ warranteth warranteth
+ warrantise warrantis
+ warrantize warrant
+ warrants warrant
+ warranty warranti
+ warren warren
+ warrener warren
+ warring war
+ warrior warrior
+ warriors warrior
+ wars war
+ wart wart
+ warwick warwick
+ warwickshire warwickshir
+ wary wari
+ was wa
+ wash wash
+ washed wash
+ washer washer
+ washes wash
+ washford washford
+ washing wash
+ wasp wasp
+ waspish waspish
+ wasps wasp
+ wassail wassail
+ wassails wassail
+ wast wast
+ waste wast
+ wasted wast
+ wasteful wast
+ wasters waster
+ wastes wast
+ wasting wast
+ wat wat
+ watch watch
+ watched watch
+ watchers watcher
+ watches watch
+ watchful watch
+ watching watch
+ watchings watch
+ watchman watchman
+ watchmen watchmen
+ watchword watchword
+ water water
+ waterdrops waterdrop
+ watered water
+ waterfly waterfli
+ waterford waterford
+ watering water
+ waterish waterish
+ waterpots waterpot
+ waterrugs waterrug
+ waters water
+ waterton waterton
+ watery wateri
+ wav wav
+ wave wave
+ waved wave
+ waver waver
+ waverer waver
+ wavering waver
+ waves wave
+ waving wave
+ waw waw
+ wawl wawl
+ wax wax
+ waxed wax
+ waxen waxen
+ waxes wax
+ waxing wax
+ way wai
+ waylaid waylaid
+ waylay waylai
+ ways wai
+ wayward wayward
+ waywarder wayward
+ waywardness wayward
+ we we
+ weak weak
+ weaken weaken
+ weakens weaken
+ weaker weaker
+ weakest weakest
+ weakling weakl
+ weakly weakli
+ weakness weak
+ weal weal
+ wealsmen wealsmen
+ wealth wealth
+ wealthiest wealthiest
+ wealthily wealthili
+ wealthy wealthi
+ wealtlly wealtlli
+ wean wean
+ weapon weapon
+ weapons weapon
+ wear wear
+ wearer wearer
+ wearers wearer
+ wearied weari
+ wearies weari
+ weariest weariest
+ wearily wearili
+ weariness weari
+ wearing wear
+ wearisome wearisom
+ wears wear
+ weary weari
+ weasel weasel
+ weather weather
+ weathercock weathercock
+ weathers weather
+ weav weav
+ weave weav
+ weaver weaver
+ weavers weaver
+ weaves weav
+ weaving weav
+ web web
+ wed wed
+ wedded wed
+ wedding wed
+ wedg wedg
+ wedged wedg
+ wedges wedg
+ wedlock wedlock
+ wednesday wednesdai
+ weed weed
+ weeded weed
+ weeder weeder
+ weeding weed
+ weeds weed
+ weedy weedi
+ week week
+ weeke week
+ weekly weekli
+ weeks week
+ ween ween
+ weening ween
+ weep weep
+ weeper weeper
+ weeping weep
+ weepingly weepingli
+ weepings weep
+ weeps weep
+ weet weet
+ weigh weigh
+ weighed weigh
+ weighing weigh
+ weighs weigh
+ weight weight
+ weightier weightier
+ weightless weightless
+ weights weight
+ weighty weighti
+ weird weird
+ welcom welcom
+ welcome welcom
+ welcomer welcom
+ welcomes welcom
+ welcomest welcomest
+ welfare welfar
+ welkin welkin
+ well well
+ wells well
+ welsh welsh
+ welshman welshman
+ welshmen welshmen
+ welshwomen welshwomen
+ wench wench
+ wenches wench
+ wenching wench
+ wend wend
+ went went
+ wept wept
+ weraday weradai
+ were were
+ wert wert
+ west west
+ western western
+ westminster westminst
+ westmoreland westmoreland
+ westward westward
+ wet wet
+ wether wether
+ wetting wet
+ wezand wezand
+ whale whale
+ whales whale
+ wharf wharf
+ wharfs wharf
+ what what
+ whate whate
+ whatever whatev
+ whatsoe whatso
+ whatsoever whatsoev
+ whatsome whatsom
+ whe whe
+ wheat wheat
+ wheaten wheaten
+ wheel wheel
+ wheeling wheel
+ wheels wheel
+ wheer wheer
+ wheeson wheeson
+ wheezing wheez
+ whelk whelk
+ whelks whelk
+ whelm whelm
+ whelp whelp
+ whelped whelp
+ whelps whelp
+ when when
+ whenas whena
+ whence whenc
+ whencesoever whencesoev
+ whene whene
+ whenever whenev
+ whensoever whensoev
+ where where
+ whereabout whereabout
+ whereas wherea
+ whereat whereat
+ whereby wherebi
+ wherefore wherefor
+ wherein wherein
+ whereinto whereinto
+ whereof whereof
+ whereon whereon
+ whereout whereout
+ whereso whereso
+ wheresoe whereso
+ wheresoever wheresoev
+ wheresome wheresom
+ whereto whereto
+ whereuntil whereuntil
+ whereunto whereunto
+ whereupon whereupon
+ wherever wherev
+ wherewith wherewith
+ wherewithal wherewith
+ whet whet
+ whether whether
+ whetstone whetston
+ whetted whet
+ whew whew
+ whey whei
+ which which
+ whiff whiff
+ whiffler whiffler
+ while while
+ whiles while
+ whilst whilst
+ whin whin
+ whine whine
+ whined whine
+ whinid whinid
+ whining whine
+ whip whip
+ whipp whipp
+ whippers whipper
+ whipping whip
+ whips whip
+ whipster whipster
+ whipstock whipstock
+ whipt whipt
+ whirl whirl
+ whirled whirl
+ whirligig whirligig
+ whirling whirl
+ whirlpool whirlpool
+ whirls whirl
+ whirlwind whirlwind
+ whirlwinds whirlwind
+ whisp whisp
+ whisper whisper
+ whispering whisper
+ whisperings whisper
+ whispers whisper
+ whist whist
+ whistle whistl
+ whistles whistl
+ whistling whistl
+ whit whit
+ white white
+ whitehall whitehal
+ whitely white
+ whiteness white
+ whiter whiter
+ whites white
+ whitest whitest
+ whither whither
+ whiting white
+ whitmore whitmor
+ whitsters whitster
+ whitsun whitsun
+ whittle whittl
+ whizzing whizz
+ who who
+ whoa whoa
+ whoe whoe
+ whoever whoever
+ whole whole
+ wholesom wholesom
+ wholesome wholesom
+ wholly wholli
+ whom whom
+ whoobub whoobub
+ whoop whoop
+ whooping whoop
+ whor whor
+ whore whore
+ whoremaster whoremast
+ whoremasterly whoremasterli
+ whoremonger whoremong
+ whores whore
+ whoreson whoreson
+ whoresons whoreson
+ whoring whore
+ whorish whorish
+ whose whose
+ whoso whoso
+ whosoe whoso
+ whosoever whosoev
+ why why
+ wi wi
+ wick wick
+ wicked wick
+ wickednes wickedn
+ wickedness wicked
+ wicket wicket
+ wicky wicki
+ wid wid
+ wide wide
+ widens widen
+ wider wider
+ widow widow
+ widowed widow
+ widower widow
+ widowhood widowhood
+ widows widow
+ wield wield
+ wife wife
+ wight wight
+ wights wight
+ wild wild
+ wildcats wildcat
+ wilder wilder
+ wilderness wilder
+ wildest wildest
+ wildfire wildfir
+ wildly wildli
+ wildness wild
+ wilds wild
+ wiles wile
+ wilful wil
+ wilfull wilful
+ wilfully wilfulli
+ wilfulnes wilfuln
+ wilfulness wil
+ will will
+ willed will
+ willers willer
+ willeth willeth
+ william william
+ williams william
+ willing will
+ willingly willingli
+ willingness willing
+ willoughby willoughbi
+ willow willow
+ wills will
+ wilt wilt
+ wiltshire wiltshir
+ wimpled wimpl
+ win win
+ wince winc
+ winch winch
+ winchester winchest
+ wincot wincot
+ wind wind
+ winded wind
+ windgalls windgal
+ winding wind
+ windlasses windlass
+ windmill windmil
+ window window
+ windows window
+ windpipe windpip
+ winds wind
+ windsor windsor
+ windy windi
+ wine wine
+ wing wing
+ winged wing
+ wingfield wingfield
+ wingham wingham
+ wings wing
+ wink wink
+ winking wink
+ winks wink
+ winner winner
+ winners winner
+ winning win
+ winnow winnow
+ winnowed winnow
+ winnows winnow
+ wins win
+ winter winter
+ winterly winterli
+ winters winter
+ wip wip
+ wipe wipe
+ wiped wipe
+ wipes wipe
+ wiping wipe
+ wire wire
+ wires wire
+ wiry wiri
+ wisdom wisdom
+ wisdoms wisdom
+ wise wise
+ wiselier wiseli
+ wisely wise
+ wiser wiser
+ wisest wisest
+ wish wish
+ wished wish
+ wisher wisher
+ wishers wisher
+ wishes wish
+ wishest wishest
+ wisheth wisheth
+ wishful wish
+ wishing wish
+ wishtly wishtli
+ wisp wisp
+ wist wist
+ wit wit
+ witb witb
+ witch witch
+ witchcraft witchcraft
+ witches witch
+ witching witch
+ with with
+ withal withal
+ withdraw withdraw
+ withdrawing withdraw
+ withdrawn withdrawn
+ withdrew withdrew
+ wither wither
+ withered wither
+ withering wither
+ withers wither
+ withheld withheld
+ withhold withhold
+ withholds withhold
+ within within
+ withold withold
+ without without
+ withstand withstand
+ withstanding withstand
+ withstood withstood
+ witless witless
+ witness wit
+ witnesses wit
+ witnesseth witnesseth
+ witnessing wit
+ wits wit
+ witted wit
+ wittenberg wittenberg
+ wittiest wittiest
+ wittily wittili
+ witting wit
+ wittingly wittingli
+ wittol wittol
+ wittolly wittolli
+ witty witti
+ wiv wiv
+ wive wive
+ wived wive
+ wives wive
+ wiving wive
+ wizard wizard
+ wizards wizard
+ wo wo
+ woe woe
+ woeful woeful
+ woefull woeful
+ woefullest woefullest
+ woes woe
+ woful woful
+ wolf wolf
+ wolfish wolfish
+ wolsey wolsei
+ wolves wolv
+ wolvish wolvish
+ woman woman
+ womanhood womanhood
+ womanish womanish
+ womankind womankind
+ womanly womanli
+ womb womb
+ wombs womb
+ womby wombi
+ women women
+ won won
+ woncot woncot
+ wond wond
+ wonder wonder
+ wondered wonder
+ wonderful wonder
+ wonderfully wonderfulli
+ wondering wonder
+ wonders wonder
+ wondrous wondrou
+ wondrously wondrous
+ wont wont
+ wonted wont
+ woo woo
+ wood wood
+ woodbine woodbin
+ woodcock woodcock
+ woodcocks woodcock
+ wooden wooden
+ woodland woodland
+ woodman woodman
+ woodmonger woodmong
+ woods wood
+ woodstock woodstock
+ woodville woodvil
+ wooed woo
+ wooer wooer
+ wooers wooer
+ wooes wooe
+ woof woof
+ wooing woo
+ wooingly wooingli
+ wool wool
+ woollen woollen
+ woolly woolli
+ woolsack woolsack
+ woolsey woolsei
+ woolward woolward
+ woos woo
+ wor wor
+ worcester worcest
+ word word
+ words word
+ wore wore
+ worins worin
+ work work
+ workers worker
+ working work
+ workings work
+ workman workman
+ workmanly workmanli
+ workmanship workmanship
+ workmen workmen
+ works work
+ worky worki
+ world world
+ worldlings worldl
+ worldly worldli
+ worlds world
+ worm worm
+ worms worm
+ wormwood wormwood
+ wormy wormi
+ worn worn
+ worried worri
+ worries worri
+ worry worri
+ worrying worri
+ worse wors
+ worser worser
+ worship worship
+ worshipful worship
+ worshipfully worshipfulli
+ worshipp worshipp
+ worshipper worshipp
+ worshippers worshipp
+ worshippest worshippest
+ worships worship
+ worst worst
+ worsted worst
+ wort wort
+ worth worth
+ worthied worthi
+ worthier worthier
+ worthies worthi
+ worthiest worthiest
+ worthily worthili
+ worthiness worthi
+ worthless worthless
+ worths worth
+ worthy worthi
+ worts wort
+ wot wot
+ wots wot
+ wotting wot
+ wouid wouid
+ would would
+ wouldest wouldest
+ wouldst wouldst
+ wound wound
+ wounded wound
+ wounding wound
+ woundings wound
+ woundless woundless
+ wounds wound
+ wouns woun
+ woven woven
+ wow wow
+ wrack wrack
+ wrackful wrack
+ wrangle wrangl
+ wrangler wrangler
+ wranglers wrangler
+ wrangling wrangl
+ wrap wrap
+ wrapp wrapp
+ wraps wrap
+ wrapt wrapt
+ wrath wrath
+ wrathful wrath
+ wrathfully wrathfulli
+ wraths wrath
+ wreak wreak
+ wreakful wreak
+ wreaks wreak
+ wreath wreath
+ wreathed wreath
+ wreathen wreathen
+ wreaths wreath
+ wreck wreck
+ wrecked wreck
+ wrecks wreck
+ wren wren
+ wrench wrench
+ wrenching wrench
+ wrens wren
+ wrest wrest
+ wrested wrest
+ wresting wrest
+ wrestle wrestl
+ wrestled wrestl
+ wrestler wrestler
+ wrestling wrestl
+ wretch wretch
+ wretchcd wretchcd
+ wretched wretch
+ wretchedness wretched
+ wretches wretch
+ wring wring
+ wringer wringer
+ wringing wring
+ wrings wring
+ wrinkle wrinkl
+ wrinkled wrinkl
+ wrinkles wrinkl
+ wrist wrist
+ wrists wrist
+ writ writ
+ write write
+ writer writer
+ writers writer
+ writes write
+ writhled writhl
+ writing write
+ writings write
+ writs writ
+ written written
+ wrong wrong
+ wronged wrong
+ wronger wronger
+ wrongful wrong
+ wrongfully wrongfulli
+ wronging wrong
+ wrongly wrongli
+ wrongs wrong
+ wronk wronk
+ wrote wrote
+ wroth wroth
+ wrought wrought
+ wrung wrung
+ wry wry
+ wrying wry
+ wt wt
+ wul wul
+ wye wye
+ x x
+ xanthippe xanthipp
+ xi xi
+ xii xii
+ xiii xiii
+ xiv xiv
+ xv xv
+ y y
+ yard yard
+ yards yard
+ yare yare
+ yarely yare
+ yarn yarn
+ yaughan yaughan
+ yaw yaw
+ yawn yawn
+ yawning yawn
+ ycleped yclepe
+ ycliped yclipe
+ ye ye
+ yea yea
+ yead yead
+ year year
+ yearly yearli
+ yearn yearn
+ yearns yearn
+ years year
+ yeas yea
+ yeast yeast
+ yedward yedward
+ yell yell
+ yellow yellow
+ yellowed yellow
+ yellowing yellow
+ yellowness yellow
+ yellows yellow
+ yells yell
+ yelping yelp
+ yeoman yeoman
+ yeomen yeomen
+ yerk yerk
+ yes ye
+ yesterday yesterdai
+ yesterdays yesterdai
+ yesternight yesternight
+ yesty yesti
+ yet yet
+ yew yew
+ yicld yicld
+ yield yield
+ yielded yield
+ yielder yielder
+ yielders yielder
+ yielding yield
+ yields yield
+ yok yok
+ yoke yoke
+ yoked yoke
+ yokefellow yokefellow
+ yokes yoke
+ yoketh yoketh
+ yon yon
+ yond yond
+ yonder yonder
+ yongrey yongrei
+ yore yore
+ yorick yorick
+ york york
+ yorkists yorkist
+ yorks york
+ yorkshire yorkshir
+ you you
+ young young
+ younger younger
+ youngest youngest
+ youngling youngl
+ younglings youngl
+ youngly youngli
+ younker younker
+ your your
+ yours your
+ yourself yourself
+ yourselves yourselv
+ youth youth
+ youthful youth
+ youths youth
+ youtli youtli
+ zanies zani
+ zany zani
+ zeal zeal
+ zealous zealou
+ zeals zeal
+ zed zed
+ zenelophon zenelophon
+ zenith zenith
+ zephyrs zephyr
+ zir zir
+ zo zo
+ zodiac zodiac
+ zodiacs zodiac
+ zone zone
+ zounds zound
+ zwagger zwagger
+}
+
+# Create a full-text index to use for testing the stemmer.
+#
+db close
+sqlite3 db :memory:
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(word, tokenize Porter);
+}
+
+foreach {pfrom pto} $porter_test_data {
+ do_test fts1porter-$pfrom {
+ execsql {
+ DELETE FROM t1_term;
+ DELETE FROM t1_content;
+ INSERT INTO t1(word) VALUES($pfrom);
+ SELECT term FROM t1_term;
+ }
+ } $pto
+}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2.test b/third_party/sqlite/test/fts2.test
new file mode 100755
index 0000000..fa49b06
--- /dev/null
+++ b/third_party/sqlite/test/fts2.test
@@ -0,0 +1,68 @@
+# 2008 July 22
+#
+# 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 runs all tests.
+#
+# $Id: fts2.test,v 1.2 2008/07/23 18:17:32 drh Exp $
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ -soak {
+ set SOAKTEST 1
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ return
+}
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ fts2.test
+}
+
+# Files to include in the test. If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/fts2*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+}
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/fts2a.test b/third_party/sqlite/test/fts2a.test
new file mode 100755
index 0000000..2d1566f
--- /dev/null
+++ b/third_party/sqlite/test/fts2a.test
@@ -0,0 +1,202 @@
+# 2006 September 9
+#
+# 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 testing the FTS2 module.
+#
+# $Id: fts2a.test,v 1.2 2007/05/21 21:59:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1(content) VALUES('one');
+ INSERT INTO t1(content) VALUES('two');
+ INSERT INTO t1(content) VALUES('one two');
+ INSERT INTO t1(content) VALUES('three');
+ INSERT INTO t1(content) VALUES('one three');
+ INSERT INTO t1(content) VALUES('two three');
+ INSERT INTO t1(content) VALUES('one two three');
+ INSERT INTO t1(content) VALUES('four');
+ INSERT INTO t1(content) VALUES('one four');
+ INSERT INTO t1(content) VALUES('two four');
+ INSERT INTO t1(content) VALUES('one two four');
+ INSERT INTO t1(content) VALUES('three four');
+ INSERT INTO t1(content) VALUES('one three four');
+ INSERT INTO t1(content) VALUES('two three four');
+ INSERT INTO t1(content) VALUES('one two three four');
+ INSERT INTO t1(content) VALUES('five');
+ INSERT INTO t1(content) VALUES('one five');
+ INSERT INTO t1(content) VALUES('two five');
+ INSERT INTO t1(content) VALUES('one two five');
+ INSERT INTO t1(content) VALUES('three five');
+ INSERT INTO t1(content) VALUES('one three five');
+ INSERT INTO t1(content) VALUES('two three five');
+ INSERT INTO t1(content) VALUES('one two three five');
+ INSERT INTO t1(content) VALUES('four five');
+ INSERT INTO t1(content) VALUES('one four five');
+ INSERT INTO t1(content) VALUES('two four five');
+ INSERT INTO t1(content) VALUES('one two four five');
+ INSERT INTO t1(content) VALUES('three four five');
+ INSERT INTO t1(content) VALUES('one three four five');
+ INSERT INTO t1(content) VALUES('two three four five');
+ INSERT INTO t1(content) VALUES('one two three four five');
+}
+
+do_test fts2a-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts2a-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two'}
+} {3 7 11 15 19 23 27 31}
+do_test fts2a-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one'}
+} {3 7 11 15 19 23 27 31}
+do_test fts2a-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two three'}
+} {7 15 23 31}
+do_test fts2a-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one three two'}
+} {7 15 23 31}
+do_test fts2a-1.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two three one'}
+} {7 15 23 31}
+do_test fts2a-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one three'}
+} {7 15 23 31}
+do_test fts2a-1.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three one two'}
+} {7 15 23 31}
+do_test fts2a-1.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three two one'}
+} {7 15 23 31}
+do_test fts2a-1.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two THREE'}
+} {7 15 23 31}
+do_test fts2a-1.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH ' ONE Two three '}
+} {7 15 23 31}
+
+do_test fts2a-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one"'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts2a-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two"'}
+} {3 7 11 15 19 23 27 31}
+do_test fts2a-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"two one"'}
+} {}
+do_test fts2a-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three"'}
+} {7 15 23 31}
+do_test fts2a-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two"'}
+} {}
+do_test fts2a-2.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three four"'}
+} {15 31}
+do_test fts2a-2.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two four"'}
+} {}
+do_test fts2a-2.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three five"'}
+} {21}
+do_test fts2a-2.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" five'}
+} {21 29}
+do_test fts2a-2.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three"'}
+} {21 29}
+do_test fts2a-2.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three" four'}
+} {29}
+do_test fts2a-2.12 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five four "one three"'}
+} {29}
+do_test fts2a-2.13 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" four five'}
+} {29}
+
+do_test fts2a-3.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts2a-3.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one -two'}
+} {1 5 9 13 17 21 25 29}
+do_test fts2a-3.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'}
+} {1 5 9 13 17 21 25 29}
+
+do_test fts2a-4.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one OR two'}
+} {1 2 3 5 6 7 9 10 11 13 14 15 17 18 19 21 22 23 25 26 27 29 30 31}
+do_test fts2a-4.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two" OR three'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts2a-4.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR "one two"'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts2a-4.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts2a-4.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR two one'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts2a-4.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three OR four'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+do_test fts2a-4.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two OR three OR four one'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+
+# Test the ability to handle NULL content
+#
+do_test fts2a-5.1 {
+ execsql {INSERT INTO t1(content) VALUES(NULL)}
+} {}
+do_test fts2a-5.2 {
+ set rowid [db last_insert_rowid]
+ execsql {SELECT content FROM t1 WHERE rowid=$rowid}
+} {{}}
+do_test fts2a-5.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH NULL}
+} {}
+
+# Test the ability to handle non-positive rowids
+#
+do_test fts2a-6.0 {
+ execsql {INSERT INTO t1(rowid, content) VALUES(0, 'four five')}
+} {}
+do_test fts2a-6.1 {
+ execsql {SELECT content FROM t1 WHERE rowid = 0}
+} {{four five}}
+do_test fts2a-6.2 {
+ execsql {INSERT INTO t1(rowid, content) VALUES(-1, 'three four')}
+} {}
+do_test fts2a-6.3 {
+ execsql {SELECT content FROM t1 WHERE rowid = -1}
+} {{three four}}
+do_test fts2a-6.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'four'}
+} {-1 0 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2b.test b/third_party/sqlite/test/fts2b.test
new file mode 100755
index 0000000..169cd8a
--- /dev/null
+++ b/third_party/sqlite/test/fts2b.test
@@ -0,0 +1,147 @@
+# 2006 September 13
+#
+# 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 testing the FTS2 module.
+#
+# $Id: fts2b.test,v 1.1 2006/10/19 23:36:26 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Fill the full-text index "t1" with phrases in english, spanish,
+# and german. For the i-th row, fill in the names for the bits
+# that are set in the value of i. The least significant bit is
+# 1. For example, the value 5 is 101 in binary which will be
+# converted to "one three" in english.
+#
+proc fill_multilanguage_fulltext_t1 {} {
+ set english {one two three four five}
+ set spanish {un dos tres cuatro cinco}
+ set german {eine zwei drei vier funf}
+
+ for {set i 1} {$i<=31} {incr i} {
+ set cmd "INSERT INTO t1 VALUES"
+ set vset {}
+ foreach lang {english spanish german} {
+ set words {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend words [lindex [set $lang] $j]}
+ }
+ lappend vset "'$words'"
+ }
+ set sql "INSERT INTO t1(english,spanish,german) VALUES([join $vset ,])"
+ # puts $sql
+ db eval $sql
+ }
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(english,spanish,german);
+}
+fill_multilanguage_fulltext_t1
+
+do_test fts2b-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE english MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts2b-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE spanish MATCH 'one'}
+} {}
+do_test fts2b-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE german MATCH 'one'}
+} {}
+do_test fts2b-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts2b-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one dos drei'}
+} {7 15 23 31}
+do_test fts2b-1.6 {
+ execsql {SELECT english, spanish, german FROM t1 WHERE rowid=1}
+} {one un eine}
+do_test fts2b-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"one un"'}
+} {}
+
+do_test fts2b-2.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t2 USING fts2(from,to);
+ INSERT INTO t2([from],[to]) VALUES ('one two three', 'four five six');
+ SELECT [from], [to] FROM t2
+ }
+} {{one two three} {four five six}}
+
+
+# Compute an SQL string that contains the words one, two, three,... to
+# describe bits set in the value $i. Only the lower 5 bits are examined.
+#
+proc wordset {i} {
+ set x {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend x [lindex {one two three four five} $j]}
+ }
+ return '$x'
+}
+
+# Create a new FTS table with three columns:
+#
+# norm: words for the bits of rowid
+# plusone: words for the bits of rowid+1
+# invert: words for the bits of ~rowid
+#
+db eval {
+ CREATE VIRTUAL TABLE t4 USING fts2([norm],'plusone',"invert");
+}
+for {set i 1} {$i<=15} {incr i} {
+ set vset [list [wordset $i] [wordset [expr {$i+1}]] [wordset [expr {~$i}]]]
+ db eval "INSERT INTO t4(norm,plusone,invert) VALUES([join $vset ,]);"
+}
+
+do_test fts2b-4.1 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts2b-4.2 {
+ execsql {SELECT rowid FROM t4 WHERE norm MATCH 'one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts2b-4.3 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'one'}
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
+do_test fts2b-4.4 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:one'}
+} {2 4 6 8 10 12 14}
+do_test fts2b-4.5 {
+ execsql {SELECT rowid FROM t4 WHERE plusone MATCH 'one'}
+} {2 4 6 8 10 12 14}
+do_test fts2b-4.6 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one plusone:two'}
+} {1 5 9 13}
+do_test fts2b-4.7 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one two'}
+} {1 3 5 7 9 11 13 15}
+do_test fts2b-4.8 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:two norm:one'}
+} {1 5 9 13}
+do_test fts2b-4.9 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'two norm:one'}
+} {1 3 5 7 9 11 13 15}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts2c.test b/third_party/sqlite/test/fts2c.test
new file mode 100755
index 0000000..cc6c9bb
--- /dev/null
+++ b/third_party/sqlite/test/fts2c.test
@@ -0,0 +1,1213 @@
+# 2006 September 14
+#
+# 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 testing the FTS2 module.
+#
+# $Id: fts2c.test,v 1.1 2006/10/19 23:36:26 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Create a table of sample email data. The data comes from email
+# archives of Enron executives that was published as part of the
+# litigation against that company.
+#
+do_test fts2c-1.1 {
+ db eval {
+ CREATE VIRTUAL TABLE email USING fts2([from],[to],subject,body);
+ BEGIN TRANSACTION;
+INSERT INTO email([from],[to],subject,body) VALUES('savita.puthigai@enron.com', 'traders.eol@enron.com, traders.eol@enron.com', 'EnronOnline- Change to Autohedge', 'Effective Monday, October 22, 2001 the following changes will be made to the Autohedge functionality on EnronOnline.
+
+The volume on the hedge will now respect the minimum volume and volume increment settings on the parent product. See rules below:
+
+? If the transaction volume on the child is less than half of the parent''s minimum volume no hedge will occur.
+? If the transaction volume on the child is more than half the parent''s minimum volume but less than half the volume increment on the parent, the hedge will volume will be the parent''s minimum volume.
+? For all other volumes, the same rounding rules will apply based on the volume increment on the parent product.
+
+Please see example below:
+
+Parent''s Settings:
+Minimum: 5000
+Increment: 1000
+
+Volume on Autohedge transaction Volume Hedged
+1 - 2499 0
+2500 - 5499 5000
+5500 - 6499 6000');
+INSERT INTO email([from],[to],subject,body) VALUES('dana.davis@enron.com', 'laynie.east@enron.com, lisa.king@enron.com, lisa.best@enron.com,', 'Leaving Early', 'FYI:
+If it''s ok with everyone''s needs, I would like to leave @4pm. If you think
+you will need my assistance past the 4 o''clock hour just let me know; I''ll
+be more than willing to stay.');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'louise.kitchen@enron.com', '<<Concur Expense Document>> - CC02.06.02', 'The following expense report is ready for approval:
+
+Employee Name: Christopher F. Calger
+Status last changed by: Mollie E. Gustafson Ms
+Expense Report Name: CC02.06.02
+Report Total: $3,972.93
+Amount Due Employee: $3,972.93
+
+
+To approve this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('jeff.duff@enron.com', 'julie.johnson@enron.com', 'Work request', 'Julie,
+
+Could you print off the current work request report by 1:30 today?
+
+Gentlemen,
+
+I''d like to review this today at 1:30 in our office. Also, could you provide
+me with your activity reports so I can have Julie enter this information.
+
+JD');
+INSERT INTO email([from],[to],subject,body) VALUES('v.weldon@enron.com', 'gary.l.carrier@usa.dupont.com, scott.joyce@bankofamerica.com', 'Enron News', 'This could turn into something big....
+http://biz.yahoo.com/rf/010129/n29305829.html');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.haedicke@enron.com', 'paul.simons@enron.com', 'Re: First Polish Deal!', 'Congrats! Things seem to be building rapidly now on the Continent. Mark');
+INSERT INTO email([from],[to],subject,body) VALUES('e..carter@enron.com', 't..robinson@enron.com', 'FW: Producers Newsletter 9-24-2001', '
+The producer lumber pricing sheet.
+ -----Original Message-----
+From: Johnson, Jay
+Sent: Tuesday, October 16, 2001 3:42 PM
+To: Carter, Karen E.
+Subject: FW: Producers Newsletter 9-24-2001
+
+
+
+ -----Original Message-----
+From: Daigre, Sergai
+Sent: Friday, September 21, 2001 8:33 PM
+Subject: Producers Newsletter 9-24-2001
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('david.delainey@enron.com', 'kenneth.lay@enron.com', 'Greater Houston Partnership', 'Ken, in response to the letter from Mr Miguel San Juan, my suggestion would
+be to offer up the Falcon for their use; however, given the tight time frame
+and your recent visit with Mr. Fox that it would be difficult for either you
+or me to participate.
+
+I spoke to Max and he agrees with this approach.
+
+I hope this meets with your approval.
+
+Regards
+Delainey');
+INSERT INTO email([from],[to],subject,body) VALUES('lachandra.fenceroy@enron.com', 'lindy.donoho@enron.com', 'FW: Bus Applications Meeting Follow Up', 'Lindy,
+
+Here is the original memo we discussed earlier. Please provide any information that you may have.
+
+Your cooperation is greatly appreciated.
+
+Thanks,
+
+lachandra.fenceroy@enron.com
+713.853.3884
+877.498.3401 Pager
+
+ -----Original Message-----
+From: Bisbee, Joanne
+Sent: Wednesday, September 26, 2001 7:50 AM
+To: Fenceroy, LaChandra
+Subject: FW: Bus Applications Meeting Follow Up
+
+Lachandra, Please get with David Duff today and see what this is about. Who are our TW accounting business users?
+
+ -----Original Message-----
+From: Koh, Wendy
+Sent: Tuesday, September 25, 2001 2:41 PM
+To: Bisbee, Joanne
+Subject: Bus Applications Meeting Follow Up
+
+Lisa brought up a TW change effective Nov 1. It involves eliminating a turnback surcharge. I have no other information, but you might check with the business folks for any system changes required.
+
+Wendy');
+INSERT INTO email([from],[to],subject,body) VALUES('danny.mccarty@enron.com', 'fran.fagan@enron.com', 'RE: worksheets', 'Fran,
+ If Julie''s merit needs to be lump sum, just move it over to that column. Also, send me Eric Gadd''s sheets as well. Thanks.
+Dan
+
+ -----Original Message-----
+From: Fagan, Fran
+Sent: Thursday, December 20, 2001 11:10 AM
+To: McCarty, Danny
+Subject: worksheets
+
+As discussed, attached are your sheets for bonus and merit.
+
+Thanks,
+
+Fran Fagan
+Sr. HR Rep
+713.853.5219
+
+
+ << File: McCartyMerit.xls >> << File: mccartyBonusCommercial_UnP.xls >>
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('bert.meyers@enron.com', 'shift.dl-portland@enron.com', 'OCTOBER SCHEDULE', 'TEAM,
+
+PLEASE SEND ME ANY REQUESTS THAT YOU HAVE FOR OCTOBER. SO FAR I HAVE THEM FOR LEAF. I WOULD LIKE TO HAVE IT DONE BY THE 15TH OF THE MONTH. ANY QUESTIONS PLEASE GIVE ME A CALL.
+
+BERT');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'john.arnold@enron.com, bilal.bajwa@enron.com, john.griffith@enron.com,', 'TRV Notification: (NG - PROPT P/L - 09/27/2001)', 'The report named: NG - PROPT P/L <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=11&report_name=NG+-+PROPT+P/L&category_cd=5&category_name=FINANCIAL&toc_hide=1&sTV1=5&TV1Exp=Y&current_efct_date=09/27/2001>, published as of 09/27/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('patrice.mims@enron.com', 'calvin.eakins@enron.com', 'Re: Small business supply assistance', 'Hi Calvin
+
+
+I spoke with Rickey (boy, is he long-winded!!). Gave him the name of our
+credit guy, Russell Diamond.
+
+Thank for your help!');
+INSERT INTO email([from],[to],subject,body) VALUES('legal <.hall@enron.com>', 'stephanie.panus@enron.com', 'Termination update', 'City of Vernon and Salt River Project terminated their contracts. I will fax these notices to you.');
+INSERT INTO email([from],[to],subject,body) VALUES('d..steffes@enron.com', 'richard.shapiro@enron.com', 'EES / ENA Government Affairs Staffing & Outside Services', 'Rick --
+
+Here is the information on staffing and outside services. Call if you need anything else.
+
+Jim
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('gelliott@industrialinfo.com', 'pcopello@industrialinfo.com', 'ECAAR (Gavin), WSCC (Diablo Canyon), & NPCC (Seabrook)', 'Dear Power Outage Database Customer,
+Attached you will find an excel document. The outages contained within are forced or rescheduled outages. Your daily delivery will still contain these outages.
+In addition to the two excel documents, there is a dbf file that is formatted like your daily deliveries you receive nightly. This will enable you to load the data into your regular database. Any questions please let me know. Thanks.
+Greg Elliott
+IIR, Inc.
+713-783-5147 x 3481
+outages@industrialinfo.com
+THE INFORMATION CONTAINED IN THIS E-MAIL IS LEGALLY PRIVILEGED AND CONFIDENTIAL INFORMATION INTENDED ONLY FOR THE USE OF THE INDIVIDUAL OR ENTITY NAMED ABOVE. YOU ARE HEREBY NOTIFIED THAT ANY DISSEMINATION, DISTRIBUTION, OR COPY OF THIS E-MAIL TO UNAUTHORIZED ENTITIES IS STRICTLY PROHIBITED. IF YOU HAVE RECEIVED THIS
+E-MAIL IN ERROR, PLEASE DELETE IT.
+ - OUTAGE.dbf
+ - 111201R.xls
+ - 111201.xls ');
+INSERT INTO email([from],[to],subject,body) VALUES('enron.announcements@enron.com', 'all_ena_egm_eim@enron.com', 'EWS Brown Bag', 'MARK YOUR LUNCH CALENDARS NOW !
+
+You are invited to attend the EWS Brown Bag Lunch Series
+
+Featuring: RAY BOWEN, COO
+
+Topic: Enron Industrial Markets
+
+Thursday, March 15, 2001
+11:30 am - 12:30 pm
+EB 5 C2
+
+
+You bring your lunch, Limited Seating
+We provide drinks and dessert. RSVP x 3-9610');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.germany@enron.com', 'ingrid.immer@williams.com', 'Re: About St Pauls', 'Sounds good to me. I bet this is next to the Warick?? Hotel.
+
+
+
+
+"Immer, Ingrid" <Ingrid.Immer@Williams.com> on 12/21/2000 11:48:47 AM
+To: "''chris.germany@enron.com''" <chris.germany@enron.com>
+cc:
+Subject: About St Pauls
+
+
+
+
+ <<About St Pauls.url>>
+?
+?http://www.stpaulshouston.org/about.html
+
+Chris,
+
+I like the looks of this place.? What do you think about going here Christmas
+eve?? They have an 11:00 a.m. service and a candlelight service at 5:00 p.m.,
+among others.
+
+Let me know.?? ii
+
+ - About St Pauls.url
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('nas@cpuc.ca.gov', 'skatz@sempratrading.com, kmccrea@sablaw.com, thompson@wrightlaw.com,', 'Reply Brief filed July 31, 2000', ' - CPUC01-#76371-v1-Revised_Reply_Brief__Due_today_7_31_.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('gascontrol@aglresources.com', 'dscott4@enron.com, lcampbel@enron.com', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder
+As discussed in the Winter Operations Meeting on Sept.29,2000,
+E-Gas(Emergency Gas) will not be offered this winter as a service from AGLC.
+Marketers and Poolers can receive gas via Peaking and IBSS nominations(daisy
+chain) from other marketers up to the 6 p.m. Same Day 2 nomination cycle.
+');
+INSERT INTO email([from],[to],subject,body) VALUES('dutch.quigley@enron.com', 'rwolkwitz@powermerchants.com', '', '
+
+Here is a goody for you');
+INSERT INTO email([from],[to],subject,body) VALUES('ryan.o''rourke@enron.com', 'k..allen@enron.com, randy.bhatia@enron.com, frank.ermis@enron.com,', 'TRV Notification: (West VaR - 11/07/2001)', 'The report named: West VaR <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=36&report_name=West+VaR&category_cd=2&category_name=WEST&toc_hide=1&sTV1=2&TV1Exp=Y&current_efct_date=11/07/2001>, published as of 11/07/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('mjones7@txu.com', 'cstone1@txu.com, ggreen2@txu.com, timpowell@txu.com,', 'Enron / HPL Actuals for July 10, 2000', 'Teco Tap 10.000 / Enron ; 110.000 / HPL IFERC
+
+LS HPL LSK IC 30.000 / Enron
+');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.pereira@enron.com', 'kkw816@aol.com', 'soccer practice', 'Kathy-
+
+Is it safe to assume that practice is cancelled for tonight??
+
+Susan Pereira');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.whitt@enron.com', 'barry.tycholiz@enron.com', 'Huber Internal Memo', 'Please look at this. I didn''t know how deep to go with the desk. Do you think this works.
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('m..forney@enron.com', 'george.phillips@enron.com', '', 'George,
+Give me a call and we will further discuss opportunities on the 13st floor.
+
+Thanks,
+JMForney
+3-7160');
+INSERT INTO email([from],[to],subject,body) VALUES('brad.mckay@enron.com', 'angusmcka@aol.com', 'Re: (no subject)', 'not yet');
+INSERT INTO email([from],[to],subject,body) VALUES('adam.bayer@enron.com', 'jonathan.mckay@enron.com', 'FW: Curve Fetch File', 'Here is the curve fetch file sent to me. It has plenty of points in it. If you give me a list of which ones you need we may be able to construct a secondary worksheet to vlookup the values.
+
+adam
+35227
+
+
+ -----Original Message-----
+From: Royed, Jeff
+Sent: Tuesday, September 25, 2001 11:37 AM
+To: Bayer, Adam
+Subject: Curve Fetch File
+
+Let me know if it works. It may be required to have a certain version of Oracle for it to work properly.
+
+
+
+Jeff Royed
+Enron
+Energy Operations
+Phone: 713-853-5295');
+INSERT INTO email([from],[to],subject,body) VALUES('matt.smith@enron.com', 'yan.wang@enron.com', 'Report Formats', 'Yan,
+
+The merged reports look great. I believe the only orientation changes are to
+"unmerge" the following six reports:
+
+31 Keystone Receipts
+15 Questar Pipeline
+40 Rockies Production
+22 West_2
+23 West_3
+25 CIG_WIC
+
+The orientation of the individual reports should be correct. Thanks.
+
+Mat
+
+PS. Just a reminder to add the "*" by the title of calculated points.');
+INSERT INTO email([from],[to],subject,body) VALUES('michelle.lokay@enron.com', 'jimboman@bigfoot.com', 'Egyptian Festival', '---------------------- Forwarded by Michelle Lokay/ET&S/Enron on 09/07/2000
+10:08 AM ---------------------------
+
+
+"Karkour, Randa" <Randa.Karkour@COMPAQ.com> on 09/07/2000 09:01:04 AM
+To: "''Agheb (E-mail)" <Agheb@aol.com>, "Leila Mankarious (E-mail)"
+<Leila_Mankarious@mhhs.org>, "''Marymankarious (E-mail)"
+<marymankarious@aol.com>, "Michelle lokay (E-mail)" <mlokay@enron.com>, "Ramy
+Mankarious (E-mail)" <Mankarious@aol.com>
+cc:
+
+Subject: Egyptian Festival
+
+
+ <<Egyptian Festival.url>>
+
+ http://www.egyptianfestival.com/
+
+ - Egyptian Festival.url
+');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'sherry.dawson@enron.com', 'Urgent!!! --- New EAST books', 'This has to be done..................................
+
+Thanks
+---------------------- Forwarded by Errol McLaughlin/Corp/Enron on 12/20/2000
+08:39 AM ---------------------------
+
+
+
+ From: William Kelly @ ECT 12/20/2000 08:31 AM
+
+
+To: Kam Keiser/HOU/ECT@ECT, Darron C Giron/HOU/ECT@ECT, David
+Baumbach/HOU/ECT@ECT, Errol McLaughlin/Corp/Enron@ENRON
+cc: Kimat Singla/HOU/ECT@ECT, Kulvinder Fowler/NA/Enron@ENRON, Kyle R
+Lilly/HOU/ECT@ECT, Jeff Royed/Corp/Enron@ENRON, Alejandra
+Chavez/NA/Enron@ENRON, Crystal Hyde/HOU/ECT@ECT
+
+Subject: New EAST books
+
+We have new book names in TAGG for our intramonth portfolios and it is
+extremely important that any deal booked to the East is communicated quickly
+to someone on my team. I know it will take some time for the new names to
+sink in and I do not want us to miss any positions or P&L.
+
+Thanks for your help on this.
+
+New:
+Scott Neal : East Northeast
+Dick Jenkins: East Marketeast
+
+WK
+');
+INSERT INTO email([from],[to],subject,body) VALUES('david.forster@enron.com', 'eol.wide@enron.com', 'Change to Stack Manager', 'Effective immediately, there is a change to the Stack Manager which will
+affect any Inactive Child.
+
+An inactive Child with links to Parent products will not have their
+calculated prices updated until the Child product is Activated.
+
+When the Child Product is activated, the price will be recalculated and
+updated BEFORE it is displayed on the web.
+
+This means that if you are inputting a basis price on a Child product, you
+will not see the final, calculated price until you Activate the product, at
+which time the customer will also see it.
+
+If you have any questions, please contact the Help Desk on:
+
+Americas: 713 853 4357
+Europe: + 44 (0) 20 7783 7783
+Asia/Australia: +61 2 9229 2300
+
+Dave');
+INSERT INTO email([from],[to],subject,body) VALUES('vince.kaminski@enron.com', 'jhh1@email.msn.com', 'Re: Light reading - see pieces beginning on page 7', 'John,
+
+I saw it. Very interesting.
+
+Vince
+
+
+
+
+
+"John H Herbert" <jhh1@email.msn.com> on 07/28/2000 08:38:08 AM
+To: "Vince J Kaminski" <Vince_J_Kaminski@enron.com>
+cc:
+Subject: Light reading - see pieces beginning on page 7
+
+
+Cheers and have a nice weekend,
+
+
+JHHerbert
+
+
+
+
+ - gd000728.pdf
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('matthew.lenhart@enron.com', 'mmmarcantel@equiva.com', 'RE:', 'i will try to line up a pig for you ');
+INSERT INTO email([from],[to],subject,body) VALUES('jae.black@enron.com', 'claudette.harvey@enron.com, chaun.roberts@enron.com, judy.martinez@enron.com,', 'Disaster Recovery Equipment', 'As a reminder...there are several pieces of equipment that are set up on the 30th Floor, as well as on our floor, for the Disaster Recovery Team. PLEASE DO NOT TAKE, BORROW OR USE this equipment. Should you need to use another computer system, other than yours, or make conference calls please work with your Assistant to help find or set up equipment for you to use.
+
+Thanks for your understanding in this matter.
+
+T.Jae Black
+East Power Trading
+Assistant to Kevin Presto
+off. 713-853-5800
+fax 713-646-8272
+cell 713-539-4760');
+INSERT INTO email([from],[to],subject,body) VALUES('eric.bass@enron.com', 'dale.neuner@enron.com', '5 X 24', 'Dale,
+
+Have you heard anything more on the 5 X 24s? We would like to get this
+product out ASAP.
+
+
+Thanks,
+
+Eric');
+INSERT INTO email([from],[to],subject,body) VALUES('messenger@smartreminders.com', 'm..tholt@enron.com', '10% Coupon - PrintPal Printer Cartridges - 100% Guaranteed', '[IMAGE]
+[IMAGE][IMAGE][IMAGE]
+Dear SmartReminders Member,
+ [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+We respect your privacy and are a Certified Participant of the BBBOnLine
+ Privacy Program. To be removed from future offers,click here.
+SmartReminders.com is a permission based service. To unsubscribe click here . ');
+INSERT INTO email([from],[to],subject,body) VALUES('benjamin.rogers@enron.com', 'mark.bernstein@enron.com', '', 'The guy you are talking about left CIN under a "cloud of suspicion" sort of
+speak. He was the one who got into several bad deals and PPA''s in California
+for CIN, thus he left on a bad note. Let me know if you need more detail
+than that, I felt this was the type of info you were looking for. Thanks!
+Ben');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'michelle.cash@enron.com', 'Expense Report Receipts Not Received', 'Employee Name: Michelle Cash
+Report Name: Houston Cellular 8-11-01
+Report Date: 12/13/01
+Report ID: 594D37C9ED2111D5B452
+Submitted On: 12/13/01
+
+You are only allowed 2 reports with receipts outstanding. Your expense reports will not be paid until you meet this requirement.');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.mara@enron.com', 'ray.alvarez@enron.com, mark.palmer@enron.com, karen.denne@enron.com,', 'CAISO Emergency Motion -- to discontinue market-based rates for', 'FYI. the latest broadside against the generators.
+
+Sue Mara
+Enron Corp.
+Tel: (415) 782-7802
+Fax:(415) 782-7854
+----- Forwarded by Susan J Mara/NA/Enron on 06/08/2001 12:24 PM -----
+
+
+ "Milner, Marcie" <MMilner@coral-energy.com> 06/08/2001 11:13 AM To: "''smara@enron.com''" <smara@enron.com> cc: Subject: CAISO Emergency Motion
+
+
+Sue, did you see this emergency motion the CAISO filed today? Apparently
+they are requesting that FERC discontinue market-based rates immediately and
+grant refunds plus interest on the difference between cost-based rates and
+market revenues received back to May 2000. They are requesting the
+commission act within 14 days. Have you heard anything about what they are
+doing?
+
+Marcie
+
+http://www.caiso.com/docs/2001/06/08/200106081005526469.pdf
+');
+INSERT INTO email([from],[to],subject,body) VALUES('fletcher.sturm@enron.com', 'eloy.escobar@enron.com', 'Re: General Brinks Position Meeting', 'Eloy,
+
+Who is General Brinks?
+
+Fletch');
+INSERT INTO email([from],[to],subject,body) VALUES('nailia.dindarova@enron.com', 'richard.shapiro@enron.com', 'Documents for Mark Frevert (on EU developments and lessons from', 'Rick,
+
+Here are the documents that Peter has prepared for Mark Frevert.
+
+Nailia
+---------------------- Forwarded by Nailia Dindarova/LON/ECT on 25/06/2001
+16:36 ---------------------------
+
+
+Nailia Dindarova
+25/06/2001 15:36
+To: Michael Brown/Enron@EUEnronXGate
+cc: Ross Sankey/Enron@EUEnronXGate, Eric Shaw/ENRON@EUEnronXGate, Peter
+Styles/LON/ECT@ECT
+
+Subject: Documents for Mark Frevert (on EU developments and lessons from
+California)
+
+Michael,
+
+
+These are the documents that Peter promised to give to you for Mark Frevert.
+He has now handed them to him in person but asked me to transmit them
+electronically to you, as well as Eric and Ross.
+
+Nailia
+
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('peggy.a.kostial@accenture.com', 'dave.samuels@enron.com', 'EOL-Accenture Deal Sheet', 'Dave -
+
+Attached are our comments and suggested changes. Please call to review.
+
+On the time line for completion, we have four critical steps to complete:
+ Finalize market analysis to refine business case, specifically
+ projected revenue stream
+ Complete counterparty surveying, including targeting 3 CPs for letters
+ of intent
+ Review Enron asset base for potential reuse/ licensing
+ Contract negotiations
+
+Joe will come back to us with an updated time line, but it is my
+expectation that we are still on the same schedule (we just begun week
+three) with possibly a week or so slippage.....contract negotiations will
+probably be the critical path.
+
+We will send our cut at the actual time line here shortly. Thanks,
+
+Peggy
+
+(See attached file: accenture-dealpoints v2.doc)
+ - accenture-dealpoints v2.doc ');
+INSERT INTO email([from],[to],subject,body) VALUES('thomas.martin@enron.com', 'thomas.martin@enron.com', 'Re: Guadalupe Power Partners LP', '---------------------- Forwarded by Thomas A Martin/HOU/ECT on 03/20/2001
+03:49 PM ---------------------------
+
+
+Thomas A Martin
+10/11/2000 03:55 PM
+To: Patrick Wade/HOU/ECT@ECT
+cc:
+Subject: Re: Guadalupe Power Partners LP
+
+The deal is physically served at Oasis Waha or Oasis Katy and is priced at
+either HSC, Waha or Katytailgate GD at buyers option three days prior to
+NYMEX close.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('judy.townsend@enron.com', 'dan.junek@enron.com, chris.germany@enron.com', 'Columbia Distribution''s Capacity Available for Release - Sum', '---------------------- Forwarded by Judy Townsend/HOU/ECT on 03/09/2001 11:04
+AM ---------------------------
+
+
+agoddard@nisource.com on 03/08/2001 09:16:57 AM
+To: " - *Koch, Kent" <kkoch@nisource.com>, " -
+*Millar, Debra" <dmillar@nisource.com>, " - *Burke, Lynn"
+<lburke@nisource.com>
+cc: " - *Heckathorn, Tom" <theckathorn@nisource.com>
+Subject: Columbia Distribution''s Capacity Available for Release - Sum
+
+
+Attached is Columbia Distribution''s notice of capacity available for release
+for
+the summer of 2001 (Apr. 2001 through Oct. 2001).
+
+Please note that the deadline for bids is 3:00pm EST on March 20, 2001.
+
+If you have any questions, feel free to contact any of the representatives
+listed
+at the bottom of the attachment.
+
+Aaron Goddard
+
+
+
+
+ - 2001Summer.doc
+');
+INSERT INTO email([from],[to],subject,body) VALUES('rhonda.denton@enron.com', 'tim.belden@enron.com, dana.davis@enron.com, genia.fitzgerald@enron.com,', 'Split Rock Energy LLC', 'We have received the executed EEI contract from this CP dated 12/12/2000.
+Copies will be distributed to Legal and Credit.');
+INSERT INTO email([from],[to],subject,body) VALUES('kerrymcelroy@dwt.com', 'jack.speer@alcoa.com, crow@millernash.com, michaelearly@earthlink.net,', 'Oral Argument Request', ' - Oral Argument Request.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('mike.carson@enron.com', 'rlmichaelis@hormel.com', '', 'Did you come in town this wk end..... My new number at our house is :
+713-668-3712...... my cell # is 281-381-7332
+
+the kid');
+INSERT INTO email([from],[to],subject,body) VALUES('cooper.richey@enron.com', 'trycooper@hotmail.com', 'FW: Contact Info', '
+
+-----Original Message-----
+From: Punja, Karim
+Sent: Thursday, December 13, 2001 2:35 PM
+To: Richey, Cooper
+Subject: Contact Info
+
+
+Cooper,
+
+Its been a real pleasure working with you (even though it was for only a small amount of time)
+I hope we can stay in touch.
+
+Home# 234-0249
+email: kpunja@hotmail.com
+
+Take Care,
+
+Karim.
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('bjm30@earthlink.net', 'mcguinn.k@enron.com, mcguinn.ian@enron.com, mcguinn.stephen@enron.com,', 'email address change', 'Hello all.
+
+I haven''t talked to many of you via email recently but I do want to give you
+my new address for your email file:
+
+ bjm30@earthlink.net
+
+I hope all is well.
+
+Brian McGuinn');
+INSERT INTO email([from],[to],subject,body) VALUES('shelley.corman@enron.com', 'steve.hotte@enron.com', 'Flat Panels', 'Can you please advise what is going on with the flat panels that we had planned to distribute to our gas logistics team. It was in the budget and we had the okay, but now I''m hearing there is some hold-up & the units are stored on 44.
+
+Shelley');
+INSERT INTO email([from],[to],subject,body) VALUES('sara.davidson@enron.com', 'john.schwartzenburg@enron.com, scott.dieball@enron.com, recipients@enron.com,', '2001 Enron Law Conference (Distribution List 2)', ' Enron Law Conference
+
+San Antonio, Texas May 2-4, 2001 Westin Riverwalk
+
+ See attached memo for more details!!
+
+
+? Registration for the law conference this year will be handled through an
+Online RSVP Form on the Enron Law Conference Website at
+http://lawconference.corp.enron.com. The website is still under construction
+and will not be available until Thursday, March 15, 2001.
+
+? We will send you another e-mail to confirm when the Law Conference Website
+is operational.
+
+? Please complete the Online RSVP Form as soon as it is available and submit
+it no later than Friday, March 30th.
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('tori.kuykendall@enron.com', 'heath.b.taylor@accenture.com', 'Re:', 'hey - thats funny about john - he definitely remembers him - i''ll call pat
+and let him know - we are coming on saturday - i just havent had a chance to
+call you guys back -- looking forward to it -- i probably need the
+directions again though');
+INSERT INTO email([from],[to],subject,body) VALUES('darron.giron@enron.com', 'bryce.baxter@enron.com', 'Re: Feedback for Audrey Cook', 'Bryce,
+
+I''ll get it done today.
+
+DG 3-9573
+
+
+
+
+
+ From: Bryce Baxter 06/12/2000 07:15 PM
+
+
+To: Darron C Giron/HOU/ECT@ECT
+cc:
+Subject: Feedback for Audrey Cook
+
+You were identified as a reviewer for Audrey Cook. If possible, could you
+complete her feedback by end of business Wednesday? It will really help me
+in the PRC process to have your input. Thanks.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('casey.evans@enron.com', 'stephanie.sever@enron.com', 'Gas EOL ID', 'Stephanie,
+
+In conjunction with the recent movement of several power traders, they are changing the names of their gas books as well. The names of the new gas books and traders are as follows:
+
+PWR-NG-LT-SPP: Mike Carson
+PWR-NG-LT-SERC: Jeff King
+
+If you need to know their power desk to map their ID to their gas books, those desks are as follows:
+
+EPMI-LT-SPP: Mike Carson
+EPMI-LT-SERC: Jeff King
+
+I will be in training this afternoon, but will be back when class is over. Let me know if you have any questions.
+
+Thanks for your help!
+Casey');
+INSERT INTO email([from],[to],subject,body) VALUES('darrell.schoolcraft@enron.com', 'david.roensch@enron.com, kimberly.watson@enron.com, michelle.lokay@enron.com,', 'Postings', 'Please see the attached.
+
+
+ds
+
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('mcominsky@aol.com', 'cpatman@bracepatt.com, james_derrick@enron.com', 'Jurisprudence Luncheon', 'Carrin & Jim --
+
+It was an honor and a pleasure to meet both of you yesterday. I know we will
+have fun working together on this very special event.
+
+Jeff left the jurisprudence luncheon lists for me before he left on vacation.
+ I wasn''t sure whether he transmitted them to you as well. Would you please
+advise me if you would like them sent to you? I can email the MS Excel files
+or I can fax the hard copies to you. Please advise what is most convenient.
+
+I plan to be in town through the holidays and can be reached by phone, email,
+or cell phone at any time. My cell phone number is 713/705-4829.
+
+Thanks again for your interest in the ADL''s work. Martin.
+
+Martin B. Cominsky
+Director, Southwest Region
+Anti-Defamation League
+713/627-3490, ext. 122
+713/627-2011 (fax)
+MCominsky@aol.com');
+INSERT INTO email([from],[to],subject,body) VALUES('phillip.love@enron.com', 'todagost@utmb.edu, gbsonnta@utmb.edu', 'New President', 'I had a little bird put a word in my ear. Is there any possibility for Ben
+Raimer to be Bush''s secretary of HHS? Just curious about that infamous UTMB
+rumor mill. Hope things are well, happy holidays.
+PL');
+INSERT INTO email([from],[to],subject,body) VALUES('marie.heard@enron.com', 'ehamilton@fna.com', 'ISDA Master Agreement', 'Erin:
+
+Pursuant to your request, attached are the Schedule to the ISDA Master Agreement, together with Paragraph 13 to the ISDA Credit Support Annex. Please let me know if you need anything else. We look forward to hearing your comments.
+
+Marie
+
+Marie Heard
+Senior Legal Specialist
+Enron North America Corp.
+Phone: (713) 853-3907
+Fax: (713) 646-3490
+marie.heard@enron.com
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('andrea.ring@enron.com', 'beverly.beaty@enron.com', 'Re: Tennessee Buy - Louis Dreyfus', 'Beverly - once again thanks so much for your help on this.
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('karolyn.criado@enron.com', 'j..bonin@enron.com, felicia.case@enron.com, b..clapp@enron.com,', 'Price List week of Oct. 8-9, 2001', '
+Please contact me if you have any questions regarding last weeks prices.
+
+Thank you,
+Karolyn Criado
+3-9441
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.presto@enron.com', 'edward.baughman@enron.com, billy.braddock@enron.com', 'Associated', 'Please begin working on filling our Associated short position in 02. I would like to take this risk off the books.
+
+In addition, please find out what a buy-out of VEPCO would cost us. With Rogers transitioning to run our retail risk management, I would like to clean up our customer positions.
+
+We also need to continue to explore a JEA buy-out.
+
+Thanks.');
+INSERT INTO email([from],[to],subject,body) VALUES('stacy.dickson@enron.com', 'gregg.penman@enron.com', 'RE: Constellation TC 5-7-01', 'Gregg,
+
+I am at home with a sick baby. (Lots of fun!) I will call you about this
+tomorrow.
+
+Stacy');
+INSERT INTO email([from],[to],subject,body) VALUES('joe.quenet@enron.com', 'dfincher@utilicorp.com', '', 'hey big guy.....check this out.....
+
+ w ww.gorelieberman-2000.com/');
+INSERT INTO email([from],[to],subject,body) VALUES('k..allen@enron.com', 'jacqestc@aol.com', '', 'Jacques,
+
+I sent you a fax of Kevin Kolb''s comments on the release. The payoff on the note would be $36,248 ($36090(principal) + $158 (accrued interest)).
+This is assuming we wrap this up on Tuesday.
+
+Please email to confirm that their changes are ok so I can set up a meeting on Tuesday to reach closure.
+
+Phillip');
+INSERT INTO email([from],[to],subject,body) VALUES('kourtney.nelson@enron.com', 'mike.swerzbin@enron.com', 'Adjusted L/R Balance', 'Mike,
+
+I placed the adjusted L/R Balance on the Enronwest site. It is under the "Staff/Kourtney Nelson". There are two links:
+
+1) "Adj L_R" is the same data/format from the weekly strategy meeting.
+2) "New Gen 2001_2002" link has all of the supply side info that is used to calculate the L/R balance
+ -Please note the Data Flag column, a value of "3" indicates the project was cancelled, on hold, etc and is not included in the calc.
+
+Both of these sheets are interactive Excel spreadsheets and thus you can play around with the data as you please. Also, James Bruce is working to get his gen report on the web. That will help with your access to information on new gen.
+
+Please let me know if you have any questions or feedback,
+
+Kourtney
+
+
+
+Kourtney Nelson
+Fundamental Analysis
+Enron North America
+(503) 464-8280
+kourtney.nelson@enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('d..thomas@enron.com', 'naveed.ahmed@enron.com', 'FW: Current Enron TCC Portfolio', '
+
+-----Original Message-----
+From: Grace, Rebecca M.
+Sent: Monday, December 17, 2001 9:44 AM
+To: Thomas, Paul D.
+Cc: Cashion, Jim; Allen, Thresa A.; May, Tom
+Subject: RE: Current Enron TCC Portfolio
+
+
+Paul,
+
+I reviewed NY''s list. I agree with all of their contracts numbers and mw amounts.
+
+Call if you have any more questions.
+
+Rebecca
+
+
+
+ -----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:08 AM
+To: Grace, Rebecca M.
+Subject: FW: Current Enron TCC Portfolio
+
+ << File: enrontccs.xls >>
+Rebecca,
+Let me know if you see any differences.
+
+Paul
+X 3-0403
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:04 AM
+To: Ahmed, Naveed
+Subject: FW: Current Enron TCC Portfolio
+
+
+
+
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Thursday, December 13, 2001 10:01 AM
+To: Baughman, Edward D.
+Subject: Current Enron TCC Portfolio
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('stephanie.panus@enron.com', 'william.bradford@enron.com, debbie.brackett@enron.com,', 'Coastal Merchant Energy/El Paso Merchant Energy', 'Coastal Merchant Energy, L.P. merged with and into El Paso Merchant Energy,
+L.P., effective February 1, 2001, with the surviving entity being El Paso
+Merchant Energy, L.P. We currently have ISDA Master Agreements with both
+counterparties. Please see the attached memo regarding the existing Masters
+and let us know which agreement should be terminated.
+
+Thanks,
+Stephanie
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kam.keiser@enron.com', 'c..kenne@enron.com', 'RE: What about this too???', '
+
+ -----Original Message-----
+From: Kenne, Dawn C.
+Sent: Wednesday, February 06, 2002 11:50 AM
+To: Keiser, Kam
+Subject: What about this too???
+
+
+ << File: Netco Trader Matrix.xls >>
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.meyer@enron.com', 'joe.parks@enron.com', 'Centana', 'Talked to Chip. We do need Cash Committe approval given the netting feature of your deal, which means Batch Funding Request. Please update per my previous e-mail and forward.
+
+Thanks
+
+chris
+x31666');
+INSERT INTO email([from],[to],subject,body) VALUES('debra.perlingiere@enron.com', 'jworman@academyofhealth.com', '', 'Have a great weekend! Happy Fathers Day!
+
+
+Debra Perlingiere
+Enron North America Corp.
+1400 Smith Street, EB 3885
+Houston, Texas 77002
+dperlin@enron.com
+Phone 713-853-7658
+Fax 713-646-3490');
+INSERT INTO email([from],[to],subject,body) VALUES('outlook.team@enron.com', '', 'Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia &', 'CALENDAR ENTRY: APPOINTMENT
+
+Description:
+ Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia & Dir Rpts. - 4102
+
+Date: 1/5/2001
+Time: 9:00 AM - 10:00 AM (Central Standard Time)
+
+Chairperson: Outlook Migration Team
+
+Detailed Description:');
+INSERT INTO email([from],[to],subject,body) VALUES('diana.seifert@enron.com', 'mark.taylor@enron.com', 'Guest access Chile', 'Hello Mark,
+
+Justin Boyd told me that your can help me with questions regarding Chile.
+We got a request for guest access through MG.
+The company is called Escondida and is a subsidiary of BHP Australia.
+
+Please advise if I can set up a guest account or not.
+F.Y.I.: MG is planning to put a "in w/h Chile" contract for Copper on-line as
+soon as Enron has done the due diligence for this country.
+Thanks !
+
+
+Best regards
+
+Diana Seifert
+EOL PCG');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'mark.whitt@enron.com', '<<Concur Expense Document>> - 121001', 'The Approval status has changed on the following report:
+
+Status last changed by: Barry L. Tycholiz
+Expense Report Name: 121001
+Report Total: $198.98
+Amount Due Employee: $198.98
+Amount Approved: $198.98
+Amount Paid: $0.00
+Approval Status: Approved
+Payment Status: Pending
+
+
+To review this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.hyatt@enron.com', '', 'Technical Support', 'Outside the U.S., please refer to the list below:
+
+Australia:
+1800 678-515
+support@palm-au.com
+
+Canada:
+1905 305-6530
+support@palm.com
+
+New Zealand:
+0800 446-398
+support@palm-nz.com
+
+U.K.:
+0171 867 0108
+eurosupport@palm.3com.com
+
+Please refer to the Worldwide Customer Support card for a complete technical support contact list.');
+INSERT INTO email([from],[to],subject,body) VALUES('geoff.storey@enron.com', 'dutch.quigley@enron.com', 'RE:', 'duke contact?
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 10:14 AM
+To: Storey, Geoff
+Subject: RE:
+
+bp corp Albert LaMore 281-366-4962
+
+running the reports now
+
+
+ -----Original Message-----
+From: Storey, Geoff
+Sent: Wednesday, October 31, 2001 10:10 AM
+To: Quigley, Dutch
+Subject: RE:
+
+give me a contact over there too
+BP
+
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 9:42 AM
+To: Storey, Geoff
+Subject:
+
+Coral Jeff Whitnah 713-767-5374
+Relaint Steve McGinn 713-207-4000');
+INSERT INTO email([from],[to],subject,body) VALUES('pete.davis@enron.com', 'pete.davis@enron.com', 'Start Date: 4/22/01; HourAhead hour: 3; <CODESITE>', 'Start Date: 4/22/01; HourAhead hour: 3; No ancillary schedules awarded.
+Variances detected.
+Variances detected in Load schedule.
+
+ LOG MESSAGES:
+
+PARSING FILE -->> O:\Portland\WestDesk\California Scheduling\ISO Final
+Schedules\2001042203.txt
+
+---- Load Schedule ----
+$$$ Variance found in table tblLoads.
+ Details: (Hour: 3 / Preferred: 1.92 / Final: 1.89)
+ TRANS_TYPE: FINAL
+ LOAD_ID: PGE4
+ MKT_TYPE: 2
+ TRANS_DATE: 4/22/01
+ SC_ID: EPMI
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('john.postlethwaite@enron.com', 'john.zufferli@enron.com', 'Reference', 'John, hope things are going well up there for you. The big day is almost here for you and Jessica. I was wondering if I could use your name as a job reference if need be. I am just trying to get everything in order just in case something happens.
+
+John');
+INSERT INTO email([from],[to],subject,body) VALUES('jeffrey.shankman@enron.com', 'lschiffm@jonesday.com', 'Re:', 'I saw you called on the cell this a.m. Sorry I missed you. (I was in the
+shower). I have had a shitty week--I suspect my silence (not only to you,
+but others) after our phone call is a result of the week. I''m seeing Glen at
+11:15....talk to you');
+INSERT INTO email([from],[to],subject,body) VALUES('litebytz@enron.com', '', 'Lite Bytz RSVP', '
+This week''s Lite Bytz presentation will feature the following TOOLZ speaker:
+
+Richard McDougall
+Solaris 8
+Thursday, June 7, 2001
+
+If you have not already signed up, please RSVP via email to litebytz@enron.com by the end of the day Tuesday, June 5, 2001.
+
+*Remember: this is now a Brown Bag Event--so bring your lunch and we will provide cookies and drinks.
+
+Click below for more details.
+
+http://home.enron.com:84/messaging/litebytztoolzprint.jpg');
+ COMMIT;
+ }
+} {}
+
+###############################################################################
+# Everything above just builds an interesting test database. The actual
+# tests come after this comment.
+###############################################################################
+
+do_test fts2c-1.2 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark'
+ }
+} {6 17 25 38 40 42 73 74}
+do_test fts2c-1.3 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan'
+ }
+} {24 40}
+do_test fts2c-1.4 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark susan'
+ }
+} {40}
+do_test fts2c-1.5 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan mark'
+ }
+} {40}
+do_test fts2c-1.6 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '"mark susan"'
+ }
+} {}
+do_test fts2c-1.7 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark -susan'
+ }
+} {6 17 25 38 42 73 74}
+do_test fts2c-1.8 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '-mark susan'
+ }
+} {24}
+do_test fts2c-1.9 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark OR susan'
+ }
+} {6 17 24 25 38 40 42 73 74}
+
+# Some simple tests of the automatic "offsets(email)" column. In the sample
+# data set above, only one message, number 20, contains the words
+# "gas" and "reminder" in both body and subject.
+#
+do_test fts2c-2.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts2c-2.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 1 54 8}}
+do_test fts2c-2.3 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'body:gas reminder'
+ }
+} {20 {2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts2c-2.4 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE subject MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8}}
+do_test fts2c-2.5 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'gas reminder'
+ }
+} {20 {3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+
+# Document 32 contains 5 instances of the world "child". But only
+# 3 of them are paired with "product". Make sure only those instances
+# that match the phrase appear in the offsets(email) list.
+#
+do_test fts2c-3.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'child product' AND +rowid=32
+ }
+} {32 {3 0 94 5 3 0 114 5 3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7 3 1 493 7}}
+do_test fts2c-3.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH '"child product"'
+ }
+} {32 {3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7}}
+
+# Snippet generator tests
+#
+do_test fts2c-4.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {{Alert Posted 10:00 AM November 20,2000: E-<b>GAS</b> Request <b>Reminder</b>}}
+do_test fts2c-4.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'christmas candlelight'
+ }
+} {{<b>...</b> place.? What do you think about going here <b>Christmas</b>
+eve?? They have an 11:00 a.m. service and a <b>candlelight</b> service at 5:00 p.m.,
+among others. <b>...</b>}}
+
+do_test fts2c-4.3 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <b>Deal</b> <b>Sheet</b> <b>...</b> intent
+ Review Enron asset base for <b>potential</b> <b>reuse</b>/ licensing
+ Contract negotiations <b>...</b>}}
+do_test fts2c-4.4 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <<<Deal>>> <<<Sheet>>> intent
+ Review Enron asset base for <<<potential>>> <<<reuse>>>/ licensing
+ Contract negotiations }}
+do_test fts2c-4.5 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'first things'
+ }
+} {{Re: <<<First>>> Polish Deal! Congrats! <<<Things>>> seem to be building rapidly now on the }}
+do_test fts2c-4.6 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'chris is here'
+ }
+} {{<b>chris</b>.germany@enron.com <b>...</b> Sounds good to me. I bet this <b>is</b> next to the Warick?? Hotel. <b>...</b> place.? What do you think about going <b>here</b> Christmas
+eve?? They have an 11:00 a.m. <b>...</b>}}
+do_test fts2c-4.7 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH '"pursuant to"'
+ }
+} {{Erin:
+
+<b>Pursuant</b> <b>to</b> your request, attached are the Schedule to <b>...</b>}}
+do_test fts2c-4.8 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'ancillary load davis'
+ }
+} {{pete.<b>davis</b>@enron.com <b>...</b> Start Date: 4/22/01; HourAhead hour: 3; No <b>ancillary</b> schedules awarded.
+Variances detected.
+Variances detected in <b>Load</b> schedule.
+
+ LOG MESSAGES:
+
+PARSING <b>...</b>}}
+
+# Combinations of AND and OR operators:
+#
+do_test fts2c-5.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'questar enron OR com'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+do_test fts2c-5.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'enron OR com questar'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2d.test b/third_party/sqlite/test/fts2d.test
new file mode 100755
index 0000000..d8090d8
--- /dev/null
+++ b/third_party/sqlite/test/fts2d.test
@@ -0,0 +1,65 @@
+# 2006 October 1
+#
+# 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 testing the FTS2 module, and in particular
+# the Porter stemmer.
+#
+# $Id: fts2d.test,v 1.1 2006/10/19 23:36:26 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+do_test fts2d-1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts2(content, tokenize porter);
+ INSERT INTO t1(rowid, content) VALUES(1, 'running and jumping');
+ SELECT rowid FROM t1 WHERE content MATCH 'run jump';
+ }
+} {1}
+do_test fts2d-1.2 {
+ execsql {
+ SELECT snippet(t1) FROM t1 WHERE t1 MATCH 'run jump';
+ }
+} {{<b>running</b> and <b>jumping</b>}}
+do_test fts2d-1.3 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(2, 'abcdefghijklmnopqrstuvwyxz');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts2d-1.4 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijXXXXqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts2d-1.5 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(3, 'The value is 123456789');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123789'
+ }
+} {3 {The value is <b>123456789</b>}}
+do_test fts2d-1.6 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123000000789'
+ }
+} {3 {The value is <b>123456789</b>}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts2e.test b/third_party/sqlite/test/fts2e.test
new file mode 100755
index 0000000..71845ac
--- /dev/null
+++ b/third_party/sqlite/test/fts2e.test
@@ -0,0 +1,85 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing deletions in the FTS2 module.
+#
+# $Id: fts2e.test,v 1.1 2006/10/19 23:36:26 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 30 INSERT and
+# DELETE statements, so that we'll test both the segmentMerge() merge
+# (over the first 16) and the termSelect() merge (over the level-1
+# segment and 14 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ DELETE FROM t1 WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ DELETE FROM t1 WHERE rowid = 22;
+}
+
+do_test fts2f-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {14}
+
+do_test fts2e-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {3 5 9 11 15 17 21}
+
+do_test fts2e-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 11 14 15 18}
+
+do_test fts2e-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {5 6 12 14 15 20 21}
+
+do_test fts2e-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {8 9 11 12 14 15}
+
+do_test fts2e-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {17 18 20 21}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2f.test b/third_party/sqlite/test/fts2f.test
new file mode 100755
index 0000000..49cff14
--- /dev/null
+++ b/third_party/sqlite/test/fts2f.test
@@ -0,0 +1,90 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing updates in the FTS2 module.
+#
+# $Id: fts2f.test,v 1.2 2007/02/23 00:14:06 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 31 INSERT,
+# UPDATE, and DELETE statements, so that we'll test both the
+# segmentMerge() merge (over the first 16) and the termSelect() merge
+# (over the level-1 segment and 15 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ UPDATE t1 SET content = 'update one three' WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ UPDATE t1 SET content = 'update two five' WHERE rowid = 8;
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ UPDATE t1 SET content = 'update' WHERE rowid = 15;
+}
+
+do_test fts2f-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {16}
+
+do_test fts2f-2.0 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'update'}
+} {1 8 15}
+
+do_test fts2f-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 9 11 17 21}
+
+do_test fts2f-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 8 11 14 18 22}
+
+do_test fts2f-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {1 5 6 12 14 20 21 22}
+
+do_test fts2f-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {9 11 12 14}
+
+do_test fts2f-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {8 17 18 20 21 22}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2g.test b/third_party/sqlite/test/fts2g.test
new file mode 100755
index 0000000..4cffb91
--- /dev/null
+++ b/third_party/sqlite/test/fts2g.test
@@ -0,0 +1,93 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing handling of edge cases for various doclist
+# merging functions in the FTS2 module query logic.
+#
+# $Id: fts2g.test,v 1.3 2007/11/16 00:23:08 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'this is a test');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'also a test');
+}
+
+# No hits at all. Returns empty doclists from termSelect().
+do_test fts2g-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
+} {}
+
+# Empty left in docListExceptMerge().
+do_test fts2g-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this something'}
+} {}
+
+# Empty right in docListExceptMerge().
+do_test fts2g-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this -something'}
+} {1}
+
+# Empty left in docListPhraseMerge().
+do_test fts2g-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"this something"'}
+} {}
+
+# Empty right in docListPhraseMerge().
+do_test fts2g-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"something is"'}
+} {}
+
+# Empty left in docListOrMerge().
+do_test fts2g-1.6 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something OR this'}
+} {1}
+
+# Empty right in docListOrMerge().
+do_test fts2g-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR something'}
+} {1}
+
+# Empty left in docListAndMerge().
+do_test fts2g-1.8 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something this'}
+} {}
+
+# Empty right in docListAndMerge().
+do_test fts2g-1.9 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this something'}
+} {}
+
+# No support for all-except queries.
+do_test fts2g-1.10 {
+ catchsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this -something'}
+} {1 {SQL logic error or missing database}}
+
+# Test that docListOrMerge() correctly handles reaching the end of one
+# doclist before it reaches the end of the other.
+do_test fts2g-1.11 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR also'}
+} {1 2}
+do_test fts2g-1.12 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'also OR this'}
+} {1 2}
+
+# Empty left and right in docListOrMerge(). Each term matches neither
+# row, and when combined there was an assertion failure.
+do_test fts2g-1.13 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something OR nothing'}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2h.test b/third_party/sqlite/test/fts2h.test
new file mode 100755
index 0000000..72561d8
--- /dev/null
+++ b/third_party/sqlite/test/fts2h.test
@@ -0,0 +1,76 @@
+# 2006 October 31 (scaaarey)
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# here is testing correct handling of excessively long terms.
+#
+# $Id: fts2h.test,v 1.1 2006/11/29 21:03:01 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Generate a term of len copies of char.
+proc bigterm {char len} {
+ for {set term ""} {$len>0} {incr len -1} {
+ append term $char
+ }
+ return $term
+}
+
+# Generate a document of bigterms based on characters from the list
+# chars.
+proc bigtermdoc {chars len} {
+ set doc ""
+ foreach char $chars {
+ append doc " " [bigterm $char $len]
+ }
+ return $doc
+}
+
+set len 5000
+set doc1 [bigtermdoc {a b c d} $len]
+set doc2 [bigtermdoc {b d e f} $len]
+set doc3 [bigtermdoc {a c e} $len]
+
+set aterm [bigterm a $len]
+set bterm [bigterm b $len]
+set xterm [bigterm x $len]
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, $doc1);
+ INSERT INTO t1 (rowid, content) VALUES(2, $doc2);
+ INSERT INTO t1 (rowid, content) VALUES(3, $doc3);
+}
+
+# No hits at all. Returns empty doclists from termSelect().
+do_test fts2h-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
+} {}
+
+do_test fts2h-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH $aterm}
+} {1 3}
+
+do_test fts2h-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH $xterm}
+} {}
+
+do_test fts2h-1.3 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '$aterm -$xterm'"
+} {1 3}
+
+do_test fts2h-1.4 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"$aterm $bterm\"'"
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2i.test b/third_party/sqlite/test/fts2i.test
new file mode 100755
index 0000000..e732e6a
--- /dev/null
+++ b/third_party/sqlite/test/fts2i.test
@@ -0,0 +1,87 @@
+# 2007 January 17
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite fts2 library. The
+# focus here is testing handling of UPDATE when using UTF-16-encoded
+# databases.
+#
+# $Id: fts2i.test,v 1.2 2007/01/24 03:46:35 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+# NOTE(shess) Copied from capi3.test.
+proc utf16 {str {nt 1}} {
+ set r [encoding convertto unicode $str]
+ if {$nt} {
+ append r "\x00\x00"
+ }
+ return $r
+}
+
+db eval {
+ PRAGMA encoding = "UTF-16le";
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+}
+
+do_test fts2i-1.0 {
+ execsql {PRAGMA encoding}
+} {UTF-16le}
+
+do_test fts2i-1.1 {
+ execsql {INSERT INTO t1 (rowid, content) VALUES(1, 'one')}
+ execsql {SELECT content FROM t1 WHERE rowid = 1}
+} {one}
+
+do_test fts2i-1.2 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(2, 'two')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 2}
+} {two}
+
+do_test fts2i-1.3 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(3, 'three')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'trois' WHERE rowid = 3"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 3}
+} {trois}
+
+do_test fts2i-1.4 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(4, 'four')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 4}
+} {four}
+
+do_test fts2i-1.5 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(5, 'five')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'cinq' WHERE rowid = 5"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 5}
+} {cinq}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2j.test b/third_party/sqlite/test/fts2j.test
new file mode 100755
index 0000000..b8a89b2
--- /dev/null
+++ b/third_party/sqlite/test/fts2j.test
@@ -0,0 +1,89 @@
+# 2007 February 6
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. This
+# tests creating fts2 tables in an attached database.
+#
+# $Id: fts2j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Clean up anything left over from a previous pass.
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts2(content);
+ INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
+}
+
+db2 eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# This has always worked because the t1_* tables used by fts2 will be
+# the defaults.
+do_test fts2j-1.1 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+# Make certain we're detached if there was an error.
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this appears to work fine, but the t2_* tables used
+# by fts2 will be created in database 'main' instead of database
+# 'two'. It appears to work fine because the tables end up being the
+# defaults, but obviously is badly broken if you hope to use things
+# other than in the exact same ATTACH setup.
+do_test fts2j-1.2 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ CREATE VIRTUAL TABLE two.t2 USING fts2(content);
+ INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this broke because the fts2 code attempted to create
+# t3_* tables in database 'main', but they already existed. Normally
+# this wouldn't happen without t3 itself existing, in which case the
+# fts2 code would never be called in the first place.
+do_test fts2j-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+
+ CREATE VIRTUAL TABLE two.t3 USING fts2(content);
+ INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
+
+ DETACH DATABASE two;
+ } db2
+} {2}
+catch {db eval {DETACH DATABASE two}}
+
+catch {db2 close}
+file delete -force test2.db
+
+finish_test
diff --git a/third_party/sqlite/test/fts2k.test b/third_party/sqlite/test/fts2k.test
new file mode 100755
index 0000000..e7d5f0d
--- /dev/null
+++ b/third_party/sqlite/test/fts2k.test
@@ -0,0 +1,105 @@
+# 2007 March 9
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. These
+# make sure that fts2 insertion buffering is fully transparent when
+# using transactions.
+#
+# $Id: fts2k.test,v 1.2 2007/08/10 23:47:04 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# Test that possibly-buffered inserts went through after commit.
+do_test fts2k-1.1 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(4, "false world");
+ INSERT INTO t1 (rowid, content) VALUES(5, "false door");
+ COMMIT TRANSACTION;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4}
+
+# Test that buffered inserts are seen by selects in the same
+# transaction.
+do_test fts2k-1.2 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(6, "another world");
+ INSERT INTO t1 (rowid, content) VALUES(7, "another test");
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ COMMIT TRANSACTION;
+ }
+} {1 3 4 6}
+
+# Test that buffered inserts are seen within a transaction. This is
+# really the same test as 1.2.
+do_test fts2k-1.3 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(8, "second world");
+ INSERT INTO t1 (rowid, content) VALUES(9, "second sight");
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ ROLLBACK TRANSACTION;
+ }
+} {1 3 4 6 8}
+
+# Double-check that the previous result doesn't persist past the
+# rollback!
+do_test fts2k-1.4 {
+ execsql {
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4 6}
+
+# Test it all together.
+do_test fts2k-1.5 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(10, "second world");
+ INSERT INTO t1 (rowid, content) VALUES(11, "second sight");
+ ROLLBACK TRANSACTION;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4 6}
+
+# Test that the obvious case works.
+do_test fts2k-1.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 (rowid, content) VALUES(12, "third world");
+ COMMIT;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'third';
+ }
+} {12}
+
+# This is exactly the same as the previous test, except that older
+# code loses the INSERT due to an SQLITE_SCHEMA error.
+do_test fts2k-1.7 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 (rowid, content) VALUES(13, "third dimension");
+ CREATE TABLE x (c);
+ COMMIT;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'dimension';
+ }
+} {13}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2l.test b/third_party/sqlite/test/fts2l.test
new file mode 100755
index 0000000..42f5ba1
--- /dev/null
+++ b/third_party/sqlite/test/fts2l.test
@@ -0,0 +1,69 @@
+# 2007 March 28
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing isspace/isalnum/tolower problems with the
+# FTS2 module. Unfortunately, this code isn't a really principled set
+# of tests, because it is impossible to know where new uses of these
+# functions might appear.
+#
+# $Id: fts2l.test,v 1.2 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Tests that startsWith() (calls isspace, tolower, isalnum) can handle
+# hi-bit chars. parseSpec() also calls isalnum here.
+do_test fts2l-1.1 {
+ execsql "CREATE VIRTUAL TABLE t1 USING fts2(content, \x80)"
+} {}
+
+# Additionally tests isspace() call in getToken(), and isalnum() call
+# in tokenListToIdList().
+do_test fts2l-1.2 {
+ catch {
+ execsql "CREATE VIRTUAL TABLE t2 USING fts2(content, tokenize \x80)"
+ }
+ sqlite3_errmsg $DB
+} "unknown tokenizer: \x80"
+
+# Additionally test final isalnum() in startsWith().
+do_test fts2l-1.3 {
+ execsql "CREATE VIRTUAL TABLE t3 USING fts2(content, tokenize\x80)"
+} {}
+
+# The snippet-generation code has calls to isspace() which are sort of
+# hard to get to. It finds convenient breakpoints by starting ~40
+# chars before and after the matched term, and scanning ~10 chars
+# around that position for isspace() characters. The long word with
+# embedded hi-bit chars causes one of these isspace() calls to be
+# exercised. The version with a couple extra spaces should cause the
+# other isspace() call to be exercised. [Both cases have been tested
+# in the debugger, but I'm hoping to continue to catch it if simple
+# constant changes change things slightly.
+#
+# The trailing and leading hi-bit chars help with code which tests for
+# isspace() to coalesce multiple spaces.
+
+set word "\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80"
+set phrase1 "$word $word $word target $word $word $word"
+set phrase2 "$word $word $word target $word $word $word"
+
+db eval {CREATE VIRTUAL TABLE t4 USING fts2(content)}
+db eval "INSERT INTO t4 (content) VALUES ('$phrase1')"
+db eval "INSERT INTO t4 (content) VALUES ('$phrase2')"
+
+do_test fts2l-1.4 {
+ execsql {SELECT rowid, length(snippet(t4)) FROM t4 WHERE t4 MATCH 'target'}
+} {1 111 2 117}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2m.test b/third_party/sqlite/test/fts2m.test
new file mode 100755
index 0000000..6552637
--- /dev/null
+++ b/third_party/sqlite/test/fts2m.test
@@ -0,0 +1,65 @@
+# 2007 April 9
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. fts2
+# DELETE handling assumed all fields were non-null. This was not
+# the intention at all.
+#
+# $Id: fts2m.test,v 1.1 2007/04/09 20:45:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(col_a, col_b);
+
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(1, 'testing', 'testing');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(2, 'only a', null);
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(3, null, 'only b');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(4, null, null);
+}
+
+do_test fts2m-1.0 {
+ execsql {
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {2 2 4}
+
+do_test fts2m-1.1 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 1;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {1 1 3}
+
+do_test fts2m-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 2;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 1 2}
+
+do_test fts2m-1.3 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 3;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 1}
+
+do_test fts2m-1.4 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 4;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 0}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2n.test b/third_party/sqlite/test/fts2n.test
new file mode 100755
index 0000000..ca0b4fe
--- /dev/null
+++ b/third_party/sqlite/test/fts2n.test
@@ -0,0 +1,196 @@
+# 2007 April 26
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements tests for prefix-searching in the fts2
+# component of the SQLite library.
+#
+# $Id: fts2n.test,v 1.2 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# A large string to prime the pump with.
+set text {
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas
+ iaculis mollis ipsum. Praesent rhoncus placerat justo. Duis non quam
+ sed turpis posuere placerat. Curabitur et lorem in lorem porttitor
+ aliquet. Pellentesque bibendum tincidunt diam. Vestibulum blandit
+ ante nec elit. In sapien diam, facilisis eget, dictum sed, viverra
+ at, felis. Vestibulum magna. Sed magna dolor, vestibulum rhoncus,
+ ornare vel, vulputate sit amet, felis. Integer malesuada, tellus at
+ luctus gravida, diam nunc porta nibh, nec imperdiet massa metus eu
+ lectus. Aliquam nisi. Nunc fringilla nulla at lectus. Suspendisse
+ potenti. Cum sociis natoque penatibus et magnis dis parturient
+ montes, nascetur ridiculus mus. Pellentesque odio nulla, feugiat eu,
+ suscipit nec, consequat quis, risus.
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+
+ INSERT INTO t1(rowid, c) VALUES(1, $text);
+ INSERT INTO t1(rowid, c) VALUES(2, 'Another lovely row');
+}
+
+# Exact match
+do_test fts2n-1.1 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lorem'"
+} {1}
+
+# And a prefix
+do_test fts2n-1.2 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lore*'"
+} {1}
+
+# Prefix includes exact match
+do_test fts2n-1.3 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lorem*'"
+} {1}
+
+# Make certain everything isn't considered a prefix!
+do_test fts2n-1.4 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lore'"
+} {}
+
+# Prefix across multiple rows.
+do_test fts2n-1.5 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lo*'"
+} {1 2}
+
+# Likewise, with multiple hits in one document.
+do_test fts2n-1.6 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'l*'"
+} {1 2}
+
+# Prefix which should only hit one document.
+do_test fts2n-1.7 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lov*'"
+} {2}
+
+# * not at end is dropped.
+do_test fts2n-1.8 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lo *'"
+} {}
+
+# Stand-alone * is dropped.
+do_test fts2n-1.9 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '*'"
+} {}
+
+# Phrase-query prefix.
+do_test fts2n-1.10 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"lovely r*\"'"
+} {2}
+do_test fts2n-1.11 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"lovely r\"'"
+} {}
+
+# Phrase query with multiple prefix matches.
+do_test fts2n-1.12 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"a* l*\"'"
+} {1 2}
+
+# Phrase query with multiple prefix matches.
+do_test fts2n-1.13 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"a* l* row\"'"
+} {2}
+
+
+
+
+# Test across updates (and, by implication, deletes).
+
+# Version of text without "lorem".
+regsub -all {[Ll]orem} $text '' ntext
+
+db eval {
+ CREATE VIRTUAL TABLE t2 USING fts2(c);
+
+ INSERT INTO t2(rowid, c) VALUES(1, $text);
+ INSERT INTO t2(rowid, c) VALUES(2, 'Another lovely row');
+ UPDATE t2 SET c = $ntext WHERE rowid = 1;
+}
+
+# Can't see lorem as an exact match.
+do_test fts2n-2.1 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lorem'"
+} {}
+
+# Can't see a prefix of lorem, either.
+do_test fts2n-2.2 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lore*'"
+} {}
+
+# Can see lovely in the other document.
+do_test fts2n-2.3 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lo*'"
+} {2}
+
+# Can still see other hits.
+do_test fts2n-2.4 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'l*'"
+} {1 2}
+
+# Prefix which should only hit one document.
+do_test fts2n-2.5 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lov*'"
+} {2}
+
+
+
+# Test with a segment which will have multiple levels in the tree.
+
+# Build a big document with lots of unique terms.
+set bigtext $text
+foreach c {a b c d e} {
+ regsub -all {[A-Za-z]+} $bigtext "&$c" t
+ append bigtext $t
+}
+
+# Populate a table with many copies of the big document, so that we
+# can test the number of hits found. Populate $ret with the expected
+# hit counts for each row. offsets() returns 4 elements for every
+# hit. We'll have 6 hits for row 1, 1 for row 2, and 6*(2^5)==192 for
+# $bigtext.
+set ret {6 1}
+db eval {
+ BEGIN;
+ CREATE VIRTUAL TABLE t3 USING fts2(c);
+
+ INSERT INTO t3(rowid, c) VALUES(1, $text);
+ INSERT INTO t3(rowid, c) VALUES(2, 'Another lovely row');
+}
+for {set i 0} {$i<100} {incr i} {
+ db eval {INSERT INTO t3(rowid, c) VALUES(3+$i, $bigtext)}
+ lappend ret 192
+}
+db eval {COMMIT;}
+
+# Test that we get the expected number of hits.
+do_test fts2n-3.1 {
+ set t {}
+ db eval {SELECT offsets(t3) as o FROM t3 WHERE t3 MATCH 'l*'} {
+ set l [llength $o]
+ lappend t [expr {$l/4}]
+ }
+ set t
+} $ret
+
+# TODO(shess) It would be useful to test a couple edge cases, but I
+# don't know if we have the precision to manage it from here at this
+# time. Prefix hits can cross leaves, which the code above _should_
+# hit by virtue of size. There are two variations on this. If the
+# tree is 2 levels high, the code will find the leaf-node extent
+# directly, but if its higher, the code will have to follow two
+# separate interior branches down the tree. Both should be tested.
+
+finish_test
diff --git a/third_party/sqlite/test/fts2o.test b/third_party/sqlite/test/fts2o.test
new file mode 100755
index 0000000..5a33c45
--- /dev/null
+++ b/third_party/sqlite/test/fts2o.test
@@ -0,0 +1,169 @@
+# 2007 June 20
+#
+# 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 testing the FTS2 module.
+#
+# $Id: fts2o.test,v 1.4 2007/07/02 10:16:50 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is not defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+#---------------------------------------------------------------------
+# These tests, fts2o-1.*, test that ticket #2429 is fixed.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two');
+}
+do_test fts2o-1.1 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE c MATCH 'four';
+ }
+} {1 {one <b>four</b> two}}
+do_test fts2o-1.2 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE b MATCH 'four';
+ }
+} {1 {one <b>four</b>}}
+do_test fts2o-1.3 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE a MATCH 'four';
+ }
+} {1 {one three <b>four</b>}}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts2 table.
+#
+do_test fts2o-2.1 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {t1 t1_content t1_segments t1_segdir}
+do_test fts2o-2.2 {
+ execsql { ALTER TABLE t1 RENAME to fts_t1; }
+} {}
+do_test fts2o-2.3 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts2o-2.4 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir}
+
+# See what happens when renaming the fts2 table fails.
+#
+do_test fts2o-2.5 {
+ catchsql {
+ CREATE TABLE t1_segdir(a, b, c);
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+do_test fts2o-2.6 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts2o-2.7 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir}
+
+# See what happens when renaming the fts2 table fails inside a transaction.
+#
+do_test fts2o-2.8 {
+ execsql {
+ BEGIN;
+ INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two');
+ }
+} {}
+do_test fts2o-2.9 {
+ catchsql {
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+do_test fts2o-2.10 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts2o-2.11 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir}
+do_test fts2o-2.12 {
+ execsql COMMIT
+ execsql {SELECT a FROM fts_t1}
+} {{one three four} {one two three}}
+do_test fts2o-2.12 {
+ execsql { SELECT a, b, c FROM fts_t1 WHERE c MATCH 'four'; }
+} {{one three four} {one four} {one four two}}
+
+#-------------------------------------------------------------------
+# Close, delete and reopen the database. The following test should
+# be run on an initially empty db.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+do_test fts2o-3.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts2(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one two');
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two}}
+
+# This test was crashing at one point.
+#
+do_test fts2o-3.2 {
+ execsql {
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ CREATE TABLE t3(a, b, c);
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two} {one three four} {one four} {one two}}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts2 table in an attached
+# database.
+#
+file delete -force test2.db test2.db-journal
+
+do_test fts2o-3.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE VIRTUAL TABLE aux.t1 USING fts2(a, b, c);
+ INSERT INTO aux.t1(a, b, c) VALUES(
+ 'neung song sahm', 'neung see', 'neung see song'
+ );
+ }
+} {}
+
+do_test fts2o-3.2 {
+ execsql { SELECT a, b, c FROM aux.t1 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts2o-3.3 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one two}}
+
+do_test fts2o-3.4 {
+ execsql { ALTER TABLE aux.t1 RENAME TO t2 }
+} {}
+
+do_test fts2o-3.2 {
+ execsql { SELECT a, b, c FROM t2 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts2o-3.3 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one two}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2p.test b/third_party/sqlite/test/fts2p.test
new file mode 100755
index 0000000..38a8079
--- /dev/null
+++ b/third_party/sqlite/test/fts2p.test
@@ -0,0 +1,357 @@
+# 2008 June 26
+#
+# 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 exercises some new testing functions in the FTS2 module,
+# and then uses them to do some basic tests that FTS2 is internally
+# working as expected.
+#
+# $Id: fts2p.test,v 1.1 2008/07/22 23:32:28 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is not defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Probe to see if support for these functions is compiled in.
+# TODO(shess): Change main.mk to do the right thing and remove this test.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'x');
+}
+
+set s {SELECT dump_terms(t1, 1) FROM t1 LIMIT 1}
+set r {1 {unable to use function dump_terms in the requested context}}
+if {[catchsql $s]==$r} {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Test that the new functions give appropriate errors.
+do_test fts2p-0.0 {
+ catchsql {
+ SELECT dump_terms(t1, 1) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: incorrect arguments}}
+
+do_test fts2p-0.1 {
+ catchsql {
+ SELECT dump_terms(t1, 0, 0, 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: incorrect arguments}}
+
+do_test fts2p-0.2 {
+ catchsql {
+ SELECT dump_terms(1, t1) FROM t1 LIMIT 1;
+ }
+} {1 {unable to use function dump_terms in the requested context}}
+
+do_test fts2p-0.3 {
+ catchsql {
+ SELECT dump_terms(t1, 16, 16) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: segment not found}}
+
+do_test fts2p-0.4 {
+ catchsql {
+ SELECT dump_doclist(t1) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts2p-0.5 {
+ catchsql {
+ SELECT dump_doclist(t1, NULL) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: empty second argument}}
+
+do_test fts2p-0.6 {
+ catchsql {
+ SELECT dump_doclist(t1, '') FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: empty second argument}}
+
+do_test fts2p-0.7 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts2p-0.8 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 0, 0, 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts2p-0.9 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 16, 16) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: segment not found}}
+
+#*************************************************************************
+# Utility function to check for the expected terms in the segment
+# level/index. _all version does same but for entire index.
+proc check_terms {test level index terms} {
+ # TODO(shess): Figure out why uplevel in do_test can't catch
+ # $level and $index directly.
+ set ::level $level
+ set ::index $index
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+proc check_terms_all {test terms} {
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+
+# Utility function to check for the expected doclist for the term in
+# segment level/index. _all version does same for entire index.
+proc check_doclist {test level index term doclist} {
+ # TODO(shess): Again, why can't the non-:: versions work?
+ set ::term $term
+ set ::level $level
+ set ::index $index
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+proc check_doclist_all {test term doclist} {
+ set ::term $term
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+
+#*************************************************************************
+# Test the segments resulting from straight-forward inserts.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+}
+
+# Check for expected segments and expected matches.
+do_test fts2p-1.0.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2}
+do_test fts2p-1.0.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+# Check the specifics of the segments constructed.
+# Logical view of entire index.
+check_terms_all fts2p-1.0.1 {a is test that this was}
+check_doclist_all fts2p-1.0.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts2p-1.0.1.2 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts2p-1.0.1.3 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts2p-1.0.1.4 that {[2 0[0]]}
+check_doclist_all fts2p-1.0.1.5 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts2p-1.0.1.6 was {[2 0[1]]}
+
+# Segment 0,0
+check_terms fts2p-1.0.2 0 0 {a is test this}
+check_doclist fts2p-1.0.2.1 0 0 a {[1 0[2]]}
+check_doclist fts2p-1.0.2.2 0 0 is {[1 0[1]]}
+check_doclist fts2p-1.0.2.3 0 0 test {[1 0[3]]}
+check_doclist fts2p-1.0.2.4 0 0 this {[1 0[0]]}
+
+# Segment 0,1
+check_terms fts2p-1.0.3 0 1 {a test that was}
+check_doclist fts2p-1.0.3.1 0 1 a {[2 0[2]]}
+check_doclist fts2p-1.0.3.2 0 1 test {[2 0[3]]}
+check_doclist fts2p-1.0.3.3 0 1 that {[2 0[0]]}
+check_doclist fts2p-1.0.3.4 0 1 was {[2 0[1]]}
+
+# Segment 0,2
+check_terms fts2p-1.0.4 0 2 {a is test this}
+check_doclist fts2p-1.0.4.1 0 2 a {[3 0[2]]}
+check_doclist fts2p-1.0.4.2 0 2 is {[3 0[1]]}
+check_doclist fts2p-1.0.4.3 0 2 test {[3 0[3]]}
+check_doclist fts2p-1.0.4.4 0 2 this {[3 0[0]]}
+
+#*************************************************************************
+# Test the segments resulting from inserts followed by a delete.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE rowid = 1;
+}
+
+do_test fts2p-1.1.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2 0 3}
+do_test fts2p-1.1.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}}
+
+check_terms_all fts2p-1.1.1 {a is test that this was}
+check_doclist_all fts2p-1.1.1.1 a {[2 0[2]] [3 0[2]]}
+check_doclist_all fts2p-1.1.1.2 is {[3 0[1]]}
+check_doclist_all fts2p-1.1.1.3 test {[2 0[3]] [3 0[3]]}
+check_doclist_all fts2p-1.1.1.4 that {[2 0[0]]}
+check_doclist_all fts2p-1.1.1.5 this {[3 0[0]]}
+check_doclist_all fts2p-1.1.1.6 was {[2 0[1]]}
+
+check_terms fts2p-1.1.2 0 0 {a is test this}
+check_doclist fts2p-1.1.2.1 0 0 a {[1 0[2]]}
+check_doclist fts2p-1.1.2.2 0 0 is {[1 0[1]]}
+check_doclist fts2p-1.1.2.3 0 0 test {[1 0[3]]}
+check_doclist fts2p-1.1.2.4 0 0 this {[1 0[0]]}
+
+check_terms fts2p-1.1.3 0 1 {a test that was}
+check_doclist fts2p-1.1.3.1 0 1 a {[2 0[2]]}
+check_doclist fts2p-1.1.3.2 0 1 test {[2 0[3]]}
+check_doclist fts2p-1.1.3.3 0 1 that {[2 0[0]]}
+check_doclist fts2p-1.1.3.4 0 1 was {[2 0[1]]}
+
+check_terms fts2p-1.1.4 0 2 {a is test this}
+check_doclist fts2p-1.1.4.1 0 2 a {[3 0[2]]}
+check_doclist fts2p-1.1.4.2 0 2 is {[3 0[1]]}
+check_doclist fts2p-1.1.4.3 0 2 test {[3 0[3]]}
+check_doclist fts2p-1.1.4.4 0 2 this {[3 0[0]]}
+
+check_terms fts2p-1.1.5 0 3 {a is test this}
+check_doclist fts2p-1.1.5.1 0 3 a {[1]}
+check_doclist fts2p-1.1.5.2 0 3 is {[1]}
+check_doclist fts2p-1.1.5.3 0 3 test {[1]}
+check_doclist fts2p-1.1.5.4 0 3 this {[1]}
+
+#*************************************************************************
+# Test results when all references to certain tokens are deleted.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE rowid IN (1,3);
+}
+
+# Still 4 segments because 0,3 will contain deletes for rowid 1 and 3.
+do_test fts2p-1.2.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2 0 3}
+do_test fts2p-1.2.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts2p-1.2.1 {a is test that this was}
+check_doclist_all fts2p-1.2.1.1 a {[2 0[2]]}
+check_doclist_all fts2p-1.2.1.2 is {}
+check_doclist_all fts2p-1.2.1.3 test {[2 0[3]]}
+check_doclist_all fts2p-1.2.1.4 that {[2 0[0]]}
+check_doclist_all fts2p-1.2.1.5 this {}
+check_doclist_all fts2p-1.2.1.6 was {[2 0[1]]}
+
+check_terms fts2p-1.2.2 0 0 {a is test this}
+check_doclist fts2p-1.2.2.1 0 0 a {[1 0[2]]}
+check_doclist fts2p-1.2.2.2 0 0 is {[1 0[1]]}
+check_doclist fts2p-1.2.2.3 0 0 test {[1 0[3]]}
+check_doclist fts2p-1.2.2.4 0 0 this {[1 0[0]]}
+
+check_terms fts2p-1.2.3 0 1 {a test that was}
+check_doclist fts2p-1.2.3.1 0 1 a {[2 0[2]]}
+check_doclist fts2p-1.2.3.2 0 1 test {[2 0[3]]}
+check_doclist fts2p-1.2.3.3 0 1 that {[2 0[0]]}
+check_doclist fts2p-1.2.3.4 0 1 was {[2 0[1]]}
+
+check_terms fts2p-1.2.4 0 2 {a is test this}
+check_doclist fts2p-1.2.4.1 0 2 a {[3 0[2]]}
+check_doclist fts2p-1.2.4.2 0 2 is {[3 0[1]]}
+check_doclist fts2p-1.2.4.3 0 2 test {[3 0[3]]}
+check_doclist fts2p-1.2.4.4 0 2 this {[3 0[0]]}
+
+check_terms fts2p-1.2.5 0 3 {a is test this}
+check_doclist fts2p-1.2.5.1 0 3 a {[1] [3]}
+check_doclist fts2p-1.2.5.2 0 3 is {[1] [3]}
+check_doclist fts2p-1.2.5.3 0 3 test {[1] [3]}
+check_doclist fts2p-1.2.5.4 0 3 this {[1] [3]}
+
+#*************************************************************************
+# Test results when everything is optimized manually.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE rowid IN (1,3);
+ DROP TABLE IF EXISTS t1old;
+ ALTER TABLE t1 RENAME TO t1old;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) SELECT rowid, c FROM t1old;
+ DROP TABLE t1old;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts2p-1.3.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts2p-1.3.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts2p-1.3.1 {a test that was}
+check_doclist_all fts2p-1.3.1.1 a {[2 0[2]]}
+check_doclist_all fts2p-1.3.1.2 test {[2 0[3]]}
+check_doclist_all fts2p-1.3.1.3 that {[2 0[0]]}
+check_doclist_all fts2p-1.3.1.4 was {[2 0[1]]}
+
+check_terms fts2p-1.3.2 0 0 {a test that was}
+check_doclist fts2p-1.3.2.1 0 0 a {[2 0[2]]}
+check_doclist fts2p-1.3.2.2 0 0 test {[2 0[3]]}
+check_doclist fts2p-1.3.2.3 0 0 that {[2 0[0]]}
+check_doclist fts2p-1.3.2.4 0 0 was {[2 0[1]]}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2q.test b/third_party/sqlite/test/fts2q.test
new file mode 100755
index 0000000..cba78d5
--- /dev/null
+++ b/third_party/sqlite/test/fts2q.test
@@ -0,0 +1,346 @@
+# 2008 June 26
+#
+# 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 testing the FTS2 module's optimize() function.
+#
+# $Id: fts2q.test,v 1.2 2008/07/22 23:49:44 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is not defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Probe to see if support for the FTS2 dump_* functions is compiled in.
+# TODO(shess): Change main.mk to do the right thing and remove this test.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'x');
+}
+
+set s {SELECT dump_terms(t1, 1) FROM t1 LIMIT 1}
+set r {1 {unable to use function dump_terms in the requested context}}
+if {[catchsql $s]==$r} {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Utility function to check for the expected terms in the segment
+# level/index. _all version does same but for entire index.
+proc check_terms {test level index terms} {
+ # TODO(shess): Figure out why uplevel in do_test can't catch
+ # $level and $index directly.
+ set ::level $level
+ set ::index $index
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+proc check_terms_all {test terms} {
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+
+# Utility function to check for the expected doclist for the term in
+# segment level/index. _all version does same for entire index.
+proc check_doclist {test level index term doclist} {
+ # TODO(shess): Again, why can't the non-:: versions work?
+ set ::term $term
+ set ::level $level
+ set ::index $index
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+proc check_doclist_all {test term doclist} {
+ set ::term $term
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+
+#*************************************************************************
+# Test results when all rows are deleted and one is added back.
+# Previously older segments would continue to exist, but now the index
+# should be dropped when the table is empty. The results should look
+# exactly like we never added the earlier rows in the first place.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE 1=1; -- Delete each row rather than dropping table.
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+}
+
+# Should be a single initial segment.
+do_test fts2q-1.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts2q-1.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}}
+
+check_terms_all fts2q-1.1 {a is test this}
+check_doclist_all fts2q-1.1.1 a {[1 0[2]]}
+check_doclist_all fts2q-1.1.2 is {[1 0[1]]}
+check_doclist_all fts2q-1.1.3 test {[1 0[3]]}
+check_doclist_all fts2q-1.1.4 this {[1 0[0]]}
+
+check_terms fts2q-1.2 0 0 {a is test this}
+check_doclist fts2q-1.2.1 0 0 a {[1 0[2]]}
+check_doclist fts2q-1.2.2 0 0 is {[1 0[1]]}
+check_doclist fts2q-1.2.3 0 0 test {[1 0[3]]}
+check_doclist fts2q-1.2.4 0 0 this {[1 0[0]]}
+
+#*************************************************************************
+# Test results when everything is optimized manually.
+# NOTE(shess): This is a copy of fts2c-1.3. I've pulled a copy here
+# because fts2q-2 and fts2q-3 should have identical results.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE rowid IN (1,3);
+ DROP TABLE IF EXISTS t1old;
+ ALTER TABLE t1 RENAME TO t1old;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) SELECT rowid, c FROM t1old;
+ DROP TABLE t1old;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts2q-2.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts2q-2.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts2q-2.1 {a test that was}
+check_doclist_all fts2q-2.1.1 a {[2 0[2]]}
+check_doclist_all fts2q-2.1.2 test {[2 0[3]]}
+check_doclist_all fts2q-2.1.3 that {[2 0[0]]}
+check_doclist_all fts2q-2.1.4 was {[2 0[1]]}
+
+check_terms fts2q-2.2 0 0 {a test that was}
+check_doclist fts2q-2.2.1 0 0 a {[2 0[2]]}
+check_doclist fts2q-2.2.2 0 0 test {[2 0[3]]}
+check_doclist fts2q-2.2.3 0 0 that {[2 0[0]]}
+check_doclist fts2q-2.2.4 0 0 was {[2 0[1]]}
+
+#*************************************************************************
+# Test results when everything is optimized via optimize().
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE rowid IN (1,3);
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts2q-3.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts2q-3.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts2q-3.1 {a test that was}
+check_doclist_all fts2q-3.1.1 a {[2 0[2]]}
+check_doclist_all fts2q-3.1.2 test {[2 0[3]]}
+check_doclist_all fts2q-3.1.3 that {[2 0[0]]}
+check_doclist_all fts2q-3.1.4 was {[2 0[1]]}
+
+check_terms fts2q-3.2 0 0 {a test that was}
+check_doclist fts2q-3.2.1 0 0 a {[2 0[2]]}
+check_doclist fts2q-3.2.2 0 0 test {[2 0[3]]}
+check_doclist fts2q-3.2.3 0 0 that {[2 0[0]]}
+check_doclist fts2q-3.2.4 0 0 was {[2 0[1]]}
+
+#*************************************************************************
+# Test optimize() against a table involving segment merges.
+# NOTE(shess): Since there's no transaction, each of the INSERT/UPDATE
+# statements generates a segment.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+
+ UPDATE t1 SET c = 'This is a test one' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test one' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test one' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test two' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test two' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test two' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test three' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test three' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test three' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test four' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test four' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test four' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test' WHERE rowid = 3;
+}
+
+# 2 segments in level 0, 1 in level 1 (18 segments created, 16
+# merged).
+do_test fts2q-4.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 1 0}
+
+do_test fts2q-4.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+check_terms_all fts2q-4.1 {a four is one test that this three two was}
+check_doclist_all fts2q-4.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts2q-4.1.2 four {}
+check_doclist_all fts2q-4.1.3 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts2q-4.1.4 one {}
+check_doclist_all fts2q-4.1.5 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts2q-4.1.6 that {[2 0[0]]}
+check_doclist_all fts2q-4.1.7 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts2q-4.1.8 three {}
+check_doclist_all fts2q-4.1.9 two {}
+check_doclist_all fts2q-4.1.10 was {[2 0[1]]}
+
+check_terms fts2q-4.2 0 0 {a four test that was}
+check_doclist fts2q-4.2.1 0 0 a {[2 0[2]]}
+check_doclist fts2q-4.2.2 0 0 four {[2]}
+check_doclist fts2q-4.2.3 0 0 test {[2 0[3]]}
+check_doclist fts2q-4.2.4 0 0 that {[2 0[0]]}
+check_doclist fts2q-4.2.5 0 0 was {[2 0[1]]}
+
+check_terms fts2q-4.3 0 1 {a four is test this}
+check_doclist fts2q-4.3.1 0 1 a {[3 0[2]]}
+check_doclist fts2q-4.3.2 0 1 four {[3]}
+check_doclist fts2q-4.3.3 0 1 is {[3 0[1]]}
+check_doclist fts2q-4.3.4 0 1 test {[3 0[3]]}
+check_doclist fts2q-4.3.5 0 1 this {[3 0[0]]}
+
+check_terms fts2q-4.4 1 0 {a four is one test that this three two was}
+check_doclist fts2q-4.4.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist fts2q-4.4.2 1 0 four {[1] [2 0[4]] [3 0[4]]}
+check_doclist fts2q-4.4.3 1 0 is {[1 0[1]] [3 0[1]]}
+check_doclist fts2q-4.4.4 1 0 one {[1] [2] [3]}
+check_doclist fts2q-4.4.5 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist fts2q-4.4.6 1 0 that {[2 0[0]]}
+check_doclist fts2q-4.4.7 1 0 this {[1 0[0]] [3 0[0]]}
+check_doclist fts2q-4.4.8 1 0 three {[1] [2] [3]}
+check_doclist fts2q-4.4.9 1 0 two {[1] [2] [3]}
+check_doclist fts2q-4.4.10 1 0 was {[2 0[1]]}
+
+# Optimize should leave the result in the level of the highest-level
+# prior segment.
+do_test fts2q-4.5 {
+ execsql {
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index optimized} 1 0}
+
+# Identical to fts2q-4.matches.
+do_test fts2q-4.5.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+check_terms_all fts2q-4.5.1 {a is test that this was}
+check_doclist_all fts2q-4.5.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts2q-4.5.1.2 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts2q-4.5.1.3 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts2q-4.5.1.4 that {[2 0[0]]}
+check_doclist_all fts2q-4.5.1.5 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts2q-4.5.1.6 was {[2 0[1]]}
+
+check_terms fts2q-4.5.2 1 0 {a is test that this was}
+check_doclist fts2q-4.5.2.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist fts2q-4.5.2.2 1 0 is {[1 0[1]] [3 0[1]]}
+check_doclist fts2q-4.5.2.3 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist fts2q-4.5.2.4 1 0 that {[2 0[0]]}
+check_doclist fts2q-4.5.2.5 1 0 this {[1 0[0]] [3 0[0]]}
+check_doclist fts2q-4.5.2.6 1 0 was {[2 0[1]]}
+
+# Re-optimizing does nothing.
+do_test fts2q-5.0 {
+ execsql {
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index already optimal} 1 0}
+
+# Even if we move things around, still does nothing.
+do_test fts2q-5.1 {
+ execsql {
+ UPDATE t1_segdir SET level = 2 WHERE level = 1 AND idx = 0;
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index already optimal} 2 0}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2r.test b/third_party/sqlite/test/fts2r.test
new file mode 100755
index 0000000..c0be367
--- /dev/null
+++ b/third_party/sqlite/test/fts2r.test
@@ -0,0 +1,121 @@
+# 2008 July 29
+#
+# 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.
+#
+#*************************************************************************
+# These tests exercise the various types of fts2 cursors.
+#
+# $Id: fts2r.test,v 1.1 2008/07/29 20:38:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is not defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Test table scan (QUERY_GENERIC). This kind of query happens for
+# queries with no WHERE clause, or for WHERE clauses which cannot be
+# satisfied by an index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+}
+
+do_test fts2e-1.1 {
+ execsql {
+ SELECT rowid FROM t1 ORDER BY rowid;
+ }
+} {1 2 3}
+
+do_test fts2e-1.2 {
+ execsql {
+ SELECT rowid FROM t1 WHERE c LIKE '%test' ORDER BY rowid;
+ }
+} {1 2 3}
+
+do_test fts2e-1.3 {
+ execsql {
+ SELECT rowid FROM t1 WHERE c LIKE 'That%' ORDER BY rowid;
+ }
+} {2}
+
+#*************************************************************************
+# Test lookup by rowid (QUERY_ROWID). This kind of query happens for
+# queries which select by the rowid implicit index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, weight INTEGER UNIQUE);
+ INSERT INTO t2 VALUES (null, 10);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'This is a test');
+ INSERT INTO t2 VALUES (null, 5);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'That was a test');
+ INSERT INTO t2 VALUES (null, 20);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'This is a test');
+}
+
+# TODO(shess): This actually is doing QUERY_GENERIC? I'd have
+# expected QUERY_ROWID in this case, as for a very large table the
+# full scan is less efficient.
+do_test fts2e-2.1 {
+ execsql {
+ SELECT rowid FROM t1 WHERE rowid in (1, 2, 10);
+ }
+} {1 2}
+
+do_test fts2e-2.2 {
+ execsql {
+ SELECT t1.rowid, weight FROM t1, t2 WHERE t2.id = t1.rowid ORDER BY weight;
+ }
+} {2 5 1 10 3 20}
+
+do_test fts2e-2.3 {
+ execsql {
+ SELECT t1.rowid, weight FROM t1, t2
+ WHERE t2.weight>5 AND t2.id = t1.rowid ORDER BY weight;
+ }
+} {1 10 3 20}
+
+#*************************************************************************
+# Test lookup by MATCH (QUERY_FULLTEXT). This is the fulltext index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE VIRTUAL TABLE t1 USING fts2(c);
+ CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, weight INTEGER UNIQUE);
+ INSERT INTO t2 VALUES (null, 10);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'This is a test');
+ INSERT INTO t2 VALUES (null, 5);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'That was a test');
+ INSERT INTO t2 VALUES (null, 20);
+ INSERT INTO t1 (rowid, c) VALUES (last_insert_rowid(), 'This is a test');
+}
+
+do_test fts2e-3.1 {
+ execsql {
+ SELECT rowid FROM t1 WHERE t1 MATCH 'this' ORDER BY rowid;
+ }
+} {1 3}
+
+do_test fts2e-3.2 {
+ execsql {
+ SELECT t1.rowid, weight FROM t1, t2
+ WHERE t1 MATCH 'this' AND t1.rowid = t2.id ORDER BY weight;
+ }
+} {1 10 3 20}
+
+finish_test
diff --git a/third_party/sqlite/test/fts2token.test b/third_party/sqlite/test/fts2token.test
new file mode 100755
index 0000000..de5f94d
--- /dev/null
+++ b/third_party/sqlite/test/fts2token.test
@@ -0,0 +1,174 @@
+# 2007 June 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing the pluggable tokeniser feature of the
+# FTS2 module.
+#
+# $Id: fts2token.test,v 1.3 2007/06/25 12:05:40 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+proc escape_string {str} {
+ set out ""
+ foreach char [split $str ""] {
+ scan $char %c i
+ if {$i<=127} {
+ append out $char
+ } else {
+ append out [format {\x%.4x} $i]
+ }
+ }
+ set out
+}
+
+#--------------------------------------------------------------------------
+# Test cases fts2token-1.* are the warm-body test for the SQL scalar
+# function fts2_tokenizer(). The procedure is as follows:
+#
+# 1: Verify that there is no such fts2 tokenizer as 'blah'.
+#
+# 2: Query for the built-in tokenizer 'simple'. Insert a copy of the
+# retrieved value as tokenizer 'blah'.
+#
+# 3: Test that the value returned for tokenizer 'blah' is now the
+# same as that retrieved for 'simple'.
+#
+# 4: Test that it is now possible to create an fts2 table using
+# tokenizer 'blah' (it was not possible in step 1).
+#
+# 5: Test that the table created to use tokenizer 'blah' is usable.
+#
+do_test fts2token-1.1 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING fts2(content, tokenize blah);
+ }
+} {1 {unknown tokenizer: blah}}
+do_test fts2token-1.2 {
+ execsql {
+ SELECT fts2_tokenizer('blah', fts2_tokenizer('simple')) IS NULL;
+ }
+} {0}
+do_test fts2token-1.3 {
+ execsql {
+ SELECT fts2_tokenizer('blah') == fts2_tokenizer('simple');
+ }
+} {1}
+do_test fts2token-1.4 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING fts2(content, tokenize blah);
+ }
+} {0 {}}
+do_test fts2token-1.5 {
+ execsql {
+ INSERT INTO t1(content) VALUES('There was movement at the station');
+ INSERT INTO t1(content) VALUES('For the word has passed around');
+ INSERT INTO t1(content) VALUES('That the colt from ol regret had got away');
+ SELECT content FROM t1 WHERE content MATCH 'movement'
+ }
+} {{There was movement at the station}}
+
+#--------------------------------------------------------------------------
+# Test cases fts2token-2.* test error cases in the scalar function based
+# API for getting and setting tokenizers.
+#
+do_test fts2token-2.1 {
+ catchsql {
+ SELECT fts2_tokenizer('nosuchtokenizer');
+ }
+} {1 {unknown tokenizer: nosuchtokenizer}}
+
+#--------------------------------------------------------------------------
+# Test cases fts2token-3.* test the three built-in tokenizers with a
+# simple input string via the built-in test function. This is as much
+# to test the test function as the tokenizer implementations.
+#
+do_test fts2token-3.1 {
+ execsql {
+ SELECT fts2_tokenizer_test('simple', 'I don''t see how');
+ }
+} {{0 i I 1 don don 2 t t 3 see see 4 how how}}
+do_test fts2token-3.2 {
+ execsql {
+ SELECT fts2_tokenizer_test('porter', 'I don''t see how');
+ }
+} {{0 i I 1 don don 2 t t 3 see see 4 how how}}
+ifcapable icu {
+ do_test fts2token-3.3 {
+ execsql {
+ SELECT fts2_tokenizer_test('icu', 'I don''t see how');
+ }
+ } {{0 i I 1 don't don't 2 see see 3 how how}}
+}
+
+#--------------------------------------------------------------------------
+# Test cases fts2token-4.* test the ICU tokenizer. In practice, this
+# tokenizer only has two modes - "thai" and "everybody else". Some other
+# Asian languages (Lao, Khmer etc.) require the same special treatment as
+# Thai, but ICU doesn't support them yet.
+#
+ifcapable icu {
+
+ proc do_icu_test {name locale input output} {
+ set ::out [db eval { SELECT fts2_tokenizer_test('icu', $locale, $input) }]
+ do_test $name {
+ lindex $::out 0
+ } $output
+ }
+
+ do_icu_test fts2token-4.1 en_US {} {}
+ do_icu_test fts2token-4.2 en_US {Test cases fts2} [list \
+ 0 test Test 1 cases cases 2 fts2 fts2
+ ]
+
+ # The following test shows that ICU is smart enough to recognise
+ # Thai chararacters, even when the locale is set to English/United
+ # States.
+ #
+ set input "\u0e2d\u0e30\u0e44\u0e23\u0e19\u0e30\u0e04\u0e23\u0e31\u0e1a"
+ set output "0 \u0e2d\u0e30\u0e44\u0e23 \u0e2d\u0e30\u0e44\u0e23 "
+ append output "1 \u0e19\u0e30 \u0e19\u0e30 "
+ append output "2 \u0e04\u0e23\u0e31\u0e1a \u0e04\u0e23\u0e31\u0e1a"
+
+ do_icu_test fts2token-4.3 th_TH $input $output
+ do_icu_test fts2token-4.4 en_US $input $output
+
+ # ICU handles an unknown locale by falling back to the default.
+ # So this is not an error.
+ do_icu_test fts2token-4.5 MiddleOfTheOcean $input $output
+
+ set longtoken "AReallyReallyLongTokenOneThatWillSurelyRequire"
+ append longtoken "AReallocInTheIcuTokenizerCode"
+
+ set input "short tokens then "
+ append input $longtoken
+ set output "0 short short "
+ append output "1 tokens tokens "
+ append output "2 then then "
+ append output "3 [string tolower $longtoken] $longtoken"
+
+ do_icu_test fts2token-4.6 MiddleOfTheOcean $input $output
+ do_icu_test fts2token-4.7 th_TH $input $output
+ do_icu_test fts2token-4.8 en_US $input $output
+}
+
+do_test fts2token-internal {
+ execsql { SELECT fts2_tokenizer_internal_test() }
+} {ok}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3.test b/third_party/sqlite/test/fts3.test
new file mode 100755
index 0000000..2e65389
--- /dev/null
+++ b/third_party/sqlite/test/fts3.test
@@ -0,0 +1,68 @@
+# 2007 November 23
+#
+# 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 runs all tests.
+#
+# $Id: fts3.test,v 1.2 2008/07/23 18:17:32 drh Exp $
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ -soak {
+ set SOAKTEST 1
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ return
+}
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ fts3.test
+}
+
+# Files to include in the test. If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/fts3*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+}
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/fts3aa.test b/third_party/sqlite/test/fts3aa.test
new file mode 100755
index 0000000..46304fb
--- /dev/null
+++ b/third_party/sqlite/test/fts3aa.test
@@ -0,0 +1,202 @@
+# 2006 September 9
+#
+# 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 testing the FTS3 module.
+#
+# $Id: fts3aa.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1(content) VALUES('one');
+ INSERT INTO t1(content) VALUES('two');
+ INSERT INTO t1(content) VALUES('one two');
+ INSERT INTO t1(content) VALUES('three');
+ INSERT INTO t1(content) VALUES('one three');
+ INSERT INTO t1(content) VALUES('two three');
+ INSERT INTO t1(content) VALUES('one two three');
+ INSERT INTO t1(content) VALUES('four');
+ INSERT INTO t1(content) VALUES('one four');
+ INSERT INTO t1(content) VALUES('two four');
+ INSERT INTO t1(content) VALUES('one two four');
+ INSERT INTO t1(content) VALUES('three four');
+ INSERT INTO t1(content) VALUES('one three four');
+ INSERT INTO t1(content) VALUES('two three four');
+ INSERT INTO t1(content) VALUES('one two three four');
+ INSERT INTO t1(content) VALUES('five');
+ INSERT INTO t1(content) VALUES('one five');
+ INSERT INTO t1(content) VALUES('two five');
+ INSERT INTO t1(content) VALUES('one two five');
+ INSERT INTO t1(content) VALUES('three five');
+ INSERT INTO t1(content) VALUES('one three five');
+ INSERT INTO t1(content) VALUES('two three five');
+ INSERT INTO t1(content) VALUES('one two three five');
+ INSERT INTO t1(content) VALUES('four five');
+ INSERT INTO t1(content) VALUES('one four five');
+ INSERT INTO t1(content) VALUES('two four five');
+ INSERT INTO t1(content) VALUES('one two four five');
+ INSERT INTO t1(content) VALUES('three four five');
+ INSERT INTO t1(content) VALUES('one three four five');
+ INSERT INTO t1(content) VALUES('two three four five');
+ INSERT INTO t1(content) VALUES('one two three four five');
+}
+
+do_test fts3aa-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts3aa-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two'}
+} {3 7 11 15 19 23 27 31}
+do_test fts3aa-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one'}
+} {3 7 11 15 19 23 27 31}
+do_test fts3aa-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two three'}
+} {7 15 23 31}
+do_test fts3aa-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one three two'}
+} {7 15 23 31}
+do_test fts3aa-1.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two three one'}
+} {7 15 23 31}
+do_test fts3aa-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one three'}
+} {7 15 23 31}
+do_test fts3aa-1.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three one two'}
+} {7 15 23 31}
+do_test fts3aa-1.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three two one'}
+} {7 15 23 31}
+do_test fts3aa-1.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two THREE'}
+} {7 15 23 31}
+do_test fts3aa-1.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH ' ONE Two three '}
+} {7 15 23 31}
+
+do_test fts3aa-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one"'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts3aa-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two"'}
+} {3 7 11 15 19 23 27 31}
+do_test fts3aa-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"two one"'}
+} {}
+do_test fts3aa-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three"'}
+} {7 15 23 31}
+do_test fts3aa-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two"'}
+} {}
+do_test fts3aa-2.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three four"'}
+} {15 31}
+do_test fts3aa-2.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two four"'}
+} {}
+do_test fts3aa-2.8 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three five"'}
+} {21}
+do_test fts3aa-2.9 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" five'}
+} {21 29}
+do_test fts3aa-2.10 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three"'}
+} {21 29}
+do_test fts3aa-2.11 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three" four'}
+} {29}
+do_test fts3aa-2.12 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five four "one three"'}
+} {29}
+do_test fts3aa-2.13 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" four five'}
+} {29}
+
+do_test fts3aa-3.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts3aa-3.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one -two'}
+} {1 5 9 13 17 21 25 29}
+do_test fts3aa-3.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'}
+} {1 5 9 13 17 21 25 29}
+
+do_test fts3aa-4.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one OR two'}
+} {1 2 3 5 6 7 9 10 11 13 14 15 17 18 19 21 22 23 25 26 27 29 30 31}
+do_test fts3aa-4.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two" OR three'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts3aa-4.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR "one two"'}
+} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
+do_test fts3aa-4.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts3aa-4.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR two one'}
+} {3 5 7 11 13 15 19 21 23 27 29 31}
+do_test fts3aa-4.6 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three OR four'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+do_test fts3aa-4.7 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two OR three OR four one'}
+} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
+
+# Test the ability to handle NULL content
+#
+do_test fts3aa-5.1 {
+ execsql {INSERT INTO t1(content) VALUES(NULL)}
+} {}
+do_test fts3aa-5.2 {
+ set rowid [db last_insert_rowid]
+ execsql {SELECT content FROM t1 WHERE rowid=$rowid}
+} {{}}
+do_test fts3aa-5.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH NULL}
+} {}
+
+# Test the ability to handle non-positive rowids
+#
+do_test fts3aa-6.0 {
+ execsql {INSERT INTO t1(rowid, content) VALUES(0, 'four five')}
+} {}
+do_test fts3aa-6.1 {
+ execsql {SELECT content FROM t1 WHERE rowid = 0}
+} {{four five}}
+do_test fts3aa-6.2 {
+ execsql {INSERT INTO t1(rowid, content) VALUES(-1, 'three four')}
+} {}
+do_test fts3aa-6.3 {
+ execsql {SELECT content FROM t1 WHERE rowid = -1}
+} {{three four}}
+do_test fts3aa-6.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'four'}
+} {-1 0 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ab.test b/third_party/sqlite/test/fts3ab.test
new file mode 100755
index 0000000..86124f7
--- /dev/null
+++ b/third_party/sqlite/test/fts3ab.test
@@ -0,0 +1,147 @@
+# 2006 September 13
+#
+# 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 testing the FTS3 module.
+#
+# $Id: fts3ab.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Fill the full-text index "t1" with phrases in english, spanish,
+# and german. For the i-th row, fill in the names for the bits
+# that are set in the value of i. The least significant bit is
+# 1. For example, the value 5 is 101 in binary which will be
+# converted to "one three" in english.
+#
+proc fill_multilanguage_fulltext_t1 {} {
+ set english {one two three four five}
+ set spanish {un dos tres cuatro cinco}
+ set german {eine zwei drei vier funf}
+
+ for {set i 1} {$i<=31} {incr i} {
+ set cmd "INSERT INTO t1 VALUES"
+ set vset {}
+ foreach lang {english spanish german} {
+ set words {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend words [lindex [set $lang] $j]}
+ }
+ lappend vset "'$words'"
+ }
+ set sql "INSERT INTO t1(english,spanish,german) VALUES([join $vset ,])"
+ # puts $sql
+ db eval $sql
+ }
+}
+
+# Construct a full-text search table containing five keywords:
+# one, two, three, four, and five, in various combinations. The
+# rowid for each will be a bitmask for the elements it contains.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(english,spanish,german);
+}
+fill_multilanguage_fulltext_t1
+
+do_test fts3ab-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE english MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts3ab-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE spanish MATCH 'one'}
+} {}
+do_test fts3ab-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE german MATCH 'one'}
+} {}
+do_test fts3ab-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one'}
+} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
+do_test fts3ab-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one dos drei'}
+} {7 15 23 31}
+do_test fts3ab-1.6 {
+ execsql {SELECT english, spanish, german FROM t1 WHERE rowid=1}
+} {one un eine}
+do_test fts3ab-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"one un"'}
+} {}
+
+do_test fts3ab-2.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t2 USING fts3(from,to);
+ INSERT INTO t2([from],[to]) VALUES ('one two three', 'four five six');
+ SELECT [from], [to] FROM t2
+ }
+} {{one two three} {four five six}}
+
+
+# Compute an SQL string that contains the words one, two, three,... to
+# describe bits set in the value $i. Only the lower 5 bits are examined.
+#
+proc wordset {i} {
+ set x {}
+ for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
+ if {$k&$i} {lappend x [lindex {one two three four five} $j]}
+ }
+ return '$x'
+}
+
+# Create a new FTS table with three columns:
+#
+# norm: words for the bits of rowid
+# plusone: words for the bits of rowid+1
+# invert: words for the bits of ~rowid
+#
+db eval {
+ CREATE VIRTUAL TABLE t4 USING fts3([norm],'plusone',"invert");
+}
+for {set i 1} {$i<=15} {incr i} {
+ set vset [list [wordset $i] [wordset [expr {$i+1}]] [wordset [expr {~$i}]]]
+ db eval "INSERT INTO t4(norm,plusone,invert) VALUES([join $vset ,]);"
+}
+
+do_test fts3ab-4.1 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts3ab-4.2 {
+ execsql {SELECT rowid FROM t4 WHERE norm MATCH 'one'}
+} {1 3 5 7 9 11 13 15}
+do_test fts3ab-4.3 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'one'}
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
+do_test fts3ab-4.4 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:one'}
+} {2 4 6 8 10 12 14}
+do_test fts3ab-4.5 {
+ execsql {SELECT rowid FROM t4 WHERE plusone MATCH 'one'}
+} {2 4 6 8 10 12 14}
+do_test fts3ab-4.6 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one plusone:two'}
+} {1 5 9 13}
+do_test fts3ab-4.7 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one two'}
+} {1 3 5 7 9 11 13 15}
+do_test fts3ab-4.8 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:two norm:one'}
+} {1 5 9 13}
+do_test fts3ab-4.9 {
+ execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'two norm:one'}
+} {1 3 5 7 9 11 13 15}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ac.test b/third_party/sqlite/test/fts3ac.test
new file mode 100755
index 0000000..72e5410
--- /dev/null
+++ b/third_party/sqlite/test/fts3ac.test
@@ -0,0 +1,1213 @@
+# 2006 September 14
+#
+# 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 testing the FTS3 module.
+#
+# $Id: fts3ac.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Create a table of sample email data. The data comes from email
+# archives of Enron executives that was published as part of the
+# litigation against that company.
+#
+do_test fts3ac-1.1 {
+ db eval {
+ CREATE VIRTUAL TABLE email USING fts3([from],[to],subject,body);
+ BEGIN TRANSACTION;
+INSERT INTO email([from],[to],subject,body) VALUES('savita.puthigai@enron.com', 'traders.eol@enron.com, traders.eol@enron.com', 'EnronOnline- Change to Autohedge', 'Effective Monday, October 22, 2001 the following changes will be made to the Autohedge functionality on EnronOnline.
+
+The volume on the hedge will now respect the minimum volume and volume increment settings on the parent product. See rules below:
+
+? If the transaction volume on the child is less than half of the parent''s minimum volume no hedge will occur.
+? If the transaction volume on the child is more than half the parent''s minimum volume but less than half the volume increment on the parent, the hedge will volume will be the parent''s minimum volume.
+? For all other volumes, the same rounding rules will apply based on the volume increment on the parent product.
+
+Please see example below:
+
+Parent''s Settings:
+Minimum: 5000
+Increment: 1000
+
+Volume on Autohedge transaction Volume Hedged
+1 - 2499 0
+2500 - 5499 5000
+5500 - 6499 6000');
+INSERT INTO email([from],[to],subject,body) VALUES('dana.davis@enron.com', 'laynie.east@enron.com, lisa.king@enron.com, lisa.best@enron.com,', 'Leaving Early', 'FYI:
+If it''s ok with everyone''s needs, I would like to leave @4pm. If you think
+you will need my assistance past the 4 o''clock hour just let me know; I''ll
+be more than willing to stay.');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'louise.kitchen@enron.com', '<<Concur Expense Document>> - CC02.06.02', 'The following expense report is ready for approval:
+
+Employee Name: Christopher F. Calger
+Status last changed by: Mollie E. Gustafson Ms
+Expense Report Name: CC02.06.02
+Report Total: $3,972.93
+Amount Due Employee: $3,972.93
+
+
+To approve this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('jeff.duff@enron.com', 'julie.johnson@enron.com', 'Work request', 'Julie,
+
+Could you print off the current work request report by 1:30 today?
+
+Gentlemen,
+
+I''d like to review this today at 1:30 in our office. Also, could you provide
+me with your activity reports so I can have Julie enter this information.
+
+JD');
+INSERT INTO email([from],[to],subject,body) VALUES('v.weldon@enron.com', 'gary.l.carrier@usa.dupont.com, scott.joyce@bankofamerica.com', 'Enron News', 'This could turn into something big....
+http://biz.yahoo.com/rf/010129/n29305829.html');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.haedicke@enron.com', 'paul.simons@enron.com', 'Re: First Polish Deal!', 'Congrats! Things seem to be building rapidly now on the Continent. Mark');
+INSERT INTO email([from],[to],subject,body) VALUES('e..carter@enron.com', 't..robinson@enron.com', 'FW: Producers Newsletter 9-24-2001', '
+The producer lumber pricing sheet.
+ -----Original Message-----
+From: Johnson, Jay
+Sent: Tuesday, October 16, 2001 3:42 PM
+To: Carter, Karen E.
+Subject: FW: Producers Newsletter 9-24-2001
+
+
+
+ -----Original Message-----
+From: Daigre, Sergai
+Sent: Friday, September 21, 2001 8:33 PM
+Subject: Producers Newsletter 9-24-2001
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('david.delainey@enron.com', 'kenneth.lay@enron.com', 'Greater Houston Partnership', 'Ken, in response to the letter from Mr Miguel San Juan, my suggestion would
+be to offer up the Falcon for their use; however, given the tight time frame
+and your recent visit with Mr. Fox that it would be difficult for either you
+or me to participate.
+
+I spoke to Max and he agrees with this approach.
+
+I hope this meets with your approval.
+
+Regards
+Delainey');
+INSERT INTO email([from],[to],subject,body) VALUES('lachandra.fenceroy@enron.com', 'lindy.donoho@enron.com', 'FW: Bus Applications Meeting Follow Up', 'Lindy,
+
+Here is the original memo we discussed earlier. Please provide any information that you may have.
+
+Your cooperation is greatly appreciated.
+
+Thanks,
+
+lachandra.fenceroy@enron.com
+713.853.3884
+877.498.3401 Pager
+
+ -----Original Message-----
+From: Bisbee, Joanne
+Sent: Wednesday, September 26, 2001 7:50 AM
+To: Fenceroy, LaChandra
+Subject: FW: Bus Applications Meeting Follow Up
+
+Lachandra, Please get with David Duff today and see what this is about. Who are our TW accounting business users?
+
+ -----Original Message-----
+From: Koh, Wendy
+Sent: Tuesday, September 25, 2001 2:41 PM
+To: Bisbee, Joanne
+Subject: Bus Applications Meeting Follow Up
+
+Lisa brought up a TW change effective Nov 1. It involves eliminating a turnback surcharge. I have no other information, but you might check with the business folks for any system changes required.
+
+Wendy');
+INSERT INTO email([from],[to],subject,body) VALUES('danny.mccarty@enron.com', 'fran.fagan@enron.com', 'RE: worksheets', 'Fran,
+ If Julie''s merit needs to be lump sum, just move it over to that column. Also, send me Eric Gadd''s sheets as well. Thanks.
+Dan
+
+ -----Original Message-----
+From: Fagan, Fran
+Sent: Thursday, December 20, 2001 11:10 AM
+To: McCarty, Danny
+Subject: worksheets
+
+As discussed, attached are your sheets for bonus and merit.
+
+Thanks,
+
+Fran Fagan
+Sr. HR Rep
+713.853.5219
+
+
+ << File: McCartyMerit.xls >> << File: mccartyBonusCommercial_UnP.xls >>
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('bert.meyers@enron.com', 'shift.dl-portland@enron.com', 'OCTOBER SCHEDULE', 'TEAM,
+
+PLEASE SEND ME ANY REQUESTS THAT YOU HAVE FOR OCTOBER. SO FAR I HAVE THEM FOR LEAF. I WOULD LIKE TO HAVE IT DONE BY THE 15TH OF THE MONTH. ANY QUESTIONS PLEASE GIVE ME A CALL.
+
+BERT');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'john.arnold@enron.com, bilal.bajwa@enron.com, john.griffith@enron.com,', 'TRV Notification: (NG - PROPT P/L - 09/27/2001)', 'The report named: NG - PROPT P/L <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=11&report_name=NG+-+PROPT+P/L&category_cd=5&category_name=FINANCIAL&toc_hide=1&sTV1=5&TV1Exp=Y&current_efct_date=09/27/2001>, published as of 09/27/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('patrice.mims@enron.com', 'calvin.eakins@enron.com', 'Re: Small business supply assistance', 'Hi Calvin
+
+
+I spoke with Rickey (boy, is he long-winded!!). Gave him the name of our
+credit guy, Russell Diamond.
+
+Thank for your help!');
+INSERT INTO email([from],[to],subject,body) VALUES('legal <.hall@enron.com>', 'stephanie.panus@enron.com', 'Termination update', 'City of Vernon and Salt River Project terminated their contracts. I will fax these notices to you.');
+INSERT INTO email([from],[to],subject,body) VALUES('d..steffes@enron.com', 'richard.shapiro@enron.com', 'EES / ENA Government Affairs Staffing & Outside Services', 'Rick --
+
+Here is the information on staffing and outside services. Call if you need anything else.
+
+Jim
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('gelliott@industrialinfo.com', 'pcopello@industrialinfo.com', 'ECAAR (Gavin), WSCC (Diablo Canyon), & NPCC (Seabrook)', 'Dear Power Outage Database Customer,
+Attached you will find an excel document. The outages contained within are forced or rescheduled outages. Your daily delivery will still contain these outages.
+In addition to the two excel documents, there is a dbf file that is formatted like your daily deliveries you receive nightly. This will enable you to load the data into your regular database. Any questions please let me know. Thanks.
+Greg Elliott
+IIR, Inc.
+713-783-5147 x 3481
+outages@industrialinfo.com
+THE INFORMATION CONTAINED IN THIS E-MAIL IS LEGALLY PRIVILEGED AND CONFIDENTIAL INFORMATION INTENDED ONLY FOR THE USE OF THE INDIVIDUAL OR ENTITY NAMED ABOVE. YOU ARE HEREBY NOTIFIED THAT ANY DISSEMINATION, DISTRIBUTION, OR COPY OF THIS E-MAIL TO UNAUTHORIZED ENTITIES IS STRICTLY PROHIBITED. IF YOU HAVE RECEIVED THIS
+E-MAIL IN ERROR, PLEASE DELETE IT.
+ - OUTAGE.dbf
+ - 111201R.xls
+ - 111201.xls ');
+INSERT INTO email([from],[to],subject,body) VALUES('enron.announcements@enron.com', 'all_ena_egm_eim@enron.com', 'EWS Brown Bag', 'MARK YOUR LUNCH CALENDARS NOW !
+
+You are invited to attend the EWS Brown Bag Lunch Series
+
+Featuring: RAY BOWEN, COO
+
+Topic: Enron Industrial Markets
+
+Thursday, March 15, 2001
+11:30 am - 12:30 pm
+EB 5 C2
+
+
+You bring your lunch, Limited Seating
+We provide drinks and dessert. RSVP x 3-9610');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.germany@enron.com', 'ingrid.immer@williams.com', 'Re: About St Pauls', 'Sounds good to me. I bet this is next to the Warick?? Hotel.
+
+
+
+
+"Immer, Ingrid" <Ingrid.Immer@Williams.com> on 12/21/2000 11:48:47 AM
+To: "''chris.germany@enron.com''" <chris.germany@enron.com>
+cc:
+Subject: About St Pauls
+
+
+
+
+ <<About St Pauls.url>>
+?
+?http://www.stpaulshouston.org/about.html
+
+Chris,
+
+I like the looks of this place.? What do you think about going here Christmas
+eve?? They have an 11:00 a.m. service and a candlelight service at 5:00 p.m.,
+among others.
+
+Let me know.?? ii
+
+ - About St Pauls.url
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('nas@cpuc.ca.gov', 'skatz@sempratrading.com, kmccrea@sablaw.com, thompson@wrightlaw.com,', 'Reply Brief filed July 31, 2000', ' - CPUC01-#76371-v1-Revised_Reply_Brief__Due_today_7_31_.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('gascontrol@aglresources.com', 'dscott4@enron.com, lcampbel@enron.com', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder', 'Alert Posted 10:00 AM November 20,2000: E-GAS Request Reminder
+As discussed in the Winter Operations Meeting on Sept.29,2000,
+E-Gas(Emergency Gas) will not be offered this winter as a service from AGLC.
+Marketers and Poolers can receive gas via Peaking and IBSS nominations(daisy
+chain) from other marketers up to the 6 p.m. Same Day 2 nomination cycle.
+');
+INSERT INTO email([from],[to],subject,body) VALUES('dutch.quigley@enron.com', 'rwolkwitz@powermerchants.com', '', '
+
+Here is a goody for you');
+INSERT INTO email([from],[to],subject,body) VALUES('ryan.o''rourke@enron.com', 'k..allen@enron.com, randy.bhatia@enron.com, frank.ermis@enron.com,', 'TRV Notification: (West VaR - 11/07/2001)', 'The report named: West VaR <http://trv.corp.enron.com/linkFromExcel.asp?report_cd=36&report_name=West+VaR&category_cd=2&category_name=WEST&toc_hide=1&sTV1=2&TV1Exp=Y&current_efct_date=11/07/2001>, published as of 11/07/2001 is now available for viewing on the website.');
+INSERT INTO email([from],[to],subject,body) VALUES('mjones7@txu.com', 'cstone1@txu.com, ggreen2@txu.com, timpowell@txu.com,', 'Enron / HPL Actuals for July 10, 2000', 'Teco Tap 10.000 / Enron ; 110.000 / HPL IFERC
+
+LS HPL LSK IC 30.000 / Enron
+');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.pereira@enron.com', 'kkw816@aol.com', 'soccer practice', 'Kathy-
+
+Is it safe to assume that practice is cancelled for tonight??
+
+Susan Pereira');
+INSERT INTO email([from],[to],subject,body) VALUES('mark.whitt@enron.com', 'barry.tycholiz@enron.com', 'Huber Internal Memo', 'Please look at this. I didn''t know how deep to go with the desk. Do you think this works.
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('m..forney@enron.com', 'george.phillips@enron.com', '', 'George,
+Give me a call and we will further discuss opportunities on the 13st floor.
+
+Thanks,
+JMForney
+3-7160');
+INSERT INTO email([from],[to],subject,body) VALUES('brad.mckay@enron.com', 'angusmcka@aol.com', 'Re: (no subject)', 'not yet');
+INSERT INTO email([from],[to],subject,body) VALUES('adam.bayer@enron.com', 'jonathan.mckay@enron.com', 'FW: Curve Fetch File', 'Here is the curve fetch file sent to me. It has plenty of points in it. If you give me a list of which ones you need we may be able to construct a secondary worksheet to vlookup the values.
+
+adam
+35227
+
+
+ -----Original Message-----
+From: Royed, Jeff
+Sent: Tuesday, September 25, 2001 11:37 AM
+To: Bayer, Adam
+Subject: Curve Fetch File
+
+Let me know if it works. It may be required to have a certain version of Oracle for it to work properly.
+
+
+
+Jeff Royed
+Enron
+Energy Operations
+Phone: 713-853-5295');
+INSERT INTO email([from],[to],subject,body) VALUES('matt.smith@enron.com', 'yan.wang@enron.com', 'Report Formats', 'Yan,
+
+The merged reports look great. I believe the only orientation changes are to
+"unmerge" the following six reports:
+
+31 Keystone Receipts
+15 Questar Pipeline
+40 Rockies Production
+22 West_2
+23 West_3
+25 CIG_WIC
+
+The orientation of the individual reports should be correct. Thanks.
+
+Mat
+
+PS. Just a reminder to add the "*" by the title of calculated points.');
+INSERT INTO email([from],[to],subject,body) VALUES('michelle.lokay@enron.com', 'jimboman@bigfoot.com', 'Egyptian Festival', '---------------------- Forwarded by Michelle Lokay/ET&S/Enron on 09/07/2000
+10:08 AM ---------------------------
+
+
+"Karkour, Randa" <Randa.Karkour@COMPAQ.com> on 09/07/2000 09:01:04 AM
+To: "''Agheb (E-mail)" <Agheb@aol.com>, "Leila Mankarious (E-mail)"
+<Leila_Mankarious@mhhs.org>, "''Marymankarious (E-mail)"
+<marymankarious@aol.com>, "Michelle lokay (E-mail)" <mlokay@enron.com>, "Ramy
+Mankarious (E-mail)" <Mankarious@aol.com>
+cc:
+
+Subject: Egyptian Festival
+
+
+ <<Egyptian Festival.url>>
+
+ http://www.egyptianfestival.com/
+
+ - Egyptian Festival.url
+');
+INSERT INTO email([from],[to],subject,body) VALUES('errol.mclaughlin@enron.com', 'sherry.dawson@enron.com', 'Urgent!!! --- New EAST books', 'This has to be done..................................
+
+Thanks
+---------------------- Forwarded by Errol McLaughlin/Corp/Enron on 12/20/2000
+08:39 AM ---------------------------
+
+
+
+ From: William Kelly @ ECT 12/20/2000 08:31 AM
+
+
+To: Kam Keiser/HOU/ECT@ECT, Darron C Giron/HOU/ECT@ECT, David
+Baumbach/HOU/ECT@ECT, Errol McLaughlin/Corp/Enron@ENRON
+cc: Kimat Singla/HOU/ECT@ECT, Kulvinder Fowler/NA/Enron@ENRON, Kyle R
+Lilly/HOU/ECT@ECT, Jeff Royed/Corp/Enron@ENRON, Alejandra
+Chavez/NA/Enron@ENRON, Crystal Hyde/HOU/ECT@ECT
+
+Subject: New EAST books
+
+We have new book names in TAGG for our intramonth portfolios and it is
+extremely important that any deal booked to the East is communicated quickly
+to someone on my team. I know it will take some time for the new names to
+sink in and I do not want us to miss any positions or P&L.
+
+Thanks for your help on this.
+
+New:
+Scott Neal : East Northeast
+Dick Jenkins: East Marketeast
+
+WK
+');
+INSERT INTO email([from],[to],subject,body) VALUES('david.forster@enron.com', 'eol.wide@enron.com', 'Change to Stack Manager', 'Effective immediately, there is a change to the Stack Manager which will
+affect any Inactive Child.
+
+An inactive Child with links to Parent products will not have their
+calculated prices updated until the Child product is Activated.
+
+When the Child Product is activated, the price will be recalculated and
+updated BEFORE it is displayed on the web.
+
+This means that if you are inputting a basis price on a Child product, you
+will not see the final, calculated price until you Activate the product, at
+which time the customer will also see it.
+
+If you have any questions, please contact the Help Desk on:
+
+Americas: 713 853 4357
+Europe: + 44 (0) 20 7783 7783
+Asia/Australia: +61 2 9229 2300
+
+Dave');
+INSERT INTO email([from],[to],subject,body) VALUES('vince.kaminski@enron.com', 'jhh1@email.msn.com', 'Re: Light reading - see pieces beginning on page 7', 'John,
+
+I saw it. Very interesting.
+
+Vince
+
+
+
+
+
+"John H Herbert" <jhh1@email.msn.com> on 07/28/2000 08:38:08 AM
+To: "Vince J Kaminski" <Vince_J_Kaminski@enron.com>
+cc:
+Subject: Light reading - see pieces beginning on page 7
+
+
+Cheers and have a nice weekend,
+
+
+JHHerbert
+
+
+
+
+ - gd000728.pdf
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('matthew.lenhart@enron.com', 'mmmarcantel@equiva.com', 'RE:', 'i will try to line up a pig for you ');
+INSERT INTO email([from],[to],subject,body) VALUES('jae.black@enron.com', 'claudette.harvey@enron.com, chaun.roberts@enron.com, judy.martinez@enron.com,', 'Disaster Recovery Equipment', 'As a reminder...there are several pieces of equipment that are set up on the 30th Floor, as well as on our floor, for the Disaster Recovery Team. PLEASE DO NOT TAKE, BORROW OR USE this equipment. Should you need to use another computer system, other than yours, or make conference calls please work with your Assistant to help find or set up equipment for you to use.
+
+Thanks for your understanding in this matter.
+
+T.Jae Black
+East Power Trading
+Assistant to Kevin Presto
+off. 713-853-5800
+fax 713-646-8272
+cell 713-539-4760');
+INSERT INTO email([from],[to],subject,body) VALUES('eric.bass@enron.com', 'dale.neuner@enron.com', '5 X 24', 'Dale,
+
+Have you heard anything more on the 5 X 24s? We would like to get this
+product out ASAP.
+
+
+Thanks,
+
+Eric');
+INSERT INTO email([from],[to],subject,body) VALUES('messenger@smartreminders.com', 'm..tholt@enron.com', '10% Coupon - PrintPal Printer Cartridges - 100% Guaranteed', '[IMAGE]
+[IMAGE][IMAGE][IMAGE]
+Dear SmartReminders Member,
+ [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE] [IMAGE]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+We respect your privacy and are a Certified Participant of the BBBOnLine
+ Privacy Program. To be removed from future offers,click here.
+SmartReminders.com is a permission based service. To unsubscribe click here . ');
+INSERT INTO email([from],[to],subject,body) VALUES('benjamin.rogers@enron.com', 'mark.bernstein@enron.com', '', 'The guy you are talking about left CIN under a "cloud of suspicion" sort of
+speak. He was the one who got into several bad deals and PPA''s in California
+for CIN, thus he left on a bad note. Let me know if you need more detail
+than that, I felt this was the type of info you were looking for. Thanks!
+Ben');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'michelle.cash@enron.com', 'Expense Report Receipts Not Received', 'Employee Name: Michelle Cash
+Report Name: Houston Cellular 8-11-01
+Report Date: 12/13/01
+Report ID: 594D37C9ED2111D5B452
+Submitted On: 12/13/01
+
+You are only allowed 2 reports with receipts outstanding. Your expense reports will not be paid until you meet this requirement.');
+INSERT INTO email([from],[to],subject,body) VALUES('susan.mara@enron.com', 'ray.alvarez@enron.com, mark.palmer@enron.com, karen.denne@enron.com,', 'CAISO Emergency Motion -- to discontinue market-based rates for', 'FYI. the latest broadside against the generators.
+
+Sue Mara
+Enron Corp.
+Tel: (415) 782-7802
+Fax:(415) 782-7854
+----- Forwarded by Susan J Mara/NA/Enron on 06/08/2001 12:24 PM -----
+
+
+ "Milner, Marcie" <MMilner@coral-energy.com> 06/08/2001 11:13 AM To: "''smara@enron.com''" <smara@enron.com> cc: Subject: CAISO Emergency Motion
+
+
+Sue, did you see this emergency motion the CAISO filed today? Apparently
+they are requesting that FERC discontinue market-based rates immediately and
+grant refunds plus interest on the difference between cost-based rates and
+market revenues received back to May 2000. They are requesting the
+commission act within 14 days. Have you heard anything about what they are
+doing?
+
+Marcie
+
+http://www.caiso.com/docs/2001/06/08/200106081005526469.pdf
+');
+INSERT INTO email([from],[to],subject,body) VALUES('fletcher.sturm@enron.com', 'eloy.escobar@enron.com', 'Re: General Brinks Position Meeting', 'Eloy,
+
+Who is General Brinks?
+
+Fletch');
+INSERT INTO email([from],[to],subject,body) VALUES('nailia.dindarova@enron.com', 'richard.shapiro@enron.com', 'Documents for Mark Frevert (on EU developments and lessons from', 'Rick,
+
+Here are the documents that Peter has prepared for Mark Frevert.
+
+Nailia
+---------------------- Forwarded by Nailia Dindarova/LON/ECT on 25/06/2001
+16:36 ---------------------------
+
+
+Nailia Dindarova
+25/06/2001 15:36
+To: Michael Brown/Enron@EUEnronXGate
+cc: Ross Sankey/Enron@EUEnronXGate, Eric Shaw/ENRON@EUEnronXGate, Peter
+Styles/LON/ECT@ECT
+
+Subject: Documents for Mark Frevert (on EU developments and lessons from
+California)
+
+Michael,
+
+
+These are the documents that Peter promised to give to you for Mark Frevert.
+He has now handed them to him in person but asked me to transmit them
+electronically to you, as well as Eric and Ross.
+
+Nailia
+
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('peggy.a.kostial@accenture.com', 'dave.samuels@enron.com', 'EOL-Accenture Deal Sheet', 'Dave -
+
+Attached are our comments and suggested changes. Please call to review.
+
+On the time line for completion, we have four critical steps to complete:
+ Finalize market analysis to refine business case, specifically
+ projected revenue stream
+ Complete counterparty surveying, including targeting 3 CPs for letters
+ of intent
+ Review Enron asset base for potential reuse/ licensing
+ Contract negotiations
+
+Joe will come back to us with an updated time line, but it is my
+expectation that we are still on the same schedule (we just begun week
+three) with possibly a week or so slippage.....contract negotiations will
+probably be the critical path.
+
+We will send our cut at the actual time line here shortly. Thanks,
+
+Peggy
+
+(See attached file: accenture-dealpoints v2.doc)
+ - accenture-dealpoints v2.doc ');
+INSERT INTO email([from],[to],subject,body) VALUES('thomas.martin@enron.com', 'thomas.martin@enron.com', 'Re: Guadalupe Power Partners LP', '---------------------- Forwarded by Thomas A Martin/HOU/ECT on 03/20/2001
+03:49 PM ---------------------------
+
+
+Thomas A Martin
+10/11/2000 03:55 PM
+To: Patrick Wade/HOU/ECT@ECT
+cc:
+Subject: Re: Guadalupe Power Partners LP
+
+The deal is physically served at Oasis Waha or Oasis Katy and is priced at
+either HSC, Waha or Katytailgate GD at buyers option three days prior to
+NYMEX close.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('judy.townsend@enron.com', 'dan.junek@enron.com, chris.germany@enron.com', 'Columbia Distribution''s Capacity Available for Release - Sum', '---------------------- Forwarded by Judy Townsend/HOU/ECT on 03/09/2001 11:04
+AM ---------------------------
+
+
+agoddard@nisource.com on 03/08/2001 09:16:57 AM
+To: " - *Koch, Kent" <kkoch@nisource.com>, " -
+*Millar, Debra" <dmillar@nisource.com>, " - *Burke, Lynn"
+<lburke@nisource.com>
+cc: " - *Heckathorn, Tom" <theckathorn@nisource.com>
+Subject: Columbia Distribution''s Capacity Available for Release - Sum
+
+
+Attached is Columbia Distribution''s notice of capacity available for release
+for
+the summer of 2001 (Apr. 2001 through Oct. 2001).
+
+Please note that the deadline for bids is 3:00pm EST on March 20, 2001.
+
+If you have any questions, feel free to contact any of the representatives
+listed
+at the bottom of the attachment.
+
+Aaron Goddard
+
+
+
+
+ - 2001Summer.doc
+');
+INSERT INTO email([from],[to],subject,body) VALUES('rhonda.denton@enron.com', 'tim.belden@enron.com, dana.davis@enron.com, genia.fitzgerald@enron.com,', 'Split Rock Energy LLC', 'We have received the executed EEI contract from this CP dated 12/12/2000.
+Copies will be distributed to Legal and Credit.');
+INSERT INTO email([from],[to],subject,body) VALUES('kerrymcelroy@dwt.com', 'jack.speer@alcoa.com, crow@millernash.com, michaelearly@earthlink.net,', 'Oral Argument Request', ' - Oral Argument Request.doc');
+INSERT INTO email([from],[to],subject,body) VALUES('mike.carson@enron.com', 'rlmichaelis@hormel.com', '', 'Did you come in town this wk end..... My new number at our house is :
+713-668-3712...... my cell # is 281-381-7332
+
+the kid');
+INSERT INTO email([from],[to],subject,body) VALUES('cooper.richey@enron.com', 'trycooper@hotmail.com', 'FW: Contact Info', '
+
+-----Original Message-----
+From: Punja, Karim
+Sent: Thursday, December 13, 2001 2:35 PM
+To: Richey, Cooper
+Subject: Contact Info
+
+
+Cooper,
+
+Its been a real pleasure working with you (even though it was for only a small amount of time)
+I hope we can stay in touch.
+
+Home# 234-0249
+email: kpunja@hotmail.com
+
+Take Care,
+
+Karim.
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('bjm30@earthlink.net', 'mcguinn.k@enron.com, mcguinn.ian@enron.com, mcguinn.stephen@enron.com,', 'email address change', 'Hello all.
+
+I haven''t talked to many of you via email recently but I do want to give you
+my new address for your email file:
+
+ bjm30@earthlink.net
+
+I hope all is well.
+
+Brian McGuinn');
+INSERT INTO email([from],[to],subject,body) VALUES('shelley.corman@enron.com', 'steve.hotte@enron.com', 'Flat Panels', 'Can you please advise what is going on with the flat panels that we had planned to distribute to our gas logistics team. It was in the budget and we had the okay, but now I''m hearing there is some hold-up & the units are stored on 44.
+
+Shelley');
+INSERT INTO email([from],[to],subject,body) VALUES('sara.davidson@enron.com', 'john.schwartzenburg@enron.com, scott.dieball@enron.com, recipients@enron.com,', '2001 Enron Law Conference (Distribution List 2)', ' Enron Law Conference
+
+San Antonio, Texas May 2-4, 2001 Westin Riverwalk
+
+ See attached memo for more details!!
+
+
+? Registration for the law conference this year will be handled through an
+Online RSVP Form on the Enron Law Conference Website at
+http://lawconference.corp.enron.com. The website is still under construction
+and will not be available until Thursday, March 15, 2001.
+
+? We will send you another e-mail to confirm when the Law Conference Website
+is operational.
+
+? Please complete the Online RSVP Form as soon as it is available and submit
+it no later than Friday, March 30th.
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('tori.kuykendall@enron.com', 'heath.b.taylor@accenture.com', 'Re:', 'hey - thats funny about john - he definitely remembers him - i''ll call pat
+and let him know - we are coming on saturday - i just havent had a chance to
+call you guys back -- looking forward to it -- i probably need the
+directions again though');
+INSERT INTO email([from],[to],subject,body) VALUES('darron.giron@enron.com', 'bryce.baxter@enron.com', 'Re: Feedback for Audrey Cook', 'Bryce,
+
+I''ll get it done today.
+
+DG 3-9573
+
+
+
+
+
+ From: Bryce Baxter 06/12/2000 07:15 PM
+
+
+To: Darron C Giron/HOU/ECT@ECT
+cc:
+Subject: Feedback for Audrey Cook
+
+You were identified as a reviewer for Audrey Cook. If possible, could you
+complete her feedback by end of business Wednesday? It will really help me
+in the PRC process to have your input. Thanks.
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('casey.evans@enron.com', 'stephanie.sever@enron.com', 'Gas EOL ID', 'Stephanie,
+
+In conjunction with the recent movement of several power traders, they are changing the names of their gas books as well. The names of the new gas books and traders are as follows:
+
+PWR-NG-LT-SPP: Mike Carson
+PWR-NG-LT-SERC: Jeff King
+
+If you need to know their power desk to map their ID to their gas books, those desks are as follows:
+
+EPMI-LT-SPP: Mike Carson
+EPMI-LT-SERC: Jeff King
+
+I will be in training this afternoon, but will be back when class is over. Let me know if you have any questions.
+
+Thanks for your help!
+Casey');
+INSERT INTO email([from],[to],subject,body) VALUES('darrell.schoolcraft@enron.com', 'david.roensch@enron.com, kimberly.watson@enron.com, michelle.lokay@enron.com,', 'Postings', 'Please see the attached.
+
+
+ds
+
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('mcominsky@aol.com', 'cpatman@bracepatt.com, james_derrick@enron.com', 'Jurisprudence Luncheon', 'Carrin & Jim --
+
+It was an honor and a pleasure to meet both of you yesterday. I know we will
+have fun working together on this very special event.
+
+Jeff left the jurisprudence luncheon lists for me before he left on vacation.
+ I wasn''t sure whether he transmitted them to you as well. Would you please
+advise me if you would like them sent to you? I can email the MS Excel files
+or I can fax the hard copies to you. Please advise what is most convenient.
+
+I plan to be in town through the holidays and can be reached by phone, email,
+or cell phone at any time. My cell phone number is 713/705-4829.
+
+Thanks again for your interest in the ADL''s work. Martin.
+
+Martin B. Cominsky
+Director, Southwest Region
+Anti-Defamation League
+713/627-3490, ext. 122
+713/627-2011 (fax)
+MCominsky@aol.com');
+INSERT INTO email([from],[to],subject,body) VALUES('phillip.love@enron.com', 'todagost@utmb.edu, gbsonnta@utmb.edu', 'New President', 'I had a little bird put a word in my ear. Is there any possibility for Ben
+Raimer to be Bush''s secretary of HHS? Just curious about that infamous UTMB
+rumor mill. Hope things are well, happy holidays.
+PL');
+INSERT INTO email([from],[to],subject,body) VALUES('marie.heard@enron.com', 'ehamilton@fna.com', 'ISDA Master Agreement', 'Erin:
+
+Pursuant to your request, attached are the Schedule to the ISDA Master Agreement, together with Paragraph 13 to the ISDA Credit Support Annex. Please let me know if you need anything else. We look forward to hearing your comments.
+
+Marie
+
+Marie Heard
+Senior Legal Specialist
+Enron North America Corp.
+Phone: (713) 853-3907
+Fax: (713) 646-3490
+marie.heard@enron.com
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('andrea.ring@enron.com', 'beverly.beaty@enron.com', 'Re: Tennessee Buy - Louis Dreyfus', 'Beverly - once again thanks so much for your help on this.
+
+
+
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('karolyn.criado@enron.com', 'j..bonin@enron.com, felicia.case@enron.com, b..clapp@enron.com,', 'Price List week of Oct. 8-9, 2001', '
+Please contact me if you have any questions regarding last weeks prices.
+
+Thank you,
+Karolyn Criado
+3-9441
+
+
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.presto@enron.com', 'edward.baughman@enron.com, billy.braddock@enron.com', 'Associated', 'Please begin working on filling our Associated short position in 02. I would like to take this risk off the books.
+
+In addition, please find out what a buy-out of VEPCO would cost us. With Rogers transitioning to run our retail risk management, I would like to clean up our customer positions.
+
+We also need to continue to explore a JEA buy-out.
+
+Thanks.');
+INSERT INTO email([from],[to],subject,body) VALUES('stacy.dickson@enron.com', 'gregg.penman@enron.com', 'RE: Constellation TC 5-7-01', 'Gregg,
+
+I am at home with a sick baby. (Lots of fun!) I will call you about this
+tomorrow.
+
+Stacy');
+INSERT INTO email([from],[to],subject,body) VALUES('joe.quenet@enron.com', 'dfincher@utilicorp.com', '', 'hey big guy.....check this out.....
+
+ w ww.gorelieberman-2000.com/');
+INSERT INTO email([from],[to],subject,body) VALUES('k..allen@enron.com', 'jacqestc@aol.com', '', 'Jacques,
+
+I sent you a fax of Kevin Kolb''s comments on the release. The payoff on the note would be $36,248 ($36090(principal) + $158 (accrued interest)).
+This is assuming we wrap this up on Tuesday.
+
+Please email to confirm that their changes are ok so I can set up a meeting on Tuesday to reach closure.
+
+Phillip');
+INSERT INTO email([from],[to],subject,body) VALUES('kourtney.nelson@enron.com', 'mike.swerzbin@enron.com', 'Adjusted L/R Balance', 'Mike,
+
+I placed the adjusted L/R Balance on the Enronwest site. It is under the "Staff/Kourtney Nelson". There are two links:
+
+1) "Adj L_R" is the same data/format from the weekly strategy meeting.
+2) "New Gen 2001_2002" link has all of the supply side info that is used to calculate the L/R balance
+ -Please note the Data Flag column, a value of "3" indicates the project was cancelled, on hold, etc and is not included in the calc.
+
+Both of these sheets are interactive Excel spreadsheets and thus you can play around with the data as you please. Also, James Bruce is working to get his gen report on the web. That will help with your access to information on new gen.
+
+Please let me know if you have any questions or feedback,
+
+Kourtney
+
+
+
+Kourtney Nelson
+Fundamental Analysis
+Enron North America
+(503) 464-8280
+kourtney.nelson@enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('d..thomas@enron.com', 'naveed.ahmed@enron.com', 'FW: Current Enron TCC Portfolio', '
+
+-----Original Message-----
+From: Grace, Rebecca M.
+Sent: Monday, December 17, 2001 9:44 AM
+To: Thomas, Paul D.
+Cc: Cashion, Jim; Allen, Thresa A.; May, Tom
+Subject: RE: Current Enron TCC Portfolio
+
+
+Paul,
+
+I reviewed NY''s list. I agree with all of their contracts numbers and mw amounts.
+
+Call if you have any more questions.
+
+Rebecca
+
+
+
+ -----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:08 AM
+To: Grace, Rebecca M.
+Subject: FW: Current Enron TCC Portfolio
+
+ << File: enrontccs.xls >>
+Rebecca,
+Let me know if you see any differences.
+
+Paul
+X 3-0403
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Monday, December 17, 2001 9:04 AM
+To: Ahmed, Naveed
+Subject: FW: Current Enron TCC Portfolio
+
+
+
+
+-----Original Message-----
+From: Thomas, Paul D.
+Sent: Thursday, December 13, 2001 10:01 AM
+To: Baughman, Edward D.
+Subject: Current Enron TCC Portfolio
+
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('stephanie.panus@enron.com', 'william.bradford@enron.com, debbie.brackett@enron.com,', 'Coastal Merchant Energy/El Paso Merchant Energy', 'Coastal Merchant Energy, L.P. merged with and into El Paso Merchant Energy,
+L.P., effective February 1, 2001, with the surviving entity being El Paso
+Merchant Energy, L.P. We currently have ISDA Master Agreements with both
+counterparties. Please see the attached memo regarding the existing Masters
+and let us know which agreement should be terminated.
+
+Thanks,
+Stephanie
+');
+INSERT INTO email([from],[to],subject,body) VALUES('kam.keiser@enron.com', 'c..kenne@enron.com', 'RE: What about this too???', '
+
+ -----Original Message-----
+From: Kenne, Dawn C.
+Sent: Wednesday, February 06, 2002 11:50 AM
+To: Keiser, Kam
+Subject: What about this too???
+
+
+ << File: Netco Trader Matrix.xls >>
+ ');
+INSERT INTO email([from],[to],subject,body) VALUES('chris.meyer@enron.com', 'joe.parks@enron.com', 'Centana', 'Talked to Chip. We do need Cash Committe approval given the netting feature of your deal, which means Batch Funding Request. Please update per my previous e-mail and forward.
+
+Thanks
+
+chris
+x31666');
+INSERT INTO email([from],[to],subject,body) VALUES('debra.perlingiere@enron.com', 'jworman@academyofhealth.com', '', 'Have a great weekend! Happy Fathers Day!
+
+
+Debra Perlingiere
+Enron North America Corp.
+1400 Smith Street, EB 3885
+Houston, Texas 77002
+dperlin@enron.com
+Phone 713-853-7658
+Fax 713-646-3490');
+INSERT INTO email([from],[to],subject,body) VALUES('outlook.team@enron.com', '', 'Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia &', 'CALENDAR ENTRY: APPOINTMENT
+
+Description:
+ Demo by Martha Janousek of Dashboard & Pipeline Profile / Julia & Dir Rpts. - 4102
+
+Date: 1/5/2001
+Time: 9:00 AM - 10:00 AM (Central Standard Time)
+
+Chairperson: Outlook Migration Team
+
+Detailed Description:');
+INSERT INTO email([from],[to],subject,body) VALUES('diana.seifert@enron.com', 'mark.taylor@enron.com', 'Guest access Chile', 'Hello Mark,
+
+Justin Boyd told me that your can help me with questions regarding Chile.
+We got a request for guest access through MG.
+The company is called Escondida and is a subsidiary of BHP Australia.
+
+Please advise if I can set up a guest account or not.
+F.Y.I.: MG is planning to put a "in w/h Chile" contract for Copper on-line as
+soon as Enron has done the due diligence for this country.
+Thanks !
+
+
+Best regards
+
+Diana Seifert
+EOL PCG');
+INSERT INTO email([from],[to],subject,body) VALUES('enron_update@concureworkplace.com', 'mark.whitt@enron.com', '<<Concur Expense Document>> - 121001', 'The Approval status has changed on the following report:
+
+Status last changed by: Barry L. Tycholiz
+Expense Report Name: 121001
+Report Total: $198.98
+Amount Due Employee: $198.98
+Amount Approved: $198.98
+Amount Paid: $0.00
+Approval Status: Approved
+Payment Status: Pending
+
+
+To review this expense report, click on the following link for Concur Expense.
+http://expensexms.enron.com');
+INSERT INTO email([from],[to],subject,body) VALUES('kevin.hyatt@enron.com', '', 'Technical Support', 'Outside the U.S., please refer to the list below:
+
+Australia:
+1800 678-515
+support@palm-au.com
+
+Canada:
+1905 305-6530
+support@palm.com
+
+New Zealand:
+0800 446-398
+support@palm-nz.com
+
+U.K.:
+0171 867 0108
+eurosupport@palm.3com.com
+
+Please refer to the Worldwide Customer Support card for a complete technical support contact list.');
+INSERT INTO email([from],[to],subject,body) VALUES('geoff.storey@enron.com', 'dutch.quigley@enron.com', 'RE:', 'duke contact?
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 10:14 AM
+To: Storey, Geoff
+Subject: RE:
+
+bp corp Albert LaMore 281-366-4962
+
+running the reports now
+
+
+ -----Original Message-----
+From: Storey, Geoff
+Sent: Wednesday, October 31, 2001 10:10 AM
+To: Quigley, Dutch
+Subject: RE:
+
+give me a contact over there too
+BP
+
+
+ -----Original Message-----
+From: Quigley, Dutch
+Sent: Wednesday, October 31, 2001 9:42 AM
+To: Storey, Geoff
+Subject:
+
+Coral Jeff Whitnah 713-767-5374
+Relaint Steve McGinn 713-207-4000');
+INSERT INTO email([from],[to],subject,body) VALUES('pete.davis@enron.com', 'pete.davis@enron.com', 'Start Date: 4/22/01; HourAhead hour: 3; <CODESITE>', 'Start Date: 4/22/01; HourAhead hour: 3; No ancillary schedules awarded.
+Variances detected.
+Variances detected in Load schedule.
+
+ LOG MESSAGES:
+
+PARSING FILE -->> O:\Portland\WestDesk\California Scheduling\ISO Final
+Schedules\2001042203.txt
+
+---- Load Schedule ----
+$$$ Variance found in table tblLoads.
+ Details: (Hour: 3 / Preferred: 1.92 / Final: 1.89)
+ TRANS_TYPE: FINAL
+ LOAD_ID: PGE4
+ MKT_TYPE: 2
+ TRANS_DATE: 4/22/01
+ SC_ID: EPMI
+
+');
+INSERT INTO email([from],[to],subject,body) VALUES('john.postlethwaite@enron.com', 'john.zufferli@enron.com', 'Reference', 'John, hope things are going well up there for you. The big day is almost here for you and Jessica. I was wondering if I could use your name as a job reference if need be. I am just trying to get everything in order just in case something happens.
+
+John');
+INSERT INTO email([from],[to],subject,body) VALUES('jeffrey.shankman@enron.com', 'lschiffm@jonesday.com', 'Re:', 'I saw you called on the cell this a.m. Sorry I missed you. (I was in the
+shower). I have had a shitty week--I suspect my silence (not only to you,
+but others) after our phone call is a result of the week. I''m seeing Glen at
+11:15....talk to you');
+INSERT INTO email([from],[to],subject,body) VALUES('litebytz@enron.com', '', 'Lite Bytz RSVP', '
+This week''s Lite Bytz presentation will feature the following TOOLZ speaker:
+
+Richard McDougall
+Solaris 8
+Thursday, June 7, 2001
+
+If you have not already signed up, please RSVP via email to litebytz@enron.com by the end of the day Tuesday, June 5, 2001.
+
+*Remember: this is now a Brown Bag Event--so bring your lunch and we will provide cookies and drinks.
+
+Click below for more details.
+
+http://home.enron.com:84/messaging/litebytztoolzprint.jpg');
+ COMMIT;
+ }
+} {}
+
+###############################################################################
+# Everything above just builds an interesting test database. The actual
+# tests come after this comment.
+###############################################################################
+
+do_test fts3ac-1.2 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark'
+ }
+} {6 17 25 38 40 42 73 74}
+do_test fts3ac-1.3 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan'
+ }
+} {24 40}
+do_test fts3ac-1.4 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark susan'
+ }
+} {40}
+do_test fts3ac-1.5 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'susan mark'
+ }
+} {40}
+do_test fts3ac-1.6 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '"mark susan"'
+ }
+} {}
+do_test fts3ac-1.7 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark -susan'
+ }
+} {6 17 25 38 42 73 74}
+do_test fts3ac-1.8 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH '-mark susan'
+ }
+} {24}
+do_test fts3ac-1.9 {
+ execsql {
+ SELECT rowid FROM email WHERE email MATCH 'mark OR susan'
+ }
+} {6 17 24 25 38 40 42 73 74}
+
+# Some simple tests of the automatic "offsets(email)" column. In the sample
+# data set above, only one message, number 20, contains the words
+# "gas" and "reminder" in both body and subject.
+#
+do_test fts3ac-2.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts3ac-2.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8 3 1 54 8}}
+do_test fts3ac-2.3 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE email MATCH 'body:gas reminder'
+ }
+} {20 {2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+do_test fts3ac-2.4 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE subject MATCH 'gas reminder'
+ }
+} {20 {2 0 42 3 2 1 54 8}}
+do_test fts3ac-2.5 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'gas reminder'
+ }
+} {20 {3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}}
+
+# Document 32 contains 5 instances of the world "child". But only
+# 3 of them are paired with "product". Make sure only those instances
+# that match the phrase appear in the offsets(email) list.
+#
+do_test fts3ac-3.1 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH 'child product' AND +rowid=32
+ }
+} {32 {3 0 94 5 3 0 114 5 3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7 3 1 493 7}}
+do_test fts3ac-3.2 {
+ execsql {
+ SELECT rowid, offsets(email) FROM email
+ WHERE body MATCH '"child product"'
+ }
+} {32 {3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7}}
+
+# Snippet generator tests
+#
+do_test fts3ac-4.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'subject:gas reminder'
+ }
+} {{Alert Posted 10:00 AM November 20,2000: E-<b>GAS</b> Request <b>Reminder</b>}}
+do_test fts3ac-4.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'christmas candlelight'
+ }
+} {{<b>...</b> place.? What do you think about going here <b>Christmas</b>
+eve?? They have an 11:00 a.m. service and a <b>candlelight</b> service at 5:00 p.m.,
+among others. <b>...</b>}}
+
+do_test fts3ac-4.3 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <b>Deal</b> <b>Sheet</b> <b>...</b> intent
+ Review Enron asset base for <b>potential</b> <b>reuse</b>/ licensing
+ Contract negotiations <b>...</b>}}
+do_test fts3ac-4.4 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'deal sheet potential reuse'
+ }
+} {{EOL-Accenture <<<Deal>>> <<<Sheet>>> intent
+ Review Enron asset base for <<<potential>>> <<<reuse>>>/ licensing
+ Contract negotiations }}
+do_test fts3ac-4.5 {
+ execsql {
+ SELECT snippet(email,'<<<','>>>',' ') FROM email
+ WHERE email MATCH 'first things'
+ }
+} {{Re: <<<First>>> Polish Deal! Congrats! <<<Things>>> seem to be building rapidly now on the }}
+do_test fts3ac-4.6 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'chris is here'
+ }
+} {{<b>chris</b>.germany@enron.com <b>...</b> Sounds good to me. I bet this <b>is</b> next to the Warick?? Hotel. <b>...</b> place.? What do you think about going <b>here</b> Christmas
+eve?? They have an 11:00 a.m. <b>...</b>}}
+do_test fts3ac-4.7 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH '"pursuant to"'
+ }
+} {{Erin:
+
+<b>Pursuant</b> <b>to</b> your request, attached are the Schedule to <b>...</b>}}
+do_test fts3ac-4.8 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'ancillary load davis'
+ }
+} {{pete.<b>davis</b>@enron.com <b>...</b> Start Date: 4/22/01; HourAhead hour: 3; No <b>ancillary</b> schedules awarded.
+Variances detected.
+Variances detected in <b>Load</b> schedule.
+
+ LOG MESSAGES:
+
+PARSING <b>...</b>}}
+
+# Combinations of AND and OR operators:
+#
+do_test fts3ac-5.1 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'questar enron OR com'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+do_test fts3ac-5.2 {
+ execsql {
+ SELECT snippet(email) FROM email
+ WHERE email MATCH 'enron OR com questar'
+ }
+} {{matt.smith@<b>enron</b>.<b>com</b> <b>...</b> six reports:
+
+31 Keystone Receipts
+15 <b>Questar</b> Pipeline
+40 Rockies Production
+22 West_2 <b>...</b>}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ad.test b/third_party/sqlite/test/fts3ad.test
new file mode 100755
index 0000000..420b5b2
--- /dev/null
+++ b/third_party/sqlite/test/fts3ad.test
@@ -0,0 +1,65 @@
+# 2006 October 1
+#
+# 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 testing the FTS3 module, and in particular
+# the Porter stemmer.
+#
+# $Id: fts3ad.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_test fts3ad-1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize porter);
+ INSERT INTO t1(rowid, content) VALUES(1, 'running and jumping');
+ SELECT rowid FROM t1 WHERE content MATCH 'run jump';
+ }
+} {1}
+do_test fts3ad-1.2 {
+ execsql {
+ SELECT snippet(t1) FROM t1 WHERE t1 MATCH 'run jump';
+ }
+} {{<b>running</b> and <b>jumping</b>}}
+do_test fts3ad-1.3 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(2, 'abcdefghijklmnopqrstuvwyxz');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts3ad-1.4 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijXXXXqrstuvwyxz'
+ }
+} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
+do_test fts3ad-1.5 {
+ execsql {
+ INSERT INTO t1(rowid, content)
+ VALUES(3, 'The value is 123456789');
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123789'
+ }
+} {3 {The value is <b>123456789</b>}}
+do_test fts3ad-1.6 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123000000789'
+ }
+} {3 {The value is <b>123456789</b>}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ae.test b/third_party/sqlite/test/fts3ae.test
new file mode 100755
index 0000000..949a72b
--- /dev/null
+++ b/third_party/sqlite/test/fts3ae.test
@@ -0,0 +1,85 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing deletions in the FTS3 module.
+#
+# $Id: fts3ae.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 30 INSERT and
+# DELETE statements, so that we'll test both the segmentMerge() merge
+# (over the first 16) and the termSelect() merge (over the level-1
+# segment and 14 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ DELETE FROM t1 WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ DELETE FROM t1 WHERE rowid = 22;
+}
+
+do_test fts3af-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {14}
+
+do_test fts3ae-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {3 5 9 11 15 17 21}
+
+do_test fts3ae-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 11 14 15 18}
+
+do_test fts3ae-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {5 6 12 14 15 20 21}
+
+do_test fts3ae-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {8 9 11 12 14 15}
+
+do_test fts3ae-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {17 18 20 21}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3af.test b/third_party/sqlite/test/fts3af.test
new file mode 100755
index 0000000..221ac4c
--- /dev/null
+++ b/third_party/sqlite/test/fts3af.test
@@ -0,0 +1,90 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing updates in the FTS3 module.
+#
+# $Id: fts3af.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Construct a full-text search table containing keywords which are the
+# ordinal numbers of the bit positions set for a sequence of integers,
+# which are used for the rowid. There are a total of 31 INSERT,
+# UPDATE, and DELETE statements, so that we'll test both the
+# segmentMerge() merge (over the first 16) and the termSelect() merge
+# (over the level-1 segment and 15 level-0 segments).
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'one');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'two');
+ INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
+ INSERT INTO t1 (rowid, content) VALUES(4, 'three');
+ INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
+ INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
+ INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
+ DELETE FROM t1 WHERE rowid = 4;
+ INSERT INTO t1 (rowid, content) VALUES(8, 'four');
+ UPDATE t1 SET content = 'update one three' WHERE rowid = 1;
+ INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
+ INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
+ DELETE FROM t1 WHERE rowid = 7;
+ INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
+ INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
+ INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
+ DELETE FROM t1 WHERE rowid = 10;
+ INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
+ INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
+ UPDATE t1 SET content = 'update two five' WHERE rowid = 8;
+ INSERT INTO t1 (rowid, content) VALUES(16, 'five');
+ DELETE FROM t1 WHERE rowid = 13;
+ INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
+ INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
+ INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
+ DELETE FROM t1 WHERE rowid = 16;
+ INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
+ INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
+ INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
+ DELETE FROM t1 WHERE rowid = 19;
+ UPDATE t1 SET content = 'update' WHERE rowid = 15;
+}
+
+do_test fts3af-1.1 {
+ execsql {SELECT COUNT(*) FROM t1}
+} {16}
+
+do_test fts3af-2.0 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'update'}
+} {1 8 15}
+
+do_test fts3af-2.1 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
+} {1 3 5 9 11 17 21}
+
+do_test fts3af-2.2 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
+} {2 3 6 8 11 14 18 22}
+
+do_test fts3af-2.3 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
+} {1 5 6 12 14 20 21 22}
+
+do_test fts3af-2.4 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
+} {9 11 12 14}
+
+do_test fts3af-2.5 {
+ execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
+} {8 17 18 20 21 22}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ag.test b/third_party/sqlite/test/fts3ag.test
new file mode 100755
index 0000000..f4f9c8f
--- /dev/null
+++ b/third_party/sqlite/test/fts3ag.test
@@ -0,0 +1,93 @@
+# 2006 October 19
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing handling of edge cases for various doclist
+# merging functions in the FTS3 module query logic.
+#
+# $Id: fts3ag.test,v 1.2 2007/11/16 00:23:08 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, 'this is a test');
+ INSERT INTO t1 (rowid, content) VALUES(2, 'also a test');
+}
+
+# No hits at all. Returns empty doclists from termSelect().
+do_test fts3ag-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
+} {}
+
+# Empty left in docListExceptMerge().
+do_test fts3ag-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this something'}
+} {}
+
+# Empty right in docListExceptMerge().
+do_test fts3ag-1.3 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this -something'}
+} {1}
+
+# Empty left in docListPhraseMerge().
+do_test fts3ag-1.4 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"this something"'}
+} {}
+
+# Empty right in docListPhraseMerge().
+do_test fts3ag-1.5 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"something is"'}
+} {}
+
+# Empty left in docListOrMerge().
+do_test fts3ag-1.6 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something OR this'}
+} {1}
+
+# Empty right in docListOrMerge().
+do_test fts3ag-1.7 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR something'}
+} {1}
+
+# Empty left in docListAndMerge().
+do_test fts3ag-1.8 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something this'}
+} {}
+
+# Empty right in docListAndMerge().
+do_test fts3ag-1.9 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this something'}
+} {}
+
+# No support for all-except queries.
+do_test fts3ag-1.10 {
+ catchsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this -something'}
+} {1 {SQL logic error or missing database}}
+
+# Test that docListOrMerge() correctly handles reaching the end of one
+# doclist before it reaches the end of the other.
+do_test fts3ag-1.11 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR also'}
+} {1 2}
+do_test fts3ag-1.12 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'also OR this'}
+} {1 2}
+
+# Empty left and right in docListOrMerge(). Each term matches neither
+# row, and when combined there was an assertion failure.
+do_test fts3ag-1.13 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something OR nothing'}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ah.test b/third_party/sqlite/test/fts3ah.test
new file mode 100755
index 0000000..1a58e49
--- /dev/null
+++ b/third_party/sqlite/test/fts3ah.test
@@ -0,0 +1,76 @@
+# 2006 October 31 (scaaarey)
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# here is testing correct handling of excessively long terms.
+#
+# $Id: fts3ah.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Generate a term of len copies of char.
+proc bigterm {char len} {
+ for {set term ""} {$len>0} {incr len -1} {
+ append term $char
+ }
+ return $term
+}
+
+# Generate a document of bigterms based on characters from the list
+# chars.
+proc bigtermdoc {chars len} {
+ set doc ""
+ foreach char $chars {
+ append doc " " [bigterm $char $len]
+ }
+ return $doc
+}
+
+set len 5000
+set doc1 [bigtermdoc {a b c d} $len]
+set doc2 [bigtermdoc {b d e f} $len]
+set doc3 [bigtermdoc {a c e} $len]
+
+set aterm [bigterm a $len]
+set bterm [bigterm b $len]
+set xterm [bigterm x $len]
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, $doc1);
+ INSERT INTO t1 (rowid, content) VALUES(2, $doc2);
+ INSERT INTO t1 (rowid, content) VALUES(3, $doc3);
+}
+
+# No hits at all. Returns empty doclists from termSelect().
+do_test fts3ah-1.1 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
+} {}
+
+do_test fts3ah-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH $aterm}
+} {1 3}
+
+do_test fts3ah-1.2 {
+ execsql {SELECT rowid FROM t1 WHERE t1 MATCH $xterm}
+} {}
+
+do_test fts3ah-1.3 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '$aterm -$xterm'"
+} {1 3}
+
+do_test fts3ah-1.4 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"$aterm $bterm\"'"
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ai.test b/third_party/sqlite/test/fts3ai.test
new file mode 100755
index 0000000..144b4c3
--- /dev/null
+++ b/third_party/sqlite/test/fts3ai.test
@@ -0,0 +1,87 @@
+# 2007 January 17
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite fts3 library. The
+# focus here is testing handling of UPDATE when using UTF-16-encoded
+# databases.
+#
+# $Id: fts3ai.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+# NOTE(shess) Copied from capi3.test.
+proc utf16 {str {nt 1}} {
+ set r [encoding convertto unicode $str]
+ if {$nt} {
+ append r "\x00\x00"
+ }
+ return $r
+}
+
+db eval {
+ PRAGMA encoding = "UTF-16le";
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+}
+
+do_test fts3ai-1.0 {
+ execsql {PRAGMA encoding}
+} {UTF-16le}
+
+do_test fts3ai-1.1 {
+ execsql {INSERT INTO t1 (rowid, content) VALUES(1, 'one')}
+ execsql {SELECT content FROM t1 WHERE rowid = 1}
+} {one}
+
+do_test fts3ai-1.2 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(2, 'two')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 2}
+} {two}
+
+do_test fts3ai-1.3 {
+ set sql "INSERT INTO t1 (rowid, content) VALUES(3, 'three')"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'trois' WHERE rowid = 3"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 3}
+} {trois}
+
+do_test fts3ai-1.4 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(4, 'four')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 4}
+} {four}
+
+do_test fts3ai-1.5 {
+ set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(5, 'five')}]
+ set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ set sql "UPDATE t1 SET content = 'cinq' WHERE rowid = 5"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+ sqlite3_finalize $STMT
+ execsql {SELECT content FROM t1 WHERE rowid = 5}
+} {cinq}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3aj.test b/third_party/sqlite/test/fts3aj.test
new file mode 100755
index 0000000..60d26c0
--- /dev/null
+++ b/third_party/sqlite/test/fts3aj.test
@@ -0,0 +1,89 @@
+# 2007 February 6
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. This
+# tests creating fts3 tables in an attached database.
+#
+# $Id: fts3aj.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Clean up anything left over from a previous pass.
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts3(content);
+ INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
+}
+
+db2 eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# This has always worked because the t1_* tables used by fts3 will be
+# the defaults.
+do_test fts3aj-1.1 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+# Make certain we're detached if there was an error.
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this appears to work fine, but the t2_* tables used
+# by fts3 will be created in database 'main' instead of database
+# 'two'. It appears to work fine because the tables end up being the
+# defaults, but obviously is badly broken if you hope to use things
+# other than in the exact same ATTACH setup.
+do_test fts3aj-1.2 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ CREATE VIRTUAL TABLE two.t2 USING fts3(content);
+ INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this broke because the fts3 code attempted to create
+# t3_* tables in database 'main', but they already existed. Normally
+# this wouldn't happen without t3 itself existing, in which case the
+# fts3 code would never be called in the first place.
+do_test fts3aj-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+
+ CREATE VIRTUAL TABLE two.t3 USING fts3(content);
+ INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
+
+ DETACH DATABASE two;
+ } db2
+} {2}
+catch {db eval {DETACH DATABASE two}}
+
+catch {db2 close}
+file delete -force test2.db
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ak.test b/third_party/sqlite/test/fts3ak.test
new file mode 100755
index 0000000..a263f0b
--- /dev/null
+++ b/third_party/sqlite/test/fts3ak.test
@@ -0,0 +1,105 @@
+# 2007 March 9
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. These
+# make sure that fts3 insertion buffering is fully transparent when
+# using transactions.
+#
+# $Id: fts3ak.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# Test that possibly-buffered inserts went through after commit.
+do_test fts3ak-1.1 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(4, "false world");
+ INSERT INTO t1 (rowid, content) VALUES(5, "false door");
+ COMMIT TRANSACTION;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4}
+
+# Test that buffered inserts are seen by selects in the same
+# transaction.
+do_test fts3ak-1.2 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(6, "another world");
+ INSERT INTO t1 (rowid, content) VALUES(7, "another test");
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ COMMIT TRANSACTION;
+ }
+} {1 3 4 6}
+
+# Test that buffered inserts are seen within a transaction. This is
+# really the same test as 1.2.
+do_test fts3ak-1.3 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(8, "second world");
+ INSERT INTO t1 (rowid, content) VALUES(9, "second sight");
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ ROLLBACK TRANSACTION;
+ }
+} {1 3 4 6 8}
+
+# Double-check that the previous result doesn't persist past the
+# rollback!
+do_test fts3ak-1.4 {
+ execsql {
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4 6}
+
+# Test it all together.
+do_test fts3ak-1.5 {
+ execsql {
+ BEGIN TRANSACTION;
+ INSERT INTO t1 (rowid, content) VALUES(10, "second world");
+ INSERT INTO t1 (rowid, content) VALUES(11, "second sight");
+ ROLLBACK TRANSACTION;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'world';
+ }
+} {1 3 4 6}
+
+# Test that the obvious case works.
+do_test fts3ak-1.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 (rowid, content) VALUES(12, "third world");
+ COMMIT;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'third';
+ }
+} {12}
+
+# This is exactly the same as the previous test, except that older
+# code loses the INSERT due to an SQLITE_SCHEMA error.
+do_test fts3ak-1.7 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 (rowid, content) VALUES(13, "third dimension");
+ CREATE TABLE x (c);
+ COMMIT;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'dimension';
+ }
+} {13}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3al.test b/third_party/sqlite/test/fts3al.test
new file mode 100755
index 0000000..be01ecb
--- /dev/null
+++ b/third_party/sqlite/test/fts3al.test
@@ -0,0 +1,69 @@
+# 2007 March 28
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing isspace/isalnum/tolower problems with the
+# FTS3 module. Unfortunately, this code isn't a really principled set
+# of tests, because it is impossible to know where new uses of these
+# functions might appear.
+#
+# $Id: fts3al.test,v 1.2 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Tests that startsWith() (calls isspace, tolower, isalnum) can handle
+# hi-bit chars. parseSpec() also calls isalnum here.
+do_test fts3al-1.1 {
+ execsql "CREATE VIRTUAL TABLE t1 USING fts3(content, \x80)"
+} {}
+
+# Additionally tests isspace() call in getToken(), and isalnum() call
+# in tokenListToIdList().
+do_test fts3al-1.2 {
+ catch {
+ execsql "CREATE VIRTUAL TABLE t2 USING fts3(content, tokenize \x80)"
+ }
+ sqlite3_errmsg $DB
+} "unknown tokenizer: \x80"
+
+# Additionally test final isalnum() in startsWith().
+do_test fts3al-1.3 {
+ execsql "CREATE VIRTUAL TABLE t3 USING fts3(content, tokenize\x80)"
+} {}
+
+# The snippet-generation code has calls to isspace() which are sort of
+# hard to get to. It finds convenient breakpoints by starting ~40
+# chars before and after the matched term, and scanning ~10 chars
+# around that position for isspace() characters. The long word with
+# embedded hi-bit chars causes one of these isspace() calls to be
+# exercised. The version with a couple extra spaces should cause the
+# other isspace() call to be exercised. [Both cases have been tested
+# in the debugger, but I'm hoping to continue to catch it if simple
+# constant changes change things slightly.
+#
+# The trailing and leading hi-bit chars help with code which tests for
+# isspace() to coalesce multiple spaces.
+
+set word "\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80xxxxx\x80"
+set phrase1 "$word $word $word target $word $word $word"
+set phrase2 "$word $word $word target $word $word $word"
+
+db eval {CREATE VIRTUAL TABLE t4 USING fts3(content)}
+db eval "INSERT INTO t4 (content) VALUES ('$phrase1')"
+db eval "INSERT INTO t4 (content) VALUES ('$phrase2')"
+
+do_test fts3al-1.4 {
+ execsql {SELECT rowid, length(snippet(t4)) FROM t4 WHERE t4 MATCH 'target'}
+} {1 111 2 117}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3am.test b/third_party/sqlite/test/fts3am.test
new file mode 100755
index 0000000..4a09cd8
--- /dev/null
+++ b/third_party/sqlite/test/fts3am.test
@@ -0,0 +1,65 @@
+# 2007 April 9
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. fts3
+# DELETE handling assumed all fields were non-null. This was not
+# the intention at all.
+#
+# $Id: fts3am.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(col_a, col_b);
+
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(1, 'testing', 'testing');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(2, 'only a', null);
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(3, null, 'only b');
+ INSERT INTO t1(rowid, col_a, col_b) VALUES(4, null, null);
+}
+
+do_test fts3am-1.0 {
+ execsql {
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {2 2 4}
+
+do_test fts3am-1.1 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 1;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {1 1 3}
+
+do_test fts3am-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 2;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 1 2}
+
+do_test fts3am-1.3 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 3;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 1}
+
+do_test fts3am-1.4 {
+ execsql {
+ DELETE FROM t1 WHERE rowid = 4;
+ SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1;
+ }
+} {0 0 0}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3an.test b/third_party/sqlite/test/fts3an.test
new file mode 100755
index 0000000..f19d573
--- /dev/null
+++ b/third_party/sqlite/test/fts3an.test
@@ -0,0 +1,196 @@
+# 2007 April 26
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements tests for prefix-searching in the fts3
+# component of the SQLite library.
+#
+# $Id: fts3an.test,v 1.2 2007/12/13 21:54:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# A large string to prime the pump with.
+set text {
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas
+ iaculis mollis ipsum. Praesent rhoncus placerat justo. Duis non quam
+ sed turpis posuere placerat. Curabitur et lorem in lorem porttitor
+ aliquet. Pellentesque bibendum tincidunt diam. Vestibulum blandit
+ ante nec elit. In sapien diam, facilisis eget, dictum sed, viverra
+ at, felis. Vestibulum magna. Sed magna dolor, vestibulum rhoncus,
+ ornare vel, vulputate sit amet, felis. Integer malesuada, tellus at
+ luctus gravida, diam nunc porta nibh, nec imperdiet massa metus eu
+ lectus. Aliquam nisi. Nunc fringilla nulla at lectus. Suspendisse
+ potenti. Cum sociis natoque penatibus et magnis dis parturient
+ montes, nascetur ridiculus mus. Pellentesque odio nulla, feugiat eu,
+ suscipit nec, consequat quis, risus.
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+
+ INSERT INTO t1(rowid, c) VALUES(1, $text);
+ INSERT INTO t1(rowid, c) VALUES(2, 'Another lovely row');
+}
+
+# Exact match
+do_test fts3an-1.1 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lorem'"
+} {1}
+
+# And a prefix
+do_test fts3an-1.2 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lore*'"
+} {1}
+
+# Prefix includes exact match
+do_test fts3an-1.3 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lorem*'"
+} {1}
+
+# Make certain everything isn't considered a prefix!
+do_test fts3an-1.4 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lore'"
+} {}
+
+# Prefix across multiple rows.
+do_test fts3an-1.5 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lo*'"
+} {1 2}
+
+# Likewise, with multiple hits in one document.
+do_test fts3an-1.6 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'l*'"
+} {1 2}
+
+# Prefix which should only hit one document.
+do_test fts3an-1.7 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lov*'"
+} {2}
+
+# * not at end is dropped.
+do_test fts3an-1.8 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH 'lo *'"
+} {}
+
+# Stand-alone * is dropped.
+do_test fts3an-1.9 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '*'"
+} {}
+
+# Phrase-query prefix.
+do_test fts3an-1.10 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"lovely r*\"'"
+} {2}
+do_test fts3an-1.11 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"lovely r\"'"
+} {}
+
+# Phrase query with multiple prefix matches.
+do_test fts3an-1.12 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"a* l*\"'"
+} {1 2}
+
+# Phrase query with multiple prefix matches.
+do_test fts3an-1.13 {
+ execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"a* l* row\"'"
+} {2}
+
+
+
+
+# Test across updates (and, by implication, deletes).
+
+# Version of text without "lorem".
+regsub -all {[Ll]orem} $text '' ntext
+
+db eval {
+ CREATE VIRTUAL TABLE t2 USING fts3(c);
+
+ INSERT INTO t2(rowid, c) VALUES(1, $text);
+ INSERT INTO t2(rowid, c) VALUES(2, 'Another lovely row');
+ UPDATE t2 SET c = $ntext WHERE rowid = 1;
+}
+
+# Can't see lorem as an exact match.
+do_test fts3an-2.1 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lorem'"
+} {}
+
+# Can't see a prefix of lorem, either.
+do_test fts3an-2.2 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lore*'"
+} {}
+
+# Can see lovely in the other document.
+do_test fts3an-2.3 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lo*'"
+} {2}
+
+# Can still see other hits.
+do_test fts3an-2.4 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'l*'"
+} {1 2}
+
+# Prefix which should only hit one document.
+do_test fts3an-2.5 {
+ execsql "SELECT rowid FROM t2 WHERE t2 MATCH 'lov*'"
+} {2}
+
+
+
+# Test with a segment which will have multiple levels in the tree.
+
+# Build a big document with lots of unique terms.
+set bigtext $text
+foreach c {a b c d e} {
+ regsub -all {[A-Za-z]+} $bigtext "&$c" t
+ append bigtext $t
+}
+
+# Populate a table with many copies of the big document, so that we
+# can test the number of hits found. Populate $ret with the expected
+# hit counts for each row. offsets() returns 4 elements for every
+# hit. We'll have 6 hits for row 1, 1 for row 2, and 6*(2^5)==192 for
+# $bigtext.
+set ret {6 1}
+db eval {
+ BEGIN;
+ CREATE VIRTUAL TABLE t3 USING fts3(c);
+
+ INSERT INTO t3(rowid, c) VALUES(1, $text);
+ INSERT INTO t3(rowid, c) VALUES(2, 'Another lovely row');
+}
+for {set i 0} {$i<100} {incr i} {
+ db eval {INSERT INTO t3(rowid, c) VALUES(3+$i, $bigtext)}
+ lappend ret 192
+}
+db eval {COMMIT;}
+
+# Test that we get the expected number of hits.
+do_test fts3an-3.1 {
+ set t {}
+ db eval {SELECT offsets(t3) as o FROM t3 WHERE t3 MATCH 'l*'} {
+ set l [llength $o]
+ lappend t [expr {$l/4}]
+ }
+ set t
+} $ret
+
+# TODO(shess) It would be useful to test a couple edge cases, but I
+# don't know if we have the precision to manage it from here at this
+# time. Prefix hits can cross leaves, which the code above _should_
+# hit by virtue of size. There are two variations on this. If the
+# tree is 2 levels high, the code will find the leaf-node extent
+# directly, but if it is higher, the code will have to follow two
+# separate interior branches down the tree. Both should be tested.
+
+finish_test
diff --git a/third_party/sqlite/test/fts3ao.test b/third_party/sqlite/test/fts3ao.test
new file mode 100755
index 0000000..c3d356e
--- /dev/null
+++ b/third_party/sqlite/test/fts3ao.test
@@ -0,0 +1,169 @@
+# 2007 June 20
+#
+# 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 testing the FTS3 module.
+#
+# $Id: fts3ao.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+#---------------------------------------------------------------------
+# These tests, fts3ao-1.*, test that ticket #2429 is fixed.
+#
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two');
+}
+do_test fts3ao-1.1 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE c MATCH 'four';
+ }
+} {1 {one <b>four</b> two}}
+do_test fts3ao-1.2 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE b MATCH 'four';
+ }
+} {1 {one <b>four</b>}}
+do_test fts3ao-1.3 {
+ execsql {
+ SELECT rowid, snippet(t1) FROM t1 WHERE a MATCH 'four';
+ }
+} {1 {one three <b>four</b>}}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts3 table.
+#
+do_test fts3ao-2.1 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {t1 t1_content t1_segments t1_segdir}
+do_test fts3ao-2.2 {
+ execsql { ALTER TABLE t1 RENAME to fts_t1; }
+} {}
+do_test fts3ao-2.3 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts3ao-2.4 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir}
+
+# See what happens when renaming the fts3 table fails.
+#
+do_test fts3ao-2.5 {
+ catchsql {
+ CREATE TABLE t1_segdir(a, b, c);
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+do_test fts3ao-2.6 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts3ao-2.7 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir}
+
+# See what happens when renaming the fts3 table fails inside a transaction.
+#
+do_test fts3ao-2.8 {
+ execsql {
+ BEGIN;
+ INSERT INTO fts_t1(a, b, c) VALUES('one two three', 'one four', 'one two');
+ }
+} {}
+do_test fts3ao-2.9 {
+ catchsql {
+ ALTER TABLE fts_t1 RENAME to t1;
+ }
+} {1 {SQL logic error or missing database}}
+do_test fts3ao-2.10 {
+ execsql { SELECT rowid, snippet(fts_t1) FROM fts_t1 WHERE a MATCH 'four'; }
+} {1 {one three <b>four</b>}}
+do_test fts3ao-2.11 {
+ execsql { SELECT tbl_name FROM sqlite_master WHERE type = 'table'}
+} {fts_t1 fts_t1_content fts_t1_segments fts_t1_segdir t1_segdir}
+do_test fts3ao-2.12 {
+ execsql COMMIT
+ execsql {SELECT a FROM fts_t1}
+} {{one three four} {one two three}}
+do_test fts3ao-2.12 {
+ execsql { SELECT a, b, c FROM fts_t1 WHERE c MATCH 'four'; }
+} {{one three four} {one four} {one four two}}
+
+#-------------------------------------------------------------------
+# Close, delete and reopen the database. The following test should
+# be run on an initially empty db.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+do_test fts3ao-3.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts3(a, b, c);
+ INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one two');
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two}}
+
+# This test was crashing at one point.
+#
+do_test fts3ao-3.2 {
+ execsql {
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ CREATE TABLE t3(a, b, c);
+ SELECT a, b, c FROM t1 WHERE c MATCH 'two';
+ }
+} {{one three four} {one four} {one two} {one three four} {one four} {one two}}
+
+#---------------------------------------------------------------------
+# Test that it is possible to rename an fts3 table in an attached
+# database.
+#
+file delete -force test2.db test2.db-journal
+
+do_test fts3ao-3.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE VIRTUAL TABLE aux.t1 USING fts3(a, b, c);
+ INSERT INTO aux.t1(a, b, c) VALUES(
+ 'neung song sahm', 'neung see', 'neung see song'
+ );
+ }
+} {}
+
+do_test fts3ao-3.2 {
+ execsql { SELECT a, b, c FROM aux.t1 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts3ao-3.3 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one two}}
+
+do_test fts3ao-3.4 {
+ execsql { ALTER TABLE aux.t1 RENAME TO t2 }
+} {}
+
+do_test fts3ao-3.2 {
+ execsql { SELECT a, b, c FROM t2 WHERE a MATCH 'song'; }
+} {{neung song sahm} {neung see} {neung see song}}
+
+do_test fts3ao-3.3 {
+ execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
+} {{one three four} {one four} {one two}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3atoken.test b/third_party/sqlite/test/fts3atoken.test
new file mode 100755
index 0000000..cf9574e
--- /dev/null
+++ b/third_party/sqlite/test/fts3atoken.test
@@ -0,0 +1,174 @@
+# 2007 June 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The focus
+# of this script is testing the pluggable tokeniser feature of the
+# FTS3 module.
+#
+# $Id: fts3atoken.test,v 1.1 2007/08/20 17:38:42 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+proc escape_string {str} {
+ set out ""
+ foreach char [split $str ""] {
+ scan $char %c i
+ if {$i<=127} {
+ append out $char
+ } else {
+ append out [format {\x%.4x} $i]
+ }
+ }
+ set out
+}
+
+#--------------------------------------------------------------------------
+# Test cases fts3token-1.* are the warm-body test for the SQL scalar
+# function fts3_tokenizer(). The procedure is as follows:
+#
+# 1: Verify that there is no such fts3 tokenizer as 'blah'.
+#
+# 2: Query for the built-in tokenizer 'simple'. Insert a copy of the
+# retrieved value as tokenizer 'blah'.
+#
+# 3: Test that the value returned for tokenizer 'blah' is now the
+# same as that retrieved for 'simple'.
+#
+# 4: Test that it is now possible to create an fts3 table using
+# tokenizer 'blah' (it was not possible in step 1).
+#
+# 5: Test that the table created to use tokenizer 'blah' is usable.
+#
+do_test fts3token-1.1 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah);
+ }
+} {1 {unknown tokenizer: blah}}
+do_test fts3token-1.2 {
+ execsql {
+ SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL;
+ }
+} {0}
+do_test fts3token-1.3 {
+ execsql {
+ SELECT fts3_tokenizer('blah') == fts3_tokenizer('simple');
+ }
+} {1}
+do_test fts3token-1.4 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah);
+ }
+} {0 {}}
+do_test fts3token-1.5 {
+ execsql {
+ INSERT INTO t1(content) VALUES('There was movement at the station');
+ INSERT INTO t1(content) VALUES('For the word has passed around');
+ INSERT INTO t1(content) VALUES('That the colt from ol regret had got away');
+ SELECT content FROM t1 WHERE content MATCH 'movement'
+ }
+} {{There was movement at the station}}
+
+#--------------------------------------------------------------------------
+# Test cases fts3token-2.* test error cases in the scalar function based
+# API for getting and setting tokenizers.
+#
+do_test fts3token-2.1 {
+ catchsql {
+ SELECT fts3_tokenizer('nosuchtokenizer');
+ }
+} {1 {unknown tokenizer: nosuchtokenizer}}
+
+#--------------------------------------------------------------------------
+# Test cases fts3token-3.* test the three built-in tokenizers with a
+# simple input string via the built-in test function. This is as much
+# to test the test function as the tokenizer implementations.
+#
+do_test fts3token-3.1 {
+ execsql {
+ SELECT fts3_tokenizer_test('simple', 'I don''t see how');
+ }
+} {{0 i I 1 don don 2 t t 3 see see 4 how how}}
+do_test fts3token-3.2 {
+ execsql {
+ SELECT fts3_tokenizer_test('porter', 'I don''t see how');
+ }
+} {{0 i I 1 don don 2 t t 3 see see 4 how how}}
+ifcapable icu {
+ do_test fts3token-3.3 {
+ execsql {
+ SELECT fts3_tokenizer_test('icu', 'I don''t see how');
+ }
+ } {{0 i I 1 don't don't 2 see see 3 how how}}
+}
+
+#--------------------------------------------------------------------------
+# Test cases fts3token-4.* test the ICU tokenizer. In practice, this
+# tokenizer only has two modes - "thai" and "everybody else". Some other
+# Asian languages (Lao, Khmer etc.) require the same special treatment as
+# Thai, but ICU doesn't support them yet.
+#
+ifcapable icu {
+
+ proc do_icu_test {name locale input output} {
+ set ::out [db eval { SELECT fts3_tokenizer_test('icu', $locale, $input) }]
+ do_test $name {
+ lindex $::out 0
+ } $output
+ }
+
+ do_icu_test fts3token-4.1 en_US {} {}
+ do_icu_test fts3token-4.2 en_US {Test cases fts3} [list \
+ 0 test Test 1 cases cases 2 fts3 fts3
+ ]
+
+ # The following test shows that ICU is smart enough to recognise
+ # Thai chararacters, even when the locale is set to English/United
+ # States.
+ #
+ set input "\u0e2d\u0e30\u0e44\u0e23\u0e19\u0e30\u0e04\u0e23\u0e31\u0e1a"
+ set output "0 \u0e2d\u0e30\u0e44\u0e23 \u0e2d\u0e30\u0e44\u0e23 "
+ append output "1 \u0e19\u0e30 \u0e19\u0e30 "
+ append output "2 \u0e04\u0e23\u0e31\u0e1a \u0e04\u0e23\u0e31\u0e1a"
+
+ do_icu_test fts3token-4.3 th_TH $input $output
+ do_icu_test fts3token-4.4 en_US $input $output
+
+ # ICU handles an unknown locale by falling back to the default.
+ # So this is not an error.
+ do_icu_test fts3token-4.5 MiddleOfTheOcean $input $output
+
+ set longtoken "AReallyReallyLongTokenOneThatWillSurelyRequire"
+ append longtoken "AReallocInTheIcuTokenizerCode"
+
+ set input "short tokens then "
+ append input $longtoken
+ set output "0 short short "
+ append output "1 tokens tokens "
+ append output "2 then then "
+ append output "3 [string tolower $longtoken] $longtoken"
+
+ do_icu_test fts3token-4.6 MiddleOfTheOcean $input $output
+ do_icu_test fts3token-4.7 th_TH $input $output
+ do_icu_test fts3token-4.8 en_US $input $output
+}
+
+do_test fts3token-internal {
+ execsql { SELECT fts3_tokenizer_internal_test() }
+} {ok}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3b.test b/third_party/sqlite/test/fts3b.test
new file mode 100755
index 0000000..17ee0da
--- /dev/null
+++ b/third_party/sqlite/test/fts3b.test
@@ -0,0 +1,218 @@
+# 2007 August 20
+#
+# 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. This
+# script tests for the fts2 rowid-versus-vacuum problem (ticket #2566).
+#
+# $Id: fts3b.test,v 1.3 2007/09/13 18:14:49 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (c) VALUES('this is a test');
+ INSERT INTO t1 (c) VALUES('that was a test');
+ INSERT INTO t1 (c) VALUES('this is fun');
+ DELETE FROM t1 WHERE c = 'that was a test';
+}
+
+# Baseline test.
+do_test fts3b-1.1 {
+ execsql {
+ SELECT rowid FROM t1 WHERE c MATCH 'this';
+ }
+} {1 3}
+
+db eval {VACUUM}
+
+# The VACUUM renumbered the t1_content table in fts2, which breaks
+# this.
+do_test fts3b-1.2 {
+ execsql {
+ SELECT rowid FROM t1 WHERE c MATCH 'this';
+ }
+} {1 3}
+
+# The t2 table is unfortunately pretty contrived. We need documents
+# that are bigger than ROOT_MAX (1024) to force segments out of the
+# segdir and into %_segments. We also need to force segment merging
+# to generate a hole in the %_segments table, which needs more than 16
+# docs. Beyond that, to test correct operation of BLOCK_SELECT_STMT,
+# we need to merge a mult-level tree, which is where the 10,000 comes
+# from. Which is slow, thus the set of transactions, with the 500
+# being a number such that 10,000/500 > 16.
+set text {
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas
+ iaculis mollis ipsum. Praesent rhoncus placerat justo. Duis non quam
+ sed turpis posuere placerat. Curabitur et lorem in lorem porttitor
+ aliquet. Pellentesque bibendum tincidunt diam. Vestibulum blandit
+ ante nec elit. In sapien diam, facilisis eget, dictum sed, viverra
+ at, felis. Vestibulum magna. Sed magna dolor, vestibulum rhoncus,
+ ornare vel, vulputate sit amet, felis. Integer malesuada, tellus at
+ luctus gravida, diam nunc porta nibh, nec imperdiet massa metus eu
+ lectus. Aliquam nisi. Nunc fringilla nulla at lectus. Suspendisse
+ potenti. Cum sociis natoque penatibus et magnis dis parturient
+ montes, nascetur ridiculus mus. Pellentesque odio nulla, feugiat eu,
+ suscipit nec, consequat quis, risus.
+}
+append text $text
+
+db eval {CREATE VIRTUAL TABLE t2 USING fts3(c)}
+set res {}
+db eval {BEGIN}
+for {set ii 0} {$ii<10000} {incr ii} {
+ db eval {INSERT INTO t2 (c) VALUES ($text)}
+ lappend res [expr {$ii+1}]
+ if {($ii%500)==0} {
+ db eval {
+ COMMIT;
+ BEGIN;
+ }
+ }
+}
+db eval {COMMIT}
+
+do_test fts3b-2.1 {
+ execsql {
+ SELECT rowid FROM t2 WHERE c MATCH 'lorem';
+ }
+} $res
+
+db eval {VACUUM}
+
+# The VACUUM renumbered the t2_segment table in fts2, which would
+# break the following.
+do_test fts3b-2.2 {
+ execsql {
+ SELECT rowid FROM t2 WHERE c MATCH 'lorem';
+ }
+} $res
+
+# Since fts3 is already an API break, I've marked the table-named
+# column HIDDEN.
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts3(c);
+ INSERT INTO t3 (c) VALUES('this is a test');
+ INSERT INTO t3 (c) VALUES('that was a test');
+ INSERT INTO t3 (c) VALUES('this is fun');
+ DELETE FROM t3 WHERE c = 'that was a test';
+}
+
+# Test that the table-named column still works.
+do_test fts3b-3.1 {
+ execsql {
+ SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'test';
+ }
+} {{this is a <b>test</b>}}
+
+# Test that the column doesn't appear when selecting all columns.
+do_test fts3b-3.2 {
+ execsql {
+ SELECT * FROM t3 WHERE rowid = 1;
+ }
+} {{this is a test}}
+
+# Test that the column doesn't conflict with inserts that don't name
+# columns.
+do_test fts3b-3.3 {
+ execsql {
+ INSERT INTO t3 VALUES ('another test');
+ }
+} {}
+
+# fts3 adds a new implicit column, docid, which acts as an alias for
+# rowid.
+
+db eval {
+ CREATE VIRTUAL TABLE t4 USING fts3(c);
+ INSERT INTO t4 (c) VALUES('this is a test');
+ INSERT INTO t4 (c) VALUES('that was a test');
+ INSERT INTO t4 (c) VALUES('this is fun');
+ DELETE FROM t4 WHERE c = 'that was a test';
+}
+
+# Test that docid is present and identical to rowid.
+do_test fts3b-4.1 {
+ execsql {
+ SELECT rowid FROM t4 WHERE rowid <> docid;
+ }
+} {}
+
+# Test that docid is hidden.
+do_test fts3b-4.2 {
+ execsql {
+ SELECT * FROM t4 WHERE rowid = 1;
+ }
+} {{this is a test}}
+
+# Test that docid can be selected.
+do_test fts3b-4.3 {
+ execsql {
+ SELECT docid, * FROM t4 WHERE rowid = 1;
+ }
+} {1 {this is a test}}
+
+# Test that docid can be used in WHERE.
+do_test fts3b-4.4 {
+ execsql {
+ SELECT docid, * FROM t4 WHERE docid = 1;
+ }
+} {1 {this is a test}}
+
+# Test that the column doesn't conflict with inserts that don't name
+# columns. [Yes, this is the same as fts3b-3.3, here just in case the
+# goals of that test change.]
+do_test fts3b-4.5 {
+ execsql {
+ INSERT INTO t4 VALUES ('another test');
+ }
+} {}
+
+# Test that the docid can be forced on insert.
+do_test fts3b-4.6 {
+ execsql {
+ INSERT INTO t4 (docid, c) VALUES (10, 'yet another test');
+ SELECT * FROM t4 WHERE docid = 10;
+ }
+} {{yet another test}}
+
+# Test that rowid can also be forced.
+do_test fts3b-4.7 {
+ execsql {
+ INSERT INTO t4 (docid, c) VALUES (12, 'still testing');
+ SELECT * FROM t4 WHERE docid = 12;
+ }
+} {{still testing}}
+
+# If an insert tries to set both docid and rowid, require an error.
+do_test fts3b-4.8 {
+ catchsql {
+ INSERT INTO t4 (rowid, docid, c) VALUES (14, 15, 'bad test');
+ SELECT * FROM t4 WHERE docid = 14;
+ }
+} {1 {SQL logic error or missing database}}
+
+# Don't allow update of docid, to match rowid behaviour.
+do_test fts3b-4.9 {
+ catchsql {
+ UPDATE t4 SET docid = 14 WHERE docid = 12;
+ }
+} {1 {SQL logic error or missing database}}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3c.test b/third_party/sqlite/test/fts3c.test
new file mode 100755
index 0000000..2c73d4b
--- /dev/null
+++ b/third_party/sqlite/test/fts3c.test
@@ -0,0 +1,357 @@
+# 2008 June 26
+#
+# 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 exercises some new testing functions in the FTS3 module,
+# and then uses them to do some basic tests that FTS3 is internally
+# working as expected.
+#
+# $Id: fts3c.test,v 1.1 2008/07/03 19:53:22 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Probe to see if support for these functions is compiled in.
+# TODO(shess): Change main.mk to do the right thing and remove this test.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'x');
+}
+
+set s {SELECT dump_terms(t1, 1) FROM t1 LIMIT 1}
+set r {1 {unable to use function dump_terms in the requested context}}
+if {[catchsql $s]==$r} {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Test that the new functions give appropriate errors.
+do_test fts3c-0.0 {
+ catchsql {
+ SELECT dump_terms(t1, 1) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: incorrect arguments}}
+
+do_test fts3c-0.1 {
+ catchsql {
+ SELECT dump_terms(t1, 0, 0, 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: incorrect arguments}}
+
+do_test fts3c-0.2 {
+ catchsql {
+ SELECT dump_terms(1, t1) FROM t1 LIMIT 1;
+ }
+} {1 {unable to use function dump_terms in the requested context}}
+
+do_test fts3c-0.3 {
+ catchsql {
+ SELECT dump_terms(t1, 16, 16) FROM t1 LIMIT 1;
+ }
+} {1 {dump_terms: segment not found}}
+
+do_test fts3c-0.4 {
+ catchsql {
+ SELECT dump_doclist(t1) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts3c-0.5 {
+ catchsql {
+ SELECT dump_doclist(t1, NULL) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: empty second argument}}
+
+do_test fts3c-0.6 {
+ catchsql {
+ SELECT dump_doclist(t1, '') FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: empty second argument}}
+
+do_test fts3c-0.7 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts3c-0.8 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 0, 0, 0) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: incorrect arguments}}
+
+do_test fts3c-0.9 {
+ catchsql {
+ SELECT dump_doclist(t1, 'a', 16, 16) FROM t1 LIMIT 1;
+ }
+} {1 {dump_doclist: segment not found}}
+
+#*************************************************************************
+# Utility function to check for the expected terms in the segment
+# level/index. _all version does same but for entire index.
+proc check_terms {test level index terms} {
+ # TODO(shess): Figure out why uplevel in do_test can't catch
+ # $level and $index directly.
+ set ::level $level
+ set ::index $index
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+proc check_terms_all {test terms} {
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+
+# Utility function to check for the expected doclist for the term in
+# segment level/index. _all version does same for entire index.
+proc check_doclist {test level index term doclist} {
+ # TODO(shess): Again, why can't the non-:: versions work?
+ set ::term $term
+ set ::level $level
+ set ::index $index
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+proc check_doclist_all {test term doclist} {
+ set ::term $term
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+
+#*************************************************************************
+# Test the segments resulting from straight-forward inserts.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+}
+
+# Check for expected segments and expected matches.
+do_test fts3c-1.0.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2}
+do_test fts3c-1.0.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+# Check the specifics of the segments constructed.
+# Logical view of entire index.
+check_terms_all fts3c-1.0.1 {a is test that this was}
+check_doclist_all fts3c-1.0.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts3c-1.0.1.2 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts3c-1.0.1.3 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts3c-1.0.1.4 that {[2 0[0]]}
+check_doclist_all fts3c-1.0.1.5 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts3c-1.0.1.6 was {[2 0[1]]}
+
+# Segment 0,0
+check_terms fts3c-1.0.2 0 0 {a is test this}
+check_doclist fts3c-1.0.2.1 0 0 a {[1 0[2]]}
+check_doclist fts3c-1.0.2.2 0 0 is {[1 0[1]]}
+check_doclist fts3c-1.0.2.3 0 0 test {[1 0[3]]}
+check_doclist fts3c-1.0.2.4 0 0 this {[1 0[0]]}
+
+# Segment 0,1
+check_terms fts3c-1.0.3 0 1 {a test that was}
+check_doclist fts3c-1.0.3.1 0 1 a {[2 0[2]]}
+check_doclist fts3c-1.0.3.2 0 1 test {[2 0[3]]}
+check_doclist fts3c-1.0.3.3 0 1 that {[2 0[0]]}
+check_doclist fts3c-1.0.3.4 0 1 was {[2 0[1]]}
+
+# Segment 0,2
+check_terms fts3c-1.0.4 0 2 {a is test this}
+check_doclist fts3c-1.0.4.1 0 2 a {[3 0[2]]}
+check_doclist fts3c-1.0.4.2 0 2 is {[3 0[1]]}
+check_doclist fts3c-1.0.4.3 0 2 test {[3 0[3]]}
+check_doclist fts3c-1.0.4.4 0 2 this {[3 0[0]]}
+
+#*************************************************************************
+# Test the segments resulting from inserts followed by a delete.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE docid = 1;
+}
+
+do_test fts3c-1.1.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2 0 3}
+do_test fts3c-1.1.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}}
+
+check_terms_all fts3c-1.1.1 {a is test that this was}
+check_doclist_all fts3c-1.1.1.1 a {[2 0[2]] [3 0[2]]}
+check_doclist_all fts3c-1.1.1.2 is {[3 0[1]]}
+check_doclist_all fts3c-1.1.1.3 test {[2 0[3]] [3 0[3]]}
+check_doclist_all fts3c-1.1.1.4 that {[2 0[0]]}
+check_doclist_all fts3c-1.1.1.5 this {[3 0[0]]}
+check_doclist_all fts3c-1.1.1.6 was {[2 0[1]]}
+
+check_terms fts3c-1.1.2 0 0 {a is test this}
+check_doclist fts3c-1.1.2.1 0 0 a {[1 0[2]]}
+check_doclist fts3c-1.1.2.2 0 0 is {[1 0[1]]}
+check_doclist fts3c-1.1.2.3 0 0 test {[1 0[3]]}
+check_doclist fts3c-1.1.2.4 0 0 this {[1 0[0]]}
+
+check_terms fts3c-1.1.3 0 1 {a test that was}
+check_doclist fts3c-1.1.3.1 0 1 a {[2 0[2]]}
+check_doclist fts3c-1.1.3.2 0 1 test {[2 0[3]]}
+check_doclist fts3c-1.1.3.3 0 1 that {[2 0[0]]}
+check_doclist fts3c-1.1.3.4 0 1 was {[2 0[1]]}
+
+check_terms fts3c-1.1.4 0 2 {a is test this}
+check_doclist fts3c-1.1.4.1 0 2 a {[3 0[2]]}
+check_doclist fts3c-1.1.4.2 0 2 is {[3 0[1]]}
+check_doclist fts3c-1.1.4.3 0 2 test {[3 0[3]]}
+check_doclist fts3c-1.1.4.4 0 2 this {[3 0[0]]}
+
+check_terms fts3c-1.1.5 0 3 {a is test this}
+check_doclist fts3c-1.1.5.1 0 3 a {[1]}
+check_doclist fts3c-1.1.5.2 0 3 is {[1]}
+check_doclist fts3c-1.1.5.3 0 3 test {[1]}
+check_doclist fts3c-1.1.5.4 0 3 this {[1]}
+
+#*************************************************************************
+# Test results when all references to certain tokens are deleted.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE docid IN (1,3);
+}
+
+# Still 4 segments because 0,3 will contain deletes for docid 1 and 3.
+do_test fts3c-1.2.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 0 2 0 3}
+do_test fts3c-1.2.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts3c-1.2.1 {a is test that this was}
+check_doclist_all fts3c-1.2.1.1 a {[2 0[2]]}
+check_doclist_all fts3c-1.2.1.2 is {}
+check_doclist_all fts3c-1.2.1.3 test {[2 0[3]]}
+check_doclist_all fts3c-1.2.1.4 that {[2 0[0]]}
+check_doclist_all fts3c-1.2.1.5 this {}
+check_doclist_all fts3c-1.2.1.6 was {[2 0[1]]}
+
+check_terms fts3c-1.2.2 0 0 {a is test this}
+check_doclist fts3c-1.2.2.1 0 0 a {[1 0[2]]}
+check_doclist fts3c-1.2.2.2 0 0 is {[1 0[1]]}
+check_doclist fts3c-1.2.2.3 0 0 test {[1 0[3]]}
+check_doclist fts3c-1.2.2.4 0 0 this {[1 0[0]]}
+
+check_terms fts3c-1.2.3 0 1 {a test that was}
+check_doclist fts3c-1.2.3.1 0 1 a {[2 0[2]]}
+check_doclist fts3c-1.2.3.2 0 1 test {[2 0[3]]}
+check_doclist fts3c-1.2.3.3 0 1 that {[2 0[0]]}
+check_doclist fts3c-1.2.3.4 0 1 was {[2 0[1]]}
+
+check_terms fts3c-1.2.4 0 2 {a is test this}
+check_doclist fts3c-1.2.4.1 0 2 a {[3 0[2]]}
+check_doclist fts3c-1.2.4.2 0 2 is {[3 0[1]]}
+check_doclist fts3c-1.2.4.3 0 2 test {[3 0[3]]}
+check_doclist fts3c-1.2.4.4 0 2 this {[3 0[0]]}
+
+check_terms fts3c-1.2.5 0 3 {a is test this}
+check_doclist fts3c-1.2.5.1 0 3 a {[1] [3]}
+check_doclist fts3c-1.2.5.2 0 3 is {[1] [3]}
+check_doclist fts3c-1.2.5.3 0 3 test {[1] [3]}
+check_doclist fts3c-1.2.5.4 0 3 this {[1] [3]}
+
+#*************************************************************************
+# Test results when everything is optimized manually.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE docid IN (1,3);
+ DROP TABLE IF EXISTS t1old;
+ ALTER TABLE t1 RENAME TO t1old;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) SELECT docid, c FROM t1old;
+ DROP TABLE t1old;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts3c-1.3.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts3c-1.3.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts3c-1.3.1 {a test that was}
+check_doclist_all fts3c-1.3.1.1 a {[2 0[2]]}
+check_doclist_all fts3c-1.3.1.2 test {[2 0[3]]}
+check_doclist_all fts3c-1.3.1.3 that {[2 0[0]]}
+check_doclist_all fts3c-1.3.1.4 was {[2 0[1]]}
+
+check_terms fts3c-1.3.2 0 0 {a test that was}
+check_doclist fts3c-1.3.2.1 0 0 a {[2 0[2]]}
+check_doclist fts3c-1.3.2.2 0 0 test {[2 0[3]]}
+check_doclist fts3c-1.3.2.3 0 0 that {[2 0[0]]}
+check_doclist fts3c-1.3.2.4 0 0 was {[2 0[1]]}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3d.test b/third_party/sqlite/test/fts3d.test
new file mode 100755
index 0000000..bec488d
--- /dev/null
+++ b/third_party/sqlite/test/fts3d.test
@@ -0,0 +1,346 @@
+# 2008 June 26
+#
+# 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 testing the FTS3 module's optimize() function.
+#
+# $Id: fts3d.test,v 1.2 2008/07/15 21:32:07 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Probe to see if support for the FTS3 dump_* functions is compiled in.
+# TODO(shess): Change main.mk to do the right thing and remove this test.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'x');
+}
+
+set s {SELECT dump_terms(t1, 1) FROM t1 LIMIT 1}
+set r {1 {unable to use function dump_terms in the requested context}}
+if {[catchsql $s]==$r} {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Utility function to check for the expected terms in the segment
+# level/index. _all version does same but for entire index.
+proc check_terms {test level index terms} {
+ # TODO(shess): Figure out why uplevel in do_test can't catch
+ # $level and $index directly.
+ set ::level $level
+ set ::index $index
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+proc check_terms_all {test terms} {
+ do_test $test.terms {
+ execsql {
+ SELECT dump_terms(t1) FROM t1 LIMIT 1;
+ }
+ } [list $terms]
+}
+
+# Utility function to check for the expected doclist for the term in
+# segment level/index. _all version does same for entire index.
+proc check_doclist {test level index term doclist} {
+ # TODO(shess): Again, why can't the non-:: versions work?
+ set ::term $term
+ set ::level $level
+ set ::index $index
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term, $::level, $::index) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+proc check_doclist_all {test term doclist} {
+ set ::term $term
+ do_test $test {
+ execsql {
+ SELECT dump_doclist(t1, $::term) FROM t1 LIMIT 1;
+ }
+ } [list $doclist]
+}
+
+#*************************************************************************
+# Test results when all rows are deleted and one is added back.
+# Previously older segments would continue to exist, but now the index
+# should be dropped when the table is empty. The results should look
+# exactly like we never added the earlier rows in the first place.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE 1=1; -- Delete each row rather than dropping table.
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+}
+
+# Should be a single initial segment.
+do_test fts3d-1.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts3d-1.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}}
+
+check_terms_all fts3d-1.1 {a is test this}
+check_doclist_all fts3d-1.1.1 a {[1 0[2]]}
+check_doclist_all fts3d-1.1.2 is {[1 0[1]]}
+check_doclist_all fts3d-1.1.3 test {[1 0[3]]}
+check_doclist_all fts3d-1.1.4 this {[1 0[0]]}
+
+check_terms fts3d-1.2 0 0 {a is test this}
+check_doclist fts3d-1.2.1 0 0 a {[1 0[2]]}
+check_doclist fts3d-1.2.2 0 0 is {[1 0[1]]}
+check_doclist fts3d-1.2.3 0 0 test {[1 0[3]]}
+check_doclist fts3d-1.2.4 0 0 this {[1 0[0]]}
+
+#*************************************************************************
+# Test results when everything is optimized manually.
+# NOTE(shess): This is a copy of fts3c-1.3. I've pulled a copy here
+# because fts3d-2 and fts3d-3 should have identical results.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE docid IN (1,3);
+ DROP TABLE IF EXISTS t1old;
+ ALTER TABLE t1 RENAME TO t1old;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) SELECT docid, c FROM t1old;
+ DROP TABLE t1old;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts3d-2.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts3d-2.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts3d-2.1 {a test that was}
+check_doclist_all fts3d-2.1.1 a {[2 0[2]]}
+check_doclist_all fts3d-2.1.2 test {[2 0[3]]}
+check_doclist_all fts3d-2.1.3 that {[2 0[0]]}
+check_doclist_all fts3d-2.1.4 was {[2 0[1]]}
+
+check_terms fts3d-2.2 0 0 {a test that was}
+check_doclist fts3d-2.2.1 0 0 a {[2 0[2]]}
+check_doclist fts3d-2.2.2 0 0 test {[2 0[3]]}
+check_doclist fts3d-2.2.3 0 0 that {[2 0[0]]}
+check_doclist fts3d-2.2.4 0 0 was {[2 0[1]]}
+
+#*************************************************************************
+# Test results when everything is optimized via optimize().
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+ DELETE FROM t1 WHERE docid IN (1,3);
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+}
+
+# Should be a single optimal segment with the same logical results.
+do_test fts3d-3.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0}
+do_test fts3d-3.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
+
+check_terms_all fts3d-3.1 {a test that was}
+check_doclist_all fts3d-3.1.1 a {[2 0[2]]}
+check_doclist_all fts3d-3.1.2 test {[2 0[3]]}
+check_doclist_all fts3d-3.1.3 that {[2 0[0]]}
+check_doclist_all fts3d-3.1.4 was {[2 0[1]]}
+
+check_terms fts3d-3.2 0 0 {a test that was}
+check_doclist fts3d-3.2.1 0 0 a {[2 0[2]]}
+check_doclist fts3d-3.2.2 0 0 test {[2 0[3]]}
+check_doclist fts3d-3.2.3 0 0 that {[2 0[0]]}
+check_doclist fts3d-3.2.4 0 0 was {[2 0[1]]}
+
+#*************************************************************************
+# Test optimize() against a table involving segment merges.
+# NOTE(shess): Since there's no transaction, each of the INSERT/UPDATE
+# statements generates a segment.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+
+ INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
+
+ UPDATE t1 SET c = 'This is a test one' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test one' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test one' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test two' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test two' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test two' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test three' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test three' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test three' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test four' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test four' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test four' WHERE rowid = 3;
+
+ UPDATE t1 SET c = 'This is a test' WHERE rowid = 1;
+ UPDATE t1 SET c = 'That was a test' WHERE rowid = 2;
+ UPDATE t1 SET c = 'This is a test' WHERE rowid = 3;
+}
+
+# 2 segments in level 0, 1 in level 1 (18 segments created, 16
+# merged).
+do_test fts3d-4.segments {
+ execsql {
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {0 0 0 1 1 0}
+
+do_test fts3d-4.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+check_terms_all fts3d-4.1 {a four is one test that this three two was}
+check_doclist_all fts3d-4.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts3d-4.1.2 four {}
+check_doclist_all fts3d-4.1.3 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts3d-4.1.4 one {}
+check_doclist_all fts3d-4.1.5 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts3d-4.1.6 that {[2 0[0]]}
+check_doclist_all fts3d-4.1.7 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts3d-4.1.8 three {}
+check_doclist_all fts3d-4.1.9 two {}
+check_doclist_all fts3d-4.1.10 was {[2 0[1]]}
+
+check_terms fts3d-4.2 0 0 {a four test that was}
+check_doclist fts3d-4.2.1 0 0 a {[2 0[2]]}
+check_doclist fts3d-4.2.2 0 0 four {[2]}
+check_doclist fts3d-4.2.3 0 0 test {[2 0[3]]}
+check_doclist fts3d-4.2.4 0 0 that {[2 0[0]]}
+check_doclist fts3d-4.2.5 0 0 was {[2 0[1]]}
+
+check_terms fts3d-4.3 0 1 {a four is test this}
+check_doclist fts3d-4.3.1 0 1 a {[3 0[2]]}
+check_doclist fts3d-4.3.2 0 1 four {[3]}
+check_doclist fts3d-4.3.3 0 1 is {[3 0[1]]}
+check_doclist fts3d-4.3.4 0 1 test {[3 0[3]]}
+check_doclist fts3d-4.3.5 0 1 this {[3 0[0]]}
+
+check_terms fts3d-4.4 1 0 {a four is one test that this three two was}
+check_doclist fts3d-4.4.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist fts3d-4.4.2 1 0 four {[1] [2 0[4]] [3 0[4]]}
+check_doclist fts3d-4.4.3 1 0 is {[1 0[1]] [3 0[1]]}
+check_doclist fts3d-4.4.4 1 0 one {[1] [2] [3]}
+check_doclist fts3d-4.4.5 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist fts3d-4.4.6 1 0 that {[2 0[0]]}
+check_doclist fts3d-4.4.7 1 0 this {[1 0[0]] [3 0[0]]}
+check_doclist fts3d-4.4.8 1 0 three {[1] [2] [3]}
+check_doclist fts3d-4.4.9 1 0 two {[1] [2] [3]}
+check_doclist fts3d-4.4.10 1 0 was {[2 0[1]]}
+
+# Optimize should leave the result in the level of the highest-level
+# prior segment.
+do_test fts3d-4.5 {
+ execsql {
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index optimized} 1 0}
+
+# Identical to fts3d-4.matches.
+do_test fts3d-4.5.matches {
+ execsql {
+ SELECT OFFSETS(t1) FROM t1
+ WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
+ }
+} [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
+ {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
+ {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
+
+check_terms_all fts3d-4.5.1 {a is test that this was}
+check_doclist_all fts3d-4.5.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist_all fts3d-4.5.1.2 is {[1 0[1]] [3 0[1]]}
+check_doclist_all fts3d-4.5.1.3 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist_all fts3d-4.5.1.4 that {[2 0[0]]}
+check_doclist_all fts3d-4.5.1.5 this {[1 0[0]] [3 0[0]]}
+check_doclist_all fts3d-4.5.1.6 was {[2 0[1]]}
+
+check_terms fts3d-4.5.2 1 0 {a is test that this was}
+check_doclist fts3d-4.5.2.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
+check_doclist fts3d-4.5.2.2 1 0 is {[1 0[1]] [3 0[1]]}
+check_doclist fts3d-4.5.2.3 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
+check_doclist fts3d-4.5.2.4 1 0 that {[2 0[0]]}
+check_doclist fts3d-4.5.2.5 1 0 this {[1 0[0]] [3 0[0]]}
+check_doclist fts3d-4.5.2.6 1 0 was {[2 0[1]]}
+
+# Re-optimizing does nothing.
+do_test fts3d-5.0 {
+ execsql {
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index already optimal} 1 0}
+
+# Even if we move things around, still does nothing.
+do_test fts3d-5.1 {
+ execsql {
+ UPDATE t1_segdir SET level = 2 WHERE level = 1 AND idx = 0;
+ SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
+ SELECT level, idx FROM t1_segdir ORDER BY level, idx;
+ }
+} {{Index already optimal} 2 0}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3e.test b/third_party/sqlite/test/fts3e.test
new file mode 100755
index 0000000..03caaf8
--- /dev/null
+++ b/third_party/sqlite/test/fts3e.test
@@ -0,0 +1,125 @@
+# 2008 July 29
+#
+# 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.
+#
+#*************************************************************************
+# These tests exercise the various types of fts3 cursors.
+#
+# $Id: fts3e.test,v 1.1 2008/07/29 20:24:46 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+#*************************************************************************
+# Test table scan (QUERY_GENERIC). This kind of query happens for
+# queries with no WHERE clause, or for WHERE clauses which cannot be
+# satisfied by an index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
+ INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
+ INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
+}
+
+do_test fts3e-1.1 {
+ execsql {
+ SELECT docid FROM t1 ORDER BY docid;
+ }
+} {1 2 3}
+
+do_test fts3e-1.2 {
+ execsql {
+ SELECT docid FROM t1 WHERE c LIKE '%test' ORDER BY docid;
+ }
+} {1 2 3}
+
+do_test fts3e-1.3 {
+ execsql {
+ SELECT docid FROM t1 WHERE c LIKE 'That%' ORDER BY docid;
+ }
+} {2}
+
+#*************************************************************************
+# Test lookup by docid (QUERY_DOCID). This kind of query happens for
+# queries which select by the docid/rowid implicit index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, weight INTEGER UNIQUE);
+ INSERT INTO t2 VALUES (null, 10);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'This is a test');
+ INSERT INTO t2 VALUES (null, 5);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'That was a test');
+ INSERT INTO t2 VALUES (null, 20);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'This is a test');
+}
+
+# TODO(shess): This actually is doing QUERY_GENERIC? I'd have
+# expected QUERY_DOCID in this case, as for a very large table the
+# full scan is less efficient.
+do_test fts3e-2.1 {
+ execsql {
+ SELECT docid FROM t1 WHERE docid in (1, 2, 10);
+ SELECT rowid FROM t1 WHERE rowid in (1, 2, 10);
+ }
+} {1 2 1 2}
+
+do_test fts3e-2.2 {
+ execsql {
+ SELECT docid, weight FROM t1, t2 WHERE t2.id = t1.docid ORDER BY weight;
+ SELECT t1.rowid, weight FROM t1, t2 WHERE t2.id = t1.rowid ORDER BY weight;
+ }
+} {2 5 1 10 3 20 2 5 1 10 3 20}
+
+do_test fts3e-2.3 {
+ execsql {
+ SELECT docid, weight FROM t1, t2
+ WHERE t2.weight>5 AND t2.id = t1.docid ORDER BY weight;
+ SELECT t1.rowid, weight FROM t1, t2
+ WHERE t2.weight>5 AND t2.id = t1.rowid ORDER BY weight;
+ }
+} {1 10 3 20 1 10 3 20}
+
+#*************************************************************************
+# Test lookup by MATCH (QUERY_FULLTEXT). This is the fulltext index.
+db eval {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE VIRTUAL TABLE t1 USING fts3(c);
+ CREATE TABLE t2(id INTEGER PRIMARY KEY AUTOINCREMENT, weight INTEGER UNIQUE);
+ INSERT INTO t2 VALUES (null, 10);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'This is a test');
+ INSERT INTO t2 VALUES (null, 5);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'That was a test');
+ INSERT INTO t2 VALUES (null, 20);
+ INSERT INTO t1 (docid, c) VALUES (last_insert_rowid(), 'This is a test');
+}
+
+do_test fts3e-3.1 {
+ execsql {
+ SELECT docid FROM t1 WHERE t1 MATCH 'this' ORDER BY docid;
+ }
+} {1 3}
+
+do_test fts3e-3.2 {
+ execsql {
+ SELECT docid, weight FROM t1, t2
+ WHERE t1 MATCH 'this' AND t1.docid = t2.id ORDER BY weight;
+ }
+} {1 10 3 20}
+
+finish_test
diff --git a/third_party/sqlite/test/fts3near.test b/third_party/sqlite/test/fts3near.test
new file mode 100755
index 0000000..2a64762
--- /dev/null
+++ b/third_party/sqlite/test/fts3near.test
@@ -0,0 +1,168 @@
+
+# 2007 October 15
+#
+# 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.
+#
+#*************************************************************************
+#
+# $Id: fts3near.test,v 1.1 2007/10/22 18:02:20 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+db eval {
+ CREATE VIRTUAL TABLE t1 USING fts3(content);
+ INSERT INTO t1(content) VALUES('one three four five');
+ INSERT INTO t1(content) VALUES('two three four five');
+ INSERT INTO t1(content) VALUES('one two three four five');
+}
+
+do_test fts3near-1.1 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'one NEAR/0 three'}
+} {1}
+do_test fts3near-1.2 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'one NEAR/1 two'}
+} {3}
+do_test fts3near-1.3 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'one NEAR/1 three'}
+} {1 3}
+do_test fts3near-1.4 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'three NEAR/1 one'}
+} {1 3}
+do_test fts3near-1.5 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH '"one two" NEAR/1 five'}
+} {}
+do_test fts3near-1.6 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH '"one two" NEAR/2 five'}
+} {3}
+do_test fts3near-1.7 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'one NEAR four'}
+} {1 3}
+do_test fts3near-1.8 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'four NEAR three'}
+} {1 2 3}
+do_test fts3near-1.9 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH '"four five" NEAR/0 three'}
+} {1 2 3}
+do_test fts3near-1.10 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH '"four five" NEAR/2 one'}
+} {1 3}
+do_test fts3near-1.11 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH '"four five" NEAR/1 one'}
+} {1}
+do_test fts3near-1.12 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'five NEAR/1 "two three"'}
+} {2 3}
+do_test fts3near-1.13 {
+ execsql {SELECT docid FROM t1 WHERE content MATCH 'one NEAR five'}
+} {1 3}
+
+
+# Output format of the offsets() function:
+#
+# <column number> <term number> <starting offset> <number of bytes>
+#
+db eval {
+ INSERT INTO t1(content) VALUES('A X B C D A B');
+}
+do_test fts3near-2.1 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'A NEAR/0 B'
+ }
+} {{0 0 10 1 0 1 12 1}}
+do_test fts3near-2.2 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'B NEAR/0 A'
+ }
+} {{0 1 10 1 0 0 12 1}}
+do_test fts3near-2.3 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH '"C D" NEAR/0 A'
+ }
+} {{0 0 6 1 0 1 8 1 0 2 10 1}}
+do_test fts3near-2.4 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'A NEAR/0 "C D"'
+ }
+} {{0 1 6 1 0 2 8 1 0 0 10 1}}
+do_test fts3near-2.5 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'A NEAR A'
+ }
+} {{0 0 0 1 0 1 0 1 0 0 10 1 0 1 10 1}}
+do_test fts3near-2.6 {
+ execsql {
+ INSERT INTO t1 VALUES('A A A');
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'A NEAR/2 A';
+ }
+} [list [list 0 0 0 1 0 1 0 1 0 0 2 1 0 1 2 1 0 0 4 1 0 1 4 1]]
+do_test fts3near-2.7 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES('A A A A');
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'A NEAR A NEAR A';
+ }
+} [list [list \
+ 0 0 0 1 0 1 0 1 0 2 0 1 0 0 2 1 \
+ 0 1 2 1 0 2 2 1 0 0 4 1 0 1 4 1 \
+ 0 2 4 1 0 0 6 1 0 1 6 1 0 2 6 1 \
+]]
+
+db eval {
+ DELETE FROM t1;
+ INSERT INTO t1(content) VALUES(
+ 'one two three two four six three six nine four eight twelve'
+ );
+}
+
+do_test fts3near-3.1 {
+ execsql {SELECT offsets(t1) FROM t1 WHERE content MATCH 'three NEAR/1 one'}
+} {{0 1 0 3 0 0 8 5}}
+do_test fts3near-3.2 {
+ execsql {SELECT offsets(t1) FROM t1 WHERE content MATCH 'one NEAR/1 three'}
+} {{0 0 0 3 0 1 8 5}}
+do_test fts3near-3.3 {
+ execsql {SELECT offsets(t1) FROM t1 WHERE content MATCH 'three NEAR/1 two'}
+} {{0 1 4 3 0 0 8 5 0 1 14 3}}
+do_test fts3near-3.4 {
+ execsql {SELECT offsets(t1) FROM t1 WHERE content MATCH 'three NEAR/2 two'}
+} {{0 1 4 3 0 0 8 5 0 1 14 3 0 0 27 5}}
+do_test fts3near-3.5 {
+ execsql {SELECT offsets(t1) FROM t1 WHERE content MATCH 'two NEAR/2 three'}
+} {{0 0 4 3 0 1 8 5 0 0 14 3 0 1 27 5}}
+do_test fts3near-3.6 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH 'three NEAR/0 "two four"'
+ }
+} {{0 0 8 5 0 1 14 3 0 2 18 4}}
+do_test fts3near-3.7 {
+ execsql {
+ SELECT offsets(t1) FROM t1 WHERE content MATCH '"two four" NEAR/0 three'}
+} {{0 2 8 5 0 0 14 3 0 1 18 4}}
+
+db eval {
+ INSERT INTO t1(content) VALUES('
+ This specification defines Cascading Style Sheets, level 2 (CSS2). CSS2 is a style sheet language that allows authors and users to attach style (e.g., fonts, spacing, and aural cues) to structured documents (e.g., HTML documents and XML applications). By separating the presentation style of documents from the content of documents, CSS2 simplifies Web authoring and site maintenance.
+
+ CSS2 builds on CSS1 (see [CSS1]) and, with very few exceptions, all valid CSS1 style sheets are valid CSS2 style sheets. CSS2 supports media-specific style sheets so that authors may tailor the presentation of their documents to visual browsers, aural devices, printers, braille devices, handheld devices, etc. This specification also supports content positioning, downloadable fonts, table layout, features for internationalization, automatic counters and numbering, and some properties related to user interface.
+ ')
+}
+do_test fts3near-4.1 {
+ execsql {
+ SELECT snippet(t1) FROM t1 WHERE content MATCH 'specification NEAR supports'
+ }
+} {{<b>...</b> devices, handheld devices, etc. This <b>specification</b> also <b>supports</b> content positioning, downloadable fonts, <b>...</b>}}
+
+finish_test
diff --git a/third_party/sqlite/test/func.test b/third_party/sqlite/test/func.test
new file mode 100755
index 0000000..b61593f
--- /dev/null
+++ b/third_party/sqlite/test/func.test
@@ -0,0 +1,1032 @@
+# 2001 September 15
+#
+# 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 file is testing built-in functions.
+#
+# $Id: func.test,v 1.86 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table to work with.
+#
+do_test func-0.0 {
+ execsql {CREATE TABLE tbl1(t1 text)}
+ foreach word {this program is free software} {
+ execsql "INSERT INTO tbl1 VALUES('$word')"
+ }
+ execsql {SELECT t1 FROM tbl1 ORDER BY t1}
+} {free is program software this}
+do_test func-0.1 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(NULL);
+ INSERT INTO t2 VALUES(345);
+ INSERT INTO t2 VALUES(NULL);
+ INSERT INTO t2 VALUES(67890);
+ SELECT * FROM t2;
+ }
+} {1 {} 345 {} 67890}
+
+# Check out the length() function
+#
+do_test func-1.0 {
+ execsql {SELECT length(t1) FROM tbl1 ORDER BY t1}
+} {4 2 7 8 4}
+do_test func-1.1 {
+ set r [catch {execsql {SELECT length(*) FROM tbl1 ORDER BY t1}} msg]
+ lappend r $msg
+} {1 {wrong number of arguments to function length()}}
+do_test func-1.2 {
+ set r [catch {execsql {SELECT length(t1,5) FROM tbl1 ORDER BY t1}} msg]
+ lappend r $msg
+} {1 {wrong number of arguments to function length()}}
+do_test func-1.3 {
+ execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1)
+ ORDER BY length(t1)}
+} {2 1 4 2 7 1 8 1}
+do_test func-1.4 {
+ execsql {SELECT coalesce(length(a),-1) FROM t2}
+} {1 -1 3 -1 5}
+
+# Check out the substr() function
+#
+do_test func-2.0 {
+ execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1}
+} {fr is pr so th}
+do_test func-2.1 {
+ execsql {SELECT substr(t1,2,1) FROM tbl1 ORDER BY t1}
+} {r s r o h}
+do_test func-2.2 {
+ execsql {SELECT substr(t1,3,3) FROM tbl1 ORDER BY t1}
+} {ee {} ogr ftw is}
+do_test func-2.3 {
+ execsql {SELECT substr(t1,-1,1) FROM tbl1 ORDER BY t1}
+} {e s m e s}
+do_test func-2.4 {
+ execsql {SELECT substr(t1,-1,2) FROM tbl1 ORDER BY t1}
+} {e s m e s}
+do_test func-2.5 {
+ execsql {SELECT substr(t1,-2,1) FROM tbl1 ORDER BY t1}
+} {e i a r i}
+do_test func-2.6 {
+ execsql {SELECT substr(t1,-2,2) FROM tbl1 ORDER BY t1}
+} {ee is am re is}
+do_test func-2.7 {
+ execsql {SELECT substr(t1,-4,2) FROM tbl1 ORDER BY t1}
+} {fr {} gr wa th}
+do_test func-2.8 {
+ execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)}
+} {this software free program is}
+do_test func-2.9 {
+ execsql {SELECT substr(a,1,1) FROM t2}
+} {1 {} 3 {} 6}
+do_test func-2.10 {
+ execsql {SELECT substr(a,2,2) FROM t2}
+} {{} {} 45 {} 78}
+
+# Only do the following tests if TCL has UTF-8 capabilities
+#
+if {"\u1234"!="u1234"} {
+
+# Put some UTF-8 characters in the database
+#
+do_test func-3.0 {
+ execsql {DELETE FROM tbl1}
+ foreach word "contains UTF-8 characters hi\u1234ho" {
+ execsql "INSERT INTO tbl1 VALUES('$word')"
+ }
+ execsql {SELECT t1 FROM tbl1 ORDER BY t1}
+} "UTF-8 characters contains hi\u1234ho"
+do_test func-3.1 {
+ execsql {SELECT length(t1) FROM tbl1 ORDER BY t1}
+} {5 10 8 5}
+do_test func-3.2 {
+ execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1}
+} {UT ch co hi}
+do_test func-3.3 {
+ execsql {SELECT substr(t1,1,3) FROM tbl1 ORDER BY t1}
+} "UTF cha con hi\u1234"
+do_test func-3.4 {
+ execsql {SELECT substr(t1,2,2) FROM tbl1 ORDER BY t1}
+} "TF ha on i\u1234"
+do_test func-3.5 {
+ execsql {SELECT substr(t1,2,3) FROM tbl1 ORDER BY t1}
+} "TF- har ont i\u1234h"
+do_test func-3.6 {
+ execsql {SELECT substr(t1,3,2) FROM tbl1 ORDER BY t1}
+} "F- ar nt \u1234h"
+do_test func-3.7 {
+ execsql {SELECT substr(t1,4,2) FROM tbl1 ORDER BY t1}
+} "-8 ra ta ho"
+do_test func-3.8 {
+ execsql {SELECT substr(t1,-1,1) FROM tbl1 ORDER BY t1}
+} "8 s s o"
+do_test func-3.9 {
+ execsql {SELECT substr(t1,-3,2) FROM tbl1 ORDER BY t1}
+} "F- er in \u1234h"
+do_test func-3.10 {
+ execsql {SELECT substr(t1,-4,3) FROM tbl1 ORDER BY t1}
+} "TF- ter ain i\u1234h"
+do_test func-3.99 {
+ execsql {DELETE FROM tbl1}
+ foreach word {this program is free software} {
+ execsql "INSERT INTO tbl1 VALUES('$word')"
+ }
+ execsql {SELECT t1 FROM tbl1}
+} {this program is free software}
+
+} ;# End \u1234!=u1234
+
+# Test the abs() and round() functions.
+#
+do_test func-4.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(2,1.2345678901234,-12345.67890);
+ INSERT INTO t1 VALUES(3,-2,-5);
+ }
+ catchsql {SELECT abs(a,b) FROM t1}
+} {1 {wrong number of arguments to function abs()}}
+do_test func-4.2 {
+ catchsql {SELECT abs() FROM t1}
+} {1 {wrong number of arguments to function abs()}}
+do_test func-4.3 {
+ catchsql {SELECT abs(b) FROM t1 ORDER BY a}
+} {0 {2 1.2345678901234 2}}
+do_test func-4.4 {
+ catchsql {SELECT abs(c) FROM t1 ORDER BY a}
+} {0 {3 12345.6789 5}}
+do_test func-4.4.1 {
+ execsql {SELECT abs(a) FROM t2}
+} {1 {} 345 {} 67890}
+do_test func-4.4.2 {
+ execsql {SELECT abs(t1) FROM tbl1}
+} {0.0 0.0 0.0 0.0 0.0}
+
+do_test func-4.5 {
+ catchsql {SELECT round(a,b,c) FROM t1}
+} {1 {wrong number of arguments to function round()}}
+do_test func-4.6 {
+ catchsql {SELECT round(b,2) FROM t1 ORDER BY b}
+} {0 {-2.0 1.23 2.0}}
+do_test func-4.7 {
+ catchsql {SELECT round(b,0) FROM t1 ORDER BY a}
+} {0 {2.0 1.0 -2.0}}
+do_test func-4.8 {
+ catchsql {SELECT round(c) FROM t1 ORDER BY a}
+} {0 {3.0 -12346.0 -5.0}}
+do_test func-4.9 {
+ catchsql {SELECT round(c,a) FROM t1 ORDER BY a}
+} {0 {3.0 -12345.68 -5.0}}
+do_test func-4.10 {
+ catchsql {SELECT 'x' || round(c,a) || 'y' FROM t1 ORDER BY a}
+} {0 {x3.0y x-12345.68y x-5.0y}}
+do_test func-4.11 {
+ catchsql {SELECT round() FROM t1 ORDER BY a}
+} {1 {wrong number of arguments to function round()}}
+do_test func-4.12 {
+ execsql {SELECT coalesce(round(a,2),'nil') FROM t2}
+} {1.0 nil 345.0 nil 67890.0}
+do_test func-4.13 {
+ execsql {SELECT round(t1,2) FROM tbl1}
+} {0.0 0.0 0.0 0.0 0.0}
+do_test func-4.14 {
+ execsql {SELECT typeof(round(5.1,1));}
+} {real}
+do_test func-4.15 {
+ execsql {SELECT typeof(round(5.1));}
+} {real}
+
+
+# Test the upper() and lower() functions
+#
+do_test func-5.1 {
+ execsql {SELECT upper(t1) FROM tbl1}
+} {THIS PROGRAM IS FREE SOFTWARE}
+do_test func-5.2 {
+ execsql {SELECT lower(upper(t1)) FROM tbl1}
+} {this program is free software}
+do_test func-5.3 {
+ execsql {SELECT upper(a), lower(a) FROM t2}
+} {1 1 {} {} 345 345 {} {} 67890 67890}
+ifcapable !icu {
+ do_test func-5.4 {
+ catchsql {SELECT upper(a,5) FROM t2}
+ } {1 {wrong number of arguments to function upper()}}
+}
+do_test func-5.5 {
+ catchsql {SELECT upper(*) FROM t2}
+} {1 {wrong number of arguments to function upper()}}
+
+# Test the coalesce() and nullif() functions
+#
+do_test func-6.1 {
+ execsql {SELECT coalesce(a,'xyz') FROM t2}
+} {1 xyz 345 xyz 67890}
+do_test func-6.2 {
+ execsql {SELECT coalesce(upper(a),'nil') FROM t2}
+} {1 nil 345 nil 67890}
+do_test func-6.3 {
+ execsql {SELECT coalesce(nullif(1,1),'nil')}
+} {nil}
+do_test func-6.4 {
+ execsql {SELECT coalesce(nullif(1,2),'nil')}
+} {1}
+do_test func-6.5 {
+ execsql {SELECT coalesce(nullif(1,NULL),'nil')}
+} {1}
+
+
+# Test the last_insert_rowid() function
+#
+do_test func-7.1 {
+ execsql {SELECT last_insert_rowid()}
+} [db last_insert_rowid]
+
+# Tests for aggregate functions and how they handle NULLs.
+#
+do_test func-8.1 {
+ ifcapable explain {
+ execsql {EXPLAIN SELECT sum(a) FROM t2;}
+ }
+ execsql {
+ SELECT sum(a), count(a), round(avg(a),2), min(a), max(a), count(*) FROM t2;
+ }
+} {68236 3 22745.33 1 67890 5}
+do_test func-8.2 {
+ execsql {
+ SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t2;
+ }
+} {z+67890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
+
+ifcapable tempdb {
+ do_test func-8.3 {
+ execsql {
+ CREATE TEMP TABLE t3 AS SELECT a FROM t2 ORDER BY a DESC;
+ SELECT min('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3;
+ }
+ } {z+1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
+} else {
+ do_test func-8.3 {
+ execsql {
+ CREATE TABLE t3 AS SELECT a FROM t2 ORDER BY a DESC;
+ SELECT min('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3;
+ }
+ } {z+1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
+}
+do_test func-8.4 {
+ execsql {
+ SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3;
+ }
+} {z+67890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
+ifcapable compound {
+ do_test func-8.5 {
+ execsql {
+ SELECT sum(x) FROM (SELECT '9223372036' || '854775807' AS x
+ UNION ALL SELECT -9223372036854775807)
+ }
+ } {0}
+ do_test func-8.6 {
+ execsql {
+ SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775807' AS x
+ UNION ALL SELECT -9223372036854775807)
+ }
+ } {integer}
+ do_test func-8.7 {
+ execsql {
+ SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775808' AS x
+ UNION ALL SELECT -9223372036854775807)
+ }
+ } {real}
+ do_test func-8.8 {
+ execsql {
+ SELECT sum(x)>0.0 FROM (SELECT '9223372036' || '854775808' AS x
+ UNION ALL SELECT -9223372036850000000)
+ }
+ } {1}
+}
+
+# How do you test the random() function in a meaningful, deterministic way?
+#
+do_test func-9.1 {
+ execsql {
+ SELECT random() is not null;
+ }
+} {1}
+do_test func-9.2 {
+ execsql {
+ SELECT typeof(random());
+ }
+} {integer}
+do_test func-9.3 {
+ execsql {
+ SELECT randomblob(32) is not null;
+ }
+} {1}
+do_test func-9.4 {
+ execsql {
+ SELECT typeof(randomblob(32));
+ }
+} {blob}
+do_test func-9.5 {
+ execsql {
+ SELECT length(randomblob(32)), length(randomblob(-5)),
+ length(randomblob(2000))
+ }
+} {32 1 2000}
+
+# The "hex()" function was added in order to be able to render blobs
+# generated by randomblob(). So this seems like a good place to test
+# hex().
+#
+ifcapable bloblit {
+ do_test func-9.10 {
+ execsql {SELECT hex(x'00112233445566778899aAbBcCdDeEfF')}
+ } {00112233445566778899AABBCCDDEEFF}
+}
+set encoding [db one {PRAGMA encoding}]
+if {$encoding=="UTF-16le"} {
+ do_test func-9.11-utf16le {
+ execsql {SELECT hex(replace('abcdefg','ef','12'))}
+ } {6100620063006400310032006700}
+ do_test func-9.12-utf16le {
+ execsql {SELECT hex(replace('abcdefg','','12'))}
+ } {{}}
+ do_test func-9.13-utf16le {
+ execsql {SELECT hex(replace('aabcdefg','a','aaa'))}
+ } {610061006100610061006100620063006400650066006700}
+} elseif {$encoding=="UTF-8"} {
+ do_test func-9.11-utf8 {
+ execsql {SELECT hex(replace('abcdefg','ef','12'))}
+ } {61626364313267}
+ do_test func-9.12-utf8 {
+ execsql {SELECT hex(replace('abcdefg','','12'))}
+ } {{}}
+ do_test func-9.13-utf8 {
+ execsql {SELECT hex(replace('aabcdefg','a','aaa'))}
+ } {616161616161626364656667}
+}
+
+# Use the "sqlite_register_test_function" TCL command which is part of
+# the text fixture in order to verify correct operation of some of
+# the user-defined SQL function APIs that are not used by the built-in
+# functions.
+#
+set ::DB [sqlite3_connection_pointer db]
+sqlite_register_test_function $::DB testfunc
+do_test func-10.1 {
+ catchsql {
+ SELECT testfunc(NULL,NULL);
+ }
+} {1 {first argument should be one of: int int64 string double null value}}
+do_test func-10.2 {
+ execsql {
+ SELECT testfunc(
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'int', 1234
+ );
+ }
+} {1234}
+do_test func-10.3 {
+ execsql {
+ SELECT testfunc(
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'string', NULL
+ );
+ }
+} {{}}
+do_test func-10.4 {
+ execsql {
+ SELECT testfunc(
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'double', 1.234
+ );
+ }
+} {1.234}
+do_test func-10.5 {
+ execsql {
+ SELECT testfunc(
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'int', 1234,
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'string', NULL,
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'double', 1.234,
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'int', 1234,
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'string', NULL,
+ 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'double', 1.234
+ );
+ }
+} {1.234}
+
+# Test the built-in sqlite_version(*) SQL function.
+#
+do_test func-11.1 {
+ execsql {
+ SELECT sqlite_version(*);
+ }
+} [sqlite3 -version]
+
+# Test that destructors passed to sqlite3 by calls to sqlite3_result_text()
+# etc. are called. These tests use two special user-defined functions
+# (implemented in func.c) only available in test builds.
+#
+# Function test_destructor() takes one argument and returns a copy of the
+# text form of that argument. A destructor is associated with the return
+# value. Function test_destructor_count() returns the number of outstanding
+# destructor calls for values returned by test_destructor().
+#
+if {[db eval {PRAGMA encoding}]=="UTF-8"} {
+ do_test func-12.1-utf8 {
+ execsql {
+ SELECT test_destructor('hello world'), test_destructor_count();
+ }
+ } {{hello world} 1}
+} else {
+ ifcapable {utf16} {
+ do_test func-12.1-utf16 {
+ execsql {
+ SELECT test_destructor16('hello world'), test_destructor_count();
+ }
+ } {{hello world} 1}
+ }
+}
+do_test func-12.2 {
+ execsql {
+ SELECT test_destructor_count();
+ }
+} {0}
+do_test func-12.3 {
+ execsql {
+ SELECT test_destructor('hello')||' world'
+ }
+} {{hello world}}
+do_test func-12.4 {
+ execsql {
+ SELECT test_destructor_count();
+ }
+} {0}
+do_test func-12.5 {
+ execsql {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES(test_destructor('hello'));
+ INSERT INTO t4 VALUES(test_destructor('world'));
+ SELECT min(test_destructor(x)), max(test_destructor(x)) FROM t4;
+ }
+} {hello world}
+do_test func-12.6 {
+ execsql {
+ SELECT test_destructor_count();
+ }
+} {0}
+do_test func-12.7 {
+ execsql {
+ DROP TABLE t4;
+ }
+} {}
+
+
+# Test that the auxdata API for scalar functions works. This test uses
+# a special user-defined function only available in test builds,
+# test_auxdata(). Function test_auxdata() takes any number of arguments.
+do_test func-13.1 {
+ execsql {
+ SELECT test_auxdata('hello world');
+ }
+} {0}
+
+do_test func-13.2 {
+ execsql {
+ CREATE TABLE t4(a, b);
+ INSERT INTO t4 VALUES('abc', 'def');
+ INSERT INTO t4 VALUES('ghi', 'jkl');
+ }
+} {}
+do_test func-13.3 {
+ execsql {
+ SELECT test_auxdata('hello world') FROM t4;
+ }
+} {0 1}
+do_test func-13.4 {
+ execsql {
+ SELECT test_auxdata('hello world', 123) FROM t4;
+ }
+} {{0 0} {1 1}}
+do_test func-13.5 {
+ execsql {
+ SELECT test_auxdata('hello world', a) FROM t4;
+ }
+} {{0 0} {1 0}}
+do_test func-13.6 {
+ execsql {
+ SELECT test_auxdata('hello'||'world', a) FROM t4;
+ }
+} {{0 0} {1 0}}
+
+# Test that auxilary data is preserved between calls for SQL variables.
+do_test func-13.7 {
+ set DB [sqlite3_connection_pointer db]
+ set sql "SELECT test_auxdata( ? , a ) FROM t4;"
+ set STMT [sqlite3_prepare $DB $sql -1 TAIL]
+ sqlite3_bind_text $STMT 1 hello\000 -1
+ set res [list]
+ while { "SQLITE_ROW"==[sqlite3_step $STMT] } {
+ lappend res [sqlite3_column_text $STMT 0]
+ }
+ lappend res [sqlite3_finalize $STMT]
+} {{0 0} {1 0} SQLITE_OK}
+
+# Make sure that a function with a very long name is rejected
+do_test func-14.1 {
+ catch {
+ db function [string repeat X 254] {return "hello"}
+ }
+} {0}
+do_test func-14.2 {
+ catch {
+ db function [string repeat X 256] {return "hello"}
+ }
+} {1}
+
+do_test func-15.1 {
+ catchsql {select test_error(NULL)}
+} {1 {}}
+do_test func-15.2 {
+ catchsql {select test_error('this is the error message')}
+} {1 {this is the error message}}
+do_test func-15.3 {
+ catchsql {select test_error('this is the error message',12)}
+} {1 {this is the error message}}
+do_test func-15.4 {
+ db errorcode
+} {12}
+
+# Test the quote function for BLOB and NULL values.
+do_test func-16.1 {
+ execsql {
+ CREATE TABLE tbl2(a, b);
+ }
+ set STMT [sqlite3_prepare $::DB "INSERT INTO tbl2 VALUES(?, ?)" -1 TAIL]
+ sqlite3_bind_blob $::STMT 1 abc 3
+ sqlite3_step $::STMT
+ sqlite3_finalize $::STMT
+ execsql {
+ SELECT quote(a), quote(b) FROM tbl2;
+ }
+} {X'616263' NULL}
+
+# Correctly handle function error messages that include %. Ticket #1354
+#
+do_test func-17.1 {
+ proc testfunc1 args {error "Error %d with %s percents %p"}
+ db function testfunc1 ::testfunc1
+ catchsql {
+ SELECT testfunc1(1,2,3);
+ }
+} {1 {Error %d with %s percents %p}}
+
+# The SUM function should return integer results when all inputs are integer.
+#
+do_test func-18.1 {
+ execsql {
+ CREATE TABLE t5(x);
+ INSERT INTO t5 VALUES(1);
+ INSERT INTO t5 VALUES(-99);
+ INSERT INTO t5 VALUES(10000);
+ SELECT sum(x) FROM t5;
+ }
+} {9902}
+do_test func-18.2 {
+ execsql {
+ INSERT INTO t5 VALUES(0.0);
+ SELECT sum(x) FROM t5;
+ }
+} {9902.0}
+
+# The sum of nothing is NULL. But the sum of all NULLs is NULL.
+#
+# The TOTAL of nothing is 0.0.
+#
+do_test func-18.3 {
+ execsql {
+ DELETE FROM t5;
+ SELECT sum(x), total(x) FROM t5;
+ }
+} {{} 0.0}
+do_test func-18.4 {
+ execsql {
+ INSERT INTO t5 VALUES(NULL);
+ SELECT sum(x), total(x) FROM t5
+ }
+} {{} 0.0}
+do_test func-18.5 {
+ execsql {
+ INSERT INTO t5 VALUES(NULL);
+ SELECT sum(x), total(x) FROM t5
+ }
+} {{} 0.0}
+do_test func-18.6 {
+ execsql {
+ INSERT INTO t5 VALUES(123);
+ SELECT sum(x), total(x) FROM t5
+ }
+} {123 123.0}
+
+# Ticket #1664, #1669, #1670, #1674: An integer overflow on SUM causes
+# an error. The non-standard TOTAL() function continues to give a helpful
+# result.
+#
+do_test func-18.10 {
+ execsql {
+ CREATE TABLE t6(x INTEGER);
+ INSERT INTO t6 VALUES(1);
+ INSERT INTO t6 VALUES(1<<62);
+ SELECT sum(x) - ((1<<62)+1) from t6;
+ }
+} 0
+do_test func-18.11 {
+ execsql {
+ SELECT typeof(sum(x)) FROM t6
+ }
+} integer
+do_test func-18.12 {
+ catchsql {
+ INSERT INTO t6 VALUES(1<<62);
+ SELECT sum(x) - ((1<<62)*2.0+1) from t6;
+ }
+} {1 {integer overflow}}
+do_test func-18.13 {
+ execsql {
+ SELECT total(x) - ((1<<62)*2.0+1) FROM t6
+ }
+} 0.0
+do_test func-18.14 {
+ execsql {
+ SELECT sum(-9223372036854775805);
+ }
+} -9223372036854775805
+
+ifcapable compound&&subquery {
+
+do_test func-18.15 {
+ catchsql {
+ SELECT sum(x) FROM
+ (SELECT 9223372036854775807 AS x UNION ALL
+ SELECT 10 AS x);
+ }
+} {1 {integer overflow}}
+do_test func-18.16 {
+ catchsql {
+ SELECT sum(x) FROM
+ (SELECT 9223372036854775807 AS x UNION ALL
+ SELECT -10 AS x);
+ }
+} {0 9223372036854775797}
+do_test func-18.17 {
+ catchsql {
+ SELECT sum(x) FROM
+ (SELECT -9223372036854775807 AS x UNION ALL
+ SELECT 10 AS x);
+ }
+} {0 -9223372036854775797}
+do_test func-18.18 {
+ catchsql {
+ SELECT sum(x) FROM
+ (SELECT -9223372036854775807 AS x UNION ALL
+ SELECT -10 AS x);
+ }
+} {1 {integer overflow}}
+do_test func-18.19 {
+ catchsql {
+ SELECT sum(x) FROM (SELECT 9 AS x UNION ALL SELECT -10 AS x);
+ }
+} {0 -1}
+do_test func-18.20 {
+ catchsql {
+ SELECT sum(x) FROM (SELECT -9 AS x UNION ALL SELECT 10 AS x);
+ }
+} {0 1}
+do_test func-18.21 {
+ catchsql {
+ SELECT sum(x) FROM (SELECT -10 AS x UNION ALL SELECT 9 AS x);
+ }
+} {0 -1}
+do_test func-18.22 {
+ catchsql {
+ SELECT sum(x) FROM (SELECT 10 AS x UNION ALL SELECT -9 AS x);
+ }
+} {0 1}
+
+} ;# ifcapable compound&&subquery
+
+# Integer overflow on abs()
+#
+do_test func-18.31 {
+ catchsql {
+ SELECT abs(-9223372036854775807);
+ }
+} {0 9223372036854775807}
+do_test func-18.32 {
+ catchsql {
+ SELECT abs(-9223372036854775807-1);
+ }
+} {1 {integer overflow}}
+
+# The MATCH function exists but is only a stub and always throws an error.
+#
+do_test func-19.1 {
+ execsql {
+ SELECT match(a,b) FROM t1 WHERE 0;
+ }
+} {}
+do_test func-19.2 {
+ catchsql {
+ SELECT 'abc' MATCH 'xyz';
+ }
+} {1 {unable to use function MATCH in the requested context}}
+do_test func-19.3 {
+ catchsql {
+ SELECT 'abc' NOT MATCH 'xyz';
+ }
+} {1 {unable to use function MATCH in the requested context}}
+do_test func-19.4 {
+ catchsql {
+ SELECT match(1,2,3);
+ }
+} {1 {wrong number of arguments to function match()}}
+
+# Soundex tests.
+#
+if {![catch {db eval {SELECT soundex('hello')}}]} {
+ set i 0
+ foreach {name sdx} {
+ euler E460
+ EULER E460
+ Euler E460
+ ellery E460
+ gauss G200
+ ghosh G200
+ hilbert H416
+ Heilbronn H416
+ knuth K530
+ kant K530
+ Lloyd L300
+ LADD L300
+ Lukasiewicz L222
+ Lissajous L222
+ A A000
+ 12345 ?000
+ } {
+ incr i
+ do_test func-20.$i {
+ execsql {SELECT soundex($name)}
+ } $sdx
+ }
+}
+
+# Tests of the REPLACE function.
+#
+do_test func-21.1 {
+ catchsql {
+ SELECT replace(1,2);
+ }
+} {1 {wrong number of arguments to function replace()}}
+do_test func-21.2 {
+ catchsql {
+ SELECT replace(1,2,3,4);
+ }
+} {1 {wrong number of arguments to function replace()}}
+do_test func-21.3 {
+ execsql {
+ SELECT typeof(replace("This is the main test string", NULL, "ALT"));
+ }
+} {null}
+do_test func-21.4 {
+ execsql {
+ SELECT typeof(replace(NULL, "main", "ALT"));
+ }
+} {null}
+do_test func-21.5 {
+ execsql {
+ SELECT typeof(replace("This is the main test string", "main", NULL));
+ }
+} {null}
+do_test func-21.6 {
+ execsql {
+ SELECT replace("This is the main test string", "main", "ALT");
+ }
+} {{This is the ALT test string}}
+do_test func-21.7 {
+ execsql {
+ SELECT replace("This is the main test string", "main", "larger-main");
+ }
+} {{This is the larger-main test string}}
+do_test func-21.8 {
+ execsql {
+ SELECT replace("aaaaaaa", "a", "0123456789");
+ }
+} {0123456789012345678901234567890123456789012345678901234567890123456789}
+
+ifcapable tclvar {
+ do_test func-21.9 {
+ # Attempt to exploit a buffer-overflow that at one time existed
+ # in the REPLACE function.
+ set ::str "[string repeat A 29998]CC[string repeat A 35537]"
+ set ::rep [string repeat B 65536]
+ execsql {
+ SELECT LENGTH(REPLACE($::str, 'C', $::rep));
+ }
+ } [expr 29998 + 2*65536 + 35537]
+}
+
+# Tests for the TRIM, LTRIM and RTRIM functions.
+#
+do_test func-22.1 {
+ catchsql {SELECT trim(1,2,3)}
+} {1 {wrong number of arguments to function trim()}}
+do_test func-22.2 {
+ catchsql {SELECT ltrim(1,2,3)}
+} {1 {wrong number of arguments to function ltrim()}}
+do_test func-22.3 {
+ catchsql {SELECT rtrim(1,2,3)}
+} {1 {wrong number of arguments to function rtrim()}}
+do_test func-22.4 {
+ execsql {SELECT trim(' hi ');}
+} {hi}
+do_test func-22.5 {
+ execsql {SELECT ltrim(' hi ');}
+} {{hi }}
+do_test func-22.6 {
+ execsql {SELECT rtrim(' hi ');}
+} {{ hi}}
+do_test func-22.7 {
+ execsql {SELECT trim(' hi ','xyz');}
+} {{ hi }}
+do_test func-22.8 {
+ execsql {SELECT ltrim(' hi ','xyz');}
+} {{ hi }}
+do_test func-22.9 {
+ execsql {SELECT rtrim(' hi ','xyz');}
+} {{ hi }}
+do_test func-22.10 {
+ execsql {SELECT trim('xyxzy hi zzzy','xyz');}
+} {{ hi }}
+do_test func-22.11 {
+ execsql {SELECT ltrim('xyxzy hi zzzy','xyz');}
+} {{ hi zzzy}}
+do_test func-22.12 {
+ execsql {SELECT rtrim('xyxzy hi zzzy','xyz');}
+} {{xyxzy hi }}
+do_test func-22.13 {
+ execsql {SELECT trim(' hi ','');}
+} {{ hi }}
+if {[db one {PRAGMA encoding}]=="UTF-8"} {
+ do_test func-22.14 {
+ execsql {SELECT hex(trim(x'c280e1bfbff48fbfbf6869',x'6162e1bfbfc280'))}
+ } {F48FBFBF6869}
+ do_test func-22.15 {
+ execsql {SELECT hex(trim(x'6869c280e1bfbff48fbfbf61',
+ x'6162e1bfbfc280f48fbfbf'))}
+ } {6869}
+ do_test func-22.16 {
+ execsql {SELECT hex(trim(x'ceb1ceb2ceb3',x'ceb1'));}
+ } {CEB2CEB3}
+}
+do_test func-22.20 {
+ execsql {SELECT typeof(trim(NULL));}
+} {null}
+do_test func-22.21 {
+ execsql {SELECT typeof(trim(NULL,'xyz'));}
+} {null}
+do_test func-22.22 {
+ execsql {SELECT typeof(trim('hello',NULL));}
+} {null}
+
+# This is to test the deprecated sqlite3_aggregate_count() API.
+#
+do_test func-23.1 {
+ sqlite3_create_aggregate db
+ execsql {
+ SELECT legacy_count() FROM t6;
+ }
+} {3}
+
+# The group_concat() function.
+#
+do_test func-24.1 {
+ execsql {
+ SELECT group_concat(t1) FROM tbl1
+ }
+} {this,program,is,free,software}
+do_test func-24.2 {
+ execsql {
+ SELECT group_concat(t1,' ') FROM tbl1
+ }
+} {{this program is free software}}
+do_test func-24.3 {
+ execsql {
+ SELECT group_concat(t1,' ' || rowid || ' ') FROM tbl1
+ }
+} {{this 2 program 3 is 4 free 5 software}}
+do_test func-24.4 {
+ execsql {
+ SELECT group_concat(NULL,t1) FROM tbl1
+ }
+} {{}}
+do_test func-24.5 {
+ execsql {
+ SELECT group_concat(t1,NULL) FROM tbl1
+ }
+} {thisprogramisfreesoftware}
+do_test func-24.6 {
+ execsql {
+ SELECT 'BEGIN-'||group_concat(t1) FROM tbl1
+ }
+} {BEGIN-this,program,is,free,software}
+unset -nocomplain midargs
+set midargs {}
+unset -nocomplain midres
+set midres {}
+unset -nocomplain result
+for {set i 1} {$i<[sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1]-1} {incr i} {
+ append midargs ,'/$i'
+ append midres /$i
+ set result \
+ "this$midres:program$midres:is$midres:free$midres:software$midres"
+ set sql "SELECT group_concat(t1$midargs,':') FROM tbl1"
+ do_test func-24.7.$i {
+ db eval $::sql
+ } $result
+}
+
+# Use the test_isolation function to make sure that type conversions
+# on function arguments do not effect subsequent arguments.
+#
+do_test func-25.1 {
+ execsql {SELECT test_isolation(t1,t1) FROM tbl1}
+} {this program is free software}
+
+# Try to misuse the sqlite3_create_function() interface. Verify that
+# errors are returned.
+#
+do_test func-26.1 {
+ abuse_create_function db
+} {}
+
+# The previous test (func-26.1) registered a function with a very long
+# function name that takes many arguments and always returns NULL. Verify
+# that this function works correctly.
+#
+do_test func-26.2 {
+ set a {}
+ for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG} {incr i} {
+ lappend a $i
+ }
+ db eval "
+ SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]);
+ "
+} {{}}
+do_test func-26.3 {
+ set a {}
+ for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG+1} {incr i} {
+ lappend a $i
+ }
+ catchsql "
+ SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]);
+ "
+} {1 {too many arguments on function nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789}}
+do_test func-26.4 {
+ set a {}
+ for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG-1} {incr i} {
+ lappend a $i
+ }
+ catchsql "
+ SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]);
+ "
+} {1 {wrong number of arguments to function nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789()}}
+do_test func-26.5 {
+ catchsql "
+ SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678a(0);
+ "
+} {1 {no such function: nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678a}}
+do_test func-26.6 {
+ catchsql "
+ SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789a(0);
+ "
+} {1 {no such function: nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789a}}
+
+finish_test
diff --git a/third_party/sqlite/test/fuzz.test b/third_party/sqlite/test/fuzz.test
new file mode 100755
index 0000000..76e3aad
--- /dev/null
+++ b/third_party/sqlite/test/fuzz.test
@@ -0,0 +1,251 @@
+# 2007 May 10
+#
+# 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 file is generating semi-random strings of SQL
+# (a.k.a. "fuzz") and sending it into the parser to try to
+# generate errors.
+#
+# The tests in this file are really about testing fuzzily generated
+# SQL parse-trees. The majority of the fuzzily generated SQL is
+# valid as far as the parser is concerned.
+#
+# The most complicated trees are for SELECT statements.
+#
+# $Id: fuzz.test,v 1.14 2007/05/30 10:36:47 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set ::REPEATS 5000
+
+# If running quick.test, don't do so many iterations.
+if {[info exists ::ISQUICK]} {
+ if {$::ISQUICK} { set ::REPEATS 20 }
+}
+
+source $testdir/fuzz_common.tcl
+
+#----------------------------------------------------------------
+# These tests caused errors that were first caught by the tests
+# in this file. They are still here.
+do_test fuzz-1.1 {
+ execsql {
+ SELECT 'abc' LIKE X'ABCD';
+ }
+} {0}
+do_test fuzz-1.2 {
+ execsql {
+ SELECT 'abc' LIKE zeroblob(10);
+ }
+} {0}
+do_test fuzz-1.3 {
+ execsql {
+ SELECT zeroblob(10) LIKE 'abc';
+ }
+} {0}
+do_test fuzz-1.4 {
+ execsql {
+ SELECT (- -21) % NOT (456 LIKE zeroblob(10));
+ }
+} {0}
+do_test fuzz-1.5 {
+ execsql {
+ SELECT (SELECT (
+ SELECT (SELECT -2147483648) FROM (SELECT 1) ORDER BY 1
+ ))
+ }
+} {-2147483648}
+do_test fuzz-1.6 {
+ execsql {
+ SELECT 'abc', zeroblob(1) FROM (SELECT 1) ORDER BY 1
+ }
+} [execsql {SELECT 'abc', zeroblob(1)}]
+
+do_test fuzz-1.7 {
+ execsql {
+ SELECT ( SELECT zeroblob(1000) FROM (
+ SELECT * FROM (SELECT 'first') ORDER BY NOT 'in')
+ )
+ }
+} [execsql {SELECT zeroblob(1000)}]
+
+do_test fuzz-1.8 {
+ # Problems with opcode OP_ToText (did not account for MEM_Zero).
+ # Also MemExpandBlob() was marking expanded blobs as nul-terminated.
+ # They are not.
+ execsql {
+ SELECT CAST(zeroblob(1000) AS text);
+ }
+} {{}}
+
+do_test fuzz-1.9 {
+ # This was causing a NULL pointer dereference of Expr.pList.
+ execsql {
+ SELECT 1 FROM (SELECT * FROM sqlite_master WHERE random())
+ }
+} {}
+
+do_test fuzz-1.10 {
+ # Bug in calculation of Parse.ckOffset causing an assert()
+ # to fail. Probably harmless.
+ execsql {
+ SELECT coalesce(1, substr( 1, 2, length('in' IN (SELECT 1))))
+ }
+} {1}
+
+do_test fuzz-1.11 {
+ # The literals (A, B, C, D) are not important, they are just used
+ # to make the EXPLAIN output easier to read.
+ #
+ # The problem here is that the EXISTS(...) expression leaves an
+ # extra value on the VDBE stack. This is confusing the parent and
+ # leads to an assert() failure when OP_Insert encounters an integer
+ # when it expects a record blob.
+ #
+ # Update: Any query with (LIMIT 0) was leaking stack.
+ #
+ execsql {
+ SELECT 'A' FROM (SELECT 'B') ORDER BY EXISTS (
+ SELECT 'C' FROM (SELECT 'D' LIMIT 0)
+ )
+ }
+} {A}
+
+do_test fuzz-1.12.1 {
+ # Create a table with a single row.
+ execsql {
+ CREATE TABLE abc(b);
+ INSERT INTO abc VALUES('ABCDE');
+ }
+
+ # The following query was crashing. The later subquery (in the FROM)
+ # clause was flattened into the parent, but the code was not repairng
+ # the "b" reference in the other sub-query. When the query was executed,
+ # that "b" refered to a non-existant vdbe table-cursor.
+ #
+ execsql {
+ SELECT 1 IN ( SELECT b UNION SELECT 1 ) FROM (SELECT b FROM abc);
+ }
+} {1}
+do_test fuzz-1.12.2 {
+ # Clean up after the previous query.
+ execsql {
+ DROP TABLE abc;
+ }
+} {}
+
+
+do_test fuzz-1.13 {
+ # The problem here was that when there were more expressions in
+ # the ORDER BY list than the result-set list. The temporary b-tree
+ # used for sorting was being misconfigured in this case.
+ #
+ execsql {
+ SELECT 'abcd' UNION SELECT 'efgh' ORDER BY 1 ASC, 1 ASC;
+ }
+} {abcd efgh}
+
+do_test fuzz-1.14.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(123, 456, 789);
+ }
+
+ # The [a] reference in the sub-select was causing a problem. Because
+ # the internal walkSelectExpr() function was not considering compound
+ # SELECT operators.
+ execsql {
+ SELECT 1 FROM abc
+ GROUP BY c HAVING EXISTS (SELECT a UNION SELECT 123);
+ }
+} {1}
+do_test fuzz-1.14.2 {
+ execsql {
+ DROP TABLE abc;
+ }
+} {}
+
+#----------------------------------------------------------------
+# Test some fuzzily generated expressions.
+#
+do_fuzzy_test fuzz-2 -template { SELECT [Expr] }
+
+do_test fuzz-3.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE def(a, b, c);
+ CREATE TABLE ghi(a, b, c);
+ }
+} {}
+set ::TableList [list abc def ghi]
+
+#----------------------------------------------------------------
+# Test some fuzzily generated SELECT statements.
+#
+do_fuzzy_test fuzz-3.2 -template {[Select]}
+
+#----------------------------------------------------------------
+# Insert a small amount of data into the database and then run
+# some more generated SELECT statements.
+#
+do_test fuzz-4.1 {
+ execsql {
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ INSERT INTO abc VALUES(7, 8, 9);
+ INSERT INTO def VALUES(1, 2, 3);
+ INSERT INTO def VALUES(4, 5, 6);
+ INSERT INTO def VALUES(7, 8, 9);
+ INSERT INTO ghi VALUES(1, 2, 3);
+ INSERT INTO ghi VALUES(4, 5, 6);
+ INSERT INTO ghi VALUES(7, 8, 9);
+ CREATE INDEX abc_i ON abc(a, b, c);
+ CREATE INDEX def_i ON def(c, a, b);
+ CREATE INDEX ghi_i ON ghi(b, c, a);
+ }
+} {}
+do_fuzzy_test fuzz-4.2 -template {[Select]}
+
+#----------------------------------------------------------------
+# Test some fuzzy INSERT statements:
+#
+do_test fuzz-5.1 {execsql BEGIN} {}
+do_fuzzy_test fuzz-5.2 -template {[Insert]} -errorlist table
+integrity_check fuzz-5.2.integrity
+do_test fuzz-5.3 {execsql COMMIT} {}
+integrity_check fuzz-5.4.integrity
+
+#----------------------------------------------------------------
+# Now that there is data in the database, run some more SELECT
+# statements
+#
+set ::ColumnList [list a b c]
+set E {{no such col} {ambiguous column name}}
+do_fuzzy_test fuzz-6.1 -template {[Select]} -errorlist $E
+
+#----------------------------------------------------------------
+# Run some SELECTs, INSERTs, UPDATEs and DELETEs in a transaction.
+#
+set E {{no such col} {ambiguous column name} {table}}
+do_test fuzz-7.1 {execsql BEGIN} {}
+do_fuzzy_test fuzz-7.2 -template {[Statement]} -errorlist $E
+integrity_check fuzz-7.3.integrity
+do_test fuzz-7.4 {execsql COMMIT} {}
+integrity_check fuzz-7.5.integrity
+
+#----------------------------------------------------------------
+# Many CREATE and DROP TABLE statements:
+#
+set E [list table duplicate {no such col} {ambiguous column name} {use DROP}]
+do_fuzzy_test fuzz-8.1 -template {[CreateOrDropTableOrView]} -errorlist $E
+
+close $::log
+finish_test
diff --git a/third_party/sqlite/test/fuzz2.test b/third_party/sqlite/test/fuzz2.test
new file mode 100755
index 0000000..eb5eb83
--- /dev/null
+++ b/third_party/sqlite/test/fuzz2.test
@@ -0,0 +1,102 @@
+# 2007 May 10
+#
+# 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.
+#
+# This file checks error recovery from malformed SQL strings.
+#
+# $Id: fuzz2.test,v 1.3 2007/05/15 16:51:37 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+proc fuzzcatch {sql} {
+ return [lindex [catchsql $sql] 0]
+}
+
+do_test fuzz2-1.1 {
+ fuzzcatch {SELECT ALL "AAAAAA" . * GROUP BY LIMIT round(1), #12}
+} {1}
+do_test fuzz2-2.0 {
+ fuzzcatch {SELECT + #100}
+} {1}
+do_test fuzz2-2.1 {
+ fuzzcatch {SELECT 1 WHERE ( #61 NOT MATCH ROUND( 1 ) )}
+} {1}
+do_test fuzz2-2.2 {
+ fuzzcatch {SELECT 1 LIMIT NOT #59 COLLATE AAAAAA NOT IN
+ ( "AAAAAA" NOTNULL <= x'414141414141' IS NULL , ( ROUND ( 1.0 ) ) )}
+} {1}
+do_test fuzz2-2.3 {
+ fuzzcatch {INSERT OR REPLACE INTO AAAAAA . "AAAAAA" ( "AAAAAA" ) SELECT DISTINCT * , ( SELECT #252 IN ( SELECT DISTINCT AAAAAA . * ) )}
+} {1}
+do_test fuzz2-2.4 {
+ fuzzcatch {SELECT 1 LIMIT NOT #59 COLLATE AAAAAA NOT IN round(1.0)}
+} {1}
+do_test fuzz2-2.5 {
+ fuzzcatch {SELECT( #239 )}
+} {1}
+do_test fuzz2-2.6 {
+ fuzzcatch {DELETE FROM AAAAAA WHERE #65 NOT NULL}
+} {1}
+do_test fuzz2-2.7 {
+ fuzzcatch {ATTACH ROUND( 1.0 ) in AAAAAA . "AAAAAA" AS #122 ISNULL}
+} {1}
+do_test fuzz2-2.8 {
+ fuzzcatch {SELECT 1 LIMIT #122 ISNULL}
+} {1}
+do_test fuzz2-2.9 {
+ fuzzcatch {CREATE VIEW AAAAAA . "AAAAAA" AS SELECT DISTINCT #162 IS NULL "AAAAAA"}
+} {1}
+do_test fuzz2-2.10 {
+ fuzzcatch {DELETE FROM AAAAAA WHERE #202 IS NOT NULL ISNULL}
+} {1}
+do_test fuzz2-2.11 {
+ fuzzcatch {UPDATE OR IGNORE "AAAAAA" . "AAAAAA" SET "AAAAAA" = NOT #96}
+} {1}
+do_test fuzz2-2.12 {
+ fuzzcatch {SELECT - #196}
+} {1}
+do_test fuzz2-3.0 {
+ fuzzcatch {CREATE TRIGGER "AAAAAA" . "AAAAAA" AFTER UPDATE OF "AAAAAA" , "AAAAAA" ON "AAAAAA" . "AAAAAA" FOR EACH ROW BEGIN UPDATE AAAAAA SET "AAAAAA" = #162; END}
+} {1}
+do_test fuzz2-3.1 {
+ fuzzcatch {CREATE TRIGGER IF NOT EXISTS "AAAAAA" UPDATE ON "AAAAAA" . AAAAAA FOR EACH ROW BEGIN DELETE FROM "AAAAAA" ; INSERT INTO AAAAAA ( "AAAAAA" ) SELECT DISTINCT "AAAAAA" "AAAAAA" , #167 AAAAAA , "AAAAAA" . * ORDER BY "AAAAAA" ASC , x'414141414141' BETWEEN RAISE ( FAIL , "AAAAAA" ) AND AAAAAA ( * ) NOT NULL DESC LIMIT AAAAAA ; REPLACE INTO AAAAAA ( AAAAAA ) VALUES ( AAAAAA ( * ) ) ; END}
+} {1}
+do_test fuzz2-3.2 {
+ fuzzcatch {CREATE TEMP TRIGGER IF NOT EXISTS AAAAAA . "AAAAAA" BEFORE UPDATE OF "AAAAAA" ON AAAAAA . "AAAAAA" BEGIN SELECT ALL * , #175 "AAAAAA" FROM "AAAAAA" . AAAAAA; END}
+} {1}
+do_test fuzz2-4.0 {
+ fuzzcatch {ATTACH DATABASE #168 AS whatever}
+} {1}
+do_test fuzz2-4.1 {
+ fuzzcatch {DETACH #133}
+} {1}
+do_test fuzz2-5.0 {
+ fuzzcatch {SELECT 1 LIMIT ( SELECT DISTINCT * , AAAAAA , * , AAAAAA , "AAAAAA" . * FROM "AAAAAA" ON ROUND( 1 ) COLLATE AAAAAA OR "AAAAAA" USING ( AAAAAA , "AAAAAA" ) WHERE ROUND( 1 ) GROUP BY ORDER BY #84 ASC , #44 DESC , ( SELECT "AAAAAA" . * , "AAAAAA" . * FROM , ( ) "AAAAAA" USING ( )}
+} {1}
+do_test fuzz2-5.1 {
+ fuzzcatch {SELECT 1 WHERE 1 == AAAAAA ( * ) BETWEEN + - ~ + "AAAAAA" . AAAAAA | RAISE ( IGNORE ) COLLATE AAAAAA NOT IN ( SELECT DISTINCT "AAAAAA" . * , * , * WHERE ( SELECT ALL AAAAAA AS "AAAAAA" HAVING CAST ( "AAAAAA" . "AAAAAA" . "AAAAAA" AS AAAAAA ) ORDER BY , , IS NULL ASC , ~ AND DESC LIMIT ( ( "AAAAAA" ) NOT BETWEEN ( ) NOT IN ( ) AND AAAAAA ( ) IS NOT NULL ) OFFSET AAAAAA ( ALL , , ) ) GROUP BY ORDER BY "AAAAAA" . AAAAAA ASC , NULL IN ( SELECT UNION ALL SELECT ALL WHERE HAVING ORDER BY LIMIT UNION SELECT DISTINCT FROM ( ) WHERE + HAVING >> ORDER BY LIMIT . . , "AAAAAA" ) , CAST ( ~ "AAAAAA" . AAAAAA AS "AAAAAA" AAAAAA "AAAAAA" ( + 4294967295 , - 4294967296.0 ) ) ASC LIMIT AAAAAA INTERSECT SELECT ALL * GROUP BY , AAAAAA ( DISTINCT , ) != #241 NOT IN ( , , ) , , CTIME_KW HAVING AAAAAA ORDER BY #103 DESC , #81 ASC LIMIT AAAAAA OFFSET ~ AAAAAA ( ALL AAAAAA . AAAAAA >= AAAAAA . "AAAAAA" . "AAAAAA" ) ) NOTNULL NOT NULL}
+} {1}
+do_test fuzz2-5.2 {
+ fuzzcatch {SELECT 1 WHERE 1 == AAAAAA ( * ) BETWEEN + - ~ + "AAAAAA" . AAAAAA | RAISE ( IGNORE ) COLLATE AAAAAA NOT IN ( SELECT DISTINCT "AAAAAA" . * , * , * WHERE ( SELECT ALL AAAAAA AS "AAAAAA" HAVING CAST ( "AAAAAA" . "AAAAAA" . "AAAAAA" AS AAAAAA ) ORDER BY , , IS NULL ASC , ~ AND DESC LIMIT ( ( "AAAAAA" ) NOT BETWEEN ( ) NOT IN ( ) AND AAAAAA ( ) IS NOT NULL ) OFFSET AAAAAA ( ALL , , ) ) GROUP BY ORDER BY "AAAAAA" . AAAAAA ASC , NULL IN ( SELECT UNION ALL SELECT ALL WHERE HAVING ORDER BY LIMIT UNION SELECT DISTINCT FROM ( ) WHERE + HAVING >> ORDER BY LIMIT . . , "AAAAAA" ) , CAST ( ~ "AAAAAA" . AAAAAA AS "AAAAAA" AAAAAA "AAAAAA" ( + 4294967295 , - 4294967296.0 ) ) ASC LIMIT AAAAAA INTERSECT SELECT ALL * GROUP BY , AAAAAA ( DISTINCT , ) != #241 NOT IN ( , , ) , , CTIME_KW HAVING AAAAAA ORDER BY #103 DESC , #81 ASC LIMIT AAAAAA OFFSET ~ AAAAAA ( ALL AAAAAA . AAAAAA >= AAAAAA . "AAAAAA" . "AAAAAA" ) ) NOTNULL NOT NULL}
+} {1}
+do_test fuzz2-5.3 {
+ fuzzcatch {UPDATE "AAAAAA" SET "AAAAAA" = - EXISTS ( SELECT DISTINCT * , * ORDER BY #202 ASC , #147 , ~ AAAAAA . "AAAAAA" ASC LIMIT AAAAAA . "AAAAAA" , RAISE ( ABORT , AAAAAA ) UNION ALL SELECT DISTINCT AAAAAA . * , * FROM ( SELECT DISTINCT}
+} {1}
+do_test fuzz2-5.4 {
+ fuzzcatch {REPLACE INTO AAAAAA SELECT DISTINCT "AAAAAA" . * WHERE AAAAAA ( AAAAAA ( ) ) GROUP BY AAAAAA . AAAAAA . "AAAAAA" IN "AAAAAA" | AAAAAA ( ALL , ) ORDER BY #238, #92 DESC LIMIT 0 OFFSET - RAISE ( IGNORE ) NOT NULL > RAISE ( IGNORE ) IS NULL}
+} {1}
+do_test fuzz2-5.5 {
+ fuzzcatch {SELECT ALL * GROUP BY EXISTS ( SELECT "AAAAAA" . * , AAAAAA ( * ) AS AAAAAA FROM "AAAAAA" . "AAAAAA" AS "AAAAAA" USING ( AAAAAA , "AAAAAA" , "AAAAAA" ) WHERE AAAAAA ( DISTINCT ) - RAISE ( FAIL , "AAAAAA" ) HAVING "AAAAAA" . "AAAAAA" . AAAAAA ORDER BY #182 , #55 ) BETWEEN EXISTS ( SELECT ALL * FROM ( ( }
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/fuzz_common.tcl b/third_party/sqlite/test/fuzz_common.tcl
new file mode 100755
index 0000000..b552129
--- /dev/null
+++ b/third_party/sqlite/test/fuzz_common.tcl
@@ -0,0 +1,392 @@
+# 2007 May 10
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: fuzz_common.tcl,v 1.1 2007/05/30 10:36:47 danielk1977 Exp $
+
+proc fuzz {TemplateList} {
+ set n [llength $TemplateList]
+ set i [expr {int(rand()*$n)}]
+ set r [uplevel 1 subst -novar [list [lindex $TemplateList $i]]]
+
+ string map {"\n" " "} $r
+}
+
+# Fuzzy generation primitives:
+#
+# Literal
+# UnaryOp
+# BinaryOp
+# Expr
+# Table
+# Select
+# Insert
+#
+
+# Returns a string representing an SQL literal.
+#
+proc Literal {} {
+ set TemplateList {
+ 456 0 -456 1 -1
+ 2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
+ 'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
+ zeroblob(1000)
+ NULL
+ 56.1 -56.1
+ 123456789.1234567899
+ }
+ fuzz $TemplateList
+}
+
+# Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
+#
+proc UnaryOp {} {
+ set TemplateList {+ - NOT ~}
+ fuzz $TemplateList
+}
+
+# Returns a string containing an SQL binary operator (e.g. "*" or "/").
+#
+proc BinaryOp {} {
+ set TemplateList {
+ || * / % + - << >> & | < <= > >= = == != <> AND OR
+ LIKE GLOB {NOT LIKE}
+ }
+ fuzz $TemplateList
+}
+
+# Return the complete text of an SQL expression.
+#
+set ::ExprDepth 0
+proc Expr { {c {}} } {
+ incr ::ExprDepth
+
+ set TemplateList [concat $c $c $c {[Literal]}]
+ if {$::ExprDepth < 3} {
+ lappend TemplateList \
+ {[Expr $c] [BinaryOp] [Expr $c]} \
+ {[UnaryOp] [Expr $c]} \
+ {[Expr $c] ISNULL} \
+ {[Expr $c] NOTNULL} \
+ {CAST([Expr $c] AS blob)} \
+ {CAST([Expr $c] AS text)} \
+ {CAST([Expr $c] AS integer)} \
+ {CAST([Expr $c] AS real)} \
+ {abs([Expr])} \
+ {coalesce([Expr], [Expr])} \
+ {hex([Expr])} \
+ {length([Expr])} \
+ {lower([Expr])} \
+ {upper([Expr])} \
+ {quote([Expr])} \
+ {random()} \
+ {randomblob(min(max([Expr],1), 500))} \
+ {typeof([Expr])} \
+ {substr([Expr],[Expr],[Expr])} \
+ {CASE WHEN [Expr $c] THEN [Expr $c] ELSE [Expr $c] END} \
+ {[Literal]} {[Literal]} {[Literal]} \
+ {[Literal]} {[Literal]} {[Literal]} \
+ {[Literal]} {[Literal]} {[Literal]} \
+ {[Literal]} {[Literal]} {[Literal]}
+ }
+ if {$::SelectDepth < 4} {
+ lappend TemplateList \
+ {([Select 1])} \
+ {[Expr $c] IN ([Select 1])} \
+ {[Expr $c] NOT IN ([Select 1])} \
+ {EXISTS ([Select 1])} \
+ }
+ set res [fuzz $TemplateList]
+ incr ::ExprDepth -1
+ return $res
+}
+
+# Return a valid table name.
+#
+set ::TableList [list]
+proc Table {} {
+ set TemplateList [concat sqlite_master $::TableList]
+ fuzz $TemplateList
+}
+
+# Return one of:
+#
+# "SELECT DISTINCT", "SELECT ALL" or "SELECT"
+#
+proc SelectKw {} {
+ set TemplateList {
+ "SELECT DISTINCT"
+ "SELECT ALL"
+ "SELECT"
+ }
+ fuzz $TemplateList
+}
+
+# Return a result set for a SELECT statement.
+#
+proc ResultSet {{nRes 0} {c ""}} {
+ if {$nRes == 0} {
+ set nRes [expr {rand()*2 + 1}]
+ }
+
+ set aRes [list]
+ for {set ii 0} {$ii < $nRes} {incr ii} {
+ lappend aRes [Expr $c]
+ }
+
+ join $aRes ", "
+}
+
+set ::SelectDepth 0
+set ::ColumnList [list]
+proc SimpleSelect {{nRes 0}} {
+
+ set TemplateList {
+ {[SelectKw] [ResultSet $nRes]}
+ }
+
+ # The ::SelectDepth variable contains the number of ancestor SELECT
+ # statements (i.e. for a top level SELECT it is set to 0, for a
+ # sub-select 1, for a sub-select of a sub-select 2 etc.).
+ #
+ # If this is already greater than 3, do not generate a complicated
+ # SELECT statement. This tends to cause parser stack overflow (too
+ # boring to bother with).
+ #
+ if {$::SelectDepth < 4} {
+ lappend TemplateList \
+ {[SelectKw] [ResultSet $nRes $::ColumnList] FROM ([Select])} \
+ {[SelectKw] [ResultSet $nRes] FROM ([Select])} \
+ {[SelectKw] [ResultSet $nRes $::ColumnList] FROM [Table]} \
+ {
+ [SelectKw] [ResultSet $nRes $::ColumnList]
+ FROM ([Select])
+ GROUP BY [Expr]
+ HAVING [Expr]
+ } \
+
+ if {0 == $nRes} {
+ lappend TemplateList \
+ {[SelectKw] * FROM ([Select])} \
+ {[SelectKw] * FROM [Table]} \
+ {[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \
+ {
+ [SelectKw] *
+ FROM [Table],[Table] AS t2
+ WHERE [Expr $::ColumnList]
+ } {
+ [SelectKw] *
+ FROM [Table] LEFT OUTER JOIN [Table] AS t2
+ ON [Expr $::ColumnList]
+ WHERE [Expr $::ColumnList]
+ }
+ }
+ }
+
+ fuzz $TemplateList
+}
+
+# Return a SELECT statement.
+#
+# If boolean parameter $isExpr is set to true, make sure the
+# returned SELECT statement returns a single column of data.
+#
+proc Select {{nMulti 0}} {
+ set TemplateList {
+ {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
+ {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
+ {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
+ {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
+ {[SimpleSelect $nMulti] ORDER BY [Expr] DESC}
+ {[SimpleSelect $nMulti] ORDER BY [Expr] ASC}
+ {[SimpleSelect $nMulti] ORDER BY [Expr] ASC, [Expr] DESC}
+ {[SimpleSelect $nMulti] ORDER BY [Expr] LIMIT [Expr] OFFSET [Expr]}
+ }
+
+ if {$::SelectDepth < 4} {
+ if {$nMulti == 0} {
+ set nMulti [expr {(rand()*2)+1}]
+ }
+ lappend TemplateList \
+ {[SimpleSelect $nMulti] UNION [Select $nMulti]} \
+ {[SimpleSelect $nMulti] UNION ALL [Select $nMulti]} \
+ {[SimpleSelect $nMulti] EXCEPT [Select $nMulti]} \
+ {[SimpleSelect $nMulti] INTERSECT [Select $nMulti]}
+ }
+
+ incr ::SelectDepth
+ set res [fuzz $TemplateList]
+ incr ::SelectDepth -1
+ set res
+}
+
+# Generate and return a fuzzy INSERT statement.
+#
+proc Insert {} {
+ set TemplateList {
+ {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);}
+ {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);}
+ {INSERT INTO [Table] VALUES([Expr], [Expr]);}
+ }
+ fuzz $TemplateList
+}
+
+proc Column {} {
+ fuzz $::ColumnList
+}
+
+# Generate and return a fuzzy UPDATE statement.
+#
+proc Update {} {
+ set TemplateList {
+ {UPDATE [Table]
+ SET [Column] = [Expr $::ColumnList]
+ WHERE [Expr $::ColumnList]}
+ }
+ fuzz $TemplateList
+}
+
+proc Delete {} {
+ set TemplateList {
+ {DELETE FROM [Table] WHERE [Expr $::ColumnList]}
+ }
+ fuzz $TemplateList
+}
+
+proc Statement {} {
+ set TemplateList {
+ {[Update]}
+ {[Insert]}
+ {[Select]}
+ {[Delete]}
+ }
+ fuzz $TemplateList
+}
+
+# Return an identifier. This just chooses randomly from a fixed set
+# of strings.
+proc Identifier {} {
+ set TemplateList {
+ This just chooses randomly a fixed
+ We would also thank the developers
+ for their analysis Samba
+ }
+ fuzz $TemplateList
+}
+
+proc Check {} {
+ # Use a large value for $::SelectDepth, because sub-selects are
+ # not allowed in expressions used by CHECK constraints.
+ #
+ set sd $::SelectDepth
+ set ::SelectDepth 500
+ set TemplateList {
+ {}
+ {CHECK ([Expr])}
+ }
+ set res [fuzz $TemplateList]
+ set ::SelectDepth $sd
+ set res
+}
+
+proc Coltype {} {
+ set TemplateList {
+ {INTEGER PRIMARY KEY}
+ {VARCHAR [Check]}
+ {PRIMARY KEY}
+ }
+ fuzz $TemplateList
+}
+
+proc DropTable {} {
+ set TemplateList {
+ {DROP TABLE IF EXISTS [Identifier]}
+ }
+ fuzz $TemplateList
+}
+
+proc CreateView {} {
+ set TemplateList {
+ {CREATE VIEW [Identifier] AS [Select]}
+ }
+ fuzz $TemplateList
+}
+proc DropView {} {
+ set TemplateList {
+ {DROP VIEW IF EXISTS [Identifier]}
+ }
+ fuzz $TemplateList
+}
+
+proc CreateTable {} {
+ set TemplateList {
+ {CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])}
+ {CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])}
+ }
+ fuzz $TemplateList
+}
+
+proc CreateOrDropTableOrView {} {
+ set TemplateList {
+ {[CreateTable]}
+ {[DropTable]}
+ {[CreateView]}
+ {[DropView]}
+ }
+ fuzz $TemplateList
+}
+
+########################################################################
+
+set ::log [open fuzzy.log w]
+
+#
+# Usage: do_fuzzy_test <testname> ?<options>?
+#
+# -template
+# -errorlist
+# -repeats
+#
+proc do_fuzzy_test {testname args} {
+ set ::fuzzyopts(-errorlist) [list]
+ set ::fuzzyopts(-repeats) $::REPEATS
+ array set ::fuzzyopts $args
+
+ lappend ::fuzzyopts(-errorlist) {parser stack overflow}
+ lappend ::fuzzyopts(-errorlist) {ORDER BY}
+ lappend ::fuzzyopts(-errorlist) {GROUP BY}
+ lappend ::fuzzyopts(-errorlist) {datatype mismatch}
+
+ for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
+ do_test ${testname}.$ii {
+ set ::sql [subst $::fuzzyopts(-template)]
+ puts $::log $::sql
+ flush $::log
+ set rc [catch {execsql $::sql} msg]
+ set e 1
+ if {$rc} {
+ set e 0
+ foreach error $::fuzzyopts(-errorlist) {
+ if {0 == [string first $error $msg]} {
+ set e 1
+ break
+ }
+ }
+ }
+ if {$e == 0} {
+ puts ""
+ puts $::sql
+ puts $msg
+ }
+ set e
+ } {1}
+ }
+}
+
diff --git a/third_party/sqlite/test/fuzz_malloc.test b/third_party/sqlite/test/fuzz_malloc.test
new file mode 100755
index 0000000..2d78807
--- /dev/null
+++ b/third_party/sqlite/test/fuzz_malloc.test
@@ -0,0 +1,93 @@
+#
+# 2007 May 10
+#
+# 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 tests malloc failures in concert with fuzzy SQL generation.
+#
+# $Id: fuzz_malloc.test,v 1.9 2007/09/03 15:42:48 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !memdebug {
+ finish_test
+ return
+}
+
+source $testdir/malloc_common.tcl
+source $testdir/fuzz_common.tcl
+
+if {[info exists ISQUICK]} {
+ set ::REPEATS 20
+} elseif {[info exists SOAKTEST]} {
+ set ::REPEATS 100
+} else {
+ set ::REPEATS 40
+}
+
+#
+# Usage: do_fuzzy_malloc_test <testname> ?<options>?
+#
+# -template
+# -sqlprep
+# -repeats
+#
+proc do_fuzzy_malloc_test {testname args} {
+ set ::fuzzyopts(-repeats) $::REPEATS
+ set ::fuzzyopts(-sqlprep) {}
+ array set ::fuzzyopts $args
+
+ sqlite3_memdebug_fail -1
+ db close
+ file delete test.db test.db-journal
+ sqlite3 db test.db
+ set ::prep $::fuzzyopts(-sqlprep)
+ execsql $::prep
+ set jj 0
+ for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
+ expr srand($jj)
+ incr jj
+ set ::sql [subst $::fuzzyopts(-template)]
+ foreach {rc res} [catchsql "$::sql"] {}
+ if {$rc==0} {
+ do_malloc_test $testname-$ii -sqlbody $::sql -sqlprep $::prep
+ } else {
+ incr ii -1
+ }
+ }
+}
+
+#----------------------------------------------------------------
+# Test malloc failure during parsing (and execution) of a fuzzily
+# generated expressions.
+#
+do_fuzzy_malloc_test fuzzy_malloc-1 -template {Select [Expr]}
+do_fuzzy_malloc_test fuzzy_malloc-2 -template {[Select]}
+
+set ::SQLPREP {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE def(a, b, c);
+ CREATE TABLE ghi(a, b, c);
+ INSERT INTO abc VALUES(1.5, 3, 'a short string');
+ INSERT INTO def VALUES(NULL, X'ABCDEF',
+ 'a longer string. Long enough that it doesn''t fit in Mem.zShort');
+ INSERT INTO ghi VALUES(zeroblob(1000), 'hello world', -1257900987654321);
+ COMMIT;
+}
+set ::TableList [list abc def ghi]
+set ::ColumnList [list a b c]
+
+do_fuzzy_malloc_test fuzzy_malloc-3 \
+ -template {[Select]} \
+ -sqlprep $::SQLPREP
+
+finish_test
diff --git a/third_party/sqlite/test/hook.test b/third_party/sqlite/test/hook.test
new file mode 100755
index 0000000..39892b3
--- /dev/null
+++ b/third_party/sqlite/test/hook.test
@@ -0,0 +1,317 @@
+# 2004 Jan 14
+#
+# 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 TCL interface to the
+# SQLite library.
+#
+# The focus of the tests in this file is the following interface:
+#
+# sqlite_commit_hook (tests hook-1..hook-3 inclusive)
+# sqlite_update_hook (tests hook-4-*)
+# sqlite_rollback_hook (tests hook-5.*)
+#
+# $Id: hook.test,v 1.13 2008/01/19 20:11:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test hook-1.2 {
+ db commit_hook
+} {}
+
+
+do_test hook-3.1 {
+ set commit_cnt 0
+ proc commit_hook {} {
+ incr ::commit_cnt
+ return 0
+ }
+ db commit_hook ::commit_hook
+ db commit_hook
+} {::commit_hook}
+do_test hook-3.2 {
+ set commit_cnt
+} {0}
+do_test hook-3.3 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ }
+ set commit_cnt
+} {1}
+do_test hook-3.4 {
+ execsql {
+ INSERT INTO t2 VALUES(1,2);
+ INSERT INTO t2 SELECT a+1, b+1 FROM t2;
+ INSERT INTO t2 SELECT a+2, b+2 FROM t2;
+ }
+ set commit_cnt
+} {4}
+do_test hook-3.5 {
+ set commit_cnt {}
+ proc commit_hook {} {
+ set ::commit_cnt [execsql {SELECT * FROM t2}]
+ return 0
+ }
+ execsql {
+ INSERT INTO t2 VALUES(5,6);
+ }
+ set commit_cnt
+} {1 2 2 3 3 4 4 5 5 6}
+do_test hook-3.6 {
+ set commit_cnt {}
+ proc commit_hook {} {
+ set ::commit_cnt [execsql {SELECT * FROM t2}]
+ return 1
+ }
+ catchsql {
+ INSERT INTO t2 VALUES(6,7);
+ }
+} {1 {constraint failed}}
+do_test hook-3.7 {
+ set ::commit_cnt
+} {1 2 2 3 3 4 4 5 5 6 6 7}
+do_test hook-3.8 {
+ execsql {SELECT * FROM t2}
+} {1 2 2 3 3 4 4 5 5 6}
+
+# Test turnning off the commit hook
+#
+do_test hook-3.9 {
+ db commit_hook {}
+ set ::commit_cnt {}
+ execsql {
+ INSERT INTO t2 VALUES(7,8);
+ }
+ set ::commit_cnt
+} {}
+
+#----------------------------------------------------------------------------
+# Tests for the update-hook.
+#
+# 4.1.* - Very simple tests. Test that the update hook is invoked correctly
+# for INSERT, DELETE and UPDATE statements, including DELETE
+# statements with no WHERE clause.
+# 4.2.* - Check that the update-hook is invoked for rows modified by trigger
+# bodies. Also that the database name is correctly reported when
+# an attached database is modified.
+# 4.3.* - Do some sorting, grouping, compound queries, population and
+# depopulation of indices, to make sure the update-hook is not
+# invoked incorrectly.
+#
+
+# Simple tests
+do_test hook-4.1.1 {
+ catchsql {
+ DROP TABLE t1;
+ }
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ }
+ db update_hook [list lappend ::update_hook]
+} {}
+do_test hook-4.1.2 {
+ execsql {
+ INSERT INTO t1 VALUES(4, 'four');
+ DELETE FROM t1 WHERE b = 'two';
+ UPDATE t1 SET b = '' WHERE a = 1 OR a = 3;
+ DELETE FROM t1 WHERE 1; -- Avoid the truncate optimization (for now)
+ }
+ set ::update_hook
+} [list \
+ INSERT main t1 4 \
+ DELETE main t1 2 \
+ UPDATE main t1 1 \
+ UPDATE main t1 3 \
+ DELETE main t1 1 \
+ DELETE main t1 3 \
+ DELETE main t1 4 \
+]
+
+# Update hook is not invoked for changes to sqlite_master
+#
+do_test hook-4.1.3 {
+ set ::update_hook {}
+ execsql {
+ CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT RAISE(IGNORE); END;
+ }
+ set ::update_hook
+} {}
+do_test hook-4.1.4 {
+ set ::update_hook {}
+ execsql {
+ DROP TRIGGER r1;
+ }
+ set ::update_hook
+} {}
+
+
+set ::update_hook {}
+ifcapable trigger {
+ do_test hook-4.2.1 {
+ catchsql {
+ DROP TABLE t2;
+ }
+ execsql {
+ CREATE TABLE t2(c INTEGER PRIMARY KEY, d);
+ CREATE TRIGGER t1_trigger AFTER INSERT ON t1 BEGIN
+ INSERT INTO t2 VALUES(new.a, new.b);
+ UPDATE t2 SET d = d || ' via trigger' WHERE new.a = c;
+ DELETE FROM t2 WHERE new.a = c;
+ END;
+ }
+ } {}
+ do_test hook-4.2.2 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ }
+ set ::update_hook
+ } [list \
+ INSERT main t1 1 \
+ INSERT main t2 1 \
+ UPDATE main t2 1 \
+ DELETE main t2 1 \
+ INSERT main t1 2 \
+ INSERT main t2 2 \
+ UPDATE main t2 2 \
+ DELETE main t2 2 \
+ ]
+} else {
+ execsql {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ }
+}
+
+# Update-hook + ATTACH
+set ::update_hook {}
+ifcapable attach {
+ do_test hook-4.2.3 {
+ file delete -force test2.db
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b);
+ INSERT INTO aux.t3 SELECT * FROM t1;
+ UPDATE t3 SET b = 'two or so' WHERE a = 2;
+ DELETE FROM t3 WHERE 1; -- Avoid the truncate optimization (for now)
+ }
+ set ::update_hook
+ } [list \
+ INSERT aux t3 1 \
+ INSERT aux t3 2 \
+ UPDATE aux t3 2 \
+ DELETE aux t3 1 \
+ DELETE aux t3 2 \
+ ]
+}
+
+ifcapable trigger {
+ execsql {
+ DROP TRIGGER t1_trigger;
+ }
+}
+
+# Test that other vdbe operations involving btree structures do not
+# incorrectly invoke the update-hook.
+set ::update_hook {}
+do_test hook-4.3.1 {
+ execsql {
+ CREATE INDEX t1_i ON t1(b);
+ INSERT INTO t1 VALUES(3, 'three');
+ UPDATE t1 SET b = '';
+ DELETE FROM t1 WHERE a > 1;
+ }
+ set ::update_hook
+} [list \
+ INSERT main t1 3 \
+ UPDATE main t1 1 \
+ UPDATE main t1 2 \
+ UPDATE main t1 3 \
+ DELETE main t1 2 \
+ DELETE main t1 3 \
+]
+set ::update_hook {}
+ifcapable compound&&attach {
+ do_test hook-4.3.2 {
+ execsql {
+ SELECT * FROM t1 UNION SELECT * FROM t3;
+ SELECT * FROM t1 UNION ALL SELECT * FROM t3;
+ SELECT * FROM t1 INTERSECT SELECT * FROM t3;
+ SELECT * FROM t1 EXCEPT SELECT * FROM t3;
+ SELECT * FROM t1 ORDER BY b;
+ SELECT * FROM t1 GROUP BY b;
+ }
+ set ::update_hook
+ } [list]
+}
+db update_hook {}
+#
+#----------------------------------------------------------------------------
+
+#----------------------------------------------------------------------------
+# Test the rollback-hook. The rollback-hook is a bit more complicated than
+# either the commit or update hooks because a rollback can happen
+# explicitly (an sql ROLLBACK statement) or implicitly (a constraint or
+# error condition).
+#
+# hook-5.1.* - Test explicit rollbacks.
+# hook-5.2.* - Test implicit rollbacks caused by constraint failure.
+#
+# hook-5.3.* - Test implicit rollbacks caused by IO errors.
+# hook-5.4.* - Test implicit rollbacks caused by malloc() failure.
+# hook-5.5.* - Test hot-journal rollbacks. Or should the rollback hook
+# not be called for these?
+#
+
+do_test hook-5.0 {
+ # Configure the rollback hook to increment global variable
+ # $::rollback_hook each time it is invoked.
+ set ::rollback_hook 0
+ db rollback_hook [list incr ::rollback_hook]
+} {}
+
+# Test explicit rollbacks. Not much can really go wrong here.
+#
+do_test hook-5.1.1 {
+ set ::rollback_hook 0
+ execsql {
+ BEGIN;
+ ROLLBACK;
+ }
+ set ::rollback_hook
+} {1}
+
+# Test implicit rollbacks caused by constraints.
+#
+do_test hook-5.2.1 {
+ set ::rollback_hook 0
+ catchsql {
+ DROP TABLE t1;
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES('one', 'I');
+ INSERT INTO t1 VALUES('one', 'I');
+ }
+ set ::rollback_hook
+} {1}
+do_test hook-5.2.2 {
+ # Check that the INSERT transaction above really was rolled back.
+ execsql {
+ SELECT count(*) FROM t1;
+ }
+} {1}
+
+#
+# End rollback-hook testing.
+#----------------------------------------------------------------------------
+
+finish_test
diff --git a/third_party/sqlite/test/icu.test b/third_party/sqlite/test/icu.test
new file mode 100755
index 0000000..1138b3d
--- /dev/null
+++ b/third_party/sqlite/test/icu.test
@@ -0,0 +1,117 @@
+# 2007 May 1
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: icu.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !icu {
+ finish_test
+ return
+}
+
+# Create a table to work with.
+#
+execsql {CREATE TABLE test1(i1 int, i2 int, r1 real, r2 real, t1 text, t2 text)}
+execsql {INSERT INTO test1 VALUES(1,2,1.1,2.2,'hello','world')}
+proc test_expr {name settings expr result} {
+ do_test $name [format {
+ db one {
+ BEGIN;
+ UPDATE test1 SET %s;
+ SELECT %s FROM test1;
+ ROLLBACK;
+ }
+ } $settings $expr] $result
+}
+
+# Tests of the REGEXP operator.
+#
+test_expr icu-1.1 {i1='hello'} {i1 REGEXP 'hello'} 1
+test_expr icu-1.2 {i1='hello'} {i1 REGEXP '.ello'} 1
+test_expr icu-1.3 {i1='hello'} {i1 REGEXP '.ell'} 0
+test_expr icu-1.4 {i1='hello'} {i1 REGEXP '.ell.*'} 1
+test_expr icu-1.5 {i1=NULL} {i1 REGEXP '.ell.*'} {}
+
+# Some non-ascii characters with defined case mappings
+#
+set ::EGRAVE "\xC8"
+set ::egrave "\xE8"
+
+set ::OGRAVE "\xD2"
+set ::ograve "\xF2"
+
+# That German letter that looks a bit like a B. The
+# upper-case version of which is "SS" (two characters).
+#
+set ::szlig "\xDF"
+
+# Tests of the upper()/lower() functions.
+#
+test_expr icu-2.1 {i1='HellO WorlD'} {upper(i1)} {HELLO WORLD}
+test_expr icu-2.2 {i1='HellO WorlD'} {lower(i1)} {hello world}
+test_expr icu-2.3 {i1=$::egrave} {lower(i1)} $::egrave
+test_expr icu-2.4 {i1=$::egrave} {upper(i1)} $::EGRAVE
+test_expr icu-2.5 {i1=$::ograve} {lower(i1)} $::ograve
+test_expr icu-2.6 {i1=$::ograve} {upper(i1)} $::OGRAVE
+test_expr icu-2.3 {i1=$::EGRAVE} {lower(i1)} $::egrave
+test_expr icu-2.4 {i1=$::EGRAVE} {upper(i1)} $::EGRAVE
+test_expr icu-2.5 {i1=$::OGRAVE} {lower(i1)} $::ograve
+test_expr icu-2.6 {i1=$::OGRAVE} {upper(i1)} $::OGRAVE
+
+test_expr icu-2.7 {i1=$::szlig} {upper(i1)} "SS"
+test_expr icu-2.8 {i1='SS'} {lower(i1)} "ss"
+
+# In turkish (locale="tr_TR"), the lower case version of I
+# is "small dotless i" (code point 0x131 (decimal 305)).
+#
+set ::small_dotless_i "\u0131"
+test_expr icu-3.1 {i1='I'} {lower(i1)} "i"
+test_expr icu-3.2 {i1='I'} {lower(i1, 'tr_tr')} $::small_dotless_i
+test_expr icu-3.3 {i1='I'} {lower(i1, 'en_AU')} "i"
+
+#--------------------------------------------------------------------
+# Test the collation sequence function.
+#
+do_test icu-4.1 {
+ execsql {
+ CREATE TABLE fruit(name);
+ INSERT INTO fruit VALUES('plum');
+ INSERT INTO fruit VALUES('cherry');
+ INSERT INTO fruit VALUES('apricot');
+ INSERT INTO fruit VALUES('peach');
+ INSERT INTO fruit VALUES('chokecherry');
+ INSERT INTO fruit VALUES('yamot');
+ }
+} {}
+do_test icu-4.2 {
+ execsql {
+ SELECT icu_load_collation('en_US', 'AmericanEnglish');
+ SELECT icu_load_collation('lt_LT', 'Lithuanian');
+ }
+ execsql {
+ SELECT name FROM fruit ORDER BY name COLLATE AmericanEnglish ASC;
+ }
+} {apricot cherry chokecherry peach plum yamot}
+
+
+# Test collation using Lithuanian rules. In the Lithuanian
+# alphabet, "y" comes right after "i".
+#
+do_test icu-4.3 {
+ execsql {
+ SELECT name FROM fruit ORDER BY name COLLATE Lithuanian ASC;
+ }
+} {apricot cherry chokecherry yamot peach plum}
+
+finish_test
diff --git a/third_party/sqlite/test/in.test b/third_party/sqlite/test/in.test
new file mode 100755
index 0000000..8fbfbed
--- /dev/null
+++ b/third_party/sqlite/test/in.test
@@ -0,0 +1,580 @@
+# 2001 September 15
+#
+# 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 file is testing the IN and BETWEEN operator.
+#
+# $Id: in.test,v 1.22 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Generate the test data we will need for the first squences of tests.
+#
+do_test in-1.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a int, b int);
+ }
+ for {set i 1} {$i<=10} {incr i} {
+ execsql "INSERT INTO t1 VALUES($i,[expr {1<<$i}])"
+ }
+ execsql {
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+} {10}
+
+# Do basic testing of BETWEEN.
+#
+do_test in-1.1 {
+ execsql {SELECT a FROM t1 WHERE b BETWEEN 10 AND 50 ORDER BY a}
+} {4 5}
+do_test in-1.2 {
+ execsql {SELECT a FROM t1 WHERE b NOT BETWEEN 10 AND 50 ORDER BY a}
+} {1 2 3 6 7 8 9 10}
+do_test in-1.3 {
+ execsql {SELECT a FROM t1 WHERE b BETWEEN a AND a*5 ORDER BY a}
+} {1 2 3 4}
+do_test in-1.4 {
+ execsql {SELECT a FROM t1 WHERE b NOT BETWEEN a AND a*5 ORDER BY a}
+} {5 6 7 8 9 10}
+do_test in-1.6 {
+ execsql {SELECT a FROM t1 WHERE b BETWEEN a AND a*5 OR b=512 ORDER BY a}
+} {1 2 3 4 9}
+do_test in-1.7 {
+ execsql {SELECT a+ 100*(a BETWEEN 1 and 3) FROM t1 ORDER BY b}
+} {101 102 103 4 5 6 7 8 9 10}
+
+# The rest of this file concentrates on testing the IN operator.
+# Skip this if the library is compiled with SQLITE_OMIT_SUBQUERY
+# (because the IN operator is unavailable).
+#
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+# Testing of the IN operator using static lists on the right-hand side.
+#
+do_test in-2.1 {
+ execsql {SELECT a FROM t1 WHERE b IN (8,12,16,24,32) ORDER BY a}
+} {3 4 5}
+do_test in-2.2 {
+ execsql {SELECT a FROM t1 WHERE b NOT IN (8,12,16,24,32) ORDER BY a}
+} {1 2 6 7 8 9 10}
+do_test in-2.3 {
+ execsql {SELECT a FROM t1 WHERE b IN (8,12,16,24,32) OR b=512 ORDER BY a}
+} {3 4 5 9}
+do_test in-2.4 {
+ execsql {SELECT a FROM t1 WHERE b NOT IN (8,12,16,24,32) OR b=512 ORDER BY a}
+} {1 2 6 7 8 9 10}
+do_test in-2.5 {
+ execsql {SELECT a+100*(b IN (8,16,24)) FROM t1 ORDER BY b}
+} {1 2 103 104 5 6 7 8 9 10}
+
+do_test in-2.6 {
+ execsql {SELECT a FROM t1 WHERE b IN (b+8,64)}
+} {6}
+do_test in-2.7 {
+ execsql {SELECT a FROM t1 WHERE b IN (max(5,10,b),20)}
+} {4 5 6 7 8 9 10}
+do_test in-2.8 {
+ execsql {SELECT a FROM t1 WHERE b IN (8*2,64/2) ORDER BY b}
+} {4 5}
+do_test in-2.9 {
+ execsql {SELECT a FROM t1 WHERE b IN (max(5,10),20)}
+} {}
+do_test in-2.10 {
+ execsql {SELECT a FROM t1 WHERE min(0,b IN (a,30))}
+} {}
+do_test in-2.11 {
+ set v [catch {execsql {SELECT a FROM t1 WHERE c IN (10,20)}} msg]
+ lappend v $msg
+} {1 {no such column: c}}
+
+# Testing the IN operator where the right-hand side is a SELECT
+#
+do_test in-3.1 {
+ execsql {
+ SELECT a FROM t1
+ WHERE b IN (SELECT b FROM t1 WHERE a<5)
+ ORDER BY a
+ }
+} {1 2 3 4}
+do_test in-3.2 {
+ execsql {
+ SELECT a FROM t1
+ WHERE b IN (SELECT b FROM t1 WHERE a<5) OR b==512
+ ORDER BY a
+ }
+} {1 2 3 4 9}
+do_test in-3.3 {
+ execsql {
+ SELECT a + 100*(b IN (SELECT b FROM t1 WHERE a<5)) FROM t1 ORDER BY b
+ }
+} {101 102 103 104 5 6 7 8 9 10}
+
+# Make sure the UPDATE and DELETE commands work with IN-SELECT
+#
+do_test in-4.1 {
+ execsql {
+ UPDATE t1 SET b=b*2
+ WHERE b IN (SELECT b FROM t1 WHERE a>8)
+ }
+ execsql {SELECT b FROM t1 ORDER BY b}
+} {2 4 8 16 32 64 128 256 1024 2048}
+do_test in-4.2 {
+ execsql {
+ DELETE FROM t1 WHERE b IN (SELECT b FROM t1 WHERE a>8)
+ }
+ execsql {SELECT a FROM t1 ORDER BY a}
+} {1 2 3 4 5 6 7 8}
+do_test in-4.3 {
+ execsql {
+ DELETE FROM t1 WHERE b NOT IN (SELECT b FROM t1 WHERE a>4)
+ }
+ execsql {SELECT a FROM t1 ORDER BY a}
+} {5 6 7 8}
+
+# Do an IN with a constant RHS but where the RHS has many, many
+# elements. We need to test that collisions in the hash table
+# are resolved properly.
+#
+do_test in-5.1 {
+ execsql {
+ INSERT INTO t1 VALUES('hello', 'world');
+ SELECT * FROM t1
+ WHERE a IN (
+ 'Do','an','IN','with','a','constant','RHS','but','where','the',
+ 'has','many','elements','We','need','to','test','that',
+ 'collisions','hash','table','are','resolved','properly',
+ 'This','in-set','contains','thirty','one','entries','hello');
+ }
+} {hello world}
+
+# Make sure the IN operator works with INTEGER PRIMARY KEY fields.
+#
+do_test in-6.1 {
+ execsql {
+ CREATE TABLE ta(a INTEGER PRIMARY KEY, b);
+ INSERT INTO ta VALUES(1,1);
+ INSERT INTO ta VALUES(2,2);
+ INSERT INTO ta VALUES(3,3);
+ INSERT INTO ta VALUES(4,4);
+ INSERT INTO ta VALUES(6,6);
+ INSERT INTO ta VALUES(8,8);
+ INSERT INTO ta VALUES(10,
+ 'This is a key that is long enough to require a malloc in the VDBE');
+ SELECT * FROM ta WHERE a<10;
+ }
+} {1 1 2 2 3 3 4 4 6 6 8 8}
+do_test in-6.2 {
+ execsql {
+ CREATE TABLE tb(a INTEGER PRIMARY KEY, b);
+ INSERT INTO tb VALUES(1,1);
+ INSERT INTO tb VALUES(2,2);
+ INSERT INTO tb VALUES(3,3);
+ INSERT INTO tb VALUES(5,5);
+ INSERT INTO tb VALUES(7,7);
+ INSERT INTO tb VALUES(9,9);
+ INSERT INTO tb VALUES(11,
+ 'This is a key that is long enough to require a malloc in the VDBE');
+ SELECT * FROM tb WHERE a<10;
+ }
+} {1 1 2 2 3 3 5 5 7 7 9 9}
+do_test in-6.3 {
+ execsql {
+ SELECT a FROM ta WHERE b IN (SELECT a FROM tb);
+ }
+} {1 2 3}
+do_test in-6.4 {
+ execsql {
+ SELECT a FROM ta WHERE b NOT IN (SELECT a FROM tb);
+ }
+} {4 6 8 10}
+do_test in-6.5 {
+ execsql {
+ SELECT a FROM ta WHERE b IN (SELECT b FROM tb);
+ }
+} {1 2 3 10}
+do_test in-6.6 {
+ execsql {
+ SELECT a FROM ta WHERE b NOT IN (SELECT b FROM tb);
+ }
+} {4 6 8}
+do_test in-6.7 {
+ execsql {
+ SELECT a FROM ta WHERE a IN (SELECT a FROM tb);
+ }
+} {1 2 3}
+do_test in-6.8 {
+ execsql {
+ SELECT a FROM ta WHERE a NOT IN (SELECT a FROM tb);
+ }
+} {4 6 8 10}
+do_test in-6.9 {
+ execsql {
+ SELECT a FROM ta WHERE a IN (SELECT b FROM tb);
+ }
+} {1 2 3}
+do_test in-6.10 {
+ execsql {
+ SELECT a FROM ta WHERE a NOT IN (SELECT b FROM tb);
+ }
+} {4 6 8 10}
+
+# Tests of IN operator against empty sets. (Ticket #185)
+#
+do_test in-7.1 {
+ execsql {
+ SELECT a FROM t1 WHERE a IN ();
+ }
+} {}
+do_test in-7.2 {
+ execsql {
+ SELECT a FROM t1 WHERE a IN (5);
+ }
+} {5}
+do_test in-7.3 {
+ execsql {
+ SELECT a FROM t1 WHERE a NOT IN () ORDER BY a;
+ }
+} {5 6 7 8 hello}
+do_test in-7.4 {
+ execsql {
+ SELECT a FROM t1 WHERE a IN (5) AND b IN ();
+ }
+} {}
+do_test in-7.5 {
+ execsql {
+ SELECT a FROM t1 WHERE a IN (5) AND b NOT IN ();
+ }
+} {5}
+do_test in-7.6 {
+ execsql {
+ SELECT a FROM ta WHERE a IN ();
+ }
+} {}
+do_test in-7.7 {
+ execsql {
+ SELECT a FROM ta WHERE a NOT IN ();
+ }
+} {1 2 3 4 6 8 10}
+
+do_test in-8.1 {
+ execsql {
+ SELECT b FROM t1 WHERE a IN ('hello','there')
+ }
+} {world}
+do_test in-8.2 {
+ execsql {
+ SELECT b FROM t1 WHERE a IN ("hello",'there')
+ }
+} {world}
+
+# Test constructs of the form: expr IN tablename
+#
+do_test in-9.1 {
+ execsql {
+ CREATE TABLE t4 AS SELECT a FROM tb;
+ SELECT * FROM t4;
+ }
+} {1 2 3 5 7 9 11}
+do_test in-9.2 {
+ execsql {
+ SELECT b FROM t1 WHERE a IN t4;
+ }
+} {32 128}
+do_test in-9.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a NOT IN t4;
+ }
+} {64 256 world}
+do_test in-9.4 {
+ catchsql {
+ SELECT b FROM t1 WHERE a NOT IN tb;
+ }
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+
+# IN clauses in CHECK constraints. Ticket #1645
+#
+do_test in-10.1 {
+ execsql {
+ CREATE TABLE t5(
+ a INTEGER,
+ CHECK( a IN (111,222,333) )
+ );
+ INSERT INTO t5 VALUES(111);
+ SELECT * FROM t5;
+ }
+} {111}
+do_test in-10.2 {
+ catchsql {
+ INSERT INTO t5 VALUES(4);
+ }
+} {1 {constraint failed}}
+
+# Ticket #1821
+#
+# Type affinity applied to the right-hand side of an IN operator.
+#
+do_test in-11.1 {
+ execsql {
+ CREATE TABLE t6(a,b NUMERIC);
+ INSERT INTO t6 VALUES(1,2);
+ INSERT INTO t6 VALUES(2,3);
+ SELECT * FROM t6 WHERE b IN (2);
+ }
+} {1 2}
+do_test in-11.2 {
+ # The '2' should be coerced into 2 because t6.b is NUMERIC
+ execsql {
+ SELECT * FROM t6 WHERE b IN ('2');
+ }
+} {1 2}
+do_test in-11.3 {
+ # No coercion should occur here because of the unary + before b.
+ execsql {
+ SELECT * FROM t6 WHERE +b IN ('2');
+ }
+} {}
+do_test in-11.4 {
+ # No coercion because column a as affinity NONE
+ execsql {
+ SELECT * FROM t6 WHERE a IN ('2');
+ }
+} {}
+do_test in-11.5 {
+ execsql {
+ SELECT * FROM t6 WHERE a IN (2);
+ }
+} {2 3}
+do_test in-11.6 {
+ # No coercion because column a as affinity NONE
+ execsql {
+ SELECT * FROM t6 WHERE +a IN ('2');
+ }
+} {}
+
+# Test error conditions with expressions of the form IN(<compound select>).
+#
+ifcapable compound {
+do_test in-12.1 {
+ execsql {
+ CREATE TABLE t2(a, b, c);
+ CREATE TABLE t3(a, b, c);
+ }
+} {}
+do_test in-12.2 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
+ );
+ }
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+do_test in-12.3 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a, b FROM t3 UNION SELECT a, b FROM t2
+ );
+ }
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+do_test in-12.4 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
+ );
+ }
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+do_test in-12.5 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
+ );
+ }
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+do_test in-12.6 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a FROM t3 UNION ALL SELECT a, b FROM t2
+ );
+ }
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+do_test in-12.7 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a FROM t3 UNION SELECT a, b FROM t2
+ );
+ }
+} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
+do_test in-12.8 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a FROM t3 EXCEPT SELECT a, b FROM t2
+ );
+ }
+} {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}}
+do_test in-12.9 {
+ catchsql {
+ SELECT * FROM t2 WHERE a IN (
+ SELECT a FROM t3 INTERSECT SELECT a, b FROM t2
+ );
+ }
+} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
+}
+
+
+#------------------------------------------------------------------------
+# The following tests check that NULL is handled correctly when it
+# appears as part of a set of values on the right-hand side of an
+# IN or NOT IN operator.
+#
+# When it appears in such a set, NULL is handled as an "unknown value".
+# If, because of the unknown value in the set, the result of the expression
+# cannot be determined, then it itself evaluates to NULL.
+#
+
+# Warm body test to demonstrate the principles being tested:
+#
+do_test in-13.1 {
+ db nullvalue "null"
+ execsql { SELECT
+ 1 IN (NULL, 1, 2), -- The value 1 is a member of the set, return true.
+ 3 IN (NULL, 1, 2), -- Ambiguous, return NULL.
+ 1 NOT IN (NULL, 1, 2), -- The value 1 is a member of the set, return false.
+ 3 NOT IN (NULL, 1, 2) -- Ambiguous, return NULL.
+ }
+} {1 null 0 null}
+
+do_test in-13.2 {
+ execsql {
+ CREATE TABLE t7(a, b, c NOT NULL);
+ INSERT INTO t7 VALUES(1, 1, 1);
+ INSERT INTO t7 VALUES(2, 2, 2);
+ INSERT INTO t7 VALUES(3, 3, 3);
+ INSERT INTO t7 VALUES(NULL, 4, 4);
+ INSERT INTO t7 VALUES(NULL, 5, 5);
+ }
+} {}
+
+do_test in-13.3 {
+ execsql { SELECT 2 IN (SELECT a FROM t7) }
+} {1}
+do_test in-13.4 {
+ execsql { SELECT 6 IN (SELECT a FROM t7) }
+} {null}
+
+do_test in-13.5 {
+ execsql { SELECT 2 IN (SELECT b FROM t7) }
+} {1}
+do_test in-13.6 {
+ execsql { SELECT 6 IN (SELECT b FROM t7) }
+} {0}
+
+do_test in-13.7 {
+ execsql { SELECT 2 IN (SELECT c FROM t7) }
+} {1}
+do_test in-13.8 {
+ execsql { SELECT 6 IN (SELECT c FROM t7) }
+} {0}
+
+do_test in-13.9 {
+ execsql {
+ SELECT
+ 2 NOT IN (SELECT a FROM t7),
+ 6 NOT IN (SELECT a FROM t7),
+ 2 NOT IN (SELECT b FROM t7),
+ 6 NOT IN (SELECT b FROM t7),
+ 2 NOT IN (SELECT c FROM t7),
+ 6 NOT IN (SELECT c FROM t7)
+ }
+} {0 null 0 1 0 1}
+
+do_test in-13.10 {
+ execsql {
+ SELECT b IN (
+ SELECT inside.a
+ FROM t7 AS inside
+ WHERE inside.b BETWEEN outside.b+1 AND outside.b+2
+ )
+ FROM t7 AS outside ORDER BY b;
+ }
+} {0 null null null 0}
+
+do_test in-13.11 {
+ execsql {
+ SELECT b NOT IN (
+ SELECT inside.a
+ FROM t7 AS inside
+ WHERE inside.b BETWEEN outside.b+1 AND outside.b+2
+ )
+ FROM t7 AS outside ORDER BY b;
+ }
+} {1 null null null 1}
+
+do_test in-13.12 {
+ execsql {
+ CREATE INDEX i1 ON t7(a);
+ CREATE INDEX i2 ON t7(b);
+ CREATE INDEX i3 ON t7(c);
+ }
+ execsql {
+ SELECT
+ 2 IN (SELECT a FROM t7),
+ 6 IN (SELECT a FROM t7),
+ 2 IN (SELECT b FROM t7),
+ 6 IN (SELECT b FROM t7),
+ 2 IN (SELECT c FROM t7),
+ 6 IN (SELECT c FROM t7)
+ }
+} {1 null 1 0 1 0}
+
+do_test in-13.13 {
+ execsql {
+ SELECT
+ 2 NOT IN (SELECT a FROM t7),
+ 6 NOT IN (SELECT a FROM t7),
+ 2 NOT IN (SELECT b FROM t7),
+ 6 NOT IN (SELECT b FROM t7),
+ 2 NOT IN (SELECT c FROM t7),
+ 6 NOT IN (SELECT c FROM t7)
+ }
+} {0 null 0 1 0 1}
+
+do_test in-13.14 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE a(id INTEGER);
+ INSERT INTO a VALUES(1);
+ INSERT INTO a VALUES(2);
+ INSERT INTO a VALUES(3);
+ CREATE TABLE b(id INTEGER);
+ INSERT INTO b VALUES(NULL);
+ INSERT INTO b VALUES(3);
+ INSERT INTO b VALUES(4);
+ INSERT INTO b VALUES(5);
+ COMMIT;
+ SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
+ }
+} {}
+do_test in-13.14 {
+ execsql {
+ CREATE INDEX i5 ON b(id);
+ SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
+ }
+} {}
+
+
+do_test in-13.X {
+ db nullvalue ""
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/in2.test b/third_party/sqlite/test/in2.test
new file mode 100755
index 0000000..4324082
--- /dev/null
+++ b/third_party/sqlite/test/in2.test
@@ -0,0 +1,67 @@
+# 2007 May 12
+#
+# 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 tests a special case in the b-tree code that can be
+# hit by the "IN" operator (or EXISTS, NOT IN, etc.).
+#
+# $Id: in2.test,v 1.3 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test in2-1 {
+ execsql {
+ CREATE TABLE a(i INTEGER PRIMARY KEY, a);
+ }
+} {}
+
+set ::N 2000
+
+do_test in2-2 {
+ db transaction {
+ for {set ::ii 0} {$::ii < $::N} {incr ::ii} {
+ execsql {INSERT INTO a VALUES($::ii, $::ii)}
+ }
+ execsql {INSERT INTO a VALUES(4000, '')}
+
+ for {set ::ii 0} {$::ii < $::N} {incr ::ii} {
+ set ::t [format "x%04d" $ii]
+ execsql {INSERT INTO a VALUES(NULL, $::t)}
+ }
+ }
+} {}
+
+# Each iteration of this loop builds a slightly different b-tree to
+# evaluate the "IN (...)" operator in the SQL statement. The contents
+# of the b-tree are (in sorted order):
+#
+# $::ii integers.
+# a string of zero length.
+# $::N short strings.
+#
+# Records are inserted in sorted order.
+#
+# The string of zero-length is stored in a b-tree cell with 3 bytes
+# of payload. Moving this cell from a leaf node to a internal node
+# during b-tree balancing was causing an assertion failure.
+#
+# This bug only applied to b-trees generated to evaluate IN (..)
+# clauses, as it is impossible for persistent b-trees (SQL tables +
+# indices) to contain cells smaller than 4 bytes.
+#
+for {set ::ii 3} {$::ii < $::N} {incr ::ii} {
+ do_test in2-$::ii {
+ execsql {
+ SELECT 1 IN (SELECT a FROM a WHERE (i < $::ii) OR (i >= $::N))
+ }
+ } {1}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/in3.test b/third_party/sqlite/test/in3.test
new file mode 100755
index 0000000..012c9b4
--- /dev/null
+++ b/third_party/sqlite/test/in3.test
@@ -0,0 +1,289 @@
+# 2007 November 29
+#
+# 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 tests the optimisations made in November 2007 of expressions
+# of the following form:
+#
+# <value> IN (SELECT <column> FROM <table>)
+#
+# $Id: in3.test,v 1.5 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+# Return the number of OpenEphemeral instructions used in the
+# implementation of the sql statement passed as a an argument.
+#
+proc nEphemeral {sql} {
+ set nEph 0
+ foreach op [execsql "EXPLAIN $sql"] {
+ if {$op eq "OpenEphemeral"} {incr nEph}
+ }
+ set nEph
+}
+
+# This proc works the same way as execsql, except that the number
+# of OpenEphemeral instructions used in the implementation of the
+# statement is inserted into the start of the returned list.
+#
+proc exec_neph {sql} {
+ return [concat [nEphemeral $sql] [execsql $sql]]
+}
+
+do_test in3-1.1 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t1 VALUES(5, 6);
+ }
+} {}
+
+# All of these queries should avoid using a temp-table:
+#
+do_test in3-1.2 {
+ exec_neph { SELECT rowid FROM t1 WHERE rowid IN (SELECT rowid FROM t1); }
+} {0 1 2 3}
+do_test in3-1.3 {
+ exec_neph { SELECT a FROM t1 WHERE a IN (SELECT a FROM t1); }
+} {0 1 3 5}
+do_test in3-1.4 {
+ exec_neph { SELECT rowid FROM t1 WHERE rowid+0 IN (SELECT rowid FROM t1); }
+} {0 1 2 3}
+do_test in3-1.5 {
+ exec_neph { SELECT a FROM t1 WHERE a+0 IN (SELECT a FROM t1); }
+} {0 1 3 5}
+
+# Because none of the sub-select queries in the following statements
+# match the pattern ("SELECT <column> FROM <table>"), the following do
+# require a temp table.
+#
+do_test in3-1.6 {
+ exec_neph { SELECT rowid FROM t1 WHERE rowid IN (SELECT rowid+0 FROM t1); }
+} {1 1 2 3}
+do_test in3-1.7 {
+ exec_neph { SELECT a FROM t1 WHERE a IN (SELECT a+0 FROM t1); }
+} {1 1 3 5}
+do_test in3-1.8 {
+ exec_neph { SELECT a FROM t1 WHERE a IN (SELECT a FROM t1 WHERE 1); }
+} {1 1 3 5}
+do_test in3-1.9 {
+ exec_neph { SELECT a FROM t1 WHERE a IN (SELECT a FROM t1 GROUP BY a); }
+} {1 1 3 5}
+
+# This should not use a temp-table. Even though the sub-select does
+# not exactly match the pattern "SELECT <column> FROM <table>", in
+# this case the ORDER BY is a no-op and can be ignored.
+do_test in3-1.10 {
+ exec_neph { SELECT a FROM t1 WHERE a IN (SELECT a FROM t1 ORDER BY a); }
+} {0 1 3 5}
+
+# These do use the temp-table. Adding the LIMIT clause means the
+# ORDER BY cannot be ignored.
+do_test in3-1.11 {
+ exec_neph {SELECT a FROM t1 WHERE a IN (SELECT a FROM t1 ORDER BY a LIMIT 1)}
+} {1 1}
+do_test in3-1.12 {
+ exec_neph {
+ SELECT a FROM t1 WHERE a IN (SELECT a FROM t1 ORDER BY a LIMIT 1 OFFSET 1)
+ }
+} {1 3}
+
+# Has to use a temp-table because of the compound sub-select.
+#
+ifcapable compound {
+ do_test in3-1.13 {
+ exec_neph {
+ SELECT a FROM t1 WHERE a IN (
+ SELECT a FROM t1 UNION ALL SELECT a FROM t1
+ )
+ }
+ } {1 1 3 5}
+}
+
+# The first of these queries has to use the temp-table, because the
+# collation sequence used for the index on "t1.a" does not match the
+# collation sequence used by the "IN" comparison. The second does not
+# require a temp-table, because the collation sequences match.
+#
+do_test in3-1.14 {
+ exec_neph { SELECT a FROM t1 WHERE a COLLATE nocase IN (SELECT a FROM t1) }
+} {1 1 3 5}
+do_test in3-1.15 {
+ exec_neph { SELECT a FROM t1 WHERE a COLLATE binary IN (SELECT a FROM t1) }
+} {0 1 3 5}
+
+# Neither of these queries require a temp-table. The collation sequence
+# makes no difference when using a rowid.
+#
+do_test in3-1.16 {
+ exec_neph {SELECT a FROM t1 WHERE a COLLATE nocase IN (SELECT rowid FROM t1)}
+} {0 1 3}
+do_test in3-1.17 {
+ exec_neph {SELECT a FROM t1 WHERE a COLLATE binary IN (SELECT rowid FROM t1)}
+} {0 1 3}
+
+# The following tests - in3.2.* - test a bug that was difficult to track
+# down during development. They are not particularly well focused.
+#
+do_test in3-2.1 {
+ execsql {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(w int, x int, y int);
+ CREATE TABLE t2(p int, q int, r int, s int);
+ }
+ for {set i 1} {$i<=100} {incr i} {
+ set w $i
+ set x [expr {int(log($i)/log(2))}]
+ set y [expr {$i*$i + 2*$i + 1}]
+ execsql "INSERT INTO t1 VALUES($w,$x,$y)"
+ }
+ set maxy [execsql {select max(y) from t1}]
+ db eval { INSERT INTO t2 SELECT 101-w, x, $maxy+1-y, y FROM t1 }
+} {}
+do_test in3-2.2 {
+ execsql {
+ SELECT rowid
+ FROM t1
+ WHERE rowid IN (SELECT rowid FROM t1 WHERE rowid IN (1, 2));
+ }
+} {1 2}
+do_test in3-2.3 {
+ execsql {
+ select rowid from t1 where rowid IN (-1,2,4)
+ }
+} {2 4}
+do_test in3-2.4 {
+ execsql {
+ SELECT rowid FROM t1 WHERE rowid IN
+ (select rowid from t1 where rowid IN (-1,2,4))
+ }
+} {2 4}
+
+#-------------------------------------------------------------------------
+# This next block of tests - in3-3.* - verify that column affinity is
+# correctly handled in cases where an index might be used to optimise
+# an IN (SELECT) expression.
+#
+do_test in3-3.1 {
+ catch {execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }}
+
+ execsql {
+
+ CREATE TABLE t1(a BLOB, b NUMBER ,c TEXT);
+ CREATE UNIQUE INDEX t1_i1 ON t1(a); /* no affinity */
+ CREATE UNIQUE INDEX t1_i2 ON t1(b); /* numeric affinity */
+ CREATE UNIQUE INDEX t1_i3 ON t1(c); /* text affinity */
+
+ CREATE TABLE t2(x BLOB, y NUMBER, z TEXT);
+ CREATE UNIQUE INDEX t2_i1 ON t2(x); /* no affinity */
+ CREATE UNIQUE INDEX t2_i2 ON t2(y); /* numeric affinity */
+ CREATE UNIQUE INDEX t2_i3 ON t2(z); /* text affinity */
+
+ INSERT INTO t1 VALUES(1, 1, 1);
+ INSERT INTO t2 VALUES('1', '1', '1');
+ }
+} {}
+
+do_test in3-3.2 {
+ # No affinity is applied before comparing "x" and "a". Therefore
+ # the index can be used (the comparison is false, text!=number).
+ exec_neph { SELECT x IN (SELECT a FROM t1) FROM t2 }
+} {0 0}
+do_test in3-3.3 {
+ # Logically, numeric affinity is applied to both sides before
+ # the comparison. Therefore it is possible to use index t1_i2.
+ exec_neph { SELECT x IN (SELECT b FROM t1) FROM t2 }
+} {0 1}
+do_test in3-3.4 {
+ # No affinity is applied before the comparison takes place. Making
+ # it possible to use index t1_i3.
+ exec_neph { SELECT x IN (SELECT c FROM t1) FROM t2 }
+} {0 1}
+
+do_test in3-3.5 {
+ # Numeric affinity should be applied to each side before the comparison
+ # takes place. Therefore we cannot use index t1_i1, which has no affinity.
+ exec_neph { SELECT y IN (SELECT a FROM t1) FROM t2 }
+} {1 1}
+do_test in3-3.6 {
+ # Numeric affinity is applied to both sides before
+ # the comparison. Therefore it is possible to use index t1_i2.
+ exec_neph { SELECT y IN (SELECT b FROM t1) FROM t2 }
+} {0 1}
+do_test in3-3.7 {
+ # Numeric affinity is applied before the comparison takes place.
+ # Making it impossible to use index t1_i3.
+ exec_neph { SELECT y IN (SELECT c FROM t1) FROM t2 }
+} {1 1}
+
+#---------------------------------------------------------------------
+#
+# Test using a multi-column index.
+#
+do_test in3-4.1 {
+ execsql {
+ CREATE TABLE t3(a, b, c);
+ CREATE UNIQUE INDEX t3_i ON t3(b, a);
+ }
+
+ execsql {
+ INSERT INTO t3 VALUES(1, 'numeric', 2);
+ INSERT INTO t3 VALUES(2, 'text', 2);
+ INSERT INTO t3 VALUES(3, 'real', 2);
+ INSERT INTO t3 VALUES(4, 'none', 2);
+ }
+} {}
+do_test in3-4.2 {
+ exec_neph { SELECT 'text' IN (SELECT b FROM t3) }
+} {0 1}
+do_test in3-4.3 {
+ exec_neph { SELECT 'TEXT' COLLATE nocase IN (SELECT b FROM t3) }
+} {1 1}
+do_test in3-4.4 {
+ # A temp table must be used because t3_i.b is not guaranteed to be unique.
+ exec_neph { SELECT b FROM t3 WHERE b IN (SELECT b FROM t3) }
+} {1 none numeric real text}
+do_test in3-4.5 {
+ execsql { CREATE UNIQUE INDEX t3_i2 ON t3(b) }
+ exec_neph { SELECT b FROM t3 WHERE b IN (SELECT b FROM t3) }
+} {0 none numeric real text}
+do_test in3-4.6 {
+ execsql { DROP INDEX t3_i2 }
+} {}
+
+# The following two test cases verify that ticket #2991 has been fixed.
+#
+do_test in3-5.1 {
+ execsql {
+ CREATE TABLE Folders(
+ folderid INTEGER PRIMARY KEY,
+ parentid INTEGER,
+ rootid INTEGER,
+ path VARCHAR(255)
+ );
+ }
+} {}
+do_test in3-5.2 {
+ catchsql {
+ DELETE FROM Folders WHERE folderid IN
+ (SELECT folderid FROM Folder WHERE path LIKE 'C:\MP3\Albums\' || '%');
+ }
+} {1 {no such table: Folder}}
+
+finish_test
diff --git a/third_party/sqlite/test/incrblob.test b/third_party/sqlite/test/incrblob.test
new file mode 100755
index 0000000..cb64eea
--- /dev/null
+++ b/third_party/sqlite/test/incrblob.test
@@ -0,0 +1,619 @@
+# 2007 May 1
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: incrblob.test,v 1.20 2008/04/24 09:49:55 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!autovacuum || !pragma || !incrblob} {
+ finish_test
+ return
+}
+
+do_test incrblob-1.1 {
+ execsql {
+ CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
+ INSERT INTO blobs VALUES('one', X'0102030405060708090A');
+ INSERT INTO blobs VALUES('two', X'0A090807060504030201');
+ }
+} {}
+
+do_test incrblob-1.2.1 {
+ set ::blob [db incrblob blobs v 1]
+ string match incrblob_* $::blob
+} {1}
+unset -nocomplain data
+do_test incrblob-1.2.2 {
+ binary scan [read $::blob] c* data
+ set data
+} {1 2 3 4 5 6 7 8 9 10}
+do_test incrblob-1.2.3 {
+ seek $::blob 0
+ puts -nonewline $::blob "1234567890"
+ flush $::blob
+} {}
+do_test incrblob-1.2.4 {
+ seek $::blob 0
+ binary scan [read $::blob] c* data
+ set data
+} {49 50 51 52 53 54 55 56 57 48}
+do_test incrblob-1.2.5 {
+ close $::blob
+} {}
+do_test incrblob-1.2.6 {
+ execsql {
+ SELECT v FROM blobs WHERE rowid = 1;
+ }
+} {1234567890}
+
+#--------------------------------------------------------------------
+# Test cases incrblob-1.3.X check that it is possible to read and write
+# regions of a blob that lie on overflow pages.
+#
+do_test incrblob-1.3.1 {
+ set ::str "[string repeat . 10000]"
+ execsql {
+ INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str);
+ }
+} {}
+
+do_test incrblob-1.3.2 {
+ set ::blob [db incrblob blobs v 3]
+ seek $::blob 8500
+ read $::blob 10
+} {..........}
+do_test incrblob-1.3.3 {
+ seek $::blob 8500
+ puts -nonewline $::blob 1234567890
+} {}
+do_test incrblob-1.3.4 {
+ seek $::blob 8496
+ read $::blob 10
+} {....123456}
+do_test incrblob-1.3.10 {
+ close $::blob
+} {}
+
+#------------------------------------------------------------------------
+# incrblob-2.*:
+#
+# Test that the following operations use ptrmap pages to reduce
+# unnecessary reads:
+#
+# * Reading near the end of a blob,
+# * Writing near the end of a blob, and
+# * SELECT a column value that is located on an overflow page.
+#
+proc nRead {db} {
+ set bt [btree_from_db $db]
+ db_enter $db
+ array set stats [btree_pager_stats $bt]
+ db_leave $db
+ return $stats(read)
+}
+proc nWrite {db} {
+ set bt [btree_from_db $db]
+ db_enter $db
+ array set stats [btree_pager_stats $bt]
+ db_leave $db
+ return $stats(write)
+}
+
+sqlite3_soft_heap_limit 0
+
+foreach AutoVacuumMode [list 0 1] {
+
+ if {$AutoVacuumMode>0} {
+ ifcapable !autovacuum {
+ break
+ }
+ }
+
+ db close
+ file delete -force test.db test.db-journal
+
+ sqlite3 db test.db
+ execsql "PRAGMA auto_vacuum = $AutoVacuumMode"
+
+ do_test incrblob-2.$AutoVacuumMode.1 {
+ set ::str [string repeat abcdefghij 2900]
+ execsql {
+ BEGIN;
+ CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER);
+ DELETE FROM blobs;
+ INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45);
+ COMMIT;
+ }
+ expr [file size test.db]/1024
+ } [expr 31 + $AutoVacuumMode]
+
+ ifcapable autovacuum {
+ do_test incrblob-2.$AutoVacuumMode.2 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+ } $AutoVacuumMode
+ }
+
+ do_test incrblob-2.$AutoVacuumMode.3 {
+ # Open and close the db to make sure the page cache is empty.
+ db close
+ sqlite3 db test.db
+
+ # Read the last 20 bytes of the blob via a blob handle.
+ set ::blob [db incrblob blobs v 1]
+ seek $::blob -20 end
+ set ::fragment [read $::blob]
+ close $::blob
+
+ # If the database is not in auto-vacuum mode, the whole of
+ # the overflow-chain must be scanned. In auto-vacuum mode,
+ # sqlite uses the ptrmap pages to avoid reading the other pages.
+ #
+ nRead db
+ } [expr $AutoVacuumMode ? 4 : 30]
+
+ do_test incrblob-2.$AutoVacuumMode.4 {
+ string range [db one {SELECT v FROM blobs}] end-19 end
+ } $::fragment
+
+ do_test incrblob-2.$AutoVacuumMode.5 {
+ # Open and close the db to make sure the page cache is empty.
+ db close
+ sqlite3 db test.db
+
+ # Write the second-to-last 20 bytes of the blob via a blob handle.
+ #
+ set ::blob [db incrblob blobs v 1]
+ seek $::blob -40 end
+ puts -nonewline $::blob "1234567890abcdefghij"
+ flush $::blob
+
+ # If the database is not in auto-vacuum mode, the whole of
+ # the overflow-chain must be scanned. In auto-vacuum mode,
+ # sqlite uses the ptrmap pages to avoid reading the other pages.
+ #
+ nRead db
+ } [expr $AutoVacuumMode ? 4 : 30]
+
+ # Pages 1 (the write-counter) and 32 (the blob data) were written.
+ do_test incrblob-2.$AutoVacuumMode.6 {
+ close $::blob
+ nWrite db
+ } 2
+
+ do_test incrblob-2.$AutoVacuumMode.7 {
+ string range [db one {SELECT v FROM blobs}] end-39 end-20
+ } "1234567890abcdefghij"
+
+ do_test incrblob-2.$AutoVacuumMode.8 {
+ # Open and close the db to make sure the page cache is empty.
+ db close
+ sqlite3 db test.db
+
+ execsql { SELECT i FROM blobs }
+ } {45}
+
+ do_test incrblob-2.$AutoVacuumMode.9 {
+ nRead db
+ } [expr $AutoVacuumMode ? 4 : 30]
+}
+sqlite3_soft_heap_limit $soft_limit
+
+#------------------------------------------------------------------------
+# incrblob-3.*:
+#
+# Test the outcome of trying to write to a read-only blob handle.
+#
+do_test incrblob-3.1 {
+ set ::blob [db incrblob -readonly blobs v 1]
+ seek $::blob -40 end
+ read $::blob 20
+} "1234567890abcdefghij"
+do_test incrblob-3.2 {
+ seek $::blob 0
+ set rc [catch {
+ puts -nonewline $::blob "helloworld"
+ } msg]
+ close $::blob
+ list $rc $msg
+} "1 {channel \"$::blob\" wasn't opened for writing}"
+
+do_test incrblob-3.3 {
+ set ::blob [db incrblob -readonly blobs v 1]
+ seek $::blob -40 end
+ read $::blob 20
+} "1234567890abcdefghij"
+do_test incrblob-3.4 {
+ set rc [catch {
+ sqlite3_blob_write $::blob 20 "qwertyuioplkjhgfds"
+ } msg]
+ list $rc $msg
+} {1 SQLITE_READONLY}
+catch {close $::blob}
+
+#------------------------------------------------------------------------
+# incrblob-4.*:
+#
+# Try a couple of error conditions:
+#
+# 4.1 - Attempt to open a row that does not exist.
+# 4.2 - Attempt to open a column that does not exist.
+# 4.3 - Attempt to open a table that does not exist.
+# 4.4 - Attempt to open a database that does not exist.
+#
+# 4.5 - Attempt to open an integer
+# 4.6 - Attempt to open a real value
+# 4.7 - Attempt to open an SQL null
+#
+# 4.8 - Attempt to open an indexed column for writing
+# 4.9 - Attempt to open an indexed column for reading (this works)
+#
+# 4.11 - Attempt to open a column of a view.
+# 4.12 - Attempt to open a column of a virtual table.
+#
+do_test incrblob-4.1 {
+ set rc [catch {
+ set ::blob [db incrblob blobs v 2]
+ } msg ]
+ list $rc $msg
+} {1 {no such rowid: 2}}
+do_test incrblob-4.2 {
+ set rc [catch {
+ set ::blob [db incrblob blobs blue 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such column: "blue"}}
+do_test incrblob-4.3 {
+ set rc [catch {
+ set ::blob [db incrblob nosuchtable blue 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such table: main.nosuchtable}}
+do_test incrblob-4.4 {
+ set rc [catch {
+ set ::blob [db incrblob nosuchdb blobs v 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such table: nosuchdb.blobs}}
+
+do_test incrblob-4.5 {
+ set rc [catch {
+ set ::blob [db incrblob blobs i 1]
+ } msg ]
+ list $rc $msg
+} {1 {cannot open value of type integer}}
+do_test incrblob-4.6 {
+ execsql {
+ INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL);
+ }
+ set rc [catch {
+ set ::blob [db incrblob blobs v 2]
+ } msg ]
+ list $rc $msg
+} {1 {cannot open value of type real}}
+do_test incrblob-4.7 {
+ set rc [catch {
+ set ::blob [db incrblob blobs i 2]
+ } msg ]
+ list $rc $msg
+} {1 {cannot open value of type null}}
+
+do_test incrblob-4.8 {
+ execsql {
+ INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world');
+ }
+ set rc [catch {
+ set ::blob [db incrblob blobs k 3]
+ } msg ]
+ list $rc $msg
+} {1 {cannot open indexed column for writing}}
+
+do_test incrblob-4.9.1 {
+ set rc [catch {
+ set ::blob [db incrblob -readonly blobs k 3]
+ } msg]
+} {0}
+do_test incrblob-4.9.2 {
+ binary scan [read $::blob] c* c
+ close $::blob
+ set c
+} {1 2 3 4 5 6 7 8 9}
+
+do_test incrblob-4.10 {
+ set ::blob [db incrblob -readonly blobs k 3]
+ set rc [catch { sqlite3_blob_read $::blob 10 100 } msg]
+ list $rc $msg
+} {1 SQLITE_ERROR}
+do_test incrblob-4.10.2 {
+ close $::blob
+} {}
+
+ifcapable view {
+ do_test incrblob-4.11 {
+ execsql { CREATE VIEW blobs_view AS SELECT k, v, i FROM blobs }
+ set rc [catch { db incrblob blobs_view v 3 } msg]
+ list $rc $msg
+ } {1 {cannot open view: blobs_view}}
+}
+ifcapable vtab {
+ register_echo_module [sqlite3_connection_pointer db]
+ do_test incrblob-4.12 {
+ execsql { CREATE VIRTUAL TABLE blobs_echo USING echo(blobs) }
+ set rc [catch { db incrblob blobs_echo v 3 } msg]
+ list $rc $msg
+ } {1 {cannot open virtual table: blobs_echo}}
+}
+
+
+#------------------------------------------------------------------------
+# incrblob-5.*:
+#
+# Test that opening a blob in an attached database works.
+#
+ifcapable attach {
+ do_test incrblob-5.1 {
+ file delete -force test2.db test2.db-journal
+ set ::size [expr [file size [info script]]]
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.files(name, text);
+ INSERT INTO aux.files VALUES('this one', zeroblob($::size));
+ }
+ set fd [db incrblob aux files text 1]
+ fconfigure $fd -translation binary
+ set fd2 [open [info script]]
+ fconfigure $fd2 -translation binary
+ puts -nonewline $fd [read $fd2]
+ close $fd
+ close $fd2
+ set ::text [db one {select text from aux.files}]
+ string length $::text
+ } [file size [info script]]
+ do_test incrblob-5.2 {
+ set fd2 [open [info script]]
+ fconfigure $fd2 -translation binary
+ set ::data [read $fd2]
+ close $fd2
+ set ::data
+ } $::text
+}
+
+# free memory
+unset -nocomplain ::data
+unset -nocomplain ::text
+
+#------------------------------------------------------------------------
+# incrblob-6.*:
+#
+# Test that opening a blob for write-access is impossible if
+# another connection has the database RESERVED lock.
+#
+# Then test that blob writes that take place inside of a
+# transaction are not visible to external connections until
+# after the transaction is commited and the blob channel
+# closed.
+#
+sqlite3_soft_heap_limit 0
+do_test incrblob-6.1 {
+ sqlite3 db2 test.db
+ execsql {
+ BEGIN;
+ INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
+ } db2
+} {}
+do_test incrblob-6.2 {
+ execsql {
+ SELECT rowid FROM blobs
+ }
+} {1 2 3}
+do_test incrblob-6.3 {
+ set rc [catch {
+ db incrblob blobs v 1
+ } msg]
+ list $rc $msg
+} {1 {database is locked}}
+do_test incrblob-6.4 {
+ set rc [catch {
+ db incrblob blobs v 3
+ } msg]
+ list $rc $msg
+} {1 {database is locked}}
+do_test incrblob-6.5 {
+ set ::blob [db incrblob -readonly blobs v 3]
+ read $::blob
+} {hello}
+do_test incrblob-6.6 {
+ close $::blob
+} {}
+
+do_test incrblob-6.7 {
+ set ::blob [db2 incrblob blobs i 4]
+ gets $::blob
+} {connection}
+do_test incrblob-6.8 {
+ tell $::blob
+} {10}
+do_test incrblob-6.9 {
+ seek $::blob 0
+ puts -nonewline $::blob "invocation"
+ flush $::blob
+} {}
+
+# At this point rollback or commit should be illegal (because
+# there is an open blob channel).
+do_test incrblob-6.10 {
+ catchsql {
+ ROLLBACK;
+ } db2
+} {1 {cannot rollback transaction - SQL statements in progress}}
+do_test incrblob-6.11 {
+ catchsql {
+ COMMIT;
+ } db2
+} {1 {cannot commit transaction - SQL statements in progress}}
+
+do_test incrblob-6.12 {
+ execsql {
+ SELECT * FROM blobs WHERE rowid = 4;
+ }
+} {}
+do_test incrblob-6.13 {
+ close $::blob
+ execsql {
+ COMMIT;
+ } db2
+} {}
+do_test incrblob-6.14 {
+ execsql {
+ SELECT * FROM blobs WHERE rowid = 4;
+ }
+} {a different invocation}
+db2 close
+sqlite3_soft_heap_limit $soft_limit
+
+#-----------------------------------------------------------------------
+# The following tests verify the behaviour of the incremental IO
+# APIs in the following cases:
+#
+# 7.1 A row that containing an open blob is modified.
+#
+# 7.2 A CREATE TABLE requires that an overflow page that is part
+# of an open blob is moved.
+#
+# 7.3 An INCREMENTAL VACUUM moves an overflow page that is part
+# of an open blob.
+#
+# In the first case above, correct behaviour is for all subsequent
+# read/write operations on the blob-handle to return SQLITE_ABORT.
+# More accurately, blob-handles are invalidated whenever the table
+# they belong to is written to.
+#
+# The second two cases have no external effect. They are testing
+# that the internal cache of overflow page numbers is correctly
+# invalidated.
+#
+do_test incrblob-7.1.0 {
+ execsql {
+ BEGIN;
+ DROP TABLE blobs;
+ CREATE TABLE t1 (a, b, c, d BLOB);
+ INSERT INTO t1(a, b, c, d) VALUES(1, 2, 3, 4);
+ COMMIT;
+ }
+} {}
+
+foreach {tn arg} {1 "" 2 -readonly} {
+
+ execsql {
+ UPDATE t1 SET d = zeroblob(10000);
+ }
+
+ do_test incrblob-7.1.$tn.1 {
+ set ::b [eval db incrblob $arg t1 d 1]
+ binary scan [sqlite3_blob_read $::b 5000 5] c* c
+ set c
+ } {0 0 0 0 0}
+ do_test incrblob-7.1.$tn.2 {
+ execsql {
+ UPDATE t1 SET d = 15;
+ }
+ } {}
+ do_test incrblob-7.1.$tn.3 {
+ set rc [catch { sqlite3_blob_read $::b 5000 5 } msg]
+ list $rc $msg
+ } {1 SQLITE_ABORT}
+ do_test incrblob-7.1.$tn.4 {
+ execsql {
+ SELECT d FROM t1;
+ }
+ } {15}
+ do_test incrblob-7.1.$tn.5 {
+ set rc [catch { close $::b } msg]
+ list $rc $msg
+ } {0 {}}
+ do_test incrblob-7.1.$tn.6 {
+ execsql {
+ SELECT d FROM t1;
+ }
+ } {15}
+
+}
+
+set fd [open [info script]]
+fconfigure $fd -translation binary
+set ::data [read $fd 14000]
+close $fd
+
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+do_test incrblob-7.2.1 {
+ execsql {
+ PRAGMA auto_vacuum = "incremental";
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b); -- root@page3
+ INSERT INTO t1 VALUES(123, $::data);
+ }
+ set ::b [db incrblob -readonly t1 b 123]
+ read $::b
+} $::data
+do_test incrblob-7.2.2 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b); -- root@page4
+ }
+ seek $::b 0
+ read $::b
+} $::data
+do_test incrblob-7.2.3 {
+ close $::b
+ execsql {
+ SELECT rootpage FROM sqlite_master;
+ }
+} {3 4}
+
+set ::otherdata "[string range $::data 0 1000][string range $::data 1001 end]"
+do_test incrblob-7.3.1 {
+ execsql {
+ INSERT INTO t2 VALUES(456, $::otherdata);
+ }
+ set ::b [db incrblob -readonly t2 b 456]
+ read $::b
+} $::otherdata
+do_test incrblob-7.3.2 {
+ expr [file size test.db]/1024
+} 30
+do_test incrblob-7.3.3 {
+ execsql {
+ DELETE FROM t1 WHERE a = 123;
+ PRAGMA INCREMENTAL_VACUUM(0);
+ }
+ seek $::b 0
+ read $::b
+} $::otherdata
+
+# Attempt to write on a read-only blob. Make sure the error code
+# gets set. Ticket #2464.
+#
+do_test incrblob-7.4 {
+ set rc [catch {sqlite3_blob_write $::b 10 HELLO} msg]
+ lappend rc $msg
+} {1 SQLITE_READONLY}
+do_test incrblob-7.5 {
+ sqlite3_errcode db
+} {SQLITE_READONLY}
+do_test incrblob-7.6 {
+ sqlite3_errmsg db
+} {attempt to write a readonly database}
+
+finish_test
diff --git a/third_party/sqlite/test/incrblob2.test b/third_party/sqlite/test/incrblob2.test
new file mode 100755
index 0000000..90295e8
--- /dev/null
+++ b/third_party/sqlite/test/incrblob2.test
@@ -0,0 +1,409 @@
+# 2008 June 9
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test that it is possible to have two open blob handles on a single
+# blob object.
+#
+# $Id: incrblob2.test,v 1.8 2008/06/28 15:33:26 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!autovacuum || !pragma || !incrblob} {
+ finish_test
+ return
+}
+
+do_test incrblob2-1.0 {
+ execsql {
+ CREATE TABLE blobs(id INTEGER PRIMARY KEY, data BLOB);
+ INSERT INTO blobs VALUES(NULL, zeroblob(5000));
+ INSERT INTO blobs VALUES(NULL, zeroblob(5000));
+ INSERT INTO blobs VALUES(NULL, zeroblob(5000));
+ INSERT INTO blobs VALUES(NULL, zeroblob(5000));
+ }
+} {}
+
+foreach iOffset [list 0 256 4094] {
+ do_test incrblob2-1.$iOffset.1 {
+ set fd [db incrblob blobs data 1]
+ puts $fd "[string repeat x $iOffset]SQLite version 3.6.0"
+ close $fd
+ } {}
+
+ do_test incrblob2-1.$iOffset.2 {
+ set fd1 [db incrblob blobs data 1]
+ set fd2 [db incrblob blobs data 1]
+ fconfigure $fd1 -buffering none
+ fconfigure $fd2 -buffering none
+ if {$iOffset != 0} {
+ seek $fd2 $iOffset start
+ seek $fd1 $iOffset start
+ }
+ read $fd1 6
+ } {SQLite}
+
+ do_test incrblob2-1.$iOffset.3 {
+ read $fd2 6
+ } {SQLite}
+
+ do_test incrblob2-1.$iOffset.4 {
+ seek $fd2 $iOffset start
+ seek $fd1 $iOffset start
+ puts -nonewline $fd2 "etiLQS"
+ } {}
+
+
+ do_test incrblob2-1.$iOffset.5 {
+ seek $fd1 $iOffset start
+ read $fd1 6
+ } {etiLQS}
+
+ do_test incrblob2-1.$iOffset.6 {
+ seek $fd2 $iOffset start
+ read $fd2 6
+ } {etiLQS}
+
+ do_test incrblob2-1.$iOffset.7 {
+ seek $fd1 $iOffset start
+ read $fd1 6
+ } {etiLQS}
+
+ do_test incrblob2-1.$iOffset.8 {
+ close $fd1
+ close $fd2
+ } {}
+}
+
+#--------------------------------------------------------------------------
+
+foreach iOffset [list 0 256 4094] {
+
+ do_test incrblob2-2.$iOffset.1 {
+ set fd1 [db incrblob blobs data 1]
+ seek $fd1 [expr $iOffset - 5000] end
+ fconfigure $fd1 -buffering none
+
+ set fd2 [db incrblob blobs data 1]
+ seek $fd2 [expr $iOffset - 5000] end
+ fconfigure $fd2 -buffering none
+
+ puts -nonewline $fd1 "123456"
+ } {}
+
+ do_test incrblob2-2.$iOffset.2 {
+ read $fd2 6
+ } {123456}
+
+ do_test incrblob2-2.$iOffset.3 {
+ close $fd1
+ close $fd2
+ } {}
+}
+
+do_test incrblob2-3.1 {
+ set fd1 [db incrblob blobs data 1]
+ fconfigure $fd1 -buffering none
+} {}
+do_test incrblob2-3.2 {
+ execsql {
+ INSERT INTO blobs VALUES(5, zeroblob(10240));
+ }
+} {}
+do_test incrblob2-3.3 {
+ set rc [catch { read $fd1 6 } msg]
+ list $rc $msg
+} {0 123456}
+do_test incrblob2-3.4 {
+ close $fd1
+} {}
+
+#--------------------------------------------------------------------------
+# The following tests - incrblob2-4.* - test that blob handles are
+# invalidated at the correct times.
+#
+do_test incrblob2-4.1 {
+ unset -nocomplain data
+ db eval BEGIN
+ db eval { CREATE TABLE t1(id INTEGER PRIMARY KEY, data BLOB); }
+ for {set ii 1} {$ii < 100} {incr ii} {
+ set data [string repeat "blob$ii" 500]
+ db eval { INSERT INTO t1 VALUES($ii, $data) }
+ }
+ db eval COMMIT
+} {}
+
+proc aborted_handles {} {
+ global handles
+
+ set aborted {}
+ for {set ii 1} {$ii < 100} {incr ii} {
+ set str "blob$ii"
+ set nByte [string length $str]
+ set iOffset [expr $nByte * $ii * 2]
+
+ set rc [catch {sqlite3_blob_read $handles($ii) $iOffset $nByte} msg]
+ if {$rc && $msg eq "SQLITE_ABORT"} {
+ lappend aborted $ii
+ } else {
+ if {$rc || $msg ne $str} {
+ error "blob $ii: $msg"
+ }
+ }
+ }
+ set aborted
+}
+
+do_test incrblob2-4.2 {
+ for {set ii 1} {$ii < 100} {incr ii} {
+ set handles($ii) [db incrblob t1 data $ii]
+ }
+ aborted_handles
+} {}
+
+# Update row 3. This should abort handle 3 but leave all others untouched.
+#
+do_test incrblob2-4.3 {
+ db eval {UPDATE t1 SET data = data || '' WHERE id = 3}
+ aborted_handles
+} {3}
+
+# Test that a write to handle 3 also returns SQLITE_ABORT.
+#
+do_test incrblob2-4.3.1 {
+ set rc [catch {sqlite3_blob_write $::handles(3) 10 HELLO} msg]
+ list $rc $msg
+} {1 SQLITE_ABORT}
+
+# Delete row 14. This should abort handle 6 but leave all others untouched.
+#
+do_test incrblob2-4.4 {
+ db eval {DELETE FROM t1 WHERE id = 14}
+ aborted_handles
+} {3 14}
+
+# Change the rowid of row 15 to 102. Should abort handle 15.
+#
+do_test incrblob2-4.5 {
+ db eval {UPDATE t1 SET id = 102 WHERE id = 15}
+ aborted_handles
+} {3 14 15}
+
+# Clobber row 92 using INSERT OR REPLACE.
+#
+do_test incrblob2-4.6 {
+ db eval {INSERT OR REPLACE INTO t1 VALUES(92, zeroblob(1000))}
+ aborted_handles
+} {3 14 15 92}
+
+# Clobber row 65 using UPDATE OR REPLACE on row 35. This should abort
+# handles 35 and 65.
+#
+do_test incrblob2-4.7 {
+ db eval {UPDATE OR REPLACE t1 SET id = 65 WHERE id = 35}
+ aborted_handles
+} {3 14 15 35 65 92}
+
+# Insert a couple of new rows. This should not invalidate any handles.
+#
+do_test incrblob2-4.9 {
+ db eval {INSERT INTO t1 SELECT NULL, data FROM t1}
+ aborted_handles
+} {3 14 15 35 65 92}
+
+# Delete all rows from 1 to 25. This should abort all handles up to 25.
+#
+do_test incrblob2-4.9 {
+ db eval {DELETE FROM t1 WHERE id >=1 AND id <= 25}
+ aborted_handles
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 35 65 92}
+
+# Delete the whole table (this will use sqlite3BtreeClearTable()). All handles
+# should now be aborted.
+#
+do_test incrblob2-4.10 {
+ db eval {DELETE FROM t1}
+ aborted_handles
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99}
+
+do_test incrblob2-4.1.X {
+ for {set ii 1} {$ii < 100} {incr ii} {
+ close $handles($ii)
+ }
+} {}
+
+#--------------------------------------------------------------------------
+# The following tests - incrblob2-5.* - test that in shared cache an open
+# blob handle counts as a read-lock on its table.
+#
+ifcapable shared_cache {
+ db close
+ set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+ do_test incrblob2-5.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+
+ execsql {
+ INSERT INTO t1 VALUES(1, 'abcde');
+ }
+ } {}
+
+ do_test incrblob2-5.2 {
+ catchsql { INSERT INTO t1 VALUES(2, 'fghij') } db2
+ } {0 {}}
+
+ do_test incrblob2-5.3 {
+ set blob [db incrblob t1 data 1]
+ catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2
+ } {1 {database is locked}}
+
+ do_test incrblob2-5.4 {
+ close $blob
+ execsql BEGIN db2
+ catchsql { INSERT INTO t1 VALUES(4, 'pqrst') } db2
+ } {0 {}}
+
+ do_test incrblob2-5.5 {
+ set blob [db incrblob -readonly t1 data 1]
+ catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2
+ } {1 {database table is locked}}
+
+ do_test incrblob2-5.6 {
+ close $blob
+ catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2
+ } {0 {}}
+
+ db2 close
+ db close
+ sqlite3_enable_shared_cache $::enable_shared_cache
+}
+
+#--------------------------------------------------------------------------
+# The following tests - incrblob2-6.* - test a specific scenario that might
+# be causing an error.
+#
+sqlite3 db test.db
+do_test incrblob2-6.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1, zeroblob(100));
+ }
+
+ set rdHandle [db incrblob -readonly t1 data 1]
+ set wrHandle [db incrblob t1 data 1]
+
+ sqlite3_blob_read $rdHandle 0 100
+
+ sqlite3_blob_write $wrHandle 0 ABCDEF
+
+ close $wrHandle
+ close $rdHandle
+} {}
+
+do_test incrblob2-6.2 {
+ set rdHandle [db incrblob -readonly t1 data 1]
+ sqlite3_blob_read $rdHandle 0 2
+} {AB}
+
+do_test incrblob2-6.3 {
+ set wrHandle [db incrblob t1 data 1]
+ sqlite3_blob_write $wrHandle 0 ZZZZZZZZZZ
+ sqlite3_blob_read $rdHandle 2 4
+} {ZZZZ}
+
+do_test incrblob2-6.4 {
+ close $wrHandle
+ close $rdHandle
+} {}
+
+sqlite3_memory_highwater 1
+do_test incrblob2-7.1 {
+ db eval {
+ CREATE TABLE t2(B BLOB);
+ INSERT INTO t2 VALUES(zeroblob(10 * 1024 * 1024));
+ }
+ expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)}
+} {1}
+
+do_test incrblob2-7.2 {
+ set h [db incrblob t2 B 1]
+ expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)}
+} {1}
+
+do_test incrblob2-7.3 {
+ seek $h 0 end
+ tell $h
+} [expr 10 * 1024 * 1024]
+
+do_test incrblob2-7.4 {
+ expr {[sqlite3_memory_highwater]<(5 * 1024 * 1024)}
+} {1}
+
+do_test incrblob2-7.5 {
+ close $h
+} {}
+
+#---------------------------------------------------------------------------
+# The following tests, incrblob2-8.*, test that nothing terrible happens
+# when a statement transaction is rolled back while there are open
+# incremental-blob handles. At one point an assert() was failing when
+# this was attempted.
+#
+do_test incrblob2-8.1 {
+ execsql BEGIN
+ set h [db incrblob t2 B 1]
+ set rc [catch {
+ db eval {SELECT * FROM t2} { execsql "DROP TABLE t2" }
+ } msg]
+ list $rc $msg
+} {1 {database table is locked}}
+do_test incrblob2-8.2 {
+ close $h
+ execsql COMMIT
+} {}
+do_test incrblob2-8.3 {
+ execsql {
+ CREATE TABLE t3(a INTEGER UNIQUE, b TEXT);
+ INSERT INTO t3 VALUES(1, 'aaaaaaaaaaaaaaaaaaaa');
+ INSERT INTO t3 VALUES(2, 'bbbbbbbbbbbbbbbbbbbb');
+ INSERT INTO t3 VALUES(3, 'cccccccccccccccccccc');
+ INSERT INTO t3 VALUES(4, 'dddddddddddddddddddd');
+ INSERT INTO t3 VALUES(5, 'eeeeeeeeeeeeeeeeeeee');
+ }
+} {}
+do_test incrblob2-8.4 {
+ execsql BEGIN
+ set h [db incrblob t3 b 3]
+ sqlite3_blob_read $h 0 20
+} {cccccccccccccccccccc}
+do_test incrblob2-8.5 {
+ catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
+} {1 {column a is not unique}}
+do_test incrblob2-8.6 {
+ catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
+} {1 {column a is not unique}}
+do_test incrblob2-8.7 {
+ sqlite3_blob_read $h 0 20
+} {cccccccccccccccccccc}
+do_test incrblob2-8.8 {
+ catchsql {UPDATE t3 SET a = 6 WHERE a = 3 OR a = 5}
+} {1 {column a is not unique}}
+do_test incrblob2-8.9 {
+ set rc [catch {sqlite3_blob_read $h 0 20} msg]
+ list $rc $msg
+} {1 SQLITE_ABORT}
+do_test incrblob2-8.X {
+ close $h
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/incrblob_err.test b/third_party/sqlite/test/incrblob_err.test
new file mode 100755
index 0000000..b4c2587
--- /dev/null
+++ b/third_party/sqlite/test/incrblob_err.test
@@ -0,0 +1,137 @@
+# 2007 May 1
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: incrblob_err.test,v 1.14 2008/07/18 17:16:27 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!incrblob || !memdebug || !tclvar} {
+ finish_test
+ return
+}
+
+source $testdir/malloc_common.tcl
+
+unset -nocomplain ::fd ::data
+set ::fd [open [info script]]
+set ::data [read $::fd]
+close $::fd
+
+do_malloc_test 1 -tclprep {
+ set bytes [file size [info script]]
+ execsql {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, zeroblob($::bytes));
+ }
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ set rc [catch {puts -nonewline $::blob $::data}]
+ if {$rc} { error "out of memory" }
+}
+
+do_malloc_test 2 -tclprep {
+ execsql {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, $::data);
+ }
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ set rc [catch {set ::r [read $::blob]}]
+ if {$rc} {
+ error "out of memory"
+ } elseif {$::r ne $::data} {
+ error "Bad data read..."
+ }
+}
+
+do_malloc_test 3 -tclprep {
+ execsql {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, $::data);
+ }
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ set rc [catch {set ::r [read $::blob]}]
+ if {$rc} {
+ error "out of memory"
+ } elseif {$::r ne $::data} {
+ error "Bad data read..."
+ }
+ set rc [catch {close $::blob}]
+ if {$rc} {
+ error "out of memory"
+ }
+}
+
+
+do_ioerr_test incrblob_err-4 -cksum 1 -sqlprep {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, $::data);
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ read $::blob
+}
+
+do_ioerr_test incrblob_err-5 -cksum 1 -sqlprep {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, zeroblob(length(CAST($::data AS BLOB))));
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ puts -nonewline $::blob $::data
+ close $::blob
+}
+
+do_ioerr_test incrblob_err-6 -cksum 1 -sqlprep {
+ CREATE TABLE blobs(k, v BLOB);
+ INSERT INTO blobs VALUES(1, $::data || $::data || $::data);
+} -tclbody {
+ set ::blob [db incrblob blobs v 1]
+ seek $::blob -20 end
+ puts -nonewline $::blob "12345678900987654321"
+ close $::blob
+}
+
+do_ioerr_test incrblob_err-7 -cksum 1 -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB);
+ INSERT INTO blobs VALUES(1, zeroblob(500 * 1020));
+} -tclbody {
+ # Read some data from the end of the large blob inserted into table
+ # "blobs". This forces the IO error to occur while reading a pointer
+ # map page for the purposes of seeking to the end of the blob.
+ #
+ sqlite3 db2 test.db
+ set ::blob [db2 incrblob blobs v 1]
+ sqlite3_blob_read $::blob [expr 500*1020-20] 20
+ close $::blob
+}
+catch {db2 close}
+
+do_ioerr_test incrblob_err-8 -cksum 1 -sqlprep {
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB);
+ INSERT INTO blobs VALUES(1, zeroblob(500 * 1020));
+} -tclbody {
+ # Read some data from the end of the large blob inserted into table
+ # "blobs". This forces the IO error to occur while reading a pointer
+ # map page for the purposes of seeking to the end of the blob.
+ #
+ sqlite3 db2 test.db
+ set ::blob [db2 incrblob blobs v 1]
+ sqlite3_blob_write $::blob [expr 500*1020-20] 12345678900987654321
+ close $::blob
+}
+
+catch {db2 close}
+
+finish_test
diff --git a/third_party/sqlite/test/incrvacuum.test b/third_party/sqlite/test/incrvacuum.test
new file mode 100755
index 0000000..04ea03d
--- /dev/null
+++ b/third_party/sqlite/test/incrvacuum.test
@@ -0,0 +1,737 @@
+# 2007 April 26
+#
+# 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 file is testing the incremental vacuum feature.
+#
+# Note: There are also some tests for incremental vacuum and IO
+# errors in incrvacuum_ioerr.test.
+#
+# $Id: incrvacuum.test,v 1.19 2008/07/30 17:28:04 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum || !pragma} {
+ finish_test
+ return
+}
+
+#---------------------------------------------------------------------
+# Test the pragma on an empty database.
+#
+do_test incrvacuum-1.1 {
+ execsql {
+ pragma auto_vacuum;
+ }
+} $sqlite_options(default_autovacuum)
+do_test incrvacuum-1.2.0 {
+ # File size is sometimes 1 instead of 0 due to the hack we put in
+ # to work around ticket #3260. Search for comments on #3260 in
+ # os_unix.c.
+ expr {[file size test.db] > 1}
+} {0}
+do_test incrvacuum-1.2 {
+ # This command will create the database.
+ execsql {
+ pragma auto_vacuum = 'full';
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-1.2.1 {
+ expr {[file size test.db] > 0}
+} {1}
+do_test incrvacuum-1.3 {
+ execsql {
+ pragma auto_vacuum = 'incremental';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.4 {
+ # In this case the invalid value is ignored and the auto_vacuum
+ # setting remains unchanged.
+ execsql {
+ pragma auto_vacuum = 'invalid';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.5 {
+ execsql {
+ pragma auto_vacuum = 1;
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-1.6 {
+ execsql {
+ pragma auto_vacuum = '2';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-1.7 {
+ # Invalid value. auto_vacuum setting remains unchanged.
+ execsql {
+ pragma auto_vacuum = 5;
+ pragma auto_vacuum;
+ }
+} {2}
+
+#---------------------------------------------------------------------
+# Test the pragma on a non-empty database. It is possible to toggle
+# the connection between "full" and "incremental" mode, but not to
+# change from either of these to "none", or from "none" to "full" or
+# "incremental".
+#
+do_test incrvacuum-2.1 {
+ execsql {
+ pragma auto_vacuum = 1;
+ CREATE TABLE abc(a, b, c);
+ }
+} {}
+do_test incrvacuum-2.2 {
+ execsql {
+ pragma auto_vacuum = 'none';
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-2.2.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-2.3 {
+ execsql {
+ pragma auto_vacuum = 'incremental';
+ pragma auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-2.4 {
+ execsql {
+ pragma auto_vacuum = 'full';
+ pragma auto_vacuum;
+ }
+} {1}
+
+#---------------------------------------------------------------------
+# Test that when the auto_vacuum mode is "incremental", the database
+# does not shrink when pages are removed from it. But it does if
+# the mode is set to "full".
+#
+do_test incrvacuum-3.1 {
+ execsql {
+ pragma auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-3.2 {
+ set ::str [string repeat 1234567890 110]
+ execsql {
+ PRAGMA auto_vacuum = 2;
+ BEGIN;
+ CREATE TABLE tbl2(str);
+ INSERT INTO tbl2 VALUES($::str);
+ COMMIT;
+ }
+ # 5 pages:
+ #
+ # 1 -> database header
+ # 2 -> first back-pointer page
+ # 3 -> table abc
+ # 4 -> table tbl2
+ # 5 -> table tbl2 overflow page.
+ #
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-3.3 {
+ execsql {
+ DROP TABLE abc;
+ DELETE FROM tbl2;
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-3.4 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ INSERT INTO tbl2 VALUES('hello world');
+ }
+ expr {[file size test.db] / 1024}
+} {3}
+
+#---------------------------------------------------------------------
+# Try to run a very simple incremental vacuum. Also verify that
+# PRAGMA incremental_vacuum is a harmless no-op against a database that
+# does not support auto-vacuum.
+#
+do_test incrvacuum-4.1 {
+ set ::str [string repeat 1234567890 110]
+ execsql {
+ PRAGMA auto_vacuum = 2;
+ INSERT INTO tbl2 VALUES($::str);
+ CREATE TABLE tbl1(a, b, c);
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-4.2 {
+ execsql {
+ DELETE FROM tbl2;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+} {5}
+do_test incrvacuum-4.3 {
+ set ::nStep 0
+ db eval {pragma incremental_vacuum(10)} {
+ incr ::nStep
+ }
+ list [expr {[file size test.db] / 1024}] $::nStep
+} {3 2}
+
+#---------------------------------------------------------------------
+# The following tests - incrvacuum-5.* - test incremental vacuum
+# from within a transaction.
+#
+do_test incrvacuum-5.1.1 {
+ expr {[file size test.db] / 1024}
+} {3}
+do_test incrvacuum-5.1.2 {
+ execsql {
+ BEGIN;
+ DROP TABLE tbl2;
+ PRAGMA incremental_vacuum;
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {1}
+
+do_test incrvacuum-5.2.1 {
+ set ::str [string repeat abcdefghij 110]
+ execsql {
+ BEGIN;
+ CREATE TABLE tbl1(a);
+ INSERT INTO tbl1 VALUES($::str);
+ PRAGMA incremental_vacuum; -- this is a no-op.
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {4}
+do_test incrvacuum-5.2.2 {
+ set ::str [string repeat abcdefghij 110]
+ execsql {
+ BEGIN;
+ INSERT INTO tbl1 VALUES($::str);
+ INSERT INTO tbl1 SELECT * FROM tbl1;
+ DELETE FROM tbl1 WHERE oid%2; -- Put 2 overflow pages on free-list.
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {7}
+do_test incrvacuum-5.2.3 {
+ execsql {
+ BEGIN;
+ PRAGMA incremental_vacuum; -- Vacuum up the two pages.
+ CREATE TABLE tbl2(b); -- Use one free page as a table root.
+ INSERT INTO tbl2 VALUES('a nice string');
+ COMMIT;
+ }
+ expr {[file size test.db] / 1024}
+} {6}
+do_test incrvacuum-5.2.4 {
+ execsql {
+ SELECT * FROM tbl2;
+ }
+} {{a nice string}}
+do_test incrvacuum-5.2.5 {
+ execsql {
+ DROP TABLE tbl1;
+ DROP TABLE tbl2;
+ PRAGMA incremental_vacuum;
+ }
+ expr {[file size test.db] / 1024}
+} {1}
+
+
+# Test cases incrvacuum-5.3.* use the following list as input data.
+# Two new databases are opened, one with incremental vacuum enabled,
+# the other with no auto-vacuum completely disabled. After executing
+# each element of the following list on both databases, test that
+# the integrity-check passes and the contents of each are identical.
+#
+set TestScriptList [list {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+ CREATE INDEX t1_i ON t1(a);
+ CREATE INDEX t2_i ON t2(a);
+} {
+ INSERT INTO t1 VALUES($::str1, $::str2);
+ INSERT INTO t1 VALUES($::str1||$::str2, $::str2||$::str1);
+ INSERT INTO t2 SELECT b, a FROM t1;
+ INSERT INTO t2 SELECT a, b FROM t1;
+ INSERT INTO t1 SELECT b, a FROM t2;
+ UPDATE t2 SET b = '';
+ PRAGMA incremental_vacuum;
+} {
+ UPDATE t2 SET b = (SELECT b FROM t1 WHERE t1.oid = t2.oid);
+ PRAGMA incremental_vacuum;
+} {
+ CREATE TABLE t3(a, b);
+ INSERT INTO t3 SELECT * FROM t2;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum;
+} {
+ CREATE INDEX t3_i ON t3(a);
+ COMMIT;
+} {
+ BEGIN;
+ DROP INDEX t3_i;
+ PRAGMA incremental_vacuum;
+ INSERT INTO t3 VALUES('hello', 'world');
+ ROLLBACK;
+} {
+ INSERT INTO t3 VALUES('hello', 'world');
+}
+]
+
+# If this build omits subqueries, step 2 in the above list will not
+# work. Replace it with "" in this case.
+#
+ifcapable !subquery { lset TestScriptList 2 "" }
+
+# Compare the contents of databases $A and $B.
+#
+proc compare_dbs {A B tname} {
+ set tbl_list [execsql {
+ SELECT tbl_name FROM sqlite_master WHERE type = 'table'
+ } $A]
+
+ do_test ${tname}.1 [subst {
+ execsql {
+ SELECT tbl_name FROM sqlite_master WHERE type = 'table'
+ } $B
+ }] $tbl_list
+
+ set tn 1
+ foreach tbl $tbl_list {
+ set control [execsql "SELECT * FROM $tbl" $A]
+ do_test ${tname}.[incr tn] [subst {
+ execsql "SELECT * FROM $tbl" $B
+ }] $control
+ }
+}
+
+set ::str1 [string repeat abcdefghij 130]
+set ::str2 [string repeat 1234567890 105]
+
+file delete -force test1.db test1.db-journal test2.db test2.db-journal
+sqlite3 db1 test1.db
+sqlite3 db2 test2.db
+execsql { PRAGMA auto_vacuum = 'none' } db1
+execsql { PRAGMA auto_vacuum = 'incremental' } db2
+
+set tn 1
+foreach sql $::TestScriptList {
+ execsql $sql db1
+ execsql $sql db2
+
+ compare_dbs db1 db2 incrvacuum-5.3.${tn}
+ do_test incrvacuum-5.3.${tn}.integrity1 {
+ execsql { PRAGMA integrity_check; } db1
+ } {ok}
+ do_test incrvacuum-5.3.${tn}.integrity2 {
+ execsql { PRAGMA integrity_check; } db2
+ } {ok}
+ incr tn
+}
+db1 close
+db2 close
+#
+# End of test cases 5.3.*
+
+#---------------------------------------------------------------------
+# The following tests - incrvacuum-6.* - test running incremental
+# vacuum while another statement (a read) is being executed.
+#
+for {set jj 0} {$jj < 10} {incr jj} {
+ # Build some test data. Two tables are created in an empty
+ # database. tbl1 data is a contiguous block starting at page 5 (pages
+ # 3 and 4 are the table roots). tbl2 is a contiguous block starting
+ # right after tbl1.
+ #
+ # Then drop tbl1 so that when an incr vacuum is run the pages
+ # of tbl2 have to be moved to fill the gap.
+ #
+ do_test incrvacuum-6.${jj}.1 {
+ execsql {
+ DROP TABLE IF EXISTS tbl1;
+ DROP TABLE IF EXISTS tbl2;
+ PRAGMA incremental_vacuum;
+ CREATE TABLE tbl1(a, b);
+ CREATE TABLE tbl2(a, b);
+ BEGIN;
+ }
+ for {set ii 0} {$ii < 1000} {incr ii} {
+ db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
+ }
+ execsql {
+ INSERT INTO tbl2 SELECT * FROM tbl1;
+ COMMIT;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+ } {36}
+
+ # Run a linear scan query on tbl2. After reading ($jj*100) rows,
+ # run the incremental vacuum to shrink the database.
+ #
+ do_test incrvacuum-6.${jj}.2 {
+ set ::nRow 0
+ db eval {SELECT a FROM tbl2} {} {
+ if {$a == [expr $jj*100]} {
+ db eval {PRAGMA incremental_vacuum}
+ }
+ incr ::nRow
+ }
+ list [expr {[file size test.db] / 1024}] $nRow
+ } {19 1000}
+}
+
+#---------------------------------------------------------------------
+# This test - incrvacuum-7.* - is to check that the database can be
+# written in the middle of an incremental vacuum.
+#
+set ::iWrite 1
+while 1 {
+ do_test incrvacuum-7.${::iWrite}.1 {
+ execsql {
+ DROP TABLE IF EXISTS tbl1;
+ DROP TABLE IF EXISTS tbl2;
+ PRAGMA incremental_vacuum;
+ CREATE TABLE tbl1(a, b);
+ CREATE TABLE tbl2(a, b);
+ BEGIN;
+ }
+ for {set ii 0} {$ii < 1000} {incr ii} {
+ db eval {INSERT INTO tbl1 VALUES($ii, $ii || $ii)}
+ }
+ execsql {
+ INSERT INTO tbl2 SELECT * FROM tbl1;
+ COMMIT;
+ DROP TABLE tbl1;
+ }
+ expr {[file size test.db] / 1024}
+ } {36}
+
+ do_test incrvacuum-7.${::iWrite}.2 {
+ set ::nRow 0
+ db eval {PRAGMA incremental_vacuum} {
+ incr ::nRow
+ if {$::nRow == $::iWrite} {
+ db eval {
+ CREATE TABLE tbl1(a, b);
+ INSERT INTO tbl1 VALUES('hello', 'world');
+ }
+ }
+ }
+ list [expr {[file size test.db] / 1024}]
+ } {20}
+
+ do_test incrvacuum-7.${::iWrite}.3 {
+ execsql {
+ SELECT * FROM tbl1;
+ }
+ } {hello world}
+
+ if {$::nRow == $::iWrite} break
+ incr ::iWrite
+}
+
+#---------------------------------------------------------------------
+# This test - incrvacuum-8.* - is to check that nothing goes wrong
+# with an incremental-vacuum if it is the first statement executed
+# after an existing database is opened.
+#
+# At one point, this would always return SQLITE_SCHEMA (which
+# causes an infinite loop in tclsqlite.c if using the Tcl interface).
+#
+do_test incrvacuum-8.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA incremental_vacuum(50);
+ }
+} {}
+
+#---------------------------------------------------------------------
+# At one point this test case was causing an assert() to fail.
+#
+do_test incrvacuum-9.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+
+ execsql {
+ PRAGMA auto_vacuum = 'incremental';
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+ INSERT INTO t2 VALUES(randstr(500,500),randstr(500,500),randstr(500,500));
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ INSERT INTO t1 SELECT a||a, b||b, c||c FROM t1;
+ }
+} {}
+
+do_test incrvacuum-9.2 {
+ execsql {
+ PRAGMA synchronous = 'OFF';
+ BEGIN;
+ UPDATE t1 SET a = a, b = b, c = c;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum(10);
+ ROLLBACK;
+ }
+} {}
+
+do_test incrvacuum-9.3 {
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ UPDATE t1 SET a = a, b = b, c = c;
+ DROP TABLE t2;
+ PRAGMA incremental_vacuum(10);
+ ROLLBACK;
+ }
+} {}
+
+#---------------------------------------------------------------------
+# Test that the parameter to the incremental_vacuum pragma works. That
+# is, if the user executes "PRAGMA incremental_vacuum(N)", at most
+# N pages are vacuumed.
+#
+do_test incrvacuum-10.1 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }
+ expr [file size test.db] / 1024
+} {29}
+
+do_test incrvacuum-10.2 {
+ execsql {
+ PRAGMA incremental_vacuum(1);
+ }
+ expr [file size test.db] / 1024
+} {28}
+
+do_test incrvacuum-10.3 {
+ execsql {
+ PRAGMA incremental_vacuum(5);
+ }
+ expr [file size test.db] / 1024
+} {23}
+
+do_test incrvacuum-10.4 {
+ execsql {
+ PRAGMA incremental_vacuum('1');
+ }
+ expr [file size test.db] / 1024
+} {22}
+
+do_test incrvacuum-10.5 {
+ execsql {
+ PRAGMA incremental_vacuum("+3");
+ }
+ expr [file size test.db] / 1024
+} {19}
+
+do_test incrvacuum-10.6 {
+ execsql {
+ PRAGMA incremental_vacuum = 1;
+ }
+ expr [file size test.db] / 1024
+} {18}
+
+do_test incrvacuum-10.7 {
+ # Use a really big number as an argument to incremetal_vacuum. Should
+ # be interpreted as "free all possible space".
+ execsql {
+ PRAGMA incremental_vacuum(2147483649);
+ }
+ expr [file size test.db] / 1024
+} {1}
+
+do_test incrvacuum-10.8 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(hex(randomblob(1000)));
+ DROP TABLE t1;
+ }
+ # A negative number means free all possible space.
+ execsql {
+ PRAGMA incremental_vacuum=-1;
+ }
+ expr [file size test.db] / 1024
+} {1}
+
+#----------------------------------------------------------------
+# Test that if we set the auto_vacuum mode to 'incremental', then
+# create a database, thereafter that database defaults to incremental
+# vacuum mode.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+ifcapable default_autovacuum {
+ do_test incrvacuum-11.1-av-dflt-on {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+ } {1}
+} else {
+ do_test incrvacuum-11.1-av-dflt-off {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+ } {0}
+}
+do_test incrvacuum-11.2 {
+ execsql {
+ PRAGMA auto_vacuum = incremental;
+ }
+} {}
+do_test incrvacuum-11.3 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {2}
+do_test incrvacuum-11.4 {
+ # The database has now been created.
+ expr {[file size test.db]>0}
+} {1}
+do_test incrvacuum-11.5 {
+ # Close and reopen the connection.
+ db close
+ sqlite3 db test.db
+
+ # Test we are still in incremental vacuum mode.
+ execsql { PRAGMA auto_vacuum; }
+} {2}
+do_test incrvacuum-11.6 {
+ execsql {
+ PRAGMA auto_vacuum = 'full';
+ PRAGMA auto_vacuum;
+ }
+} {1}
+do_test incrvacuum-11.7 {
+ # Close and reopen the connection.
+ db close
+ sqlite3 db test.db
+
+ # Test we are still in "full" auto-vacuum mode.
+ execsql { PRAGMA auto_vacuum; }
+} {1}
+
+#----------------------------------------------------------------------
+# Special case: What happens if the database is locked when a "PRAGMA
+# auto_vacuum = XXX" statement is executed.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+do_test incrvacuum-12.1 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ }
+ expr {[file size test.db]>0}
+} {1}
+
+# Try to change the auto-vacuum from "full" to "incremental" while the
+# database is locked. Nothing should change.
+#
+do_test incrvacuum-12.2 {
+ sqlite3 db2 test.db
+ execsql { BEGIN EXCLUSIVE; } db2
+ catchsql { PRAGMA auto_vacuum = 2; }
+} {1 {database is locked}}
+
+do_test incrvacuum-12.3 {
+ execsql { ROLLBACK; } db2
+ execsql { PRAGMA auto_vacuum }
+} {1}
+
+do_test incrvacuum-12.3 {
+ execsql { SELECT * FROM sqlite_master }
+ execsql { PRAGMA auto_vacuum }
+} {1}
+
+#----------------------------------------------------------------------
+# Special case #2: What if one process prepares a "PRAGMA auto_vacuum = XXX"
+# statement when the database is empty, but doesn't execute it until
+# after some other process has created the database.
+#
+db2 close
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db ; set ::DB [sqlite3_connection_pointer db]
+sqlite3 db2 test.db
+
+do_test incrvacuum-13.1 {
+ # File size is sometimes 1 instead of 0 due to the hack we put in
+ # to work around ticket #3260. Search for comments on #3260 in
+ # os_unix.c.
+ expr {[file size test.db]>1}
+} {0}
+do_test incrvacuum-13.2 {
+ set ::STMT [sqlite3_prepare $::DB {PRAGMA auto_vacuum = 2} -1 DUMMY]
+ execsql {
+ PRAGMA auto_vacuum = none;
+ PRAGMA default_cache_size = 1024;
+ PRAGMA auto_vacuum;
+ } db2
+} {0}
+do_test incrvacuum-13.3 {
+ expr {[file size test.db]>0}
+} {1}
+do_test incrvacuum-13.4 {
+ set rc [sqlite3_step $::STMT]
+ list $rc [sqlite3_finalize $::STMT]
+} {SQLITE_DONE SQLITE_OK}
+do_test incrvacuum-13.5 {
+ execsql {
+ PRAGMA auto_vacuum;
+ }
+} {0}
+
+
+# Verify that the incremental_vacuum pragma fails gracefully if it
+# is used against an invalid database file.
+#
+do_test incrvacuum-14.1 {
+ set out [open invalid.db w]
+ puts $out "This is not an SQLite database file"
+ close $out
+ sqlite3 db3 invalid.db
+ catchsql {
+ PRAGMA incremental_vacuum(10);
+ } db3
+} {1 {file is encrypted or is not a database}}
+
+db2 close
+db3 close
+finish_test
diff --git a/third_party/sqlite/test/incrvacuum2.test b/third_party/sqlite/test/incrvacuum2.test
new file mode 100755
index 0000000..257edb9
--- /dev/null
+++ b/third_party/sqlite/test/incrvacuum2.test
@@ -0,0 +1,146 @@
+# 2007 May 04
+#
+# 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 file is testing the incremental vacuum feature.
+#
+# $Id: incrvacuum2.test,v 1.5 2008/05/07 07:13:16 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum || !pragma} {
+ finish_test
+ return
+}
+
+# If the OMIT_INCRBLOB symbol was defined at compile time, there
+# is no zeroblob() function available. So create a similar
+# function here using Tcl. It doesn't return a blob, but it returns
+# data of the required length, which is good enough for this
+# test file.
+ifcapable !incrblob {
+ proc zeroblob {n} { string repeat 0 $n }
+ db function zeroblob zeroblob
+}
+
+
+# Create a database in incremental vacuum mode that has many
+# pages on the freelist.
+#
+do_test incrvacuum2-1.1 {
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA auto_vacuum=incremental;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(zeroblob(30000));
+ DELETE FROM t1;
+ }
+ file size test.db
+} {32768}
+
+# Vacuum off a single page.
+#
+do_test incrvacuum2-1.2 {
+ execsql {
+ PRAGMA incremental_vacuum(1);
+ }
+ file size test.db
+} {31744}
+
+# Vacuum off five pages
+#
+do_test incrvacuum2-1.3 {
+ execsql {
+ PRAGMA incremental_vacuum(5);
+ }
+ file size test.db
+} {26624}
+
+# Vacuum off all the rest
+#
+do_test incrvacuum2-1.4 {
+ execsql {
+ PRAGMA incremental_vacuum(1000);
+ }
+ file size test.db
+} {3072}
+
+# Make sure incremental vacuum works on attached databases.
+#
+ifcapable attach {
+ do_test incrvacuum2-2.1 {
+ file delete -force test2.db test2.db-journal
+ execsql {
+ ATTACH DATABASE 'test2.db' AS aux;
+ PRAGMA aux.auto_vacuum=incremental;
+ CREATE TABLE aux.t2(x);
+ INSERT INTO t2 VALUES(zeroblob(30000));
+ INSERT INTO t1 SELECT * FROM t2;
+ DELETE FROM t2;
+ DELETE FROM t1;
+ }
+ list [file size test.db] [file size test2.db]
+ } {32768 32768}
+ do_test incrvacuum2-2.2 {
+ execsql {
+ PRAGMA aux.incremental_vacuum(1)
+ }
+ list [file size test.db] [file size test2.db]
+ } {32768 31744}
+ do_test incrvacuum2-2.3 {
+ execsql {
+ PRAGMA aux.incremental_vacuum(5)
+ }
+ list [file size test.db] [file size test2.db]
+ } {32768 26624}
+ do_test incrvacuum2-2.4 {
+ execsql {
+ PRAGMA main.incremental_vacuum(5)
+ }
+ list [file size test.db] [file size test2.db]
+ } {27648 26624}
+ do_test incrvacuum2-2.5 {
+ execsql {
+ PRAGMA aux.incremental_vacuum
+ }
+ list [file size test.db] [file size test2.db]
+ } {27648 3072}
+ do_test incrvacuum2-2.6 {
+ execsql {
+ PRAGMA incremental_vacuum(1)
+ }
+ list [file size test.db] [file size test2.db]
+ } {26624 3072}
+}
+
+do_test incrvacuum2-3.1 {
+ execsql {
+ PRAGMA auto_vacuum = 'full';
+ BEGIN;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1500,1500));
+ COMMIT;
+ }
+} {}
+do_test incrvacuum2-3.2 {
+ execsql {
+ BEGIN;
+ DELETE FROM abc;
+ PRAGMA incremental_vacuum;
+ COMMIT;
+ }
+} {}
+
+integrity_check incremental2-3.3
+
+finish_test
diff --git a/third_party/sqlite/test/incrvacuum_ioerr.test b/third_party/sqlite/test/incrvacuum_ioerr.test
new file mode 100755
index 0000000..47027b8
--- /dev/null
+++ b/third_party/sqlite/test/incrvacuum_ioerr.test
@@ -0,0 +1,181 @@
+# 2001 October 12
+#
+# 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 file is testing for correct handling of I/O errors
+# such as writes failing because the disk is full.
+#
+# The tests in this file use special facilities that are only
+# available in the SQLite test fixture.
+#
+# $Id: incrvacuum_ioerr.test,v 1.6 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If this build of the library does not support auto-vacuum, omit this
+# whole file.
+ifcapable {!autovacuum} {
+ finish_test
+ return
+}
+
+do_ioerr_test incrvacuum-ioerr-1 -cksum 1 -sqlprep {
+ PRAGMA auto_vacuum = 'incremental';
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1500,1500));
+} -sqlbody {
+ BEGIN;
+ CREATE TABLE abc2(a);
+ DELETE FROM abc;
+ PRAGMA incremental_vacuum;
+ COMMIT;
+}
+
+# do_ioerr_test incrvacuum-ioerr-3 -start 1 -cksum 1 -tclprep {
+# db eval {
+# PRAGMA auto_vacuum = 'full';
+# PRAGMA cache_size = 10;
+# BEGIN;
+# CREATE TABLE abc(a, UNIQUE(a));
+# }
+# for {set ii 0} {$ii < 25} {incr ii} {
+# db eval {INSERT INTO abc VALUES(randstr(1500,1500))}
+# }
+# db eval COMMIT
+# } -sqlbody {
+# BEGIN;
+# DELETE FROM abc WHERE (oid%3)==0;
+# INSERT INTO abc SELECT a || '1234567890' FROM abc WHERE oid%2;
+# CREATE INDEX abc_i ON abc(a);
+# DELETE FROM abc WHERE (oid%2)==0;
+# DROP INDEX abc_i;
+# COMMIT;
+# }
+
+do_ioerr_test incrvacuum-ioerr-2 -start 1 -cksum 1 -tclprep {
+ db eval {
+ PRAGMA auto_vacuum = 'full';
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE abc(a, UNIQUE(a));
+ }
+ for {set ii 0} {$ii < 25} {incr ii} {
+ db eval {INSERT INTO abc VALUES(randstr(1500,1500))}
+ }
+ db eval COMMIT
+} -sqlbody {
+ BEGIN;
+ PRAGMA incremental_vacuum;
+ DELETE FROM abc WHERE (oid%3)==0;
+ PRAGMA incremental_vacuum;
+ INSERT INTO abc SELECT a || '1234567890' FROM abc WHERE oid%2;
+ PRAGMA incremental_vacuum;
+ CREATE INDEX abc_i ON abc(a);
+ DELETE FROM abc WHERE (oid%2)==0;
+ PRAGMA incremental_vacuum;
+ DROP INDEX abc_i;
+ PRAGMA incremental_vacuum;
+ COMMIT;
+}
+
+do_ioerr_test incrvacuum-ioerr-3 -start 1 -cksum 1 -tclprep {
+ db eval {
+ PRAGMA auto_vacuum = 'incremental';
+ BEGIN;
+ CREATE TABLE a(i integer, b blob);
+ INSERT INTO a VALUES(1, randstr(1500,1500));
+ INSERT INTO a VALUES(2, randstr(1500,1500));
+ }
+ db eval COMMIT
+ db eval {DELETE FROM a WHERE oid}
+} -sqlbody {
+ PRAGMA incremental_vacuum(5);
+} -cleanup {
+ sqlite3 db test.db
+ integrity_check incrvacuum-ioerr-2.$n.integritycheck
+ db close
+}
+
+
+ifcapable shared_cache {
+
+ catch { db close }
+ file delete -force test.db
+ set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+ # Create two connections to a single shared-cache:
+ #
+ sqlite3 db1 test.db
+ sqlite3 db2 test.db
+
+ # Create a database with around 20 free pages.
+ #
+ do_test incrvacuum-ioerr-4.0 {
+ execsql {
+ PRAGMA page_size = 1024;
+ PRAGMA locking_mode = exclusive;
+ PRAGMA auto_vacuum = 'incremental';
+ BEGIN;
+ CREATE TABLE a(i integer, b blob);
+ } db1
+ for {set ii 0} {$ii < 20} {incr ii} {
+ execsql { INSERT INTO a VALUES($ii, randstr(800,1500)); } db1
+ }
+ execsql COMMIT db1
+ execsql {DELETE FROM a WHERE oid} db1
+ } {}
+
+ set ::rc 1
+ for {set iTest 1} {$::rc && $iTest<2000} {incr iTest} {
+
+ # Figure out how big the database is and how many free pages it
+ # has before running incremental-vacuum.
+ #
+ set nPage [expr {[file size test.db]/1024}]
+ set nFree [execsql {pragma freelist_count} db1]
+
+ # Now run incremental-vacuum to vacuum 5 pages from the db file.
+ # The iTest'th I/O call is set to fail.
+ #
+ set ::sqlite_io_error_pending $iTest
+ set ::sqlite_io_error_persist 1
+ do_test incrvacuum-ioerr-4.$iTest.1 {
+ set ::rc [catch {execsql {pragma incremental_vacuum(5)} db1} msg]
+ expr {$::rc==0 || $msg eq "disk I/O error"}
+ } {1}
+
+ set ::sqlite_io_error_pending 0
+ set ::sqlite_io_error_persist 0
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_hardhit 0
+
+ set nFree2 [execsql {pragma freelist_count} db1]
+ set nPage2 [expr {[file size test.db]/1024}]
+
+ do_test incrvacuum-ioerr-4.$iTest.2 {
+ set shrink [expr {$nPage-$nPage2}]
+ expr {$shrink==0 || $shrink==5}
+ } {1}
+
+ do_test incrvacuum-ioerr-4.$iTest.3 {
+ expr {$nPage - $nPage2}
+ } [expr {$nFree - $nFree2}]
+ }
+
+ # Close the two database connections and restore the default
+ # shared-cache mode setting.
+ #
+ db1 close
+ db2 close
+ sqlite3_enable_shared_cache $::enable_shared_cache
+}
+
+finish_test
diff --git a/third_party/sqlite/test/index.test b/third_party/sqlite/test/index.test
new file mode 100755
index 0000000..a278ac8
--- /dev/null
+++ b/third_party/sqlite/test/index.test
@@ -0,0 +1,711 @@
+# 2001 September 15
+#
+# 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 file is testing the CREATE INDEX statement.
+#
+# $Id: index.test,v 1.43 2008/01/16 18:20:42 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a basic index and verify it is added to sqlite_master
+#
+do_test index-1.1 {
+ execsql {CREATE TABLE test1(f1 int, f2 int, f3 int)}
+ execsql {CREATE INDEX index1 ON test1(f1)}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {index1 test1}
+do_test index-1.1b {
+ execsql {SELECT name, sql, tbl_name, type FROM sqlite_master
+ WHERE name='index1'}
+} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
+do_test index-1.1c {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT name, sql, tbl_name, type FROM sqlite_master
+ WHERE name='index1'}
+} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
+do_test index-1.1d {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {index1 test1}
+
+# Verify that the index dies with the table
+#
+do_test index-1.2 {
+ execsql {DROP TABLE test1}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {}
+
+# Try adding an index to a table that does not exist
+#
+do_test index-2.1 {
+ set v [catch {execsql {CREATE INDEX index1 ON test1(f1)}} msg]
+ lappend v $msg
+} {1 {no such table: main.test1}}
+
+# Try adding an index on a column of a table where the table
+# exists but the column does not.
+#
+do_test index-2.1 {
+ execsql {CREATE TABLE test1(f1 int, f2 int, f3 int)}
+ set v [catch {execsql {CREATE INDEX index1 ON test1(f4)}} msg]
+ lappend v $msg
+} {1 {table test1 has no column named f4}}
+
+# Try an index with some columns that match and others that do now.
+#
+do_test index-2.2 {
+ set v [catch {execsql {CREATE INDEX index1 ON test1(f1, f2, f4, f3)}} msg]
+ execsql {DROP TABLE test1}
+ lappend v $msg
+} {1 {table test1 has no column named f4}}
+
+# Try creating a bunch of indices on the same table
+#
+set r {}
+for {set i 1} {$i<100} {incr i} {
+ lappend r [format index%02d $i]
+}
+do_test index-3.1 {
+ execsql {CREATE TABLE test1(f1 int, f2 int, f3 int, f4 int, f5 int)}
+ for {set i 1} {$i<100} {incr i} {
+ set sql "CREATE INDEX [format index%02d $i] ON test1(f[expr {($i%5)+1}])"
+ execsql $sql
+ }
+ execsql {SELECT name FROM sqlite_master
+ WHERE type='index' AND tbl_name='test1'
+ ORDER BY name}
+} $r
+integrity_check index-3.2.1
+ifcapable {reindex} {
+ do_test index-3.2.2 {
+ execsql REINDEX
+ } {}
+}
+integrity_check index-3.2.3
+
+
+# Verify that all the indices go away when we drop the table.
+#
+do_test index-3.3 {
+ execsql {DROP TABLE test1}
+ execsql {SELECT name FROM sqlite_master
+ WHERE type='index' AND tbl_name='test1'
+ ORDER BY name}
+} {}
+
+# Create a table and insert values into that table. Then create
+# an index on that table. Verify that we can select values
+# from the table correctly using the index.
+#
+# Note that the index names "index9" and "indext" are chosen because
+# they both have the same hash.
+#
+do_test index-4.1 {
+ execsql {CREATE TABLE test1(cnt int, power int)}
+ for {set i 1} {$i<20} {incr i} {
+ execsql "INSERT INTO test1 VALUES($i,[expr {1<<$i}])"
+ }
+ execsql {CREATE INDEX index9 ON test1(cnt)}
+ execsql {CREATE INDEX indext ON test1(power)}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {index9 indext test1}
+do_test index-4.2 {
+ execsql {SELECT cnt FROM test1 WHERE power=4}
+} {2}
+do_test index-4.3 {
+ execsql {SELECT cnt FROM test1 WHERE power=1024}
+} {10}
+do_test index-4.4 {
+ execsql {SELECT power FROM test1 WHERE cnt=6}
+} {64}
+do_test index-4.5 {
+ execsql {DROP INDEX indext}
+ execsql {SELECT power FROM test1 WHERE cnt=6}
+} {64}
+do_test index-4.6 {
+ execsql {SELECT cnt FROM test1 WHERE power=1024}
+} {10}
+do_test index-4.7 {
+ execsql {CREATE INDEX indext ON test1(cnt)}
+ execsql {SELECT power FROM test1 WHERE cnt=6}
+} {64}
+do_test index-4.8 {
+ execsql {SELECT cnt FROM test1 WHERE power=1024}
+} {10}
+do_test index-4.9 {
+ execsql {DROP INDEX index9}
+ execsql {SELECT power FROM test1 WHERE cnt=6}
+} {64}
+do_test index-4.10 {
+ execsql {SELECT cnt FROM test1 WHERE power=1024}
+} {10}
+do_test index-4.11 {
+ execsql {DROP INDEX indext}
+ execsql {SELECT power FROM test1 WHERE cnt=6}
+} {64}
+do_test index-4.12 {
+ execsql {SELECT cnt FROM test1 WHERE power=1024}
+} {10}
+do_test index-4.13 {
+ execsql {DROP TABLE test1}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {}
+integrity_check index-4.14
+
+# Do not allow indices to be added to sqlite_master
+#
+do_test index-5.1 {
+ set v [catch {execsql {CREATE INDEX index1 ON sqlite_master(name)}} msg]
+ lappend v $msg
+} {1 {table sqlite_master may not be indexed}}
+do_test index-5.2 {
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Do not allow indices with duplicate names to be added
+#
+do_test index-6.1 {
+ execsql {CREATE TABLE test1(f1 int, f2 int)}
+ execsql {CREATE TABLE test2(g1 real, g2 real)}
+ execsql {CREATE INDEX index1 ON test1(f1)}
+ set v [catch {execsql {CREATE INDEX index1 ON test2(g1)}} msg]
+ lappend v $msg
+} {1 {index index1 already exists}}
+do_test index-6.1.1 {
+ catchsql {CREATE INDEX [index1] ON test2(g1)}
+} {1 {index index1 already exists}}
+do_test index-6.1b {
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {index1 test1 test2}
+do_test index-6.1c {
+ catchsql {CREATE INDEX IF NOT EXISTS index1 ON test1(f1)}
+} {0 {}}
+do_test index-6.2 {
+ set v [catch {execsql {CREATE INDEX test1 ON test2(g1)}} msg]
+ lappend v $msg
+} {1 {there is already a table named test1}}
+do_test index-6.2b {
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {index1 test1 test2}
+do_test index-6.3 {
+ execsql {DROP TABLE test1}
+ execsql {DROP TABLE test2}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {}
+do_test index-6.4 {
+ execsql {
+ CREATE TABLE test1(a,b);
+ CREATE INDEX index1 ON test1(a);
+ CREATE INDEX index2 ON test1(b);
+ CREATE INDEX index3 ON test1(a,b);
+ DROP TABLE test1;
+ SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name;
+ }
+} {}
+integrity_check index-6.5
+
+
+# Create a primary key
+#
+do_test index-7.1 {
+ execsql {CREATE TABLE test1(f1 int, f2 int primary key)}
+ for {set i 1} {$i<20} {incr i} {
+ execsql "INSERT INTO test1 VALUES($i,[expr {1<<$i}])"
+ }
+ execsql {SELECT count(*) FROM test1}
+} {19}
+do_test index-7.2 {
+ execsql {SELECT f1 FROM test1 WHERE f2=65536}
+} {16}
+do_test index-7.3 {
+ execsql {
+ SELECT name FROM sqlite_master
+ WHERE type='index' AND tbl_name='test1'
+ }
+} {sqlite_autoindex_test1_1}
+do_test index-7.4 {
+ execsql {DROP table test1}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+integrity_check index-7.5
+
+# Make sure we cannot drop a non-existant index.
+#
+do_test index-8.1 {
+ set v [catch {execsql {DROP INDEX index1}} msg]
+ lappend v $msg
+} {1 {no such index: index1}}
+
+# Make sure we don't actually create an index when the EXPLAIN keyword
+# is used.
+#
+do_test index-9.1 {
+ execsql {CREATE TABLE tab1(a int)}
+ ifcapable {explain} {
+ execsql {EXPLAIN CREATE INDEX idx1 ON tab1(a)}
+ }
+ execsql {SELECT name FROM sqlite_master WHERE tbl_name='tab1'}
+} {tab1}
+do_test index-9.2 {
+ execsql {CREATE INDEX idx1 ON tab1(a)}
+ execsql {SELECT name FROM sqlite_master WHERE tbl_name='tab1' ORDER BY name}
+} {idx1 tab1}
+integrity_check index-9.3
+
+# Allow more than one entry with the same key.
+#
+do_test index-10.0 {
+ execsql {
+ CREATE TABLE t1(a int, b int);
+ CREATE INDEX i1 ON t1(a);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(2,4);
+ INSERT INTO t1 VALUES(3,8);
+ INSERT INTO t1 VALUES(1,12);
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {2 12}
+do_test index-10.1 {
+ execsql {
+ SELECT b FROM t1 WHERE a=2 ORDER BY b;
+ }
+} {4}
+do_test index-10.2 {
+ execsql {
+ DELETE FROM t1 WHERE b=12;
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {2}
+do_test index-10.3 {
+ execsql {
+ DELETE FROM t1 WHERE b=2;
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {}
+do_test index-10.4 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES (1,1);
+ INSERT INTO t1 VALUES (1,2);
+ INSERT INTO t1 VALUES (1,3);
+ INSERT INTO t1 VALUES (1,4);
+ INSERT INTO t1 VALUES (1,5);
+ INSERT INTO t1 VALUES (1,6);
+ INSERT INTO t1 VALUES (1,7);
+ INSERT INTO t1 VALUES (1,8);
+ INSERT INTO t1 VALUES (1,9);
+ INSERT INTO t1 VALUES (2,0);
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {1 2 3 4 5 6 7 8 9}
+do_test index-10.5 {
+ ifcapable subquery {
+ execsql { DELETE FROM t1 WHERE b IN (2, 4, 6, 8); }
+ } else {
+ execsql { DELETE FROM t1 WHERE b = 2 OR b = 4 OR b = 6 OR b = 8; }
+ }
+ execsql {
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {1 3 5 7 9}
+do_test index-10.6 {
+ execsql {
+ DELETE FROM t1 WHERE b>2;
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {1}
+do_test index-10.7 {
+ execsql {
+ DELETE FROM t1 WHERE b=1;
+ SELECT b FROM t1 WHERE a=1 ORDER BY b;
+ }
+} {}
+do_test index-10.8 {
+ execsql {
+ SELECT b FROM t1 ORDER BY b;
+ }
+} {0}
+integrity_check index-10.9
+
+# Automatically create an index when we specify a primary key.
+#
+do_test index-11.1 {
+ execsql {
+ CREATE TABLE t3(
+ a text,
+ b int,
+ c float,
+ PRIMARY KEY(b)
+ );
+ }
+ for {set i 1} {$i<=50} {incr i} {
+ execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)"
+ }
+ set sqlite_search_count 0
+ concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count
+} {0.1 3}
+integrity_check index-11.2
+
+
+# Numeric strings should compare as if they were numbers. So even if the
+# strings are not character-by-character the same, if they represent the
+# same number they should compare equal to one another. Verify that this
+# is true in indices.
+#
+# Updated for sqlite3 v3: SQLite will now store these values as numbers
+# (because the affinity of column a is NUMERIC) so the quirky
+# representations are not retained. i.e. '+1.0' becomes '1'.
+do_test index-12.1 {
+ execsql {
+ CREATE TABLE t4(a NUM,b);
+ INSERT INTO t4 VALUES('0.0',1);
+ INSERT INTO t4 VALUES('0.00',2);
+ INSERT INTO t4 VALUES('abc',3);
+ INSERT INTO t4 VALUES('-1.0',4);
+ INSERT INTO t4 VALUES('+1.0',5);
+ INSERT INTO t4 VALUES('0',6);
+ INSERT INTO t4 VALUES('00000',7);
+ SELECT a FROM t4 ORDER BY b;
+ }
+} {0 0 abc -1 1 0 0}
+do_test index-12.2 {
+ execsql {
+ SELECT a FROM t4 WHERE a==0 ORDER BY b
+ }
+} {0 0 0 0}
+do_test index-12.3 {
+ execsql {
+ SELECT a FROM t4 WHERE a<0.5 ORDER BY b
+ }
+} {0 0 -1 0 0}
+do_test index-12.4 {
+ execsql {
+ SELECT a FROM t4 WHERE a>-0.5 ORDER BY b
+ }
+} {0 0 abc 1 0 0}
+do_test index-12.5 {
+ execsql {
+ CREATE INDEX t4i1 ON t4(a);
+ SELECT a FROM t4 WHERE a==0 ORDER BY b
+ }
+} {0 0 0 0}
+do_test index-12.6 {
+ execsql {
+ SELECT a FROM t4 WHERE a<0.5 ORDER BY b
+ }
+} {0 0 -1 0 0}
+do_test index-12.7 {
+ execsql {
+ SELECT a FROM t4 WHERE a>-0.5 ORDER BY b
+ }
+} {0 0 abc 1 0 0}
+integrity_check index-12.8
+
+# Make sure we cannot drop an automatically created index.
+#
+do_test index-13.1 {
+ execsql {
+ CREATE TABLE t5(
+ a int UNIQUE,
+ b float PRIMARY KEY,
+ c varchar(10),
+ UNIQUE(a,c)
+ );
+ INSERT INTO t5 VALUES(1,2,3);
+ SELECT * FROM t5;
+ }
+} {1 2.0 3}
+do_test index-13.2 {
+ set ::idxlist [execsql {
+ SELECT name FROM sqlite_master WHERE type="index" AND tbl_name="t5";
+ }]
+ llength $::idxlist
+} {3}
+for {set i 0} {$i<[llength $::idxlist]} {incr i} {
+ do_test index-13.3.$i {
+ catchsql "
+ DROP INDEX '[lindex $::idxlist $i]';
+ "
+ } {1 {index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped}}
+}
+do_test index-13.4 {
+ execsql {
+ INSERT INTO t5 VALUES('a','b','c');
+ SELECT * FROM t5;
+ }
+} {1 2.0 3 a b c}
+integrity_check index-13.5
+
+# Check the sort order of data in an index.
+#
+do_test index-14.1 {
+ execsql {
+ CREATE TABLE t6(a,b,c);
+ CREATE INDEX t6i1 ON t6(a,b);
+ INSERT INTO t6 VALUES('','',1);
+ INSERT INTO t6 VALUES('',NULL,2);
+ INSERT INTO t6 VALUES(NULL,'',3);
+ INSERT INTO t6 VALUES('abc',123,4);
+ INSERT INTO t6 VALUES(123,'abc',5);
+ SELECT c FROM t6 ORDER BY a,b;
+ }
+} {3 5 2 1 4}
+do_test index-14.2 {
+ execsql {
+ SELECT c FROM t6 WHERE a='';
+ }
+} {2 1}
+do_test index-14.3 {
+ execsql {
+ SELECT c FROM t6 WHERE b='';
+ }
+} {1 3}
+do_test index-14.4 {
+ execsql {
+ SELECT c FROM t6 WHERE a>'';
+ }
+} {4}
+do_test index-14.5 {
+ execsql {
+ SELECT c FROM t6 WHERE a>='';
+ }
+} {2 1 4}
+do_test index-14.6 {
+ execsql {
+ SELECT c FROM t6 WHERE a>123;
+ }
+} {2 1 4}
+do_test index-14.7 {
+ execsql {
+ SELECT c FROM t6 WHERE a>=123;
+ }
+} {5 2 1 4}
+do_test index-14.8 {
+ execsql {
+ SELECT c FROM t6 WHERE a<'abc';
+ }
+} {5 2 1}
+do_test index-14.9 {
+ execsql {
+ SELECT c FROM t6 WHERE a<='abc';
+ }
+} {5 2 1 4}
+do_test index-14.10 {
+ execsql {
+ SELECT c FROM t6 WHERE a<='';
+ }
+} {5 2 1}
+do_test index-14.11 {
+ execsql {
+ SELECT c FROM t6 WHERE a<'';
+ }
+} {5}
+integrity_check index-14.12
+
+do_test index-15.1 {
+ execsql {
+ DELETE FROM t1;
+ SELECT * FROM t1;
+ }
+} {}
+do_test index-15.2 {
+ execsql {
+ INSERT INTO t1 VALUES('1.234e5',1);
+ INSERT INTO t1 VALUES('12.33e04',2);
+ INSERT INTO t1 VALUES('12.35E4',3);
+ INSERT INTO t1 VALUES('12.34e',4);
+ INSERT INTO t1 VALUES('12.32e+4',5);
+ INSERT INTO t1 VALUES('12.36E+04',6);
+ INSERT INTO t1 VALUES('12.36E+',7);
+ INSERT INTO t1 VALUES('+123.10000E+0003',8);
+ INSERT INTO t1 VALUES('+',9);
+ INSERT INTO t1 VALUES('+12347.E+02',10);
+ INSERT INTO t1 VALUES('+12347E+02',11);
+ SELECT b FROM t1 ORDER BY a;
+ }
+} {8 5 2 1 3 6 11 9 10 4 7}
+integrity_check index-15.1
+
+# The following tests - index-16.* - test that when a table definition
+# includes qualifications that specify the same constraint twice only a
+# single index is generated to enforce the constraint.
+#
+# For example: "CREATE TABLE abc( x PRIMARY KEY, UNIQUE(x) );"
+#
+do_test index-16.1 {
+ execsql {
+ CREATE TABLE t7(c UNIQUE PRIMARY KEY);
+ SELECT count(*) FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {1}
+do_test index-16.2 {
+ execsql {
+ DROP TABLE t7;
+ CREATE TABLE t7(c UNIQUE PRIMARY KEY);
+ SELECT count(*) FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {1}
+do_test index-16.3 {
+ execsql {
+ DROP TABLE t7;
+ CREATE TABLE t7(c PRIMARY KEY, UNIQUE(c) );
+ SELECT count(*) FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {1}
+do_test index-16.4 {
+ execsql {
+ DROP TABLE t7;
+ CREATE TABLE t7(c, d , UNIQUE(c, d), PRIMARY KEY(c, d) );
+ SELECT count(*) FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {1}
+do_test index-16.5 {
+ execsql {
+ DROP TABLE t7;
+ CREATE TABLE t7(c, d , UNIQUE(c), PRIMARY KEY(c, d) );
+ SELECT count(*) FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {2}
+
+# Test that automatically create indices are named correctly. The current
+# convention is: "sqlite_autoindex_<table name>_<integer>"
+#
+# Then check that it is an error to try to drop any automtically created
+# indices.
+do_test index-17.1 {
+ execsql {
+ DROP TABLE t7;
+ CREATE TABLE t7(c, d UNIQUE, UNIQUE(c), PRIMARY KEY(c, d) );
+ SELECT name FROM sqlite_master WHERE tbl_name = 't7' AND type = 'index';
+ }
+} {sqlite_autoindex_t7_1 sqlite_autoindex_t7_2 sqlite_autoindex_t7_3}
+do_test index-17.2 {
+ catchsql {
+ DROP INDEX sqlite_autoindex_t7_1;
+ }
+} {1 {index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped}}
+do_test index-17.3 {
+ catchsql {
+ DROP INDEX IF EXISTS sqlite_autoindex_t7_1;
+ }
+} {1 {index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped}}
+do_test index-17.4 {
+ catchsql {
+ DROP INDEX IF EXISTS no_such_index;
+ }
+} {0 {}}
+
+
+# The following tests ensure that it is not possible to explicitly name
+# a schema object with a name beginning with "sqlite_". Granted that is a
+# little outside the focus of this test scripts, but this has got to be
+# tested somewhere.
+do_test index-18.1 {
+ catchsql {
+ CREATE TABLE sqlite_t1(a, b, c);
+ }
+} {1 {object name reserved for internal use: sqlite_t1}}
+do_test index-18.2 {
+ catchsql {
+ CREATE INDEX sqlite_i1 ON t7(c);
+ }
+} {1 {object name reserved for internal use: sqlite_i1}}
+ifcapable view {
+do_test index-18.3 {
+ catchsql {
+ CREATE VIEW sqlite_v1 AS SELECT * FROM t7;
+ }
+} {1 {object name reserved for internal use: sqlite_v1}}
+} ;# ifcapable view
+ifcapable {trigger} {
+ do_test index-18.4 {
+ catchsql {
+ CREATE TRIGGER sqlite_tr1 BEFORE INSERT ON t7 BEGIN SELECT 1; END;
+ }
+ } {1 {object name reserved for internal use: sqlite_tr1}}
+}
+do_test index-18.5 {
+ execsql {
+ DROP TABLE t7;
+ }
+} {}
+
+# These tests ensure that if multiple table definition constraints are
+# implemented by a single indice, the correct ON CONFLICT policy applies.
+ifcapable conflict {
+ do_test index-19.1 {
+ execsql {
+ CREATE TABLE t7(a UNIQUE PRIMARY KEY);
+ CREATE TABLE t8(a UNIQUE PRIMARY KEY ON CONFLICT ROLLBACK);
+ INSERT INTO t7 VALUES(1);
+ INSERT INTO t8 VALUES(1);
+ }
+ } {}
+ do_test index-19.2 {
+ catchsql {
+ BEGIN;
+ INSERT INTO t7 VALUES(1);
+ }
+ } {1 {column a is not unique}}
+ do_test index-19.3 {
+ catchsql {
+ BEGIN;
+ }
+ } {1 {cannot start a transaction within a transaction}}
+ do_test index-19.4 {
+ catchsql {
+ INSERT INTO t8 VALUES(1);
+ }
+ } {1 {column a is not unique}}
+ do_test index-19.5 {
+ catchsql {
+ BEGIN;
+ COMMIT;
+ }
+ } {0 {}}
+ do_test index-19.6 {
+ catchsql {
+ DROP TABLE t7;
+ DROP TABLE t8;
+ CREATE TABLE t7(
+ a PRIMARY KEY ON CONFLICT FAIL,
+ UNIQUE(a) ON CONFLICT IGNORE
+ );
+ }
+ } {1 {conflicting ON CONFLICT clauses specified}}
+} ; # end of "ifcapable conflict" block
+
+ifcapable {reindex} {
+ do_test index-19.7 {
+ execsql REINDEX
+ } {}
+}
+integrity_check index-19.8
+
+# Drop index with a quoted name. Ticket #695.
+#
+do_test index-20.1 {
+ execsql {
+ CREATE INDEX "t6i2" ON t6(c);
+ DROP INDEX "t6i2";
+ }
+} {}
+do_test index-20.2 {
+ execsql {
+ DROP INDEX "t6i1";
+ }
+} {}
+
+
+finish_test
diff --git a/third_party/sqlite/test/index2.test b/third_party/sqlite/test/index2.test
new file mode 100755
index 0000000..48d0c38
--- /dev/null
+++ b/third_party/sqlite/test/index2.test
@@ -0,0 +1,74 @@
+# 2005 January 11
+#
+# 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 file is testing the CREATE INDEX statement.
+#
+# $Id: index2.test,v 1.3 2006/03/03 19:12:30 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table with a large number of columns
+#
+do_test index2-1.1 {
+ set sql {CREATE TABLE t1(}
+ for {set i 1} {$i<1000} {incr i} {
+ append sql "c$i,"
+ }
+ append sql "c1000);"
+ execsql $sql
+} {}
+do_test index2-1.2 {
+ set sql {INSERT INTO t1 VALUES(}
+ for {set i 1} {$i<1000} {incr i} {
+ append sql $i,
+ }
+ append sql {1000);}
+ execsql $sql
+} {}
+do_test index2-1.3 {
+ execsql {SELECT c123 FROM t1}
+} 123
+do_test index2-1.4 {
+ execsql BEGIN
+ for {set j 1} {$j<=100} {incr j} {
+ set sql {INSERT INTO t1 VALUES(}
+ for {set i 1} {$i<1000} {incr i} {
+ append sql [expr {$j*10000+$i}],
+ }
+ append sql "[expr {$j*10000+1000}]);"
+ execsql $sql
+ }
+ execsql COMMIT
+ execsql {SELECT count(*) FROM t1}
+} 101
+do_test index2-1.5 {
+ execsql {SELECT round(sum(c1000)) FROM t1}
+} {50601000.0}
+
+# Create indices with many columns
+#
+do_test index2-2.1 {
+ set sql "CREATE INDEX t1i1 ON t1("
+ for {set i 1} {$i<1000} {incr i} {
+ append sql c$i,
+ }
+ append sql c1000)
+ execsql $sql
+} {}
+do_test index2-2.2 {
+ ifcapable explain {
+ execsql {EXPLAIN SELECT c9 FROM t1 ORDER BY c1, c2, c3, c4, c5}
+ }
+ execsql {SELECT c9 FROM t1 ORDER BY c1, c2, c3, c4, c5, c6 LIMIT 5}
+} {9 10009 20009 30009 40009}
+
+finish_test
diff --git a/third_party/sqlite/test/index3.test b/third_party/sqlite/test/index3.test
new file mode 100755
index 0000000..9549f55
--- /dev/null
+++ b/third_party/sqlite/test/index3.test
@@ -0,0 +1,58 @@
+# 2005 February 14
+#
+# 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 file is testing the CREATE INDEX statement.
+#
+# $Id: index3.test,v 1.3 2008/03/19 13:03:34 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Ticket #1115. Make sure that when a UNIQUE index is created on a
+# non-unique column (or columns) that it fails and that it leaves no
+# residue behind.
+#
+do_test index3-1.1 {
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(1);
+ SELECT * FROM t1;
+ }
+} {1 1}
+do_test index3-1.2 {
+ catchsql {
+ BEGIN;
+ CREATE UNIQUE INDEX i1 ON t1(a);
+ }
+} {1 {indexed columns are not unique}}
+do_test index3-1.3 {
+ catchsql COMMIT;
+} {0 {}}
+integrity_check index3-1.4
+
+# This test corrupts the database file so it must be the last test
+# in the series.
+#
+do_test index3-99.1 {
+ execsql {
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET sql='nonsense';
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ DROP INDEX i1;
+ }
+} {1 {malformed database schema (t1) - near "nonsense": syntax error}}
+
+finish_test
diff --git a/third_party/sqlite/test/insert.test b/third_party/sqlite/test/insert.test
new file mode 100755
index 0000000..9ea9cd7
--- /dev/null
+++ b/third_party/sqlite/test/insert.test
@@ -0,0 +1,391 @@
+# 2001 September 15
+#
+# 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 file is testing the INSERT statement.
+#
+# $Id: insert.test,v 1.31 2007/04/05 11:25:59 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Try to insert into a non-existant table.
+#
+do_test insert-1.1 {
+ set v [catch {execsql {INSERT INTO test1 VALUES(1,2,3)}} msg]
+ lappend v $msg
+} {1 {no such table: test1}}
+
+# Try to insert into sqlite_master
+#
+do_test insert-1.2 {
+ set v [catch {execsql {INSERT INTO sqlite_master VALUES(1,2,3,4)}} msg]
+ lappend v $msg
+} {1 {table sqlite_master may not be modified}}
+
+# Try to insert the wrong number of entries.
+#
+do_test insert-1.3 {
+ execsql {CREATE TABLE test1(one int, two int, three int)}
+ set v [catch {execsql {INSERT INTO test1 VALUES(1,2)}} msg]
+ lappend v $msg
+} {1 {table test1 has 3 columns but 2 values were supplied}}
+do_test insert-1.3b {
+ set v [catch {execsql {INSERT INTO test1 VALUES(1,2,3,4)}} msg]
+ lappend v $msg
+} {1 {table test1 has 3 columns but 4 values were supplied}}
+do_test insert-1.3c {
+ set v [catch {execsql {INSERT INTO test1(one,two) VALUES(1,2,3,4)}} msg]
+ lappend v $msg
+} {1 {4 values for 2 columns}}
+do_test insert-1.3d {
+ set v [catch {execsql {INSERT INTO test1(one,two) VALUES(1)}} msg]
+ lappend v $msg
+} {1 {1 values for 2 columns}}
+
+# Try to insert into a non-existant column of a table.
+#
+do_test insert-1.4 {
+ set v [catch {execsql {INSERT INTO test1(one,four) VALUES(1,2)}} msg]
+ lappend v $msg
+} {1 {table test1 has no column named four}}
+
+# Make sure the inserts actually happen
+#
+do_test insert-1.5 {
+ execsql {INSERT INTO test1 VALUES(1,2,3)}
+ execsql {SELECT * FROM test1}
+} {1 2 3}
+do_test insert-1.5b {
+ execsql {INSERT INTO test1 VALUES(4,5,6)}
+ execsql {SELECT * FROM test1 ORDER BY one}
+} {1 2 3 4 5 6}
+do_test insert-1.5c {
+ execsql {INSERT INTO test1 VALUES(7,8,9)}
+ execsql {SELECT * FROM test1 ORDER BY one}
+} {1 2 3 4 5 6 7 8 9}
+
+do_test insert-1.6 {
+ execsql {DELETE FROM test1}
+ execsql {INSERT INTO test1(one,two) VALUES(1,2)}
+ execsql {SELECT * FROM test1 ORDER BY one}
+} {1 2 {}}
+do_test insert-1.6b {
+ execsql {INSERT INTO test1(two,three) VALUES(5,6)}
+ execsql {SELECT * FROM test1 ORDER BY one}
+} {{} 5 6 1 2 {}}
+do_test insert-1.6c {
+ execsql {INSERT INTO test1(three,one) VALUES(7,8)}
+ execsql {SELECT * FROM test1 ORDER BY one}
+} {{} 5 6 1 2 {} 8 {} 7}
+
+# A table to use for testing default values
+#
+do_test insert-2.1 {
+ execsql {
+ CREATE TABLE test2(
+ f1 int default -111,
+ f2 real default +4.32,
+ f3 int default +222,
+ f4 int default 7.89
+ )
+ }
+ execsql {SELECT * from test2}
+} {}
+do_test insert-2.2 {
+ execsql {INSERT INTO test2(f1,f3) VALUES(+10,-10)}
+ execsql {SELECT * FROM test2}
+} {10 4.32 -10 7.89}
+do_test insert-2.3 {
+ execsql {INSERT INTO test2(f2,f4) VALUES(1.23,-3.45)}
+ execsql {SELECT * FROM test2 WHERE f1==-111}
+} {-111 1.23 222 -3.45}
+do_test insert-2.4 {
+ execsql {INSERT INTO test2(f1,f2,f4) VALUES(77,+1.23,3.45)}
+ execsql {SELECT * FROM test2 WHERE f1==77}
+} {77 1.23 222 3.45}
+do_test insert-2.10 {
+ execsql {
+ DROP TABLE test2;
+ CREATE TABLE test2(
+ f1 int default 111,
+ f2 real default -4.32,
+ f3 text default hi,
+ f4 text default 'abc-123',
+ f5 varchar(10)
+ )
+ }
+ execsql {SELECT * from test2}
+} {}
+do_test insert-2.11 {
+ execsql {INSERT INTO test2(f2,f4) VALUES(-2.22,'hi!')}
+ execsql {SELECT * FROM test2}
+} {111 -2.22 hi hi! {}}
+do_test insert-2.12 {
+ execsql {INSERT INTO test2(f1,f5) VALUES(1,'xyzzy')}
+ execsql {SELECT * FROM test2 ORDER BY f1}
+} {1 -4.32 hi abc-123 xyzzy 111 -2.22 hi hi! {}}
+
+# Do additional inserts with default values, but this time
+# on a table that has indices. In particular we want to verify
+# that the correct default values are inserted into the indices.
+#
+do_test insert-3.1 {
+ execsql {
+ DELETE FROM test2;
+ CREATE INDEX index9 ON test2(f1,f2);
+ CREATE INDEX indext ON test2(f4,f5);
+ SELECT * from test2;
+ }
+} {}
+
+# Update for sqlite3 v3:
+# Change the 111 to '111' in the following two test cases, because
+# the default value is being inserted as a string. TODO: It shouldn't be.
+do_test insert-3.2 {
+ execsql {INSERT INTO test2(f2,f4) VALUES(-3.33,'hum')}
+ execsql {SELECT * FROM test2 WHERE f1='111' AND f2=-3.33}
+} {111 -3.33 hi hum {}}
+do_test insert-3.3 {
+ execsql {INSERT INTO test2(f1,f2,f5) VALUES(22,-4.44,'wham')}
+ execsql {SELECT * FROM test2 WHERE f1='111' AND f2=-3.33}
+} {111 -3.33 hi hum {}}
+do_test insert-3.4 {
+ execsql {SELECT * FROM test2 WHERE f1=22 AND f2=-4.44}
+} {22 -4.44 hi abc-123 wham}
+ifcapable {reindex} {
+ do_test insert-3.5 {
+ execsql REINDEX
+ } {}
+}
+integrity_check insert-3.5
+
+# Test of expressions in the VALUES clause
+#
+do_test insert-4.1 {
+ execsql {
+ CREATE TABLE t3(a,b,c);
+ INSERT INTO t3 VALUES(1+2+3,4,5);
+ SELECT * FROM t3;
+ }
+} {6 4 5}
+do_test insert-4.2 {
+ ifcapable subquery {
+ execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1,5,6);}
+ } else {
+ set maxa [execsql {SELECT max(a) FROM t3}]
+ execsql "INSERT INTO t3 VALUES($maxa+1,5,6);"
+ }
+ execsql {
+ SELECT * FROM t3 ORDER BY a;
+ }
+} {6 4 5 7 5 6}
+ifcapable subquery {
+ do_test insert-4.3 {
+ catchsql {
+ INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1,t3.a,6);
+ SELECT * FROM t3 ORDER BY a;
+ }
+ } {1 {no such column: t3.a}}
+}
+do_test insert-4.4 {
+ ifcapable subquery {
+ execsql {INSERT INTO t3 VALUES((SELECT b FROM t3 WHERE a=0),6,7);}
+ } else {
+ set b [execsql {SELECT b FROM t3 WHERE a = 0}]
+ if {$b==""} {set b NULL}
+ execsql "INSERT INTO t3 VALUES($b,6,7);"
+ }
+ execsql {
+ SELECT * FROM t3 ORDER BY a;
+ }
+} {{} 6 7 6 4 5 7 5 6}
+do_test insert-4.5 {
+ execsql {
+ SELECT b,c FROM t3 WHERE a IS NULL;
+ }
+} {6 7}
+do_test insert-4.6 {
+ catchsql {
+ INSERT INTO t3 VALUES(notafunc(2,3),2,3);
+ }
+} {1 {no such function: notafunc}}
+do_test insert-4.7 {
+ execsql {
+ INSERT INTO t3 VALUES(min(1,2,3),max(1,2,3),99);
+ SELECT * FROM t3 WHERE c=99;
+ }
+} {1 3 99}
+
+# Test the ability to insert from a temporary table into itself.
+# Ticket #275.
+#
+ifcapable tempdb {
+ do_test insert-5.1 {
+ execsql {
+ CREATE TEMP TABLE t4(x);
+ INSERT INTO t4 VALUES(1);
+ SELECT * FROM t4;
+ }
+ } {1}
+ do_test insert-5.2 {
+ execsql {
+ INSERT INTO t4 SELECT x+1 FROM t4;
+ SELECT * FROM t4;
+ }
+ } {1 2}
+ ifcapable {explain} {
+ do_test insert-5.3 {
+ # verify that a temporary table is used to copy t4 to t4
+ set x [execsql {
+ EXPLAIN INSERT INTO t4 SELECT x+2 FROM t4;
+ }]
+ expr {[lsearch $x OpenEphemeral]>0}
+ } {1}
+ }
+
+ do_test insert-5.4 {
+ # Verify that table "test1" begins on page 3. This should be the same
+ # page number used by "t4" above.
+ #
+ # Update for v3 - the first table now begins on page 2 of each file, not 3.
+ execsql {
+ SELECT rootpage FROM sqlite_master WHERE name='test1';
+ }
+ } [expr $AUTOVACUUM?3:2]
+ do_test insert-5.5 {
+ # Verify that "t4" begins on page 3.
+ #
+ # Update for v3 - the first table now begins on page 2 of each file, not 3.
+ execsql {
+ SELECT rootpage FROM sqlite_temp_master WHERE name='t4';
+ }
+ } {2}
+ do_test insert-5.6 {
+ # This should not use an intermediate temporary table.
+ execsql {
+ INSERT INTO t4 SELECT one FROM test1 WHERE three=7;
+ SELECT * FROM t4
+ }
+ } {1 2 8}
+ ifcapable {explain} {
+ do_test insert-5.7 {
+ # verify that no temporary table is used to copy test1 to t4
+ set x [execsql {
+ EXPLAIN INSERT INTO t4 SELECT one FROM test1;
+ }]
+ expr {[lsearch $x OpenTemp]>0}
+ } {0}
+ }
+}
+
+# Ticket #334: REPLACE statement corrupting indices.
+#
+ifcapable conflict {
+ # The REPLACE command is not available if SQLITE_OMIT_CONFLICT is
+ # defined at compilation time.
+ do_test insert-6.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(2,3);
+ SELECT b FROM t1 WHERE b=2;
+ }
+ } {2}
+ do_test insert-6.2 {
+ execsql {
+ REPLACE INTO t1 VALUES(1,4);
+ SELECT b FROM t1 WHERE b=2;
+ }
+ } {}
+ do_test insert-6.3 {
+ execsql {
+ UPDATE OR REPLACE t1 SET a=2 WHERE b=4;
+ SELECT * FROM t1 WHERE b=4;
+ }
+ } {2 4}
+ do_test insert-6.4 {
+ execsql {
+ SELECT * FROM t1 WHERE b=3;
+ }
+ } {}
+ ifcapable {reindex} {
+ do_test insert-6.5 {
+ execsql REINDEX
+ } {}
+ }
+ do_test insert-6.6 {
+ execsql {
+ DROP TABLE t1;
+ }
+ } {}
+}
+
+# Test that the special optimization for queries of the form
+# "SELECT max(x) FROM tbl" where there is an index on tbl(x) works with
+# INSERT statments.
+do_test insert-7.1 {
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ CREATE INDEX i1 ON t1(a);
+ }
+} {}
+do_test insert-7.2 {
+ execsql {
+ INSERT INTO t1 SELECT max(a) FROM t1;
+ }
+} {}
+do_test insert-7.3 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} {1 2 2}
+
+# Ticket #1140: Check for an infinite loop in the algorithm that tests
+# to see if the right-hand side of an INSERT...SELECT references the left-hand
+# side.
+#
+ifcapable subquery&&compound {
+ do_test insert-8.1 {
+ execsql {
+ INSERT INTO t3 SELECT * FROM (SELECT * FROM t3 UNION ALL SELECT 1,2,3)
+ }
+ } {}
+}
+
+# Make sure the rowid cache in the VDBE is reset correctly when
+# an explicit rowid is given.
+#
+do_test insert-9.1 {
+ execsql {
+ CREATE TABLE t5(x);
+ INSERT INTO t5 VALUES(1);
+ INSERT INTO t5 VALUES(2);
+ INSERT INTO t5 VALUES(3);
+ INSERT INTO t5(rowid, x) SELECT nullif(x*2+10,14), x+100 FROM t5;
+ SELECT rowid, x FROM t5;
+ }
+} {1 1 2 2 3 3 12 101 13 102 16 103}
+do_test insert-9.2 {
+ execsql {
+ CREATE TABLE t6(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t6 VALUES(1,1);
+ INSERT INTO t6 VALUES(2,2);
+ INSERT INTO t6 VALUES(3,3);
+ INSERT INTO t6 SELECT nullif(y*2+10,14), y+100 FROM t6;
+ SELECT x, y FROM t6;
+ }
+} {1 1 2 2 3 3 12 101 13 102 16 103}
+
+integrity_check insert-99.0
+
+finish_test
diff --git a/third_party/sqlite/test/insert2.test b/third_party/sqlite/test/insert2.test
new file mode 100755
index 0000000..6876d53
--- /dev/null
+++ b/third_party/sqlite/test/insert2.test
@@ -0,0 +1,278 @@
+# 2001 September 15
+#
+# 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 file is testing the INSERT statement that takes is
+# result from a SELECT.
+#
+# $Id: insert2.test,v 1.19 2008/01/16 18:20:42 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create some tables with data that we can select against
+#
+do_test insert2-1.0 {
+ execsql {CREATE TABLE d1(n int, log int);}
+ for {set i 1} {$i<=20} {incr i} {
+ for {set j 0} {(1<<$j)<$i} {incr j} {}
+ execsql "INSERT INTO d1 VALUES($i,$j)"
+ }
+ execsql {SELECT * FROM d1 ORDER BY n}
+} {1 0 2 1 3 2 4 2 5 3 6 3 7 3 8 3 9 4 10 4 11 4 12 4 13 4 14 4 15 4 16 4 17 5 18 5 19 5 20 5}
+
+# Insert into a new table from the old one.
+#
+do_test insert2-1.1.1 {
+ execsql {
+ CREATE TABLE t1(log int, cnt int);
+ PRAGMA count_changes=on;
+ }
+ ifcapable explain {
+ execsql {
+ EXPLAIN INSERT INTO t1 SELECT log, count(*) FROM d1 GROUP BY log;
+ }
+ }
+ execsql {
+ INSERT INTO t1 SELECT log, count(*) FROM d1 GROUP BY log;
+ }
+} {6}
+do_test insert2-1.1.2 {
+ db changes
+} {6}
+do_test insert2-1.1.3 {
+ execsql {SELECT * FROM t1 ORDER BY log}
+} {0 1 1 1 2 2 3 4 4 8 5 4}
+
+ifcapable compound {
+do_test insert2-1.2.1 {
+ catch {execsql {DROP TABLE t1}}
+ execsql {
+ CREATE TABLE t1(log int, cnt int);
+ INSERT INTO t1
+ SELECT log, count(*) FROM d1 GROUP BY log
+ EXCEPT SELECT n-1,log FROM d1;
+ }
+} {4}
+do_test insert2-1.2.2 {
+ execsql {
+ SELECT * FROM t1 ORDER BY log;
+ }
+} {0 1 3 4 4 8 5 4}
+do_test insert2-1.3.1 {
+ catch {execsql {DROP TABLE t1}}
+ execsql {
+ CREATE TABLE t1(log int, cnt int);
+ PRAGMA count_changes=off;
+ INSERT INTO t1
+ SELECT log, count(*) FROM d1 GROUP BY log
+ INTERSECT SELECT n-1,log FROM d1;
+ }
+} {}
+do_test insert2-1.3.2 {
+ execsql {
+ SELECT * FROM t1 ORDER BY log;
+ }
+} {1 1 2 2}
+} ;# ifcapable compound
+execsql {PRAGMA count_changes=off;}
+
+do_test insert2-1.4 {
+ catch {execsql {DROP TABLE t1}}
+ set r [execsql {
+ CREATE TABLE t1(log int, cnt int);
+ CREATE INDEX i1 ON t1(log);
+ CREATE INDEX i2 ON t1(cnt);
+ INSERT INTO t1 SELECT log, count() FROM d1 GROUP BY log;
+ SELECT * FROM t1 ORDER BY log;
+ }]
+ lappend r [execsql {SELECT cnt FROM t1 WHERE log=3}]
+ lappend r [execsql {SELECT log FROM t1 WHERE cnt=4 ORDER BY log}]
+} {0 1 1 1 2 2 3 4 4 8 5 4 4 {3 5}}
+
+do_test insert2-2.0 {
+ execsql {
+ CREATE TABLE t3(a,b,c);
+ CREATE TABLE t4(x,y);
+ INSERT INTO t4 VALUES(1,2);
+ SELECT * FROM t4;
+ }
+} {1 2}
+do_test insert2-2.1 {
+ execsql {
+ INSERT INTO t3(a,c) SELECT * FROM t4;
+ SELECT * FROM t3;
+ }
+} {1 {} 2}
+do_test insert2-2.2 {
+ execsql {
+ DELETE FROM t3;
+ INSERT INTO t3(c,b) SELECT * FROM t4;
+ SELECT * FROM t3;
+ }
+} {{} 2 1}
+do_test insert2-2.3 {
+ execsql {
+ DELETE FROM t3;
+ INSERT INTO t3(c,a,b) SELECT x, 'hi', y FROM t4;
+ SELECT * FROM t3;
+ }
+} {hi 2 1}
+
+integrity_check insert2-3.0
+
+# File table t4 with lots of data
+#
+do_test insert2-3.1 {
+ execsql {
+ SELECT * from t4;
+ }
+} {1 2}
+do_test insert2-3.2 {
+ set x [db total_changes]
+ execsql {
+ BEGIN;
+ INSERT INTO t4 VALUES(2,4);
+ INSERT INTO t4 VALUES(3,6);
+ INSERT INTO t4 VALUES(4,8);
+ INSERT INTO t4 VALUES(5,10);
+ INSERT INTO t4 VALUES(6,12);
+ INSERT INTO t4 VALUES(7,14);
+ INSERT INTO t4 VALUES(8,16);
+ INSERT INTO t4 VALUES(9,18);
+ INSERT INTO t4 VALUES(10,20);
+ COMMIT;
+ }
+ expr [db total_changes] - $x
+} {9}
+do_test insert2-3.2.1 {
+ execsql {
+ SELECT count(*) FROM t4;
+ }
+} {10}
+do_test insert2-3.3 {
+ ifcapable subquery {
+ execsql {
+ BEGIN;
+ INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
+ INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
+ INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
+ INSERT INTO t4 SELECT x+(SELECT max(x) FROM t4),y FROM t4;
+ COMMIT;
+ SELECT count(*) FROM t4;
+ }
+ } else {
+ db function max_x_t4 {execsql {SELECT max(x) FROM t4}}
+ execsql {
+ BEGIN;
+ INSERT INTO t4 SELECT x+max_x_t4() ,y FROM t4;
+ INSERT INTO t4 SELECT x+max_x_t4() ,y FROM t4;
+ INSERT INTO t4 SELECT x+max_x_t4() ,y FROM t4;
+ INSERT INTO t4 SELECT x+max_x_t4() ,y FROM t4;
+ COMMIT;
+ SELECT count(*) FROM t4;
+ }
+ }
+} {160}
+do_test insert2-3.4 {
+ execsql {
+ BEGIN;
+ UPDATE t4 SET y='lots of data for the row where x=' || x
+ || ' and y=' || y || ' - even more data to fill space';
+ COMMIT;
+ SELECT count(*) FROM t4;
+ }
+} {160}
+do_test insert2-3.5 {
+ ifcapable subquery {
+ execsql {
+ BEGIN;
+ INSERT INTO t4 SELECT x+(SELECT max(x)+1 FROM t4),y FROM t4;
+ SELECT count(*) from t4;
+ ROLLBACK;
+ }
+ } else {
+ execsql {
+ BEGIN;
+ INSERT INTO t4 SELECT x+max_x_t4()+1,y FROM t4;
+ SELECT count(*) from t4;
+ ROLLBACK;
+ }
+ }
+} {320}
+do_test insert2-3.6 {
+ execsql {
+ SELECT count(*) FROM t4;
+ }
+} {160}
+do_test insert2-3.7 {
+ execsql {
+ BEGIN;
+ DELETE FROM t4 WHERE x!=123;
+ SELECT count(*) FROM t4;
+ ROLLBACK;
+ }
+} {1}
+do_test insert2-3.8 {
+ db changes
+} {159}
+integrity_check insert2-3.9
+
+# Ticket #901
+#
+ifcapable tempdb {
+ do_test insert2-4.1 {
+ execsql {
+ CREATE TABLE Dependencies(depId integer primary key,
+ class integer, name str, flag str);
+ CREATE TEMPORARY TABLE DepCheck(troveId INT, depNum INT,
+ flagCount INT, isProvides BOOL, class INTEGER, name STRING,
+ flag STRING);
+ INSERT INTO DepCheck
+ VALUES(-1, 0, 1, 0, 2, 'libc.so.6', 'GLIBC_2.0');
+ INSERT INTO Dependencies
+ SELECT DISTINCT
+ NULL,
+ DepCheck.class,
+ DepCheck.name,
+ DepCheck.flag
+ FROM DepCheck LEFT OUTER JOIN Dependencies ON
+ DepCheck.class == Dependencies.class AND
+ DepCheck.name == Dependencies.name AND
+ DepCheck.flag == Dependencies.flag
+ WHERE
+ Dependencies.depId is NULL;
+ };
+ } {}
+}
+
+#--------------------------------------------------------------------
+# Test that the INSERT works when the SELECT statement (a) references
+# the table being inserted into and (b) is optimized to use an index
+# only.
+do_test insert2-5.1 {
+ execsql {
+ CREATE TABLE t2(a, b);
+ INSERT INTO t2 VALUES(1, 2);
+ CREATE INDEX t2i1 ON t2(a);
+ INSERT INTO t2 SELECT a, 3 FROM t2 WHERE a = 1;
+ SELECT * FROM t2;
+ }
+} {1 2 1 3}
+ifcapable subquery {
+ do_test insert2-5.2 {
+ execsql {
+ INSERT INTO t2 SELECT (SELECT a FROM t2), 4;
+ SELECT * FROM t2;
+ }
+ } {1 2 1 3 1 4}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/insert3.test b/third_party/sqlite/test/insert3.test
new file mode 100755
index 0000000..825b2ac
--- /dev/null
+++ b/third_party/sqlite/test/insert3.test
@@ -0,0 +1,171 @@
+# 2005 January 13
+#
+# 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 file is testing corner cases of the INSERT statement.
+#
+# $Id: insert3.test,v 1.7 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# All the tests in this file require trigger support
+#
+ifcapable {trigger} {
+
+# Create a table and a corresponding insert trigger. Do a self-insert
+# into the table.
+#
+do_test insert3-1.0 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ CREATE TABLE log(x UNIQUE, y);
+ CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
+ UPDATE log SET y=y+1 WHERE x=new.a;
+ INSERT OR IGNORE INTO log VALUES(new.a, 1);
+ END;
+ INSERT INTO t1 VALUES('hello','world');
+ INSERT INTO t1 VALUES(5,10);
+ SELECT * FROM log ORDER BY x;
+ }
+} {5 1 hello 1}
+do_test insert3-1.1 {
+ execsql {
+ INSERT INTO t1 SELECT a, b+10 FROM t1;
+ SELECT * FROM log ORDER BY x;
+ }
+} {5 2 hello 2}
+do_test insert3-1.2 {
+ execsql {
+ CREATE TABLE log2(x PRIMARY KEY,y);
+ CREATE TRIGGER r2 BEFORE INSERT ON t1 BEGIN
+ UPDATE log2 SET y=y+1 WHERE x=new.b;
+ INSERT OR IGNORE INTO log2 VALUES(new.b,1);
+ END;
+ INSERT INTO t1 VALUES(453,'hi');
+ SELECT * FROM log ORDER BY x;
+ }
+} {5 2 453 1 hello 2}
+do_test insert3-1.3 {
+ execsql {
+ SELECT * FROM log2 ORDER BY x;
+ }
+} {hi 1}
+ifcapable compound {
+ do_test insert3-1.4.1 {
+ execsql {
+ INSERT INTO t1 SELECT * FROM t1;
+ SELECT 'a:', x, y FROM log UNION ALL
+ SELECT 'b:', x, y FROM log2 ORDER BY x;
+ }
+ } {a: 5 4 b: 10 2 b: 20 1 a: 453 2 a: hello 4 b: hi 2 b: world 1}
+ do_test insert3-1.4.2 {
+ execsql {
+ SELECT 'a:', x, y FROM log UNION ALL
+ SELECT 'b:', x, y FROM log2 ORDER BY x, y;
+ }
+ } {a: 5 4 b: 10 2 b: 20 1 a: 453 2 a: hello 4 b: hi 2 b: world 1}
+ do_test insert3-1.5 {
+ execsql {
+ INSERT INTO t1(a) VALUES('xyz');
+ SELECT * FROM log ORDER BY x;
+ }
+ } {5 4 453 2 hello 4 xyz 1}
+}
+
+do_test insert3-2.1 {
+ execsql {
+ CREATE TABLE t2(
+ a INTEGER PRIMARY KEY,
+ b DEFAULT 'b',
+ c DEFAULT 'c'
+ );
+ CREATE TABLE t2dup(a,b,c);
+ CREATE TRIGGER t2r1 BEFORE INSERT ON t2 BEGIN
+ INSERT INTO t2dup(a,b,c) VALUES(new.a,new.b,new.c);
+ END;
+ INSERT INTO t2(a) VALUES(123);
+ INSERT INTO t2(b) VALUES(234);
+ INSERT INTO t2(c) VALUES(345);
+ SELECT * FROM t2dup;
+ }
+} {123 b c -1 234 c -1 b 345}
+do_test insert3-2.2 {
+ execsql {
+ DELETE FROM t2dup;
+ INSERT INTO t2(a) SELECT 1 FROM t1 LIMIT 1;
+ INSERT INTO t2(b) SELECT 987 FROM t1 LIMIT 1;
+ INSERT INTO t2(c) SELECT 876 FROM t1 LIMIT 1;
+ SELECT * FROM t2dup;
+ }
+} {1 b c -1 987 c -1 b 876}
+
+# Test for proper detection of malformed WHEN clauses on INSERT triggers.
+#
+do_test insert3-3.1 {
+ execsql {
+ CREATE TABLE t3(a,b,c);
+ CREATE TRIGGER t3r1 BEFORE INSERT on t3 WHEN nosuchcol BEGIN
+ SELECT 'illegal WHEN clause';
+ END;
+ }
+} {}
+do_test insert3-3.2 {
+ catchsql {
+ INSERT INTO t3 VALUES(1,2,3)
+ }
+} {1 {no such column: nosuchcol}}
+do_test insert3-3.3 {
+ execsql {
+ CREATE TABLE t4(a,b,c);
+ CREATE TRIGGER t4r1 AFTER INSERT on t4 WHEN nosuchcol BEGIN
+ SELECT 'illegal WHEN clause';
+ END;
+ }
+} {}
+do_test insert3-3.4 {
+ catchsql {
+ INSERT INTO t4 VALUES(1,2,3)
+ }
+} {1 {no such column: nosuchcol}}
+
+} ;# ifcapable {trigger}
+
+# Tests for the INSERT INTO ... DEFAULT VALUES construct
+#
+do_test insert3-3.5 {
+ execsql {
+ CREATE TABLE t5(
+ a INTEGER PRIMARY KEY,
+ b DEFAULT 'xyz'
+ );
+ INSERT INTO t5 DEFAULT VALUES;
+ SELECT * FROM t5;
+ }
+} {1 xyz}
+do_test insert3-3.6 {
+ execsql {
+ INSERT INTO t5 DEFAULT VALUES;
+ SELECT * FROM t5;
+ }
+} {1 xyz 2 xyz}
+
+ifcapable bloblit {
+ do_test insert3-3.7 {
+ execsql {
+ CREATE TABLE t6(x,y DEFAULT 4.3, z DEFAULT x'6869');
+ INSERT INTO t6 DEFAULT VALUES;
+ SELECT * FROM t6;
+ }
+ } {{} 4.3 hi}
+}
+db close
+
+finish_test
diff --git a/third_party/sqlite/test/insert4.test b/third_party/sqlite/test/insert4.test
new file mode 100755
index 0000000..7e87612
--- /dev/null
+++ b/third_party/sqlite/test/insert4.test
@@ -0,0 +1,306 @@
+# 2007 January 24
+#
+# 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 file is testing the INSERT transfer optimization.
+#
+# $Id: insert4.test,v 1.10 2008/01/21 16:22:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !view||!subquery {
+ finish_test
+ return
+}
+
+# The sqlite3_xferopt_count variable is incremented whenever the
+# insert transfer optimization applies.
+#
+# This procedure runs a test to see if the sqlite3_xferopt_count is
+# set to N.
+#
+proc xferopt_test {testname N} {
+ do_test $testname {set ::sqlite3_xferopt_count} $N
+}
+
+# Create tables used for testing.
+#
+execsql {
+ PRAGMA legacy_file_format = 0;
+ CREATE TABLE t1(a int, b int, check(b>a));
+ CREATE TABLE t2(x int, y int);
+ CREATE VIEW v2 AS SELECT y, x FROM t2;
+ CREATE TABLE t3(a int, b int);
+}
+
+# Ticket #2252. Make sure the an INSERT from identical tables
+# does not violate constraints.
+#
+do_test insert4-1.1 {
+ set sqlite3_xferopt_count 0
+ execsql {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t2 VALUES(9,1);
+ }
+ catchsql {
+ INSERT INTO t1 SELECT * FROM t2;
+ }
+} {1 {constraint failed}}
+xferopt_test insert4-1.2 0
+do_test insert4-1.3 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {}
+
+# Tests to make sure that the transfer optimization is not occurring
+# when it is not a valid optimization.
+#
+# The SELECT must be against a real table.
+do_test insert4-2.1.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT 4, 8;
+ SELECT * FROM t1;
+ }
+} {4 8}
+xferopt_test insert4-2.1.2 0
+do_test insert4-2.2.1 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT * FROM v2;
+ SELECT * FROM t1;
+ }
+} {0 {1 9}}
+xferopt_test insert4-2.2.2 0
+
+# Do not run the transfer optimization if there is a LIMIT clause
+#
+do_test insert4-2.3.1 {
+ execsql {
+ DELETE FROM t2;
+ INSERT INTO t2 VALUES(9,1);
+ INSERT INTO t2 SELECT y, x FROM t2;
+ INSERT INTO t3 SELECT * FROM t2 LIMIT 1;
+ SELECT * FROM t3;
+ }
+} {9 1}
+xferopt_test insert4-2.3.2 0
+do_test insert4-2.3.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT * FROM t2 LIMIT 1;
+ SELECT * FROM t1;
+ }
+} {1 {constraint failed}}
+xferopt_test insert4-2.3.4 0
+
+# Do not run the transfer optimization if there is a DISTINCT
+#
+do_test insert4-2.4.1 {
+ execsql {
+ DELETE FROM t3;
+ INSERT INTO t3 SELECT DISTINCT * FROM t2;
+ SELECT * FROM t3;
+ }
+} {1 9 9 1}
+xferopt_test insert4-2.4.2 0
+do_test insert4-2.4.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT DISTINCT * FROM t2;
+ }
+} {1 {constraint failed}}
+xferopt_test insert4-2.4.4 0
+
+# The following procedure constructs two tables then tries to transfer
+# data from one table to the other. Checks are made to make sure the
+# transfer is successful and that the transfer optimization was used or
+# not, as appropriate.
+#
+# xfer_check TESTID XFER-USED INIT-DATA DEST-SCHEMA SRC-SCHEMA
+#
+# The TESTID argument is the symbolic name for this test. The XFER-USED
+# argument is true if the transfer optimization should be employed and
+# false if not. INIT-DATA is a single row of data that is to be
+# transfered. DEST-SCHEMA and SRC-SCHEMA are table declarations for
+# the destination and source tables.
+#
+proc xfer_check {testid xferused initdata destschema srcschema} {
+ execsql "CREATE TABLE dest($destschema)"
+ execsql "CREATE TABLE src($srcschema)"
+ execsql "INSERT INTO src VALUES([join $initdata ,])"
+ set ::sqlite3_xferopt_count 0
+ do_test $testid.1 {
+ execsql {
+ INSERT INTO dest SELECT * FROM src;
+ SELECT * FROM dest;
+ }
+ } $initdata
+ do_test $testid.2 {
+ set ::sqlite3_xferopt_count
+ } $xferused
+ execsql {
+ DROP TABLE dest;
+ DROP TABLE src;
+ }
+}
+
+
+# Do run the transfer optimization if tables have identical
+# CHECK constraints.
+#
+xfer_check insert4-3.1 1 {1 9} \
+ {a int, b int CHECK(b>a)} \
+ {x int, y int CHECK(y>x)}
+xfer_check insert4-3.2 1 {1 9} \
+ {a int, b int CHECK(b>a)} \
+ {x int CHECK(y>x), y int}
+
+# Do run the transfer optimization if the destination table lacks
+# any CHECK constraints regardless of whether or not there are CHECK
+# constraints on the source table.
+#
+xfer_check insert4-3.3 1 {1 9} \
+ {a int, b int} \
+ {x int, y int CHECK(y>x)}
+
+# Do run the transfer optimization if the destination table omits
+# NOT NULL constraints that the source table has.
+#
+xfer_check insert4-3.4 0 {1 9} \
+ {a int, b int CHECK(b>a)} \
+ {x int, y int}
+
+# Do not run the optimization if the destination has NOT NULL
+# constraints that the source table lacks.
+#
+xfer_check insert4-3.5 0 {1 9} \
+ {a int, b int NOT NULL} \
+ {x int, y int}
+xfer_check insert4-3.6 0 {1 9} \
+ {a int, b int NOT NULL} \
+ {x int NOT NULL, y int}
+xfer_check insert4-3.7 0 {1 9} \
+ {a int NOT NULL, b int NOT NULL} \
+ {x int NOT NULL, y int}
+xfer_check insert4-3.8 0 {1 9} \
+ {a int NOT NULL, b int} \
+ {x int, y int}
+
+
+# Do run the transfer optimization if the destination table and
+# source table have the same NOT NULL constraints or if the
+# source table has extra NOT NULL constraints.
+#
+xfer_check insert4-3.9 1 {1 9} \
+ {a int, b int} \
+ {x int NOT NULL, y int}
+xfer_check insert4-3.10 1 {1 9} \
+ {a int, b int} \
+ {x int NOT NULL, y int NOT NULL}
+xfer_check insert4-3.11 1 {1 9} \
+ {a int NOT NULL, b int} \
+ {x int NOT NULL, y int NOT NULL}
+xfer_check insert4-3.12 1 {1 9} \
+ {a int, b int NOT NULL} \
+ {x int NOT NULL, y int NOT NULL}
+
+# Do not run the optimization if any corresponding table
+# columns have different affinities.
+#
+xfer_check insert4-3.20 0 {1 9} \
+ {a text, b int} \
+ {x int, b int}
+xfer_check insert4-3.21 0 {1 9} \
+ {a int, b int} \
+ {x text, b int}
+
+# "int" and "integer" are equivalent so the optimization should
+# run here.
+#
+xfer_check insert4-3.22 1 {1 9} \
+ {a int, b int} \
+ {x integer, b int}
+
+# Ticket #2291.
+#
+
+do_test insert4-4.1a {
+ execsql {CREATE TABLE t4(a, b, UNIQUE(a,b))}
+} {}
+ifcapable vacuum {
+ do_test insert4-4.1b {
+ execsql {
+ INSERT INTO t4 VALUES(NULL,0);
+ INSERT INTO t4 VALUES(NULL,1);
+ INSERT INTO t4 VALUES(NULL,1);
+ VACUUM;
+ }
+ } {}
+}
+
+# Check some error conditions:
+#
+do_test insert4-5.1 {
+ # Table does not exist.
+ catchsql { INSERT INTO t2 SELECT * FROM nosuchtable }
+} {1 {no such table: nosuchtable}}
+do_test insert4-5.2 {
+ # Number of columns does not match.
+ catchsql {
+ CREATE TABLE t5(a, b, c);
+ INSERT INTO t4 SELECT * FROM t5;
+ }
+} {1 {table t4 has 2 columns but 3 values were supplied}}
+
+do_test insert4-6.1 {
+ set ::sqlite3_xferopt_count 0
+ execsql {
+ CREATE INDEX t2_i2 ON t2(x, y COLLATE nocase);
+ CREATE INDEX t2_i1 ON t2(x ASC, y DESC);
+ CREATE INDEX t3_i1 ON t3(a, b);
+ INSERT INTO t2 SELECT * FROM t3;
+ }
+ set ::sqlite3_xferopt_count
+} {0}
+do_test insert4-6.2 {
+ set ::sqlite3_xferopt_count 0
+ execsql {
+ DROP INDEX t2_i2;
+ INSERT INTO t2 SELECT * FROM t3;
+ }
+ set ::sqlite3_xferopt_count
+} {0}
+do_test insert4-6.3 {
+ set ::sqlite3_xferopt_count 0
+ execsql {
+ DROP INDEX t2_i1;
+ CREATE INDEX t2_i1 ON t2(x ASC, y ASC);
+ INSERT INTO t2 SELECT * FROM t3;
+ }
+ set ::sqlite3_xferopt_count
+} {1}
+do_test insert4-6.4 {
+ set ::sqlite3_xferopt_count 0
+ execsql {
+ DROP INDEX t2_i1;
+ CREATE INDEX t2_i1 ON t2(x ASC, y COLLATE RTRIM);
+ INSERT INTO t2 SELECT * FROM t3;
+ }
+ set ::sqlite3_xferopt_count
+} {0}
+
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/insert5.test b/third_party/sqlite/test/insert5.test
new file mode 100755
index 0000000..e48c2c3
--- /dev/null
+++ b/third_party/sqlite/test/insert5.test
@@ -0,0 +1,117 @@
+# 2007 November 23
+#
+# 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.
+#
+#***********************************************************************
+#
+# The tests in this file ensure that a temporary table is used
+# when required by an "INSERT INTO ... SELECT ..." statement.
+#
+# $Id: insert5.test,v 1.5 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+# Return true if the compilation of the sql passed as an argument
+# includes the opcode OpenEphemeral. An "INSERT INTO ... SELECT"
+# statement includes such an opcode if a temp-table is used
+# to store intermediate results.
+#
+proc uses_temp_table {sql} {
+ return [expr {[lsearch [execsql "EXPLAIN $sql"] OpenEphemeral]>=0}]
+}
+
+# Construct the sample database.
+#
+do_test insert5-1.0 {
+ file delete -force test2.db test2.db-journal
+ execsql {
+ CREATE TABLE MAIN(Id INTEGER, Id1 INTEGER);
+ CREATE TABLE B(Id INTEGER, Id1 INTEGER);
+ CREATE VIEW v1 AS SELECT * FROM B;
+ CREATE VIEW v2 AS SELECT * FROM MAIN;
+ INSERT INTO MAIN(Id,Id1) VALUES(2,3);
+ INSERT INTO B(Id,Id1) VALUES(2,3);
+ }
+} {}
+
+# Run the query.
+#
+ifcapable compound {
+ do_test insert5-1.1 {
+ execsql {
+ INSERT INTO B
+ SELECT * FROM B UNION ALL
+ SELECT * FROM MAIN WHERE exists (select * FROM B WHERE B.Id = MAIN.Id);
+ SELECT * FROM B;
+ }
+ } {2 3 2 3 2 3}
+} else {
+ do_test insert5-1.1 {
+ execsql {
+ INSERT INTO B SELECT * FROM B;
+ INSERT INTO B
+ SELECT * FROM MAIN WHERE exists (select * FROM B WHERE B.Id = MAIN.Id);
+ SELECT * FROM B;
+ }
+ } {2 3 2 3 2 3}
+}
+do_test insert5-2.1 {
+ uses_temp_table { INSERT INTO b SELECT * FROM main }
+} {0}
+do_test insert5-2.2 {
+ uses_temp_table { INSERT INTO b SELECT * FROM b }
+} {1}
+do_test insert5-2.3 {
+ uses_temp_table { INSERT INTO b SELECT (SELECT id FROM b), id1 FROM main }
+} {1}
+do_test insert5-2.4 {
+ uses_temp_table { INSERT INTO b SELECT id1, (SELECT id FROM b) FROM main }
+} {1}
+do_test insert5-2.5 {
+ uses_temp_table {
+ INSERT INTO b
+ SELECT * FROM main WHERE id = (SELECT id1 FROM b WHERE main.id = b.id) }
+} {1}
+do_test insert5-2.6 {
+ uses_temp_table { INSERT INTO b SELECT * FROM v1 }
+} {1}
+do_test insert5-2.7 {
+ uses_temp_table { INSERT INTO b SELECT * FROM v2 }
+} {0}
+do_test insert5-2.8 {
+ uses_temp_table {
+ INSERT INTO b
+ SELECT * FROM main WHERE id > 10 AND max(id1, (SELECT id FROM b)) > 10;
+ }
+} {1}
+
+# UPDATE: Using a column from the outer query (main.id) in the GROUP BY
+# or ORDER BY of a sub-query is no longer supported.
+#
+# do_test insert5-2.9 {
+# uses_temp_table {
+# INSERT INTO b
+# SELECT * FROM main
+# WHERE id > 10 AND (SELECT count(*) FROM v2 GROUP BY main.id)
+# }
+# } {}
+do_test insert5-2.9 {
+ catchsql {
+ INSERT INTO b
+ SELECT * FROM main
+ WHERE id > 10 AND (SELECT count(*) FROM v2 GROUP BY main.id)
+ }
+} {1 {no such column: main.id}}
+
+finish_test
diff --git a/third_party/sqlite/test/interrupt.test b/third_party/sqlite/test/interrupt.test
new file mode 100755
index 0000000..b311cbb
--- /dev/null
+++ b/third_party/sqlite/test/interrupt.test
@@ -0,0 +1,183 @@
+# 2004 Feb 8
+#
+# 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 the sqlite_interrupt() API.
+#
+# $Id: interrupt.test,v 1.16 2008/01/16 17:46:38 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set DB [sqlite3_connection_pointer db]
+
+# This routine attempts to execute the sql in $sql. It triggers an
+# interrupt at progressively later and later points during the processing
+# and checks to make sure SQLITE_INTERRUPT is returned. Eventually,
+# the routine completes successfully.
+#
+proc interrupt_test {testid sql result {initcnt 0}} {
+ set orig_sum [cksum]
+ set i $initcnt
+ while 1 {
+ incr i
+ set ::sqlite_interrupt_count $i
+ do_test $testid.$i.1 [format {
+ set ::r [catchsql %s]
+ set ::code [db errorcode]
+ expr {$::code==0 || $::code==9}
+ } [list $sql]] 1
+ if {$::code==9} {
+ do_test $testid.$i.2 {
+ cksum
+ } $orig_sum
+ } else {
+ do_test $testid.$i.99 {
+ set ::r
+ } [list 0 $result]
+ break
+ }
+ }
+ set ::sqlite_interrupt_count 0
+}
+
+do_test interrupt-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ SELECT name FROM sqlite_master;
+ }
+} {t1}
+interrupt_test interrupt-1.2 {DROP TABLE t1} {}
+do_test interrupt-1.3 {
+ execsql {
+ SELECT name FROM sqlite_master;
+ }
+} {}
+integrity_check interrupt-1.4
+
+do_test interrrupt-2.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,randstr(300,400));
+ INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1;
+ INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1;
+ INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1;
+ INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1;
+ INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1;
+ INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1;
+ COMMIT;
+ UPDATE t1 SET b=substr(b,-5,5);
+ SELECT count(*) from t1;
+ }
+} 64
+set origsize [file size test.db]
+set cksum [db eval {SELECT md5sum(a || b) FROM t1}]
+ifcapable {vacuum} {
+ interrupt_test interrupt-2.2 {VACUUM} {} 100
+}
+do_test interrupt-2.3 {
+ execsql {
+ SELECT md5sum(a || b) FROM t1;
+ }
+} $cksum
+ifcapable {vacuum && !default_autovacuum} {
+ do_test interrupt-2.4 {
+ expr {$::origsize>[file size test.db]}
+ } 1
+}
+ifcapable {explain} {
+ do_test interrupt-2.5 {
+ set sql {EXPLAIN SELECT max(a,b), a, b FROM t1}
+ execsql $sql
+ set rc [catch {db eval $sql {sqlite3_interrupt $DB}} msg]
+ lappend rc $msg
+ } {1 interrupted}
+}
+integrity_check interrupt-2.6
+
+# Ticket #594. If an interrupt occurs in the middle of a transaction
+# and that transaction is later rolled back, the internal schema tables do
+# not reset.
+#
+# UPDATE: Interrupting a DML statement in the middle of a transaction now
+# causes the transaction to roll back. Leaving the transaction open after
+# an SQL statement was interrupted halfway through risks database corruption.
+#
+ifcapable tempdb {
+ for {set i 1} {$i<50} {incr i 5} {
+ do_test interrupt-3.$i.1 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE t2(x,y);
+ SELECT name FROM sqlite_temp_master;
+ }
+ } {t2}
+ do_test interrupt-3.$i.2 {
+ set ::sqlite_interrupt_count $::i
+ catchsql {
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+ } {1 interrupted}
+ do_test interrupt-3.$i.3 {
+ execsql {
+ SELECT name FROM sqlite_temp_master;
+ }
+ } {}
+ do_test interrupt-3.$i.4 {
+ catchsql {
+ ROLLBACK
+ }
+ } {1 {cannot rollback - no transaction is active}}
+ do_test interrupt-3.$i.5 {
+ catchsql {SELECT name FROM sqlite_temp_master};
+ execsql {
+ SELECT name FROM sqlite_temp_master;
+ }
+ } {}
+ }
+}
+
+# There are reports of a memory leak if an interrupt occurs during
+# the beginning of a complex query - before the first callback. We
+# will try to reproduce it here:
+#
+execsql {
+ CREATE TABLE t2(a,b,c);
+ INSERT INTO t2 SELECT round(a/10), randstr(50,80), randstr(50,60) FROM t1;
+}
+set sql {
+ SELECT max(min(b,c)), min(max(b,c)), a FROM t2 GROUP BY a ORDER BY a;
+}
+set sqlite_interrupt_count 1000000
+execsql $sql
+set max_count [expr {1000000-$sqlite_interrupt_count}]
+for {set i 1} {$i<$max_count-5} {incr i 1} {
+ do_test interrupt-4.$i.1 {
+ set ::sqlite_interrupt_count $::i
+ catchsql $sql
+ } {1 interrupted}
+}
+
+# Interrupt during parsing
+#
+do_test interrupt-5.1 {
+ proc fake_interrupt {args} {
+ db collate fake_collation no-op
+ sqlite3_interrupt db
+ return SQLITE_OK
+ }
+ db collation_needed fake_interrupt
+ catchsql {
+ CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC);
+ }
+} {1 interrupt}
+
+finish_test
diff --git a/third_party/sqlite/test/intpkey.test b/third_party/sqlite/test/intpkey.test
new file mode 100755
index 0000000..05b6cdf
--- /dev/null
+++ b/third_party/sqlite/test/intpkey.test
@@ -0,0 +1,605 @@
+# 2001 September 15
+#
+# 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.
+#
+# This file implements tests for the special processing associated
+# with INTEGER PRIMARY KEY columns.
+#
+# $Id: intpkey.test,v 1.24 2007/11/29 17:43:28 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table with a primary key and a datatype other than
+# integer
+#
+do_test intpkey-1.0 {
+ execsql {
+ CREATE TABLE t1(a TEXT PRIMARY KEY, b, c);
+ }
+} {}
+
+# There should be an index associated with the primary key
+#
+do_test intpkey-1.1 {
+ execsql {
+ SELECT name FROM sqlite_master
+ WHERE type='index' AND tbl_name='t1';
+ }
+} {sqlite_autoindex_t1_1}
+
+# Now create a table with an integer primary key and verify that
+# there is no associated index.
+#
+do_test intpkey-1.2 {
+ execsql {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ SELECT name FROM sqlite_master
+ WHERE type='index' AND tbl_name='t1';
+ }
+} {}
+
+# Insert some records into the new table. Specify the primary key
+# and verify that the key is used as the record number.
+#
+do_test intpkey-1.3 {
+ execsql {
+ INSERT INTO t1 VALUES(5,'hello','world');
+ }
+ db last_insert_rowid
+} {5}
+do_test intpkey-1.4 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {5 hello world}
+do_test intpkey-1.5 {
+ execsql {
+ SELECT rowid, * FROM t1;
+ }
+} {5 5 hello world}
+
+# Attempting to insert a duplicate primary key should give a constraint
+# failure.
+#
+do_test intpkey-1.6 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES(5,'second','entry');
+ }} msg]
+ lappend r $msg
+} {1 {PRIMARY KEY must be unique}}
+do_test intpkey-1.7 {
+ execsql {
+ SELECT rowid, * FROM t1;
+ }
+} {5 5 hello world}
+do_test intpkey-1.8 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES(6,'second','entry');
+ }} msg]
+ lappend r $msg
+} {0 {}}
+do_test intpkey-1.8.1 {
+ db last_insert_rowid
+} {6}
+do_test intpkey-1.9 {
+ execsql {
+ SELECT rowid, * FROM t1;
+ }
+} {5 5 hello world 6 6 second entry}
+
+# A ROWID is automatically generated for new records that do not specify
+# the integer primary key.
+#
+do_test intpkey-1.10 {
+ execsql {
+ INSERT INTO t1(b,c) VALUES('one','two');
+ SELECT b FROM t1 ORDER BY b;
+ }
+} {hello one second}
+
+# Try to change the ROWID for the new entry.
+#
+do_test intpkey-1.11 {
+ execsql {
+ UPDATE t1 SET a=4 WHERE b='one';
+ SELECT * FROM t1;
+ }
+} {4 one two 5 hello world 6 second entry}
+
+# Make sure SELECT statements are able to use the primary key column
+# as an index.
+#
+do_test intpkey-1.12.1 {
+ execsql {
+ SELECT * FROM t1 WHERE a==4;
+ }
+} {4 one two}
+do_test intpkey-1.12.2 {
+ set sqlite_query_plan
+} {t1 *}
+
+# Try to insert a non-integer value into the primary key field. This
+# should result in a data type mismatch.
+#
+do_test intpkey-1.13.1 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES('x','y','z');
+ }} msg]
+ lappend r $msg
+} {1 {datatype mismatch}}
+do_test intpkey-1.13.2 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES('','y','z');
+ }} msg]
+ lappend r $msg
+} {1 {datatype mismatch}}
+do_test intpkey-1.14 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES(3.4,'y','z');
+ }} msg]
+ lappend r $msg
+} {1 {datatype mismatch}}
+do_test intpkey-1.15 {
+ set r [catch {execsql {
+ INSERT INTO t1 VALUES(-3,'y','z');
+ }} msg]
+ lappend r $msg
+} {0 {}}
+do_test intpkey-1.16 {
+ execsql {SELECT * FROM t1}
+} {-3 y z 4 one two 5 hello world 6 second entry}
+
+#### INDICES
+# Check to make sure indices work correctly with integer primary keys
+#
+do_test intpkey-2.1 {
+ execsql {
+ CREATE INDEX i1 ON t1(b);
+ SELECT * FROM t1 WHERE b=='y'
+ }
+} {-3 y z}
+do_test intpkey-2.1.1 {
+ execsql {
+ SELECT * FROM t1 WHERE b=='y' AND rowid<0
+ }
+} {-3 y z}
+do_test intpkey-2.1.2 {
+ execsql {
+ SELECT * FROM t1 WHERE b=='y' AND rowid<0 AND rowid>=-20
+ }
+} {-3 y z}
+do_test intpkey-2.1.3 {
+ execsql {
+ SELECT * FROM t1 WHERE b>='y'
+ }
+} {-3 y z}
+do_test intpkey-2.1.4 {
+ execsql {
+ SELECT * FROM t1 WHERE b>='y' AND rowid<10
+ }
+} {-3 y z}
+
+do_test intpkey-2.2 {
+ execsql {
+ UPDATE t1 SET a=8 WHERE b=='y';
+ SELECT * FROM t1 WHERE b=='y';
+ }
+} {8 y z}
+do_test intpkey-2.3 {
+ execsql {
+ SELECT rowid, * FROM t1;
+ }
+} {4 4 one two 5 5 hello world 6 6 second entry 8 8 y z}
+do_test intpkey-2.4 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE b<'second'
+ }
+} {5 5 hello world 4 4 one two}
+do_test intpkey-2.4.1 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE 'second'>b
+ }
+} {5 5 hello world 4 4 one two}
+do_test intpkey-2.4.2 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b
+ }
+} {4 4 one two 5 5 hello world}
+do_test intpkey-2.4.3 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b AND 0<rowid
+ }
+} {4 4 one two 5 5 hello world}
+do_test intpkey-2.5 {
+ execsql {
+ SELECT rowid, * FROM t1 WHERE b>'a'
+ }
+} {5 5 hello world 4 4 one two 6 6 second entry 8 8 y z}
+do_test intpkey-2.6 {
+ execsql {
+ DELETE FROM t1 WHERE rowid=4;
+ SELECT * FROM t1 WHERE b>'a';
+ }
+} {5 hello world 6 second entry 8 y z}
+do_test intpkey-2.7 {
+ execsql {
+ UPDATE t1 SET a=-4 WHERE rowid=8;
+ SELECT * FROM t1 WHERE b>'a';
+ }
+} {5 hello world 6 second entry -4 y z}
+do_test intpkey-2.7 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {-4 y z 5 hello world 6 second entry}
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# Create indices that include the integer primary key as one of their
+# columns.
+#
+do_test intpkey-3.1 {
+ execsql {
+ CREATE INDEX i2 ON t1(a);
+ }
+} {}
+do_test intpkey-3.2 {
+ count {
+ SELECT * FROM t1 WHERE a=5;
+ }
+} {5 hello world 0}
+do_test intpkey-3.3 {
+ count {
+ SELECT * FROM t1 WHERE a>4 AND a<6;
+ }
+} {5 hello world 2}
+do_test intpkey-3.4 {
+ count {
+ SELECT * FROM t1 WHERE b>='hello' AND b<'hello2';
+ }
+} {5 hello world 3}
+do_test intpkey-3.5 {
+ execsql {
+ CREATE INDEX i3 ON t1(c,a);
+ }
+} {}
+do_test intpkey-3.6 {
+ count {
+ SELECT * FROM t1 WHERE c=='world';
+ }
+} {5 hello world 3}
+do_test intpkey-3.7 {
+ execsql {INSERT INTO t1 VALUES(11,'hello','world')}
+ count {
+ SELECT * FROM t1 WHERE c=='world';
+ }
+} {5 hello world 11 hello world 5}
+do_test intpkey-3.8 {
+ count {
+ SELECT * FROM t1 WHERE c=='world' AND a>7;
+ }
+} {11 hello world 4}
+do_test intpkey-3.9 {
+ count {
+ SELECT * FROM t1 WHERE 7<a;
+ }
+} {11 hello world 1}
+
+# Test inequality constraints on integer primary keys and rowids
+#
+do_test intpkey-4.1 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid
+ }
+} {11 hello world 0}
+do_test intpkey-4.2 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid AND b=='hello'
+ }
+} {11 hello world 0}
+do_test intpkey-4.3 {
+ count {
+ SELECT * FROM t1 WHERE 11=rowid AND b=='hello' AND c IS NOT NULL;
+ }
+} {11 hello world 0}
+do_test intpkey-4.4 {
+ count {
+ SELECT * FROM t1 WHERE rowid==11
+ }
+} {11 hello world 0}
+do_test intpkey-4.5 {
+ count {
+ SELECT * FROM t1 WHERE oid==11 AND b=='hello'
+ }
+} {11 hello world 0}
+do_test intpkey-4.6 {
+ count {
+ SELECT * FROM t1 WHERE a==11 AND b=='hello' AND c IS NOT NULL;
+ }
+} {11 hello world 0}
+
+do_test intpkey-4.7 {
+ count {
+ SELECT * FROM t1 WHERE 8<rowid;
+ }
+} {11 hello world 1}
+do_test intpkey-4.8 {
+ count {
+ SELECT * FROM t1 WHERE 8<rowid AND 11>=oid;
+ }
+} {11 hello world 1}
+do_test intpkey-4.9 {
+ count {
+ SELECT * FROM t1 WHERE 11<=_rowid_ AND 12>=a;
+ }
+} {11 hello world 1}
+do_test intpkey-4.10 {
+ count {
+ SELECT * FROM t1 WHERE 0>=_rowid_;
+ }
+} {-4 y z 1}
+do_test intpkey-4.11 {
+ count {
+ SELECT * FROM t1 WHERE a<0;
+ }
+} {-4 y z 1}
+do_test intpkey-4.12 {
+ count {
+ SELECT * FROM t1 WHERE a<0 AND a>10;
+ }
+} {1}
+
+# Make sure it is OK to insert a rowid of 0
+#
+do_test intpkey-5.1 {
+ execsql {
+ INSERT INTO t1 VALUES(0,'zero','entry');
+ }
+ count {
+ SELECT * FROM t1 WHERE a=0;
+ }
+} {0 zero entry 0}
+do_test intpkey-5.2 {
+ execsql {
+ SELECT rowid, a FROM t1
+ }
+} {-4 -4 0 0 5 5 6 6 11 11}
+
+# Test the ability of the COPY command to put data into a
+# table that contains an integer primary key.
+#
+# COPY command has been removed. But we retain these tests so
+# that the tables will contain the right data for tests that follow.
+#
+do_test intpkey-6.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(20,'b-20','c-20');
+ INSERT INTO t1 VALUES(21,'b-21','c-21');
+ INSERT INTO t1 VALUES(22,'b-22','c-22');
+ COMMIT;
+ SELECT * FROM t1 WHERE a>=20;
+ }
+} {20 b-20 c-20 21 b-21 c-21 22 b-22 c-22}
+do_test intpkey-6.2 {
+ execsql {
+ SELECT * FROM t1 WHERE b=='hello'
+ }
+} {5 hello world 11 hello world}
+do_test intpkey-6.3 {
+ execsql {
+ DELETE FROM t1 WHERE b='b-21';
+ SELECT * FROM t1 WHERE b=='b-21';
+ }
+} {}
+do_test intpkey-6.4 {
+ execsql {
+ SELECT * FROM t1 WHERE a>=20
+ }
+} {20 b-20 c-20 22 b-22 c-22}
+
+# Do an insert of values with the columns specified out of order.
+#
+do_test intpkey-7.1 {
+ execsql {
+ INSERT INTO t1(c,b,a) VALUES('row','new',30);
+ SELECT * FROM t1 WHERE rowid>=30;
+ }
+} {30 new row}
+do_test intpkey-7.2 {
+ execsql {
+ SELECT * FROM t1 WHERE rowid>20;
+ }
+} {22 b-22 c-22 30 new row}
+
+# Do an insert from a select statement.
+#
+do_test intpkey-8.1 {
+ execsql {
+ CREATE TABLE t2(x INTEGER PRIMARY KEY, y, z);
+ INSERT INTO t2 SELECT * FROM t1;
+ SELECT rowid FROM t2;
+ }
+} {-4 0 5 6 11 20 22 30}
+do_test intpkey-8.2 {
+ execsql {
+ SELECT x FROM t2;
+ }
+} {-4 0 5 6 11 20 22 30}
+
+do_test intpkey-9.1 {
+ execsql {
+ UPDATE t1 SET c='www' WHERE c='world';
+ SELECT rowid, a, c FROM t1 WHERE c=='www';
+ }
+} {5 5 www 11 11 www}
+
+
+# Check insert of NULL for primary key
+#
+do_test intpkey-10.1 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(x INTEGER PRIMARY KEY, y, z);
+ INSERT INTO t2 VALUES(NULL, 1, 2);
+ SELECT * from t2;
+ }
+} {1 1 2}
+do_test intpkey-10.2 {
+ execsql {
+ INSERT INTO t2 VALUES(NULL, 2, 3);
+ SELECT * from t2 WHERE x=2;
+ }
+} {2 2 3}
+do_test intpkey-10.3 {
+ execsql {
+ INSERT INTO t2 SELECT NULL, z, y FROM t2;
+ SELECT * FROM t2;
+ }
+} {1 1 2 2 2 3 3 2 1 4 3 2}
+
+# This tests checks to see if a floating point number can be used
+# to reference an integer primary key.
+#
+do_test intpkey-11.1 {
+ execsql {
+ SELECT b FROM t1 WHERE a=2.0+3.0;
+ }
+} {hello}
+do_test intpkey-11.1 {
+ execsql {
+ SELECT b FROM t1 WHERE a=2.0+3.5;
+ }
+} {}
+
+integrity_check intpkey-12.1
+
+# Try to use a string that looks like a floating point number as
+# an integer primary key. This should actually work when the floating
+# point value can be rounded to an integer without loss of data.
+#
+do_test intpkey-13.1 {
+ execsql {
+ SELECT * FROM t1 WHERE a=1;
+ }
+} {}
+do_test intpkey-13.2 {
+ execsql {
+ INSERT INTO t1 VALUES('1.0',2,3);
+ SELECT * FROM t1 WHERE a=1;
+ }
+} {1 2 3}
+do_test intpkey-13.3 {
+ catchsql {
+ INSERT INTO t1 VALUES('1.5',3,4);
+ }
+} {1 {datatype mismatch}}
+ifcapable {bloblit} {
+ do_test intpkey-13.4 {
+ catchsql {
+ INSERT INTO t1 VALUES(x'123456',3,4);
+ }
+ } {1 {datatype mismatch}}
+}
+do_test intpkey-13.5 {
+ catchsql {
+ INSERT INTO t1 VALUES('+1234567890',3,4);
+ }
+} {0 {}}
+
+# Compare an INTEGER PRIMARY KEY against a TEXT expression. The INTEGER
+# affinity should be applied to the text value before the comparison
+# takes place.
+#
+do_test intpkey-14.1 {
+ execsql {
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT);
+ INSERT INTO t3 VALUES(1, 1, 'one');
+ INSERT INTO t3 VALUES(2, 2, '2');
+ INSERT INTO t3 VALUES(3, 3, 3);
+ }
+} {}
+do_test intpkey-14.2 {
+ execsql {
+ SELECT * FROM t3 WHERE a>2;
+ }
+} {3 3 3}
+do_test intpkey-14.3 {
+ execsql {
+ SELECT * FROM t3 WHERE a>'2';
+ }
+} {3 3 3}
+do_test intpkey-14.4 {
+ execsql {
+ SELECT * FROM t3 WHERE a<'2';
+ }
+} {1 1 one}
+do_test intpkey-14.5 {
+ execsql {
+ SELECT * FROM t3 WHERE a<c;
+ }
+} {1 1 one}
+do_test intpkey-14.6 {
+ execsql {
+ SELECT * FROM t3 WHERE a=c;
+ }
+} {2 2 2 3 3 3}
+
+# Check for proper handling of primary keys greater than 2^31.
+# Ticket #1188
+#
+do_test intpkey-15.1 {
+ execsql {
+ INSERT INTO t1 VALUES(2147483647, 'big-1', 123);
+ SELECT * FROM t1 WHERE a>2147483648;
+ }
+} {}
+do_test intpkey-15.2 {
+ execsql {
+ INSERT INTO t1 VALUES(NULL, 'big-2', 234);
+ SELECT b FROM t1 WHERE a>=2147483648;
+ }
+} {big-2}
+do_test intpkey-15.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a>2147483648;
+ }
+} {}
+do_test intpkey-15.4 {
+ execsql {
+ SELECT b FROM t1 WHERE a>=2147483647;
+ }
+} {big-1 big-2}
+do_test intpkey-15.5 {
+ execsql {
+ SELECT b FROM t1 WHERE a<2147483648;
+ }
+} {y zero 2 hello second hello b-20 b-22 new 3 big-1}
+do_test intpkey-15.6 {
+ execsql {
+ SELECT b FROM t1 WHERE a<12345678901;
+ }
+} {y zero 2 hello second hello b-20 b-22 new 3 big-1 big-2}
+do_test intpkey-15.7 {
+ execsql {
+ SELECT b FROM t1 WHERE a>12345678901;
+ }
+} {}
+
+
+finish_test
diff --git a/third_party/sqlite/test/io.test b/third_party/sqlite/test/io.test
new file mode 100755
index 0000000..12c756a
--- /dev/null
+++ b/third_party/sqlite/test/io.test
@@ -0,0 +1,549 @@
+# 2007 August 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.
+#
+#***********************************************************************
+#
+# The focus of this file is testing some specific characteristics of the
+# IO traffic generated by SQLite (making sure SQLite is not writing out
+# more database pages than it has to, stuff like that).
+#
+# $Id: io.test,v 1.17 2008/07/30 17:28:04 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db close
+sqlite3_simulate_device
+sqlite3 db test.db -vfs devsym
+
+# Test summary:
+#
+# io-1.* - Test that quick-balance does not journal pages unnecessarily.
+#
+# io-2.* - Test the "atomic-write optimization".
+#
+# io-3.* - Test the IO traffic enhancements triggered when the
+# IOCAP_SEQUENTIAL device capability flag is set (no
+# fsync() calls on the journal file).
+#
+# io-4.* - Test the IO traffic enhancements triggered when the
+# IOCAP_SAFE_APPEND device capability flag is set (fewer
+# fsync() calls on the journal file, no need to set nRec
+# field in the single journal header).
+#
+# io-5.* - Test that the default page size is selected and used
+# correctly.
+#
+
+set ::nWrite 0
+proc nWrite {db} {
+ set bt [btree_from_db $db]
+ db_enter $db
+ array set stats [btree_pager_stats $bt]
+ db_leave $db
+ set res [expr $stats(write) - $::nWrite]
+ set ::nWrite $stats(write)
+ set res
+}
+
+set ::nSync 0
+proc nSync {} {
+ set res [expr {$::sqlite_sync_count - $::nSync}]
+ set ::nSync $::sqlite_sync_count
+ set res
+}
+
+do_test io-1.1 {
+ execsql {
+ PRAGMA auto_vacuum = OFF;
+ PRAGMA page_size = 1024;
+ CREATE TABLE abc(a,b);
+ }
+ nWrite db
+} {2}
+
+# Insert into the table 4 records of aproximately 240 bytes each.
+# This should completely fill the root-page of the table. Each
+# INSERT causes 2 db pages to be written - the root-page of "abc"
+# and page 1 (db change-counter page).
+do_test io-1.2 {
+ set ret [list]
+ execsql { INSERT INTO abc VALUES(1,randstr(230,230)); }
+ lappend ret [nWrite db]
+ execsql { INSERT INTO abc VALUES(2,randstr(230,230)); }
+ lappend ret [nWrite db]
+ execsql { INSERT INTO abc VALUES(3,randstr(230,230)); }
+ lappend ret [nWrite db]
+ execsql { INSERT INTO abc VALUES(4,randstr(230,230)); }
+ lappend ret [nWrite db]
+} {2 2 2 2}
+
+# Insert another 240 byte record. This causes two leaf pages
+# to be added to the root page of abc. 4 pages in total
+# are written to the db file - the two leaf pages, the root
+# of abc and the change-counter page.
+do_test io-1.3 {
+ execsql { INSERT INTO abc VALUES(5,randstr(230,230)); }
+ nWrite db
+} {4}
+
+# Insert another 3 240 byte records. After this, the tree consists of
+# the root-node, which is close to empty, and two leaf pages, both of
+# which are full.
+do_test io-1.4 {
+ set ret [list]
+ execsql { INSERT INTO abc VALUES(6,randstr(230,230)); }
+ lappend ret [nWrite db]
+ execsql { INSERT INTO abc VALUES(7,randstr(230,230)); }
+ lappend ret [nWrite db]
+ execsql { INSERT INTO abc VALUES(8,randstr(230,230)); }
+ lappend ret [nWrite db]
+} {2 2 2}
+
+# This insert should use the quick-balance trick to add a third leaf
+# to the b-tree used to store table abc. It should only be necessary to
+# write to 3 pages to do this: the change-counter, the root-page and
+# the new leaf page.
+do_test io-1.5 {
+ execsql { INSERT INTO abc VALUES(9,randstr(230,230)); }
+ nWrite db
+} {3}
+
+ifcapable atomicwrite {
+
+#----------------------------------------------------------------------
+# Test cases io-2.* test the atomic-write optimization.
+#
+do_test io-2.1 {
+ execsql { DELETE FROM abc; VACUUM; }
+} {}
+
+# Clear the write and sync counts.
+nWrite db ; nSync
+
+# The following INSERT updates 2 pages and requires 4 calls to fsync():
+#
+# 1) The directory in which the journal file is created,
+# 2) The journal file (to sync the page data),
+# 3) The journal file (to sync the journal file header),
+# 4) The database file.
+#
+do_test io-2.2 {
+ execsql { INSERT INTO abc VALUES(1, 2) }
+ list [nWrite db] [nSync]
+} {2 4}
+
+# Set the device-characteristic mask to include the SQLITE_IOCAP_ATOMIC,
+# then do another INSERT similar to the one in io-2.2. This should
+# only write 1 page and require a single fsync().
+#
+# The single fsync() is the database file. Only one page is reported as
+# written because page 1 - the change-counter page - is written using
+# an out-of-band method that bypasses the write counter.
+#
+sqlite3_simulate_device -char atomic
+do_test io-2.3 {
+ execsql { INSERT INTO abc VALUES(3, 4) }
+ list [nWrite db] [nSync]
+} {1 1}
+
+# Test that the journal file is not created and the change-counter is
+# updated when the atomic-write optimization is used.
+#
+do_test io-2.4.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(5, 6);
+ }
+ sqlite3 db2 test.db -vfs devsym
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4}
+do_test io-2.4.2 {
+ file exists test.db-journal
+} {0}
+do_test io-2.4.3 {
+ execsql { COMMIT }
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4 5 6}
+db2 close
+
+# Test that the journal file is created and sync()d if the transaction
+# modifies more than one database page, even if the IOCAP_ATOMIC flag
+# is set.
+#
+do_test io-2.5.1 {
+ execsql { CREATE TABLE def(d, e) }
+ nWrite db ; nSync
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(7, 8);
+ }
+ file exists test.db-journal
+} {0}
+do_test io-2.5.2 {
+ execsql { INSERT INTO def VALUES('a', 'b'); }
+ file exists test.db-journal
+} {1}
+do_test io-2.5.3 {
+ execsql { COMMIT }
+ list [nWrite db] [nSync]
+} {3 4}
+
+# Test that the journal file is created and sync()d if the transaction
+# modifies a single database page and also appends a page to the file.
+# Internally, this case is handled differently to the one above. The
+# journal file is not actually created until the 'COMMIT' statement
+# is executed.
+#
+do_test io-2.6.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(9, randstr(1000,1000));
+ }
+ file exists test.db-journal
+} {0}
+do_test io-2.6.2 {
+ # Create a file at "test.db-journal". This will prevent SQLite from
+ # opening the journal for exclusive access. As a result, the COMMIT
+ # should fail with SQLITE_CANTOPEN and the transaction rolled back.
+ #
+ set fd [open test.db-journal w]
+ puts $fd "This is not a journal file"
+ close $fd
+ catchsql { COMMIT }
+} {1 {unable to open database file}}
+do_test io-2.6.3 {
+ file delete -force test.db-journal
+ catchsql { COMMIT }
+} {1 {cannot commit - no transaction is active}}
+do_test io-2.6.4 {
+ execsql { SELECT * FROM abc }
+} {1 2 3 4 5 6 7 8}
+
+
+# Test that if the database modification is part of multi-file commit,
+# the journal file is always created. In this case, the journal file
+# is created during execution of the COMMIT statement, so we have to
+# use the same technique to check that it is created as in the above
+# block.
+file delete -force test2.db test2.db-journal
+ifcapable attach {
+ do_test io-2.7.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.page_size = 1024;
+ CREATE TABLE aux.abc2(a, b);
+ BEGIN;
+ INSERT INTO abc VALUES(9, 10);
+ }
+ file exists test.db-journal
+ } {0}
+ do_test io-2.7.2 {
+ execsql { INSERT INTO abc2 SELECT * FROM abc }
+ file exists test2.db-journal
+ } {0}
+ do_test io-2.7.3 {
+ execsql { SELECT * FROM abc UNION ALL SELECT * FROM abc2 }
+ } {1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10}
+ do_test io-2.7.4 {
+ set fd [open test2.db-journal w]
+ puts $fd "This is not a journal file"
+ close $fd
+ catchsql { COMMIT }
+ } {1 {unable to open database file}}
+ do_test io-2.7.5 {
+ file delete -force test2.db-journal
+ catchsql { COMMIT }
+ } {1 {cannot commit - no transaction is active}}
+ do_test io-2.7.6 {
+ execsql { SELECT * FROM abc UNION ALL SELECT * FROM abc2 }
+ } {1 2 3 4 5 6 7 8}
+}
+
+# Try an explicit ROLLBACK before the journal file is created.
+#
+do_test io-2.8.1 {
+ execsql {
+ BEGIN;
+ DELETE FROM abc;
+ }
+ file exists test.db-journal
+} {0}
+do_test io-2.8.2 {
+ execsql { SELECT * FROM abc }
+} {}
+do_test io-2.8.3 {
+ execsql {
+ ROLLBACK;
+ SELECT * FROM abc;
+ }
+} {1 2 3 4 5 6 7 8}
+
+# Test that the atomic write optimisation is not enabled if the sector
+# size is larger than the page-size.
+#
+do_test io-2.9.1 {
+ sqlite3_simulate_device -char atomic -sectorsize 2048
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(9, 10);
+ }
+ file exists test.db-journal
+} {1}
+do_test io-2.9.2 {
+ execsql { ROLLBACK; }
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db -vfs devsym
+ execsql {
+ PRAGMA auto_vacuum = OFF;
+ PRAGMA page_size = 2048;
+ CREATE TABLE abc(a, b);
+ }
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(9, 10);
+ }
+ file exists test.db-journal
+} {0}
+do_test io-2.9.3 {
+ execsql { COMMIT }
+} {}
+
+# Test a couple of the more specific IOCAP_ATOMIC flags
+# (i.e IOCAP_ATOMIC2K etc.).
+#
+do_test io-2.10.1 {
+ sqlite3_simulate_device -char atomic1k
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(11, 12);
+ }
+ file exists test.db-journal
+} {1}
+do_test io-2.10.2 {
+ execsql { ROLLBACK }
+ sqlite3_simulate_device -char atomic2k
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(11, 12);
+ }
+ file exists test.db-journal
+} {0}
+do_test io-2.10.3 {
+ execsql { ROLLBACK }
+} {}
+
+do_test io-2.11.0 {
+ execsql {
+ PRAGMA locking_mode = exclusive;
+ PRAGMA locking_mode;
+ }
+} {exclusive exclusive}
+do_test io-2.11.1 {
+ execsql {
+ INSERT INTO abc VALUES(11, 12);
+ }
+ file exists test.db-journal
+} {0}
+
+do_test io-2.11.2 {
+ execsql {
+ PRAGMA locking_mode = normal;
+ INSERT INTO abc VALUES(13, 14);
+ }
+ file exists test.db-journal
+} {0}
+
+} ;# /* ifcapable atomicwrite */
+
+#----------------------------------------------------------------------
+# Test cases io-3.* test the IOCAP_SEQUENTIAL optimization.
+#
+sqlite3_simulate_device -char sequential -sectorsize 0
+ifcapable pager_pragmas {
+ do_test io-3.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db -vfs devsym
+ db eval {
+ PRAGMA auto_vacuum=OFF;
+ }
+ # File size might be 1 due to the hack to work around ticket #3260.
+ # Search for #3260 in os_unix.c for additional information.
+ expr {[file size test.db]>1}
+ } {0}
+ do_test io-3.2 {
+ execsql { CREATE TABLE abc(a, b) }
+ nSync
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ INSERT INTO abc VALUES('hello', 'world');
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ # File has grown - showing there was a cache-spill - but there
+ # have been no calls to fsync():
+ list [file size test.db] [nSync]
+ } {31744 0}
+ do_test io-3.3 {
+ # The COMMIT requires a single fsync() - to the database file.
+ execsql { COMMIT }
+ list [file size test.db] [nSync]
+ } {39936 1}
+}
+
+#----------------------------------------------------------------------
+# Test cases io-4.* test the IOCAP_SAFE_APPEND optimization.
+#
+sqlite3_simulate_device -char safe_append
+
+# With the SAFE_APPEND flag set, simple transactions require 3, rather
+# than 4, calls to fsync(). The fsync() calls are on:
+#
+# 1) The directory in which the journal file is created, (unix only)
+# 2) The journal file (to sync the page data),
+# 3) The database file.
+#
+# Normally, when the SAFE_APPEND flag is not set, there is another fsync()
+# on the journal file between steps (2) and (3) above.
+#
+if {$::tcl_platform(platform)=="unix"} {
+ set expected_sync_count 3
+} else {
+ set expected_sync_count 2
+}
+do_test io-4.1 {
+ execsql { DELETE FROM abc }
+ nSync
+ execsql { INSERT INTO abc VALUES('a', 'b') }
+ nSync
+} $expected_sync_count
+
+# With SAFE_APPEND set, the nRec field of the journal file header should
+# be set to 0xFFFFFFFF before the first journal sync. The nRec field
+# occupies bytes 8-11 of the journal file.
+#
+do_test io-4.2.1 {
+ execsql { BEGIN }
+ execsql { INSERT INTO abc VALUES('c', 'd') }
+ file exists test.db-journal
+} {1}
+if {$::tcl_platform(platform)=="unix"} {
+ do_test io-4.2.2 {
+ hexio_read test.db-journal 8 4
+ } {FFFFFFFF}
+}
+do_test io-4.2.3 {
+ execsql { COMMIT }
+ nSync
+} $expected_sync_count
+sqlite3_simulate_device -char safe_append
+
+# With SAFE_APPEND set, there should only ever be one journal-header
+# written to the database, even though the sync-mode is "full".
+#
+do_test io-4.3.1 {
+ execsql {
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ expr {[file size test.db]/1024}
+} {43}
+ifcapable pager_pragmas {
+ do_test io-4.3.2 {
+ execsql {
+ PRAGMA synchronous = full;
+ PRAGMA cache_size = 10;
+ PRAGMA synchronous;
+ }
+ } {2}
+}
+do_test io-4.3.3 {
+ execsql {
+ BEGIN;
+ UPDATE abc SET a = 'x';
+ }
+ file exists test.db-journal
+} {1}
+do_test io-4.3.4 {
+ # The UPDATE statement in the statement above modifies 41 pages
+ # (all pages in the database except page 1 and the root page of
+ # abc). Because the cache_size is set to 10, this must have required
+ # at least 4 cache-spills. If there were no journal headers written
+ # to the journal file after the cache-spill, then the size of the
+ # journal file is give by:
+ #
+ # <jrnl file size> = <jrnl header size> + nPage * (<page-size> + 8)
+ #
+ # If the journal file contains additional headers, this formula
+ # will not predict the size of the journal file.
+ #
+ file size test.db-journal
+} [expr 512 + (1024+8)*41]
+
+#----------------------------------------------------------------------
+# Test cases io-5.* test that the default page size is selected and
+# used correctly.
+#
+set tn 0
+foreach {char sectorsize pgsize} {
+ {} 512 1024
+ {} 1024 1024
+ {} 2048 2048
+ {} 8192 8192
+ {} 16384 8192
+ {atomic} 512 8192
+ {atomic512} 512 1024
+ {atomic2K} 512 2048
+ {atomic2K} 4096 4096
+ {atomic2K atomic} 512 8192
+ {atomic64K} 512 1024
+} {
+ incr tn
+ if {$pgsize>$::SQLITE_MAX_PAGE_SIZE} continue
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3_simulate_device -char $char -sectorsize $sectorsize
+ sqlite3 db test.db -vfs devsym
+ db eval {
+ PRAGMA auto_vacuum=OFF;
+ }
+ ifcapable !atomicwrite {
+ if {[regexp {^atomic} $char]} continue
+ }
+ do_test io-5.$tn {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ expr {[file size test.db]/2}
+ } $pgsize
+}
+
+sqlite3_simulate_device -char {} -sectorsize 0
+finish_test
diff --git a/third_party/sqlite/test/ioerr.test b/third_party/sqlite/test/ioerr.test
new file mode 100755
index 0000000..35743f0
--- /dev/null
+++ b/third_party/sqlite/test/ioerr.test
@@ -0,0 +1,407 @@
+# 2001 October 12
+#
+# 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 file is testing for correct handling of I/O errors
+# such as writes failing because the disk is full.
+#
+# The tests in this file use special facilities that are only
+# available in the SQLite test fixture.
+#
+# $Id: ioerr.test,v 1.41 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
+# on the 8th IO operation in the SQL script below doesn't report an error.
+#
+# This is because the 8th IO call attempts to read page 2 of the database
+# file when the file on disk is only 1 page. The pager layer detects that
+# this has happened and suppresses the error returned by the OS layer.
+#
+do_ioerr_test ioerr-1 -erc 1 -ckrefcount 1 -sqlprep {
+ SELECT * FROM sqlite_master;
+} -sqlbody {
+ CREATE TABLE t1(a,b,c);
+ SELECT * FROM sqlite_master;
+ BEGIN TRANSACTION;
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(4,5,6);
+ ROLLBACK;
+ SELECT * FROM t1;
+ BEGIN TRANSACTION;
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(4,5,6);
+ COMMIT;
+ SELECT * FROM t1;
+ DELETE FROM t1 WHERE a<100;
+} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]
+
+# Test for IO errors during a VACUUM.
+#
+# The first IO call is excluded from the test. This call attempts to read
+# the file-header of the temporary database used by VACUUM. Since the
+# database doesn't exist at that point, the IO error is not detected.
+#
+# Additionally, if auto-vacuum is enabled, the 12th IO error is not
+# detected. Same reason as the 8th in the test case above.
+#
+ifcapable vacuum {
+ do_ioerr_test ioerr-2 -cksum true -ckrefcount true -sqlprep {
+ BEGIN;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
+ INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t2;
+ } -sqlbody {
+ VACUUM;
+ } -exclude [list \
+ 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]]
+}
+
+do_ioerr_test ioerr-3 -ckrefcount true -tclprep {
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
+ }
+ for {set i 0} {$i<150} {incr i} {
+ execsql {
+ INSERT INTO abc VALUES(randstr(100,100));
+ }
+ }
+ execsql COMMIT
+} -sqlbody {
+ CREATE TABLE abc2(a);
+ BEGIN;
+ DELETE FROM abc WHERE length(a)>100;
+ UPDATE abc SET a = randstr(90,90);
+ COMMIT;
+ CREATE TABLE abc3(a);
+}
+
+# Test IO errors that can occur retrieving a record header that flows over
+# onto an overflow page.
+do_ioerr_test ioerr-4 -ckrefcount true -tclprep {
+ set sql "CREATE TABLE abc(a1"
+ for {set i 2} {$i<1300} {incr i} {
+ append sql ", a$i"
+ }
+ append sql ");"
+ execsql $sql
+ execsql {INSERT INTO abc (a1) VALUES(NULL)}
+} -sqlbody {
+ SELECT * FROM abc;
+}
+
+
+# Test IO errors that may occur during a multi-file commit.
+#
+# Tests 8 and 17 are excluded when auto-vacuum is enabled for the same
+# reason as in test cases ioerr-1.XXX
+ifcapable attach {
+ set ex ""
+ if {[string match [execsql {pragma auto_vacuum}] 1]} {
+ set ex [list 4 17]
+ }
+ do_ioerr_test ioerr-5 -restoreprng 0 -ckrefcount true -sqlprep {
+ ATTACH 'test2.db' AS test2;
+ } -sqlbody {
+ BEGIN;
+ CREATE TABLE t1(a,b,c);
+ CREATE TABLE test2.t2(a,b,c);
+ COMMIT;
+ } -exclude $ex
+}
+
+# Test IO errors when replaying two hot journals from a 2-file
+# transaction. This test only runs on UNIX.
+ifcapable crashtest&&attach {
+ if {![catch {sqlite3 -has_codec} r] && !$r} {
+ do_ioerr_test ioerr-6 -ckrefcount true -tclprep {
+ execsql {
+ ATTACH 'test2.db' as aux;
+ CREATE TABLE tx(a, b);
+ CREATE TABLE aux.ty(a, b);
+ }
+ set rc [crashsql -delay 2 -file test2.db-journal {
+ ATTACH 'test2.db' as aux;
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE aux.t2(a, b, c);
+ CREATE TABLE t1(a, b, c);
+ COMMIT;
+ }]
+ if {$rc!="1 {child process exited abnormally}"} {
+ error "Wrong error message: $rc"
+ }
+ } -sqlbody {
+ SELECT * FROM sqlite_master;
+ SELECT * FROM aux.sqlite_master;
+ }
+ }
+}
+
+# Test handling of IO errors that occur while rolling back hot journal
+# files.
+#
+# These tests can't be run on windows because the windows version of
+# SQLite holds a mandatory exclusive lock on journal files it has open.
+#
+if {$tcl_platform(platform)!="windows"} {
+ do_ioerr_test ioerr-7 -tclprep {
+ db close
+ sqlite3 db2 test2.db
+ db2 eval {
+ PRAGMA synchronous = 0;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ BEGIN;
+ INSERT INTO t1 VALUES(3, 4);
+ }
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+ db2 close
+ } -tclbody {
+ sqlite3 db test.db
+ db eval {
+ SELECT * FROM t1;
+ }
+ } -exclude 1
+}
+
+# For test coverage: Cause an I/O failure while trying to read a
+# short field (one that fits into a Mem buffer without mallocing
+# for space).
+#
+do_ioerr_test ioerr-8 -ckrefcount true -tclprep {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
+ }
+ db close
+ sqlite3 db test.db
+} -sqlbody {
+ SELECT c FROM t1;
+}
+
+# For test coverage: Cause an IO error whilst reading the master-journal
+# name from a journal file.
+if {$tcl_platform(platform)=="unix"} {
+ do_ioerr_test ioerr-9 -ckrefcount true -tclprep {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
+ BEGIN;
+ INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
+ }
+ copy_file test.db-journal test2.db-journal
+ execsql {
+ COMMIT;
+ }
+ copy_file test2.db-journal test.db-journal
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ puts -nonewline $f "hello"
+ puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04"
+ puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
+ close $f
+ } -sqlbody {
+ SELECT a FROM t1;
+ }
+}
+
+# For test coverage: Cause an IO error during statement playback (i.e.
+# a constraint).
+do_ioerr_test ioerr-10 -ckrefcount true -tclprep {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ }
+ for {set i 0} {$i < 500} {incr i} {
+ execsql {INSERT INTO t1 VALUES(:i, 'hello world');}
+ }
+ execsql {
+ COMMIT;
+ }
+} -tclbody {
+
+ catch {execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES('abc', 123);
+ INSERT INTO t1 VALUES('def', 123);
+ INSERT INTO t1 VALUES('ghi', 123);
+ INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
+ }} msg
+
+ if {$msg != "column a is not unique"} {
+ error $msg
+ }
+}
+
+# Assertion fault bug reported by alex dimitrov.
+#
+do_ioerr_test ioerr-11 -ckrefcount true -erc 1 -sqlprep {
+ CREATE TABLE A(Id INTEGER, Name TEXT);
+ INSERT INTO A(Id, Name) VALUES(1, 'Name');
+} -sqlbody {
+ UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1;
+}
+
+# Test that an io error encountered in a sync() caused by a call to
+# sqlite3_release_memory() is handled Ok. Only try this if
+# memory-management is enabled.
+#
+ifcapable memorymanage {
+ do_ioerr_test memmanage-ioerr1 -ckrefcount true -sqlprep {
+ BEGIN;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(randstr(50,50), randstr(100,100), randstr(10,10));
+ INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
+ INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
+ INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
+ INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
+ INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
+ } -tclbody {
+ sqlite3_release_memory
+ } -sqlbody {
+ COMMIT;
+ }
+}
+
+ifcapable pager_pragmas&&autovacuum {
+ do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -sqlprep {
+ PRAGMA page_size = 512;
+ PRAGMA auto_vacuum = incremental;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES( randomblob(1 * (512-4)) );
+ INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
+ INSERT INTO t1 VALUES( randomblob(2 * (512-4)) );
+ INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
+ INSERT INTO t1 VALUES( randomblob(3 * (512-4)) );
+ DELETE FROM t1 WHERE rowid = 3;
+ PRAGMA incremental_vacuum = 2;
+ DELETE FROM t1 WHERE rowid = 1;
+ } -sqlbody {
+ PRAGMA incremental_vacuum = 1;
+ }
+}
+
+# Usually, after a new page is allocated from the end of the file, it does
+# not need to be written to the journal. The exception is when the new page
+# shares its sector with an existing page that does need to be journalled.
+# This test case provokes this condition to test for the sake of coverage
+# that an IO error while journalling the coresident page is handled correctly.
+#
+sqlite3_simulate_device -char {} -sectorsize 2048
+do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -tclprep {
+ db close
+ sqlite3 db test.db -vfs devsym
+
+ # Create a test database. Page 2 is the root page of table t1. The only
+ # row inserted into t1 has an overflow page - page 3. Page 3 will be
+ # coresident on the 2048 byte sector with the next page to be allocated.
+ #
+ db eval { PRAGMA page_size = 1024 }
+ db eval { CREATE TABLE t1(x) }
+ db eval { INSERT INTO t1 VALUES(randomblob(1100)); }
+} -tclbody {
+ db eval { INSERT INTO t1 VALUES(randomblob(2000)); }
+}
+sqlite3_simulate_device -char {} -sectorsize 0
+catch {db close}
+
+do_ioerr_test ioerr-13 -ckrefcount true -erc 1 -sqlprep {
+ PRAGMA auto_vacuum = incremental;
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES(randomblob(1500));
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t1 VALUES(randomblob(20));
+ INSERT INTO t1 SELECT x FROM t1;
+ INSERT INTO t1 SELECT x FROM t1;
+ INSERT INTO t1 SELECT x FROM t1;
+ INSERT INTO t1 SELECT x FROM t1;
+ INSERT INTO t1 SELECT x FROM t1;
+ INSERT INTO t1 SELECT x FROM t1; /* 64 entries in t1 */
+ INSERT INTO t1 SELECT x FROM t1 LIMIT 14; /* 78 entries in t1 */
+ DELETE FROM t2 WHERE rowid = 3;
+} -sqlbody {
+ -- This statement uses the balance_quick() optimization. The new page
+ -- is appended to the database file. But the overflow page used by
+ -- the new record will be positioned near the start of the database
+ -- file, in the gap left by the "DELETE FROM t2 WHERE rowid=3" statement
+ -- above.
+ --
+ -- The point of this is that the statement wil need to update two pointer
+ -- map pages. Which introduces another opportunity for an IO error.
+ --
+ INSERT INTO t1 VALUES(randomblob(2000));
+}
+
+do_ioerr_test ioerr-14 -ckrefcount true -erc 1 -sqlprep {
+ PRAGMA auto_vacuum = incremental;
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES(randomblob(1500));
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT randomblob(1500) FROM t2;
+
+ -- This statement inserts a row into t1 with an overflow page at the
+ -- end of the file. A long way from its parent (the root of t1).
+ INSERT INTO t1 VALUES(randomblob(1500));
+ DELETE FROM t2 WHERE rowid<10;
+} -sqlbody {
+ -- This transaction will cause the root-page of table t1 to divide
+ -- (by calling balance_deeper()). When it does, the "parent" page of the
+ -- overflow page inserted in the -sqlprep block above will change and
+ -- the corresponding pointer map page be updated. This test case attempts
+ -- to cause an IO error during the pointer map page update.
+ --
+ BEGIN;
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ INSERT INTO t1 VALUES(randomblob(100));
+ COMMIT;
+}
+
+finish_test
diff --git a/third_party/sqlite/test/ioerr2.test b/third_party/sqlite/test/ioerr2.test
new file mode 100755
index 0000000..05b78cd
--- /dev/null
+++ b/third_party/sqlite/test/ioerr2.test
@@ -0,0 +1,134 @@
+# 2007 April 2
+#
+# 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 file is testing for correct handling of I/O errors
+# such as writes failing because the disk is full.
+#
+# The tests in this file use special facilities that are only
+# available in the SQLite test fixture.
+#
+# $Id: ioerr2.test,v 1.8 2008/07/08 15:59:52 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !integrityck {
+ finish_test
+ return
+}
+
+do_test ioerr2-1.1 {
+ execsql {
+ PRAGMA cache_size = 10;
+ PRAGMA default_cache_size = 10;
+ CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
+ INSERT INTO t1 VALUES(randstr(400,400),randstr(400,400));
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 2
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 4
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 8
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 16
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400) FROM t1; -- 32
+ }
+} {}
+
+set ::cksum [execsql {SELECT md5sum(a, b) FROM t1}]
+proc check_db {testname} {
+
+ # Make sure no I/O errors are simulated in this proc.
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_persist 0
+ set ::sqlite_io_error_pending 0
+
+ # Run an integrity-check. If "disk I/O error" is returned, the
+ # pager must be in error state. In this case open a new database
+ # connection. Otherwise, try a ROLLBACK, in case a transaction
+ # is still active.
+ set rc [catch {execsql {PRAGMA integrity_check}} msg]
+ if {$rc && ($msg eq "disk I/O error" || $msg eq "database is locked")} {
+ db close
+ sqlite3 db test.db
+ set refcnt 0
+ } else {
+ if {$rc || $msg ne "ok"} {
+ error $msg
+ }
+ catch {execsql ROLLBACK}
+ }
+
+ # Check that the database checksum is still $::cksum, and that
+ # the integrity-check passes.
+ set ck [execsql {SELECT md5sum(a, b) FROM t1}]
+ do_test ${testname}.cksum [list set ck $ck] $::cksum
+ integrity_check ${testname}.integrity
+ do_test ${testname}.refcnt {
+ lindex [sqlite3_pager_refcounts db] 0
+ } 0
+}
+
+check_db ioerr2-2
+
+set sql {
+ PRAGMA cache_size = 10;
+ PRAGMA default_cache_size = 10;
+ BEGIN;
+ DELETE FROM t1 WHERE (oid%7)==0;
+ INSERT INTO t1 SELECT randstr(400,400), randstr(400,400)
+ WHERE (random()%7)==0;
+ UPDATE t1 SET a = randstr(400,400), b = randstr(400,400)
+ WHERE (random()%7)==0;
+ ROLLBACK;
+}
+
+foreach bPersist [list 0 1] {
+ set ::go 1
+ for {set ::N 1} {$::go} {incr ::N} {
+ db close
+ sqlite3 db test.db
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_persist $bPersist
+ set ::sqlite_io_error_pending $::N
+
+ foreach {::go res} [catchsql $sql] {}
+ check_db ioerr2-3.$bPersist.$::N
+ }
+}
+foreach bPersist [list 0 1] {
+ set ::go 1
+ for {set ::N 1} {$::go} {incr ::N} {
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_persist $bPersist
+ set ::sqlite_io_error_pending $::N
+
+ foreach {::go res} [catchsql $sql] {}
+ check_db ioerr2-4.[expr {$bPersist+2}].$::N
+ }
+}
+
+do_test ioerr2-5 {
+ execsql {
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ }
+ set ::sqlite_io_error_persist 0
+ set ::go 1
+ set rc [catch {
+ for {set ::N 1} {$::N<200} {incr ::N} {
+ db eval {SELECT * FROM t1 WHERE rowid IN (1, 5, 10, 15, 20)} {
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_pending $::N
+ set sql {UPDATE t2 SET b = randstr(400,400)}
+ foreach {::go res} [catchsql $sql] {}
+ }
+ }
+ } msg]
+ list $rc $msg
+} {1 {callback requested query abort}}
+
+finish_test
diff --git a/third_party/sqlite/test/ioerr3.test b/third_party/sqlite/test/ioerr3.test
new file mode 100755
index 0000000..4598cbf
--- /dev/null
+++ b/third_party/sqlite/test/ioerr3.test
@@ -0,0 +1,42 @@
+# 2007 December 19
+#
+# 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 file is testing for correct handling of I/O errors
+# in conjunction with very small soft-heap-limit values.
+#
+# $Id: ioerr3.test,v 1.2 2008/01/19 23:50:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_ioerr_test ioerr3-1 -sqlprep {
+ CREATE TABLE t1(id INTEGER, name TEXT);
+} -tclbody {
+ sqlite3_soft_heap_limit 8192
+ db cache size 0
+ execsql BEGIN
+ for {set ii 0} {$ii < 100} {incr ii} {
+ execsql {
+ INSERT INTO t1(id, name) VALUES (1,
+'A1234567890B1234567890C1234567890D1234567890E1234567890F1234567890G1234567890H1234567890I1234567890J1234567890K1234567890L1234567890M1234567890N1234567890O1234567890P1234567890Q1234567890R1234567890'
+ );
+ }
+ }
+ execsql COMMIT
+}
+
+do_ioerr_test ioerr3-2 -sqlbody {
+ CREATE TEMP TABLE t1(x,y);
+}
+
+sqlite3_soft_heap_limit 0
+
+finish_test
diff --git a/third_party/sqlite/test/ioerr4.test b/third_party/sqlite/test/ioerr4.test
new file mode 100755
index 0000000..3a74a88
--- /dev/null
+++ b/third_party/sqlite/test/ioerr4.test
@@ -0,0 +1,100 @@
+# 2007 December 19
+#
+# 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 file is testing for correct handling of I/O errors
+# during incremental vacuum with a shared cache.
+#
+# $Id: ioerr4.test,v 1.2 2008/05/08 01:11:42 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This test requires both shared cache and incremental vacuum.
+#
+ifcapable {!shared_cache || !autovacuum} {
+ finish_test
+ return
+}
+
+# Enable shared cache mode and incremental vacuum.
+#
+do_test ioerr4-1.1 {
+ db close
+ set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+} {0}
+do_test ioerr4-1.2 {
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ db eval {
+ PRAGMA auto_vacuum=INCREMENTAL;
+ CREATE TABLE a(i INTEGER, b BLOB);
+ }
+ db2 eval {
+ SELECT name FROM sqlite_master
+ }
+} {a}
+do_test ioerr4-1.3 {
+ db eval {
+ PRAGMA auto_vacuum;
+ }
+} {2}
+
+# Insert and delete many records in order to put lots of pages
+# on the freelist.
+#
+do_test ioerr4-1.4 {
+ db eval {
+ INSERT INTO a VALUES(1, zeroblob(2000));
+ INSERT INTO a VALUES(2, zeroblob(2000));
+ INSERT INTO a SELECT i+2, zeroblob(2000) FROM a;
+ INSERT INTO a SELECT i+4, zeroblob(2000) FROM a;
+ INSERT INTO a SELECT i+8, zeroblob(2000) FROM a;
+ INSERT INTO a SELECT i+16, zeroblob(2000) FROM a;
+ SELECT count(*) FROM a;
+ }
+} {32}
+do_test ioerr4-1.5 {
+ db eval {
+ PRAGMA freelist_count
+ }
+} {0}
+do_test ioerr4-1.6 {
+ db eval {
+ DELETE FROM a;
+ PRAGMA freelist_count;
+ }
+} {64}
+
+# Set up for an I/O error on incremental vacuum
+# with two connections on shared cache.
+#
+db close
+db2 close
+file copy -force test.db test.db-bu
+do_ioerr_test ioerr4-2 -tclprep {
+ catch {db2 close}
+ db close
+ file delete -force test.db test.db-journal
+ file copy -force test.db-bu test.db
+ sqlite3_enable_shared_cache 1
+ set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
+ db eval {PRAGMA auto_vacuum=INCREMENTAL}
+ sqlite3 db2 test.db
+} -tclbody {
+ db eval {PRAGMA incremental_vacuum(5)}
+}
+
+db2 close
+file delete -force test.db-bu
+sqlite3_enable_shared_cache $::enable_shared_cache
+
+finish_test
diff --git a/third_party/sqlite/test/ioerr5.test b/third_party/sqlite/test/ioerr5.test
new file mode 100755
index 0000000..f22e039
--- /dev/null
+++ b/third_party/sqlite/test/ioerr5.test
@@ -0,0 +1,207 @@
+# 2008 May 12
+#
+# 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 tests that if sqlite3_release_memory() is called to reclaim
+# memory from a pager that is in the error-state, SQLite does not
+# incorrectly write dirty pages out to the database (not safe to do
+# once the pager is in error state).
+#
+# $Id: ioerr5.test,v 1.4 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !memorymanage||!shared_cache {
+ finish_test
+ return
+}
+
+db close
+
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+set ::soft_limit [sqlite3_soft_heap_limit 1048576]
+
+# This procedure prepares, steps and finalizes an SQL statement via the
+# UTF-16 APIs. The text representation of an SQLite error code is returned
+# ("SQLITE_OK", "SQLITE_IOERR" etc.). The actual results returned by the
+# SQL statement, if it is a SELECT, are not available.
+#
+# This can be useful for testing because it forces SQLite to make an extra
+# call to sqlite3_malloc() when translating from the supplied UTF-16 to
+# the UTF-8 encoding used internally.
+#
+proc dosql16 {zSql {db db}} {
+ set sql [encoding convertto unicode $zSql]
+ append sql "\00\00"
+ set stmt [sqlite3_prepare16 $db $sql -1 {}]
+ sqlite3_step $stmt
+ set rc [sqlite3_finalize $stmt]
+}
+
+proc compilesql16 {zSql {db db}} {
+ set sql [encoding convertto unicode $zSql]
+ append sql "\00\00"
+ set stmt [sqlite3_prepare16 $db $sql -1 {}]
+ set rc [sqlite3_finalize $stmt]
+}
+
+# Open two database connections (handle db and db2) to database "test.db".
+#
+proc opendatabases {} {
+ catch {db close}
+ catch {db2 close}
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ db2 cache size 0
+ db cache size 0
+ execsql {
+ pragma page_size=512;
+ pragma auto_vacuum=2;
+ pragma cache_size=16;
+ }
+}
+
+# Open two database connections and create a single table in the db.
+#
+do_test ioerr5-1.0 {
+ opendatabases
+ execsql { CREATE TABLE A(Id INTEGER, Name TEXT) }
+} {}
+
+foreach locking_mode {normal exclusive} {
+ for {set iFail 1} {$iFail<200} {incr iFail} {
+ sqlite3_soft_heap_limit 1048576
+ opendatabases
+ execsql { pragma locking_mode=exclusive }
+ set nRow [db one {SELECT count(*) FROM a}]
+
+ # Dirty (at least) one of the pages in the cache.
+ do_test ioerr5-1.$locking_mode-$iFail.1 {
+ execsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
+ }
+ } {}
+
+ # Now try to commit the transaction. Cause an IO error to occur
+ # within this operation, which moves the pager into the error state.
+ #
+ set ::sqlite_io_error_persist 1
+ set ::sqlite_io_error_pending $iFail
+ do_test ioerr5-1.$locking_mode-$iFail.2 {
+ set rc [catchsql {COMMIT}]
+ list
+ } {}
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_persist 0
+ set ::sqlite_io_error_pending 0
+
+ # Read the contents of the database file into a Tcl variable.
+ #
+ set fd [open test.db]
+ fconfigure $fd -translation binary -encoding binary
+ set zDatabase [read $fd]
+ close $fd
+
+ # Set a very low soft-limit and then try to compile an SQL statement
+ # from UTF-16 text. To do this, SQLite will need to reclaim memory
+ # from the pager that is in error state. Including that associated
+ # with the dirty page.
+ #
+ do_test ioerr5-1.$locking_mode-$iFail.3 {
+ sqlite3_soft_heap_limit 1024
+ compilesql16 "SELECT 10"
+ set bt [btree_from_db db]
+ array set stats [btree_pager_stats $bt]
+ set stats(page)
+ } {0}
+
+ # Ensure that nothing was written to the database while reclaiming
+ # memory from the pager in error state.
+ #
+ do_test ioerr5-1.$locking_mode-$iFail.4 {
+ set fd [open test.db]
+ fconfigure $fd -translation binary -encoding binary
+ set zDatabase2 [read $fd]
+ close $fd
+ expr {$zDatabase eq $zDatabase2}
+ } {1}
+
+ if {$rc eq [list 0 {}]} {
+ do_test ioerr5.1-$locking_mode-$iFail.3 {
+ execsql { SELECT count(*) FROM a }
+ } [expr $nRow+1]
+ break
+ }
+ }
+}
+
+# Make sure this test script doesn't leave any files open.
+#
+do_test ioerr5-1.X {
+ catch { db close }
+ catch { db2 close }
+ set sqlite_open_file_count
+} 0
+
+do_test ioerr5-2.0 {
+ sqlite3 db test.db
+ execsql { CREATE INDEX i1 ON a(id, name); }
+} {}
+
+foreach locking_mode {exclusive normal} {
+ for {set iFail 1} {$iFail<200} {incr iFail} {
+ sqlite3_soft_heap_limit 1048576
+ opendatabases
+ execsql { pragma locking_mode=exclusive }
+ set nRow [db one {SELECT count(*) FROM a}]
+
+ do_test ioerr5-2.$locking_mode-$iFail.1 {
+ execsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
+ }
+ } {}
+
+ set ::sqlite_io_error_persist 1
+ set ::sqlite_io_error_pending $iFail
+
+ sqlite3_release_memory 10000
+
+ set error_hit $::sqlite_io_error_hit
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_persist 0
+ set ::sqlite_io_error_pending 0
+ if {$error_hit} {
+ do_test ioerr5-2.$locking_mode-$iFail.3a {
+ catchsql COMMIT
+ } {1 {disk I/O error}}
+ } else {
+ do_test ioerr5-2.$locking_mode-$iFail.3b {
+ execsql COMMIT
+ } {}
+ break
+ }
+ }
+}
+
+# Make sure this test script doesn't leave any files open.
+#
+do_test ioerr5-2.X {
+ catch { db close }
+ catch { db2 close }
+ set sqlite_open_file_count
+} 0
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+sqlite3_soft_heap_limit $::soft_limit
+
+finish_test
diff --git a/third_party/sqlite/test/join.test b/third_party/sqlite/test/join.test
new file mode 100755
index 0000000..85512e9
--- /dev/null
+++ b/third_party/sqlite/test/join.test
@@ -0,0 +1,506 @@
+# 2002 May 24
+#
+# 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.
+#
+# This file implements tests for joins, including outer joins.
+#
+# $Id: join.test,v 1.24 2008/07/09 14:47:21 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test join-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(2,3,4);
+ INSERT INTO t1 VALUES(3,4,5);
+ SELECT * FROM t1;
+ }
+} {1 2 3 2 3 4 3 4 5}
+do_test join-1.2 {
+ execsql {
+ CREATE TABLE t2(b,c,d);
+ INSERT INTO t2 VALUES(1,2,3);
+ INSERT INTO t2 VALUES(2,3,4);
+ INSERT INTO t2 VALUES(3,4,5);
+ SELECT * FROM t2;
+ }
+} {1 2 3 2 3 4 3 4 5}
+
+do_test join-1.3 {
+ execsql2 {
+ SELECT * FROM t1 NATURAL JOIN t2;
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test join-1.3.1 {
+ execsql2 {
+ SELECT * FROM t2 NATURAL JOIN t1;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test join-1.3.2 {
+ execsql2 {
+ SELECT * FROM t2 AS x NATURAL JOIN t1;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test join-1.3.3 {
+ execsql2 {
+ SELECT * FROM t2 NATURAL JOIN t1 AS y;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test join-1.3.4 {
+ execsql {
+ SELECT b FROM t1 NATURAL JOIN t2;
+ }
+} {2 3}
+do_test join-1.4.1 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test join-1.4.2 {
+ execsql2 {
+ SELECT * FROM t1 AS x INNER JOIN t2 USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test join-1.4.3 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 AS y USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test join-1.4.4 {
+ execsql2 {
+ SELECT * FROM t1 AS x INNER JOIN t2 AS y USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test join-1.4.5 {
+ execsql {
+ SELECT b FROM t1 JOIN t2 USING(b);
+ }
+} {2 3}
+do_test join-1.5 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(b);
+ }
+} {a 1 b 2 c 3 c 3 d 4 a 2 b 3 c 4 c 4 d 5}
+do_test join-1.6 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(c);
+ }
+} {a 1 b 2 c 3 b 2 d 4 a 2 b 3 c 4 b 3 d 5}
+do_test join-1.7 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(c,b);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+
+do_test join-1.8 {
+ execsql {
+ SELECT * FROM t1 NATURAL CROSS JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5}
+do_test join-1.9 {
+ execsql {
+ SELECT * FROM t1 CROSS JOIN t2 USING(b,c);
+ }
+} {1 2 3 4 2 3 4 5}
+do_test join-1.10 {
+ execsql {
+ SELECT * FROM t1 NATURAL INNER JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5}
+do_test join-1.11 {
+ execsql {
+ SELECT * FROM t1 INNER JOIN t2 USING(b,c);
+ }
+} {1 2 3 4 2 3 4 5}
+do_test join-1.12 {
+ execsql {
+ SELECT * FROM t1 natural inner join t2;
+ }
+} {1 2 3 4 2 3 4 5}
+
+ifcapable subquery {
+ do_test join-1.13 {
+ execsql2 {
+ SELECT * FROM t1 NATURAL JOIN
+ (SELECT b as 'c', c as 'd', d as 'e' FROM t2) as t3
+ }
+ } {a 1 b 2 c 3 d 4 e 5}
+ do_test join-1.14 {
+ execsql2 {
+ SELECT * FROM (SELECT b as 'c', c as 'd', d as 'e' FROM t2) as 'tx'
+ NATURAL JOIN t1
+ }
+ } {c 3 d 4 e 5 a 1 b 2}
+}
+
+do_test join-1.15 {
+ execsql {
+ CREATE TABLE t3(c,d,e);
+ INSERT INTO t3 VALUES(2,3,4);
+ INSERT INTO t3 VALUES(3,4,5);
+ INSERT INTO t3 VALUES(4,5,6);
+ SELECT * FROM t3;
+ }
+} {2 3 4 3 4 5 4 5 6}
+do_test join-1.16 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t3;
+ }
+} {1 2 3 4 5 2 3 4 5 6}
+do_test join-1.17 {
+ execsql2 {
+ SELECT * FROM t1 natural join t2 natural join t3;
+ }
+} {a 1 b 2 c 3 d 4 e 5 a 2 b 3 c 4 d 5 e 6}
+do_test join-1.18 {
+ execsql {
+ CREATE TABLE t4(d,e,f);
+ INSERT INTO t4 VALUES(2,3,4);
+ INSERT INTO t4 VALUES(3,4,5);
+ INSERT INTO t4 VALUES(4,5,6);
+ SELECT * FROM t4;
+ }
+} {2 3 4 3 4 5 4 5 6}
+do_test join-1.19.1 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t4;
+ }
+} {1 2 3 4 5 6}
+do_test join-1.19.2 {
+ execsql2 {
+ SELECT * FROM t1 natural join t2 natural join t4;
+ }
+} {a 1 b 2 c 3 d 4 e 5 f 6}
+do_test join-1.20 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t3 WHERE t1.a=1
+ }
+} {1 2 3 4 5}
+
+do_test join-2.1 {
+ execsql {
+ SELECT * FROM t1 NATURAL LEFT JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5 3 4 5 {}}
+do_test join-2.2 {
+ execsql {
+ SELECT * FROM t2 NATURAL LEFT OUTER JOIN t1;
+ }
+} {1 2 3 {} 2 3 4 1 3 4 5 2}
+do_test join-2.3 {
+ catchsql {
+ SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
+ }
+} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
+do_test join-2.4 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d
+ }
+} {1 2 3 {} {} {} 2 3 4 {} {} {} 3 4 5 1 2 3}
+do_test join-2.5 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d WHERE t1.a>1
+ }
+} {2 3 4 {} {} {} 3 4 5 1 2 3}
+do_test join-2.6 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d WHERE t2.b IS NULL OR t2.b>1
+ }
+} {1 2 3 {} {} {} 2 3 4 {} {} {}}
+
+do_test join-3.1 {
+ catchsql {
+ SELECT * FROM t1 NATURAL JOIN t2 ON t1.a=t2.b;
+ }
+} {1 {a NATURAL join may not have an ON or USING clause}}
+do_test join-3.2 {
+ catchsql {
+ SELECT * FROM t1 NATURAL JOIN t2 USING(b);
+ }
+} {1 {a NATURAL join may not have an ON or USING clause}}
+do_test join-3.3 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 ON t1.a=t2.b USING(b);
+ }
+} {1 {cannot have both ON and USING clauses in the same join}}
+do_test join-3.4.1 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 USING(a);
+ }
+} {1 {cannot join using column a - column not present in both tables}}
+do_test join-3.4.2 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 USING(d);
+ }
+} {1 {cannot join using column d - column not present in both tables}}
+do_test join-3.5 {
+ catchsql {
+ SELECT * FROM t1 USING(a);
+ }
+} {0 {1 2 3 2 3 4 3 4 5}}
+do_test join-3.6 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 ON t3.a=t2.b;
+ }
+} {1 {no such column: t3.a}}
+do_test join-3.7 {
+ catchsql {
+ SELECT * FROM t1 INNER OUTER JOIN t2;
+ }
+} {1 {unknown or unsupported join type: INNER OUTER}}
+do_test join-3.8 {
+ catchsql {
+ SELECT * FROM t1 INNER OUTER CROSS JOIN t2;
+ }
+} {1 {unknown or unsupported join type: INNER OUTER CROSS}}
+do_test join-3.9 {
+ catchsql {
+ SELECT * FROM t1 OUTER NATURAL INNER JOIN t2;
+ }
+} {1 {unknown or unsupported join type: OUTER NATURAL INNER}}
+do_test join-3.10 {
+ catchsql {
+ SELECT * FROM t1 LEFT BOGUS JOIN t2;
+ }
+} {1 {unknown or unsupported join type: LEFT BOGUS}}
+do_test join-3.11 {
+ catchsql {
+ SELECT * FROM t1 INNER BOGUS CROSS JOIN t2;
+ }
+} {1 {unknown or unsupported join type: INNER BOGUS CROSS}}
+do_test join-3.12 {
+ catchsql {
+ SELECT * FROM t1 NATURAL AWK SED JOIN t2;
+ }
+} {1 {unknown or unsupported join type: NATURAL AWK SED}}
+
+do_test join-4.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t5(a INTEGER PRIMARY KEY);
+ CREATE TABLE t6(a INTEGER);
+ INSERT INTO t6 VALUES(NULL);
+ INSERT INTO t6 VALUES(NULL);
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ COMMIT;
+ }
+ execsql {
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test join-4.2 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test join-4.3 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+do_test join-4.4 {
+ execsql {
+ UPDATE t6 SET a='xyz';
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test join-4.6 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test join-4.7 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+do_test join-4.8 {
+ execsql {
+ UPDATE t6 SET a=1;
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test join-4.9 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test join-4.10 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+
+do_test join-5.1 {
+ execsql {
+ BEGIN;
+ create table centros (id integer primary key, centro);
+ INSERT INTO centros VALUES(1,'xxx');
+ create table usuarios (id integer primary key, nombre, apellidos,
+ idcentro integer);
+ INSERT INTO usuarios VALUES(1,'a','aa',1);
+ INSERT INTO usuarios VALUES(2,'b','bb',1);
+ INSERT INTO usuarios VALUES(3,'c','cc',NULL);
+ create index idcentro on usuarios (idcentro);
+ END;
+ select usuarios.id, usuarios.nombre, centros.centro from
+ usuarios left outer join centros on usuarios.idcentro = centros.id;
+ }
+} {1 a xxx 2 b xxx 3 c {}}
+
+# A test for ticket #247.
+#
+do_test join-7.1 {
+ execsql {
+ CREATE TABLE t7 (x, y);
+ INSERT INTO t7 VALUES ("pa1", 1);
+ INSERT INTO t7 VALUES ("pa2", NULL);
+ INSERT INTO t7 VALUES ("pa3", NULL);
+ INSERT INTO t7 VALUES ("pa4", 2);
+ INSERT INTO t7 VALUES ("pa30", 131);
+ INSERT INTO t7 VALUES ("pa31", 130);
+ INSERT INTO t7 VALUES ("pa28", NULL);
+
+ CREATE TABLE t8 (a integer primary key, b);
+ INSERT INTO t8 VALUES (1, "pa1");
+ INSERT INTO t8 VALUES (2, "pa4");
+ INSERT INTO t8 VALUES (3, NULL);
+ INSERT INTO t8 VALUES (4, NULL);
+ INSERT INTO t8 VALUES (130, "pa31");
+ INSERT INTO t8 VALUES (131, "pa30");
+
+ SELECT coalesce(t8.a,999) from t7 LEFT JOIN t8 on y=a;
+ }
+} {1 999 999 2 131 130 999}
+
+# Make sure a left join where the right table is really a view that
+# is itself a join works right. Ticket #306.
+#
+ifcapable view {
+do_test join-8.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t9(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t9 VALUES(1,11);
+ INSERT INTO t9 VALUES(2,22);
+ CREATE TABLE t10(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t10 VALUES(1,2);
+ INSERT INTO t10 VALUES(3,3);
+ CREATE TABLE t11(p INTEGER PRIMARY KEY, q);
+ INSERT INTO t11 VALUES(2,111);
+ INSERT INTO t11 VALUES(3,333);
+ CREATE VIEW v10_11 AS SELECT x, q FROM t10, t11 WHERE t10.y=t11.p;
+ COMMIT;
+ SELECT * FROM t9 LEFT JOIN v10_11 ON( a=x );
+ }
+} {1 11 1 111 2 22 {} {}}
+ifcapable subquery {
+ do_test join-8.2 {
+ execsql {
+ SELECT * FROM t9 LEFT JOIN (SELECT x, q FROM t10, t11 WHERE t10.y=t11.p)
+ ON( a=x);
+ }
+ } {1 11 1 111 2 22 {} {}}
+}
+do_test join-8.3 {
+ execsql {
+ SELECT * FROM v10_11 LEFT JOIN t9 ON( a=x );
+ }
+} {1 111 1 11 3 333 {} {}}
+} ;# ifcapable view
+
+# Ticket #350 describes a scenario where LEFT OUTER JOIN does not
+# function correctly if the right table in the join is really
+# subquery.
+#
+# To test the problem, we generate the same LEFT OUTER JOIN in two
+# separate selects but with on using a subquery and the other calling
+# the table directly. Then connect the two SELECTs using an EXCEPT.
+# Both queries should generate the same results so the answer should
+# be an empty set.
+#
+ifcapable compound {
+do_test join-9.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t12(a,b);
+ INSERT INTO t12 VALUES(1,11);
+ INSERT INTO t12 VALUES(2,22);
+ CREATE TABLE t13(b,c);
+ INSERT INTO t13 VALUES(22,222);
+ COMMIT;
+ }
+} {}
+
+ifcapable subquery {
+ do_test join-9.1.1 {
+ execsql {
+ SELECT * FROM t12 NATURAL LEFT JOIN t13
+ EXCEPT
+ SELECT * FROM t12 NATURAL LEFT JOIN (SELECT * FROM t13 WHERE b>0);
+ }
+ } {}
+}
+ifcapable view {
+ do_test join-9.2 {
+ execsql {
+ CREATE VIEW v13 AS SELECT * FROM t13 WHERE b>0;
+ SELECT * FROM t12 NATURAL LEFT JOIN t13
+ EXCEPT
+ SELECT * FROM t12 NATURAL LEFT JOIN v13;
+ }
+ } {}
+} ;# ifcapable view
+} ;# ifcapable compound
+
+ifcapable subquery {
+ # Ticket #1697: Left Join WHERE clause terms that contain an
+ # aggregate subquery.
+ #
+ do_test join-10.1 {
+ execsql {
+ CREATE TABLE t21(a,b,c);
+ CREATE TABLE t22(p,q);
+ CREATE INDEX i22 ON t22(q);
+ SELECT a FROM t21 LEFT JOIN t22 ON b=p WHERE q=
+ (SELECT max(m.q) FROM t22 m JOIN t21 n ON n.b=m.p WHERE n.c=1);
+ }
+ } {}
+
+ # Test a LEFT JOIN when the right-hand side of hte join is an empty
+ # sub-query. Seems fine.
+ #
+ do_test join-10.2 {
+ execsql {
+ CREATE TABLE t23(a, b, c);
+ CREATE TABLE t24(a, b, c);
+ INSERT INTO t23 VALUES(1, 2, 3);
+ }
+ execsql {
+ SELECT * FROM t23 LEFT JOIN t24;
+ }
+ } {1 2 3 {} {} {}}
+ do_test join-10.3 {
+ execsql {
+ SELECT * FROM t23 LEFT JOIN (SELECT * FROM t24);
+ }
+ } {1 2 3 {} {} {}}
+
+} ;# ifcapable subquery
+
+finish_test
diff --git a/third_party/sqlite/test/join2.test b/third_party/sqlite/test/join2.test
new file mode 100755
index 0000000..0f558c5
--- /dev/null
+++ b/third_party/sqlite/test/join2.test
@@ -0,0 +1,75 @@
+# 2002 May 24
+#
+# 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.
+#
+# This file implements tests for joins, including outer joins.
+#
+# $Id: join2.test,v 1.2 2005/01/21 03:12:16 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test join2-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,11);
+ INSERT INTO t1 VALUES(2,22);
+ INSERT INTO t1 VALUES(3,33);
+ SELECT * FROM t1;
+ }
+} {1 11 2 22 3 33}
+do_test join2-1.2 {
+ execsql {
+ CREATE TABLE t2(b,c);
+ INSERT INTO t2 VALUES(11,111);
+ INSERT INTO t2 VALUES(33,333);
+ INSERT INTO t2 VALUES(44,444);
+ SELECT * FROM t2;
+ }
+} {11 111 33 333 44 444};
+do_test join2-1.3 {
+ execsql {
+ CREATE TABLE t3(c,d);
+ INSERT INTO t3 VALUES(111,1111);
+ INSERT INTO t3 VALUES(444,4444);
+ INSERT INTO t3 VALUES(555,5555);
+ SELECT * FROM t3;
+ }
+} {111 1111 444 4444 555 5555}
+
+do_test join2-1.4 {
+ execsql {
+ SELECT * FROM
+ t1 NATURAL JOIN t2 NATURAL JOIN t3
+ }
+} {1 11 111 1111}
+do_test join2-1.5 {
+ execsql {
+ SELECT * FROM
+ t1 NATURAL JOIN t2 NATURAL LEFT OUTER JOIN t3
+ }
+} {1 11 111 1111 3 33 333 {}}
+do_test join2-1.6 {
+ execsql {
+ SELECT * FROM
+ t1 NATURAL LEFT OUTER JOIN t2 NATURAL JOIN t3
+ }
+} {1 11 111 1111}
+ifcapable subquery {
+ do_test join2-1.7 {
+ execsql {
+ SELECT * FROM
+ t1 NATURAL LEFT OUTER JOIN (t2 NATURAL JOIN t3)
+ }
+ } {1 11 111 1111 2 22 {} {} 3 33 {} {}}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/join3.test b/third_party/sqlite/test/join3.test
new file mode 100755
index 0000000..f1c273d
--- /dev/null
+++ b/third_party/sqlite/test/join3.test
@@ -0,0 +1,62 @@
+# 2002 May 24
+#
+# 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.
+#
+# This file implements tests for joins, including outer joins, where
+# there are a large number of tables involved in the join.
+#
+# $Id: join3.test,v 1.4 2005/01/19 23:24:51 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# An unrestricted join
+#
+catch {unset ::result}
+set result {}
+for {set N 1} {$N<=$bitmask_size} {incr N} {
+ lappend result $N
+ do_test join3-1.$N {
+ execsql "CREATE TABLE t${N}(x);"
+ execsql "INSERT INTO t$N VALUES($N)"
+ set sql "SELECT * FROM t1"
+ for {set i 2} {$i<=$N} {incr i} {append sql ", t$i"}
+ execsql $sql
+ } $result
+}
+
+# Joins with a comparison
+#
+set result {}
+for {set N 1} {$N<=$bitmask_size} {incr N} {
+ lappend result $N
+ do_test join3-2.$N {
+ set sql "SELECT * FROM t1"
+ for {set i 2} {$i<=$N} {incr i} {append sql ", t$i"}
+ set sep WHERE
+ for {set i 1} {$i<$N} {incr i} {
+ append sql " $sep t[expr {$i+1}].x==t$i.x+1"
+ set sep AND
+ }
+ execsql $sql
+ } $result
+}
+
+# Error of too many tables in the join
+#
+do_test join3-3.1 {
+ set sql "SELECT * FROM t1 AS t0, t1"
+ for {set i 2} {$i<=$bitmask_size} {incr i} {append sql ", t$i"}
+ catchsql $sql
+} [list 1 "at most $bitmask_size tables in a join"]
+
+
+finish_test
diff --git a/third_party/sqlite/test/join4.test b/third_party/sqlite/test/join4.test
new file mode 100755
index 0000000..77db25f
--- /dev/null
+++ b/third_party/sqlite/test/join4.test
@@ -0,0 +1,98 @@
+# 2002 May 24
+#
+# 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.
+#
+# This file implements tests for left outer joins containing WHERE
+# clauses that restrict the scope of the left term of the join.
+#
+# $Id: join4.test,v 1.4 2005/03/29 03:11:00 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable tempdb {
+ do_test join4-1.1 {
+ execsql {
+ create temp table t1(a integer, b varchar(10));
+ insert into t1 values(1,'one');
+ insert into t1 values(2,'two');
+ insert into t1 values(3,'three');
+ insert into t1 values(4,'four');
+
+ create temp table t2(x integer, y varchar(10), z varchar(10));
+ insert into t2 values(2,'niban','ok');
+ insert into t2 values(4,'yonban','err');
+ }
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x where t2.z='ok'
+ }
+ } {2 two 2 niban ok}
+} else {
+ do_test join4-1.1 {
+ execsql {
+ create table t1(a integer, b varchar(10));
+ insert into t1 values(1,'one');
+ insert into t1 values(2,'two');
+ insert into t1 values(3,'three');
+ insert into t1 values(4,'four');
+
+ create table t2(x integer, y varchar(10), z varchar(10));
+ insert into t2 values(2,'niban','ok');
+ insert into t2 values(4,'yonban','err');
+ }
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x where t2.z='ok'
+ }
+ } {2 two 2 niban ok}
+}
+do_test join4-1.2 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x and t2.z='ok'
+ }
+} {1 one {} {} {} 2 two 2 niban ok 3 three {} {} {} 4 four {} {} {}}
+do_test join4-1.3 {
+ execsql {
+ create index i2 on t2(z);
+ }
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x where t2.z='ok'
+ }
+} {2 two 2 niban ok}
+do_test join4-1.4 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x and t2.z='ok'
+ }
+} {1 one {} {} {} 2 two 2 niban ok 3 three {} {} {} 4 four {} {} {}}
+do_test join4-1.5 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x where t2.z>='ok'
+ }
+} {2 two 2 niban ok}
+do_test join4-1.4 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x and t2.z>='ok'
+ }
+} {1 one {} {} {} 2 two 2 niban ok 3 three {} {} {} 4 four {} {} {}}
+ifcapable subquery {
+ do_test join4-1.6 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x where t2.z IN ('ok')
+ }
+ } {2 two 2 niban ok}
+ do_test join4-1.7 {
+ execsql {
+ select * from t1 left outer join t2 on t1.a=t2.x and t2.z IN ('ok')
+ }
+ } {1 one {} {} {} 2 two 2 niban ok 3 three {} {} {} 4 four {} {} {}}
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/join5.test b/third_party/sqlite/test/join5.test
new file mode 100755
index 0000000..45d8a31
--- /dev/null
+++ b/third_party/sqlite/test/join5.test
@@ -0,0 +1,110 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests for left outer joins containing ON
+# clauses that restrict the scope of the left term of the join.
+#
+# $Id: join5.test,v 1.2 2007/06/08 00:20:48 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_test join5-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a integer primary key, b integer, c integer);
+ CREATE TABLE t2(x integer primary key, y);
+ CREATE TABLE t3(p integer primary key, q);
+ INSERT INTO t3 VALUES(11,'t3-11');
+ INSERT INTO t3 VALUES(12,'t3-12');
+ INSERT INTO t2 VALUES(11,'t2-11');
+ INSERT INTO t2 VALUES(12,'t2-12');
+ INSERT INTO t1 VALUES(1, 5, 0);
+ INSERT INTO t1 VALUES(2, 11, 2);
+ INSERT INTO t1 VALUES(3, 12, 1);
+ COMMIT;
+ }
+} {}
+do_test join5-1.2 {
+ execsql {
+ select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+ }
+} {1 5 0 {} {} 2 11 2 {} {} 3 12 1 12 t2-12}
+do_test join5-1.3 {
+ execsql {
+ select * from t1 left join t2 on t1.b=t2.x where t1.c=1
+ }
+} {3 12 1 12 t2-12}
+do_test join5-1.4 {
+ execsql {
+ select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+ left join t3 on t1.b=t3.p and t1.c=2
+ }
+} {1 5 0 {} {} {} {} 2 11 2 {} {} 11 t3-11 3 12 1 12 t2-12 {} {}}
+do_test join5-1.5 {
+ execsql {
+ select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+ left join t3 on t1.b=t3.p where t1.c=2
+ }
+} {2 11 2 {} {} 11 t3-11}
+
+# Ticket #2403
+#
+do_test join5-2.1 {
+ execsql {
+ CREATE TABLE ab(a,b);
+ INSERT INTO "ab" VALUES(1,2);
+ INSERT INTO "ab" VALUES(3,NULL);
+
+ CREATE TABLE xy(x,y);
+ INSERT INTO "xy" VALUES(2,3);
+ INSERT INTO "xy" VALUES(NULL,1);
+ }
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 0}
+} {2 3 {} {} {} 1 {} {}}
+do_test join5-2.2 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 1}
+} {2 3 1 2 2 3 3 {} {} 1 1 2 {} 1 3 {}}
+do_test join5-2.3 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON NULL}
+} {2 3 {} {} {} 1 {} {}}
+do_test join5-2.4 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 0 WHERE 0}
+} {}
+do_test join5-2.5 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 1 WHERE 0}
+} {}
+do_test join5-2.6 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON NULL WHERE 0}
+} {}
+do_test join5-2.7 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 0 WHERE 1}
+} {2 3 {} {} {} 1 {} {}}
+do_test join5-2.8 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 1 WHERE 1}
+} {2 3 1 2 2 3 3 {} {} 1 1 2 {} 1 3 {}}
+do_test join5-2.9 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON NULL WHERE 1}
+} {2 3 {} {} {} 1 {} {}}
+do_test join5-2.10 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 0 WHERE NULL}
+} {}
+do_test join5-2.11 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON 1 WHERE NULL}
+} {}
+do_test join5-2.12 {
+ execsql {SELECT * FROM xy LEFT JOIN ab ON NULL WHERE NULL}
+} {}
+
+
+finish_test
diff --git a/third_party/sqlite/test/journal1.test b/third_party/sqlite/test/journal1.test
new file mode 100755
index 0000000..a1b17b4
--- /dev/null
+++ b/third_party/sqlite/test/journal1.test
@@ -0,0 +1,67 @@
+# 2005 March 15
+#
+# 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.
+#
+# This file implements tests to make sure that leftover journals from
+# prior databases do not try to rollback into new databases.
+#
+# $Id: journal1.test,v 1.2 2005/03/20 22:54:56 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# These tests will not work on windows because windows uses
+# manditory file locking which breaks the file copy command.
+#
+if {$tcl_platform(platform)=="windows"} {
+ finish_test
+ return
+}
+
+# Create a smaple database
+#
+do_test journal1-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,randstr(10,400));
+ INSERT INTO t1 VALUES(2,randstr(10,400));
+ INSERT INTO t1 SELECT a+2, a||b FROM t1;
+ INSERT INTO t1 SELECT a+4, a||b FROM t1;
+ SELECT count(*) FROM t1;
+ }
+} 8
+
+# Make changes to the database and save the journal file.
+# Then delete the database. Replace the the journal file
+# and try to create a new database with the same name. The
+# old journal should not attempt to rollback into the new
+# database.
+#
+do_test journal1-1.2 {
+ execsql {
+ BEGIN;
+ DELETE FROM t1;
+ }
+ file copy -force test.db-journal test.db-journal-bu
+ execsql {
+ ROLLBACK;
+ }
+ db close
+ file delete test.db
+ file copy test.db-journal-bu test.db-journal
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM sqlite_master
+ }
+} {0 {}}
+
+finish_test
diff --git a/third_party/sqlite/test/jrnlmode.test b/third_party/sqlite/test/jrnlmode.test
new file mode 100755
index 0000000..75773dd
--- /dev/null
+++ b/third_party/sqlite/test/jrnlmode.test
@@ -0,0 +1,387 @@
+# 2008 April 17
+#
+# 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 these tests is the journal mode pragma.
+#
+# $Id: jrnlmode.test,v 1.5 2008/08/02 20:09:37 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------
+# Test cases jrnlmode-1.X test the PRAGMA logic.
+#
+do_test jrnlmode-1.0 {
+ execsql {
+ PRAGMA journal_mode;
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ }
+} [list delete delete delete]
+do_test jrnlmode-1.1 {
+ execsql {
+ PRAGMA journal_mode = persist;
+ }
+} {persist}
+do_test jrnlmode-1.2 {
+ execsql {
+ PRAGMA journal_mode;
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ }
+} [list persist persist persist]
+do_test jrnlmode-1.4 {
+ execsql {
+ PRAGMA journal_mode = off;
+ }
+} {off}
+do_test jrnlmode-1.5 {
+ execsql {
+ PRAGMA journal_mode;
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ }
+} {off off off}
+do_test jrnlmode-1.6 {
+ execsql {
+ PRAGMA journal_mode = delete;
+ }
+} {delete}
+do_test jrnlmode-1.7 {
+ execsql {
+ PRAGMA journal_mode;
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ }
+} {delete delete delete}
+do_test jrnlmode-1.8 {
+ execsql {
+ PRAGMA journal_mode = off;
+ PRAGMA journal_mode = invalid;
+ }
+} {off off}
+ifcapable attach {
+ do_test jrnlmode-1.9 {
+ execsql {
+ PRAGMA journal_mode = persist;
+ ATTACH ':memory:' as aux1;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA aux1.journal_mode;
+ }
+ } {persist persist}
+ do_test jrnlmode-1.10 {
+ execsql {
+ PRAGMA main.journal_mode = off;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ PRAGMA aux1.journal_mode;
+ }
+ } {off persist persist}
+ do_test jrnlmode-1.11 {
+ execsql {
+ PRAGMA journal_mode;
+ }
+ } {persist}
+ do_test jrnlmode-1.12 {
+ execsql {
+ ATTACH ':memory:' as aux2;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA aux1.journal_mode;
+ PRAGMA aux2.journal_mode;
+ }
+ } {off persist persist}
+ do_test jrnlmode-1.11 {
+ execsql {
+ PRAGMA aux1.journal_mode = delete;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA aux1.journal_mode;
+ PRAGMA aux2.journal_mode;
+ }
+ } {off delete persist}
+ do_test jrnlmode-1.12 {
+ execsql {
+ PRAGMA journal_mode = delete;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ PRAGMA aux1.journal_mode;
+ PRAGMA aux2.journal_mode;
+ }
+ } {delete delete delete delete}
+ do_test jrnlmode-1.13 {
+ execsql {
+ ATTACH ':memory:' as aux3;
+ }
+ execsql {
+ PRAGMA main.journal_mode;
+ PRAGMA temp.journal_mode;
+ PRAGMA aux1.journal_mode;
+ PRAGMA aux2.journal_mode;
+ PRAGMA aux3.journal_mode;
+ }
+ } {delete delete delete delete delete}
+
+ do_test jrnlmode-1.99 {
+ execsql {
+ DETACH aux1;
+ DETACH aux2;
+ DETACH aux3;
+ }
+ } {}
+}
+
+ifcapable attach {
+ file delete -force test2.db
+ do_test jrnlmode-2.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA main.journal_mode = persist;
+ PRAGMA aux.journal_mode = persist;
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE aux.def(d, e, f);
+ }
+ execsql {
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO def VALUES(4, 5, 6);
+ COMMIT;
+ }
+ list [file exists test.db-journal] [file exists test2.db-journal]
+ } {1 1}
+
+ do_test jrnlmode-2.2 {
+ file size test.db-journal
+ } {0}
+
+ do_test jrnlmode-2.3 {
+ execsql {
+ SELECT * FROM abc;
+ }
+ } {1 2 3}
+
+ do_test jrnlmode-2.4 {
+ file size test.db-journal
+ } {0}
+
+ do_test jrnlmode-2.5 {
+ execsql {
+ SELECT * FROM def;
+ }
+ } {4 5 6}
+
+#----------------------------------------------------------------------
+# Test caes jrnlmode-3.X verify that ticket #3127 has been fixed.
+#
+ db close
+ file delete -force test2.db
+ file delete -force test.db
+ sqlite3 db test.db
+
+ do_test jrnlmode-3.1 {
+ execsql {
+ CREATE TABLE x(n INTEGER);
+ ATTACH 'test2.db' AS a;
+ create table a.x ( n integer );
+ insert into a.x values(1);
+ insert into a.x values (2);
+ insert into a.x values (3);
+ insert into a.x values (4);
+ }
+ } {}
+
+ do_test jrnlmode-3.2 {
+ execsql { PRAGMA journal_mode=off; }
+ execsql {
+ BEGIN IMMEDIATE;
+ INSERT OR IGNORE INTO main.x SELECT * FROM a.x;
+ COMMIT;
+ }
+ } {}
+}
+
+ifcapable autovacuum&&pragma {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ do_test jrnlmode-4.1 {
+ execsql {
+ PRAGMA cache_size = 1;
+ PRAGMA auto_vacuum = 1;
+ CREATE TABLE abc(a, b, c);
+ }
+ execsql { PRAGMA page_count }
+ } {3}
+
+ do_test jrnlmode-4.2 {
+ execsql { PRAGMA journal_mode = off }
+ } {off}
+
+ do_test jrnlmode-4.3 {
+ execsql { INSERT INTO abc VALUES(1, 2, randomblob(2000)) }
+ } {}
+
+ # This will attempt to truncate the database file. Check that this
+ # is not a problem when journal_mode=off.
+ do_test jrnlmode-4.4 {
+ execsql { DELETE FROM abc }
+ } {}
+
+ integrity_check jrnlmode-4.5
+}
+
+#------------------------------------------------------------------------
+# The following test caes, jrnlmode-5.*, test the journal_size_limit
+# pragma.
+ifcapable pragma {
+ db close
+ file delete -force test.db test2.db test3.db
+ sqlite3 db test.db
+
+ do_test jrnlmode-5.1 {
+ execsql {pragma page_size=1024}
+ execsql {pragma journal_mode=persist}
+ } {persist}
+
+ do_test jrnlmode-5.2 {
+ execsql { PRAGMA journal_size_limit }
+ } {-1}
+ do_test jrnlmode-5.3 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.journal_size_limit;
+ }
+ } {-1}
+ do_test jrnlmode-5.4 {
+ execsql { PRAGMA aux.journal_size_limit = 10240 }
+ } {10240}
+ do_test jrnlmode-5.5 {
+ execsql { PRAGMA main.journal_size_limit = 20480 }
+ } {20480}
+ do_test jrnlmode-5.6 {
+ execsql { PRAGMA journal_size_limit }
+ } {20480}
+ do_test jrnlmode-5.7 {
+ execsql { PRAGMA aux.journal_size_limit }
+ } {10240}
+
+ do_test jrnlmode-5.8 {
+ execsql { ATTACH 'test3.db' AS aux2 }
+ } {}
+
+ do_test jrnlmode-5.9 {
+ execsql {
+ CREATE TABLE main.t1(a, b, c);
+ CREATE TABLE aux.t2(a, b, c);
+ CREATE TABLE aux2.t3(a, b, c);
+ }
+ } {}
+ do_test jrnlmode-5.10 {
+ list \
+ [file exists test.db-journal] \
+ [file exists test2.db-journal] \
+ [file exists test3.db-journal]
+ } {1 1 1}
+ do_test jrnlmode-5.11 {
+ execsql {
+ BEGIN;
+ INSERT INTO t3 VALUES(randomblob(1000),randomblob(1000),randomblob(1000));
+ INSERT INTO t3
+ SELECT randomblob(1000),randomblob(1000),randomblob(1000) FROM t3;
+ INSERT INTO t3
+ SELECT randomblob(1000),randomblob(1000),randomblob(1000) FROM t3;
+ INSERT INTO t3
+ SELECT randomblob(1000),randomblob(1000),randomblob(1000) FROM t3;
+ INSERT INTO t3
+ SELECT randomblob(1000),randomblob(1000),randomblob(1000) FROM t3;
+ INSERT INTO t3
+ SELECT randomblob(1000),randomblob(1000),randomblob(1000) FROM t3;
+ INSERT INTO t2 SELECT * FROM t3;
+ INSERT INTO t1 SELECT * FROM t2;
+ COMMIT;
+ }
+ list \
+ [file exists test.db-journal] \
+ [file exists test2.db-journal] \
+ [file exists test3.db-journal] \
+ [file size test.db-journal] \
+ [file size test2.db-journal] \
+ [file size test3.db-journal]
+ } {1 1 1 0 0 0}
+
+ do_test jrnlmode-5.12 {
+ execsql {
+ BEGIN;
+ UPDATE t1 SET a = randomblob(1000);
+ }
+ expr {[file size test.db-journal]>30000}
+ } {1}
+ do_test jrnlmode-5.13 {
+ execsql COMMIT
+ file size test.db-journal
+ } {20480}
+
+ do_test jrnlmode-5.14 {
+ execsql {
+ BEGIN;
+ UPDATE t2 SET a = randomblob(1000);
+ }
+ expr {[file size test2.db-journal]>30000}
+ } {1}
+ do_test jrnlmode-5.15 {
+ execsql COMMIT
+ file size test2.db-journal
+ } {10240}
+
+ do_test jrnlmode-5.16 {
+ execsql {
+ BEGIN;
+ UPDATE t3 SET a = randomblob(1000);
+ }
+ set journalsize [file size test3.db-journal]
+ expr {$journalsize>30000}
+ } {1}
+ do_test jrnlmode-5.17 {
+ execsql COMMIT
+ set sz [file size test3.db-journal]
+ expr {$sz>=$journalsize}
+ } {1}
+
+ do_test jrnlmode-5.18 {
+ execsql {
+ PRAGMA journal_size_limit = -4;
+ BEGIN;
+ UPDATE t1 SET a = randomblob(1000);
+ }
+ set journalsize [file size test.db-journal]
+ expr {$journalsize>30000}
+ } {1}
+ do_test jrnlmode-5.19 {
+ execsql COMMIT
+ set sz [file size test.db-journal]
+ expr {$sz>=$journalsize}
+ } {1}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/lastinsert.test b/third_party/sqlite/test/lastinsert.test
new file mode 100755
index 0000000..adeb798
--- /dev/null
+++ b/third_party/sqlite/test/lastinsert.test
@@ -0,0 +1,366 @@
+# 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.
+#
+#***********************************************************************
+#
+# Tests to make sure that value returned by last_insert_rowid() (LIRID)
+# is updated properly, especially inside triggers
+#
+# Note 1: insert into table is now the only statement which changes LIRID
+# Note 2: upon entry into before or instead of triggers,
+# LIRID is unchanged (rather than -1)
+# Note 3: LIRID is changed within the context of a trigger,
+# but is restored once the trigger exits
+# Note 4: LIRID is not changed by an insert into a view (since everything
+# is done within instead of trigger context)
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# ----------------------------------------------------------------------------
+# 1.x - basic tests (no triggers)
+
+# LIRID changed properly after an insert into a table
+do_test lastinsert-1.1 {
+ catchsql {
+ create table t1 (k integer primary key);
+ insert into t1 values (1);
+ insert into t1 values (NULL);
+ insert into t1 values (NULL);
+ select last_insert_rowid();
+ }
+} {0 3}
+
+# LIRID unchanged after an update on a table
+do_test lastinsert-1.2 {
+ catchsql {
+ update t1 set k=4 where k=2;
+ select last_insert_rowid();
+ }
+} {0 3}
+
+# LIRID unchanged after a delete from a table
+do_test lastinsert-1.3 {
+ catchsql {
+ delete from t1 where k=4;
+ select last_insert_rowid();
+ }
+} {0 3}
+
+# LIRID unchanged after create table/view statements
+do_test lastinsert-1.4.1 {
+ catchsql {
+ create table t2 (k integer primary key, val1, val2, val3);
+ select last_insert_rowid();
+ }
+} {0 3}
+ifcapable view {
+do_test lastinsert-1.4.2 {
+ catchsql {
+ create view v as select * from t1;
+ select last_insert_rowid();
+ }
+} {0 3}
+} ;# ifcapable view
+
+# All remaining tests involve triggers. Skip them if triggers are not
+# supported in this build.
+#
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# ----------------------------------------------------------------------------
+# 2.x - tests with after insert trigger
+
+# LIRID changed properly after an insert into table containing an after trigger
+do_test lastinsert-2.1 {
+ catchsql {
+ delete from t2;
+ create trigger r1 after insert on t1 for each row begin
+ insert into t2 values (NEW.k*2, last_insert_rowid(), NULL, NULL);
+ update t2 set k=k+10, val2=100+last_insert_rowid();
+ update t2 set val3=1000+last_insert_rowid();
+ end;
+ insert into t1 values (13);
+ select last_insert_rowid();
+ }
+} {0 13}
+
+# LIRID equals NEW.k upon entry into after insert trigger
+do_test lastinsert-2.2 {
+ catchsql {
+ select val1 from t2;
+ }
+} {0 13}
+
+# LIRID changed properly by insert within context of after insert trigger
+do_test lastinsert-2.3 {
+ catchsql {
+ select val2 from t2;
+ }
+} {0 126}
+
+# LIRID unchanged by update within context of after insert trigger
+do_test lastinsert-2.4 {
+ catchsql {
+ select val3 from t2;
+ }
+} {0 1026}
+
+# ----------------------------------------------------------------------------
+# 3.x - tests with after update trigger
+
+# LIRID not changed after an update onto a table containing an after trigger
+do_test lastinsert-3.1 {
+ catchsql {
+ delete from t2;
+ drop trigger r1;
+ create trigger r1 after update on t1 for each row begin
+ insert into t2 values (NEW.k*2, last_insert_rowid(), NULL, NULL);
+ update t2 set k=k+10, val2=100+last_insert_rowid();
+ update t2 set val3=1000+last_insert_rowid();
+ end;
+ update t1 set k=14 where k=3;
+ select last_insert_rowid();
+ }
+} {0 13}
+
+# LIRID unchanged upon entry into after update trigger
+do_test lastinsert-3.2 {
+ catchsql {
+ select val1 from t2;
+ }
+} {0 13}
+
+# LIRID changed properly by insert within context of after update trigger
+do_test lastinsert-3.3 {
+ catchsql {
+ select val2 from t2;
+ }
+} {0 128}
+
+# LIRID unchanged by update within context of after update trigger
+do_test lastinsert-3.4 {
+ catchsql {
+ select val3 from t2;
+ }
+} {0 1028}
+
+# ----------------------------------------------------------------------------
+# 4.x - tests with instead of insert trigger
+# These may not be run if either views or triggers were disabled at
+# compile-time
+
+ifcapable {view && trigger} {
+# LIRID not changed after an insert into view containing an instead of trigger
+do_test lastinsert-4.1 {
+ catchsql {
+ delete from t2;
+ drop trigger r1;
+ create trigger r1 instead of insert on v for each row begin
+ insert into t2 values (NEW.k*2, last_insert_rowid(), NULL, NULL);
+ update t2 set k=k+10, val2=100+last_insert_rowid();
+ update t2 set val3=1000+last_insert_rowid();
+ end;
+ insert into v values (15);
+ select last_insert_rowid();
+ }
+} {0 13}
+
+# LIRID unchanged upon entry into instead of trigger
+do_test lastinsert-4.2 {
+ catchsql {
+ select val1 from t2;
+ }
+} {0 13}
+
+# LIRID changed properly by insert within context of instead of trigger
+do_test lastinsert-4.3 {
+ catchsql {
+ select val2 from t2;
+ }
+} {0 130}
+
+# LIRID unchanged by update within context of instead of trigger
+do_test lastinsert-4.4 {
+ catchsql {
+ select val3 from t2;
+ }
+} {0 1030}
+} ;# ifcapable (view && trigger)
+
+# ----------------------------------------------------------------------------
+# 5.x - tests with before delete trigger
+
+# LIRID not changed after a delete on a table containing a before trigger
+do_test lastinsert-5.1 {
+ catchsql {
+ drop trigger r1; -- This was not created if views are disabled.
+ }
+ catchsql {
+ delete from t2;
+ create trigger r1 before delete on t1 for each row begin
+ insert into t2 values (77, last_insert_rowid(), NULL, NULL);
+ update t2 set k=k+10, val2=100+last_insert_rowid();
+ update t2 set val3=1000+last_insert_rowid();
+ end;
+ delete from t1 where k=1;
+ select last_insert_rowid();
+ }
+} {0 13}
+
+# LIRID unchanged upon entry into delete trigger
+do_test lastinsert-5.2 {
+ catchsql {
+ select val1 from t2;
+ }
+} {0 13}
+
+# LIRID changed properly by insert within context of delete trigger
+do_test lastinsert-5.3 {
+ catchsql {
+ select val2 from t2;
+ }
+} {0 177}
+
+# LIRID unchanged by update within context of delete trigger
+do_test lastinsert-5.4 {
+ catchsql {
+ select val3 from t2;
+ }
+} {0 1077}
+
+# ----------------------------------------------------------------------------
+# 6.x - tests with instead of update trigger
+# These tests may not run if either views or triggers are disabled.
+
+ifcapable {view && trigger} {
+# LIRID not changed after an update on a view containing an instead of trigger
+do_test lastinsert-6.1 {
+ catchsql {
+ delete from t2;
+ drop trigger r1;
+ create trigger r1 instead of update on v for each row begin
+ insert into t2 values (NEW.k*2, last_insert_rowid(), NULL, NULL);
+ update t2 set k=k+10, val2=100+last_insert_rowid();
+ update t2 set val3=1000+last_insert_rowid();
+ end;
+ update v set k=16 where k=14;
+ select last_insert_rowid();
+ }
+} {0 13}
+
+# LIRID unchanged upon entry into instead of trigger
+do_test lastinsert-6.2 {
+ catchsql {
+ select val1 from t2;
+ }
+} {0 13}
+
+# LIRID changed properly by insert within context of instead of trigger
+do_test lastinsert-6.3 {
+ catchsql {
+ select val2 from t2;
+ }
+} {0 132}
+
+# LIRID unchanged by update within context of instead of trigger
+do_test lastinsert-6.4 {
+ catchsql {
+ select val3 from t2;
+ }
+} {0 1032}
+} ;# ifcapable (view && trigger)
+
+# ----------------------------------------------------------------------------
+# 7.x - complex tests with temporary tables and nested instead of triggers
+# These do not run if views or triggers are disabled.
+
+ifcapable {trigger && view && tempdb} {
+do_test lastinsert-7.1 {
+ catchsql {
+ drop table t1; drop table t2; drop trigger r1;
+ create temp table t1 (k integer primary key);
+ create temp table t2 (k integer primary key);
+ create temp view v1 as select * from t1;
+ create temp view v2 as select * from t2;
+ create temp table rid (k integer primary key, rin, rout);
+ insert into rid values (1, NULL, NULL);
+ insert into rid values (2, NULL, NULL);
+ create temp trigger r1 instead of insert on v1 for each row begin
+ update rid set rin=last_insert_rowid() where k=1;
+ insert into t1 values (100+NEW.k);
+ insert into v2 values (100+last_insert_rowid());
+ update rid set rout=last_insert_rowid() where k=1;
+ end;
+ create temp trigger r2 instead of insert on v2 for each row begin
+ update rid set rin=last_insert_rowid() where k=2;
+ insert into t2 values (1000+NEW.k);
+ update rid set rout=last_insert_rowid() where k=2;
+ end;
+ insert into t1 values (77);
+ select last_insert_rowid();
+ }
+} {0 77}
+
+do_test lastinsert-7.2 {
+ catchsql {
+ insert into v1 values (5);
+ select last_insert_rowid();
+ }
+} {0 77}
+
+do_test lastinsert-7.3 {
+ catchsql {
+ select rin from rid where k=1;
+ }
+} {0 77}
+
+do_test lastinsert-7.4 {
+ catchsql {
+ select rout from rid where k=1;
+ }
+} {0 105}
+
+do_test lastinsert-7.5 {
+ catchsql {
+ select rin from rid where k=2;
+ }
+} {0 105}
+
+do_test lastinsert-7.6 {
+ catchsql {
+ select rout from rid where k=2;
+ }
+} {0 1205}
+
+do_test lastinsert-8.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
+ CREATE TABLE t3(a, b);
+ CREATE TRIGGER after_t2 AFTER INSERT ON t2 BEGIN
+ INSERT INTO t3 VALUES(new.x, new.y);
+ END;
+ INSERT INTO t2 VALUES(5000000000, 1);
+ SELECT last_insert_rowid();
+ }
+} 5000000000
+
+do_test lastinsert-9.1 {
+ db eval {INSERT INTO t2 VALUES(123456789012345,0)}
+ db last_insert_rowid
+} {123456789012345}
+
+
+} ;# ifcapable (view && trigger)
+
+finish_test
diff --git a/third_party/sqlite/test/laststmtchanges.test b/third_party/sqlite/test/laststmtchanges.test
new file mode 100755
index 0000000..13bb5fa
--- /dev/null
+++ b/third_party/sqlite/test/laststmtchanges.test
@@ -0,0 +1,281 @@
+# 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.
+#
+#***********************************************************************
+#
+# Tests to make sure that values returned by changes() and total_changes()
+# are updated properly, especially inside triggers
+#
+# Note 1: changes() remains constant within a statement and only updates
+# once the statement is finished (triggers count as part of
+# statement).
+# Note 2: changes() is changed within the context of a trigger much like
+# last_insert_rowid() (see lastinsert.test), but is restored once
+# the trigger exits.
+# Note 3: changes() is not changed by a change to a view (since everything
+# is done within instead of trigger context).
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# ----------------------------------------------------------------------------
+# 1.x - basic tests (no triggers)
+
+# changes() set properly after insert
+do_test laststmtchanges-1.1 {
+ catchsql {
+ create table t0 (x);
+ insert into t0 values (1);
+ insert into t0 values (1);
+ insert into t0 values (2);
+ insert into t0 values (2);
+ insert into t0 values (1);
+ insert into t0 values (1);
+ insert into t0 values (1);
+ insert into t0 values (2);
+ select changes(), total_changes();
+ }
+} {0 {1 8}}
+
+# changes() set properly after update
+do_test laststmtchanges-1.2 {
+ catchsql {
+ update t0 set x=3 where x=1;
+ select changes(), total_changes();
+ }
+} {0 {5 13}}
+
+# There was some goofy change-counting logic in sqlite3_exec() that
+# appears to have been left over from SQLite version 2. This test
+# makes sure it has been removed.
+#
+do_test laststmtchanges-1.2.1 {
+ db cache flush
+ sqlite3_exec_printf db {update t0 set x=4 where x=3; select 1;} {}
+ execsql {select changes()}
+} {5}
+
+# changes() unchanged within an update statement
+do_test laststmtchanges-1.3 {
+ execsql {update t0 set x=3 where x=4}
+ catchsql {
+ update t0 set x=x+changes() where x=3;
+ select count() from t0 where x=8;
+ }
+} {0 5}
+
+# changes() set properly after update on table where no rows changed
+do_test laststmtchanges-1.4 {
+ catchsql {
+ update t0 set x=77 where x=88;
+ select changes();
+ }
+} {0 0}
+
+# changes() set properly after delete from table
+do_test laststmtchanges-1.5 {
+ catchsql {
+ delete from t0 where x=2;
+ select changes();
+ }
+} {0 3}
+
+# All remaining tests involve triggers. Skip them if triggers are not
+# supported in this build.
+#
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+
+# ----------------------------------------------------------------------------
+# 2.x - tests with after insert trigger
+
+# changes() changed properly after insert into table containing after trigger
+do_test laststmtchanges-2.1 {
+ set ::tc [db total_changes]
+ catchsql {
+ create table t1 (k integer primary key);
+ create table t2 (k integer primary key, v1, v2);
+ create trigger r1 after insert on t1 for each row begin
+ insert into t2 values (NULL, changes(), NULL);
+ update t0 set x=x;
+ update t2 set v2=changes();
+ end;
+ insert into t1 values (77);
+ select changes();
+ }
+} {0 1}
+
+# changes() unchanged upon entry into after insert trigger
+do_test laststmtchanges-2.2 {
+ catchsql {
+ select v1 from t2;
+ }
+} {0 3}
+
+# changes() changed properly by update within context of after insert trigger
+do_test laststmtchanges-2.3 {
+ catchsql {
+ select v2 from t2;
+ }
+} {0 5}
+
+# Total changes caused by firing the trigger above:
+#
+# 1 from "insert into t1 values(77)" +
+# 1 from "insert into t2 values (NULL, changes(), NULL);" +
+# 5 from "update t0 set x=x;" +
+# 1 from "update t2 set v2=changes();"
+#
+do_test laststmtchanges-2.4 {
+ expr [db total_changes] - $::tc
+} {8}
+
+# ----------------------------------------------------------------------------
+# 3.x - tests with after update trigger
+
+# changes() changed properly after update into table containing after trigger
+do_test laststmtchanges-3.1 {
+ catchsql {
+ drop trigger r1;
+ delete from t2; delete from t2;
+ create trigger r1 after update on t1 for each row begin
+ insert into t2 values (NULL, changes(), NULL);
+ delete from t0 where oid=1 or oid=2;
+ update t2 set v2=changes();
+ end;
+ update t1 set k=k;
+ select changes();
+ }
+} {0 1}
+
+# changes() unchanged upon entry into after update trigger
+do_test laststmtchanges-3.2 {
+ catchsql {
+ select v1 from t2;
+ }
+} {0 0}
+
+# changes() changed properly by delete within context of after update trigger
+do_test laststmtchanges-3.3 {
+ catchsql {
+ select v2 from t2;
+ }
+} {0 2}
+
+# ----------------------------------------------------------------------------
+# 4.x - tests with before delete trigger
+
+# changes() changed properly on delete from table containing before trigger
+do_test laststmtchanges-4.1 {
+ catchsql {
+ drop trigger r1;
+ delete from t2; delete from t2;
+ create trigger r1 before delete on t1 for each row begin
+ insert into t2 values (NULL, changes(), NULL);
+ insert into t0 values (5);
+ update t2 set v2=changes();
+ end;
+ delete from t1;
+ select changes();
+ }
+} {0 1}
+
+# changes() unchanged upon entry into before delete trigger
+do_test laststmtchanges-4.2 {
+ catchsql {
+ select v1 from t2;
+ }
+} {0 0}
+
+# changes() changed properly by insert within context of before delete trigger
+do_test laststmtchanges-4.3 {
+ catchsql {
+ select v2 from t2;
+ }
+} {0 1}
+
+# ----------------------------------------------------------------------------
+# 5.x - complex tests with temporary tables and nested instead of triggers
+# These tests cannot run if the library does not have view support enabled.
+
+ifcapable view&&tempdb {
+
+do_test laststmtchanges-5.1 {
+ catchsql {
+ drop table t0; drop table t1; drop table t2;
+ create temp table t0(x);
+ create temp table t1 (k integer primary key);
+ create temp table t2 (k integer primary key);
+ create temp view v1 as select * from t1;
+ create temp view v2 as select * from t2;
+ create temp table n1 (k integer primary key, n);
+ create temp table n2 (k integer primary key, n);
+ insert into t0 values (1);
+ insert into t0 values (2);
+ insert into t0 values (1);
+ insert into t0 values (1);
+ insert into t0 values (1);
+ insert into t0 values (2);
+ insert into t0 values (2);
+ insert into t0 values (1);
+ create temp trigger r1 instead of insert on v1 for each row begin
+ insert into n1 values (NULL, changes());
+ update t0 set x=x*10 where x=1;
+ insert into n1 values (NULL, changes());
+ insert into t1 values (NEW.k);
+ insert into n1 values (NULL, changes());
+ update t0 set x=x*10 where x=0;
+ insert into v2 values (100+NEW.k);
+ insert into n1 values (NULL, changes());
+ end;
+ create temp trigger r2 instead of insert on v2 for each row begin
+ insert into n2 values (NULL, changes());
+ insert into t2 values (1000+NEW.k);
+ insert into n2 values (NULL, changes());
+ update t0 set x=x*100 where x=0;
+ insert into n2 values (NULL, changes());
+ delete from t0 where x=2;
+ insert into n2 values (NULL, changes());
+ end;
+ insert into t1 values (77);
+ select changes();
+ }
+} {0 1}
+
+do_test laststmtchanges-5.2 {
+ catchsql {
+ delete from t1 where k=88;
+ select changes();
+ }
+} {0 0}
+
+do_test laststmtchanges-5.3 {
+ catchsql {
+ insert into v1 values (5);
+ select changes();
+ }
+} {0 0}
+
+do_test laststmtchanges-5.4 {
+ catchsql {
+ select n from n1;
+ }
+} {0 {0 5 1 0}}
+
+do_test laststmtchanges-5.5 {
+ catchsql {
+ select n from n2;
+ }
+} {0 {0 1 0 3}}
+
+} ;# ifcapable view
+
+finish_test
diff --git a/third_party/sqlite/test/like.test b/third_party/sqlite/test/like.test
new file mode 100755
index 0000000..57f1086
--- /dev/null
+++ b/third_party/sqlite/test/like.test
@@ -0,0 +1,514 @@
+# 2005 August 13
+#
+# 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 file is testing the LIKE and GLOB operators and
+# in particular the optimizations that occur to help those operators
+# run faster.
+#
+# $Id: like.test,v 1.9 2008/02/23 21:55:40 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create some sample data to work with.
+#
+do_test like-1.0 {
+ execsql {
+ CREATE TABLE t1(x TEXT);
+ }
+ foreach str {
+ a
+ ab
+ abc
+ abcd
+
+ acd
+ abd
+ bc
+ bcd
+
+ xyz
+ ABC
+ CDE
+ {ABC abc xyz}
+ } {
+ db eval {INSERT INTO t1 VALUES(:str)}
+ }
+ execsql {
+ SELECT count(*) FROM t1;
+ }
+} {12}
+
+# Test that both case sensitive and insensitive version of LIKE work.
+#
+do_test like-1.1 {
+ execsql {
+ SELECT x FROM t1 WHERE x LIKE 'abc' ORDER BY 1;
+ }
+} {ABC abc}
+do_test like-1.2 {
+ execsql {
+ SELECT x FROM t1 WHERE x GLOB 'abc' ORDER BY 1;
+ }
+} {abc}
+do_test like-1.3 {
+ execsql {
+ SELECT x FROM t1 WHERE x LIKE 'ABC' ORDER BY 1;
+ }
+} {ABC abc}
+do_test like-1.4 {
+ execsql {
+ SELECT x FROM t1 WHERE x LIKE 'aBc' ORDER BY 1;
+ }
+} {ABC abc}
+do_test like-1.5 {
+ execsql {
+ PRAGMA case_sensitive_like=on;
+ SELECT x FROM t1 WHERE x LIKE 'abc' ORDER BY 1;
+ }
+} {abc}
+do_test like-1.6 {
+ execsql {
+ SELECT x FROM t1 WHERE x GLOB 'abc' ORDER BY 1;
+ }
+} {abc}
+do_test like-1.7 {
+ execsql {
+ SELECT x FROM t1 WHERE x LIKE 'ABC' ORDER BY 1;
+ }
+} {ABC}
+do_test like-1.8 {
+ execsql {
+ SELECT x FROM t1 WHERE x LIKE 'aBc' ORDER BY 1;
+ }
+} {}
+do_test like-1.9 {
+ execsql {
+ PRAGMA case_sensitive_like=off;
+ SELECT x FROM t1 WHERE x LIKE 'abc' ORDER BY 1;
+ }
+} {ABC abc}
+
+# Tests of the REGEXP operator
+#
+do_test like-2.1 {
+ proc test_regexp {a b} {
+ return [regexp $a $b]
+ }
+ db function regexp test_regexp
+ execsql {
+ SELECT x FROM t1 WHERE x REGEXP 'abc' ORDER BY 1;
+ }
+} {{ABC abc xyz} abc abcd}
+do_test like-2.2 {
+ execsql {
+ SELECT x FROM t1 WHERE x REGEXP '^abc' ORDER BY 1;
+ }
+} {abc abcd}
+
+# Tests of the MATCH operator
+#
+do_test like-2.3 {
+ proc test_match {a b} {
+ return [string match $a $b]
+ }
+ db function match test_match
+ execsql {
+ SELECT x FROM t1 WHERE x MATCH '*abc*' ORDER BY 1;
+ }
+} {{ABC abc xyz} abc abcd}
+do_test like-2.4 {
+ execsql {
+ SELECT x FROM t1 WHERE x MATCH 'abc*' ORDER BY 1;
+ }
+} {abc abcd}
+
+# For the remaining tests, we need to have the like optimizations
+# enabled.
+#
+ifcapable !like_opt {
+ finish_test
+ return
+}
+
+# This procedure executes the SQL. Then it appends to the result the
+# "sort" or "nosort" keyword (as in the cksort procedure above) then
+# it appends the ::sqlite_query_plan variable.
+#
+proc queryplan {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return [concat $data $::sqlite_query_plan]
+}
+
+# Perform tests on the like optimization.
+#
+# With no index on t1.x and with case sensitivity turned off, no optimization
+# is performed.
+#
+do_test like-3.1 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
+ }
+} {ABC {ABC abc xyz} abc abcd sort t1 {}}
+do_test like-3.2 {
+ set sqlite_like_count
+} {12}
+
+# With an index on t1.x and case sensitivity on, optimize completely.
+#
+do_test like-3.3 {
+ set sqlite_like_count 0
+ execsql {
+ PRAGMA case_sensitive_like=on;
+ CREATE INDEX i1 ON t1(x);
+ }
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
+ }
+} {abc abcd nosort {} i1}
+do_test like-3.4 {
+ set sqlite_like_count
+} 0
+
+# Partial optimization when the pattern does not end in '%'
+#
+do_test like-3.5 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'a_c' ORDER BY 1;
+ }
+} {abc nosort {} i1}
+do_test like-3.6 {
+ set sqlite_like_count
+} 6
+do_test like-3.7 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'ab%d' ORDER BY 1;
+ }
+} {abcd abd nosort {} i1}
+do_test like-3.8 {
+ set sqlite_like_count
+} 4
+do_test like-3.9 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'a_c%' ORDER BY 1;
+ }
+} {abc abcd nosort {} i1}
+do_test like-3.10 {
+ set sqlite_like_count
+} 6
+
+# No optimization when the pattern begins with a wildcard.
+# Note that the index is still used but only for sorting.
+#
+do_test like-3.11 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE '%bcd' ORDER BY 1;
+ }
+} {abcd bcd nosort {} i1}
+do_test like-3.12 {
+ set sqlite_like_count
+} 12
+
+# No optimization for case insensitive LIKE
+#
+do_test like-3.13 {
+ set sqlite_like_count 0
+ queryplan {
+ PRAGMA case_sensitive_like=off;
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
+ }
+} {ABC {ABC abc xyz} abc abcd nosort {} i1}
+do_test like-3.14 {
+ set sqlite_like_count
+} 12
+
+# No optimization without an index.
+#
+do_test like-3.15 {
+ set sqlite_like_count 0
+ queryplan {
+ PRAGMA case_sensitive_like=on;
+ DROP INDEX i1;
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
+ }
+} {abc abcd sort t1 {}}
+do_test like-3.16 {
+ set sqlite_like_count
+} 12
+
+# No GLOB optimization without an index.
+#
+do_test like-3.17 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1;
+ }
+} {abc abcd sort t1 {}}
+do_test like-3.18 {
+ set sqlite_like_count
+} 12
+
+# GLOB is optimized regardless of the case_sensitive_like setting.
+#
+do_test like-3.19 {
+ set sqlite_like_count 0
+ queryplan {
+ CREATE INDEX i1 ON t1(x);
+ SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1;
+ }
+} {abc abcd nosort {} i1}
+do_test like-3.20 {
+ set sqlite_like_count
+} 0
+do_test like-3.21 {
+ set sqlite_like_count 0
+ queryplan {
+ PRAGMA case_sensitive_like=on;
+ SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1;
+ }
+} {abc abcd nosort {} i1}
+do_test like-3.22 {
+ set sqlite_like_count
+} 0
+do_test like-3.23 {
+ set sqlite_like_count 0
+ queryplan {
+ PRAGMA case_sensitive_like=off;
+ SELECT x FROM t1 WHERE x GLOB 'a[bc]d' ORDER BY 1;
+ }
+} {abd acd nosort {} i1}
+do_test like-3.24 {
+ set sqlite_like_count
+} 6
+
+# No optimization if the LHS of the LIKE is not a column name or
+# if the RHS is not a string.
+#
+do_test like-4.1 {
+ execsql {PRAGMA case_sensitive_like=on}
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1
+ }
+} {abc abcd nosort {} i1}
+do_test like-4.2 {
+ set sqlite_like_count
+} 0
+do_test like-4.3 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE +x LIKE 'abc%' ORDER BY 1
+ }
+} {abc abcd nosort {} i1}
+do_test like-4.4 {
+ set sqlite_like_count
+} 12
+do_test like-4.5 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE ('ab' || 'c%') ORDER BY 1
+ }
+} {abc abcd nosort {} i1}
+do_test like-4.6 {
+ set sqlite_like_count
+} 12
+
+# Collating sequences on the index disable the LIKE optimization.
+# Or if the NOCASE collating sequence is used, the LIKE optimization
+# is enabled when case_sensitive_like is OFF.
+#
+do_test like-5.1 {
+ execsql {PRAGMA case_sensitive_like=off}
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1
+ }
+} {ABC {ABC abc xyz} abc abcd nosort {} i1}
+do_test like-5.2 {
+ set sqlite_like_count
+} 12
+do_test like-5.3 {
+ execsql {
+ CREATE TABLE t2(x COLLATE NOCASE);
+ INSERT INTO t2 SELECT * FROM t1;
+ CREATE INDEX i2 ON t2(x COLLATE NOCASE);
+ }
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'abc%' ORDER BY 1
+ }
+} {abc ABC {ABC abc xyz} abcd nosort {} i2}
+do_test like-5.4 {
+ set sqlite_like_count
+} 0
+do_test like-5.5 {
+ execsql {
+ PRAGMA case_sensitive_like=on;
+ }
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'abc%' ORDER BY 1
+ }
+} {abc abcd nosort {} i2}
+do_test like-5.6 {
+ set sqlite_like_count
+} 12
+do_test like-5.7 {
+ execsql {
+ PRAGMA case_sensitive_like=off;
+ }
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x GLOB 'abc*' ORDER BY 1
+ }
+} {abc abcd nosort {} i2}
+do_test like-5.8 {
+ set sqlite_like_count
+} 12
+do_test like-5.11 {
+ execsql {PRAGMA case_sensitive_like=off}
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t1 WHERE x LIKE 'ABC%' ORDER BY 1
+ }
+} {ABC {ABC abc xyz} abc abcd nosort {} i1}
+do_test like-5.12 {
+ set sqlite_like_count
+} 12
+do_test like-5.13 {
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'ABC%' ORDER BY 1
+ }
+} {abc ABC {ABC abc xyz} abcd nosort {} i2}
+do_test like-5.14 {
+ set sqlite_like_count
+} 0
+do_test like-5.15 {
+ execsql {
+ PRAGMA case_sensitive_like=on;
+ }
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'ABC%' ORDER BY 1
+ }
+} {ABC {ABC abc xyz} nosort {} i2}
+do_test like-5.16 {
+ set sqlite_like_count
+} 12
+do_test like-5.17 {
+ execsql {
+ PRAGMA case_sensitive_like=off;
+ }
+ set sqlite_like_count 0
+ queryplan {
+ SELECT x FROM t2 WHERE x GLOB 'ABC*' ORDER BY 1
+ }
+} {ABC {ABC abc xyz} nosort {} i2}
+do_test like-5.18 {
+ set sqlite_like_count
+} 12
+
+# Boundary case. The prefix for a LIKE comparison is rounded up
+# when constructing the comparison. Example: "ab" becomes "ac".
+# In other words, the last character is increased by one.
+#
+# Make sure this happens correctly when the last character is a
+# "z" and we are doing case-insensitive comparisons.
+#
+# Ticket #2959
+#
+do_test like-5.21 {
+ execsql {
+ PRAGMA case_sensitive_like=off;
+ INSERT INTO t2 VALUES('ZZ-upper-upper');
+ INSERT INTO t2 VALUES('zZ-lower-upper');
+ INSERT INTO t2 VALUES('Zz-upper-lower');
+ INSERT INTO t2 VALUES('zz-lower-lower');
+ }
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'zz%';
+ }
+} {zz-lower-lower zZ-lower-upper Zz-upper-lower ZZ-upper-upper nosort {} i2}
+do_test like-5.22 {
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'zZ%';
+ }
+} {zz-lower-lower zZ-lower-upper Zz-upper-lower ZZ-upper-upper nosort {} i2}
+do_test like-5.23 {
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'Zz%';
+ }
+} {zz-lower-lower zZ-lower-upper Zz-upper-lower ZZ-upper-upper nosort {} i2}
+do_test like-5.24 {
+ queryplan {
+ SELECT x FROM t2 WHERE x LIKE 'ZZ%';
+ }
+} {zz-lower-lower zZ-lower-upper Zz-upper-lower ZZ-upper-upper nosort {} i2}
+do_test like-5.25 {
+ queryplan {
+ PRAGMA case_sensitive_like=on;
+ CREATE TABLE t3(x);
+ CREATE INDEX i3 ON t3(x);
+ INSERT INTO t3 VALUES('ZZ-upper-upper');
+ INSERT INTO t3 VALUES('zZ-lower-upper');
+ INSERT INTO t3 VALUES('Zz-upper-lower');
+ INSERT INTO t3 VALUES('zz-lower-lower');
+ SELECT x FROM t3 WHERE x LIKE 'zz%';
+ }
+} {zz-lower-lower nosort {} i3}
+do_test like-5.26 {
+ queryplan {
+ SELECT x FROM t3 WHERE x LIKE 'zZ%';
+ }
+} {zZ-lower-upper nosort {} i3}
+do_test like-5.27 {
+ queryplan {
+ SELECT x FROM t3 WHERE x LIKE 'Zz%';
+ }
+} {Zz-upper-lower nosort {} i3}
+do_test like-5.28 {
+ queryplan {
+ SELECT x FROM t3 WHERE x LIKE 'ZZ%';
+ }
+} {ZZ-upper-upper nosort {} i3}
+
+
+# ticket #2407
+#
+# Make sure the LIKE prefix optimization does not strip off leading
+# characters of the like pattern that happen to be quote characters.
+#
+do_test like-6.1 {
+ foreach x { 'abc 'bcd 'def 'ax } {
+ set x2 '[string map {' ''} $x]'
+ db eval "INSERT INTO t2 VALUES($x2)"
+ }
+ execsql {
+ SELECT * FROM t2 WHERE x LIKE '''a%'
+ }
+} {'abc 'ax}
+
+do_test like-7.1 {
+ execsql {
+ SELECT * FROM t1 WHERE rowid GLOB '1*';
+ }
+} {a}
+
+finish_test
diff --git a/third_party/sqlite/test/like2.test b/third_party/sqlite/test/like2.test
new file mode 100755
index 0000000..4fd1642
--- /dev/null
+++ b/third_party/sqlite/test/like2.test
@@ -0,0 +1,1009 @@
+# 2008 May 26
+#
+# 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 file is testing the LIKE and GLOB operators and
+# in particular the optimizations that occur to help those operators
+# run faster.
+#
+# $Id: like2.test,v 1.1 2008/05/26 18:33:41 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test like2-1.1 {
+ db eval {
+ CREATE TABLE t1(x INT, y COLLATE NOCASE);
+ INSERT INTO t1(x,y) VALUES(1,CAST(x'01' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(2,CAST(x'02' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(3,CAST(x'03' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(4,CAST(x'04' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(5,CAST(x'05' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(6,CAST(x'06' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(7,CAST(x'07' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(8,CAST(x'08' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(9,CAST(x'09' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(10,CAST(x'0a' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(11,CAST(x'0b' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(12,CAST(x'0c' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(13,CAST(x'0d' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(14,CAST(x'0e' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(15,CAST(x'0f' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(16,CAST(x'10' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(17,CAST(x'11' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(18,CAST(x'12' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(19,CAST(x'13' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(20,CAST(x'14' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(21,CAST(x'15' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(22,CAST(x'16' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(23,CAST(x'17' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(24,CAST(x'18' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(25,CAST(x'19' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(26,CAST(x'1a' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(27,CAST(x'1b' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(28,CAST(x'1c' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(29,CAST(x'1d' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(30,CAST(x'1e' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(31,CAST(x'1f' AS TEXT));
+ INSERT INTO t1(x,y) VALUES(32,' ');
+ INSERT INTO t1(x,y) VALUES(33,'!');
+ INSERT INTO t1(x,y) VALUES(34,'"');
+ INSERT INTO t1(x,y) VALUES(35,'#');
+ INSERT INTO t1(x,y) VALUES(36,'$');
+ INSERT INTO t1(x,y) VALUES(37,'%');
+ INSERT INTO t1(x,y) VALUES(38,'&');
+ INSERT INTO t1(x,y) VALUES(39,'''');
+ INSERT INTO t1(x,y) VALUES(40,'(');
+ INSERT INTO t1(x,y) VALUES(41,')');
+ INSERT INTO t1(x,y) VALUES(42,'*');
+ INSERT INTO t1(x,y) VALUES(43,'+');
+ INSERT INTO t1(x,y) VALUES(44,',');
+ INSERT INTO t1(x,y) VALUES(45,'-');
+ INSERT INTO t1(x,y) VALUES(46,'.');
+ INSERT INTO t1(x,y) VALUES(47,'/');
+ INSERT INTO t1(x,y) VALUES(48,'0');
+ INSERT INTO t1(x,y) VALUES(49,'1');
+ INSERT INTO t1(x,y) VALUES(50,'2');
+ INSERT INTO t1(x,y) VALUES(51,'3');
+ INSERT INTO t1(x,y) VALUES(52,'4');
+ INSERT INTO t1(x,y) VALUES(53,'5');
+ INSERT INTO t1(x,y) VALUES(54,'6');
+ INSERT INTO t1(x,y) VALUES(55,'7');
+ INSERT INTO t1(x,y) VALUES(56,'8');
+ INSERT INTO t1(x,y) VALUES(57,'9');
+ INSERT INTO t1(x,y) VALUES(58,':');
+ INSERT INTO t1(x,y) VALUES(59,';');
+ INSERT INTO t1(x,y) VALUES(60,'<');
+ INSERT INTO t1(x,y) VALUES(61,'=');
+ INSERT INTO t1(x,y) VALUES(62,'>');
+ INSERT INTO t1(x,y) VALUES(63,'?');
+ INSERT INTO t1(x,y) VALUES(64,'@');
+ INSERT INTO t1(x,y) VALUES(65,'A');
+ INSERT INTO t1(x,y) VALUES(66,'B');
+ INSERT INTO t1(x,y) VALUES(67,'C');
+ INSERT INTO t1(x,y) VALUES(68,'D');
+ INSERT INTO t1(x,y) VALUES(69,'E');
+ INSERT INTO t1(x,y) VALUES(70,'F');
+ INSERT INTO t1(x,y) VALUES(71,'G');
+ INSERT INTO t1(x,y) VALUES(72,'H');
+ INSERT INTO t1(x,y) VALUES(73,'I');
+ INSERT INTO t1(x,y) VALUES(74,'J');
+ INSERT INTO t1(x,y) VALUES(75,'K');
+ INSERT INTO t1(x,y) VALUES(76,'L');
+ INSERT INTO t1(x,y) VALUES(77,'M');
+ INSERT INTO t1(x,y) VALUES(78,'N');
+ INSERT INTO t1(x,y) VALUES(79,'O');
+ INSERT INTO t1(x,y) VALUES(80,'P');
+ INSERT INTO t1(x,y) VALUES(81,'Q');
+ INSERT INTO t1(x,y) VALUES(82,'R');
+ INSERT INTO t1(x,y) VALUES(83,'S');
+ INSERT INTO t1(x,y) VALUES(84,'T');
+ INSERT INTO t1(x,y) VALUES(85,'U');
+ INSERT INTO t1(x,y) VALUES(86,'V');
+ INSERT INTO t1(x,y) VALUES(87,'W');
+ INSERT INTO t1(x,y) VALUES(88,'X');
+ INSERT INTO t1(x,y) VALUES(89,'Y');
+ INSERT INTO t1(x,y) VALUES(90,'Z');
+ INSERT INTO t1(x,y) VALUES(91,'[');
+ INSERT INTO t1(x,y) VALUES(92,'\');
+ INSERT INTO t1(x,y) VALUES(93,']');
+ INSERT INTO t1(x,y) VALUES(94,'^');
+ INSERT INTO t1(x,y) VALUES(95,'_');
+ INSERT INTO t1(x,y) VALUES(96,'`');
+ INSERT INTO t1(x,y) VALUES(97,'a');
+ INSERT INTO t1(x,y) VALUES(98,'b');
+ INSERT INTO t1(x,y) VALUES(99,'c');
+ INSERT INTO t1(x,y) VALUES(100,'d');
+ INSERT INTO t1(x,y) VALUES(101,'e');
+ INSERT INTO t1(x,y) VALUES(102,'f');
+ INSERT INTO t1(x,y) VALUES(103,'g');
+ INSERT INTO t1(x,y) VALUES(104,'h');
+ INSERT INTO t1(x,y) VALUES(105,'i');
+ INSERT INTO t1(x,y) VALUES(106,'j');
+ INSERT INTO t1(x,y) VALUES(107,'k');
+ INSERT INTO t1(x,y) VALUES(108,'l');
+ INSERT INTO t1(x,y) VALUES(109,'m');
+ INSERT INTO t1(x,y) VALUES(110,'n');
+ INSERT INTO t1(x,y) VALUES(111,'o');
+ INSERT INTO t1(x,y) VALUES(112,'p');
+ INSERT INTO t1(x,y) VALUES(113,'q');
+ INSERT INTO t1(x,y) VALUES(114,'r');
+ INSERT INTO t1(x,y) VALUES(115,'s');
+ INSERT INTO t1(x,y) VALUES(116,'t');
+ INSERT INTO t1(x,y) VALUES(117,'u');
+ INSERT INTO t1(x,y) VALUES(118,'v');
+ INSERT INTO t1(x,y) VALUES(119,'w');
+ INSERT INTO t1(x,y) VALUES(120,'x');
+ INSERT INTO t1(x,y) VALUES(121,'y');
+ INSERT INTO t1(x,y) VALUES(122,'z');
+ INSERT INTO t1(x,y) VALUES(123,'{');
+ INSERT INTO t1(x,y) VALUES(124,'|');
+ INSERT INTO t1(x,y) VALUES(125,'}');
+ INSERT INTO t1(x,y) VALUES(126,'~');
+ INSERT INTO t1(x,y) VALUES(127,CAST(x'7f' AS TEXT));
+ SELECT count(*) FROM t1;
+ }
+} {127}
+do_test like2-1.2 {
+ db eval {
+ CREATE TABLE t2(x INT, y COLLATE NOCASE);
+ INSERT INTO t2 SELECT * FROM t1;
+ CREATE INDEX i2 ON t2(y);
+ SELECT count(*) FROM t2;
+ }
+} {127}
+do_test like2-1.3 {
+ db eval {
+ CREATE TABLE t3(x INT, y COLLATE NOCASE);
+ INSERT INTO t3 SELECT x, 'abc' || y || 'xyz' FROM t1;
+ CREATE INDEX i3 ON t3(y);
+ SELECT count(*) FROM t2;
+ }
+} {127}
+do_test like-2.32.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE ' %'"
+} {32}
+do_test like-2.32.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE ' %'"
+} {32}
+do_test like-2.32.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc %'"
+} {32}
+do_test like-2.33.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '!%'"
+} {33}
+do_test like-2.33.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '!%'"
+} {33}
+do_test like-2.33.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc!%'"
+} {33}
+do_test like-2.34.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\"%'"
+} {34}
+do_test like-2.34.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\"%'"
+} {34}
+do_test like-2.34.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\"%'"
+} {34}
+do_test like-2.35.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '#%'"
+} {35}
+do_test like-2.35.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '#%'"
+} {35}
+do_test like-2.35.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc#%'"
+} {35}
+do_test like-2.36.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\$%'"
+} {36}
+do_test like-2.36.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\$%'"
+} {36}
+do_test like-2.36.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\$%'"
+} {36}
+do_test like-2.38.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '&%'"
+} {38}
+do_test like-2.38.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '&%'"
+} {38}
+do_test like-2.38.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc&%'"
+} {38}
+do_test like-2.39.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '''%'"
+} {39}
+do_test like-2.39.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '''%'"
+} {39}
+do_test like-2.39.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc''%'"
+} {39}
+do_test like-2.40.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '(%'"
+} {40}
+do_test like-2.40.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '(%'"
+} {40}
+do_test like-2.40.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc(%'"
+} {40}
+do_test like-2.41.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE ')%'"
+} {41}
+do_test like-2.41.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE ')%'"
+} {41}
+do_test like-2.41.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc)%'"
+} {41}
+do_test like-2.42.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '*%'"
+} {42}
+do_test like-2.42.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '*%'"
+} {42}
+do_test like-2.42.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc*%'"
+} {42}
+do_test like-2.43.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '+%'"
+} {43}
+do_test like-2.43.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '+%'"
+} {43}
+do_test like-2.43.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc+%'"
+} {43}
+do_test like-2.44.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE ',%'"
+} {44}
+do_test like-2.44.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE ',%'"
+} {44}
+do_test like-2.44.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc,%'"
+} {44}
+do_test like-2.45.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '-%'"
+} {45}
+do_test like-2.45.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '-%'"
+} {45}
+do_test like-2.45.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc-%'"
+} {45}
+do_test like-2.46.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '.%'"
+} {46}
+do_test like-2.46.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '.%'"
+} {46}
+do_test like-2.46.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc.%'"
+} {46}
+do_test like-2.47.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '/%'"
+} {47}
+do_test like-2.47.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '/%'"
+} {47}
+do_test like-2.47.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc/%'"
+} {47}
+do_test like-2.48.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '0%'"
+} {48}
+do_test like-2.48.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '0%'"
+} {48}
+do_test like-2.48.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc0%'"
+} {48}
+do_test like-2.49.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '1%'"
+} {49}
+do_test like-2.49.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '1%'"
+} {49}
+do_test like-2.49.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc1%'"
+} {49}
+do_test like-2.50.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '2%'"
+} {50}
+do_test like-2.50.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '2%'"
+} {50}
+do_test like-2.50.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc2%'"
+} {50}
+do_test like-2.51.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '3%'"
+} {51}
+do_test like-2.51.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '3%'"
+} {51}
+do_test like-2.51.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc3%'"
+} {51}
+do_test like-2.52.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '4%'"
+} {52}
+do_test like-2.52.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '4%'"
+} {52}
+do_test like-2.52.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc4%'"
+} {52}
+do_test like-2.53.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '5%'"
+} {53}
+do_test like-2.53.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '5%'"
+} {53}
+do_test like-2.53.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc5%'"
+} {53}
+do_test like-2.54.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '6%'"
+} {54}
+do_test like-2.54.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '6%'"
+} {54}
+do_test like-2.54.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc6%'"
+} {54}
+do_test like-2.55.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '7%'"
+} {55}
+do_test like-2.55.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '7%'"
+} {55}
+do_test like-2.55.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc7%'"
+} {55}
+do_test like-2.56.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '8%'"
+} {56}
+do_test like-2.56.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '8%'"
+} {56}
+do_test like-2.56.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc8%'"
+} {56}
+do_test like-2.57.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '9%'"
+} {57}
+do_test like-2.57.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '9%'"
+} {57}
+do_test like-2.57.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc9%'"
+} {57}
+do_test like-2.58.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE ':%'"
+} {58}
+do_test like-2.58.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE ':%'"
+} {58}
+do_test like-2.58.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc:%'"
+} {58}
+do_test like-2.59.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE ';%'"
+} {59}
+do_test like-2.59.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE ';%'"
+} {59}
+do_test like-2.59.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc;%'"
+} {59}
+do_test like-2.60.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '<%'"
+} {60}
+do_test like-2.60.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '<%'"
+} {60}
+do_test like-2.60.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc<%'"
+} {60}
+do_test like-2.61.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '=%'"
+} {61}
+do_test like-2.61.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '=%'"
+} {61}
+do_test like-2.61.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc=%'"
+} {61}
+do_test like-2.62.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '>%'"
+} {62}
+do_test like-2.62.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '>%'"
+} {62}
+do_test like-2.62.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc>%'"
+} {62}
+do_test like-2.63.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '?%'"
+} {63}
+do_test like-2.63.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '?%'"
+} {63}
+do_test like-2.63.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc?%'"
+} {63}
+do_test like-2.64.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '@%'"
+} {64}
+do_test like-2.64.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '@%'"
+} {64}
+do_test like-2.64.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc@%'"
+} {64}
+do_test like-2.65.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'A%'"
+} {65 97}
+do_test like-2.65.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'A%'"
+} {65 97}
+do_test like-2.65.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcA%'"
+} {65 97}
+do_test like-2.66.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'B%'"
+} {66 98}
+do_test like-2.66.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'B%'"
+} {66 98}
+do_test like-2.66.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcB%'"
+} {66 98}
+do_test like-2.67.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'C%'"
+} {67 99}
+do_test like-2.67.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'C%'"
+} {67 99}
+do_test like-2.67.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcC%'"
+} {67 99}
+do_test like-2.68.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'D%'"
+} {68 100}
+do_test like-2.68.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'D%'"
+} {68 100}
+do_test like-2.68.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcD%'"
+} {68 100}
+do_test like-2.69.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'E%'"
+} {69 101}
+do_test like-2.69.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'E%'"
+} {69 101}
+do_test like-2.69.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcE%'"
+} {69 101}
+do_test like-2.70.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'F%'"
+} {70 102}
+do_test like-2.70.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'F%'"
+} {70 102}
+do_test like-2.70.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcF%'"
+} {70 102}
+do_test like-2.71.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'G%'"
+} {71 103}
+do_test like-2.71.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'G%'"
+} {71 103}
+do_test like-2.71.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcG%'"
+} {71 103}
+do_test like-2.72.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'H%'"
+} {72 104}
+do_test like-2.72.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'H%'"
+} {72 104}
+do_test like-2.72.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcH%'"
+} {72 104}
+do_test like-2.73.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'I%'"
+} {73 105}
+do_test like-2.73.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'I%'"
+} {73 105}
+do_test like-2.73.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcI%'"
+} {73 105}
+do_test like-2.74.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'J%'"
+} {74 106}
+do_test like-2.74.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'J%'"
+} {74 106}
+do_test like-2.74.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcJ%'"
+} {74 106}
+do_test like-2.75.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'K%'"
+} {75 107}
+do_test like-2.75.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'K%'"
+} {75 107}
+do_test like-2.75.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcK%'"
+} {75 107}
+do_test like-2.76.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'L%'"
+} {76 108}
+do_test like-2.76.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'L%'"
+} {76 108}
+do_test like-2.76.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcL%'"
+} {76 108}
+do_test like-2.77.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'M%'"
+} {77 109}
+do_test like-2.77.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'M%'"
+} {77 109}
+do_test like-2.77.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcM%'"
+} {77 109}
+do_test like-2.78.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'N%'"
+} {78 110}
+do_test like-2.78.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'N%'"
+} {78 110}
+do_test like-2.78.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcN%'"
+} {78 110}
+do_test like-2.79.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'O%'"
+} {79 111}
+do_test like-2.79.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'O%'"
+} {79 111}
+do_test like-2.79.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcO%'"
+} {79 111}
+do_test like-2.80.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'P%'"
+} {80 112}
+do_test like-2.80.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'P%'"
+} {80 112}
+do_test like-2.80.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcP%'"
+} {80 112}
+do_test like-2.81.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'Q%'"
+} {81 113}
+do_test like-2.81.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'Q%'"
+} {81 113}
+do_test like-2.81.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcQ%'"
+} {81 113}
+do_test like-2.82.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'R%'"
+} {82 114}
+do_test like-2.82.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'R%'"
+} {82 114}
+do_test like-2.82.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcR%'"
+} {82 114}
+do_test like-2.83.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'S%'"
+} {83 115}
+do_test like-2.83.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'S%'"
+} {83 115}
+do_test like-2.83.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcS%'"
+} {83 115}
+do_test like-2.84.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'T%'"
+} {84 116}
+do_test like-2.84.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'T%'"
+} {84 116}
+do_test like-2.84.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcT%'"
+} {84 116}
+do_test like-2.85.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'U%'"
+} {85 117}
+do_test like-2.85.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'U%'"
+} {85 117}
+do_test like-2.85.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcU%'"
+} {85 117}
+do_test like-2.86.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'V%'"
+} {86 118}
+do_test like-2.86.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'V%'"
+} {86 118}
+do_test like-2.86.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcV%'"
+} {86 118}
+do_test like-2.87.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'W%'"
+} {87 119}
+do_test like-2.87.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'W%'"
+} {87 119}
+do_test like-2.87.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcW%'"
+} {87 119}
+do_test like-2.88.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'X%'"
+} {88 120}
+do_test like-2.88.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'X%'"
+} {88 120}
+do_test like-2.88.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcX%'"
+} {88 120}
+do_test like-2.89.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'Y%'"
+} {89 121}
+do_test like-2.89.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'Y%'"
+} {89 121}
+do_test like-2.89.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcY%'"
+} {89 121}
+do_test like-2.90.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'Z%'"
+} {90 122}
+do_test like-2.90.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'Z%'"
+} {90 122}
+do_test like-2.90.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcZ%'"
+} {90 122}
+do_test like-2.91.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\[%'"
+} {91}
+do_test like-2.91.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\[%'"
+} {91}
+do_test like-2.91.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\[%'"
+} {91}
+do_test like-2.92.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\\%'"
+} {92}
+do_test like-2.92.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\\%'"
+} {92}
+do_test like-2.92.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\\%'"
+} {92}
+do_test like-2.93.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\]%'"
+} {93}
+do_test like-2.93.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\]%'"
+} {93}
+do_test like-2.93.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\]%'"
+} {93}
+do_test like-2.94.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '^%'"
+} {94}
+do_test like-2.94.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '^%'"
+} {94}
+do_test like-2.94.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc^%'"
+} {94}
+do_test like-2.96.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '`%'"
+} {96}
+do_test like-2.96.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '`%'"
+} {96}
+do_test like-2.96.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc`%'"
+} {96}
+do_test like-2.97.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'a%'"
+} {65 97}
+do_test like-2.97.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'a%'"
+} {65 97}
+do_test like-2.97.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abca%'"
+} {65 97}
+do_test like-2.98.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'b%'"
+} {66 98}
+do_test like-2.98.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'b%'"
+} {66 98}
+do_test like-2.98.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcb%'"
+} {66 98}
+do_test like-2.99.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'c%'"
+} {67 99}
+do_test like-2.99.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'c%'"
+} {67 99}
+do_test like-2.99.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcc%'"
+} {67 99}
+do_test like-2.100.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'd%'"
+} {68 100}
+do_test like-2.100.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'd%'"
+} {68 100}
+do_test like-2.100.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcd%'"
+} {68 100}
+do_test like-2.101.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'e%'"
+} {69 101}
+do_test like-2.101.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'e%'"
+} {69 101}
+do_test like-2.101.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abce%'"
+} {69 101}
+do_test like-2.102.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'f%'"
+} {70 102}
+do_test like-2.102.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'f%'"
+} {70 102}
+do_test like-2.102.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcf%'"
+} {70 102}
+do_test like-2.103.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'g%'"
+} {71 103}
+do_test like-2.103.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'g%'"
+} {71 103}
+do_test like-2.103.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcg%'"
+} {71 103}
+do_test like-2.104.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'h%'"
+} {72 104}
+do_test like-2.104.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'h%'"
+} {72 104}
+do_test like-2.104.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abch%'"
+} {72 104}
+do_test like-2.105.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'i%'"
+} {73 105}
+do_test like-2.105.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'i%'"
+} {73 105}
+do_test like-2.105.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abci%'"
+} {73 105}
+do_test like-2.106.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'j%'"
+} {74 106}
+do_test like-2.106.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'j%'"
+} {74 106}
+do_test like-2.106.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcj%'"
+} {74 106}
+do_test like-2.107.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'k%'"
+} {75 107}
+do_test like-2.107.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'k%'"
+} {75 107}
+do_test like-2.107.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abck%'"
+} {75 107}
+do_test like-2.108.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'l%'"
+} {76 108}
+do_test like-2.108.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'l%'"
+} {76 108}
+do_test like-2.108.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcl%'"
+} {76 108}
+do_test like-2.109.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'm%'"
+} {77 109}
+do_test like-2.109.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'm%'"
+} {77 109}
+do_test like-2.109.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcm%'"
+} {77 109}
+do_test like-2.110.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'n%'"
+} {78 110}
+do_test like-2.110.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'n%'"
+} {78 110}
+do_test like-2.110.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcn%'"
+} {78 110}
+do_test like-2.111.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'o%'"
+} {79 111}
+do_test like-2.111.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'o%'"
+} {79 111}
+do_test like-2.111.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abco%'"
+} {79 111}
+do_test like-2.112.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'p%'"
+} {80 112}
+do_test like-2.112.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'p%'"
+} {80 112}
+do_test like-2.112.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcp%'"
+} {80 112}
+do_test like-2.113.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'q%'"
+} {81 113}
+do_test like-2.113.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'q%'"
+} {81 113}
+do_test like-2.113.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcq%'"
+} {81 113}
+do_test like-2.114.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'r%'"
+} {82 114}
+do_test like-2.114.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'r%'"
+} {82 114}
+do_test like-2.114.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcr%'"
+} {82 114}
+do_test like-2.115.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 's%'"
+} {83 115}
+do_test like-2.115.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 's%'"
+} {83 115}
+do_test like-2.115.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcs%'"
+} {83 115}
+do_test like-2.116.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 't%'"
+} {84 116}
+do_test like-2.116.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 't%'"
+} {84 116}
+do_test like-2.116.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abct%'"
+} {84 116}
+do_test like-2.117.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'u%'"
+} {85 117}
+do_test like-2.117.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'u%'"
+} {85 117}
+do_test like-2.117.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcu%'"
+} {85 117}
+do_test like-2.118.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'v%'"
+} {86 118}
+do_test like-2.118.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'v%'"
+} {86 118}
+do_test like-2.118.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcv%'"
+} {86 118}
+do_test like-2.119.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'w%'"
+} {87 119}
+do_test like-2.119.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'w%'"
+} {87 119}
+do_test like-2.119.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcw%'"
+} {87 119}
+do_test like-2.120.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'x%'"
+} {88 120}
+do_test like-2.120.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'x%'"
+} {88 120}
+do_test like-2.120.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcx%'"
+} {88 120}
+do_test like-2.121.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'y%'"
+} {89 121}
+do_test like-2.121.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'y%'"
+} {89 121}
+do_test like-2.121.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcy%'"
+} {89 121}
+do_test like-2.122.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE 'z%'"
+} {90 122}
+do_test like-2.122.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE 'z%'"
+} {90 122}
+do_test like-2.122.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abcz%'"
+} {90 122}
+do_test like-2.123.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\173%'"
+} {123}
+do_test like-2.123.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\173%'"
+} {123}
+do_test like-2.123.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\173%'"
+} {123}
+do_test like-2.124.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '|%'"
+} {124}
+do_test like-2.124.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '|%'"
+} {124}
+do_test like-2.124.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc|%'"
+} {124}
+do_test like-2.125.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '\175%'"
+} {125}
+do_test like-2.125.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '\175%'"
+} {125}
+do_test like-2.125.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc\175%'"
+} {125}
+do_test like-2.126.1 {
+ db eval "SELECT x FROM t1 WHERE y LIKE '~%'"
+} {126}
+do_test like-2.126.2 {
+ db eval "SELECT x FROM t2 WHERE y LIKE '~%'"
+} {126}
+do_test like-2.126.3 {
+ db eval "SELECT x FROM t3 WHERE y LIKE 'abc~%'"
+} {126}
+
+
+finish_test
diff --git a/third_party/sqlite/test/limit.test b/third_party/sqlite/test/limit.test
new file mode 100755
index 0000000..e5aac70
--- /dev/null
+++ b/third_party/sqlite/test/limit.test
@@ -0,0 +1,472 @@
+# 2001 November 6
+#
+# 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 file is testing the LIMIT ... OFFSET ... clause
+# of SELECT statements.
+#
+# $Id: limit.test,v 1.32 2008/08/02 03:50:39 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+execsql {
+ CREATE TABLE t1(x int, y int);
+ BEGIN;
+}
+for {set i 1} {$i<=32} {incr i} {
+ for {set j 0} {(1<<$j)<$i} {incr j} {}
+ execsql "INSERT INTO t1 VALUES([expr {32-$i}],[expr {10-$j}])"
+}
+execsql {
+ COMMIT;
+}
+
+do_test limit-1.0 {
+ execsql {SELECT count(*) FROM t1}
+} {32}
+do_test limit-1.1 {
+ execsql {SELECT count(*) FROM t1 LIMIT 5}
+} {32}
+do_test limit-1.2.1 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 5}
+} {0 1 2 3 4}
+do_test limit-1.2.2 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 5 OFFSET 2}
+} {2 3 4 5 6}
+do_test limit-1.2.3 {
+ execsql {SELECT x FROM t1 ORDER BY x+1 LIMIT 5 OFFSET -2}
+} {0 1 2 3 4}
+do_test limit-1.2.4 {
+ execsql {SELECT x FROM t1 ORDER BY x+1 LIMIT 2, -5}
+} {2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31}
+do_test limit-1.2.5 {
+ execsql {SELECT x FROM t1 ORDER BY x+1 LIMIT -2, 5}
+} {0 1 2 3 4}
+do_test limit-1.2.6 {
+ execsql {SELECT x FROM t1 ORDER BY x+1 LIMIT -2, -5}
+} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31}
+do_test limit-1.2.7 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 2, 5}
+} {2 3 4 5 6}
+do_test limit-1.3 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 5 OFFSET 5}
+} {5 6 7 8 9}
+do_test limit-1.4.1 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 50 OFFSET 30}
+} {30 31}
+do_test limit-1.4.2 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 30, 50}
+} {30 31}
+do_test limit-1.5 {
+ execsql {SELECT x FROM t1 ORDER BY x LIMIT 50 OFFSET 50}
+} {}
+do_test limit-1.6 {
+ execsql {SELECT * FROM t1 AS a, t1 AS b ORDER BY a.x, b.x LIMIT 5}
+} {0 5 0 5 0 5 1 5 0 5 2 5 0 5 3 5 0 5 4 5}
+do_test limit-1.7 {
+ execsql {SELECT * FROM t1 AS a, t1 AS b ORDER BY a.x, b.x LIMIT 5 OFFSET 32}
+} {1 5 0 5 1 5 1 5 1 5 2 5 1 5 3 5 1 5 4 5}
+
+ifcapable {view && subquery} {
+ do_test limit-2.1 {
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM t1 LIMIT 2;
+ SELECT count(*) FROM (SELECT * FROM v1);
+ }
+ } 2
+} ;# ifcapable view
+do_test limit-2.2 {
+ execsql {
+ CREATE TABLE t2 AS SELECT * FROM t1 LIMIT 2;
+ SELECT count(*) FROM t2;
+ }
+} 2
+ifcapable subquery {
+ do_test limit-2.3 {
+ execsql {
+ SELECT count(*) FROM t1 WHERE rowid IN (SELECT rowid FROM t1 LIMIT 2);
+ }
+ } 2
+}
+
+ifcapable subquery {
+ do_test limit-3.1 {
+ execsql {
+ SELECT z FROM (SELECT y*10+x AS z FROM t1 ORDER BY x LIMIT 10)
+ ORDER BY z LIMIT 5;
+ }
+ } {50 51 52 53 54}
+}
+
+do_test limit-4.1 {
+ ifcapable subquery {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 SELECT x FROM t1 ORDER BY x LIMIT 10 OFFSET 1;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ INSERT INTO t3 SELECT x+(SELECT max(x) FROM t3) FROM t3;
+ END;
+ SELECT count(*) FROM t3;
+ }
+ } else {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 SELECT x FROM t1 ORDER BY x LIMIT 10 OFFSET 1;
+ }
+ for {set i 0} {$i<10} {incr i} {
+ set max_x_t3 [execsql {SELECT max(x) FROM t3}]
+ execsql "INSERT INTO t3 SELECT x+$max_x_t3 FROM t3;"
+ }
+ execsql {
+ END;
+ SELECT count(*) FROM t3;
+ }
+ }
+} {10240}
+do_test limit-4.2 {
+ execsql {
+ SELECT x FROM t3 LIMIT 2 OFFSET 10000
+ }
+} {10001 10002}
+do_test limit-4.3 {
+ execsql {
+ CREATE TABLE t4 AS SELECT x,
+ 'abcdefghijklmnopqrstuvwyxz ABCDEFGHIJKLMNOPQRSTUVWYXZ' || x ||
+ 'abcdefghijklmnopqrstuvwyxz ABCDEFGHIJKLMNOPQRSTUVWYXZ' || x ||
+ 'abcdefghijklmnopqrstuvwyxz ABCDEFGHIJKLMNOPQRSTUVWYXZ' || x ||
+ 'abcdefghijklmnopqrstuvwyxz ABCDEFGHIJKLMNOPQRSTUVWYXZ' || x ||
+ 'abcdefghijklmnopqrstuvwyxz ABCDEFGHIJKLMNOPQRSTUVWYXZ' || x AS y
+ FROM t3 LIMIT 1000;
+ SELECT x FROM t4 ORDER BY y DESC LIMIT 1 OFFSET 999;
+ }
+} {1000}
+
+do_test limit-5.1 {
+ execsql {
+ CREATE TABLE t5(x,y);
+ INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x BETWEEN 10 AND 15
+ ORDER BY x LIMIT 2;
+ SELECT * FROM t5 ORDER BY x;
+ }
+} {5 15 6 16}
+do_test limit-5.2 {
+ execsql {
+ DELETE FROM t5;
+ INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x BETWEEN 10 AND 15
+ ORDER BY x DESC LIMIT 2;
+ SELECT * FROM t5 ORDER BY x;
+ }
+} {9 19 10 20}
+do_test limit-5.3 {
+ execsql {
+ DELETE FROM t5;
+ INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x ORDER BY x DESC LIMIT 31;
+ SELECT * FROM t5 ORDER BY x LIMIT 2;
+ }
+} {-4 6 -3 7}
+do_test limit-5.4 {
+ execsql {
+ SELECT * FROM t5 ORDER BY x DESC, y DESC LIMIT 2;
+ }
+} {21 41 21 39}
+do_test limit-5.5 {
+ execsql {
+ DELETE FROM t5;
+ INSERT INTO t5 SELECT a.x*100+b.x, a.y*100+b.y FROM t1 AS a, t1 AS b
+ ORDER BY 1, 2 LIMIT 1000;
+ SELECT count(*), sum(x), sum(y), min(x), max(x), min(y), max(y) FROM t5;
+ }
+} {1000 1528204 593161 0 3107 505 1005}
+
+# There is some contraversy about whether LIMIT 0 should be the same as
+# no limit at all or if LIMIT 0 should result in zero output rows.
+#
+do_test limit-6.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t6(a);
+ INSERT INTO t6 VALUES(1);
+ INSERT INTO t6 VALUES(2);
+ INSERT INTO t6 SELECT a+2 FROM t6;
+ COMMIT;
+ SELECT * FROM t6;
+ }
+} {1 2 3 4}
+do_test limit-6.2 {
+ execsql {
+ SELECT * FROM t6 LIMIT -1 OFFSET -1;
+ }
+} {1 2 3 4}
+do_test limit-6.3 {
+ execsql {
+ SELECT * FROM t6 LIMIT 2 OFFSET -123;
+ }
+} {1 2}
+do_test limit-6.4 {
+ execsql {
+ SELECT * FROM t6 LIMIT -432 OFFSET 2;
+ }
+} {3 4}
+do_test limit-6.5 {
+ execsql {
+ SELECT * FROM t6 LIMIT -1
+ }
+} {1 2 3 4}
+do_test limit-6.6 {
+ execsql {
+ SELECT * FROM t6 LIMIT -1 OFFSET 1
+ }
+} {2 3 4}
+do_test limit-6.7 {
+ execsql {
+ SELECT * FROM t6 LIMIT 0
+ }
+} {}
+do_test limit-6.8 {
+ execsql {
+ SELECT * FROM t6 LIMIT 0 OFFSET 1
+ }
+} {}
+
+# Make sure LIMIT works well with compound SELECT statements.
+# Ticket #393
+#
+ifcapable compound {
+do_test limit-7.1.1 {
+ catchsql {
+ SELECT x FROM t2 LIMIT 5 UNION ALL SELECT a FROM t6;
+ }
+} {1 {LIMIT clause should come after UNION ALL not before}}
+do_test limit-7.1.2 {
+ catchsql {
+ SELECT x FROM t2 LIMIT 5 UNION SELECT a FROM t6;
+ }
+} {1 {LIMIT clause should come after UNION not before}}
+do_test limit-7.1.3 {
+ catchsql {
+ SELECT x FROM t2 LIMIT 5 EXCEPT SELECT a FROM t6 LIMIT 3;
+ }
+} {1 {LIMIT clause should come after EXCEPT not before}}
+do_test limit-7.1.4 {
+ catchsql {
+ SELECT x FROM t2 LIMIT 0,5 INTERSECT SELECT a FROM t6;
+ }
+} {1 {LIMIT clause should come after INTERSECT not before}}
+do_test limit-7.2 {
+ execsql {
+ SELECT x FROM t2 UNION ALL SELECT a FROM t6 LIMIT 5;
+ }
+} {31 30 1 2 3}
+do_test limit-7.3 {
+ execsql {
+ SELECT x FROM t2 UNION ALL SELECT a FROM t6 LIMIT 3 OFFSET 1;
+ }
+} {30 1 2}
+do_test limit-7.4 {
+ execsql {
+ SELECT x FROM t2 UNION ALL SELECT a FROM t6 ORDER BY 1 LIMIT 3 OFFSET 1;
+ }
+} {2 3 4}
+do_test limit-7.5 {
+ execsql {
+ SELECT x FROM t2 UNION SELECT x+2 FROM t2 LIMIT 2 OFFSET 1;
+ }
+} {31 32}
+do_test limit-7.6 {
+ execsql {
+ SELECT x FROM t2 UNION SELECT x+2 FROM t2 ORDER BY 1 DESC LIMIT 2 OFFSET 1;
+ }
+} {32 31}
+do_test limit-7.7 {
+ execsql {
+ SELECT a+9 FROM t6 EXCEPT SELECT y FROM t2 LIMIT 2;
+ }
+} {11 12}
+do_test limit-7.8 {
+ execsql {
+ SELECT a+9 FROM t6 EXCEPT SELECT y FROM t2 ORDER BY 1 DESC LIMIT 2;
+ }
+} {13 12}
+do_test limit-7.9 {
+ execsql {
+ SELECT a+26 FROM t6 INTERSECT SELECT x FROM t2 LIMIT 1;
+ }
+} {30}
+do_test limit-7.10 {
+ execsql {
+ SELECT a+27 FROM t6 INTERSECT SELECT x FROM t2 LIMIT 1;
+ }
+} {30}
+do_test limit-7.11 {
+ execsql {
+ SELECT a+27 FROM t6 INTERSECT SELECT x FROM t2 LIMIT 1 OFFSET 1;
+ }
+} {31}
+do_test limit-7.12 {
+ execsql {
+ SELECT a+27 FROM t6 INTERSECT SELECT x FROM t2
+ ORDER BY 1 DESC LIMIT 1 OFFSET 1;
+ }
+} {30}
+} ;# ifcapable compound
+
+# Tests for limit in conjunction with distinct. The distinct should
+# occur before both the limit and the offset. Ticket #749.
+#
+do_test limit-8.1 {
+ execsql {
+ SELECT DISTINCT cast(round(x/100) as integer) FROM t3 LIMIT 5;
+ }
+} {0 1 2 3 4}
+do_test limit-8.2 {
+ execsql {
+ SELECT DISTINCT cast(round(x/100) as integer) FROM t3 LIMIT 5 OFFSET 5;
+ }
+} {5 6 7 8 9}
+do_test limit-8.3 {
+ execsql {
+ SELECT DISTINCT cast(round(x/100) as integer) FROM t3 LIMIT 5 OFFSET 25;
+ }
+} {25 26 27 28 29}
+
+# Make sure limits on multiple subqueries work correctly.
+# Ticket #1035
+#
+ifcapable subquery {
+ do_test limit-9.1 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t6 LIMIT 3);
+ }
+ } {1 2 3}
+}
+do_test limit-9.2.1 {
+ execsql {
+ CREATE TABLE t7 AS SELECT * FROM t6;
+ }
+} {}
+ifcapable subquery {
+ do_test limit-9.2.2 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t7 LIMIT 3);
+ }
+ } {1 2 3}
+}
+ifcapable compound {
+ ifcapable subquery {
+ do_test limit-9.3 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t6 LIMIT 3)
+ UNION
+ SELECT * FROM (SELECT * FROM t7 LIMIT 3)
+ ORDER BY 1
+ }
+ } {1 2 3}
+ do_test limit-9.4 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t6 LIMIT 3)
+ UNION
+ SELECT * FROM (SELECT * FROM t7 LIMIT 3)
+ ORDER BY 1
+ LIMIT 2
+ }
+ } {1 2}
+ }
+ do_test limit-9.5 {
+ catchsql {
+ SELECT * FROM t6 LIMIT 3
+ UNION
+ SELECT * FROM t7 LIMIT 3
+ }
+ } {1 {LIMIT clause should come after UNION not before}}
+}
+
+# Test LIMIT and OFFSET using SQL variables.
+do_test limit-10.1 {
+ set limit 10
+ db eval {
+ SELECT x FROM t1 LIMIT :limit;
+ }
+} {31 30 29 28 27 26 25 24 23 22}
+do_test limit-10.2 {
+ set limit 5
+ set offset 5
+ db eval {
+ SELECT x FROM t1 LIMIT :limit OFFSET :offset;
+ }
+} {26 25 24 23 22}
+do_test limit-10.3 {
+ set limit -1
+ db eval {
+ SELECT x FROM t1 WHERE x<10 LIMIT :limit;
+ }
+} {9 8 7 6 5 4 3 2 1 0}
+do_test limit-10.4 {
+ set limit 1.5
+ set rc [catch {
+ db eval {
+ SELECT x FROM t1 WHERE x<10 LIMIT :limit;
+ } } msg]
+ list $rc $msg
+} {1 {datatype mismatch}}
+do_test limit-10.5 {
+ set limit "hello world"
+ set rc [catch {
+ db eval {
+ SELECT x FROM t1 WHERE x<10 LIMIT :limit;
+ } } msg]
+ list $rc $msg
+} {1 {datatype mismatch}}
+
+ifcapable subquery {
+do_test limit-11.1 {
+ db eval {
+ SELECT x FROM (SELECT x FROM t1 ORDER BY x LIMIT 0) ORDER BY x
+ }
+} {}
+} ;# ifcapable subquery
+
+# Test error processing.
+#
+do_test limit-12.1 {
+ catchsql {
+ SELECT * FROM t1 LIMIT replace(1)
+ }
+} {1 {wrong number of arguments to function replace()}}
+do_test limit-12.2 {
+ catchsql {
+ SELECT * FROM t1 LIMIT 5 OFFSET replace(1)
+ }
+} {1 {wrong number of arguments to function replace()}}
+do_test limit-12.3 {
+ catchsql {
+ SELECT * FROM t1 LIMIT x
+ }
+} {1 {no such column: x}}
+do_test limit-12.4 {
+ catchsql {
+ SELECT * FROM t1 LIMIT 1 OFFSET x
+ }
+} {1 {no such column: x}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/loadext.test b/third_party/sqlite/test/loadext.test
new file mode 100755
index 0000000..e0fecd3
--- /dev/null
+++ b/third_party/sqlite/test/loadext.test
@@ -0,0 +1,272 @@
+# 2006 July 14
+#
+# 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 extension loading.
+#
+# $Id: loadext.test,v 1.14 2008/08/02 03:50:39 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !load_ext {
+ finish_test
+ return
+}
+
+# The name of the test extension varies by operating system.
+#
+if {$::tcl_platform(platform) eq "windows"} {
+ set testextension ./testloadext.dll
+} else {
+ set testextension ./libtestloadext.so
+}
+set gcc_shared -shared
+if {$::tcl_platform(os) eq "Darwin"} {
+ set gcc_shared -dynamiclib
+}
+
+# The error messages tested by this file are operating system dependent
+# (because they are returned by sqlite3OsDlError()). For now, they only
+# work with UNIX (and probably only certain kinds of UNIX).
+#
+# When a shared-object cannot be opened because it does not exist, the
+# format of the message returned is:
+#
+# [format $dlerror_nosuchfile <shared-object-name>]
+#
+# When a shared-object cannot be opened because it consists of the 4
+# characters "blah" only, we expect the error message to be:
+#
+# [format $dlerror_notadll <shared-object-name>]
+#
+# When a symbol cannot be found within an open shared-object, the error
+# message should be:
+#
+# [format $dlerror_nosymbol <shared-object-name> <symbol-name>]
+#
+# The exact error messages are not important. The important bit is
+# that SQLite is correctly copying the message from xDlError().
+#
+set dlerror_nosuchfile \
+ {%s: cannot open shared object file: No such file or directory}
+set dlerror_notadll {%s: file too short}
+set dlerror_nosymbol {%s: undefined symbol: %s}
+
+if {$::tcl_platform(os) eq "Darwin"} {
+ set dlerror_nosuchfile {dlopen(%s, 10): image not found}
+ set dlerror_notadll {dlopen(%1$s, 10): no suitable image found. Did find:
+ %1$s: file to short}
+ set dlerror_nosymbol {dlsym(XXX, %2$s): symbol not found}
+}
+
+# Make sure the test extension actually exists. If it does not
+# exist, try to create it. If unable to create it, then skip this
+# test file.
+#
+if {![file exists $testextension]} {
+ set srcdir [file dir $testdir]/src
+ set testextsrc $srcdir/test_loadext.c
+ if {[catch {
+ exec gcc $gcc_shared -Wall -I$srcdir -I. -g $testextsrc -o $testextension
+ } msg]} {
+ puts "Skipping loadext tests: Test extension not built..."
+ puts $msg
+ finish_test
+ return
+ }
+}
+
+# Test that loading the extension produces the expected results - adding
+# the half() function to the specified database handle.
+#
+do_test loadext-1.1 {
+ catchsql {
+ SELECT half(1.0);
+ }
+} {1 {no such function: half}}
+do_test loadext-1.2 {
+ db enable_load_extension 1
+ sqlite3_load_extension db $testextension testloadext_init
+ catchsql {
+ SELECT half(1.0);
+ }
+} {0 0.5}
+
+# Test that a second database connection (db2) can load the extension also.
+#
+do_test loadext-1.3 {
+ sqlite3 db2 test.db
+ sqlite3_enable_load_extension db2 1
+ catchsql {
+ SELECT half(1.0);
+ } db2
+} {1 {no such function: half}}
+do_test loadext-1.4 {
+ sqlite3_load_extension db2 $testextension testloadext_init
+ catchsql {
+ SELECT half(1.0);
+ } db2
+} {0 0.5}
+
+# Close the first database connection. Then check that the second database
+# can still use the half() function without a problem.
+#
+do_test loadext-1.5 {
+ db close
+ catchsql {
+ SELECT half(1.0);
+ } db2
+} {0 0.5}
+
+db2 close
+sqlite3 db test.db
+sqlite3_enable_load_extension db 1
+
+# Try to load an extension for which the file does not exist.
+#
+do_test loadext-2.1 {
+ file delete -force ${testextension}xx
+ set rc [catch {
+ sqlite3_load_extension db "${testextension}xx"
+ } msg]
+ list $rc $msg
+} [list 1 [format $dlerror_nosuchfile ${testextension}xx]]
+
+# Try to load an extension for which the file is not a shared object
+#
+do_test loadext-2.2 {
+ set fd [open "${testextension}xx" w]
+ puts $fd blah
+ close $fd
+ set rc [catch {
+ sqlite3_load_extension db "${testextension}xx"
+ } msg]
+ list $rc $msg
+} [list 1 [format $dlerror_notadll ${testextension}xx]]
+
+# Try to load an extension for which the file is present but the
+# entry point is not.
+#
+do_test loadext-2.3 {
+ set rc [catch {
+ sqlite3_load_extension db $testextension icecream
+ } msg]
+ if {$::tcl_platform(os) eq "Darwin"} {
+ regsub {0x[1234567890abcdefABCDEF]*} $msg XXX msg
+ }
+ list $rc $msg
+} [list 1 [format $dlerror_nosymbol $testextension icecream]]
+
+# Try to load an extension for which the entry point fails (returns non-zero)
+#
+do_test loadext-2.4 {
+ set rc [catch {
+ sqlite3_load_extension db $testextension testbrokenext_init
+ } msg]
+ list $rc $msg
+} {1 {error during initialization: broken!}}
+
+############################################################################
+# Tests for the load_extension() SQL function
+#
+
+db close
+sqlite3 db test.db
+sqlite3_enable_load_extension db 1
+do_test loadext-3.1 {
+ catchsql {
+ SELECT half(5);
+ }
+} {1 {no such function: half}}
+do_test loadext-3.2 {
+ set res [catchsql {
+ SELECT load_extension($::testextension)
+ }]
+ if {$::tcl_platform(os) eq "Darwin"} {
+ regsub {0x[1234567890abcdefABCDEF]*} $res XXX res
+ }
+ set res
+} [list 1 [format $dlerror_nosymbol $testextension sqlite3_extension_init]]
+do_test loadext-3.3 {
+ catchsql {
+ SELECT load_extension($::testextension,'testloadext_init')
+ }
+} {0 {{}}}
+do_test loadext-3.4 {
+ catchsql {
+ SELECT half(5);
+ }
+} {0 2.5}
+do_test loadext-3.5 {
+ db eval {
+ SELECT sqlite3_status('MEMORY_USED') AS mused
+ } break
+ puts -nonewline " (memory_used=$mused) "
+ expr {$mused>0}
+} {1}
+do_test loadext-3.6 {
+ catchsql {
+ SELECT sqlite3_status('MEMORY_USED_X') AS mused
+ }
+} {1 {unknown status property: MEMORY_USED_X}}
+do_test loadext-3.7 {
+ catchsql {
+ SELECT sqlite3_status(4.53) AS mused
+ }
+} {1 {unknown status type}}
+do_test loadext-3.8 {
+ catchsql {
+ SELECT sqlite3_status(23) AS mused
+ }
+} {1 {sqlite3_status(23,...) returns 21}}
+
+# Ticket #1863
+# Make sure the extension loading mechanism will not work unless it
+# is explicitly enabled.
+#
+db close
+sqlite3 db test.db
+do_test loadext-4.1 {
+ catchsql {
+ SELECT load_extension($::testextension,'testloadext_init')
+ }
+} {1 {not authorized}}
+do_test loadext-4.2 {
+ sqlite3_enable_load_extension db 1
+ catchsql {
+ SELECT load_extension($::testextension,'testloadext_init')
+ }
+} {0 {{}}}
+
+do_test loadext-4.3 {
+ sqlite3_enable_load_extension db 0
+ catchsql {
+ SELECT load_extension($::testextension,'testloadext_init')
+ }
+} {1 {not authorized}}
+
+source $testdir/malloc_common.tcl
+
+
+# Malloc failure in sqlite3_auto_extension and sqlite3_load_extension
+#
+do_malloc_test loadext-5 -tclprep {
+ sqlite3_reset_auto_extension
+} -tclbody {
+ if {[autoinstall_test_functions]==7} {error "out of memory"}
+}
+do_malloc_test loadext-6 -tclbody {
+ db enable_load_extension 1
+ sqlite3_load_extension db $::testextension testloadext_init
+}
+autoinstall_test_functions
+
+finish_test
diff --git a/third_party/sqlite/test/loadext2.test b/third_party/sqlite/test/loadext2.test
new file mode 100755
index 0000000..3d01539
--- /dev/null
+++ b/third_party/sqlite/test/loadext2.test
@@ -0,0 +1,144 @@
+# 2006 August 23
+#
+# 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 automatic extension loading and the
+# sqlite3_auto_extension() API.
+#
+# $Id: loadext2.test,v 1.3 2008/03/19 16:08:54 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if the approriate APIs are defined
+# in the system under test.
+#
+ifcapable !load_ext {
+ finish_test
+ return
+}
+if {[info command sqlite3_auto_extension_sqr]==""} {
+ finish_test
+ return
+}
+
+
+# None of the extension are loaded by default.
+#
+do_test loadext2-1.1 {
+ catchsql {
+ SELECT sqr(2)
+ }
+} {1 {no such function: sqr}}
+do_test loadext2-1.2 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {1 {no such function: cube}}
+
+# Register auto-loaders. Still functions do not exist.
+#
+do_test loadext2-1.3 {
+ sqlite3_auto_extension_sqr
+ sqlite3_auto_extension_cube
+ catchsql {
+ SELECT sqr(2)
+ }
+} {1 {no such function: sqr}}
+do_test loadext2-1.4 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {1 {no such function: cube}}
+
+
+# Functions do exist in a new database connection
+#
+do_test loadext2-1.5 {
+ sqlite3 db test.db
+ catchsql {
+ SELECT sqr(2)
+ }
+} {0 4.0}
+do_test loadext2-1.6 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {0 8.0}
+
+
+# Reset extension auto loading. Existing extensions still exist.
+#
+do_test loadext2-1.7 {
+ sqlite3_reset_auto_extension
+ catchsql {
+ SELECT sqr(2)
+ }
+} {0 4.0}
+do_test loadext2-1.8 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {0 8.0}
+
+
+# Register only the sqr() function.
+#
+do_test loadext2-1.9 {
+ sqlite3_auto_extension_sqr
+ sqlite3 db test.db
+ catchsql {
+ SELECT sqr(2)
+ }
+} {0 4.0}
+do_test loadext2-1.10 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {1 {no such function: cube}}
+
+# Register only the cube() function.
+#
+do_test loadext2-1.11 {
+ sqlite3_reset_auto_extension
+ sqlite3_auto_extension_cube
+ sqlite3 db test.db
+ catchsql {
+ SELECT sqr(2)
+ }
+} {1 {no such function: sqr}}
+do_test loadext2-1.12 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {0 8.0}
+
+# Register a broken entry point.
+#
+do_test loadext2-1.13 {
+ sqlite3_auto_extension_broken
+ set rc [catch {sqlite3 db test.db} errmsg]
+ lappend rc $errmsg
+} {1 {automatic extension loading failed: broken autoext!}}
+do_test loadext2-1.14 {
+ catchsql {
+ SELECT sqr(2)
+ }
+} {1 {no such function: sqr}}
+do_test loadext2-1.15 {
+ catchsql {
+ SELECT cube(2)
+ }
+} {0 8.0}
+
+
+sqlite3_reset_auto_extension
+autoinstall_test_functions
+finish_test
diff --git a/third_party/sqlite/test/lock.test b/third_party/sqlite/test/lock.test
new file mode 100755
index 0000000..e453ffd
--- /dev/null
+++ b/third_party/sqlite/test/lock.test
@@ -0,0 +1,354 @@
+# 2001 September 15
+#
+# 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 database locks.
+#
+# $Id: lock.test,v 1.33 2006/08/16 16:42:48 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create an alternative connection to the database
+#
+do_test lock-1.0 {
+ sqlite3 db2 ./test.db
+ set dummy {}
+} {}
+do_test lock-1.1 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test lock-1.2 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name} db2
+} {}
+do_test lock-1.3 {
+ execsql {CREATE TABLE t1(a int, b int)}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {t1}
+do_test lock-1.5 {
+ catchsql {
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
+ } db2
+} {0 t1}
+
+do_test lock-1.6 {
+ execsql {INSERT INTO t1 VALUES(1,2)}
+ execsql {SELECT * FROM t1}
+} {1 2}
+# Update: The schema is now brought up to date by test lock-1.5.
+# do_test lock-1.7.1 {
+# catchsql {SELECT * FROM t1} db2
+# } {1 {no such table: t1}}
+do_test lock-1.7.2 {
+ catchsql {SELECT * FROM t1} db2
+} {0 {1 2}}
+do_test lock-1.8 {
+ execsql {UPDATE t1 SET a=b, b=a} db2
+ execsql {SELECT * FROM t1} db2
+} {2 1}
+do_test lock-1.9 {
+ execsql {SELECT * FROM t1}
+} {2 1}
+do_test lock-1.10 {
+ execsql {BEGIN TRANSACTION}
+ execsql {UPDATE t1 SET a = 0 WHERE 0}
+ execsql {SELECT * FROM t1}
+} {2 1}
+do_test lock-1.11 {
+ catchsql {SELECT * FROM t1} db2
+} {0 {2 1}}
+do_test lock-1.12 {
+ execsql {ROLLBACK}
+ catchsql {SELECT * FROM t1}
+} {0 {2 1}}
+
+do_test lock-1.13 {
+ execsql {CREATE TABLE t2(x int, y int)}
+ execsql {INSERT INTO t2 VALUES(8,9)}
+ execsql {SELECT * FROM t2}
+} {8 9}
+do_test lock-1.14.1 {
+ catchsql {SELECT * FROM t2} db2
+} {1 {no such table: t2}}
+do_test lock-1.14.2 {
+ catchsql {SELECT * FROM t1} db2
+} {0 {2 1}}
+do_test lock-1.15 {
+ catchsql {SELECT * FROM t2} db2
+} {0 {8 9}}
+
+do_test lock-1.16 {
+ db eval {SELECT * FROM t1} qv {
+ set x [db eval {SELECT * FROM t1}]
+ }
+ set x
+} {2 1}
+do_test lock-1.17 {
+ db eval {SELECT * FROM t1} qv {
+ set x [db eval {SELECT * FROM t2}]
+ }
+ set x
+} {8 9}
+
+# You cannot UPDATE a table from within the callback of a SELECT
+# on that same table because the SELECT has the table locked.
+#
+# 2006-08-16: Reads no longer block writes within the same
+# database connection.
+#
+#do_test lock-1.18 {
+# db eval {SELECT * FROM t1} qv {
+# set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg]
+# lappend r $msg
+# }
+# set r
+#} {1 {database table is locked}}
+
+# But you can UPDATE a different table from the one that is used in
+# the SELECT.
+#
+do_test lock-1.19 {
+ db eval {SELECT * FROM t1} qv {
+ set r [catch {db eval {UPDATE t2 SET x=y, y=x}} msg]
+ lappend r $msg
+ }
+ set r
+} {0 {}}
+do_test lock-1.20 {
+ execsql {SELECT * FROM t2}
+} {9 8}
+
+# It is possible to do a SELECT of the same table within the
+# callback of another SELECT on that same table because two
+# or more read-only cursors can be open at once.
+#
+do_test lock-1.21 {
+ db eval {SELECT * FROM t1} qv {
+ set r [catch {db eval {SELECT a FROM t1}} msg]
+ lappend r $msg
+ }
+ set r
+} {0 2}
+
+# Under UNIX you can do two SELECTs at once with different database
+# connections, because UNIX supports reader/writer locks. Under windows,
+# this is not possible.
+#
+if {$::tcl_platform(platform)=="unix"} {
+ do_test lock-1.22 {
+ db eval {SELECT * FROM t1} qv {
+ set r [catch {db2 eval {SELECT a FROM t1}} msg]
+ lappend r $msg
+ }
+ set r
+ } {0 2}
+}
+integrity_check lock-1.23
+
+# If one thread has a transaction another thread cannot start
+# a transaction. -> Not true in version 3.0. But if one thread
+# as a RESERVED lock another thread cannot acquire one.
+#
+do_test lock-2.1 {
+ execsql {BEGIN TRANSACTION}
+ execsql {UPDATE t1 SET a = 0 WHERE 0}
+ execsql {BEGIN TRANSACTION} db2
+ set r [catch {execsql {UPDATE t1 SET a = 0 WHERE 0} db2} msg]
+ execsql {ROLLBACK} db2
+ lappend r $msg
+} {1 {database is locked}}
+
+# A thread can read when another has a RESERVED lock.
+#
+do_test lock-2.2 {
+ catchsql {SELECT * FROM t2} db2
+} {0 {9 8}}
+
+# If the other thread (the one that does not hold the transaction with
+# a RESERVED lock) tries to get a RESERVED lock, we do get a busy callback
+# as long as we were not orginally holding a READ lock.
+#
+do_test lock-2.3.1 {
+ proc callback {count} {
+ set ::callback_value $count
+ break
+ }
+ set ::callback_value {}
+ db2 busy callback
+ # db2 does not hold a lock so we should get a busy callback here
+ set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
+ lappend r $msg
+ lappend r $::callback_value
+} {1 {database is locked} 0}
+do_test lock-2.3.2 {
+ set ::callback_value {}
+ execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
+ # This time db2 does hold a read lock. No busy callback this time.
+ set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
+ lappend r $msg
+ lappend r $::callback_value
+} {1 {database is locked} {}}
+catch {execsql {ROLLBACK} db2}
+do_test lock-2.4.1 {
+ proc callback {count} {
+ lappend ::callback_value $count
+ if {$count>4} break
+ }
+ set ::callback_value {}
+ db2 busy callback
+ # We get a busy callback because db2 is not holding a lock
+ set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
+ lappend r $msg
+ lappend r $::callback_value
+} {1 {database is locked} {0 1 2 3 4 5}}
+do_test lock-2.4.2 {
+ proc callback {count} {
+ lappend ::callback_value $count
+ if {$count>4} break
+ }
+ set ::callback_value {}
+ db2 busy callback
+ execsql {BEGIN; SELECT rowid FROM sqlite_master LIMIT 1} db2
+ # No busy callback this time because we are holding a lock
+ set r [catch {execsql {UPDATE t1 SET a=b, b=a} db2} msg]
+ lappend r $msg
+ lappend r $::callback_value
+} {1 {database is locked} {}}
+catch {execsql {ROLLBACK} db2}
+do_test lock-2.5 {
+ proc callback {count} {
+ lappend ::callback_value $count
+ if {$count>4} break
+ }
+ set ::callback_value {}
+ db2 busy callback
+ set r [catch {execsql {SELECT * FROM t1} db2} msg]
+ lappend r $msg
+ lappend r $::callback_value
+} {0 {2 1} {}}
+execsql {ROLLBACK}
+
+# Test the built-in busy timeout handler
+#
+do_test lock-2.8 {
+ db2 timeout 400
+ execsql BEGIN
+ execsql {UPDATE t1 SET a = 0 WHERE 0}
+ catchsql {BEGIN EXCLUSIVE;} db2
+} {1 {database is locked}}
+do_test lock-2.9 {
+ db2 timeout 0
+ execsql COMMIT
+} {}
+integrity_check lock-2.10
+
+# Try to start two transactions in a row
+#
+do_test lock-3.1 {
+ execsql {BEGIN TRANSACTION}
+ set r [catch {execsql {BEGIN TRANSACTION}} msg]
+ execsql {ROLLBACK}
+ lappend r $msg
+} {1 {cannot start a transaction within a transaction}}
+integrity_check lock-3.2
+
+# Make sure the busy handler and error messages work when
+# opening a new pointer to the database while another pointer
+# has the database locked.
+#
+do_test lock-4.1 {
+ db2 close
+ catch {db eval ROLLBACK}
+ db eval BEGIN
+ db eval {UPDATE t1 SET a=0 WHERE 0}
+ sqlite3 db2 ./test.db
+ catchsql {UPDATE t1 SET a=0} db2
+} {1 {database is locked}}
+do_test lock-4.2 {
+ set ::callback_value {}
+ set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
+ lappend rc $msg $::callback_value
+} {1 {database is locked} {}}
+do_test lock-4.3 {
+ proc callback {count} {
+ lappend ::callback_value $count
+ if {$count>4} break
+ }
+ db2 busy callback
+ set rc [catch {db2 eval {UPDATE t1 SET a=0}} msg]
+ lappend rc $msg $::callback_value
+} {1 {database is locked} {0 1 2 3 4 5}}
+execsql {ROLLBACK}
+
+# When one thread is writing, other threads cannot read. Except if the
+# writing thread is writing to its temporary tables, the other threads
+# can still read. -> Not so in 3.0. One thread can read while another
+# holds a RESERVED lock.
+#
+proc tx_exec {sql} {
+ db2 eval $sql
+}
+do_test lock-5.1 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {2 1}
+do_test lock-5.2 {
+ db function tx_exec tx_exec
+ catchsql {
+ INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1');
+ }
+} {0 {}}
+
+ifcapable tempdb {
+ do_test lock-5.3 {
+ execsql {
+ CREATE TEMP TABLE t3(x);
+ SELECT * FROM t3;
+ }
+ } {}
+ do_test lock-5.4 {
+ catchsql {
+ INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1');
+ }
+ } {0 {}}
+ do_test lock-5.5 {
+ execsql {
+ SELECT * FROM t3;
+ }
+ } {8}
+ do_test lock-5.6 {
+ catchsql {
+ UPDATE t1 SET a=tx_exec('SELECT x FROM t2');
+ }
+ } {0 {}}
+ do_test lock-5.7 {
+ execsql {
+ SELECT * FROM t1;
+ }
+ } {9 1 9 8}
+ do_test lock-5.8 {
+ catchsql {
+ UPDATE t3 SET x=tx_exec('SELECT x FROM t2');
+ }
+ } {0 {}}
+ do_test lock-5.9 {
+ execsql {
+ SELECT * FROM t3;
+ }
+ } {9}
+}
+
+do_test lock-999.1 {
+ rename db2 {}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/lock2.test b/third_party/sqlite/test/lock2.test
new file mode 100755
index 0000000..bfca191
--- /dev/null
+++ b/third_party/sqlite/test/lock2.test
@@ -0,0 +1,169 @@
+# 2001 September 15
+#
+# 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 database locks between competing processes.
+#
+# $Id: lock2.test,v 1.9 2007/12/13 21:54:11 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Launch another testfixture process to be controlled by this one. A
+# channel name is returned that may be passed as the first argument to proc
+# 'testfixture' to execute a command. The child testfixture process is shut
+# down by closing the channel.
+proc launch_testfixture {} {
+ set prg [info nameofexec]
+ if {$prg eq ""} {
+ set prg [file join . testfixture]
+ }
+ set chan [open "|$prg tf_main.tcl" r+]
+ fconfigure $chan -buffering line
+ return $chan
+}
+
+# Execute a command in a child testfixture process, connected by two-way
+# channel $chan. Return the result of the command, or an error message.
+proc testfixture {chan cmd} {
+ puts $chan $cmd
+ puts $chan OVER
+ set r ""
+ while { 1 } {
+ set line [gets $chan]
+ if { $line == "OVER" } {
+ return $r
+ }
+ append r $line
+ }
+}
+
+# Write the main loop for the child testfixture processes into file
+# tf_main.tcl. The parent (this script) interacts with the child processes
+# via a two way pipe. The parent writes a script to the stdin of the child
+# process, followed by the word "OVER" on a line of its own. The child
+# process evaluates the script and writes the results to stdout, followed
+# by an "OVER" of its own.
+set f [open tf_main.tcl w]
+puts $f {
+ set l [open log w]
+ set script ""
+ while {![eof stdin]} {
+ flush stdout
+ set line [gets stdin]
+ puts $l "READ $line"
+ if { $line == "OVER" } {
+ catch {eval $script} result
+ puts $result
+ puts $l "WRITE $result"
+ puts OVER
+ puts $l "WRITE OVER"
+ flush stdout
+ set script ""
+ } else {
+ append script $line
+ append script " ; "
+ }
+ }
+ close $l
+}
+close $f
+
+# Simple locking test case:
+#
+# lock2-1.1: Connect a second process to the database.
+# lock2-1.2: Establish a RESERVED lock with this process.
+# lock2-1.3: Get a SHARED lock with the second process.
+# lock2-1.4: Try for a RESERVED lock with process 2. This fails.
+# lock2-1.5: Try to upgrade the first process to EXCLUSIVE, this fails so
+# it gets PENDING.
+# lock2-1.6: Release the SHARED lock held by the second process.
+# lock2-1.7: Attempt to reaquire a SHARED lock with the second process.
+# this fails due to the PENDING lock.
+# lock2-1.8: Ensure the first process can now upgrade to EXCLUSIVE.
+#
+do_test lock2-1.1 {
+ set ::tf1 [launch_testfixture]
+ testfixture $::tf1 "set sqlite_pending_byte $::sqlite_pending_byte"
+ testfixture $::tf1 {
+ sqlite3 db test.db -key xyzzy
+ db eval {select * from sqlite_master}
+ }
+} {}
+do_test lock2-1.1.1 {
+ execsql {pragma lock_status}
+} {main unlocked temp closed}
+sqlite3_soft_heap_limit 0
+do_test lock2-1.2 {
+ execsql {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ }
+} {}
+do_test lock2-1.3 {
+ testfixture $::tf1 {
+ db eval {
+ BEGIN;
+ SELECT * FROM sqlite_master;
+ }
+ }
+} {}
+do_test lock2-1.4 {
+ testfixture $::tf1 {
+ db eval {
+ CREATE TABLE def(d, e, f)
+ }
+ }
+} {database is locked}
+do_test lock2-1.5 {
+ catchsql {
+ COMMIT;
+ }
+} {1 {database is locked}}
+do_test lock2-1.6 {
+ testfixture $::tf1 {
+ db eval {
+ SELECT * FROM sqlite_master;
+ COMMIT;
+ }
+ }
+} {}
+do_test lock2-1.7 {
+ testfixture $::tf1 {
+ db eval {
+ BEGIN;
+ SELECT * FROM sqlite_master;
+ }
+ }
+} {database is locked}
+do_test lock2-1.8 {
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+do_test lock2-1.9 {
+ execsql {
+ SELECT * FROM sqlite_master;
+ }
+} "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}"
+do_test lock2-1.10 {
+ testfixture $::tf1 {
+ db eval {
+ SELECT * FROM sqlite_master;
+ }
+ }
+} "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}"
+
+catch {testfixture $::tf1 {db close}}
+catch {close $::tf1}
+sqlite3_soft_heap_limit $soft_limit
+
+finish_test
diff --git a/third_party/sqlite/test/lock3.test b/third_party/sqlite/test/lock3.test
new file mode 100755
index 0000000..1835c66
--- /dev/null
+++ b/third_party/sqlite/test/lock3.test
@@ -0,0 +1,78 @@
+# 2001 September 15
+#
+# 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 database locks and the operation of the
+# DEFERRED, IMMEDIATE, and EXCLUSIVE keywords as modifiers to the
+# BEGIN command.
+#
+# $Id: lock3.test,v 1.1 2004/10/05 02:41:43 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Establish two connections to the same database. Put some
+# sample data into the database.
+#
+do_test lock3-1.1 {
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ }
+ execsql {
+ SELECT * FROM t1
+ } db2
+} 1
+
+# Get a deferred lock on the database using one connection. The
+# other connection should still be able to write.
+#
+do_test lock3-2.1 {
+ execsql {BEGIN DEFERRED TRANSACTION}
+ execsql {INSERT INTO t1 VALUES(2)} db2
+ execsql {END TRANSACTION}
+ execsql {SELECT * FROM t1}
+} {1 2}
+
+# Get an immediate lock on the database using one connection. The
+# other connection should be able to read the database but not write
+# it.
+#
+do_test lock3-3.1 {
+ execsql {BEGIN IMMEDIATE TRANSACTION}
+ catchsql {SELECT * FROM t1} db2
+} {0 {1 2}}
+do_test lock3-3.2 {
+ catchsql {INSERT INTO t1 VALUES(3)} db2
+} {1 {database is locked}}
+do_test lock3-3.3 {
+ execsql {END TRANSACTION}
+} {}
+
+
+# Get an exclusive lock on the database using one connection. The
+# other connection should be unable to read or write the database.
+#
+do_test lock3-4.1 {
+ execsql {BEGIN EXCLUSIVE TRANSACTION}
+ catchsql {SELECT * FROM t1} db2
+} {1 {database is locked}}
+do_test lock3-4.2 {
+ catchsql {INSERT INTO t1 VALUES(3)} db2
+} {1 {database is locked}}
+do_test lock3-4.3 {
+ execsql {END TRANSACTION}
+} {}
+
+catch {db2 close}
+
+finish_test
diff --git a/third_party/sqlite/test/lock4.test b/third_party/sqlite/test/lock4.test
new file mode 100755
index 0000000..dc8586f
--- /dev/null
+++ b/third_party/sqlite/test/lock4.test
@@ -0,0 +1,115 @@
+# 2007 April 6
+#
+# 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 database locks.
+#
+# $Id: lock4.test,v 1.8 2008/03/14 08:57:42 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Initialize the test.db database so that it is non-empty
+#
+do_test lock4-1.1 {
+ db eval {
+ PRAGMA auto_vacuum=OFF;
+ CREATE TABLE t1(x);
+ }
+ file delete -force test2.db test2.db-journal
+ sqlite3 db2 test2.db
+ db2 eval {
+ PRAGMA auto_vacuum=OFF;
+ CREATE TABLE t2(x)
+ }
+ db2 close
+ list [file size test.db] [file size test2.db]
+} {2048 2048}
+
+# Create a script to drive a separate process that will
+#
+# 1. Create a second database test2.db
+# 2. Get an exclusive lock on test2.db
+# 3. Add an entry to test.db in table t1, waiting as necessary.
+# 4. Commit the change to test2.db.
+#
+# Meanwhile, this process will:
+#
+# A. Get an exclusive lock on test.db
+# B. Attempt to read from test2.db but get an SQLITE_BUSY error.
+# C. Commit the changes to test.db thus alloing the other process
+# to continue.
+#
+do_test lock4-1.2 {
+
+ # Create a script for the second process to run.
+ #
+ set out [open test2-script.tcl w]
+ puts $out "set sqlite_pending_byte [set sqlite_pending_byte]"
+ puts $out {
+ sqlite3 db2 test2.db
+ db2 eval {
+ BEGIN;
+ INSERT INTO t2 VALUES(2);
+ }
+ sqlite3 db test.db
+ db timeout 1000000
+ db eval {
+ INSERT INTO t1 VALUES(2);
+ }
+ db close
+ db2 eval COMMIT
+ exit
+ }
+ close $out
+
+ # Begin a transaction on test.db.
+ db eval {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(1);
+ }
+
+ # Kick off the second process.
+ exec [info nameofexec] ./test2-script.tcl &
+
+ # Wait until the second process has started its transaction on test2.db.
+ while {![file exists test2.db-journal]} {
+ after 10
+ }
+
+ # Try to write to test2.db. We are locked out.
+ sqlite3 db2 test2.db
+ catchsql {
+ INSERT INTO t2 VALUES(1)
+ } db2
+} {1 {database is locked}}
+do_test lock4-1.3 {
+ db eval {
+ COMMIT;
+ }
+ while {[file exists test2.db-journal]} {
+ after 10
+ }
+ # The other process has committed its transaction on test2.db by
+ # deleting the journal file. But it might retain the lock for a
+ # fraction longer
+ #
+ db2 eval {
+ SELECT * FROM t2
+ }
+} {2}
+
+
+do_test lock4-999.1 {
+ rename db2 {}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/lock5.test b/third_party/sqlite/test/lock5.test
new file mode 100755
index 0000000..b4187ca
--- /dev/null
+++ b/third_party/sqlite/test/lock5.test
@@ -0,0 +1,177 @@
+# 2008 June 28
+#
+# 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 database locks.
+#
+# $Id: lock5.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This file is only run if using the unix backend compiled with the
+# SQLITE_ENABLE_LOCKING_STYLE macro.
+db close
+if {[catch {sqlite3 db test.db -vfs unix-none} msg]} {
+puts $msg
+ finish_test
+ return
+}
+db close
+
+do_test lock5-dotfile.1 {
+ sqlite3 db test.db -vfs unix-dotfile
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ }
+} {}
+
+do_test lock5-dotfile.2 {
+ file exists test.db.lock
+} {1}
+
+do_test lock5-dotfile.3 {
+ execsql COMMIT
+ file exists test.db.lock
+} {0}
+
+do_test lock5-dotfile.4 {
+ sqlite3 db2 test.db -vfs unix-dotfile
+ execsql {
+ INSERT INTO t1 VALUES('a', 'b');
+ SELECT * FROM t1;
+ } db2
+} {a b}
+
+do_test lock5-dotfile.5 {
+ execsql {
+ BEGIN;
+ SELECT * FROM t1;
+ } db2
+} {a b}
+
+do_test lock5-dotfile.6 {
+ file exists test.db.lock
+} {1}
+
+do_test lock5-dotfile.7 {
+ catchsql { SELECT * FROM t1; }
+} {1 {database is locked}}
+
+do_test lock5-dotfile.8 {
+ execsql {
+ SELECT * FROM t1;
+ ROLLBACK;
+ } db2
+} {a b}
+
+do_test lock5-dotfile.9 {
+ catchsql { SELECT * FROM t1; }
+} {0 {a b}}
+
+do_test lock5-dotfile.10 {
+ file exists test.db.lock
+} {0}
+
+do_test lock5-dotfile.X {
+ db2 close
+ execsql {BEGIN EXCLUSIVE}
+ db close
+ file exists test.db.lock
+} {0}
+
+#####################################################################
+
+file delete -force test.db
+
+do_test lock5-flock.1 {
+ sqlite3 db test.db -vfs unix-flock
+ execsql {
+ CREATE TABLE t1(a, b);
+ BEGIN;
+ INSERT INTO t1 VALUES(1, 2);
+ }
+} {}
+
+# Make sure we are not accidentally using the dotfile locking scheme.
+do_test lock5-flock.2 {
+ file exists test.db.lock
+} {0}
+
+do_test lock5-flock.3 {
+ sqlite3 db2 test.db -vfs unix-flock
+ catchsql { SELECT * FROM t1 } db2
+} {1 {database is locked}}
+
+do_test lock5-flock.4 {
+ execsql COMMIT
+ catchsql { SELECT * FROM t1 } db2
+} {0 {1 2}}
+
+do_test lock5-flock.5 {
+ execsql BEGIN
+ catchsql { SELECT * FROM t1 } db2
+} {0 {1 2}}
+
+do_test lock5-flock.6 {
+ execsql {SELECT * FROM t1}
+ catchsql { SELECT * FROM t1 } db2
+} {1 {database is locked}}
+
+do_test lock5-flock.7 {
+ db close
+ catchsql { SELECT * FROM t1 } db2
+} {0 {1 2}}
+
+do_test lock5-flock.8 {
+ db2 close
+} {}
+
+#####################################################################
+
+do_test lock5-none.1 {
+ sqlite3 db test.db -vfs unix-none
+ sqlite3 db2 test.db -vfs unix-none
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(3, 4);
+ }
+} {}
+do_test lock5-none.2 {
+ execsql { SELECT * FROM t1 }
+} {1 2 3 4}
+do_test lock5-flock.3 {
+ execsql { SELECT * FROM t1 } db2
+} {1 2}
+do_test lock5-none.4 {
+ execsql {
+ BEGIN;
+ SELECT * FROM t1;
+ } db2
+} {1 2}
+do_test lock5-none.5 {
+ execsql COMMIT
+ execsql {SELECT * FROM t1} db2
+} {1 2}
+
+ifcapable memorymanage {
+ do_test lock5-none.6 {
+ sqlite3_release_memory 1000000
+ execsql {SELECT * FROM t1} db2
+ } {1 2 3 4}
+}
+
+do_test lock5-flock.X {
+ db close
+ db2 close
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/lookaside.test b/third_party/sqlite/test/lookaside.test
new file mode 100755
index 0000000..5b9077e
--- /dev/null
+++ b/third_party/sqlite/test/lookaside.test
@@ -0,0 +1,97 @@
+# 2008 August 01
+#
+# 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.
+#
+#***********************************************************************
+#
+# Tests for the lookaside memory allocator.
+#
+# $Id: lookaside.test,v 1.4 2008/08/05 17:53:24 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+catch {db close}
+sqlite3_shutdown
+sqlite3_config_pagecache 0 0
+sqlite3_config_scratch 0 0
+sqlite3_initialize
+sqlite3 db test.db
+
+# Make sure sqlite3_db_config() and sqlite3_db_status are working.
+#
+do_test lookaside-1.1 {
+ catch {sqlite3_config_error db}
+} {0}
+do_test lookaside-1.2 {
+ sqlite3_db_config_lookaside db 1 20 20
+} {0}
+do_test lookaside-1.3 {
+ sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0
+} {0 0 0}
+do_test lookaside-1.4 {
+ db eval {CREATE TABLE t1(x);}
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
+ expr {$x==0 && $y<$z && $z==20}
+} {1}
+do_test lookaside-1.5 {
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break
+ expr {$x==0 && $y<$z && $z==20}
+} {1}
+do_test lookaside-1.6 {
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
+ expr {$x==0 && $y==$z && $y<20}
+} {1}
+do_test lookaside-1.7 {
+ db cache flush
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
+ expr {$x==0 && $y==0 && $z<20}
+} {1}
+do_test lookaside-1.8 {
+ db cache flush
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break
+ expr {$x==0 && $y==0 && $z<20}
+} {1}
+do_test lookaside-1.9 {
+ db cache flush
+ sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0
+} {0 0 0}
+
+do_test lookaside-2.1 {
+ sqlite3_db_config_lookaside db 0 100 1000
+} {0}
+do_test lookaside-2.2 {
+ db eval {CREATE TABLE t2(x);}
+ foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
+ expr {$x==0 && $y<$z && $z>10 && $z<100}
+} {1}
+do_test lookaside-2.3 {
+ sqlite3_db_config_lookaside db 0 50 50
+} {5} ;# SQLITE_BUSY
+do_test lookaside-2.4 {
+ db cache flush
+ sqlite3_db_config_lookaside db 0 50 50
+} {0} ;# SQLITE_OK
+
+# sqlite3_db_status() with an invalid verb returns an error.
+#
+do_test lookaside-3.1 {
+ sqlite3_db_status db 99999 0
+} {1 0 0}
+
+# Test that an invalid verb on sqlite3_config() is detected and
+# reported as an error.
+#
+do_test lookaside-4.1 {
+ db close
+ sqlite3_shutdown
+ catch sqlite3_config_error
+} {0}
+sqlite3_initialize
+
+finish_test
diff --git a/third_party/sqlite/test/main.test b/third_party/sqlite/test/main.test
new file mode 100755
index 0000000..5c851b5
--- /dev/null
+++ b/third_party/sqlite/test/main.test
@@ -0,0 +1,486 @@
+# 2001 September 15
+#
+# 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 file is exercising the code in main.c.
+#
+# $Id: main.test,v 1.29 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only do the next group of tests if the sqlite3_complete API is available
+#
+ifcapable {complete} {
+
+# Tests of the sqlite_complete() function.
+#
+do_test main-1.1 {
+ db complete {This is a test}
+} {0}
+do_test main-1.2 {
+ db complete {
+ }
+} {1}
+do_test main-1.3 {
+ db complete {
+ -- a comment ;
+ }
+} {1}
+do_test main-1.4 {
+ db complete {
+ -- a comment ;
+ ;
+ }
+} {1}
+do_test main-1.5 {
+ db complete {DROP TABLE 'xyz;}
+} {0}
+do_test main-1.6 {
+ db complete {DROP TABLE 'xyz';}
+} {1}
+do_test main-1.7 {
+ db complete {DROP TABLE "xyz;}
+} {0}
+do_test main-1.8 {
+ db complete {DROP TABLE "xyz';}
+} {0}
+do_test main-1.9 {
+ db complete {DROP TABLE "xyz";}
+} {1}
+do_test main-1.10 {
+ db complete {DROP TABLE xyz; hi}
+} {0}
+do_test main-1.11 {
+ db complete {DROP TABLE xyz; }
+} {1}
+do_test main-1.12 {
+ db complete {DROP TABLE xyz; -- hi }
+} {1}
+do_test main-1.13 {
+ db complete {DROP TABLE xyz; -- hi
+ }
+} {1}
+do_test main-1.14 {
+ db complete {SELECT a-b FROM t1; }
+} {1}
+do_test main-1.15 {
+ db complete {SELECT a/e FROM t1 }
+} {0}
+do_test main-1.16 {
+ db complete {
+ CREATE TABLE abc(x,y);
+ }
+} {1}
+ifcapable {trigger} {
+ do_test main-1.17 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr;
+ }
+ } {0}
+ do_test main-1.18 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; END;
+ }
+ } {1}
+ do_test main-1.19 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE abc BEGIN
+ UPDATE pqr;
+ unknown command;
+ }
+ } {0}
+ do_test main-1.20 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE pqr;
+ }
+ } {0}
+ do_test main-1.21 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE end BEGIN
+ SELECT a, b FROM end;
+ }
+ } {0}
+ do_test main-1.22 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE end BEGIN
+ SELECT a, b FROM end;
+ END;
+ }
+ } {1}
+ do_test main-1.23 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE end BEGIN
+ SELECT a, b FROM end;
+ END;
+ SELECT a, b FROM end;
+ }
+ } {1}
+ do_test main-1.24 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE [;end;] BEGIN
+ UPDATE pqr;
+ }
+ } {0}
+ do_test main-1.25 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE cantor SET a=[;end;];;;
+ }
+ } {0}
+ do_test main-1.26 {
+ db complete {
+ CREATE -- a comment
+ TRIGGER exy AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {0}
+ do_test main-1.27.1 {
+ db complete {
+ CREATE -- a comment
+ TRIGGERX tangentxx AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {1}
+ do_test main-1.27.2 {
+ db complete {
+ CREATE/**/TRIGGER tiger00 AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {0}
+ ifcapable {explain} {
+ do_test main-1.27.3 {
+ db complete {
+ /* */ EXPLAIN -- A comment
+ CREATE/**/TRIGGER ezxyz12 AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {0}
+ }
+ do_test main-1.27.4 {
+ db complete {
+ BOGUS token
+ CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {1}
+ ifcapable {explain} {
+ do_test main-1.27.5 {
+ db complete {
+ EXPLAIN
+ CREATE TEMP TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {0}
+ }
+ do_test main-1.28 {
+ db complete {
+ CREATE TEMPORARY TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ }
+ } {0}
+ do_test main-1.29 {
+ db complete {
+ CREATE TRIGGER xyz AFTER DELETE backend BEGIN
+ UPDATE pqr SET a=5;
+ EXPLAIN select * from xyz;
+ }
+ } {0}
+
+} ;# end ifcapable {complete}
+
+}
+do_test main-1.30 {
+ db complete {
+ CREATE TABLE /* In comment ; */
+ }
+} {0}
+do_test main-1.31 {
+ db complete {
+ CREATE TABLE /* In comment ; */ hi;
+ }
+} {1}
+do_test main-1.31 {
+ db complete {
+ CREATE TABLE /* In comment ; */;
+ }
+} {1}
+do_test main-1.32 {
+ db complete {
+ stuff;
+ /*
+ CREATE TABLE
+ multiple lines
+ of text
+ */
+ }
+} {1}
+do_test main-1.33 {
+ db complete {
+ /*
+ CREATE TABLE
+ multiple lines
+ of text;
+ }
+} {0}
+do_test main-1.34 {
+ db complete {
+ /*
+ CREATE TABLE
+ multiple lines "*/
+ of text;
+ }
+} {1}
+do_test main-1.35 {
+ db complete {hi /**/ there;}
+} {1}
+do_test main-1.36 {
+ db complete {hi there/***/;}
+} {1}
+do_test main-1.37 {
+ db complete {hi there/**}
+} {0}
+do_test main-1.38 {
+ db complete {hi [there}
+} {0}
+
+ifcapable {trigger} {
+ # Characters less than \040 can never be part of an identifier.
+ # Characters greater than \u177 are always identifier characters.
+ do_test main-1.100 {
+ db complete "create \037\036\035\034trigger\001\002;"
+ } {1}
+ do_test main-1.101 {
+ db complete "create trigger\200;"
+ } {1}
+ do_test main-1.102 {
+ db complete "create \200trigger;"
+ } {1}
+}
+
+
+# Try to open a database with a corrupt database file.
+#
+do_test main-2.0 {
+ catch {db close}
+ file delete -force test.db
+ set fd [open test.db w]
+ puts $fd hi!
+ close $fd
+ set v [catch {sqlite3 db test.db} msg]
+ if {$v} {lappend v $msg} {lappend v {}}
+} {0 {}}
+
+# Here are some tests for tokenize.c.
+#
+do_test main-3.1 {
+ catch {db close}
+ foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+ file delete -force testdb
+ sqlite3 db testdb
+ set v [catch {execsql {SELECT * from T1 where x!!5}} msg]
+ lappend v $msg
+} {1 {unrecognized token: "!!"}}
+do_test main-3.2 {
+ catch {db close}
+ foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+ file delete -force testdb
+ sqlite3 db testdb
+ set v [catch {execsql {SELECT * from T1 where ^x}} msg]
+ lappend v $msg
+} {1 {unrecognized token: "^"}}
+do_test main-3.2.2 {
+ catchsql {select 'abc}
+} {1 {unrecognized token: "'abc"}}
+do_test main-3.2.3 {
+ catchsql {select "abc}
+} {1 {unrecognized token: ""abc"}}
+do_test main-3.2.4 {
+ catchsql {select [abc}
+} {1 {unrecognized token: "[abc"}}
+do_test main-3.2.5 {
+ catchsql {select x'4869}
+} {1 {unrecognized token: "x'4869"}}
+do_test main-3.2.6 {
+ catchsql {select x'4869'}
+} {0 Hi}
+do_test main-3.2.7 {
+ catchsql {select x'48695'}
+} {1 {unrecognized token: "x'48695'"}}
+do_test main-3.2.8 {
+ catchsql {select x'486x'}
+} {1 {unrecognized token: "x'486x'"}}
+do_test main-3.2.9 {
+ catchsql {select $abc(}
+} {1 {unrecognized token: "$abc("}}
+do_test main-3.2.10 {
+ catchsql {select $abc(x}
+} {1 {unrecognized token: "$abc(x"}}
+set xyz 123
+do_test main-3.2.11 {
+ catchsql {select $::xyz}
+} {0 123}
+namespace eval ::testnamespace {
+ variable xyz 321
+}
+do_test main-3.2.12 {
+ catchsql {select $testnamespace::xyz}
+} {0 321}
+do_test main-3.2.13 {
+ catchsql {select $(abc)}
+} {1 {unrecognized token: "$"}}
+do_test main-3.2.14 {
+ set hi\u1234x 987
+ db eval "select \$hi\u1234x"
+} {987}
+do_test main-3.2.15 {
+ catchsql "select 456\u1234"
+} [list 1 "unrecognized token: \"456\u1234\""]
+do_test main-3.2.16 {
+ catchsql {select cast(3.14e+4 AS integer)}
+} {0 31400}
+do_test main-3.2.17 {
+ catchsql {select cast(3.14e+04 AS integer)}
+} {0 31400}
+do_test main-3.2.18 {
+ catchsql {select cast(3.14e+004 AS integer)}
+} {0 31400}
+do_test main-3.2.19 {
+ catchsql {select cast(3.14e4 AS integer)}
+} {0 31400}
+do_test main-3.2.20 {
+ catchsql {select cast(3.14e04 AS integer)}
+} {0 31400}
+do_test main-3.2.21 {
+ catchsql {select cast(3.14e004 AS integer)}
+} {0 31400}
+do_test main-3.2.16 {
+ catchsql {select cast(3.14E+4 AS integer)}
+} {0 31400}
+do_test main-3.2.17 {
+ catchsql {select cast(3.14E+04 AS integer)}
+} {0 31400}
+do_test main-3.2.18 {
+ catchsql {select cast(3.14E+004 AS integer)}
+} {0 31400}
+do_test main-3.2.19 {
+ catchsql {select cast(3.14E4 AS integer)}
+} {0 31400}
+do_test main-3.2.20 {
+ catchsql {select cast(3.14E04 AS integer)}
+} {0 31400}
+do_test main-3.2.21 {
+ catchsql {select cast(3.14E004 AS integer)}
+} {0 31400}
+do_test main-3.2.22 {
+ catchsql {select cast(3.14e-4 * 1e8 AS integer)}
+} {0 31400}
+do_test main-3.2.23 {
+ catchsql {select cast(3.14E-04 * 1E08 AS integer)}
+} {0 31400}
+do_test main-3.2.24 {
+ catchsql {select cast(3.14e-004 * 01.0E+8 AS integer)}
+} {0 31400}
+do_test main-3.2.25 {
+ catchsql {select 123/*abc}
+} {0 123}
+do_test main-3.2.26 {
+ catchsql {select 123/***abc}
+} {0 123}
+do_test main-3.2.27 {
+ catchsql {select 123/*/*2}
+} {0 123}
+do_test main-3.2.28 {
+ catchsql {select 123/**/*2}
+} {0 246}
+do_test main-3.2.29 {
+ catchsql {select 123/}
+} {1 {near "/": syntax error}}
+do_test main-3.2.30 {
+ catchsql {select 123--5}
+} {0 123}
+
+
+do_test main-3.3 {
+ catch {db close}
+ foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+ file delete -force testdb
+ sqlite3 db testdb
+ execsql {
+ create table T1(X REAL); /* C-style comments allowed */
+ insert into T1 values(0.5);
+ insert into T1 values(0.5e2);
+ insert into T1 values(0.5e-002);
+ insert into T1 values(5e-002);
+ insert into T1 values(-5.0e-2);
+ insert into T1 values(-5.1e-2);
+ insert into T1 values(0.5e2);
+ insert into T1 values(0.5E+02);
+ insert into T1 values(5E+02);
+ insert into T1 values(5.0E+03);
+ select x*10 from T1 order by x*5;
+ }
+} {-0.51 -0.5 0.05 0.5 5.0 500.0 500.0 500.0 5000.0 50000.0}
+do_test main-3.4 {
+ set v [catch {execsql {create bogus}} msg]
+ lappend v $msg
+} {1 {near "bogus": syntax error}}
+do_test main-3.5 {
+ set v [catch {execsql {create}} msg]
+ lappend v $msg
+} {1 {near "create": syntax error}}
+do_test main-3.6 {
+ catchsql {SELECT 'abc' + #9}
+} {1 {near "#9": syntax error}}
+
+# The following test-case tests the linked list code used to manage
+# sqlite3_vfs structures.
+if {$::tcl_platform(platform)=="unix"} {
+ ifcapable threadsafe {
+ do_test main-4.1 {
+ sqlite3_crash_enable 1
+ sqlite3_crash_enable 0
+
+ sqlite3async_enable 1
+ sqlite3async_enable 0
+
+ sqlite3_crash_enable 1
+ sqlite3async_enable 1
+ sqlite3_crash_enable 0
+ sqlite3async_enable 0
+
+ sqlite3_crash_enable 1
+ sqlite3async_enable 1
+ sqlite3async_enable 0
+ sqlite3_crash_enable 0
+
+ sqlite3async_enable 1
+ sqlite3_crash_enable 1
+ sqlite3_crash_enable 0
+ sqlite3async_enable 0
+
+ sqlite3async_enable 1
+ sqlite3_crash_enable 1
+ sqlite3async_enable 0
+ sqlite3_crash_enable 0
+ } {}
+ do_test main-4.2 {
+ set rc [catch {sqlite3 db test.db -vfs crash} msg]
+ list $rc $msg
+ } {1 {no such vfs: crash}}
+ do_test main-4.3 {
+ set rc [catch {sqlite3 db test.db -vfs async} msg]
+ list $rc $msg
+ } {1 {no such vfs: async}}
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/malloc.test b/third_party/sqlite/test/malloc.test
new file mode 100755
index 0000000..d902a50
--- /dev/null
+++ b/third_party/sqlite/test/malloc.test
@@ -0,0 +1,620 @@
+# 2001 September 15
+#
+# 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 attempts to check the behavior of the SQLite library in
+# an out-of-memory situation. When compiled with -DSQLITE_DEBUG=1,
+# the SQLite library accepts a special command (sqlite3_memdebug_fail N C)
+# which causes the N-th malloc to fail. This special feature is used
+# to see what happens in the library if a malloc were to really fail
+# due to an out-of-memory situation.
+#
+# $Id: malloc.test,v 1.63 2008/07/07 14:56:57 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Only run these tests if memory debugging is turned on.
+#
+source $testdir/malloc_common.tcl
+if {!$MEMDEBUG} {
+ puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+# Do a couple of memory dumps just to exercise the memory dump logic
+# that that we can say that we have.
+#
+puts stderr "This is a test. Ignore the error that follows:"
+sqlite3_memdebug_dump $testdir
+puts "Memory dump to file memdump.txt..."
+sqlite3_memdebug_dump memdump.txt
+
+ifcapable bloblit&&subquery {
+ do_malloc_test 1 -tclprep {
+ db close
+ } -tclbody {
+ if {[catch {sqlite3 db test.db}]} {
+ error "out of memory"
+ }
+ sqlite3_extended_result_codes db 1
+ } -sqlbody {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(
+ a int, b float, c double, d text, e varchar(20),
+ primary key(a,b,c)
+ );
+ CREATE INDEX i1 ON t1(a,b);
+ INSERT INTO t1 VALUES(1,2.3,4.5,'hi',x'746865726500');
+ INSERT INTO t1 VALUES(6,7.0,0.8,'hello','out yonder');
+ SELECT * FROM t1;
+ SELECT avg(b) FROM t1 GROUP BY a HAVING b>20.0;
+ DELETE FROM t1 WHERE a IN (SELECT min(a) FROM t1);
+ SELECT count(*), group_concat(e) FROM t1;
+ SELECT b FROM t1 ORDER BY 1 COLLATE nocase;
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-1.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+ifcapable subquery {
+ do_malloc_test 2 -sqlbody {
+ CREATE TABLE t1(a int, b int default 'abc', c int default 1);
+ CREATE INDEX i1 ON t1(a,b);
+ INSERT INTO t1 VALUES(1,1,'99 abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 VALUES(2,4,'98 abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 VALUES(3,9,'97 abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 VALUES(4,16,'96 abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 VALUES(5,25,'95 abcdefghijklmnopqrstuvwxyz');
+ INSERT INTO t1 VALUES(6,36,'94 abcdefghijklmnopqrstuvwxyz');
+ SELECT 'stuff', count(*) as 'other stuff', max(a+10) FROM t1;
+ UPDATE t1 SET b=b||b||b||b;
+ UPDATE t1 SET b=a WHERE a in (10,12,22);
+ INSERT INTO t1(c,b,a) VALUES(20,10,5);
+ INSERT INTO t1 SELECT * FROM t1
+ WHERE a IN (SELECT a FROM t1 WHERE a<10);
+ DELETE FROM t1 WHERE a>=10;
+ DROP INDEX i1;
+ DELETE FROM t1;
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-2.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+do_malloc_test 3 -sqlbody {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a int, b int, c int);
+ CREATE INDEX i1 ON t1(a,b);
+ INSERT INTO t1 VALUES(1,1,99);
+ INSERT INTO t1 VALUES(2,4,98);
+ INSERT INTO t1 VALUES(3,9,97);
+ INSERT INTO t1 VALUES(4,16,96);
+ INSERT INTO t1 VALUES(5,25,95);
+ INSERT INTO t1 VALUES(6,36,94);
+ INSERT INTO t1(c,b,a) VALUES(20,10,5);
+ DELETE FROM t1 WHERE a>=10;
+ DROP INDEX i1;
+ DELETE FROM t1;
+ ROLLBACK;
+}
+
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-3.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+ifcapable subquery {
+ do_malloc_test 4 -sqlbody {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a int, b int, c int);
+ CREATE INDEX i1 ON t1(a,b);
+ INSERT INTO t1 VALUES(1,1,99);
+ INSERT INTO t1 VALUES(2,4,98);
+ INSERT INTO t1 VALUES(3,9,97);
+ INSERT INTO t1 VALUES(4,16,96);
+ INSERT INTO t1 VALUES(5,25,95);
+ INSERT INTO t1 VALUES(6,36,94);
+ UPDATE t1 SET b=a WHERE a in (10,12,22);
+ INSERT INTO t1 SELECT * FROM t1
+ WHERE a IN (SELECT a FROM t1 WHERE a<10);
+ DROP INDEX i1;
+ DELETE FROM t1;
+ COMMIT;
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-4.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+ifcapable trigger {
+ do_malloc_test 5 -sqlbody {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a,b);
+ CREATE TABLE t2(x,y);
+ CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.a = 2 BEGIN
+ INSERT INTO t2(x,y) VALUES(new.rowid,1);
+ INSERT INTO t2(x,y) SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t2;
+ UPDATE t2 SET y=y+1 WHERE x=new.rowid;
+ SELECT 123;
+ DELETE FROM t2 WHERE x=new.rowid;
+ END;
+ INSERT INTO t1(a,b) VALUES(2,3);
+ COMMIT;
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-5.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+ifcapable vacuum {
+ do_malloc_test 6 -sqlprep {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ INSERT INTO t1 SELECT a*2 FROM t1;
+ DELETE FROM t1 where rowid%5 = 0;
+ COMMIT;
+ } -sqlbody {
+ VACUUM;
+ }
+}
+
+do_malloc_test 7 -sqlprep {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t1 VALUES(5, 6);
+ INSERT INTO t1 VALUES(7, randstr(1200,1200));
+} -sqlbody {
+ SELECT min(a) FROM t1 WHERE a<6 GROUP BY b;
+ SELECT a FROM t1 WHERE a<6 ORDER BY a;
+ SELECT b FROM t1 WHERE a>6;
+}
+
+# This block is designed to test that some malloc failures that may
+# occur in vdbeapi.c. Specifically, if a malloc failure that occurs
+# when converting UTF-16 text to integers and real numbers is handled
+# correctly.
+#
+# This is done by retrieving a string from the database engine and
+# manipulating it using the sqlite3_column_*** APIs. This doesn't
+# actually return an error to the user when a malloc() fails.. That
+# could be viewed as a bug.
+#
+# These tests only run if UTF-16 support is compiled in.
+#
+ifcapable utf16 {
+ set ::STMT {}
+ do_malloc_test 8 -tclprep {
+ set sql "SELECT '[string repeat abc 20]', '[string repeat def 20]', ?"
+ set ::STMT [sqlite3_prepare db $sql -1 X]
+ sqlite3_step $::STMT
+ if { $::tcl_platform(byteOrder)=="littleEndian" } {
+ set ::bomstr "\xFF\xFE"
+ } else {
+ set ::bomstr "\xFE\xFF"
+ }
+ append ::bomstr [encoding convertto unicode "123456789_123456789_12345678"]
+ } -tclbody {
+ sqlite3_column_text16 $::STMT 0
+ sqlite3_column_int $::STMT 0
+ sqlite3_column_text16 $::STMT 1
+ sqlite3_column_double $::STMT 1
+ set rc [sqlite3_reset $::STMT]
+ if {$rc eq "SQLITE_NOMEM"} {error "out of memory"}
+ sqlite3_bind_text16 $::STMT 1 $::bomstr 60
+ #catch {sqlite3_finalize $::STMT}
+ #if {[lindex [sqlite_malloc_stat] 2]<=0} {
+ # error "out of memory"
+ #}
+ } -cleanup {
+ if {$::STMT!=""} {
+ sqlite3_finalize $::STMT
+ set ::STMT {}
+ }
+ }
+}
+
+# This block tests that malloc() failures that occur whilst commiting
+# a multi-file transaction are handled correctly.
+#
+do_malloc_test 9 -sqlprep {
+ ATTACH 'test2.db' as test2;
+ CREATE TABLE abc1(a, b, c);
+ CREATE TABLE test2.abc2(a, b, c);
+} -sqlbody {
+ BEGIN;
+ INSERT INTO abc1 VALUES(1, 2, 3);
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ COMMIT;
+}
+
+# This block tests malloc() failures that occur while opening a
+# connection to a database.
+do_malloc_test 10 -tclprep {
+ catch {db2 close}
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ sqlite3_extended_result_codes db 1
+ db eval {CREATE TABLE abc(a, b, c)}
+} -tclbody {
+ db close
+ sqlite3 db2 test.db
+ sqlite3_extended_result_codes db2 1
+ db2 eval {SELECT * FROM sqlite_master}
+ db2 close
+}
+
+# This block tests malloc() failures that occur within calls to
+# sqlite3_create_function().
+do_malloc_test 11 -tclbody {
+ set rc [sqlite3_create_function db]
+ if {[string match $rc SQLITE_OK]} {
+ set rc [sqlite3_create_aggregate db]
+ }
+ if {[string match $rc SQLITE_NOMEM]} {
+ error "out of memory"
+ }
+}
+
+do_malloc_test 12 -tclbody {
+ set sql16 [encoding convertto unicode "SELECT * FROM sqlite_master"]
+ append sql16 "\00\00"
+ set ::STMT [sqlite3_prepare16 db $sql16 -1 DUMMY]
+ sqlite3_finalize $::STMT
+}
+
+# Test malloc errors when replaying two hot journals from a 2-file
+# transaction.
+ifcapable crashtest&&attach {
+ do_malloc_test 13 -tclprep {
+ set rc [crashsql -delay 1 -file test2.db {
+ ATTACH 'test2.db' as aux;
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE aux.t2(a, b, c);
+ CREATE TABLE t1(a, b, c);
+ COMMIT;
+ }]
+ if {$rc!="1 {child process exited abnormally}"} {
+ error "Wrong error message: $rc"
+ }
+ } -tclbody {
+ db eval {ATTACH 'test2.db' as aux;}
+ set rc [catch {db eval {
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }} err]
+ if {$rc && $err!="no such table: t1"} {
+ error $err
+ }
+ }
+}
+
+if {$tcl_platform(platform)!="windows"} {
+ do_malloc_test 14 -tclprep {
+ catch {db close}
+ sqlite3 db2 test2.db
+ sqlite3_extended_result_codes db2 1
+ db2 eval {
+ PRAGMA synchronous = 0;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ BEGIN;
+ INSERT INTO t1 VALUES(3, 4);
+ }
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+ db2 close
+ } -tclbody {
+ sqlite3 db test.db
+ sqlite3_extended_result_codes db 1
+ db eval {
+ SELECT * FROM t1;
+ }
+ }
+}
+
+proc string_compare {a b} {
+ return [string compare $a $b]
+}
+
+# Test for malloc() failures in sqlite3_create_collation() and
+# sqlite3_create_collation16().
+#
+ifcapable utf16 {
+ do_malloc_test 15 -start 4 -tclbody {
+ db collate string_compare string_compare
+ if {[catch {add_test_collate db 1 1 1} msg]} {
+ if {$msg=="SQLITE_NOMEM"} {set msg "out of memory"}
+ error $msg
+ }
+
+ db complete {SELECT "hello """||'world"' [microsoft], * FROM anicetable;}
+ db complete {-- Useful comment}
+
+ execsql {
+ CREATE TABLE t1(a, b COLLATE string_compare);
+ INSERT INTO t1 VALUES(10, 'string');
+ INSERT INTO t1 VALUES(10, 'string2');
+ }
+ }
+}
+
+# Also test sqlite3_complete(). There are (currently) no malloc()
+# calls in this function, but test anyway against future changes.
+#
+do_malloc_test 16 -tclbody {
+ db complete {SELECT "hello """||'world"' [microsoft], * FROM anicetable;}
+ db complete {-- Useful comment}
+ db eval {
+ SELECT * FROM sqlite_master;
+ }
+}
+
+# Test handling of malloc() failures in sqlite3_open16().
+#
+ifcapable utf16 {
+ do_malloc_test 17 -tclbody {
+ set DB2 0
+ set STMT 0
+
+ # open database using sqlite3_open16()
+ set filename [encoding convertto unicode test.db]
+ append filename "\x00\x00"
+ set DB2 [sqlite3_open16 $filename -unused]
+ if {0==$DB2} {
+ error "out of memory"
+ }
+ sqlite3_extended_result_codes $DB2 1
+
+ # Prepare statement
+ set rc [catch {sqlite3_prepare $DB2 {SELECT * FROM sqlite_master} -1 X} msg]
+ if {[sqlite3_errcode $DB2] eq "SQLITE_IOERR+12"} {
+ error "out of memory"
+ }
+ if {$rc} {
+ error [string range $msg 4 end]
+ }
+ set STMT $msg
+
+ # Finalize statement
+ set rc [sqlite3_finalize $STMT]
+ if {$rc!="SQLITE_OK"} {
+ error [sqlite3_errmsg $DB2]
+ }
+ set STMT 0
+
+ # Close database
+ set rc [sqlite3_close $DB2]
+ if {$rc!="SQLITE_OK"} {
+ error [sqlite3_errmsg $DB2]
+ }
+ set DB2 0
+ } -cleanup {
+ if {$STMT!="0"} {
+ sqlite3_finalize $STMT
+ }
+ if {$DB2!="0"} {
+ set rc [sqlite3_close $DB2]
+ }
+ }
+}
+
+# Test handling of malloc() failures in sqlite3_errmsg16().
+#
+ifcapable utf16 {
+ do_malloc_test 18 -tclprep {
+ catch {
+ db eval "SELECT [string repeat longcolumnname 10] FROM sqlite_master"
+ }
+ } -tclbody {
+ set utf16 [sqlite3_errmsg16 [sqlite3_connection_pointer db]]
+ binary scan $utf16 c* bytes
+ if {[llength $bytes]==0} {
+ error "out of memory"
+ }
+ }
+}
+
+# This test is aimed at coverage testing. Specificly, it is supposed to
+# cause a malloc() only used when converting between the two utf-16
+# encodings to fail (i.e. little-endian->big-endian). It only actually
+# hits this malloc() on little-endian hosts.
+#
+set static_string "\x00h\x00e\x00l\x00l\x00o"
+for {set l 0} {$l<10} {incr l} {
+ append static_string $static_string
+}
+append static_string "\x00\x00"
+do_malloc_test 19 -tclprep {
+ execsql {
+ PRAGMA encoding = "UTF16be";
+ CREATE TABLE abc(a, b, c);
+ }
+} -tclbody {
+ unset -nocomplain ::STMT
+ set r [catch {
+ set ::STMT [sqlite3_prepare db {SELECT ?} -1 DUMMY]
+ sqlite3_bind_text16 -static $::STMT 1 $static_string 112
+ } msg]
+ if {$r} {error [string range $msg 4 end]}
+ set msg
+} -cleanup {
+ if {[info exists ::STMT]} {
+ sqlite3_finalize $::STMT
+ }
+}
+unset static_string
+
+# Make sure SQLITE_NOMEM is reported out on an ATTACH failure even
+# when the malloc failure occurs within the nested parse.
+#
+ifcapable attach {
+ do_malloc_test 20 -tclprep {
+ db close
+ file delete -force test2.db test2.db-journal
+ sqlite3 db test2.db
+ sqlite3_extended_result_codes db 1
+ db eval {CREATE TABLE t1(x);}
+ db close
+ } -tclbody {
+ if {[catch {sqlite3 db test.db}]} {
+ error "out of memory"
+ }
+ sqlite3_extended_result_codes db 1
+ } -sqlbody {
+ ATTACH DATABASE 'test2.db' AS t2;
+ SELECT * FROM t1;
+ DETACH DATABASE t2;
+ }
+}
+
+# Test malloc failure whilst installing a foreign key.
+#
+ifcapable foreignkey {
+ do_malloc_test 21 -sqlbody {
+ CREATE TABLE abc(a, b, c, FOREIGN KEY(a) REFERENCES abc(b))
+ }
+}
+
+# Test malloc failure in an sqlite3_prepare_v2() call.
+#
+do_malloc_test 22 -tclbody {
+ set ::STMT ""
+ set r [catch {
+ set ::STMT [
+ sqlite3_prepare_v2 db "SELECT * FROM sqlite_master" -1 DUMMY
+ ]
+ } msg]
+ if {$r} {error [string range $msg 4 end]}
+} -cleanup {
+ if {$::STMT ne ""} {
+ sqlite3_finalize $::STMT
+ set ::STMT ""
+ }
+}
+
+ifcapable {pager_pragmas} {
+ # This tests a special case - that an error that occurs while the pager
+ # is trying to recover from error-state in exclusive-access mode works.
+ #
+ do_malloc_test 23 -tclprep {
+ db eval {
+ PRAGMA cache_size = 10;
+ PRAGMA locking_mode = exclusive;
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ CREATE INDEX abc_i ON abc(a, b, c);
+ INSERT INTO abc
+ VALUES(randstr(100,100), randstr(100,100), randstr(100,100));
+ INSERT INTO abc
+ SELECT randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ COMMIT;
+ }
+
+ # This puts the pager into error state.
+ #
+ db eval BEGIN
+ db eval {UPDATE abc SET a = 0 WHERE oid%2}
+ set ::sqlite_io_error_pending 10
+ catch {db eval {ROLLBACK}} msg
+
+ } -sqlbody {
+ SELECT * FROM abc LIMIT 10;
+ } -cleanup {
+ set e [db eval {PRAGMA integrity_check}]
+ if {$e ne "ok"} {error $e}
+ }
+}
+
+ifcapable compound {
+ do_malloc_test 24 -sqlprep {
+ CREATE TABLE t1(a, b, c)
+ } -sqlbody {
+ SELECT 1 FROM t1 UNION SELECT 2 FROM t1 ORDER BY 1
+ }
+}
+
+ifcapable view&&trigger {
+ do_malloc_test 25 -sqlprep {
+ CREATE TABLE t1(a, b, c);
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ CREATE TRIGGER v1t1 INSTEAD OF DELETE ON v1 BEGIN SELECT 1; END;
+ CREATE TRIGGER v1t2 INSTEAD OF INSERT ON v1 BEGIN SELECT 1; END;
+ CREATE TRIGGER v1t3 INSTEAD OF UPDATE ON v1 BEGIN SELECT 1; END;
+ } -sqlbody {
+ DELETE FROM v1 WHERE a = 1;
+ INSERT INTO v1 VALUES(1, 2, 3);
+ UPDATE v1 SET a = 1 WHERE b = 2;
+ }
+}
+
+do_malloc_test 25 -sqlprep {
+ CREATE TABLE abc(a, b, c);
+ CREATE INDEX i1 ON abc(a, b);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+} -tclbody {
+ # For each UPDATE executed, the cursor used for the SELECT statement
+ # must be "saved". Because the cursor is open on an index, this requires
+ # a malloc() to allocate space to save the index key. This test case is
+ # aimed at testing the response of the library to a failure in that
+ # particular malloc() call.
+ db eval {SELECT a FROM abc ORDER BY a} {
+ db eval {UPDATE abc SET b = b - 1 WHERE a = $a}
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-99.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+puts open-file-count=$sqlite_open_file_count
+finish_test
diff --git a/third_party/sqlite/test/malloc3.test b/third_party/sqlite/test/malloc3.test
new file mode 100755
index 0000000..67b190b
--- /dev/null
+++ b/third_party/sqlite/test/malloc3.test
@@ -0,0 +1,664 @@
+# 2005 November 30
+#
+# 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 contains tests to ensure that the library handles malloc() failures
+# correctly. The emphasis of these tests are the _prepare(), _step() and
+# _finalize() calls.
+#
+# $Id: malloc3.test,v 1.23 2008/05/13 19:41:54 shane Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc3 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+#--------------------------------------------------------------------------
+# NOTES ON RECOVERING FROM A MALLOC FAILURE
+#
+# The tests in this file test the behaviours described in the following
+# paragraphs. These tests test the behaviour of the system when malloc() fails
+# inside of a call to _prepare(), _step(), _finalize() or _reset(). The
+# handling of malloc() failures within ancillary procedures is tested
+# elsewhere.
+#
+# Overview:
+#
+# Executing a statement is done in three stages (prepare, step and finalize). A
+# malloc() failure may occur within any stage. If a memory allocation fails
+# during statement preparation, no statement handle is returned. From the users
+# point of view the system state is as if _prepare() had never been called.
+#
+# If the memory allocation fails during the _step() or _finalize() calls, then
+# the database may be left in one of two states (after finalize() has been
+# called):
+#
+# * As if the neither _step() nor _finalize() had ever been called on
+# the statement handle (i.e. any changes made by the statement are
+# rolled back).
+# * The current transaction may be rolled back. In this case a hot-journal
+# may or may not actually be present in the filesystem.
+#
+# The caller can tell the difference between these two scenarios by invoking
+# _get_autocommit().
+#
+#
+# Handling of sqlite3_reset():
+#
+# If a malloc() fails while executing an sqlite3_reset() call, this is handled
+# in the same way as a failure within _finalize(). The statement handle
+# is not deleted and must be passed to _finalize() for resource deallocation.
+# Attempting to _step() or _reset() the statement after a failed _reset() will
+# always return SQLITE_NOMEM.
+#
+#
+# Other active SQL statements:
+#
+# The effect of a malloc failure on concurrently executing SQL statements,
+# particularly when the statement is executing with READ_UNCOMMITTED set and
+# the malloc() failure mandates statement rollback only. Currently, if
+# transaction rollback is required, all other vdbe's are aborted.
+#
+# Non-transient mallocs in btree.c:
+# * The Btree structure itself
+# * Each BtCursor structure
+#
+# Mallocs in pager.c:
+# readMasterJournal() - Space to read the master journal name
+# pager_delmaster() - Space for the entire master journal file
+#
+# sqlite3pager_open() - The pager structure itself
+# sqlite3_pagerget() - Space for a new page
+# pager_open_journal() - Pager.aInJournal[] bitmap
+# sqlite3pager_write() - For in-memory databases only: history page and
+# statement history page.
+# pager_stmt_begin() - Pager.aInStmt[] bitmap
+#
+# None of the above are a huge problem. The most troublesome failures are the
+# transient malloc() calls in btree.c, which can occur during the tree-balance
+# operation. This means the tree being balanced will be internally inconsistent
+# after the malloc() fails. To avoid the corrupt tree being read by a
+# READ_UNCOMMITTED query, we have to make sure the transaction or statement
+# rollback occurs before sqlite3_step() returns, not during a subsequent
+# sqlite3_finalize().
+#--------------------------------------------------------------------------
+
+#--------------------------------------------------------------------------
+# NOTES ON TEST IMPLEMENTATION
+#
+# The tests in this file are implemented differently from those in other
+# files. Instead, tests are specified using three primitives: SQL, PREP and
+# TEST. Each primitive has a single argument. Primitives are processed in
+# the order they are specified in the file.
+#
+# A TEST primitive specifies a TCL script as its argument. When a TEST
+# directive is encountered the Tcl script is evaluated. Usually, this Tcl
+# script contains one or more calls to [do_test].
+#
+# A PREP primitive specifies an SQL script as its argument. When a PREP
+# directive is encountered the SQL is evaluated using database connection
+# [db].
+#
+# The SQL primitives are where the action happens. An SQL primitive must
+# contain a single, valid SQL statement as its argument. When an SQL
+# primitive is encountered, it is evaluated one or more times to test the
+# behaviour of the system when malloc() fails during preparation or
+# execution of said statement. The Nth time the statement is executed,
+# the Nth malloc is said to fail. The statement is executed until it
+# succeeds, i.e. (M+1) times, where M is the number of mallocs() required
+# to prepare and execute the statement.
+#
+# Each time an SQL statement fails, the driver program (see proc [run_test]
+# below) figures out if a transaction has been automatically rolled back.
+# If not, it executes any TEST block immediately proceeding the SQL
+# statement, then reexecutes the SQL statement with the next value of N.
+#
+# If a transaction has been automatically rolled back, then the driver
+# program executes all the SQL specified as part of SQL or PREP primitives
+# between the current SQL statement and the most recent "BEGIN". Any
+# TEST block immediately proceeding the SQL statement is evaluated, and
+# then the SQL statement reexecuted with the incremented N value.
+#
+# That make any sense? If not, read the code in [run_test] and it might.
+#
+# Extra restriction imposed by the implementation:
+#
+# * If a PREP block starts a transaction, it must finish it.
+# * A PREP block may not close a transaction it did not start.
+#
+#--------------------------------------------------------------------------
+
+
+# These procs are used to build up a "program" in global variable
+# ::run_test_script. At the end of this file, the proc [run_test] is used
+# to execute the program (and all test cases contained therein).
+#
+set ::run_test_script [list]
+proc TEST {id t} {lappend ::run_test_script -test [list $id $t]}
+proc PREP {p} {lappend ::run_test_script -prep [string trim $p]}
+proc DEBUG {s} {lappend ::run_test_script -debug $s}
+
+# SQL --
+#
+# SQL ?-norollback? <sql-text>
+#
+# Add an 'SQL' primitive to the program (see notes above). If the -norollback
+# switch is present, then the statement is not allowed to automatically roll
+# back any active transaction if malloc() fails. It must rollback the statement
+# transaction only.
+#
+proc SQL {a1 {a2 ""}} {
+ # An SQL primitive parameter is a list of two elements, a boolean value
+ # indicating if the statement may cause transaction rollback when malloc()
+ # fails, and the sql statement itself.
+ if {$a2 == ""} {
+ lappend ::run_test_script -sql [list true [string trim $a1]]
+ } else {
+ lappend ::run_test_script -sql [list false [string trim $a2]]
+ }
+}
+
+# TEST_AUTOCOMMIT --
+#
+# A shorthand test to see if a transaction is active or not. The first
+# argument - $id - is the integer number of the test case. The second
+# argument is either 1 or 0, the expected value of the auto-commit flag.
+#
+proc TEST_AUTOCOMMIT {id a} {
+ TEST $id "do_test \$testid { sqlite3_get_autocommit \$::DB } {$a}"
+}
+
+#--------------------------------------------------------------------------
+# Start of test program declaration
+#
+
+
+# Warm body test. A malloc() fails in the middle of a CREATE TABLE statement
+# in a single-statement transaction on an empty database. Not too much can go
+# wrong here.
+#
+TEST 1 {
+ do_test $testid {
+ execsql {SELECT tbl_name FROM sqlite_master;}
+ } {}
+}
+SQL {
+ CREATE TABLE abc(a, b, c);
+}
+TEST 2 {
+ do_test $testid.1 {
+ execsql {SELECT tbl_name FROM sqlite_master;}
+ } {abc}
+}
+
+# Insert a couple of rows into the table. each insert is in its own
+# transaction. test that the table is unpopulated before running the inserts
+# (and hence after each failure of the first insert), and that it has been
+# populated correctly after the final insert succeeds.
+#
+TEST 3 {
+ do_test $testid.2 {
+ execsql {SELECT * FROM abc}
+ } {}
+}
+SQL {INSERT INTO abc VALUES(1, 2, 3);}
+SQL {INSERT INTO abc VALUES(4, 5, 6);}
+SQL {INSERT INTO abc VALUES(7, 8, 9);}
+TEST 4 {
+ do_test $testid {
+ execsql {SELECT * FROM abc}
+ } {1 2 3 4 5 6 7 8 9}
+}
+
+# Test a CREATE INDEX statement. Because the table 'abc' is so small, the index
+# will all fit on a single page, so this doesn't test too much that the CREATE
+# TABLE statement didn't test. A few of the transient malloc()s in btree.c
+# perhaps.
+#
+SQL {CREATE INDEX abc_i ON abc(a, b, c);}
+TEST 4 {
+ do_test $testid {
+ execsql {
+ SELECT * FROM abc ORDER BY a DESC;
+ }
+ } {7 8 9 4 5 6 1 2 3}
+}
+
+# Test a DELETE statement. Also create a trigger and a view, just to make sure
+# these statements don't have any obvious malloc() related bugs in them. Note
+# that the test above will be executed each time the DELETE fails, so we're
+# also testing rollback of a DELETE from a table with an index on it.
+#
+SQL {DELETE FROM abc WHERE a > 2;}
+SQL {CREATE TRIGGER abc_t AFTER INSERT ON abc BEGIN SELECT 'trigger!'; END;}
+SQL {CREATE VIEW abc_v AS SELECT * FROM abc;}
+TEST 5 {
+ do_test $testid {
+ execsql {
+ SELECT name, tbl_name FROM sqlite_master ORDER BY name;
+ SELECT * FROM abc;
+ }
+ } {abc abc abc_i abc abc_t abc abc_v abc_v 1 2 3}
+}
+
+set sql {
+ BEGIN;DELETE FROM abc;
+}
+for {set i 1} {$i < 15} {incr i} {
+ set a $i
+ set b "String value $i"
+ set c [string repeat X $i]
+ append sql "INSERT INTO abc VALUES ($a, '$b', '$c');"
+}
+append sql {COMMIT;}
+PREP $sql
+
+SQL {
+ DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
+}
+TEST 6 {
+ do_test $testid.1 {
+ execsql {SELECT count(*) FROM abc}
+ } {94}
+ do_test $testid.2 {
+ execsql {
+ SELECT min(
+ (oid == a) AND 'String value ' || a == b AND a == length(c)
+ ) FROM abc;
+ }
+ } {1}
+}
+SQL {
+ DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
+}
+TEST 7 {
+ do_test $testid {
+ execsql {SELECT count(*) FROM abc}
+ } {89}
+ do_test $testid {
+ execsql {
+ SELECT min(
+ (oid == a) AND 'String value ' || a == b AND a == length(c)
+ ) FROM abc;
+ }
+ } {1}
+}
+SQL {
+ DELETE FROM abc WHERE oid IN (SELECT oid FROM abc ORDER BY random() LIMIT 5);
+}
+TEST 9 {
+ do_test $testid {
+ execsql {SELECT count(*) FROM abc}
+ } {84}
+ do_test $testid {
+ execsql {
+ SELECT min(
+ (oid == a) AND 'String value ' || a == b AND a == length(c)
+ ) FROM abc;
+ }
+ } {1}
+}
+
+set padding [string repeat X 500]
+PREP [subst {
+ DROP TABLE abc;
+ CREATE TABLE abc(a PRIMARY KEY, padding, b, c);
+ INSERT INTO abc VALUES(0, '$padding', 2, 2);
+ INSERT INTO abc VALUES(3, '$padding', 5, 5);
+ INSERT INTO abc VALUES(6, '$padding', 8, 8);
+}]
+
+TEST 10 {
+ do_test $testid {
+ execsql {SELECT a, b, c FROM abc}
+ } {0 2 2 3 5 5 6 8 8}
+}
+
+SQL {BEGIN;}
+SQL {INSERT INTO abc VALUES(9, 'XXXXX', 11, 12);}
+TEST_AUTOCOMMIT 11 0
+SQL -norollback {UPDATE abc SET a = a + 1, c = c + 1;}
+TEST_AUTOCOMMIT 12 0
+SQL {DELETE FROM abc WHERE a = 10;}
+TEST_AUTOCOMMIT 13 0
+SQL {COMMIT;}
+
+TEST 14 {
+ do_test $testid.1 {
+ sqlite3_get_autocommit $::DB
+ } {1}
+ do_test $testid.2 {
+ execsql {SELECT a, b, c FROM abc}
+ } {1 2 3 4 5 6 7 8 9}
+}
+
+PREP [subst {
+ DROP TABLE abc;
+ CREATE TABLE abc(a, padding, b, c);
+ INSERT INTO abc VALUES(1, '$padding', 2, 3);
+ INSERT INTO abc VALUES(4, '$padding', 5, 6);
+ INSERT INTO abc VALUES(7, '$padding', 8, 9);
+ CREATE INDEX abc_i ON abc(a, padding, b, c);
+}]
+
+TEST 15 {
+ db eval {PRAGMA cache_size = 10}
+}
+
+SQL {BEGIN;}
+SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
+TEST 16 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 2 4 2 7 2}
+}
+SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
+TEST 17 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 4 4 4 7 4}
+}
+SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
+TEST 18 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 8 4 8 7 8}
+}
+SQL -norllbck {INSERT INTO abc (oid, a, padding, b, c) SELECT NULL, * FROM abc}
+TEST 19 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 16 4 16 7 16}
+}
+SQL {COMMIT;}
+TEST 21 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 16 4 16 7 16}
+}
+
+SQL {BEGIN;}
+SQL {DELETE FROM abc WHERE oid %2}
+TEST 22 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 8 4 8 7 8}
+}
+SQL {DELETE FROM abc}
+TEST 23 {
+ do_test $testid {
+ execsql {SELECT * FROM abc}
+ } {}
+}
+SQL {ROLLBACK;}
+TEST 24 {
+ do_test $testid {
+ execsql {SELECT a, count(*) FROM abc GROUP BY a;}
+ } {1 16 4 16 7 16}
+}
+
+# Test some schema modifications inside of a transaction. These should all
+# cause transaction rollback if they fail. Also query a view, to cover a bit
+# more code.
+#
+PREP {DROP VIEW abc_v;}
+TEST 25 {
+ do_test $testid {
+ execsql {
+ SELECT name, tbl_name FROM sqlite_master;
+ }
+ } {abc abc abc_i abc}
+}
+SQL {BEGIN;}
+SQL {CREATE TABLE def(d, e, f);}
+SQL {CREATE TABLE ghi(g, h, i);}
+TEST 26 {
+ do_test $testid {
+ execsql {
+ SELECT name, tbl_name FROM sqlite_master;
+ }
+ } {abc abc abc_i abc def def ghi ghi}
+}
+SQL {CREATE VIEW v1 AS SELECT * FROM def, ghi}
+SQL {CREATE UNIQUE INDEX ghi_i1 ON ghi(g);}
+TEST 27 {
+ do_test $testid {
+ execsql {
+ SELECT name, tbl_name FROM sqlite_master;
+ }
+ } {abc abc abc_i abc def def ghi ghi v1 v1 ghi_i1 ghi}
+}
+SQL {INSERT INTO def VALUES('a', 'b', 'c')}
+SQL {INSERT INTO def VALUES(1, 2, 3)}
+SQL -norollback {INSERT INTO ghi SELECT * FROM def}
+TEST 28 {
+ do_test $testid {
+ execsql {
+ SELECT * FROM def, ghi WHERE d = g;
+ }
+ } {a b c a b c 1 2 3 1 2 3}
+}
+SQL {COMMIT}
+TEST 29 {
+ do_test $testid {
+ execsql {
+ SELECT * FROM v1 WHERE d = g;
+ }
+ } {a b c a b c 1 2 3 1 2 3}
+}
+
+# Test a simple multi-file transaction
+#
+file delete -force test2.db
+ifcapable attach {
+ SQL {ATTACH 'test2.db' AS aux;}
+ SQL {BEGIN}
+ SQL {CREATE TABLE aux.tbl2(x, y, z)}
+ SQL {INSERT INTO tbl2 VALUES(1, 2, 3)}
+ SQL {INSERT INTO def VALUES(4, 5, 6)}
+ TEST 30 {
+ do_test $testid {
+ execsql {
+ SELECT * FROM tbl2, def WHERE d = x;
+ }
+ } {1 2 3 1 2 3}
+ }
+ SQL {COMMIT}
+ TEST 31 {
+ do_test $testid {
+ execsql {
+ SELECT * FROM tbl2, def WHERE d = x;
+ }
+ } {1 2 3 1 2 3}
+ }
+}
+
+# Test what happens when a malloc() fails while there are other active
+# statements. This changes the way sqlite3VdbeHalt() works.
+TEST 32 {
+ if {![info exists ::STMT32]} {
+ set sql "SELECT name FROM sqlite_master"
+ set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY]
+ do_test $testid {
+ sqlite3_step $::STMT32
+ } {SQLITE_ROW}
+ }
+}
+SQL BEGIN
+TEST 33 {
+ do_test $testid {
+ execsql {SELECT * FROM ghi}
+ } {a b c 1 2 3}
+}
+SQL -norollback {
+ -- There is a unique index on ghi(g), so this statement may not cause
+ -- an automatic ROLLBACK. Hence the "-norollback" switch.
+ INSERT INTO ghi SELECT '2'||g, h, i FROM ghi;
+}
+TEST 34 {
+ if {[info exists ::STMT32]} {
+ do_test $testid {
+ sqlite3_finalize $::STMT32
+ } {SQLITE_OK}
+ unset ::STMT32
+ }
+}
+SQL COMMIT
+
+#
+# End of test program declaration
+#--------------------------------------------------------------------------
+
+proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} {
+ if {[llength $arglist] %2} {
+ error "Uneven number of arguments to TEST"
+ }
+
+ for {set i 0} {$i < $pcstart} {incr i} {
+ set k2 [lindex $arglist [expr 2 * $i]]
+ set v2 [lindex $arglist [expr 2 * $i + 1]]
+ set ac [sqlite3_get_autocommit $::DB] ;# Auto-Commit
+ switch -- $k2 {
+ -sql {db eval [lindex $v2 1]}
+ -prep {db eval $v2}
+ }
+ set nac [sqlite3_get_autocommit $::DB] ;# New Auto-Commit
+ if {$ac && !$nac} {set begin_pc $i}
+ }
+
+ db rollback_hook [list incr ::rollback_hook_count]
+
+ set iFail $iFailStart
+ set pc $pcstart
+ while {$pc*2 < [llength $arglist]} {
+
+ # Id of this iteration:
+ set k [lindex $arglist [expr 2 * $pc]]
+ set iterid "pc=$pc.iFail=$iFail$k"
+ set v [lindex $arglist [expr 2 * $pc + 1]]
+
+ switch -- $k {
+
+ -test {
+ foreach {id script} $v {}
+ incr pc
+ }
+
+ -sql {
+ set ::rollback_hook_count 0
+
+ set ac [sqlite3_get_autocommit $::DB] ;# Auto-Commit
+ sqlite3_memdebug_fail $iFail -repeat 0
+ set rc [catch {db eval [lindex $v 1]} msg] ;# True error occurs
+ set nac [sqlite3_get_autocommit $::DB] ;# New Auto-Commit
+
+ if {$rc != 0 && $nac && !$ac} {
+ # Before [db eval] the auto-commit flag was clear. Now it
+ # is set. Since an error occured we assume this was not a
+ # commit - therefore a rollback occured. Check that the
+ # rollback-hook was invoked.
+ do_test malloc3-rollback_hook.$iterid {
+ set ::rollback_hook_count
+ } {1}
+ }
+
+ set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
+ if {$rc == 0} {
+ # Successful execution of sql. The number of failed malloc()
+ # calls should be equal to the number of benign failures.
+ # Otherwise a malloc() failed and the error was not reported.
+ #
+ if {$nFail!=$nBenign} {
+ error "Unreported malloc() failure"
+ }
+
+ if {$ac && !$nac} {
+ # Before the [db eval] the auto-commit flag was set, now it
+ # is clear. We can deduce that a "BEGIN" statement has just
+ # been successfully executed.
+ set begin_pc $pc
+ }
+
+ incr pc
+ set iFail 1
+ integrity_check "malloc3-(integrity).$iterid"
+ } elseif {[regexp {.*out of memory} $msg] || [db errorcode] == 3082} {
+ # Out of memory error, as expected.
+ #
+ integrity_check "malloc3-(integrity).$iterid"
+ incr iFail
+ if {$nac && !$ac} {
+
+ if {![lindex $v 0] && [db errorcode] != 3082} {
+ # error "Statement \"[lindex $v 1]\" caused a rollback"
+ }
+
+ for {set i $begin_pc} {$i < $pc} {incr i} {
+ set k2 [lindex $arglist [expr 2 * $i]]
+ set v2 [lindex $arglist [expr 2 * $i + 1]]
+ set catchupsql ""
+ switch -- $k2 {
+ -sql {set catchupsql [lindex $v2 1]}
+ -prep {set catchupsql $v2}
+ }
+ db eval $catchupsql
+ }
+ }
+ } else {
+ error $msg
+ }
+
+ while {[lindex $arglist [expr 2 * ($pc -1)]] == "-test"} {
+ incr pc -1
+ }
+ }
+
+ -prep {
+ db eval $v
+ incr pc
+ }
+
+ -debug {
+ eval $v
+ incr pc
+ }
+
+ default { error "Unknown switch: $k" }
+ }
+ }
+}
+
+# Turn of the Tcl interface's prepared statement caching facility. Then
+# run the tests with "persistent" malloc failures.
+sqlite3_extended_result_codes db 1
+db cache size 0
+run_test $::run_test_script 1
+
+# Close and reopen the db.
+db close
+file delete -force test.db test.db-journal test2.db test2.db-journal
+sqlite3 db test.db
+sqlite3_extended_result_codes db 1
+set ::DB [sqlite3_connection_pointer db]
+
+# Turn of the Tcl interface's prepared statement caching facility in
+# the new connnection. Then run the tests with "transient" malloc failures.
+db cache size 0
+run_test $::run_test_script 0
+
+sqlite3_memdebug_fail -1
+finish_test
diff --git a/third_party/sqlite/test/malloc4.test b/third_party/sqlite/test/malloc4.test
new file mode 100755
index 0000000..e2a616a
--- /dev/null
+++ b/third_party/sqlite/test/malloc4.test
@@ -0,0 +1,194 @@
+# 2005 November 30
+#
+# 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 contains tests to ensure that the library handles malloc() failures
+# correctly. The emphasis in this file is on sqlite3_column_XXX() APIs.
+#
+# $Id: malloc4.test,v 1.10 2008/02/18 22:24:58 drh Exp $
+
+#---------------------------------------------------------------------------
+# NOTES ON EXPECTED BEHAVIOUR
+#
+# [193] When a memory allocation failure occurs during sqlite3_column_name(),
+# sqlite3_column_name16(), sqlite3_column_decltype(), or
+# sqlite3_column_decltype16() the function shall return NULL.
+#
+#---------------------------------------------------------------------------
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+if {!$MEMDEBUG} {
+ puts "Skipping malloc4 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+ifcapable !utf16 {
+ finish_test
+ return
+}
+
+proc do_stmt_test {id sql} {
+ set ::sql $sql
+ set go 1
+ for {set n 0} {$go} {incr n} {
+ set testid "malloc4-$id.$n"
+
+ # Prepare the statement
+ do_test ${testid}.1 {
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ expr [string length $::STMT] > 0
+ } {1}
+
+ # Set the Nth malloc() to fail.
+ sqlite3_memdebug_fail $n -repeat 0
+
+ # Test malloc failure in the _name(), _name16(), decltype() and
+ # decltype16() APIs. Calls that occur after the malloc() failure should
+ # return NULL. No error is raised though.
+ #
+ # ${testid}.2.1 - Call _name()
+ # ${testid}.2.2 - Call _name16()
+ # ${testid}.2.3 - Call _name()
+ # ${testid}.2.4 - Check that the return values of the above three calls are
+ # consistent with each other and with the simulated
+ # malloc() failures.
+ #
+ # Because the code that implements the _decltype() and _decltype16() APIs
+ # is the same as the _name() and _name16() implementations, we don't worry
+ # about explicitly testing them.
+ #
+ do_test ${testid}.2.1 {
+ set mf1 [expr [sqlite3_memdebug_pending] < 0]
+ set ::name8 [sqlite3_column_name $::STMT 0]
+ set mf2 [expr [sqlite3_memdebug_pending] < 0]
+ expr {$mf1 == $mf2 || $::name8 == ""}
+ } {1}
+ do_test ${testid}.2.2 {
+ set mf1 [expr [sqlite3_memdebug_pending] < 0]
+ set ::name16 [sqlite3_column_name16 $::STMT 0]
+ set ::name16 [encoding convertfrom unicode $::name16]
+ set ::name16 [string range $::name16 0 end-1]
+ set mf2 [expr [sqlite3_memdebug_pending] < 0]
+ expr {$mf1 == $mf2 || $::name16 == ""}
+ } {1}
+ do_test ${testid}.2.3 {
+ set mf1 [expr [sqlite3_memdebug_pending] < 0]
+ set ::name8_2 [sqlite3_column_name $::STMT 0]
+ set mf2 [expr [sqlite3_memdebug_pending] < 0]
+ expr {$mf1 == $mf2 || $::name8_2 == ""}
+ } {1}
+ set ::mallocFailed [expr [sqlite3_memdebug_pending] < 0]
+ do_test ${testid}.2.4 {
+ expr {
+ $::name8 == $::name8_2 && $::name16 == $::name8 && !$::mallocFailed ||
+ $::name8 == $::name8_2 && $::name16 == "" && $::mallocFailed ||
+ $::name8 == $::name16 && $::name8_2 == "" && $::mallocFailed ||
+ $::name8_2 == $::name16 && $::name8 == "" && $::mallocFailed
+ }
+ } {1}
+
+ # Step the statement so that we can call _text() and _text16(). Before
+ # running sqlite3_step(), make sure that malloc() is not about to fail.
+ # Memory allocation failures that occur within sqlite3_step() are tested
+ # elsewhere.
+ set mf [sqlite3_memdebug_pending]
+ sqlite3_memdebug_fail -1
+ do_test ${testid}.3 {
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ sqlite3_memdebug_fail $mf
+
+ # Test for malloc() failures within _text() and _text16().
+ #
+ do_test ${testid}.4.1 {
+ set ::text8 [sqlite3_column_text $::STMT 0]
+ set mf [expr [sqlite3_memdebug_pending] < 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text8 == ""}
+ } {1}
+ do_test ${testid}.4.2 {
+ set ::text16 [sqlite3_column_text16 $::STMT 0]
+ set ::text16 [encoding convertfrom unicode $::text16]
+ set ::text16 [string range $::text16 0 end-1]
+ set mf [expr [sqlite3_memdebug_pending] < 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text16 == ""}
+ } {1}
+ do_test ${testid}.4.3 {
+ set ::text8_2 [sqlite3_column_text $::STMT 0]
+ set mf [expr [sqlite3_memdebug_pending] < 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")}
+ } {1}
+
+ # Test for malloc() failures within _int(), _int64() and _real(). The only
+ # way this can occur is if the string has to be translated from UTF-16 to
+ # UTF-8 before being converted to a numeric value.
+ do_test ${testid}.4.4.1 {
+ set mf [sqlite3_memdebug_pending]
+ sqlite3_memdebug_fail -1
+ sqlite3_column_text16 $::STMT 0
+ sqlite3_memdebug_fail $mf
+ sqlite3_column_int $::STMT 0
+ } {0}
+ do_test ${testid}.4.5 {
+ set mf [sqlite3_memdebug_pending]
+ sqlite3_memdebug_fail -1
+ sqlite3_column_text16 $::STMT 0
+ sqlite3_memdebug_fail $mf
+ sqlite3_column_int64 $::STMT 0
+ } {0}
+
+ do_test ${testid}.4.6 {
+ set mf [sqlite3_memdebug_pending]
+ sqlite3_memdebug_fail -1
+ sqlite3_column_text16 $::STMT 0
+ sqlite3_memdebug_fail $mf
+ sqlite3_column_double $::STMT 0
+ } {0.0}
+
+ set mallocFailedAfterStep [expr \
+ [sqlite3_memdebug_pending] < 0 && !$::mallocFailed
+ ]
+
+ sqlite3_memdebug_fail -1
+ # Test that if a malloc() failed the next call to sqlite3_step() returns
+ # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE.
+ #
+ do_test ${testid}.5 {
+ sqlite3_step $::STMT
+ } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}]
+
+ do_test ${testid}.6 {
+ sqlite3_finalize $::STMT
+ } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}]
+
+ if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} {
+ sqlite3_memdebug_fail -1
+ set go 0
+ }
+ }
+}
+
+execsql {
+ CREATE TABLE tbl(
+ the_first_reasonably_long_column_name that_also_has_quite_a_lengthy_type
+ );
+ INSERT INTO tbl VALUES(
+ 'An extra long string. Far too long to be stored in NBFS bytes.'
+ );
+}
+
+do_stmt_test 1 "SELECT * FROM tbl"
+
+sqlite3_memdebug_fail -1
+finish_test
diff --git a/third_party/sqlite/test/malloc5.test b/third_party/sqlite/test/malloc5.test
new file mode 100755
index 0000000..22f9237
--- /dev/null
+++ b/third_party/sqlite/test/malloc5.test
@@ -0,0 +1,397 @@
+# 2005 November 30
+#
+# 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 contains test cases focused on the two memory-management APIs,
+# sqlite3_soft_heap_limit() and sqlite3_release_memory().
+#
+# $Id: malloc5.test,v 1.18 2008/02/18 22:24:58 drh Exp $
+
+#---------------------------------------------------------------------------
+# NOTES ON EXPECTED BEHAVIOUR
+#
+#---------------------------------------------------------------------------
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+db close
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc5 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+# Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time.
+ifcapable !memorymanage {
+ finish_test
+ return
+}
+
+sqlite3_soft_heap_limit 0
+sqlite3 db test.db
+
+do_test malloc5-1.1 {
+ # Simplest possible test. Call sqlite3_release_memory when there is exactly
+ # one unused page in a single pager cache. This test case set's the
+ # value of the ::pgalloc variable, which is used in subsequent tests.
+ #
+ # Note: Even though executing this statement on an empty database
+ # modifies 2 pages (the root of sqlite_master and the new root page),
+ # the sqlite_master root (page 1) is never freed because the btree layer
+ # retains a reference to it for the entire transaction.
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ }
+ set ::pgalloc [sqlite3_release_memory]
+ expr $::pgalloc > 0
+} {1}
+do_test malloc5-1.2 {
+ # Test that the transaction started in the above test is still active.
+ # Because the page freed had been written to, freeing it required a
+ # journal sync and exclusive lock on the database file. Test the file
+ # appears to be locked.
+ sqlite3 db2 test.db
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {1 {database is locked}}
+do_test malloc5-1.3 {
+ # Again call [sqlite3_release_memory] when there is exactly one unused page
+ # in the cache. The same amount of memory is required, but no journal-sync
+ # or exclusive lock should be established.
+ execsql {
+ COMMIT;
+ BEGIN;
+ SELECT * FROM abc;
+ }
+ sqlite3_release_memory
+} $::pgalloc
+do_test malloc5-1.4 {
+ # Database should not be locked this time.
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {0 {}}
+do_test malloc5-1.5 {
+ # Manipulate the cache so that it contains two unused pages. One requires
+ # a journal-sync to free, the other does not.
+ db2 close
+ execsql {
+ SELECT * FROM abc;
+ CREATE TABLE def(d, e, f);
+ }
+ sqlite3_release_memory 500
+} $::pgalloc
+do_test malloc5-1.6 {
+ # Database should not be locked this time. The above test case only
+ # requested 500 bytes of memory, which can be obtained by freeing the page
+ # that does not require an fsync().
+ sqlite3 db2 test.db
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {0 {}}
+do_test malloc5-1.7 {
+ # Release another 500 bytes of memory. This time we require a sync(),
+ # so the database file will be locked afterwards.
+ db2 close
+ sqlite3_release_memory 500
+} $::pgalloc
+do_test malloc5-1.8 {
+ sqlite3 db2 test.db
+ catchsql {
+ SELECT * FROM abc;
+ } db2
+} {1 {database is locked}}
+do_test malloc5-1.9 {
+ execsql {
+ COMMIT;
+ }
+} {}
+
+do_test malloc5-2.1 {
+ # Put some data in tables abc and def. Both tables are still wholly
+ # contained within their root pages.
+ execsql {
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ INSERT INTO def VALUES(7, 8, 9);
+ INSERT INTO def VALUES(10,11,12);
+ }
+} {}
+do_test malloc5-2.2 {
+ # Load the root-page for table def into the cache. Then query table abc.
+ # Halfway through the query call sqlite3_release_memory(). The goal of this
+ # test is to make sure we don't free pages that are in use (specifically,
+ # the root of table abc).
+ set nRelease 0
+ execsql {
+ BEGIN;
+ SELECT * FROM def;
+ }
+ set data [list]
+ db eval {SELECT * FROM abc} {
+ incr nRelease [sqlite3_release_memory]
+ lappend data $a $b $c
+ }
+ execsql {
+ COMMIT;
+ }
+ list $nRelease $data
+} [list $pgalloc [list 1 2 3 4 5 6]]
+
+do_test malloc5-3.1 {
+ # Simple test to show that if two pagers are opened from within this
+ # thread, memory is freed from both when sqlite3_release_memory() is
+ # called.
+ execsql {
+ BEGIN;
+ SELECT * FROM abc;
+ }
+ execsql {
+ SELECT * FROM sqlite_master;
+ BEGIN;
+ SELECT * FROM def;
+ } db2
+ sqlite3_release_memory
+} [expr $::pgalloc * 2]
+do_test malloc5-3.2 {
+ concat \
+ [execsql {SELECT * FROM abc; COMMIT}] \
+ [execsql {SELECT * FROM def; COMMIT} db2]
+} {1 2 3 4 5 6 7 8 9 10 11 12}
+
+db2 close
+puts "Highwater mark: [sqlite3_memory_highwater]"
+
+# The following two test cases each execute a transaction in which
+# 10000 rows are inserted into table abc. The first test case is used
+# to ensure that more than 1MB of dynamic memory is used to perform
+# the transaction.
+#
+# The second test case sets the "soft-heap-limit" to 100,000 bytes (0.1 MB)
+# and tests to see that this limit is not exceeded at any point during
+# transaction execution.
+#
+# Before executing malloc5-4.* we save the value of the current soft heap
+# limit in variable ::soft_limit. The original value is restored after
+# running the tests.
+#
+set ::soft_limit [sqlite3_soft_heap_limit -1]
+execsql {PRAGMA cache_size=2000}
+do_test malloc5-4.1 {
+ execsql {BEGIN;}
+ execsql {DELETE FROM abc;}
+ for {set i 0} {$i < 10000} {incr i} {
+ execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
+ }
+ execsql {COMMIT;}
+ set nMaxBytes [sqlite3_memory_highwater 1]
+ puts -nonewline " (Highwater mark: $nMaxBytes) "
+ expr $nMaxBytes > 1000000
+} {1}
+do_test malloc5-4.2 {
+ sqlite3_release_memory
+ sqlite3_soft_heap_limit 100000
+ sqlite3_memory_highwater 1
+ execsql {BEGIN;}
+ for {set i 0} {$i < 10000} {incr i} {
+ execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
+ }
+ execsql {COMMIT;}
+ set nMaxBytes [sqlite3_memory_highwater 1]
+ puts -nonewline " (Highwater mark: $nMaxBytes) "
+
+ # We used to test ($nMaxBytes<100000), because the soft-heap-limit is
+ # 100000 bytes. But if an allocation that will exceed the
+ # soft-heap-limit is requested from within the only pager instance in
+ # the system, then there is no way to free memory and the limit has to
+ # be exceeded. An exception is memory allocated to store actual page
+ # data (the code contains a special case for this).
+ #
+ # This is not a problem because all allocations apart from those
+ # used to store cached page data are both small and transient.
+ #
+ # Summary: the actual high-water mark for memory usage may be slightly
+ # higher than the soft-heap-limit. The specific allocations that cause
+ # the problem are the calls to sqlite3_malloc() inserted into selected
+ # sqlite3OsXXX() functions in test builds.
+ #
+ expr $nMaxBytes <= 100100
+} {1}
+do_test malloc5-4.3 {
+ # Check that the content of table abc is at least roughly as expected.
+ execsql {
+ SELECT count(*), sum(a), sum(b) FROM abc;
+ }
+} [list 20000 [expr int(20000.0 * 4999.5)] [expr int(20000.0 * 4999.5)]]
+
+# Restore the soft heap limit.
+sqlite3_soft_heap_limit $::soft_limit
+
+# Test that there are no problems calling sqlite3_release_memory when
+# there are open in-memory databases.
+#
+# At one point these tests would cause a seg-fault.
+#
+do_test malloc5-5.1 {
+ db close
+ sqlite3 db :memory:
+ execsql {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES('abcdefghi', 1234567890, NULL);
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ INSERT INTO abc SELECT * FROM abc;
+ }
+ sqlite3_release_memory
+} 0
+do_test malloc5-5.2 {
+ sqlite3_soft_heap_limit 5000
+ execsql {
+ COMMIT;
+ PRAGMA temp_store = memory;
+ SELECT * FROM abc ORDER BY a;
+ }
+ expr 1
+} {1}
+sqlite3_soft_heap_limit $::soft_limit
+
+#-------------------------------------------------------------------------
+# The following test cases (malloc5-6.*) test the new global LRU list
+# used to determine the pages to recycle when sqlite3_release_memory is
+# called and there is more than one pager open.
+#
+proc nPage {db} {
+ set bt [btree_from_db $db]
+ array set stats [btree_pager_stats $bt]
+ set stats(page)
+}
+db close
+file delete -force test.db test.db-journal test2.db test2.db-journal
+
+# This block of test-cases (malloc5-6.1.*) prepares two database files
+# for the subsequent tests.
+do_test malloc5-6.1.1 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA default_cache_size=10;
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ INSERT INTO abc VALUES(randstr(50,50), randstr(75,75), randstr(100,100));
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ INSERT INTO abc
+ SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
+ COMMIT;
+ }
+ copy_file test.db test2.db
+ sqlite3 db2 test2.db
+ list \
+ [expr ([file size test.db]/1024)>20] [expr ([file size test2.db]/1024)>20]
+} {1 1}
+do_test malloc5-6.1.2 {
+ list [execsql {PRAGMA cache_size}] [execsql {PRAGMA cache_size} db2]
+} {10 10}
+
+do_test malloc5-6.2.1 {
+ execsql { SELECT * FROM abc } db2
+ execsql {SELECT * FROM abc} db
+ list [nPage db] [nPage db2]
+} {10 10}
+do_test malloc5-6.2.2 {
+ # If we now try to reclaim some memory, it should come from the db2 cache.
+ sqlite3_release_memory 3000
+ list [nPage db] [nPage db2]
+} {10 7}
+do_test malloc5-6.2.3 {
+ # Access the db2 cache again, so that all the db2 pages have been used
+ # more recently than all the db pages. Then try to reclaim 3000 bytes.
+ # This time, 3 pages should be pulled from the db cache.
+ execsql { SELECT * FROM abc } db2
+ sqlite3_release_memory 3000
+ list [nPage db] [nPage db2]
+} {7 10}
+
+
+do_test malloc5-6.3.1 {
+ # Now open a transaction and update 2 pages in the db2 cache. Then
+ # do a SELECT on the db cache so that all the db pages are more recently
+ # used than the db2 pages. When we try to free memory, SQLite should
+ # free the non-dirty db2 pages, then the db pages, then finally use
+ # sync() to free up the dirty db2 pages. The only page that cannot be
+ # freed is page1 of db2. Because there is an open transaction, the
+ # btree layer holds a reference to page 1 in the db2 cache.
+ execsql {
+ BEGIN;
+ UPDATE abc SET c = randstr(100,100)
+ WHERE rowid = 1 OR rowid = (SELECT max(rowid) FROM abc);
+ } db2
+ execsql { SELECT * FROM abc } db
+ list [nPage db] [nPage db2]
+} {10 10}
+do_test malloc5-6.3.2 {
+ # Try to release 7700 bytes. This should release all the
+ # non-dirty pages held by db2.
+ sqlite3_release_memory [expr 7*1100]
+ list [nPage db] [nPage db2]
+} {10 3}
+do_test malloc5-6.3.3 {
+ # Try to release another 1000 bytes. This should come fromt the db
+ # cache, since all three pages held by db2 are either in-use or diry.
+ sqlite3_release_memory 1000
+ list [nPage db] [nPage db2]
+} {9 3}
+do_test malloc5-6.3.4 {
+ # Now release 9900 more (about 9 pages worth). This should expunge
+ # the rest of the db cache. But the db2 cache remains intact, because
+ # SQLite tries to avoid calling sync().
+ sqlite3_release_memory 9900
+ list [nPage db] [nPage db2]
+} {0 3}
+do_test malloc5-6.3.5 {
+ # But if we are really insistent, SQLite will consent to call sync()
+ # if there is no other option.
+ sqlite3_release_memory 1000
+ list [nPage db] [nPage db2]
+} {0 2}
+do_test malloc5-6.3.6 {
+ # The referenced page (page 1 of the db2 cache) will not be freed no
+ # matter how much memory we ask for:
+ sqlite3_release_memory 31459
+ list [nPage db] [nPage db2]
+} {0 1}
+
+db2 close
+
+sqlite3_soft_heap_limit $::soft_limit
+finish_test
+catch {db close}
diff --git a/third_party/sqlite/test/malloc6.test b/third_party/sqlite/test/malloc6.test
new file mode 100755
index 0000000..ded3d1e
--- /dev/null
+++ b/third_party/sqlite/test/malloc6.test
@@ -0,0 +1,55 @@
+# 2006 June 25
+#
+# 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 attempts to check the library in an out-of-memory situation.
+#
+# $Id: malloc6.test,v 1.5 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc6 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+
+set sqlite_os_trace 0
+do_malloc_test malloc6-1 -tclprep {
+ db close
+} -tclbody {
+ if {[catch {sqlite3 db test.db}]} {
+ error "out of memory"
+ }
+ sqlite3_extended_result_codes db 1
+} -sqlbody {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE IF NOT EXISTS t1(
+ a int, b float, c double, d text, e varchar(20),
+ primary key(a,b,c)
+ );
+ CREATE TABLE IF NOT EXISTS t1(
+ a int, b float, c double, d text, e varchar(20),
+ primary key(a,b,c)
+ );
+ DROP TABLE IF EXISTS t1;
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc6-1.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+finish_test
diff --git a/third_party/sqlite/test/malloc7.test b/third_party/sqlite/test/malloc7.test
new file mode 100755
index 0000000..3e301c8
--- /dev/null
+++ b/third_party/sqlite/test/malloc7.test
@@ -0,0 +1,48 @@
+# 2006 July 26
+#
+# 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 contains additional out-of-memory checks (see malloc.tcl)
+# added to expose a bug in out-of-memory handling for sqlite3_prepare16().
+#
+# $Id: malloc7.test,v 1.5 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc7 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+
+do_malloc_test malloc7-1 -sqlprep {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX i1 ON t1(b,c);
+} -tclbody {
+ set sql16 [encoding convertto unicode "SELECT * FROM sqlite_master"]
+ append sql16 "\00\00"
+ set nbyte [string length $sql16]
+ set ::STMT [sqlite3_prepare16 db $sql16 $nbyte DUMMY]
+ sqlite3_finalize $::STMT
+}
+
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-99.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+puts open-file-count=$sqlite_open_file_count
+finish_test
diff --git a/third_party/sqlite/test/malloc8.test b/third_party/sqlite/test/malloc8.test
new file mode 100755
index 0000000..9192eaf
--- /dev/null
+++ b/third_party/sqlite/test/malloc8.test
@@ -0,0 +1,95 @@
+# 2007 April 25
+#
+# 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 contains additional out-of-memory checks (see malloc.tcl)
+# added to expose a bug in out-of-memory handling for sqlite3_value_text()
+#
+# $Id: malloc8.test,v 1.7 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc8 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+
+# The setup is a database with UTF-16 encoding that contains a single
+# large string. We will be running lots of queries against this
+# database. Because we will be extracting the string as UTF-8, there
+# is a type conversion that occurs and thus an opportunity for malloc()
+# to fail and for sqlite3_value_text() to return 0 even though
+# sqlite3_value_type() returns SQLITE_TEXT.
+#
+
+do_malloc_test malloc8-1 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+} -sqlbody {
+ SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1;
+}
+do_malloc_test malloc8-2 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+} -sqlbody {
+ SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a)
+ FROM t1;
+}
+do_malloc_test malloc8-3 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+} -sqlbody {
+ SELECT length(a), substr(a, 4, 4) FROM t1;
+}
+ifcapable datetime {
+ do_malloc_test malloc8-4 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+ } -sqlbody {
+ SELECT julianday(a,a) FROM t1;
+ }
+}
+do_malloc_test malloc8-5 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+} -sqlbody {
+ SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL;
+}
+do_malloc_test malloc8-6 -sqlprep {
+ PRAGMA encoding='UTF-16';
+ CREATE TABLE t1(a);
+ INSERT INTO t1
+ VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
+} -sqlbody {
+ SELECT hex(randomblob(100));
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-99.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+finish_test
diff --git a/third_party/sqlite/test/malloc9.test b/third_party/sqlite/test/malloc9.test
new file mode 100755
index 0000000..e142345
--- /dev/null
+++ b/third_party/sqlite/test/malloc9.test
@@ -0,0 +1,51 @@
+# 2007 April 30
+#
+# 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 contains additional out-of-memory checks (see malloc.tcl)
+# added to expose a bug in out-of-memory handling for sqlite3_prepare().
+#
+# $Id: malloc9.test,v 1.5 2008/04/04 12:21:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping malloc9 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+
+do_malloc_test malloc-9.1 -tclprep {
+ set sql {CREATE TABLE t1(x)}
+ set sqlbytes [string length $sql]
+ append sql {; INSERT INTO t1 VALUES(1)}
+} -tclbody {
+ if {[catch {sqlite3_prepare db $sql $sqlbytes TAIL} STMT]} {
+ set msg $STMT
+ set STMT {}
+ error $msg
+ }
+} -cleanup {
+ if {$STMT!=""} {
+ sqlite3_finalize $STMT
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc9-99.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocA.test b/third_party/sqlite/test/mallocA.test
new file mode 100755
index 0000000..08f6930
--- /dev/null
+++ b/third_party/sqlite/test/mallocA.test
@@ -0,0 +1,78 @@
+# 2007 April 30
+#
+# 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 contains additional out-of-memory checks (see malloc.tcl).
+#
+# $Id: mallocA.test,v 1.8 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocA tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+
+# Construct a test database
+#
+file delete -force test.db.bu
+db eval {
+ CREATE TABLE t1(a COLLATE NOCASE,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(1,2,4);
+ INSERT INTO t1 VALUES(2,3,4);
+ CREATE INDEX t1i1 ON t1(a);
+ CREATE INDEX t1i2 ON t1(b,c);
+ CREATE TABLE t2(x,y,z);
+}
+db close
+file copy test.db test.db.bu
+
+
+do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody {
+ ANALYZE
+}
+do_malloc_test mallocA-1.1 -testdb test.db.bu -sqlbody {
+ ANALYZE t1
+}
+do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody {
+ ANALYZE main
+}
+do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody {
+ ANALYZE main.t1
+}
+ifcapable reindex {
+ do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody {
+ REINDEX;
+ }
+ do_malloc_test mallocA-3 -testdb test.db.bu -sqlbody {
+ REINDEX t1;
+ }
+ do_malloc_test mallocA-4 -testdb test.db.bu -sqlbody {
+ REINDEX main.t1;
+ }
+ do_malloc_test mallocA-5 -testdb test.db.bu -sqlbody {
+ REINDEX nocase;
+ }
+}
+
+# Ensure that no file descriptors were leaked.
+do_test malloc-99.X {
+ catch {db close}
+ set sqlite_open_file_count
+} {0}
+
+file delete -force test.db.bu
+finish_test
diff --git a/third_party/sqlite/test/mallocAll.test b/third_party/sqlite/test/mallocAll.test
new file mode 100755
index 0000000..5fbd849
--- /dev/null
+++ b/third_party/sqlite/test/mallocAll.test
@@ -0,0 +1,67 @@
+#
+# 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 runs all out-of-memory tests.
+#
+# $Id: mallocAll.test,v 1.1 2007/11/26 13:36:00 drh Exp $
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ mallocAll.test
+}
+
+if {[sqlite3 -has-codec]} {
+ # lappend EXCLUDE \
+ # conflict.test
+}
+
+
+# Files to include in the test. If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/*malloc*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+}
+source $testdir/misuse.test
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/mallocB.test b/third_party/sqlite/test/mallocB.test
new file mode 100755
index 0000000..73468b5
--- /dev/null
+++ b/third_party/sqlite/test/mallocB.test
@@ -0,0 +1,51 @@
+# 2007 May 30
+#
+# 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 contains additional out-of-memory checks (see malloc.tcl).
+# These were all discovered by fuzzy generation of SQL. Apart from
+# that they have little in common.
+#
+#
+# $Id: mallocB.test,v 1.9 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocB tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+source $testdir/malloc_common.tcl
+
+do_malloc_test mallocB-1 -sqlbody {SELECT - 456}
+do_malloc_test mallocB-2 -sqlbody {SELECT - 456.1}
+do_malloc_test mallocB-3 -sqlbody {SELECT random()}
+do_malloc_test mallocB-4 -sqlbody {SELECT length(zeroblob(1000))}
+ifcapable subquery {
+ do_malloc_test mallocB-5 -sqlbody {SELECT * FROM (SELECT 1) GROUP BY 1;}
+}
+
+# The following test checks that there are no resource leaks following a
+# malloc() failure in sqlite3_set_auxdata().
+#
+# Note: This problem was not discovered by fuzzy generation of SQL. Not
+# that it really matters.
+#
+do_malloc_test mallocB-6 -sqlbody { SELECT test_auxdata('hello world'); }
+
+do_malloc_test mallocB-7 -sqlbody {
+ SELECT strftime(hex(randomblob(50)) || '%Y', 'now')
+}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocC.test b/third_party/sqlite/test/mallocC.test
new file mode 100755
index 0000000..8bdc0d1
--- /dev/null
+++ b/third_party/sqlite/test/mallocC.test
@@ -0,0 +1,107 @@
+# 2007 Aug 13
+#
+# 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 tests aspects of the malloc failure while parsing
+# CREATE TABLE statements in auto_vacuum mode.
+#
+# $Id: mallocC.test,v 1.9 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocC tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+proc do_mallocC_test {tn args} {
+ array set ::mallocopts $args
+ #set sum [allcksum db]
+
+ for {set ::n 1} {true} {incr ::n} {
+
+ # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
+ # may or may not be reported.
+ sqlite3_memdebug_fail $::n -repeat 1
+ do_test mallocC-$tn.$::n.1 {
+ set res [catchsql [string trim $::mallocopts(-sql)]]
+ set rc [expr {
+ 0==[string compare $res {1 {out of memory}}] ||
+ [db errorcode] == 3082 ||
+ 0==[lindex $res 0]
+ }]
+ if {$rc!=1} {
+ puts "Error: $res"
+ }
+ set rc
+ } {1}
+
+ # If $::n is greater than the number of malloc() calls required to
+ # execute the SQL, then this test is finished. Break out of the loop.
+ set nFail [sqlite3_memdebug_fail -1]
+ if {$nFail==0} {
+ break
+ }
+
+ # Recover from the malloc failure.
+ #
+ # Update: The new malloc() failure handling means that a transaction may
+ # still be active even if a malloc() has failed. But when these tests were
+ # written this was not the case. So do a manual ROLLBACK here so that the
+ # tests pass.
+ do_test mallocC-$tn.$::n.2 {
+ catch {
+ execsql {
+ ROLLBACK;
+ }
+ }
+ expr 0
+ } {0}
+
+ # Checksum the database.
+ #do_test mallocC-$tn.$::n.3 {
+ # allcksum db
+ #} $sum
+
+ #integrity_check mallocC-$tn.$::n.4
+ if {$::nErr>1} return
+ }
+ unset ::mallocopts
+}
+
+sqlite3_extended_result_codes db 1
+
+execsql {
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE t0(a, b, c);
+}
+do_mallocC_test 1 -sql {
+ BEGIN;
+ -- Allocate 32 new root pages. This will exercise the 'extract specific
+ -- page from the freelist' code when in auto-vacuum mode (see the
+ -- allocatePage() routine in btree.c).
+ CREATE TABLE t1(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t2(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t3(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t4(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t5(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t6(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t7(a UNIQUE, b UNIQUE, c UNIQUE);
+ CREATE TABLE t8(a UNIQUE, b UNIQUE, c UNIQUE);
+
+ ROLLBACK;
+}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocD.test b/third_party/sqlite/test/mallocD.test
new file mode 100755
index 0000000..08a2ba4
--- /dev/null
+++ b/third_party/sqlite/test/mallocD.test
@@ -0,0 +1,65 @@
+# 2007 Aug 29
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: mallocD.test,v 1.6 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocD tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+db close
+sqlite3_simulate_device -char atomic
+sqlite3 db test.db -vfs devsym
+
+set PREP {
+ PRAGMA page_size = 1024;
+ CREATE TABLE abc(a, b, c);
+}
+
+do_malloc_test mallocD-1 -sqlprep $PREP -sqlbody {
+ INSERT INTO abc VALUES(1, 2, 3);
+}
+
+do_malloc_test mallocD-2 -sqlprep $PREP -sqlbody {
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ ROLLBACK;
+}
+
+do_malloc_test mallocD-3 -sqlprep $PREP -sqlbody {
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, randstr(1500,1500));
+ COMMIT;
+}
+
+ifcapable attach {
+ do_malloc_test mallocD-4 -sqlprep $PREP -sqlbody {
+ ATTACH 'test2.db' AS aux;
+ BEGIN;
+ CREATE TABLE aux.def(d, e, f);
+ INSERT INTO abc VALUES(4, 5, 6);
+ COMMIT;
+ }
+}
+
+sqlite3_simulate_device -char {}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocE.test b/third_party/sqlite/test/mallocE.test
new file mode 100755
index 0000000..e87bc8b
--- /dev/null
+++ b/third_party/sqlite/test/mallocE.test
@@ -0,0 +1,48 @@
+# 2007 Aug 29
+#
+# 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 test script checks that tickets #2784 and #2789 have been fixed.
+#
+# $Id: mallocE.test,v 1.3 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocE tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+# ticket #2784
+#
+set PREP {
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(x, y, z);
+}
+do_malloc_test mallocE-1 -sqlprep $PREP -sqlbody {
+ SELECT p, q FROM (SELECT a+b AS p, b+c AS q FROM t1, t2 WHERE c>5)
+ LEFT JOIN t2 ON p=x;
+}
+
+# Ticket #2789
+#
+do_malloc_test mallocE-2 -sqlprep $PREP -sqlbody {
+ SELECT x, y2 FROM (SELECT a+b AS x, b+c AS y2 FROM t1, t2 WHERE c>5)
+ LEFT JOIN t2 USING(x) WHERE y2>11;
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/mallocF.test b/third_party/sqlite/test/mallocF.test
new file mode 100755
index 0000000..0015005
--- /dev/null
+++ b/third_party/sqlite/test/mallocF.test
@@ -0,0 +1,76 @@
+# 2007 Aug 29
+#
+# 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 test script checks that tickets #2794, #2795, #2796, and #2797
+# have been fixed.
+#
+# $Id: mallocF.test,v 1.4 2008/02/18 22:24:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocF tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+# tickets #2794 and #2795 and #2797
+#
+set PREP {
+ CREATE TABLE t1(x,y);
+ INSERT INTO t1 VALUES('abc123', 5);
+ INSERT INTO t1 VALUES('xyz987', 42);
+}
+do_malloc_test malloeF-1 -sqlprep $PREP -sqlbody {
+ SELECT * FROM t1 WHERE x GLOB 'abc*'
+}
+
+# ticket #2796
+#
+set PREP {
+ CREATE TABLE t1(x PRIMARY KEY,y UNIQUE);
+ INSERT INTO t1 VALUES('abc123', 5);
+ INSERT INTO t1 VALUES('xyz987', 42);
+}
+do_malloc_test malloeF-2 -sqlprep $PREP -sqlbody {
+ SELECT x FROM t1
+ WHERE y=1 OR y=2 OR y=3 OR y=4 OR y=5
+ OR y=6 OR y=7 OR y=8 OR y=9 OR y=10
+ OR y=11 OR y=12 OR y=13 OR y=14 OR y=15
+ OR y=x
+}
+
+set PREP {
+ CREATE TABLE t1(x PRIMARY KEY,y UNIQUE);
+ INSERT INTO t1 VALUES('abc123', 5);
+ INSERT INTO t1 VALUES('xyz987', 42);
+}
+do_malloc_test malloeF-3 -sqlprep $PREP -sqlbody {
+ SELECT x FROM t1 WHERE y BETWEEN 10 AND 29
+}
+
+# Ticket #2843
+#
+set PREP {
+ CREATE TABLE t1(x);
+ CREATE TRIGGER r1 BEFORE INSERT ON t1 BEGIN
+ SELECT 'hello';
+ END;
+}
+do_malloc_test mallocF-4 -sqlprep $PREP -sqlbody {
+ INSERT INTO t1 VALUES(random());
+}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocG.test b/third_party/sqlite/test/mallocG.test
new file mode 100755
index 0000000..eab533b
--- /dev/null
+++ b/third_party/sqlite/test/mallocG.test
@@ -0,0 +1,68 @@
+# 2007 Aug 29
+#
+# 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 test script checks malloc failures in various obscure operations.
+#
+# $Id: mallocG.test,v 1.5 2008/08/01 18:47:02 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+if {!$MEMDEBUG} {
+ puts "Skipping mallocG tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+# Malloc failures while opening a database connection.
+#
+do_malloc_test mallocG-1 -tclbody {
+ db close
+ sqlite3 db test.db
+}
+
+do_malloc_test mallocG-2 -sqlprep {
+ CREATE TABLE t1(x, y);
+ CREATE TABLE t2(x INTEGER PRIMARY KEY);
+} -sqlbody {
+ SELECT y FROM t1 WHERE x IN t2;
+}
+
+do_malloc_test mallocG-3 -sqlprep {
+ CREATE TABLE t1(x UNIQUE);
+ INSERT INTO t1 VALUES ('hello');
+ INSERT INTO t1 VALUES ('out there');
+} -sqlbody {
+ SELECT * FROM t1
+ WHERE x BETWEEN 'a' AND 'z'
+ AND x BETWEEN 'c' AND 'w'
+ AND x BETWEEN 'e' AND 'u'
+ AND x BETWEEN 'g' AND 'r'
+ AND x BETWEEN 'i' AND 'q'
+ AND x BETWEEN 'i' AND 'm'
+}
+
+proc utf16 {utf8} {
+ set utf16 [encoding convertto unicode $utf8]
+ append utf16 "\x00\x00"
+ return $utf16
+}
+
+do_malloc_test mallocG-4 -tclbody {
+ set rc [sqlite3_complete16 [utf16 "SELECT * FROM t1;"]]
+ if {$rc==1} {set rc 0} {error "out of memory"}
+ set rc
+}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocH.test b/third_party/sqlite/test/mallocH.test
new file mode 100755
index 0000000..7017b38
--- /dev/null
+++ b/third_party/sqlite/test/mallocH.test
@@ -0,0 +1,73 @@
+# 2008 August 01
+#
+# 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 test script checks malloc failures in various obscure operations.
+#
+# $Id: mallocH.test,v 1.2 2008/08/01 20:10:09 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Malloc failures in journaling of in-memory databases.
+#
+do_malloc_test mallocH-1 -tclprep {
+ db close
+ sqlite3 db :memory:
+ db eval {
+ CREATE TABLE t1(x UNIQUE, y);
+ INSERT INTO t1 VALUES(1,2);
+ }
+} -sqlbody {
+ INSERT INTO t1 SELECT x+1, y+100 FROM t1;
+}
+
+# Malloc failures while parsing a CASE expression.
+#
+do_malloc_test mallocH-2 -sqlbody {
+ SELECT CASE WHEN 1 THEN 1 END;
+}
+
+# Malloc failures while parsing a EXISTS(SELECT ...)
+#
+do_malloc_test mallocH-3 -sqlbody {
+ SELECT 3+EXISTS(SELECT * FROM sqlite_master);
+}
+
+# Malloc failures within the replace() function.
+#
+do_malloc_test mallocH-3 -sqlbody {
+ SELECT replace('ababa','a','xyzzy');
+}
+
+# Malloc failures during EXPLAIN.
+#
+ifcapable explain {
+ do_malloc_test mallocH-4 -sqlprep {
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ } -sqlbody {
+ EXPLAIN SELECT * FROM abc AS t2 WHERE rowid=1;
+ EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid=1;
+ }
+}
+
+# Malloc failure during integrity_check pragma.
+#
+do_malloc_test mallocH-5 -sqlprep {
+ CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
+ CREATE TABLE t2(x,y);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t2 SELECT * FROM t1;
+} -sqlbody {
+ PRAGMA integrity_check;
+}
+
+finish_test
diff --git a/third_party/sqlite/test/mallocI.test b/third_party/sqlite/test/mallocI.test
new file mode 100755
index 0000000..ace92dc
--- /dev/null
+++ b/third_party/sqlite/test/mallocI.test
@@ -0,0 +1,43 @@
+# 2008 August 01
+#
+# 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 test script checks malloc failures in various obscure operations.
+#
+# $Id: mallocI.test,v 1.1 2008/08/02 03:50:39 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+
+# Malloc failures in a view.
+#
+do_malloc_test mallocI-1 -sqlprep {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE VIEW v1 AS SELECT a*b, c*d FROM t1 ORDER BY b-d;
+} -sqlbody {
+ SELECT * FROM v1
+}
+
+# Malloc failure while trying to service a pragma on a TEMP database.
+#
+do_malloc_test mallocI-2 -sqlbody {
+ PRAGMA temp.page_size
+}
+
+# Malloc failure while creating a table from a SELECT statement.
+#
+do_malloc_test mallocI-3 -sqlprep {
+ CREATE TABLE t1(a,b,c);
+} -sqlbody {
+ CREATE TABLE t2 AS SELECT b,c FROM t1;
+}
+
+finish_test
diff --git a/third_party/sqlite/test/malloc_common.tcl b/third_party/sqlite/test/malloc_common.tcl
new file mode 100755
index 0000000..44985ae
--- /dev/null
+++ b/third_party/sqlite/test/malloc_common.tcl
@@ -0,0 +1,168 @@
+# 2007 May 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 contains common code used by many different malloc tests
+# within the test suite.
+#
+# $Id: malloc_common.tcl,v 1.21 2008/08/04 20:13:27 drh Exp $
+
+# If we did not compile with malloc testing enabled, then do nothing.
+#
+ifcapable builtin_test {
+ set MEMDEBUG 1
+} else {
+ set MEMDEBUG 0
+ return 0
+}
+
+# Usage: do_malloc_test <test number> <options...>
+#
+# The first argument, <test number>, is an integer used to name the
+# tests executed by this proc. Options are as follows:
+#
+# -tclprep TCL script to run to prepare test.
+# -sqlprep SQL script to run to prepare test.
+# -tclbody TCL script to run with malloc failure simulation.
+# -sqlbody TCL script to run with malloc failure simulation.
+# -cleanup TCL script to run after the test.
+#
+# This command runs a series of tests to verify SQLite's ability
+# to handle an out-of-memory condition gracefully. It is assumed
+# that if this condition occurs a malloc() call will return a
+# NULL pointer. Linux, for example, doesn't do that by default. See
+# the "BUGS" section of malloc(3).
+#
+# Each iteration of a loop, the TCL commands in any argument passed
+# to the -tclbody switch, followed by the SQL commands in any argument
+# passed to the -sqlbody switch are executed. Each iteration the
+# Nth call to sqliteMalloc() is made to fail, where N is increased
+# each time the loop runs starting from 1. When all commands execute
+# successfully, the loop ends.
+#
+proc do_malloc_test {tn args} {
+ array unset ::mallocopts
+ array set ::mallocopts $args
+
+ if {[string is integer $tn]} {
+ set tn malloc-$tn
+ }
+ if {[info exists ::mallocopts(-start)]} {
+ set start $::mallocopts(-start)
+ } else {
+ set start 0
+ }
+ if {[info exists ::mallocopts(-end)]} {
+ set end $::mallocopts(-end)
+ } else {
+ set end 50000
+ }
+ save_prng_state
+
+ foreach ::iRepeat {0 10000000} {
+ set ::go 1
+ for {set ::n $start} {$::go && $::n <= $end} {incr ::n} {
+
+ # If $::iRepeat is 0, then the malloc() failure is transient - it
+ # fails and then subsequent calls succeed. If $::iRepeat is 1,
+ # then the failure is persistent - once malloc() fails it keeps
+ # failing.
+ #
+ set zRepeat "transient"
+ if {$::iRepeat} {set zRepeat "persistent"}
+ restore_prng_state
+ foreach file [glob -nocomplain test.db-mj*] {file delete -force $file}
+
+ do_test ${tn}.${zRepeat}.${::n} {
+
+ # Remove all traces of database files test.db and test2.db
+ # from the file-system. Then open (empty database) "test.db"
+ # with the handle [db].
+ #
+ catch {db close}
+ catch {file delete -force test.db}
+ catch {file delete -force test.db-journal}
+ catch {file delete -force test2.db}
+ catch {file delete -force test2.db-journal}
+ if {[info exists ::mallocopts(-testdb)]} {
+ file copy $::mallocopts(-testdb) test.db
+ }
+ catch { sqlite3 db test.db }
+ if {[info commands db] ne ""} {
+ sqlite3_extended_result_codes db 1
+ }
+ sqlite3_db_config_lookaside db 0 0 0
+
+ # Execute any -tclprep and -sqlprep scripts.
+ #
+ if {[info exists ::mallocopts(-tclprep)]} {
+ eval $::mallocopts(-tclprep)
+ }
+ if {[info exists ::mallocopts(-sqlprep)]} {
+ execsql $::mallocopts(-sqlprep)
+ }
+
+ # Now set the ${::n}th malloc() to fail and execute the -tclbody
+ # and -sqlbody scripts.
+ #
+ sqlite3_memdebug_fail $::n -repeat $::iRepeat
+ set ::mallocbody {}
+ if {[info exists ::mallocopts(-tclbody)]} {
+ append ::mallocbody "$::mallocopts(-tclbody)\n"
+ }
+ if {[info exists ::mallocopts(-sqlbody)]} {
+ append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
+ }
+
+ # The following block sets local variables as follows:
+ #
+ # isFail - True if an error (any error) was reported by sqlite.
+ # nFail - The total number of simulated malloc() failures.
+ # nBenign - The number of benign simulated malloc() failures.
+ #
+ set isFail [catch $::mallocbody msg]
+ set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
+ # puts -nonewline " (isFail=$isFail nFail=$nFail nBenign=$nBenign) "
+
+ # If one or more mallocs failed, run this loop body again.
+ #
+ set go [expr {$nFail>0}]
+
+ if {($nFail-$nBenign)==0} {
+ if {$isFail} {
+ set v2 $msg
+ } else {
+ set isFail 1
+ set v2 1
+ }
+ } elseif {!$isFail} {
+ set v2 $msg
+ } elseif {
+ [info command db]=="" ||
+ [db errorcode]==7 ||
+ [db errorcode]==[expr 10+(12<<8)] ||
+ $msg=="out of memory"
+ } {
+ set v2 1
+ } else {
+ set v2 $msg
+ puts [db errorcode]
+ }
+ lappend isFail $v2
+ } {1 1}
+
+ if {[info exists ::mallocopts(-cleanup)]} {
+ catch [list uplevel #0 $::mallocopts(-cleanup)] msg
+ }
+ }
+ }
+ unset ::mallocopts
+ sqlite3_memdebug_fail -1
+}
diff --git a/third_party/sqlite/test/manydb.test b/third_party/sqlite/test/manydb.test
new file mode 100755
index 0000000..9af5465
--- /dev/null
+++ b/third_party/sqlite/test/manydb.test
@@ -0,0 +1,91 @@
+# 2005 October 3
+#
+# 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.
+#
+# This file implements tests the ability of the library to open
+# many different databases at the same time without leaking memory.
+#
+# $Id: manydb.test,v 1.3 2006/01/11 01:08:34 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set N 300
+
+# First test how many file descriptors are available for use. To open a
+# database for writing SQLite requires 3 file descriptors (the database, the
+# journal and the directory).
+set filehandles {}
+catch {
+ for {set i 0} {$i<($N * 3)} {incr i} {
+ lappend filehandles [open testfile.1 w]
+ }
+}
+foreach fd $filehandles {
+ close $fd
+}
+catch {
+ file delete -force testfile.1
+}
+set N [expr $i / 3]
+
+# Create a bunch of random database names
+#
+unset -nocomplain dbname
+unset -nocomplain used
+for {set i 0} {$i<$N} {incr i} {
+ while 1 {
+ set name test-[format %08x [expr {int(rand()*0x7fffffff)}]].db
+ if {[info exists used($name)]} continue
+ set dbname($i) $name
+ set used($name) $i
+ break
+ }
+}
+
+# Create a bunch of databases
+#
+for {set i 0} {$i<$N} {incr i} {
+ do_test manydb-1.$i {
+ sqlite3 db$i $dbname($i)
+ execsql {
+ CREATE TABLE t1(a,b);
+ BEGIN;
+ INSERT INTO t1 VALUES(1,2);
+ } db$i
+ } {}
+}
+
+# Finish the transactions
+#
+for {set i 0} {$i<$N} {incr i} {
+ do_test manydb-2.$i {
+ execsql {
+ COMMIT;
+ SELECT * FROM t1;
+ } db$i
+ } {1 2}
+}
+
+
+# Close the databases and erase the files.
+#
+for {set i 0} {$i<$N} {incr i} {
+ do_test manydb-3.$i {
+ db$i close
+ file delete -force $dbname($i)
+ } {}
+}
+
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/memdb.test b/third_party/sqlite/test/memdb.test
new file mode 100755
index 0000000..c1eb115
--- /dev/null
+++ b/third_party/sqlite/test/memdb.test
@@ -0,0 +1,417 @@
+# 2001 September 15
+#
+# 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 in-memory database backend.
+#
+# $Id: memdb.test,v 1.15 2006/01/30 22:48:44 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable memorydb {
+
+# In the following sequence of tests, compute the MD5 sum of the content
+# of a table, make lots of modifications to that table, then do a rollback.
+# Verify that after the rollback, the MD5 checksum is unchanged.
+#
+# These tests were browed from trans.tcl.
+#
+do_test memdb-1.1 {
+ db close
+ sqlite3 db :memory:
+ # sqlite3 db test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x TEXT);
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ COMMIT;
+ SELECT count(*) FROM t3;
+ }
+} {1024}
+
+# The following procedure computes a "signature" for table "t3". If
+# T3 changes in any way, the signature should change.
+#
+# This is used to test ROLLBACK. We gather a signature for t3, then
+# make lots of changes to t3, then rollback and take another signature.
+# The two signatures should be the same.
+#
+proc signature {{fn {}}} {
+ set rx [db eval {SELECT x FROM t3}]
+ # set r1 [md5 $rx\n]
+ if {$fn!=""} {
+ # set fd [open $fn w]
+ # puts $fd $rx
+ # close $fd
+ }
+ # set r [db eval {SELECT count(*), md5sum(x) FROM t3}]
+ # puts "SIG($fn)=$r1"
+ return [list [string length $rx] $rx]
+}
+
+# Do rollbacks. Make sure the signature does not change.
+#
+set limit 10
+for {set i 2} {$i<=$limit} {incr i} {
+ set ::sig [signature one]
+ # puts "sig=$sig"
+ set cnt [lindex $::sig 0]
+ if {$i%2==0} {
+ execsql {PRAGMA synchronous=FULL}
+ } else {
+ execsql {PRAGMA synchronous=NORMAL}
+ }
+ do_test memdb-1.$i.1-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ set sig2 [signature two]
+ } $sig
+ # puts "sig2=$sig2"
+ # if {$sig2!=$sig} exit
+ do_test memdb-1.$i.2-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+ } $sig
+ if {$i<$limit} {
+ do_test memdb-1.$i.9-$cnt {
+ execsql {
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0;
+ }
+ } {}
+ }
+ set ::pager_old_format 0
+}
+
+integrity_check memdb-2.1
+
+do_test memdb-3.1 {
+ execsql {
+ CREATE TABLE t4(a,b,c,d);
+ BEGIN;
+ INSERT INTO t4 VALUES(1,2,3,4);
+ SELECT * FROM t4;
+ }
+} {1 2 3 4}
+do_test memdb-3.2 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type='table';
+ }
+} {t3 t4}
+do_test memdb-3.3 {
+ execsql {
+ DROP TABLE t4;
+ SELECT name FROM sqlite_master WHERE type='table';
+ }
+} {t3}
+do_test memdb-3.4 {
+ execsql {
+ ROLLBACK;
+ SELECT name FROM sqlite_master WHERE type='table';
+ }
+} {t3 t4}
+
+# Create tables for the first group of tests.
+#
+do_test memdb-4.0 {
+ execsql {
+ CREATE TABLE t1(a, b, c, UNIQUE(a,b));
+ CREATE TABLE t2(x);
+ SELECT c FROM t1 ORDER BY c;
+ }
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 {} {INSERT OR IGNORE} 0 3 1
+ 3 {} {INSERT OR REPLACE} 0 4 1
+ 4 {} REPLACE 0 4 1
+ 5 {} {INSERT OR FAIL} 1 {} 1
+ 6 {} {INSERT OR ABORT} 1 {} 1
+ 7 {} {INSERT OR ROLLBACK} 1 {} {}
+} {
+
+ # All tests after test 1 depend on conflict resolution. So end the
+ # loop if that is not available in this build.
+ ifcapable !conflict {if {$i>1} break}
+
+ do_test memdb-4.$i {
+ if {$conf!=""} {set conf "ON CONFLICT $conf"}
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN $conf;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test memdb-5.0 {
+ execsql {
+ DROP TABLE t2;
+ DROP TABLE t3;
+ CREATE TABLE t2(a,b,c);
+ INSERT INTO t2 VALUES(1,2,1);
+ INSERT INTO t2 VALUES(2,3,2);
+ INSERT INTO t2 VALUES(3,4,1);
+ INSERT INTO t2 VALUES(4,5,4);
+ SELECT c FROM t2 ORDER BY b;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(1);
+ }
+} {1 2 1 4}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the UNIQUE constraint
+# conf2 The conflict resolution algorithm on the BEGIN statement
+# cmd An UPDATE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "b" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t3
+#
+foreach {i conf1 conf2 cmd t0 t1 t2} {
+ 1 {} {} UPDATE 1 {6 7 8 9} 1
+ 2 REPLACE {} UPDATE 0 {7 6 9} 1
+ 3 IGNORE {} UPDATE 0 {6 7 3 9} 1
+ 4 FAIL {} UPDATE 1 {6 7 3 4} 1
+ 5 ABORT {} UPDATE 1 {1 2 3 4} 1
+ 6 ROLLBACK {} UPDATE 1 {1 2 3 4} 0
+ 7 REPLACE {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 8 IGNORE {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 9 FAIL {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 10 ABORT {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 11 ROLLBACK {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 12 {} {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 13 {} {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 14 {} {} {UPDATE OR FAIL} 1 {6 7 3 4} 1
+ 15 {} {} {UPDATE OR ABORT} 1 {1 2 3 4} 1
+ 16 {} {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0
+} {
+ # All tests after test 1 depend on conflict resolution. So end the
+ # loop if that is not available in this build.
+ ifcapable !conflict {
+ if {$i>1} break
+ }
+
+ if {$t0} {set t1 {column a is not unique}}
+ do_test memdb-5.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
+ INSERT INTO t1 SELECT * FROM t2;
+ UPDATE t3 SET x=0;
+ BEGIN $conf2;
+ $cmd t3 SET x=1;
+ $cmd t1 SET b=b*2;
+ $cmd t1 SET a=c+5;
+ }]} r1]
+ catch {execsql {COMMIT}}
+ if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
+ set r2 [execsql {SELECT x FROM t3}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test memdb-6.1 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 1 2 3 2 3 4 1 4 5 4}
+do_test memdb-6.2 {
+ execsql {
+ BEGIN;
+ DROP TABLE t2;
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ }
+} {t1 t3 t4}
+do_test memdb-6.3 {
+ execsql {
+ ROLLBACK;
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ }
+} {t1 t2 t3 t4}
+do_test memdb-6.4 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 1 2 3 2 3 4 1 4 5 4}
+ifcapable compound {
+do_test memdb-6.5 {
+ execsql {
+ SELECT a FROM t2 UNION SELECT b FROM t2 ORDER BY 1;
+ }
+} {1 2 3 4 5}
+} ;# ifcapable compound
+do_test memdb-6.6 {
+ execsql {
+ CREATE INDEX i2 ON t2(c);
+ SELECT a FROM t2 ORDER BY c;
+ }
+} {1 3 2 4}
+do_test memdb-6.6 {
+ execsql {
+ SELECT a FROM t2 ORDER BY c DESC;
+ }
+} {4 2 3 1}
+do_test memdb-6.7 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t5(x,y);
+ INSERT INTO t5 VALUES(1,2);
+ SELECT * FROM t5;
+ }
+} {1 2}
+do_test memdb-6.8 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ }
+} {t1 t2 t3 t4 t5}
+do_test memdb-6.9 {
+ execsql {
+ ROLLBACK;
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ }
+} {t1 t2 t3 t4}
+do_test memdb-6.10 {
+ execsql {
+ CREATE TABLE t5(x PRIMARY KEY, y UNIQUE);
+ SELECT * FROM t5;
+ }
+} {}
+do_test memdb-6.11 {
+ execsql {
+ SELECT * FROM t5 ORDER BY y DESC;
+ }
+} {}
+
+ifcapable conflict {
+ do_test memdb-6.12 {
+ execsql {
+ INSERT INTO t5 VALUES(1,2);
+ INSERT INTO t5 VALUES(3,4);
+ REPLACE INTO t5 VALUES(1,4);
+ SELECT rowid,* FROM t5;
+ }
+ } {3 1 4}
+ do_test memdb-6.13 {
+ execsql {
+ DELETE FROM t5 WHERE x>5;
+ SELECT * FROM t5;
+ }
+ } {1 4}
+ do_test memdb-6.14 {
+ execsql {
+ DELETE FROM t5 WHERE y<3;
+ SELECT * FROM t5;
+ }
+ } {1 4}
+}
+
+do_test memdb-6.15 {
+ execsql {
+ DELETE FROM t5 WHERE x>0;
+ SELECT * FROM t5;
+ }
+} {}
+
+ifcapable subquery {
+ do_test memdb-7.1 {
+ execsql {
+ CREATE TABLE t6(x);
+ INSERT INTO t6 VALUES(1);
+ INSERT INTO t6 SELECT x+1 FROM t6;
+ INSERT INTO t6 SELECT x+2 FROM t6;
+ INSERT INTO t6 SELECT x+4 FROM t6;
+ INSERT INTO t6 SELECT x+8 FROM t6;
+ INSERT INTO t6 SELECT x+16 FROM t6;
+ INSERT INTO t6 SELECT x+32 FROM t6;
+ INSERT INTO t6 SELECT x+64 FROM t6;
+ INSERT INTO t6 SELECT x+128 FROM t6;
+ SELECT count(*) FROM (SELECT DISTINCT x FROM t6);
+ }
+ } {256}
+ for {set i 1} {$i<=256} {incr i} {
+ do_test memdb-7.2.$i {
+ execsql "DELETE FROM t6 WHERE x=\
+ (SELECT x FROM t6 ORDER BY random() LIMIT 1)"
+ execsql {SELECT count(*) FROM t6}
+ } [expr {256-$i}]
+ }
+}
+
+# Ticket #1524
+#
+do_test memdb-8.1 {
+ db close
+ sqlite3 db {:memory:}
+ execsql {
+ PRAGMA auto_vacuum=TRUE;
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(randstr(5000,6000));
+ INSERT INTO t1 VALUES(randstr(5000,6000));
+ INSERT INTO t1 VALUES(randstr(5000,6000));
+ INSERT INTO t1 VALUES(randstr(5000,6000));
+ INSERT INTO t1 VALUES(randstr(5000,6000));
+ SELECT count(*) FROM t1;
+ }
+} 5
+do_test memdb-8.2 {
+ execsql {
+ DELETE FROM t1;
+ SELECT count(*) FROM t1;
+ }
+} 0
+
+
+} ;# ifcapable memorydb
+
+finish_test
diff --git a/third_party/sqlite/test/memleak.test b/third_party/sqlite/test/memleak.test
new file mode 100755
index 0000000..f4aaf27
--- /dev/null
+++ b/third_party/sqlite/test/memleak.test
@@ -0,0 +1,98 @@
+# 2001 September 15
+#
+# 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 runs all tests.
+#
+# $Id: memleak.test,v 1.10 2007/03/30 17:17:52 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test memleak_finish_test
+proc finish_test {} {
+ catch {db close}
+ memleak_check
+}
+
+if {[file exists ./sqlite_test_count]} {
+ set COUNT [exec cat ./sqlite_test_count]
+} else {
+ set COUNT 3
+}
+
+# LeakList will hold a list of the number of unfreed mallocs after
+# each round of the test. This number should be constant. If it
+# grows, it may mean there is a memory leak in the library.
+#
+set LeakList {}
+
+set EXCLUDE {
+ all.test
+ quick.test
+ misuse.test
+ memleak.test
+ btree2.test
+ async.test
+ async2.test
+ trans.test
+ crash.test
+ autovacuum_crash.test
+}
+# Test files btree2.test and btree4.test don't work if the
+# SQLITE_DEFAULT_AUTOVACUUM macro is defined to true (because they depend
+# on tables being allocated starting at page 2).
+#
+ifcapable default_autovacuum {
+ lappend EXCLUDE btree2.test
+ lappend EXCLUDE btree4.test
+}
+
+if {[sqlite3 -has-codec]} {
+ # lappend EXCLUDE
+}
+if {[llength $argv]>0} {
+ set FILELIST $argv
+ set argv {}
+} else {
+ set FILELIST [lsort -dictionary [glob $testdir/*.test]]
+}
+
+foreach testfile $FILELIST {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ set LeakList {}
+ for {set COUNTER 0} {$COUNTER<$COUNT} {incr COUNTER} {
+ source $testfile
+ if {[info exists Leak]} {
+ lappend LeakList $Leak
+ }
+ }
+ if {$LeakList!=""} {
+ puts -nonewline memory-leak-test-$tail...
+ incr ::nTest
+ foreach x $LeakList {
+ if {$x!=[lindex $LeakList 0]} {
+ puts " failed! ($LeakList)"
+ incr ::nErr
+ lappend ::failList memory-leak-test-$tail
+ break
+ }
+ }
+ puts " Ok"
+ }
+}
+memleak_finish_test
+
+# Run the malloc tests and the misuse test after memory leak detection.
+# Both tests leak memory.
+#
+#catch {source $testdir/misuse.test}
+#catch {source $testdir/malloc.test}
+
+memleak_finish_test
diff --git a/third_party/sqlite/test/memsubsys1.test b/third_party/sqlite/test/memsubsys1.test
new file mode 100755
index 0000000..4d96df8
--- /dev/null
+++ b/third_party/sqlite/test/memsubsys1.test
@@ -0,0 +1,232 @@
+# 2008 June 18
+#
+# 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 contains tests of the memory allocation subsystem
+#
+# $Id: memsubsys1.test,v 1.8 2008/08/04 20:13:27 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+sqlite3_reset_auto_extension
+
+# This procedure constructs a new database in test.db. It fills
+# this database with many small records (enough to force multiple
+# rebalance operations in the btree-layer and to require a large
+# page cache), verifies correct results, then returns.
+#
+proc build_test_db {testname pragmas} {
+ catch {db close}
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ sqlite3_db_config_lookaside db 0 0 0
+ db eval $pragmas
+ db eval {
+ CREATE TABLE t1(x, y);
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i1 ON t1(x,y);
+ INSERT INTO t1 VALUES(1, 100);
+ INSERT INTO t1 VALUES(2, 200);
+ }
+ for {set i 2} {$i<5000} {incr i $i} {
+ db eval {INSERT INTO t2 SELECT * FROM t1}
+ db eval {INSERT INTO t1 SELECT a+$i, a+b*100 FROM t2}
+ db eval {DELETE FROM t2}
+ }
+ do_test $testname.1 {
+ db eval {SELECT count(*) FROM t1}
+ } 8192
+ integrity_check $testname.2
+}
+
+# Test 1: Both PAGECACHE and SCRATCH are shut down.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_lookaside 0 0
+sqlite3_initialize
+build_test_db memsubsys1-1 {PRAGMA page_size=1024}
+do_test memsubsys1-1.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 0
+do_test memsubsys1-1.4 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 0
+set max_pagecache [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
+#show_memstats
+
+# Test 2: Activate PAGECACHE with 20 pages
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 1024 20
+sqlite3_initialize
+build_test_db memsubsys1-2 {PRAGMA page_size=1024}
+#show_memstats
+do_test memsubsys1-2.3 {
+ set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+ expr {$pg_used*1024 + $pg_ovfl}
+} $max_pagecache
+do_test memsubsys1-2.4 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 19
+do_test memsubsys1-2.5 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 0
+
+# Test 3: Activate PAGECACHE with 20 pages but use the wrong page size
+# so that PAGECACHE is not used.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 512 20
+sqlite3_initialize
+build_test_db memsubsys1-3.1 {PRAGMA page_size=1024}
+#show_memstats
+do_test memsubsys1-3.1.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 0
+do_test memsubsys1-3.1.4 {
+ set overflow [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
+} $max_pagecache
+do_test memsubsys1-3.1.5 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 0
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 2048 20
+sqlite3_initialize
+build_test_db memsubsys1-3.2 {PRAGMA page_size=2048}
+#show_memstats
+do_test memsubsys1-3.2.3 {
+ db eval {PRAGMA page_size}
+} 2048
+do_test memsubsys1-3.2.4 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 19
+do_test memsubsys1-3.2.5 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 0
+
+# Test 4: Activate both PAGECACHE and SCRATCH.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 1024 50
+sqlite3_config_scratch 6000 2
+sqlite3_initialize
+build_test_db memsubsys1-4 {PRAGMA page_size=1024}
+#show_memstats
+do_test memsubsys1-4.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 49
+do_test memsubsys1-4.4 {
+ set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+ expr {$pg_used*1024 + $pg_ovfl}
+} $max_pagecache
+do_test memsubsys1-4.5 {
+ set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+ expr {$maxreq<7000}
+} 1
+do_test memsubsys1-4.6 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 1
+
+# Test 5: Activate both PAGECACHE and SCRATCH. But make the page size
+# such that the SCRATCH allocations are too small.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 4096 24
+sqlite3_config_scratch 6000 2
+sqlite3_initialize
+build_test_db memsubsys1-5 {PRAGMA page_size=4096}
+#show_memstats
+do_test memsubsys1-5.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 23
+do_test memsubsys1-5.4 {
+ set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+ expr {$maxreq>4096}
+} 1
+do_test memsubsys1-5.5 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 0
+do_test memsubsys1-5.6 {
+ set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
+ expr {$s_ovfl>6000}
+} 1
+
+# Test 6: Activate both PAGECACHE and SCRATCH with a 4k page size.
+# Make it so that SCRATCH is large enough
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 4096 24
+sqlite3_config_scratch 25000 1
+sqlite3_initialize
+build_test_db memsubsys1-6 {PRAGMA page_size=4096}
+#show_memstats
+do_test memsubsys1-6.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+} 23
+do_test memsubsys1-6.4 {
+ set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+} 4096
+do_test memsubsys1-6.5 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 1
+do_test memsubsys1-6.6 {
+ set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
+} 0
+
+# Test 7: Activate both PAGECACHE and SCRATCH with a 4k page size.
+# Set cache_size small so that no PAGECACHE overflow occurs. Verify
+# that maximum allocation size is small.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 4096 24
+sqlite3_config_scratch 25000 1
+sqlite3_initialize
+build_test_db memsubsys1-7 {
+ PRAGMA page_size=4096;
+ PRAGMA cache_size=10;
+ PRAGMA temp_cache_size=10;
+}
+#show_memstats
+do_test memsubsys1-7.3 {
+ set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
+ expr {$pg_used<24}
+} 1
+do_test memsubsys1-7.4 {
+ set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
+} 0
+do_test memsubsys1-7.5 {
+ set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+ expr {$maxreq<4000}
+} 1
+do_test memsubsys1-7.6 {
+ set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+} 1
+do_test memsubsys1-7.7 {
+ set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
+} 0
+
+db close
+sqlite3_shutdown
+sqlite3_config_pagecache 0 0
+sqlite3_config_scratch 0 0
+sqlite3_config_lookaside 100 500
+sqlite3_initialize
+autoinstall_test_functions
+finish_test
diff --git a/third_party/sqlite/test/memsubsys2.test b/third_party/sqlite/test/memsubsys2.test
new file mode 100755
index 0000000..3bae5da
--- /dev/null
+++ b/third_party/sqlite/test/memsubsys2.test
@@ -0,0 +1,157 @@
+# 2008 June 18
+#
+# 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 contains tests of the memory allocation subsystem.
+#
+# $Id: memsubsys2.test,v 1.1 2008/07/10 18:13:43 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+sqlite3_reset_auto_extension
+
+# This procedure constructs a new database in test.db. It fills
+# this database with many small records (enough to force multiple
+# rebalance operations in the btree-layer and to require a large
+# page cache), verifies correct results, then returns.
+#
+proc build_test_db {testname pragmas} {
+ catch {db close}
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ db eval $pragmas
+ db eval {
+ CREATE TABLE t1(x, y);
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i1 ON t1(x,y);
+ INSERT INTO t1 VALUES(1, 100);
+ INSERT INTO t1 VALUES(2, 200);
+ }
+ for {set i 2} {$i<5000} {incr i $i} {
+ db eval {INSERT INTO t2 SELECT * FROM t1}
+ db eval {INSERT INTO t1 SELECT a+$i, a+b*100 FROM t2}
+ db eval {DELETE FROM t2}
+ }
+ do_test $testname.1 {
+ db eval {SELECT count(*) FROM t1}
+ } 8192
+ integrity_check $testname.2
+}
+
+# Test 1: Verify that calling sqlite3_malloc(0) returns a NULL
+# pointer.
+#
+set highwater [sqlite3_memory_highwater 0]
+do_test memsubsys2-1.1 {
+ sqlite3_malloc 0
+} {0}
+do_test memsubsys2-1.2 {
+ sqlite3_memory_highwater 0
+} $highwater
+
+
+# Test 2: Verify that the highwater mark increases after a large
+# allocation.
+#
+sqlite3_memory_highwater 1
+set highwater [sqlite3_memory_highwater 0]
+do_test memsubsys2-2.1 {
+ sqlite3_free [set x [sqlite3_malloc 100000]]
+ expr {$x!="0"}
+} {1}
+do_test memsubsys2-2.2 {
+ expr {[sqlite3_memory_highwater 0]>=[sqlite3_memory_used]+$highwater}
+} {1}
+
+# Test 3: Verify that turning of memstatus disables the statistics
+# tracking.
+#
+db close
+sqlite3_shutdown
+sqlite3_config_memstatus 0
+sqlite3_initialize
+set highwater [sqlite3_memory_highwater 0]
+do_test memsubsys2-3.1 {
+ set highwater
+} {0}
+do_test memsubsys2-3.2 {
+ sqlite3_malloc 0
+} {0}
+do_test memsubsys2-3.3 {
+ sqlite3_memory_highwater 0
+} {0}
+do_test memsubsys2-3.4 {
+ sqlite3_memory_used
+} {0}
+do_test memsubsys2-3.5 {
+ set ::allocation [sqlite3_malloc 100000]
+ expr {$::allocation!="0"}
+} {1}
+do_test memsubsys2-3.6 {
+ sqlite3_memory_highwater 0
+} {0}
+do_test memsubsys2-3.7 {
+ sqlite3_memory_used
+} {0}
+do_test memsubsys2-3.8 {
+ sqlite3_free $::allocation
+} {}
+do_test memsubsys2-3.9 {
+ sqlite3_free 0
+} {}
+
+
+# Test 4: Verify that turning on memstatus reenables the statistics
+# tracking.
+#
+sqlite3_shutdown
+sqlite3_config_memstatus 1
+sqlite3_initialize
+set highwater [sqlite3_memory_highwater 0]
+do_test memsubsys2-4.1 {
+ set highwater
+} {0}
+do_test memsubsys2-4.2 {
+ sqlite3_malloc 0
+} {0}
+do_test memsubsys2-4.3 {
+ sqlite3_memory_highwater 0
+} {0}
+do_test memsubsys2-4.4 {
+ sqlite3_memory_used
+} {0}
+do_test memsubsys2-4.5 {
+ set ::allocation [sqlite3_malloc 100000]
+ expr {$::allocation!="0"}
+} {1}
+do_test memsubsys2-4.6 {
+ expr {[sqlite3_memory_highwater 0]>=100000}
+} {1}
+do_test memsubsys2-4.7 {
+ expr {[sqlite3_memory_used]>=100000}
+} {1}
+do_test memsubsys2-4.8 {
+ sqlite3_free $::allocation
+} {}
+do_test memsubsys2-4.9 {
+ sqlite3_free 0
+} {}
+do_test memsubsys2-4.10 {
+ expr {[sqlite3_memory_highwater 0]>=100000}
+} {1}
+do_test memsubsys2-4.11 {
+ sqlite3_memory_used
+} {0}
+
+
+
+
+autoinstall_test_functions
+finish_test
diff --git a/third_party/sqlite/test/minmax.test b/third_party/sqlite/test/minmax.test
new file mode 100755
index 0000000..5990245
--- /dev/null
+++ b/third_party/sqlite/test/minmax.test
@@ -0,0 +1,542 @@
+# 2001 September 15
+#
+# 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 file is testing SELECT statements that contain
+# aggregate min() and max() functions and which are handled as
+# as a special case.
+#
+# $Id: minmax.test,v 1.21 2008/07/08 18:05:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test minmax-1.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1,1);
+ INSERT INTO t1 VALUES(2,2);
+ INSERT INTO t1 VALUES(3,2);
+ INSERT INTO t1 VALUES(4,3);
+ INSERT INTO t1 VALUES(5,3);
+ INSERT INTO t1 VALUES(6,3);
+ INSERT INTO t1 VALUES(7,3);
+ INSERT INTO t1 VALUES(8,4);
+ INSERT INTO t1 VALUES(9,4);
+ INSERT INTO t1 VALUES(10,4);
+ INSERT INTO t1 VALUES(11,4);
+ INSERT INTO t1 VALUES(12,4);
+ INSERT INTO t1 VALUES(13,4);
+ INSERT INTO t1 VALUES(14,4);
+ INSERT INTO t1 VALUES(15,4);
+ INSERT INTO t1 VALUES(16,5);
+ INSERT INTO t1 VALUES(17,5);
+ INSERT INTO t1 VALUES(18,5);
+ INSERT INTO t1 VALUES(19,5);
+ INSERT INTO t1 VALUES(20,5);
+ COMMIT;
+ SELECT DISTINCT y FROM t1 ORDER BY y;
+ }
+} {1 2 3 4 5}
+
+do_test minmax-1.1 {
+ set sqlite_search_count 0
+ execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax-1.2 {
+ set sqlite_search_count
+} {19}
+do_test minmax-1.3 {
+ set sqlite_search_count 0
+ execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax-1.4 {
+ set sqlite_search_count
+} {19}
+do_test minmax-1.5 {
+ execsql {CREATE INDEX t1i1 ON t1(x)}
+ set sqlite_search_count 0
+ execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax-1.6 {
+ set sqlite_search_count
+} {1}
+do_test minmax-1.7 {
+ set sqlite_search_count 0
+ execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax-1.8 {
+ set sqlite_search_count
+} {0}
+do_test minmax-1.9 {
+ set sqlite_search_count 0
+ execsql {SELECT max(y) FROM t1}
+} {5}
+do_test minmax-1.10 {
+ set sqlite_search_count
+} {19}
+
+do_test minmax-1.21 {
+ execsql {SELECT min(x) FROM t1 WHERE x=5}
+} {5}
+do_test minmax-1.22 {
+ execsql {SELECT min(x) FROM t1 WHERE x>=5}
+} {5}
+do_test minmax-1.23 {
+ execsql {SELECT min(x) FROM t1 WHERE x>=4.5}
+} {5}
+do_test minmax-1.24 {
+ execsql {SELECT min(x) FROM t1 WHERE x<4.5}
+} {1}
+
+do_test minmax-2.0 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+ set sqlite_search_count 0
+ execsql {SELECT min(a) FROM t2}
+} {1}
+do_test minmax-2.1 {
+ set sqlite_search_count
+} {0}
+do_test minmax-2.2 {
+ set sqlite_search_count 0
+ execsql {SELECT max(a) FROM t2}
+} {20}
+do_test minmax-2.3 {
+ set sqlite_search_count
+} {0}
+
+do_test minmax-3.0 {
+ ifcapable subquery {
+ execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+ } else {
+ db function max_a_t2 {execsql {SELECT max(a) FROM t2}}
+ execsql {INSERT INTO t2 VALUES(max_a_t2()+1,999)}
+ }
+ set sqlite_search_count 0
+ execsql {SELECT max(a) FROM t2}
+} {21}
+do_test minmax-3.1 {
+ set sqlite_search_count
+} {0}
+do_test minmax-3.2 {
+ ifcapable subquery {
+ execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+ } else {
+ db function max_a_t2 {execsql {SELECT max(a) FROM t2}}
+ execsql {INSERT INTO t2 VALUES(max_a_t2()+1,999)}
+ }
+ set sqlite_search_count 0
+ ifcapable subquery {
+ execsql { SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) }
+ } else {
+ execsql { SELECT b FROM t2 WHERE a=max_a_t2() }
+ }
+} {999}
+do_test minmax-3.3 {
+ set sqlite_search_count
+} {0}
+
+ifcapable {compound && subquery} {
+ do_test minmax-4.1 {
+ execsql {
+ SELECT coalesce(min(x+0),-1), coalesce(max(x+0),-1) FROM
+ (SELECT * FROM t1 UNION SELECT NULL as 'x', NULL as 'y')
+ }
+ } {1 20}
+ do_test minmax-4.2 {
+ execsql {
+ SELECT y, coalesce(sum(x),0) FROM
+ (SELECT null AS x, y+1 AS y FROM t1 UNION SELECT * FROM t1)
+ GROUP BY y ORDER BY y;
+ }
+ } {1 1 2 5 3 22 4 92 5 90 6 0}
+ do_test minmax-4.3 {
+ execsql {
+ SELECT y, count(x), count(*) FROM
+ (SELECT null AS x, y+1 AS y FROM t1 UNION SELECT * FROM t1)
+ GROUP BY y ORDER BY y;
+ }
+ } {1 1 1 2 2 3 3 4 5 4 8 9 5 5 6 6 0 1}
+} ;# ifcapable compound
+
+# Make sure the min(x) and max(x) optimizations work on empty tables
+# including empty tables with indices. Ticket #296.
+#
+do_test minmax-5.1 {
+ execsql {
+ CREATE TABLE t3(x INTEGER UNIQUE NOT NULL);
+ SELECT coalesce(min(x),999) FROM t3;
+ }
+} {999}
+do_test minmax-5.2 {
+ execsql {
+ SELECT coalesce(min(rowid),999) FROM t3;
+ }
+} {999}
+do_test minmax-5.3 {
+ execsql {
+ SELECT coalesce(max(x),999) FROM t3;
+ }
+} {999}
+do_test minmax-5.4 {
+ execsql {
+ SELECT coalesce(max(rowid),999) FROM t3;
+ }
+} {999}
+do_test minmax-5.5 {
+ execsql {
+ SELECT coalesce(max(rowid),999) FROM t3 WHERE rowid<25;
+ }
+} {999}
+
+# Make sure the min(x) and max(x) optimizations work when there
+# is a LIMIT clause. Ticket #396.
+#
+do_test minmax-6.1 {
+ execsql {
+ SELECT min(a) FROM t2 LIMIT 1
+ }
+} {1}
+do_test minmax-6.2 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 3
+ }
+} {22}
+do_test minmax-6.3 {
+ execsql {
+ SELECT min(a) FROM t2 LIMIT 0,100
+ }
+} {1}
+do_test minmax-6.4 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 1,100
+ }
+} {}
+do_test minmax-6.5 {
+ execsql {
+ SELECT min(x) FROM t3 LIMIT 1
+ }
+} {{}}
+do_test minmax-6.6 {
+ execsql {
+ SELECT max(x) FROM t3 LIMIT 0
+ }
+} {}
+do_test minmax-6.7 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 0
+ }
+} {}
+
+# Make sure the max(x) and min(x) optimizations work for nested
+# queries. Ticket #587.
+#
+do_test minmax-7.1 {
+ execsql {
+ SELECT max(x) FROM t1;
+ }
+} 20
+ifcapable subquery {
+ do_test minmax-7.2 {
+ execsql {
+ SELECT * FROM (SELECT max(x) FROM t1);
+ }
+ } 20
+}
+do_test minmax-7.3 {
+ execsql {
+ SELECT min(x) FROM t1;
+ }
+} 1
+ifcapable subquery {
+ do_test minmax-7.4 {
+ execsql {
+ SELECT * FROM (SELECT min(x) FROM t1);
+ }
+ } 1
+}
+
+# Make sure min(x) and max(x) work correctly when the datatype is
+# TEXT instead of NUMERIC. Ticket #623.
+#
+do_test minmax-8.1 {
+ execsql {
+ CREATE TABLE t4(a TEXT);
+ INSERT INTO t4 VALUES('1234');
+ INSERT INTO t4 VALUES('234');
+ INSERT INTO t4 VALUES('34');
+ SELECT min(a), max(a) FROM t4;
+ }
+} {1234 34}
+do_test minmax-8.2 {
+ execsql {
+ CREATE TABLE t5(a INTEGER);
+ INSERT INTO t5 VALUES('1234');
+ INSERT INTO t5 VALUES('234');
+ INSERT INTO t5 VALUES('34');
+ SELECT min(a), max(a) FROM t5;
+ }
+} {34 1234}
+
+# Ticket #658: Test the min()/max() optimization when the FROM clause
+# is a subquery.
+#
+ifcapable {compound && subquery} {
+ do_test minmax-9.1 {
+ execsql {
+ SELECT max(rowid) FROM (
+ SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5
+ )
+ }
+ } {1}
+ do_test minmax-9.2 {
+ execsql {
+ SELECT max(rowid) FROM (
+ SELECT max(rowid) FROM t4 EXCEPT SELECT max(rowid) FROM t5
+ )
+ }
+ } {{}}
+} ;# ifcapable compound&&subquery
+
+# If there is a NULL in an aggregate max() or min(), ignore it. An
+# aggregate min() or max() will only return NULL if all values are NULL.
+#
+do_test minmax-10.1 {
+ execsql {
+ CREATE TABLE t6(x);
+ INSERT INTO t6 VALUES(1);
+ INSERT INTO t6 VALUES(2);
+ INSERT INTO t6 VALUES(NULL);
+ SELECT coalesce(min(x),-1) FROM t6;
+ }
+} {1}
+do_test minmax-10.2 {
+ execsql {
+ SELECT max(x) FROM t6;
+ }
+} {2}
+do_test minmax-10.3 {
+ execsql {
+ CREATE INDEX i6 ON t6(x);
+ SELECT coalesce(min(x),-1) FROM t6;
+ }
+} {1}
+do_test minmax-10.4 {
+ execsql {
+ SELECT max(x) FROM t6;
+ }
+} {2}
+do_test minmax-10.5 {
+ execsql {
+ DELETE FROM t6 WHERE x NOT NULL;
+ SELECT count(*) FROM t6;
+ }
+} 1
+do_test minmax-10.6 {
+ execsql {
+ SELECT count(x) FROM t6;
+ }
+} 0
+ifcapable subquery {
+ do_test minmax-10.7 {
+ execsql {
+ SELECT (SELECT min(x) FROM t6), (SELECT max(x) FROM t6);
+ }
+ } {{} {}}
+}
+do_test minmax-10.8 {
+ execsql {
+ SELECT min(x), max(x) FROM t6;
+ }
+} {{} {}}
+do_test minmax-10.9 {
+ execsql {
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ SELECT count(*) FROM t6;
+ }
+} 1024
+do_test minmax-10.10 {
+ execsql {
+ SELECT count(x) FROM t6;
+ }
+} 0
+ifcapable subquery {
+ do_test minmax-10.11 {
+ execsql {
+ SELECT (SELECT min(x) FROM t6), (SELECT max(x) FROM t6);
+ }
+ } {{} {}}
+}
+do_test minmax-10.12 {
+ execsql {
+ SELECT min(x), max(x) FROM t6;
+ }
+} {{} {}}
+
+
+do_test minmax-11.1 {
+ execsql {
+ CREATE INDEX t1i2 ON t1(y,x);
+ SELECT min(x) FROM t1 WHERE y=5;
+ }
+} {16}
+do_test minmax-11.2 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE y=5;
+ }
+} {20}
+do_test minmax-11.3 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE y=6;
+ }
+} {{}}
+do_test minmax-11.4 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE y=6;
+ }
+} {{}}
+do_test minmax-11.5 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE y=1;
+ }
+} {1}
+do_test minmax-11.6 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE y=1;
+ }
+} {1}
+do_test minmax-11.7 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE y=0;
+ }
+} {{}}
+do_test minmax-11.8 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE y=0;
+ }
+} {{}}
+do_test minmax-11.9 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE y=5 AND x>=17.5;
+ }
+} {18}
+do_test minmax-11.10 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE y=5 AND x>=17.5;
+ }
+} {20}
+
+do_test minmax-12.1 {
+ execsql {
+ CREATE TABLE t7(a,b,c);
+ INSERT INTO t7 SELECT y, x, x*y FROM t1;
+ INSERT INTO t7 SELECT y, x, x*y+1000 FROM t1;
+ CREATE INDEX t7i1 ON t7(a,b,c);
+ SELECT min(a) FROM t7;
+ }
+} {1}
+do_test minmax-12.2 {
+ execsql {
+ SELECT max(a) FROM t7;
+ }
+} {5}
+do_test minmax-12.3 {
+ execsql {
+ SELECT max(a) FROM t7 WHERE a=5;
+ }
+} {5}
+do_test minmax-12.4 {
+ execsql {
+ SELECT min(b) FROM t7 WHERE a=5;
+ }
+} {16}
+do_test minmax-12.5 {
+ execsql {
+ SELECT max(b) FROM t7 WHERE a=5;
+ }
+} {20}
+do_test minmax-12.6 {
+ execsql {
+ SELECT min(b) FROM t7 WHERE a=4;
+ }
+} {8}
+do_test minmax-12.7 {
+ execsql {
+ SELECT max(b) FROM t7 WHERE a=4;
+ }
+} {15}
+do_test minmax-12.8 {
+ execsql {
+ SELECT min(c) FROM t7 WHERE a=4 AND b=10;
+ }
+} {40}
+do_test minmax-12.9 {
+ execsql {
+ SELECT max(c) FROM t7 WHERE a=4 AND b=10;
+ }
+} {1040}
+do_test minmax-12.10 {
+ execsql {
+ SELECT min(rowid) FROM t7;
+ }
+} {1}
+do_test minmax-12.11 {
+ execsql {
+ SELECT max(rowid) FROM t7;
+ }
+} {40}
+do_test minmax-12.12 {
+ execsql {
+ SELECT min(rowid) FROM t7 WHERE a=3;
+ }
+} {4}
+do_test minmax-12.13 {
+ execsql {
+ SELECT max(rowid) FROM t7 WHERE a=3;
+ }
+} {27}
+do_test minmax-12.14 {
+ execsql {
+ SELECT min(rowid) FROM t7 WHERE a=3 AND b=5;
+ }
+} {5}
+do_test minmax-12.15 {
+ execsql {
+ SELECT max(rowid) FROM t7 WHERE a=3 AND b=5;
+ }
+} {25}
+do_test minmax-12.16 {
+ execsql {
+ SELECT min(rowid) FROM t7 WHERE a=3 AND b=5 AND c=1015;
+ }
+} {25}
+do_test minmax-12.17 {
+ execsql {
+ SELECT max(rowid) FROM t7 WHERE a=3 AND b=5 AND c=15;
+ }
+} {5}
+
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/minmax2.test b/third_party/sqlite/test/minmax2.test
new file mode 100755
index 0000000..2f504d4
--- /dev/null
+++ b/third_party/sqlite/test/minmax2.test
@@ -0,0 +1,387 @@
+# 2007 July 17
+#
+# 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 file is testing SELECT statements that contain
+# aggregate min() and max() functions and which are handled as
+# as a special case. This file makes sure that the min/max
+# optimization works right in the presence of descending
+# indices. Ticket #2514.
+#
+# $Id: minmax2.test,v 1.2 2008/01/05 17:39:30 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test minmax2-1.0 {
+ execsql {
+ PRAGMA legacy_file_format=0;
+ BEGIN;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1,1);
+ INSERT INTO t1 VALUES(2,2);
+ INSERT INTO t1 VALUES(3,2);
+ INSERT INTO t1 VALUES(4,3);
+ INSERT INTO t1 VALUES(5,3);
+ INSERT INTO t1 VALUES(6,3);
+ INSERT INTO t1 VALUES(7,3);
+ INSERT INTO t1 VALUES(8,4);
+ INSERT INTO t1 VALUES(9,4);
+ INSERT INTO t1 VALUES(10,4);
+ INSERT INTO t1 VALUES(11,4);
+ INSERT INTO t1 VALUES(12,4);
+ INSERT INTO t1 VALUES(13,4);
+ INSERT INTO t1 VALUES(14,4);
+ INSERT INTO t1 VALUES(15,4);
+ INSERT INTO t1 VALUES(16,5);
+ INSERT INTO t1 VALUES(17,5);
+ INSERT INTO t1 VALUES(18,5);
+ INSERT INTO t1 VALUES(19,5);
+ INSERT INTO t1 VALUES(20,5);
+ COMMIT;
+ SELECT DISTINCT y FROM t1 ORDER BY y;
+ }
+} {1 2 3 4 5}
+
+do_test minmax2-1.1 {
+ set sqlite_search_count 0
+ execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax2-1.2 {
+ set sqlite_search_count
+} {19}
+do_test minmax2-1.3 {
+ set sqlite_search_count 0
+ execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax2-1.4 {
+ set sqlite_search_count
+} {19}
+do_test minmax2-1.5 {
+ execsql {CREATE INDEX t1i1 ON t1(x DESC)}
+ set sqlite_search_count 0
+ execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax2-1.6 {
+ set sqlite_search_count
+} {1}
+do_test minmax2-1.7 {
+ set sqlite_search_count 0
+ execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax2-1.8 {
+ set sqlite_search_count
+} {0}
+do_test minmax2-1.9 {
+ set sqlite_search_count 0
+ execsql {SELECT max(y) FROM t1}
+} {5}
+do_test minmax2-1.10 {
+ set sqlite_search_count
+} {19}
+
+do_test minmax2-2.0 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+ set sqlite_search_count 0
+ execsql {SELECT min(a) FROM t2}
+} {1}
+do_test minmax2-2.1 {
+ set sqlite_search_count
+} {0}
+do_test minmax2-2.2 {
+ set sqlite_search_count 0
+ execsql {SELECT max(a) FROM t2}
+} {20}
+do_test minmax2-2.3 {
+ set sqlite_search_count
+} {0}
+
+do_test minmax2-3.0 {
+ ifcapable subquery {
+ execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+ } else {
+ db function max_a_t2 {execsql {SELECT max(a) FROM t2}}
+ execsql {INSERT INTO t2 VALUES(max_a_t2()+1,999)}
+ }
+ set sqlite_search_count 0
+ execsql {SELECT max(a) FROM t2}
+} {21}
+do_test minmax2-3.1 {
+ set sqlite_search_count
+} {0}
+do_test minmax2-3.2 {
+ ifcapable subquery {
+ execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+ } else {
+ db function max_a_t2 {execsql {SELECT max(a) FROM t2}}
+ execsql {INSERT INTO t2 VALUES(max_a_t2()+1,999)}
+ }
+ set sqlite_search_count 0
+ ifcapable subquery {
+ execsql { SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2) }
+ } else {
+ execsql { SELECT b FROM t2 WHERE a=max_a_t2() }
+ }
+} {999}
+do_test minmax2-3.3 {
+ set sqlite_search_count
+} {0}
+
+ifcapable {compound && subquery} {
+ do_test minmax2-4.1 {
+ execsql {
+ SELECT coalesce(min(x+0),-1), coalesce(max(x+0),-1) FROM
+ (SELECT * FROM t1 UNION SELECT NULL as 'x', NULL as 'y')
+ }
+ } {1 20}
+ do_test minmax2-4.2 {
+ execsql {
+ SELECT y, coalesce(sum(x),0) FROM
+ (SELECT null AS x, y+1 AS y FROM t1 UNION SELECT * FROM t1)
+ GROUP BY y ORDER BY y;
+ }
+ } {1 1 2 5 3 22 4 92 5 90 6 0}
+ do_test minmax2-4.3 {
+ execsql {
+ SELECT y, count(x), count(*) FROM
+ (SELECT null AS x, y+1 AS y FROM t1 UNION SELECT * FROM t1)
+ GROUP BY y ORDER BY y;
+ }
+ } {1 1 1 2 2 3 3 4 5 4 8 9 5 5 6 6 0 1}
+} ;# ifcapable compound
+
+# Make sure the min(x) and max(x) optimizations work on empty tables
+# including empty tables with indices. Ticket #296.
+#
+do_test minmax2-5.1 {
+ execsql {
+ CREATE TABLE t3(x INTEGER UNIQUE NOT NULL);
+ SELECT coalesce(min(x),999) FROM t3;
+ }
+} {999}
+do_test minmax2-5.2 {
+ execsql {
+ SELECT coalesce(min(rowid),999) FROM t3;
+ }
+} {999}
+do_test minmax2-5.3 {
+ execsql {
+ SELECT coalesce(max(x),999) FROM t3;
+ }
+} {999}
+do_test minmax2-5.4 {
+ execsql {
+ SELECT coalesce(max(rowid),999) FROM t3;
+ }
+} {999}
+do_test minmax2-5.5 {
+ execsql {
+ SELECT coalesce(max(rowid),999) FROM t3 WHERE rowid<25;
+ }
+} {999}
+
+# Make sure the min(x) and max(x) optimizations work when there
+# is a LIMIT clause. Ticket #396.
+#
+do_test minmax2-6.1 {
+ execsql {
+ SELECT min(a) FROM t2 LIMIT 1
+ }
+} {1}
+do_test minmax2-6.2 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 3
+ }
+} {22}
+do_test minmax2-6.3 {
+ execsql {
+ SELECT min(a) FROM t2 LIMIT 0,100
+ }
+} {1}
+do_test minmax2-6.4 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 1,100
+ }
+} {}
+do_test minmax2-6.5 {
+ execsql {
+ SELECT min(x) FROM t3 LIMIT 1
+ }
+} {{}}
+do_test minmax2-6.6 {
+ execsql {
+ SELECT max(x) FROM t3 LIMIT 0
+ }
+} {}
+do_test minmax2-6.7 {
+ execsql {
+ SELECT max(a) FROM t2 LIMIT 0
+ }
+} {}
+
+# Make sure the max(x) and min(x) optimizations work for nested
+# queries. Ticket #587.
+#
+do_test minmax2-7.1 {
+ execsql {
+ SELECT max(x) FROM t1;
+ }
+} 20
+ifcapable subquery {
+ do_test minmax2-7.2 {
+ execsql {
+ SELECT * FROM (SELECT max(x) FROM t1);
+ }
+ } 20
+}
+do_test minmax2-7.3 {
+ execsql {
+ SELECT min(x) FROM t1;
+ }
+} 1
+ifcapable subquery {
+ do_test minmax2-7.4 {
+ execsql {
+ SELECT * FROM (SELECT min(x) FROM t1);
+ }
+ } 1
+}
+
+# Make sure min(x) and max(x) work correctly when the datatype is
+# TEXT instead of NUMERIC. Ticket #623.
+#
+do_test minmax2-8.1 {
+ execsql {
+ CREATE TABLE t4(a TEXT);
+ INSERT INTO t4 VALUES('1234');
+ INSERT INTO t4 VALUES('234');
+ INSERT INTO t4 VALUES('34');
+ SELECT min(a), max(a) FROM t4;
+ }
+} {1234 34}
+do_test minmax2-8.2 {
+ execsql {
+ CREATE TABLE t5(a INTEGER);
+ INSERT INTO t5 VALUES('1234');
+ INSERT INTO t5 VALUES('234');
+ INSERT INTO t5 VALUES('34');
+ SELECT min(a), max(a) FROM t5;
+ }
+} {34 1234}
+
+# Ticket #658: Test the min()/max() optimization when the FROM clause
+# is a subquery.
+#
+ifcapable {compound && subquery} {
+ do_test minmax2-9.1 {
+ execsql {
+ SELECT max(rowid) FROM (
+ SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5
+ )
+ }
+ } {1}
+ do_test minmax2-9.2 {
+ execsql {
+ SELECT max(rowid) FROM (
+ SELECT max(rowid) FROM t4 EXCEPT SELECT max(rowid) FROM t5
+ )
+ }
+ } {{}}
+} ;# ifcapable compound&&subquery
+
+# If there is a NULL in an aggregate max() or min(), ignore it. An
+# aggregate min() or max() will only return NULL if all values are NULL.
+#
+do_test minmax2-10.1 {
+ execsql {
+ CREATE TABLE t6(x);
+ INSERT INTO t6 VALUES(1);
+ INSERT INTO t6 VALUES(2);
+ INSERT INTO t6 VALUES(NULL);
+ SELECT coalesce(min(x),-1) FROM t6;
+ }
+} {1}
+do_test minmax2-10.2 {
+ execsql {
+ SELECT max(x) FROM t6;
+ }
+} {2}
+do_test minmax2-10.3 {
+ execsql {
+ CREATE INDEX i6 ON t6(x DESC);
+ SELECT coalesce(min(x),-1) FROM t6;
+ }
+} {1}
+do_test minmax2-10.4 {
+ execsql {
+ SELECT max(x) FROM t6;
+ }
+} {2}
+do_test minmax2-10.5 {
+ execsql {
+ DELETE FROM t6 WHERE x NOT NULL;
+ SELECT count(*) FROM t6;
+ }
+} 1
+do_test minmax2-10.6 {
+ execsql {
+ SELECT count(x) FROM t6;
+ }
+} 0
+ifcapable subquery {
+ do_test minmax2-10.7 {
+ execsql {
+ SELECT (SELECT min(x) FROM t6), (SELECT max(x) FROM t6);
+ }
+ } {{} {}}
+}
+do_test minmax2-10.8 {
+ execsql {
+ SELECT min(x), max(x) FROM t6;
+ }
+} {{} {}}
+do_test minmax2-10.9 {
+ execsql {
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ SELECT count(*) FROM t6;
+ }
+} 1024
+do_test minmax2-10.10 {
+ execsql {
+ SELECT count(x) FROM t6;
+ }
+} 0
+ifcapable subquery {
+ do_test minmax2-10.11 {
+ execsql {
+ SELECT (SELECT min(x) FROM t6), (SELECT max(x) FROM t6);
+ }
+ } {{} {}}
+}
+do_test minmax2-10.12 {
+ execsql {
+ SELECT min(x), max(x) FROM t6;
+ }
+} {{} {}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/minmax3.test b/third_party/sqlite/test/minmax3.test
new file mode 100755
index 0000000..8e8164a
--- /dev/null
+++ b/third_party/sqlite/test/minmax3.test
@@ -0,0 +1,217 @@
+# 2008 January 5
+#
+# 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.
+#
+#***********************************************************************
+# $Id: minmax3.test,v 1.5 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# This procedure sets the value of the file-format in file 'test.db'
+# to $newval. Also, the schema cookie is incremented.
+#
+proc set_file_format {newval} {
+ hexio_write test.db 44 [hexio_render_int32 $newval]
+ set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
+ incr schemacookie
+ hexio_write test.db 40 [hexio_render_int32 $schemacookie]
+ return {}
+}
+
+do_test minmax3-1.0 {
+ execsql {
+ CREATE TABLE t1(x, y, z);
+ }
+ db close
+ set_file_format 4
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES('1', 'I', 'one');
+ INSERT INTO t1 VALUES('2', 'IV', 'four');
+ INSERT INTO t1 VALUES('2', NULL, 'three');
+ INSERT INTO t1 VALUES('2', 'II', 'two');
+ INSERT INTO t1 VALUES('2', 'V', 'five');
+ INSERT INTO t1 VALUES('3', 'VI', 'six');
+ COMMIT;
+ }
+} {}
+do_test minmax3-1.1.1 {
+ # Linear scan.
+ count { SELECT max(y) FROM t1 WHERE x = '2'; }
+} {V 5}
+do_test minmax3-1.1.2 {
+ # Index optimizes the WHERE x='2' constraint.
+ execsql { CREATE INDEX i1 ON t1(x) }
+ count { SELECT max(y) FROM t1 WHERE x = '2'; }
+} {V 9}
+do_test minmax3-1.1.3 {
+ # Index optimizes the WHERE x='2' constraint and the MAX(y).
+ execsql { CREATE INDEX i2 ON t1(x,y) }
+ count { SELECT max(y) FROM t1 WHERE x = '2'; }
+} {V 1}
+do_test minmax3-1.1.4 {
+ # Index optimizes the WHERE x='2' constraint and the MAX(y).
+ execsql { DROP INDEX i2 ; CREATE INDEX i2 ON t1(x, y DESC) }
+ count { SELECT max(y) FROM t1 WHERE x = '2'; }
+} {V 1}
+do_test minmax3-1.1.5 {
+ count { SELECT max(y) FROM t1 WHERE x = '2' AND y != 'V'; }
+} {IV 2}
+do_test minmax3-1.1.6 {
+ count { SELECT max(y) FROM t1 WHERE x = '2' AND y < 'V'; }
+} {IV 1}
+do_test minmax3-1.1.6 {
+ count { SELECT max(y) FROM t1 WHERE x = '2' AND z != 'five'; }
+} {IV 4}
+
+do_test minmax3-1.2.1 {
+ # Linear scan of t1.
+ execsql { DROP INDEX i1 ; DROP INDEX i2 }
+ count { SELECT min(y) FROM t1 WHERE x = '2'; }
+} {II 5}
+do_test minmax3-1.2.2 {
+ # Index i1 optimizes the WHERE x='2' constraint.
+ execsql { CREATE INDEX i1 ON t1(x) }
+ count { SELECT min(y) FROM t1 WHERE x = '2'; }
+} {II 9}
+do_test minmax3-1.2.3 {
+ # Index i2 optimizes the WHERE x='2' constraint and the min(y).
+ execsql { CREATE INDEX i2 ON t1(x,y) }
+ count { SELECT min(y) FROM t1 WHERE x = '2'; }
+} {II 1}
+do_test minmax3-1.2.4 {
+ # Index optimizes the WHERE x='2' constraint and the MAX(y).
+ execsql { DROP INDEX i2 ; CREATE INDEX i2 ON t1(x, y DESC) }
+ count { SELECT min(y) FROM t1 WHERE x = '2'; }
+} {II 1}
+
+do_test minmax3-1.3.1 {
+ # Linear scan
+ execsql { DROP INDEX i1 ; DROP INDEX i2 }
+ count { SELECT min(y) FROM t1; }
+} {I 5}
+do_test minmax3-1.3.2 {
+ # Index i1 optimizes the min(y)
+ execsql { CREATE INDEX i1 ON t1(y) }
+ count { SELECT min(y) FROM t1; }
+} {I 1}
+do_test minmax3-1.3.3 {
+ # Index i1 optimizes the min(y)
+ execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(y DESC) }
+ count { SELECT min(y) FROM t1; }
+} {I 1}
+
+do_test minmax3-1.4.1 {
+ # Linear scan
+ execsql { DROP INDEX i1 }
+ count { SELECT max(y) FROM t1; }
+} {VI 5}
+do_test minmax3-1.4.2 {
+ # Index i1 optimizes the max(y)
+ execsql { CREATE INDEX i1 ON t1(y) }
+ count { SELECT max(y) FROM t1; }
+} {VI 0}
+do_test minmax3-1.4.3 {
+ # Index i1 optimizes the max(y)
+ execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(y DESC) }
+ execsql { SELECT y from t1}
+ count { SELECT max(y) FROM t1; }
+} {VI 0}
+do_test minmax3-1.4.4 {
+ execsql { DROP INDEX i1 }
+} {}
+
+do_test minmax3-2.1 {
+ execsql {
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i3 ON t2(a, b);
+ INSERT INTO t2 VALUES(1, NULL);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(1, 2);
+ INSERT INTO t2 VALUES(1, 3);
+ INSERT INTO t2 VALUES(2, NULL);
+ INSERT INTO t2 VALUES(2, 1);
+ INSERT INTO t2 VALUES(2, 2);
+ INSERT INTO t2 VALUES(2, 3);
+ INSERT INTO t2 VALUES(3, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(3, 3);
+ }
+} {}
+do_test minmax3-2.2 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1; }
+} {1}
+do_test minmax3-2.3 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b>1; }
+} {2}
+do_test minmax3-2.4 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b>-1; }
+} {1}
+do_test minmax3-2.5 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1; }
+} {1}
+do_test minmax3-2.6 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b<2; }
+} {1}
+do_test minmax3-2.7 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b<1; }
+} {{}}
+do_test minmax3-2.8 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 3 AND b<1; }
+} {{}}
+
+do_test minmax3-2.1 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i3 ON t2(a, b DESC);
+ INSERT INTO t2 VALUES(1, NULL);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(1, 2);
+ INSERT INTO t2 VALUES(1, 3);
+ INSERT INTO t2 VALUES(2, NULL);
+ INSERT INTO t2 VALUES(2, 1);
+ INSERT INTO t2 VALUES(2, 2);
+ INSERT INTO t2 VALUES(2, 3);
+ INSERT INTO t2 VALUES(3, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(3, 3);
+ }
+} {}
+do_test minmax3-2.2 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1; }
+} {1}
+do_test minmax3-2.3 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b>1; }
+} {2}
+do_test minmax3-2.4 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b>-1; }
+} {1}
+do_test minmax3-2.5 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1; }
+} {1}
+do_test minmax3-2.6 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b<2; }
+} {1}
+do_test minmax3-2.7 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 1 AND b<1; }
+} {{}}
+do_test minmax3-2.8 {
+ execsql { SELECT min(b) FROM t2 WHERE a = 3 AND b<1; }
+} {{}}
+
+finish_test
diff --git a/third_party/sqlite/test/misc1.test b/third_party/sqlite/test/misc1.test
new file mode 100755
index 0000000..7f93686
--- /dev/null
+++ b/third_party/sqlite/test/misc1.test
@@ -0,0 +1,585 @@
+# 2001 September 15.
+#
+# 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.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc1.test,v 1.42 2007/11/05 14:58:23 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Mimic the SQLite 2 collation type NUMERIC.
+db collate numeric numeric_collate
+proc numeric_collate {lhs rhs} {
+ if {$lhs == $rhs} {return 0}
+ return [expr ($lhs>$rhs)?1:-1]
+}
+
+# Mimic the SQLite 2 collation type TEXT.
+db collate text text_collate
+proc numeric_collate {lhs rhs} {
+ return [string compare $lhs $rhs]
+}
+
+# Test the creation and use of tables that have a large number
+# of columns.
+#
+do_test misc1-1.1 {
+ set cmd "CREATE TABLE manycol(x0 text"
+ for {set i 1} {$i<=99} {incr i} {
+ append cmd ",x$i text"
+ }
+ append cmd ")";
+ execsql $cmd
+ set cmd "INSERT INTO manycol VALUES(0"
+ for {set i 1} {$i<=99} {incr i} {
+ append cmd ",$i"
+ }
+ append cmd ")";
+ execsql $cmd
+ execsql "SELECT x99 FROM manycol"
+} 99
+do_test misc1-1.2 {
+ execsql {SELECT x0, x10, x25, x50, x75 FROM manycol}
+} {0 10 25 50 75}
+do_test misc1-1.3.1 {
+ for {set j 100} {$j<=1000} {incr j 100} {
+ set cmd "INSERT INTO manycol VALUES($j"
+ for {set i 1} {$i<=99} {incr i} {
+ append cmd ",[expr {$i+$j}]"
+ }
+ append cmd ")"
+ execsql $cmd
+ }
+ execsql {SELECT x50 FROM manycol ORDER BY x80+0}
+} {50 150 250 350 450 550 650 750 850 950 1050}
+do_test misc1-1.3.2 {
+ execsql {SELECT x50 FROM manycol ORDER BY x80}
+} {1050 150 250 350 450 550 650 750 50 850 950}
+do_test misc1-1.4 {
+ execsql {SELECT x75 FROM manycol WHERE x50=350}
+} 375
+do_test misc1-1.5 {
+ execsql {SELECT x50 FROM manycol WHERE x99=599}
+} 550
+do_test misc1-1.6 {
+ execsql {CREATE INDEX manycol_idx1 ON manycol(x99)}
+ execsql {SELECT x50 FROM manycol WHERE x99=899}
+} 850
+do_test misc1-1.7 {
+ execsql {SELECT count(*) FROM manycol}
+} 11
+do_test misc1-1.8 {
+ execsql {DELETE FROM manycol WHERE x98=1234}
+ execsql {SELECT count(*) FROM manycol}
+} 11
+do_test misc1-1.9 {
+ execsql {DELETE FROM manycol WHERE x98=998}
+ execsql {SELECT count(*) FROM manycol}
+} 10
+do_test misc1-1.10 {
+ execsql {DELETE FROM manycol WHERE x99=500}
+ execsql {SELECT count(*) FROM manycol}
+} 10
+do_test misc1-1.11 {
+ execsql {DELETE FROM manycol WHERE x99=599}
+ execsql {SELECT count(*) FROM manycol}
+} 9
+
+# Check GROUP BY expressions that name two or more columns.
+#
+do_test misc1-2.1 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE agger(one text, two text, three text, four text);
+ INSERT INTO agger VALUES(1, 'one', 'hello', 'yes');
+ INSERT INTO agger VALUES(2, 'two', 'howdy', 'no');
+ INSERT INTO agger VALUES(3, 'thr', 'howareya', 'yes');
+ INSERT INTO agger VALUES(4, 'two', 'lothere', 'yes');
+ INSERT INTO agger VALUES(5, 'one', 'atcha', 'yes');
+ INSERT INTO agger VALUES(6, 'two', 'hello', 'no');
+ COMMIT
+ }
+ execsql {SELECT count(*) FROM agger}
+} 6
+do_test misc1-2.2 {
+ execsql {SELECT sum(one), two, four FROM agger
+ GROUP BY two, four ORDER BY sum(one) desc}
+} {8 two no 6 one yes 4 two yes 3 thr yes}
+do_test misc1-2.3 {
+ execsql {SELECT sum((one)), (two), (four) FROM agger
+ GROUP BY (two), (four) ORDER BY sum(one) desc}
+} {8 two no 6 one yes 4 two yes 3 thr yes}
+
+# Here's a test for a bug found by Joel Lucsy. The code below
+# was causing an assertion failure.
+#
+do_test misc1-3.1 {
+ set r [execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES('hi');
+ PRAGMA full_column_names=on;
+ SELECT rowid, * FROM t1;
+ }]
+ lindex $r 1
+} {hi}
+
+# Here's a test for yet another bug found by Joel Lucsy. The code
+# below was causing an assertion failure.
+#
+do_test misc1-4.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES('This is a long string to use up a lot of disk -');
+ UPDATE t2 SET a=a||a||a||a;
+ INSERT INTO t2 SELECT '1 - ' || a FROM t2;
+ INSERT INTO t2 SELECT '2 - ' || a FROM t2;
+ INSERT INTO t2 SELECT '3 - ' || a FROM t2;
+ INSERT INTO t2 SELECT '4 - ' || a FROM t2;
+ INSERT INTO t2 SELECT '5 - ' || a FROM t2;
+ INSERT INTO t2 SELECT '6 - ' || a FROM t2;
+ COMMIT;
+ SELECT count(*) FROM t2;
+ }
+} {64}
+
+# Make sure we actually see a semicolon or end-of-file in the SQL input
+# before executing a command. Thus if "WHERE" is misspelled on an UPDATE,
+# the user won't accidently update every record.
+#
+do_test misc1-5.1 {
+ catchsql {
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES(1,2);
+ INSERT INTO t3 VALUES(3,4);
+ UPDATE t3 SET a=0 WHEREwww b=2;
+ }
+} {1 {near "WHEREwww": syntax error}}
+do_test misc1-5.2 {
+ execsql {
+ SELECT * FROM t3 ORDER BY a;
+ }
+} {1 2 3 4}
+
+# Certain keywords (especially non-standard keywords like "REPLACE") can
+# also be used as identifiers. The way this works in the parser is that
+# the parser first detects a syntax error, the error handling routine
+# sees that the special keyword caused the error, then replaces the keyword
+# with "ID" and tries again.
+#
+# Check the operation of this logic.
+#
+do_test misc1-6.1 {
+ catchsql {
+ CREATE TABLE t4(
+ abort, asc, begin, cluster, conflict, copy, delimiters, desc, end,
+ explain, fail, ignore, key, offset, pragma, replace, temp,
+ vacuum, view
+ );
+ }
+} {0 {}}
+do_test misc1-6.2 {
+ catchsql {
+ INSERT INTO t4
+ VALUES(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
+ }
+} {0 {}}
+do_test misc1-6.3 {
+ execsql {
+ SELECT * FROM t4
+ }
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19}
+do_test misc1-6.4 {
+ execsql {
+ SELECT abort+asc,max(key,pragma,temp) FROM t4
+ }
+} {3 17}
+
+# Test for multi-column primary keys, and for multiple primary keys.
+#
+do_test misc1-7.1 {
+ catchsql {
+ CREATE TABLE error1(
+ a TYPE PRIMARY KEY,
+ b TYPE PRIMARY KEY
+ );
+ }
+} {1 {table "error1" has more than one primary key}}
+do_test misc1-7.2 {
+ catchsql {
+ CREATE TABLE error1(
+ a INTEGER PRIMARY KEY,
+ b TYPE PRIMARY KEY
+ );
+ }
+} {1 {table "error1" has more than one primary key}}
+do_test misc1-7.3 {
+ execsql {
+ CREATE TABLE t5(a,b,c,PRIMARY KEY(a,b));
+ INSERT INTO t5 VALUES(1,2,3);
+ SELECT * FROM t5 ORDER BY a;
+ }
+} {1 2 3}
+do_test misc1-7.4 {
+ catchsql {
+ INSERT INTO t5 VALUES(1,2,4);
+ }
+} {1 {columns a, b are not unique}}
+do_test misc1-7.5 {
+ catchsql {
+ INSERT INTO t5 VALUES(0,2,4);
+ }
+} {0 {}}
+do_test misc1-7.6 {
+ execsql {
+ SELECT * FROM t5 ORDER BY a;
+ }
+} {0 2 4 1 2 3}
+
+do_test misc1-8.1 {
+ catchsql {
+ SELECT *;
+ }
+} {1 {no tables specified}}
+do_test misc1-8.2 {
+ catchsql {
+ SELECT t1.*;
+ }
+} {1 {no such table: t1}}
+
+execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP TABLE t3;
+ DROP TABLE t4;
+}
+
+# 64-bit integers are represented exactly.
+#
+do_test misc1-9.1 {
+ catchsql {
+ CREATE TABLE t1(a unique not null, b unique not null);
+ INSERT INTO t1 VALUES('a',1234567890123456789);
+ INSERT INTO t1 VALUES('b',1234567891123456789);
+ INSERT INTO t1 VALUES('c',1234567892123456789);
+ SELECT * FROM t1;
+ }
+} {0 {a 1234567890123456789 b 1234567891123456789 c 1234567892123456789}}
+
+# A WHERE clause is not allowed to contain more than 99 terms. Check to
+# make sure this limit is enforced.
+#
+# 2005-07-16: There is no longer a limit on the number of terms in a
+# WHERE clause. But keep these tests just so that we have some tests
+# that use a large number of terms in the WHERE clause.
+#
+do_test misc1-10.0 {
+ execsql {SELECT count(*) FROM manycol}
+} {9}
+do_test misc1-10.1 {
+ set ::where {WHERE x0>=0}
+ for {set i 1} {$i<=99} {incr i} {
+ append ::where " AND x$i<>0"
+ }
+ catchsql "SELECT count(*) FROM manycol $::where"
+} {0 9}
+do_test misc1-10.2 {
+ catchsql "SELECT count(*) FROM manycol $::where AND rowid>0"
+} {0 9}
+do_test misc1-10.3 {
+ regsub "x0>=0" $::where "x0=0" ::where
+ catchsql "DELETE FROM manycol $::where"
+} {0 {}}
+do_test misc1-10.4 {
+ execsql {SELECT count(*) FROM manycol}
+} {8}
+do_test misc1-10.5 {
+ catchsql "DELETE FROM manycol $::where AND rowid>0"
+} {0 {}}
+do_test misc1-10.6 {
+ execsql {SELECT x1 FROM manycol WHERE x0=100}
+} {101}
+do_test misc1-10.7 {
+ regsub "x0=0" $::where "x0=100" ::where
+ catchsql "UPDATE manycol SET x1=x1+1 $::where"
+} {0 {}}
+do_test misc1-10.8 {
+ execsql {SELECT x1 FROM manycol WHERE x0=100}
+} {102}
+do_test misc1-10.9 {
+ catchsql "UPDATE manycol SET x1=x1+1 $::where AND rowid>0"
+} {0 {}}
+do_test misc1-10.10 {
+ execsql {SELECT x1 FROM manycol WHERE x0=100}
+} {103}
+
+# Make sure the initialization works even if a database is opened while
+# another process has the database locked.
+#
+# Update for v3: The BEGIN doesn't lock the database so the schema is read
+# and the SELECT returns successfully.
+do_test misc1-11.1 {
+ execsql {BEGIN}
+ execsql {UPDATE t1 SET a=0 WHERE 0}
+ sqlite3 db2 test.db
+ set rc [catch {db2 eval {SELECT count(*) FROM t1}} msg]
+ lappend rc $msg
+# v2 result: {1 {database is locked}}
+} {0 3}
+do_test misc1-11.2 {
+ execsql {COMMIT}
+ set rc [catch {db2 eval {SELECT count(*) FROM t1}} msg]
+ db2 close
+ lappend rc $msg
+} {0 3}
+
+# Make sure string comparisons really do compare strings in format4+.
+# Similar tests in the format3.test file show that for format3 and earlier
+# all comparisions where numeric if either operand looked like a number.
+#
+do_test misc1-12.1 {
+ execsql {SELECT '0'=='0.0'}
+} {0}
+do_test misc1-12.2 {
+ execsql {SELECT '0'==0.0}
+} {0}
+do_test misc1-12.3 {
+ execsql {SELECT '12345678901234567890'=='12345678901234567891'}
+} {0}
+do_test misc1-12.4 {
+ execsql {
+ CREATE TABLE t6(a INT UNIQUE, b TEXT UNIQUE);
+ INSERT INTO t6 VALUES('0','0.0');
+ SELECT * FROM t6;
+ }
+} {0 0.0}
+ifcapable conflict {
+ do_test misc1-12.5 {
+ execsql {
+ INSERT OR IGNORE INTO t6 VALUES(0.0,'x');
+ SELECT * FROM t6;
+ }
+ } {0 0.0}
+ do_test misc1-12.6 {
+ execsql {
+ INSERT OR IGNORE INTO t6 VALUES('y',0);
+ SELECT * FROM t6;
+ }
+ } {0 0.0 y 0}
+}
+do_test misc1-12.7 {
+ execsql {
+ CREATE TABLE t7(x INTEGER, y TEXT, z);
+ INSERT INTO t7 VALUES(0,0,1);
+ INSERT INTO t7 VALUES(0.0,0,2);
+ INSERT INTO t7 VALUES(0,0.0,3);
+ INSERT INTO t7 VALUES(0.0,0.0,4);
+ SELECT DISTINCT x, y FROM t7 ORDER BY z;
+ }
+} {0 0 0 0.0}
+do_test misc1-12.8 {
+ execsql {
+ SELECT min(z), max(z), count(z) FROM t7 GROUP BY x ORDER BY 1;
+ }
+} {1 4 4}
+do_test misc1-12.9 {
+ execsql {
+ SELECT min(z), max(z), count(z) FROM t7 GROUP BY y ORDER BY 1;
+ }
+} {1 2 2 3 4 2}
+
+# This used to be an error. But we changed the code so that arbitrary
+# identifiers can be used as a collating sequence. Collation is by text
+# if the identifier contains "text", "blob", or "clob" and is numeric
+# otherwise.
+#
+# Update: In v3, it is an error again.
+#
+#do_test misc1-12.10 {
+# catchsql {
+# SELECT * FROM t6 ORDER BY a COLLATE unknown;
+# }
+#} {0 {0 0 y 0}}
+do_test misc1-12.11 {
+ execsql {
+ CREATE TABLE t8(x TEXT COLLATE numeric, y INTEGER COLLATE text, z);
+ INSERT INTO t8 VALUES(0,0,1);
+ INSERT INTO t8 VALUES(0.0,0,2);
+ INSERT INTO t8 VALUES(0,0.0,3);
+ INSERT INTO t8 VALUES(0.0,0.0,4);
+ SELECT DISTINCT x, y FROM t8 ORDER BY z;
+ }
+} {0 0 0.0 0}
+do_test misc1-12.12 {
+ execsql {
+ SELECT min(z), max(z), count(z) FROM t8 GROUP BY x ORDER BY 1;
+ }
+} {1 3 2 2 4 2}
+do_test misc1-12.13 {
+ execsql {
+ SELECT min(z), max(z), count(z) FROM t8 GROUP BY y ORDER BY 1;
+ }
+} {1 4 4}
+
+# There was a problem with realloc() in the OP_MemStore operation of
+# the VDBE. A buffer was being reallocated but some pointers into
+# the old copy of the buffer were not being moved over to the new copy.
+# The following code tests for the problem.
+#
+ifcapable subquery {
+ do_test misc1-13.1 {
+ execsql {
+ CREATE TABLE t9(x,y);
+ INSERT INTO t9 VALUES('one',1);
+ INSERT INTO t9 VALUES('two',2);
+ INSERT INTO t9 VALUES('three',3);
+ INSERT INTO t9 VALUES('four',4);
+ INSERT INTO t9 VALUES('five',5);
+ INSERT INTO t9 VALUES('six',6);
+ INSERT INTO t9 VALUES('seven',7);
+ INSERT INTO t9 VALUES('eight',8);
+ INSERT INTO t9 VALUES('nine',9);
+ INSERT INTO t9 VALUES('ten',10);
+ INSERT INTO t9 VALUES('eleven',11);
+ SELECT y FROM t9
+ WHERE x=(SELECT x FROM t9 WHERE y=1)
+ OR x=(SELECT x FROM t9 WHERE y=2)
+ OR x=(SELECT x FROM t9 WHERE y=3)
+ OR x=(SELECT x FROM t9 WHERE y=4)
+ OR x=(SELECT x FROM t9 WHERE y=5)
+ OR x=(SELECT x FROM t9 WHERE y=6)
+ OR x=(SELECT x FROM t9 WHERE y=7)
+ OR x=(SELECT x FROM t9 WHERE y=8)
+ OR x=(SELECT x FROM t9 WHERE y=9)
+ OR x=(SELECT x FROM t9 WHERE y=10)
+ OR x=(SELECT x FROM t9 WHERE y=11)
+ OR x=(SELECT x FROM t9 WHERE y=12)
+ OR x=(SELECT x FROM t9 WHERE y=13)
+ OR x=(SELECT x FROM t9 WHERE y=14)
+ ;
+ }
+ } {1 2 3 4 5 6 7 8 9 10 11}
+}
+
+# Make sure a database connection still works after changing the
+# working directory.
+#
+do_test misc1-14.1 {
+ file mkdir tempdir
+ cd tempdir
+ execsql {BEGIN}
+ file exists ./test.db-journal
+} {0}
+do_test misc1-14.2 {
+ execsql {UPDATE t1 SET a=0 WHERE 0}
+ file exists ../test.db-journal
+} {1}
+do_test misc1-14.3 {
+ cd ..
+ file delete -force tempdir
+ execsql {COMMIT}
+ file exists ./test.db-journal
+} {0}
+
+# A failed create table should not leave the table in the internal
+# data structures. Ticket #238.
+#
+do_test misc1-15.1.1 {
+ catchsql {
+ CREATE TABLE t10 AS SELECT c1;
+ }
+} {1 {no such column: c1}}
+do_test misc1-15.1.2 {
+ catchsql {
+ CREATE TABLE t10 AS SELECT t9.c1;
+ }
+} {1 {no such column: t9.c1}}
+do_test misc1-15.1.3 {
+ catchsql {
+ CREATE TABLE t10 AS SELECT main.t9.c1;
+ }
+} {1 {no such column: main.t9.c1}}
+do_test misc1-15.2 {
+ catchsql {
+ CREATE TABLE t10 AS SELECT 1;
+ }
+ # The bug in ticket #238 causes the statement above to fail with
+ # the error "table t10 alread exists"
+} {0 {}}
+
+# Test for memory leaks when a CREATE TABLE containing a primary key
+# fails. Ticket #249.
+#
+do_test misc1-16.1 {
+ catchsql {SELECT name FROM sqlite_master LIMIT 1}
+ catchsql {
+ CREATE TABLE test(a integer, primary key(a));
+ }
+} {0 {}}
+do_test misc1-16.2 {
+ catchsql {
+ CREATE TABLE test(a integer, primary key(a));
+ }
+} {1 {table test already exists}}
+do_test misc1-16.3 {
+ catchsql {
+ CREATE TABLE test2(a text primary key, b text, primary key(a,b));
+ }
+} {1 {table "test2" has more than one primary key}}
+do_test misc1-16.4 {
+ execsql {
+ INSERT INTO test VALUES(1);
+ SELECT rowid, a FROM test;
+ }
+} {1 1}
+do_test misc1-16.5 {
+ execsql {
+ INSERT INTO test VALUES(5);
+ SELECT rowid, a FROM test;
+ }
+} {1 1 5 5}
+do_test misc1-16.6 {
+ execsql {
+ INSERT INTO test VALUES(NULL);
+ SELECT rowid, a FROM test;
+ }
+} {1 1 5 5 6 6}
+
+ifcapable trigger&&tempdb {
+# Ticket #333: Temp triggers that modify persistent tables.
+#
+do_test misc1-17.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE RealTable(TestID INTEGER PRIMARY KEY, TestString TEXT);
+ CREATE TEMP TABLE TempTable(TestID INTEGER PRIMARY KEY, TestString TEXT);
+ CREATE TEMP TRIGGER trigTest_1 AFTER UPDATE ON TempTable BEGIN
+ INSERT INTO RealTable(TestString)
+ SELECT new.TestString FROM TempTable LIMIT 1;
+ END;
+ INSERT INTO TempTable(TestString) VALUES ('1');
+ INSERT INTO TempTable(TestString) VALUES ('2');
+ UPDATE TempTable SET TestString = TestString + 1 WHERE TestID=1 OR TestId=2;
+ COMMIT;
+ SELECT TestString FROM RealTable ORDER BY 1;
+ }
+} {2 3}
+}
+
+do_test misc1-18.1 {
+ set n [sqlite3_sleep 100]
+ expr {$n>=100}
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/misc2.test b/third_party/sqlite/test/misc2.test
new file mode 100755
index 0000000..e9d85ed
--- /dev/null
+++ b/third_party/sqlite/test/misc2.test
@@ -0,0 +1,435 @@
+# 2003 June 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc2.test,v 1.28 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {trigger} {
+# Test for ticket #360
+#
+do_test misc2-1.1 {
+ catchsql {
+ CREATE TABLE FOO(bar integer);
+ CREATE TRIGGER foo_insert BEFORE INSERT ON foo BEGIN
+ SELECT CASE WHEN (NOT new.bar BETWEEN 0 AND 20)
+ THEN raise(rollback, 'aiieee') END;
+ END;
+ INSERT INTO foo(bar) VALUES (1);
+ }
+} {0 {}}
+do_test misc2-1.2 {
+ catchsql {
+ INSERT INTO foo(bar) VALUES (111);
+ }
+} {1 aiieee}
+} ;# endif trigger
+
+# Make sure ROWID works on a view and a subquery. Ticket #364
+#
+do_test misc2-2.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ CREATE TABLE t2(a,b,c);
+ INSERT INTO t2 VALUES(7,8,9);
+ }
+} {}
+ifcapable subquery {
+ do_test misc2-2.2 {
+ execsql {
+ SELECT rowid, * FROM (SELECT * FROM t1, t2);
+ }
+ } {{} 1 2 3 7 8 9}
+}
+ifcapable view {
+ do_test misc2-2.3 {
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM t1, t2;
+ SELECT rowid, * FROM v1;
+ }
+ } {{} 1 2 3 7 8 9}
+} ;# ifcapable view
+
+# Ticket #2002 and #1952.
+ifcapable subquery {
+ do_test misc2-2.4 {
+ execsql2 {
+ SELECT * FROM (SELECT a, b AS 'a', c AS 'a', 4 AS 'a' FROM t1)
+ }
+ } {a 1 a:1 2 a:2 3 a:3 4}
+}
+
+# Check name binding precedence. Ticket #387
+#
+do_test misc2-3.1 {
+ catchsql {
+ SELECT t1.b+t2.b AS a, t1.a, t2.a FROM t1, t2 WHERE a==10
+ }
+} {1 {ambiguous column name: a}}
+
+# Make sure 32-bit integer overflow is handled properly in queries.
+# ticket #408
+#
+do_test misc2-4.1 {
+ execsql {
+ INSERT INTO t1 VALUES(4000000000,'a','b');
+ SELECT a FROM t1 WHERE a>1;
+ }
+} {4000000000}
+do_test misc2-4.2 {
+ execsql {
+ INSERT INTO t1 VALUES(2147483648,'b2','c2');
+ INSERT INTO t1 VALUES(2147483647,'b3','c3');
+ SELECT a FROM t1 WHERE a>2147483647;
+ }
+} {4000000000 2147483648}
+do_test misc2-4.3 {
+ execsql {
+ SELECT a FROM t1 WHERE a<2147483648;
+ }
+} {1 2147483647}
+do_test misc2-4.4 {
+ execsql {
+ SELECT a FROM t1 WHERE a<=2147483648;
+ }
+} {1 2147483648 2147483647}
+do_test misc2-4.5 {
+ execsql {
+ SELECT a FROM t1 WHERE a<10000000000;
+ }
+} {1 4000000000 2147483648 2147483647}
+do_test misc2-4.6 {
+ execsql {
+ SELECT a FROM t1 WHERE a<1000000000000 ORDER BY 1;
+ }
+} {1 2147483647 2147483648 4000000000}
+
+# There were some issues with expanding a SrcList object using a call
+# to sqliteSrcListAppend() if the SrcList had previously been duplicated
+# using a call to sqliteSrcListDup(). Ticket #416. The following test
+# makes sure the problem has been fixed.
+#
+ifcapable view {
+do_test misc2-5.1 {
+ execsql {
+ CREATE TABLE x(a,b);
+ CREATE VIEW y AS
+ SELECT x1.b AS p, x2.b AS q FROM x AS x1, x AS x2 WHERE x1.a=x2.a;
+ CREATE VIEW z AS
+ SELECT y1.p, y2.p FROM y AS y1, y AS y2 WHERE y1.q=y2.q;
+ SELECT * from z;
+ }
+} {}
+}
+
+# Make sure we can open a database with an empty filename. What this
+# does is store the database in a temporary file that is deleted when
+# the database is closed. Ticket #432.
+#
+do_test misc2-6.1 {
+ db close
+ sqlite3 db {}
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t1;
+ }
+} {1 2}
+
+# Make sure we get an error message (not a segfault) on an attempt to
+# update a table from within the callback of a select on that same
+# table.
+#
+# 2006-08-16: This has changed. It is now permitted to update
+# the table being SELECTed from within the callback of the query.
+#
+ifcapable tclvar {
+ do_test misc2-7.1 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ SELECT * FROM t1;
+ }
+ } {1 2 3}
+ do_test misc2-7.2 {
+ set rc [catch {
+ db eval {SELECT rowid FROM t1} {} {
+ db eval "DELETE FROM t1 WHERE rowid=$rowid"
+ }
+ } msg]
+ lappend rc $msg
+ } {0 {}}
+ do_test misc2-7.3 {
+ execsql {SELECT * FROM t1}
+ } {}
+ do_test misc2-7.4 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1 WHERE rowid=$rowid}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {2 4}
+ do_test misc2-7.5 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1 WHERE rowid=$rowid+1}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {1 3}
+ do_test misc2-7.6 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {}
+ do_test misc2-7.7 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x & 1} {
+ db eval {UPDATE t1 SET x=x+100 WHERE rowid=$rowid}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {101 2 103 4}
+ do_test misc2-7.8 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x<10} {
+ db eval {INSERT INTO t1 VALUES($x+1)}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {1 2 3 4 5 6 7 8 9 10}
+
+ # Repeat the tests 7.1 through 7.8 about but this time do the SELECTs
+ # in reverse order so that we exercise the sqlite3BtreePrev() routine
+ # instead of sqlite3BtreeNext()
+ #
+ do_test misc2-7.11 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ SELECT * FROM t1;
+ }
+ } {1 2 3}
+ do_test misc2-7.12 {
+ set rc [catch {
+ db eval {SELECT rowid FROM t1 ORDER BY rowid DESC} {} {
+ db eval "DELETE FROM t1 WHERE rowid=$rowid"
+ }
+ } msg]
+ lappend rc $msg
+ } {0 {}}
+ do_test misc2-7.13 {
+ execsql {SELECT * FROM t1}
+ } {}
+ do_test misc2-7.14 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1 ORDER BY rowid DESC} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1 WHERE rowid=$rowid}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {2 4}
+ do_test misc2-7.15 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1 WHERE rowid=$rowid+1}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {1 3}
+ do_test misc2-7.16 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1 ORDER BY rowid DESC} {
+ if {$x & 1} {
+ db eval {DELETE FROM t1}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {}
+ do_test misc2-7.17 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ }
+ db eval {SELECT rowid, x FROM t1 ORDER BY rowid DESC} {
+ if {$x & 1} {
+ db eval {UPDATE t1 SET x=x+100 WHERE rowid=$rowid}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {101 2 103 4}
+ do_test misc2-7.18 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1(rowid,x) VALUES(10,10);
+ }
+ db eval {SELECT rowid, x FROM t1 ORDER BY rowid DESC} {
+ if {$x>1} {
+ db eval {INSERT INTO t1(rowid,x) VALUES($x-1,$x-1)}
+ }
+ }
+ execsql {SELECT * FROM t1}
+ } {1 2 3 4 5 6 7 8 9 10}
+}
+
+db close
+file delete -force test.db
+sqlite3 db test.db
+
+# Ticket #453. If the SQL ended with "-", the tokenizer was calling that
+# an incomplete token, which caused problem. The solution was to just call
+# it a minus sign.
+#
+do_test misc2-8.1 {
+ catchsql {-}
+} {1 {near "-": syntax error}}
+
+# Ticket #513. Make sure the VDBE stack does not grow on a 3-way join.
+#
+ifcapable tempdb {
+ do_test misc2-9.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE counts(n INTEGER PRIMARY KEY);
+ INSERT INTO counts VALUES(0);
+ INSERT INTO counts VALUES(1);
+ INSERT INTO counts SELECT n+2 FROM counts;
+ INSERT INTO counts SELECT n+4 FROM counts;
+ INSERT INTO counts SELECT n+8 FROM counts;
+ COMMIT;
+
+ CREATE TEMP TABLE x AS
+ SELECT dim1.n, dim2.n, dim3.n
+ FROM counts AS dim1, counts AS dim2, counts AS dim3
+ WHERE dim1.n<10 AND dim2.n<10 AND dim3.n<10;
+
+ SELECT count(*) FROM x;
+ }
+ } {1000}
+ do_test misc2-9.2 {
+ execsql {
+ DROP TABLE x;
+ CREATE TEMP TABLE x AS
+ SELECT dim1.n, dim2.n, dim3.n
+ FROM counts AS dim1, counts AS dim2, counts AS dim3
+ WHERE dim1.n>=6 AND dim2.n>=6 AND dim3.n>=6;
+
+ SELECT count(*) FROM x;
+ }
+ } {1000}
+ do_test misc2-9.3 {
+ execsql {
+ DROP TABLE x;
+ CREATE TEMP TABLE x AS
+ SELECT dim1.n, dim2.n, dim3.n, dim4.n
+ FROM counts AS dim1, counts AS dim2, counts AS dim3, counts AS dim4
+ WHERE dim1.n<5 AND dim2.n<5 AND dim3.n<5 AND dim4.n<5;
+
+ SELECT count(*) FROM x;
+ }
+ } [expr 5*5*5*5]
+}
+
+# Ticket #1229. Sometimes when a "NEW.X" appears in a SELECT without
+# a FROM clause deep within a trigger, the code generator is unable to
+# trace the NEW.X back to an original table and thus figure out its
+# declared datatype.
+#
+# The SQL code below was causing a segfault.
+#
+ifcapable subquery&&trigger {
+ do_test misc2-10.1 {
+ execsql {
+ CREATE TABLE t1229(x);
+ CREATE TRIGGER r1229 BEFORE INSERT ON t1229 BEGIN
+ INSERT INTO t1229 SELECT y FROM (SELECT new.x y);
+ END;
+ INSERT INTO t1229 VALUES(1);
+ }
+ } {}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/misc3.test b/third_party/sqlite/test/misc3.test
new file mode 100755
index 0000000..1534aaf
--- /dev/null
+++ b/third_party/sqlite/test/misc3.test
@@ -0,0 +1,341 @@
+# 2003 December 17
+#
+# 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.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc3.test,v 1.19 2008/06/25 02:47:57 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {integrityck} {
+ # Ticket #529. Make sure an ABORT does not damage the in-memory cache
+ # that will be used by subsequent statements in the same transaction.
+ #
+ do_test misc3-1.1 {
+ execsql {
+ CREATE TABLE t1(a UNIQUE,b);
+ INSERT INTO t1
+ VALUES(1,'a23456789_b23456789_c23456789_d23456789_e23456789_');
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ UPDATE t1 SET b=b||b;
+ INSERT INTO t1 VALUES(2,'x');
+ UPDATE t1 SET b=substr(b,1,500);
+ BEGIN;
+ }
+ catchsql {UPDATE t1 SET a=CASE a WHEN 2 THEN 1 ELSE a END, b='y';}
+ execsql {
+ CREATE TABLE t2(x,y);
+ COMMIT;
+ PRAGMA integrity_check;
+ }
+ } ok
+}
+ifcapable {integrityck} {
+ do_test misc3-1.2 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ }
+ ifcapable {vacuum} {execsql VACUUM}
+ execsql {
+ CREATE TABLE t1(a UNIQUE,b);
+ INSERT INTO t1
+ VALUES(1,'a23456789_b23456789_c23456789_d23456789_e23456789_');
+ INSERT INTO t1 SELECT a+1, b||b FROM t1;
+ INSERT INTO t1 SELECT a+2, b||b FROM t1;
+ INSERT INTO t1 SELECT a+4, b FROM t1;
+ INSERT INTO t1 SELECT a+8, b FROM t1;
+ INSERT INTO t1 SELECT a+16, b FROM t1;
+ INSERT INTO t1 SELECT a+32, b FROM t1;
+ INSERT INTO t1 SELECT a+64, b FROM t1;
+ BEGIN;
+ }
+ catchsql {UPDATE t1 SET a=CASE a WHEN 128 THEN 127 ELSE a END, b='';}
+ execsql {
+ INSERT INTO t1 VALUES(200,'hello out there');
+ COMMIT;
+ PRAGMA integrity_check;
+ }
+ } ok
+}
+
+# Tests of the sqliteAtoF() function in util.c
+#
+do_test misc3-2.1 {
+ execsql {SELECT 2e-25*0.5e25}
+} 1.0
+do_test misc3-2.2 {
+ execsql {SELECT 2.0e-25*000000.500000000000000000000000000000e+00025}
+} 1.0
+do_test misc3-2.3 {
+ execsql {SELECT 000000000002e-0000000025*0.5e25}
+} 1.0
+do_test misc3-2.4 {
+ execsql {SELECT 2e-25*0.5e250}
+} 1e+225
+do_test misc3-2.5 {
+ execsql {SELECT 2.0e-250*0.5e25}
+} 1e-225
+do_test misc3-2.6 {
+ execsql {SELECT '-2.0e-127' * '-0.5e27'}
+} 1e-100
+do_test misc3-2.7 {
+ execsql {SELECT '+2.0e-127' * '-0.5e27'}
+} -1e-100
+do_test misc3-2.8 {
+ execsql {SELECT 2.0e-27 * '+0.5e+127'}
+} 1e+100
+do_test misc3-2.9 {
+ execsql {SELECT 2.0e-27 * '+0.000005e+132'}
+} 1e+100
+
+# Ticket #522. Make sure integer overflow is handled properly in
+# indices.
+#
+integrity_check misc3-3.1
+do_test misc3-3.2 {
+ execsql {
+ CREATE TABLE t2(a INT UNIQUE);
+ }
+} {}
+integrity_check misc3-3.2.1
+do_test misc3-3.3 {
+ execsql {
+ INSERT INTO t2 VALUES(2147483648);
+ }
+} {}
+integrity_check misc3-3.3.1
+do_test misc3-3.4 {
+ execsql {
+ INSERT INTO t2 VALUES(-2147483649);
+ }
+} {}
+integrity_check misc3-3.4.1
+do_test misc3-3.5 {
+ execsql {
+ INSERT INTO t2 VALUES(+2147483649);
+ }
+} {}
+integrity_check misc3-3.5.1
+do_test misc3-3.6 {
+ execsql {
+ INSERT INTO t2 VALUES(+2147483647);
+ INSERT INTO t2 VALUES(-2147483648);
+ INSERT INTO t2 VALUES(-2147483647);
+ INSERT INTO t2 VALUES(2147483646);
+ SELECT * FROM t2 ORDER BY a;
+ }
+} {-2147483649 -2147483648 -2147483647 2147483646 2147483647 2147483648 2147483649}
+do_test misc3-3.7 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=-2147483648 ORDER BY a;
+ }
+} {-2147483648 -2147483647 2147483646 2147483647 2147483648 2147483649}
+do_test misc3-3.8 {
+ execsql {
+ SELECT * FROM t2 WHERE a>-2147483648 ORDER BY a;
+ }
+} {-2147483647 2147483646 2147483647 2147483648 2147483649}
+do_test misc3-3.9 {
+ execsql {
+ SELECT * FROM t2 WHERE a>-2147483649 ORDER BY a;
+ }
+} {-2147483648 -2147483647 2147483646 2147483647 2147483648 2147483649}
+do_test misc3-3.10 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=0 AND a<2147483649 ORDER BY a DESC;
+ }
+} {2147483648 2147483647 2147483646}
+do_test misc3-3.11 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=0 AND a<=2147483648 ORDER BY a DESC;
+ }
+} {2147483648 2147483647 2147483646}
+do_test misc3-3.12 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=0 AND a<2147483648 ORDER BY a DESC;
+ }
+} {2147483647 2147483646}
+do_test misc3-3.13 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=0 AND a<=2147483647 ORDER BY a DESC;
+ }
+} {2147483647 2147483646}
+do_test misc3-3.14 {
+ execsql {
+ SELECT * FROM t2 WHERE a>=0 AND a<2147483647 ORDER BY a DESC;
+ }
+} {2147483646}
+
+# Ticket #565. A stack overflow is occurring when the subquery to the
+# right of an IN operator contains many NULLs
+#
+do_test misc3-4.1 {
+ execsql {
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t3(b) VALUES('abc');
+ INSERT INTO t3(b) VALUES('xyz');
+ INSERT INTO t3(b) VALUES(NULL);
+ INSERT INTO t3(b) VALUES(NULL);
+ INSERT INTO t3(b) SELECT b||'d' FROM t3;
+ INSERT INTO t3(b) SELECT b||'e' FROM t3;
+ INSERT INTO t3(b) SELECT b||'f' FROM t3;
+ INSERT INTO t3(b) SELECT b||'g' FROM t3;
+ INSERT INTO t3(b) SELECT b||'h' FROM t3;
+ SELECT count(a), count(b) FROM t3;
+ }
+} {128 64}
+ifcapable subquery {
+do_test misc3-4.2 {
+ execsql {
+ SELECT count(a) FROM t3 WHERE b IN (SELECT b FROM t3);
+ }
+ } {64}
+ do_test misc3-4.3 {
+ execsql {
+ SELECT count(a) FROM t3 WHERE b IN (SELECT b FROM t3 ORDER BY a+1);
+ }
+ } {64}
+}
+
+# Ticket #601: Putting a left join inside "SELECT * FROM (<join-here>)"
+# gives different results that if the outer "SELECT * FROM ..." is omitted.
+#
+ifcapable subquery {
+ do_test misc3-5.1 {
+ execsql {
+ CREATE TABLE x1 (b, c);
+ INSERT INTO x1 VALUES('dog',3);
+ INSERT INTO x1 VALUES('cat',1);
+ INSERT INTO x1 VALUES('dog',4);
+ CREATE TABLE x2 (c, e);
+ INSERT INTO x2 VALUES(1,'one');
+ INSERT INTO x2 VALUES(2,'two');
+ INSERT INTO x2 VALUES(3,'three');
+ INSERT INTO x2 VALUES(4,'four');
+ SELECT x2.c AS c, e, b FROM x2 LEFT JOIN
+ (SELECT b, max(c)+0 AS c FROM x1 GROUP BY b)
+ USING(c);
+ }
+ } {1 one cat 2 two {} 3 three {} 4 four dog}
+ do_test misc3-5.2 {
+ execsql {
+ SELECT * FROM (
+ SELECT x2.c AS c, e, b FROM x2 LEFT JOIN
+ (SELECT b, max(c)+0 AS c FROM x1 GROUP BY b)
+ USING(c)
+ );
+ }
+ } {1 one cat 2 two {} 3 three {} 4 four dog}
+}
+
+ifcapable {explain} {
+ # Ticket #626: make sure EXPLAIN prevents BEGIN and COMMIT from working.
+ #
+ do_test misc3-6.1 {
+ execsql {EXPLAIN BEGIN}
+ catchsql {BEGIN}
+ } {0 {}}
+ do_test misc3-6.2 {
+ execsql {EXPLAIN COMMIT}
+ catchsql {COMMIT}
+ } {0 {}}
+ do_test misc3-6.3 {
+ execsql {BEGIN; EXPLAIN ROLLBACK}
+ catchsql {ROLLBACK}
+ } {0 {}}
+
+ # Do some additional EXPLAIN operations to exercise the displayP4 logic.
+ do_test misc3-6.10 {
+ set x [execsql {
+ CREATE TABLE ex1(
+ a INTEGER DEFAULT 54321,
+ b TEXT DEFAULT "hello",
+ c REAL DEFAULT 3.1415926
+ );
+ CREATE UNIQUE INDEX ex1i1 ON ex1(a);
+ EXPLAIN REINDEX;
+ }]
+ regexp { IsUnique \d+ \d+ \d+ \d+ } $x
+ } {1}
+ do_test misc3-6.11 {
+ set x [execsql {
+ EXPLAIN SELECT a+123456789012, b*4.5678, c FROM ex1 ORDER BY +a, b DESC
+ }]
+ set y [regexp { 123456789012 } $x]
+ lappend y [regexp { 4.5678 } $x]
+ lappend y [regexp { hello } $x]
+ lappend y [regexp {,-BINARY} $x]
+ } {1 1 1 1}
+
+}
+
+ifcapable {trigger} {
+# Ticket #640: vdbe stack overflow with a LIMIT clause on a SELECT inside
+# of a trigger.
+#
+do_test misc3-7.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE y1(a);
+ CREATE TABLE y2(b);
+ CREATE TABLE y3(c);
+ CREATE TRIGGER r1 AFTER DELETE ON y1 FOR EACH ROW BEGIN
+ INSERT INTO y3(c) SELECT b FROM y2 ORDER BY b LIMIT 1;
+ END;
+ INSERT INTO y1 VALUES(1);
+ INSERT INTO y1 VALUES(2);
+ INSERT INTO y1 SELECT a+2 FROM y1;
+ INSERT INTO y1 SELECT a+4 FROM y1;
+ INSERT INTO y1 SELECT a+8 FROM y1;
+ INSERT INTO y1 SELECT a+16 FROM y1;
+ INSERT INTO y2 SELECT a FROM y1;
+ COMMIT;
+ SELECT count(*) FROM y1;
+ }
+} 32
+do_test misc3-7.2 {
+ execsql {
+ DELETE FROM y1;
+ SELECT count(*) FROM y1;
+ }
+} 0
+do_test misc3-7.3 {
+ execsql {
+ SELECT count(*) FROM y3;
+ }
+} 32
+} ;# endif trigger
+
+# Ticket #668: VDBE stack overflow occurs when the left-hand side
+# of an IN expression is NULL and the result is used as an integer, not
+# as a jump.
+#
+ifcapable subquery {
+ do_test misc-8.1 {
+ execsql {
+ SELECT count(CASE WHEN b IN ('abc','xyz') THEN 'x' END) FROM t3
+ }
+ } {2}
+ do_test misc-8.2 {
+ execsql {
+ SELECT count(*) FROM t3 WHERE 1+(b IN ('abc','xyz'))==2
+ }
+ } {2}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/misc4.test b/third_party/sqlite/test/misc4.test
new file mode 100755
index 0000000..026dd03
--- /dev/null
+++ b/third_party/sqlite/test/misc4.test
@@ -0,0 +1,211 @@
+# 2004 Jun 27
+#
+# 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.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc4.test,v 1.23 2007/12/08 18:01:31 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Prepare a statement that will create a temporary table. Then do
+# a rollback. Then try to execute the prepared statement.
+#
+do_test misc4-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ }
+} {}
+
+ifcapable tempdb {
+ do_test misc4-1.2 {
+ set sql {CREATE TEMP TABLE t2 AS SELECT * FROM t1}
+ set stmt [sqlite3_prepare $DB $sql -1 TAIL]
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(a,b,c);
+ INSERT INTO t1 SELECT * FROM t1;
+ ROLLBACK;
+ }
+ } {}
+
+ # Because the previous transaction included a DDL statement and
+ # was rolled back, statement $stmt was marked as expired. Executing it
+ # now returns SQLITE_SCHEMA.
+ do_test misc4-1.2.1 {
+ list [sqlite3_step $stmt] [sqlite3_finalize $stmt]
+ } {SQLITE_ERROR SQLITE_SCHEMA}
+ do_test misc4-1.2.2 {
+ set stmt [sqlite3_prepare $DB $sql -1 TAIL]
+ set TAIL
+ } {}
+
+ do_test misc4-1.3 {
+ sqlite3_step $stmt
+ } SQLITE_DONE
+ do_test misc4-1.4 {
+ execsql {
+ SELECT * FROM temp.t2;
+ }
+ } {1}
+
+ # Drop the temporary table, then rerun the prepared statement to
+ # recreate it again. This recreates ticket #807.
+ #
+ do_test misc4-1.5 {
+ execsql {DROP TABLE t2}
+ sqlite3_reset $stmt
+ sqlite3_step $stmt
+ } {SQLITE_ERROR}
+ do_test misc4-1.6 {
+ sqlite3_finalize $stmt
+ } {SQLITE_SCHEMA}
+}
+
+# Prepare but do not execute various CREATE statements. Then before
+# those statements are executed, try to use the tables, indices, views,
+# are triggers that were created.
+#
+do_test misc4-2.1 {
+ set stmt [sqlite3_prepare $DB {CREATE TABLE t3(x);} -1 TAIL]
+ catchsql {
+ INSERT INTO t3 VALUES(1);
+ }
+} {1 {no such table: t3}}
+do_test misc4-2.2 {
+ sqlite3_step $stmt
+} SQLITE_DONE
+do_test misc4-2.3 {
+ sqlite3_finalize $stmt
+} SQLITE_OK
+do_test misc4-2.4 {
+ catchsql {
+ INSERT INTO t3 VALUES(1);
+ }
+} {0 {}}
+
+# Ticket #966
+#
+do_test misc4-3.1 {
+ execsql {
+ CREATE TABLE Table1(ID integer primary key, Value TEXT);
+ INSERT INTO Table1 VALUES(1, 'x');
+ CREATE TABLE Table2(ID integer NOT NULL, Value TEXT);
+ INSERT INTO Table2 VALUES(1, 'z');
+ INSERT INTO Table2 VALUES (1, 'a');
+ }
+ catchsql {
+ SELECT ID, max(Value) FROM Table2 GROUP BY 1, 2 ORDER BY 1, 2;
+ }
+} {1 {aggregate functions are not allowed in the GROUP BY clause}}
+ifcapable compound {
+ do_test misc4-3.2 {
+ execsql {
+ SELECT ID, Value FROM Table1
+ UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1
+ ORDER BY 1, 2;
+ }
+ } {1 x 1 z}
+ do_test misc4-3.3 {
+ catchsql {
+ SELECT ID, Value FROM Table1
+ UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1, 2
+ ORDER BY 1, 2;
+ }
+ } {1 {aggregate functions are not allowed in the GROUP BY clause}}
+ do_test misc4-3.4 {
+ catchsql {
+ SELECT ID, max(Value) FROM Table2 GROUP BY 1, 2
+ UNION SELECT ID, Value FROM Table1
+ ORDER BY 1, 2;
+ }
+ } {1 {aggregate functions are not allowed in the GROUP BY clause}}
+} ;# ifcapable compound
+
+# Ticket #1047. Make sure column types are preserved in subqueries.
+#
+ifcapable subquery {
+ do_test misc4-4.1 {
+ execsql {
+ create table a(key varchar, data varchar);
+ create table b(key varchar, period integer);
+ insert into a values('01','data01');
+ insert into a values('+1','data+1');
+
+ insert into b values ('01',1);
+ insert into b values ('01',2);
+ insert into b values ('+1',3);
+ insert into b values ('+1',4);
+
+ select a.*, x.*
+ from a, (select key,sum(period) from b group by key) as x
+ where a.key=x.key;
+ }
+ } {01 data01 01 3 +1 data+1 +1 7}
+
+ # This test case tests the same property as misc4-4.1, but it is
+ # a bit smaller which makes it easier to work with while debugging.
+ do_test misc4-4.2 {
+ execsql {
+ CREATE TABLE ab(a TEXT, b TEXT);
+ INSERT INTO ab VALUES('01', '1');
+ }
+ execsql {
+ select * from ab, (select b from ab) as x where x.b = ab.a;
+ }
+ } {}
+}
+
+
+# Ticket #1036. When creating tables from a SELECT on a view, use the
+# short names of columns.
+#
+ifcapable view {
+ do_test misc4-5.1 {
+ execsql {
+ create table t4(a,b);
+ create table t5(a,c);
+ insert into t4 values (1,2);
+ insert into t5 values (1,3);
+ create view myview as select t4.a a from t4 inner join t5 on t4.a=t5.a;
+ create table problem as select * from myview;
+ }
+ execsql2 {
+ select * FROM problem;
+ }
+ } {a 1}
+ do_test misc4-5.2 {
+ execsql2 {
+ create table t6 as select * from t4, t5;
+ select * from t6;
+ }
+ } {a 1 b 2 a:1 1 c 3}
+}
+
+# Ticket #1086
+do_test misc4-6.1 {
+ execsql {
+ CREATE TABLE abc(a);
+ INSERT INTO abc VALUES(1);
+ CREATE TABLE def(d, e, f, PRIMARY KEY(d, e));
+ }
+} {}
+do_test misc4-6.2 {
+ execsql {
+ SELECT a FROM abc LEFT JOIN def ON (abc.a=def.d);
+ }
+} {1}
+
+finish_test
diff --git a/third_party/sqlite/test/misc5.test b/third_party/sqlite/test/misc5.test
new file mode 100755
index 0000000..a28f491
--- /dev/null
+++ b/third_party/sqlite/test/misc5.test
@@ -0,0 +1,1872 @@
+# 2005 Mar 16
+#
+# 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.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc5.test,v 1.22 2008/07/29 10:26:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build records using the MakeRecord opcode such that the size of the
+# header is at the transition point in the size of a varint.
+#
+# This test causes an assertion failure or a buffer overrun in version
+# 3.1.5 and earlier.
+#
+for {set i 120} {$i<140} {incr i} {
+ do_test misc5-1.$i {
+ catchsql {DROP TABLE t1}
+ set sql1 {CREATE TABLE t1}
+ set sql2 {INSERT INTO t1 VALUES}
+ set sep (
+ for {set j 0} {$j<$i} {incr j} {
+ append sql1 ${sep}a$j
+ append sql2 ${sep}$j
+ set sep ,
+ }
+ append sql1 {);}
+ append sql2 {);}
+ execsql $sql1$sql2
+ } {}
+}
+
+# Make sure large integers are stored correctly.
+#
+ifcapable conflict {
+ do_test misc5-2.1 {
+ execsql {
+ create table t2(x unique);
+ insert into t2 values(1);
+ insert or ignore into t2 select x*2 from t2;
+ insert or ignore into t2 select x*4 from t2;
+ insert or ignore into t2 select x*16 from t2;
+ insert or ignore into t2 select x*256 from t2;
+ insert or ignore into t2 select x*65536 from t2;
+ insert or ignore into t2 select x*2147483648 from t2;
+ insert or ignore into t2 select x-1 from t2;
+ insert or ignore into t2 select x+1 from t2;
+ insert or ignore into t2 select -x from t2;
+ select count(*) from t2;
+ }
+ } 371
+} else {
+ do_test misc5-2.1 {
+ execsql {
+ BEGIN;
+ create table t2(x unique);
+ create table t2_temp(x);
+ insert into t2_temp values(1);
+ insert into t2_temp select x*2 from t2_temp;
+ insert into t2_temp select x*4 from t2_temp;
+ insert into t2_temp select x*16 from t2_temp;
+ insert into t2_temp select x*256 from t2_temp;
+ insert into t2_temp select x*65536 from t2_temp;
+ insert into t2_temp select x*2147483648 from t2_temp;
+ insert into t2_temp select x-1 from t2_temp;
+ insert into t2_temp select x+1 from t2_temp;
+ insert into t2_temp select -x from t2_temp;
+ INSERT INTO t2 SELECT DISTINCT(x) FROM t2_temp;
+ DROP TABLE t2_temp;
+ COMMIT;
+ select count(*) from t2;
+ }
+ } 371
+}
+do_test misc5-2.2 {
+ execsql {
+ select x from t2 order by x;
+ }
+} \
+"-4611686018427387905\
+-4611686018427387904\
+-4611686018427387903\
+-2305843009213693953\
+-2305843009213693952\
+-2305843009213693951\
+-1152921504606846977\
+-1152921504606846976\
+-1152921504606846975\
+-576460752303423489\
+-576460752303423488\
+-576460752303423487\
+-288230376151711745\
+-288230376151711744\
+-288230376151711743\
+-144115188075855873\
+-144115188075855872\
+-144115188075855871\
+-72057594037927937\
+-72057594037927936\
+-72057594037927935\
+-36028797018963969\
+-36028797018963968\
+-36028797018963967\
+-18014398509481985\
+-18014398509481984\
+-18014398509481983\
+-9007199254740993\
+-9007199254740992\
+-9007199254740991\
+-4503599627370497\
+-4503599627370496\
+-4503599627370495\
+-2251799813685249\
+-2251799813685248\
+-2251799813685247\
+-1125899906842625\
+-1125899906842624\
+-1125899906842623\
+-562949953421313\
+-562949953421312\
+-562949953421311\
+-281474976710657\
+-281474976710656\
+-281474976710655\
+-140737488355329\
+-140737488355328\
+-140737488355327\
+-70368744177665\
+-70368744177664\
+-70368744177663\
+-35184372088833\
+-35184372088832\
+-35184372088831\
+-17592186044417\
+-17592186044416\
+-17592186044415\
+-8796093022209\
+-8796093022208\
+-8796093022207\
+-4398046511105\
+-4398046511104\
+-4398046511103\
+-2199023255553\
+-2199023255552\
+-2199023255551\
+-1099511627777\
+-1099511627776\
+-1099511627775\
+-549755813889\
+-549755813888\
+-549755813887\
+-274877906945\
+-274877906944\
+-274877906943\
+-137438953473\
+-137438953472\
+-137438953471\
+-68719476737\
+-68719476736\
+-68719476735\
+-34359738369\
+-34359738368\
+-34359738367\
+-17179869185\
+-17179869184\
+-17179869183\
+-8589934593\
+-8589934592\
+-8589934591\
+-4294967297\
+-4294967296\
+-4294967295\
+-2147483649\
+-2147483648\
+-2147483647\
+-1073741825\
+-1073741824\
+-1073741823\
+-536870913\
+-536870912\
+-536870911\
+-268435457\
+-268435456\
+-268435455\
+-134217729\
+-134217728\
+-134217727\
+-67108865\
+-67108864\
+-67108863\
+-33554433\
+-33554432\
+-33554431\
+-16777217\
+-16777216\
+-16777215\
+-8388609\
+-8388608\
+-8388607\
+-4194305\
+-4194304\
+-4194303\
+-2097153\
+-2097152\
+-2097151\
+-1048577\
+-1048576\
+-1048575\
+-524289\
+-524288\
+-524287\
+-262145\
+-262144\
+-262143\
+-131073\
+-131072\
+-131071\
+-65537\
+-65536\
+-65535\
+-32769\
+-32768\
+-32767\
+-16385\
+-16384\
+-16383\
+-8193\
+-8192\
+-8191\
+-4097\
+-4096\
+-4095\
+-2049\
+-2048\
+-2047\
+-1025\
+-1024\
+-1023\
+-513\
+-512\
+-511\
+-257\
+-256\
+-255\
+-129\
+-128\
+-127\
+-65\
+-64\
+-63\
+-33\
+-32\
+-31\
+-17\
+-16\
+-15\
+-9\
+-8\
+-7\
+-5\
+-4\
+-3\
+-2\
+-1\
+0\
+1\
+2\
+3\
+4\
+5\
+7\
+8\
+9\
+15\
+16\
+17\
+31\
+32\
+33\
+63\
+64\
+65\
+127\
+128\
+129\
+255\
+256\
+257\
+511\
+512\
+513\
+1023\
+1024\
+1025\
+2047\
+2048\
+2049\
+4095\
+4096\
+4097\
+8191\
+8192\
+8193\
+16383\
+16384\
+16385\
+32767\
+32768\
+32769\
+65535\
+65536\
+65537\
+131071\
+131072\
+131073\
+262143\
+262144\
+262145\
+524287\
+524288\
+524289\
+1048575\
+1048576\
+1048577\
+2097151\
+2097152\
+2097153\
+4194303\
+4194304\
+4194305\
+8388607\
+8388608\
+8388609\
+16777215\
+16777216\
+16777217\
+33554431\
+33554432\
+33554433\
+67108863\
+67108864\
+67108865\
+134217727\
+134217728\
+134217729\
+268435455\
+268435456\
+268435457\
+536870911\
+536870912\
+536870913\
+1073741823\
+1073741824\
+1073741825\
+2147483647\
+2147483648\
+2147483649\
+4294967295\
+4294967296\
+4294967297\
+8589934591\
+8589934592\
+8589934593\
+17179869183\
+17179869184\
+17179869185\
+34359738367\
+34359738368\
+34359738369\
+68719476735\
+68719476736\
+68719476737\
+137438953471\
+137438953472\
+137438953473\
+274877906943\
+274877906944\
+274877906945\
+549755813887\
+549755813888\
+549755813889\
+1099511627775\
+1099511627776\
+1099511627777\
+2199023255551\
+2199023255552\
+2199023255553\
+4398046511103\
+4398046511104\
+4398046511105\
+8796093022207\
+8796093022208\
+8796093022209\
+17592186044415\
+17592186044416\
+17592186044417\
+35184372088831\
+35184372088832\
+35184372088833\
+70368744177663\
+70368744177664\
+70368744177665\
+140737488355327\
+140737488355328\
+140737488355329\
+281474976710655\
+281474976710656\
+281474976710657\
+562949953421311\
+562949953421312\
+562949953421313\
+1125899906842623\
+1125899906842624\
+1125899906842625\
+2251799813685247\
+2251799813685248\
+2251799813685249\
+4503599627370495\
+4503599627370496\
+4503599627370497\
+9007199254740991\
+9007199254740992\
+9007199254740993\
+18014398509481983\
+18014398509481984\
+18014398509481985\
+36028797018963967\
+36028797018963968\
+36028797018963969\
+72057594037927935\
+72057594037927936\
+72057594037927937\
+144115188075855871\
+144115188075855872\
+144115188075855873\
+288230376151711743\
+288230376151711744\
+288230376151711745\
+576460752303423487\
+576460752303423488\
+576460752303423489\
+1152921504606846975\
+1152921504606846976\
+1152921504606846977\
+2305843009213693951\
+2305843009213693952\
+2305843009213693953\
+4611686018427387903\
+4611686018427387904\
+4611686018427387905"
+
+# Ticket #1210. Do proper reference counting of Table structures
+# so that deeply nested SELECT statements can be flattened correctly.
+#
+ifcapable subquery {
+ do_test misc5-3.1 {
+ execsql {
+ CREATE TABLE songs(songid, artist, timesplayed);
+ INSERT INTO songs VALUES(1,'one',1);
+ INSERT INTO songs VALUES(2,'one',2);
+ INSERT INTO songs VALUES(3,'two',3);
+ INSERT INTO songs VALUES(4,'three',5);
+ INSERT INTO songs VALUES(5,'one',7);
+ INSERT INTO songs VALUES(6,'two',11);
+ SELECT DISTINCT artist
+ FROM (
+ SELECT DISTINCT artist
+ FROM songs
+ WHERE songid IN (
+ SELECT songid
+ FROM songs
+ WHERE LOWER(artist) = (
+ -- This sub-query is indeterminate. Because there is no ORDER BY,
+ -- it may return 'one', 'two' or 'three'. Because of this, the
+ -- outermost parent query may correctly return any of 'one', 'two'
+ -- or 'three' as well.
+ SELECT DISTINCT LOWER(artist)
+ FROM (
+ -- This sub-query returns the table:
+ --
+ -- two 14
+ -- one 10
+ -- three 5
+ --
+ SELECT DISTINCT artist,sum(timesplayed) AS total
+ FROM songs
+ GROUP BY LOWER(artist)
+ ORDER BY total DESC
+ LIMIT 10
+ )
+ WHERE artist <> ''
+ )
+ )
+ )
+ ORDER BY LOWER(artist) ASC;
+ }
+ } {one}
+}
+
+# Ticket #1370. Do not overwrite small files (less than 1024 bytes)
+# when trying to open them as a database.
+#
+do_test misc5-4.1 {
+ db close
+ file delete -force test.db
+ set fd [open test.db w]
+ puts $fd "This is not really a database"
+ close $fd
+ sqlite3 db test.db
+ catchsql {
+ CREATE TABLE t1(a,b,c);
+ }
+} {1 {file is encrypted or is not a database}}
+
+# Ticket #1371. Allow floating point numbers of the form .N or N.
+#
+do_test misc5-5.1 {
+ execsql {SELECT .1 }
+} 0.1
+do_test misc5-5.2 {
+ execsql {SELECT 2. }
+} 2.0
+do_test misc5-5.3 {
+ execsql {SELECT 3.e0 }
+} 3.0
+do_test misc5-5.4 {
+ execsql {SELECT .4e+1}
+} 4.0
+
+# Ticket #1582. Ensure that an unknown table in a LIMIT clause applied to
+# a UNION ALL query causes an error, not a crash.
+#
+db close
+file delete -force test.db
+sqlite3 db test.db
+ifcapable subquery&&compound {
+ do_test misc5-6.1 {
+ catchsql {
+ SELECT * FROM sqlite_master
+ UNION ALL
+ SELECT * FROM sqlite_master
+ LIMIT (SELECT count(*) FROM blah);
+ }
+ } {1 {no such table: blah}}
+ do_test misc5-6.2 {
+ execsql {
+ CREATE TABLE logs(msg TEXT, timestamp INTEGER, dbtime TEXT);
+ }
+ catchsql {
+ SELECT * FROM logs WHERE logs.oid >= (SELECT head FROM logs_base)
+ UNION ALL
+ SELECT * FROM logs
+ LIMIT (SELECT lmt FROM logs_base) ;
+ }
+ } {1 {no such table: logs_base}}
+}
+
+# Overflow the lemon parser stack by providing an overly complex
+# expression. Make sure that the overflow is detected and reported.
+#
+do_test misc5-7.1 {
+ execsql {CREATE TABLE t1(x)}
+ set sql "INSERT INTO t1 VALUES("
+ set tail ""
+ for {set i 0} {$i<200} {incr i} {
+ append sql "(1+"
+ append tail ")"
+ }
+ append sql 2$tail
+ catchsql $sql
+} {1 {parser stack overflow}}
+
+# Ticket #1911
+#
+ifcapable compound {
+ do_test misc5-9.1 {
+ execsql {
+ SELECT name, type FROM sqlite_master WHERE name IS NULL
+ UNION
+ SELECT type, name FROM sqlite_master WHERE type IS NULL
+ ORDER BY 1, 2, 1, 2, 1, 2
+ }
+ } {}
+ do_test misc5-9.2 {
+ execsql {
+ SELECT name, type FROM sqlite_master WHERE name IS NULL
+ UNION
+ SELECT type, name FROM sqlite_master WHERE type IS NULL
+ ORDER BY 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2
+ }
+ } {}
+}
+
+# Ticket #1912. Make the tokenizer require a space after a numeric
+# literal.
+#
+do_test misc5-10.1 {
+ catchsql {
+ SELECT 123abc
+ }
+} {1 {unrecognized token: "123abc"}}
+do_test misc5-10.2 {
+ catchsql {
+ SELECT 1*123.4e5ghi;
+ }
+} {1 {unrecognized token: "123.4e5ghi"}}
+
+
+# Additional integer encoding tests.
+#
+do_test misc5-11.1 {
+ db eval {
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(-18);
+ INSERT INTO t3 VALUES(-17);
+ INSERT INTO t3 VALUES(-16);
+ INSERT INTO t3 VALUES(-15);
+ INSERT INTO t3 VALUES(-14);
+ INSERT INTO t3 VALUES(-13);
+ INSERT INTO t3 VALUES(-12);
+ INSERT INTO t3 VALUES(-11);
+ INSERT INTO t3 VALUES(-10);
+ INSERT INTO t3 VALUES(-9);
+ INSERT INTO t3 VALUES(-8);
+ INSERT INTO t3 VALUES(-7);
+ INSERT INTO t3 VALUES(-6);
+ INSERT INTO t3 VALUES(-5);
+ INSERT INTO t3 VALUES(-4);
+ INSERT INTO t3 VALUES(-3);
+ INSERT INTO t3 VALUES(-2);
+ INSERT INTO t3 VALUES(-1);
+ INSERT INTO t3 VALUES(0);
+ INSERT INTO t3 VALUES(1);
+ INSERT INTO t3 VALUES(2);
+ INSERT INTO t3 VALUES(3);
+ INSERT INTO t3 VALUES(4);
+ INSERT INTO t3 VALUES(5);
+ INSERT INTO t3 VALUES(6);
+ INSERT INTO t3 VALUES(7);
+ INSERT INTO t3 VALUES(8);
+ INSERT INTO t3 VALUES(9);
+ INSERT INTO t3 VALUES(10);
+ INSERT INTO t3 VALUES(11);
+ INSERT INTO t3 VALUES(12);
+ INSERT INTO t3 VALUES(13);
+ INSERT INTO t3 VALUES(14);
+ INSERT INTO t3 VALUES(15);
+ INSERT INTO t3 VALUES(16);
+ INSERT INTO t3 VALUES(17);
+ INSERT INTO t3 VALUES(18);
+ INSERT INTO t3 VALUES(30);
+ INSERT INTO t3 VALUES(31);
+ INSERT INTO t3 VALUES(32);
+ INSERT INTO t3 VALUES(33);
+ INSERT INTO t3 VALUES(34);
+ INSERT INTO t3 VALUES(-30);
+ INSERT INTO t3 VALUES(-31);
+ INSERT INTO t3 VALUES(-32);
+ INSERT INTO t3 VALUES(-33);
+ INSERT INTO t3 VALUES(-34);
+ INSERT INTO t3 VALUES(62);
+ INSERT INTO t3 VALUES(63);
+ INSERT INTO t3 VALUES(64);
+ INSERT INTO t3 VALUES(65);
+ INSERT INTO t3 VALUES(66);
+ INSERT INTO t3 VALUES(-62);
+ INSERT INTO t3 VALUES(-63);
+ INSERT INTO t3 VALUES(-64);
+ INSERT INTO t3 VALUES(-65);
+ INSERT INTO t3 VALUES(-66);
+ INSERT INTO t3 VALUES(126);
+ INSERT INTO t3 VALUES(127);
+ INSERT INTO t3 VALUES(128);
+ INSERT INTO t3 VALUES(129);
+ INSERT INTO t3 VALUES(130);
+ INSERT INTO t3 VALUES(-126);
+ INSERT INTO t3 VALUES(-127);
+ INSERT INTO t3 VALUES(-128);
+ INSERT INTO t3 VALUES(-129);
+ INSERT INTO t3 VALUES(-130);
+ INSERT INTO t3 VALUES(254);
+ INSERT INTO t3 VALUES(255);
+ INSERT INTO t3 VALUES(256);
+ INSERT INTO t3 VALUES(257);
+ INSERT INTO t3 VALUES(258);
+ INSERT INTO t3 VALUES(-254);
+ INSERT INTO t3 VALUES(-255);
+ INSERT INTO t3 VALUES(-256);
+ INSERT INTO t3 VALUES(-257);
+ INSERT INTO t3 VALUES(-258);
+ INSERT INTO t3 VALUES(510);
+ INSERT INTO t3 VALUES(511);
+ INSERT INTO t3 VALUES(512);
+ INSERT INTO t3 VALUES(513);
+ INSERT INTO t3 VALUES(514);
+ INSERT INTO t3 VALUES(-510);
+ INSERT INTO t3 VALUES(-511);
+ INSERT INTO t3 VALUES(-512);
+ INSERT INTO t3 VALUES(-513);
+ INSERT INTO t3 VALUES(-514);
+ INSERT INTO t3 VALUES(1022);
+ INSERT INTO t3 VALUES(1023);
+ INSERT INTO t3 VALUES(1024);
+ INSERT INTO t3 VALUES(1025);
+ INSERT INTO t3 VALUES(1026);
+ INSERT INTO t3 VALUES(-1022);
+ INSERT INTO t3 VALUES(-1023);
+ INSERT INTO t3 VALUES(-1024);
+ INSERT INTO t3 VALUES(-1025);
+ INSERT INTO t3 VALUES(-1026);
+ INSERT INTO t3 VALUES(2046);
+ INSERT INTO t3 VALUES(2047);
+ INSERT INTO t3 VALUES(2048);
+ INSERT INTO t3 VALUES(2049);
+ INSERT INTO t3 VALUES(2050);
+ INSERT INTO t3 VALUES(-2046);
+ INSERT INTO t3 VALUES(-2047);
+ INSERT INTO t3 VALUES(-2048);
+ INSERT INTO t3 VALUES(-2049);
+ INSERT INTO t3 VALUES(-2050);
+ INSERT INTO t3 VALUES(4094);
+ INSERT INTO t3 VALUES(4095);
+ INSERT INTO t3 VALUES(4096);
+ INSERT INTO t3 VALUES(4097);
+ INSERT INTO t3 VALUES(4098);
+ INSERT INTO t3 VALUES(-4094);
+ INSERT INTO t3 VALUES(-4095);
+ INSERT INTO t3 VALUES(-4096);
+ INSERT INTO t3 VALUES(-4097);
+ INSERT INTO t3 VALUES(-4098);
+ INSERT INTO t3 VALUES(8190);
+ INSERT INTO t3 VALUES(8191);
+ INSERT INTO t3 VALUES(8192);
+ INSERT INTO t3 VALUES(8193);
+ INSERT INTO t3 VALUES(8194);
+ INSERT INTO t3 VALUES(-8190);
+ INSERT INTO t3 VALUES(-8191);
+ INSERT INTO t3 VALUES(-8192);
+ INSERT INTO t3 VALUES(-8193);
+ INSERT INTO t3 VALUES(-8194);
+ INSERT INTO t3 VALUES(16382);
+ INSERT INTO t3 VALUES(16383);
+ INSERT INTO t3 VALUES(16384);
+ INSERT INTO t3 VALUES(16385);
+ INSERT INTO t3 VALUES(16386);
+ INSERT INTO t3 VALUES(-16382);
+ INSERT INTO t3 VALUES(-16383);
+ INSERT INTO t3 VALUES(-16384);
+ INSERT INTO t3 VALUES(-16385);
+ INSERT INTO t3 VALUES(-16386);
+ INSERT INTO t3 VALUES(32766);
+ INSERT INTO t3 VALUES(32767);
+ INSERT INTO t3 VALUES(32768);
+ INSERT INTO t3 VALUES(32769);
+ INSERT INTO t3 VALUES(32770);
+ INSERT INTO t3 VALUES(-32766);
+ INSERT INTO t3 VALUES(-32767);
+ INSERT INTO t3 VALUES(-32768);
+ INSERT INTO t3 VALUES(-32769);
+ INSERT INTO t3 VALUES(-32770);
+ INSERT INTO t3 VALUES(65534);
+ INSERT INTO t3 VALUES(65535);
+ INSERT INTO t3 VALUES(65536);
+ INSERT INTO t3 VALUES(65537);
+ INSERT INTO t3 VALUES(65538);
+ INSERT INTO t3 VALUES(-65534);
+ INSERT INTO t3 VALUES(-65535);
+ INSERT INTO t3 VALUES(-65536);
+ INSERT INTO t3 VALUES(-65537);
+ INSERT INTO t3 VALUES(-65538);
+ INSERT INTO t3 VALUES(131070);
+ INSERT INTO t3 VALUES(131071);
+ INSERT INTO t3 VALUES(131072);
+ INSERT INTO t3 VALUES(131073);
+ INSERT INTO t3 VALUES(131074);
+ INSERT INTO t3 VALUES(-131070);
+ INSERT INTO t3 VALUES(-131071);
+ INSERT INTO t3 VALUES(-131072);
+ INSERT INTO t3 VALUES(-131073);
+ INSERT INTO t3 VALUES(-131074);
+ INSERT INTO t3 VALUES(262142);
+ INSERT INTO t3 VALUES(262143);
+ INSERT INTO t3 VALUES(262144);
+ INSERT INTO t3 VALUES(262145);
+ INSERT INTO t3 VALUES(262146);
+ INSERT INTO t3 VALUES(-262142);
+ INSERT INTO t3 VALUES(-262143);
+ INSERT INTO t3 VALUES(-262144);
+ INSERT INTO t3 VALUES(-262145);
+ INSERT INTO t3 VALUES(-262146);
+ INSERT INTO t3 VALUES(524286);
+ INSERT INTO t3 VALUES(524287);
+ INSERT INTO t3 VALUES(524288);
+ INSERT INTO t3 VALUES(524289);
+ INSERT INTO t3 VALUES(524290);
+ INSERT INTO t3 VALUES(-524286);
+ INSERT INTO t3 VALUES(-524287);
+ INSERT INTO t3 VALUES(-524288);
+ INSERT INTO t3 VALUES(-524289);
+ INSERT INTO t3 VALUES(-524290);
+ INSERT INTO t3 VALUES(1048574);
+ INSERT INTO t3 VALUES(1048575);
+ INSERT INTO t3 VALUES(1048576);
+ INSERT INTO t3 VALUES(1048577);
+ INSERT INTO t3 VALUES(1048578);
+ INSERT INTO t3 VALUES(-1048574);
+ INSERT INTO t3 VALUES(-1048575);
+ INSERT INTO t3 VALUES(-1048576);
+ INSERT INTO t3 VALUES(-1048577);
+ INSERT INTO t3 VALUES(-1048578);
+ INSERT INTO t3 VALUES(2097150);
+ INSERT INTO t3 VALUES(2097151);
+ INSERT INTO t3 VALUES(2097152);
+ INSERT INTO t3 VALUES(2097153);
+ INSERT INTO t3 VALUES(2097154);
+ INSERT INTO t3 VALUES(-2097150);
+ INSERT INTO t3 VALUES(-2097151);
+ INSERT INTO t3 VALUES(-2097152);
+ INSERT INTO t3 VALUES(-2097153);
+ INSERT INTO t3 VALUES(-2097154);
+ INSERT INTO t3 VALUES(4194302);
+ INSERT INTO t3 VALUES(4194303);
+ INSERT INTO t3 VALUES(4194304);
+ INSERT INTO t3 VALUES(4194305);
+ INSERT INTO t3 VALUES(4194306);
+ INSERT INTO t3 VALUES(-4194302);
+ INSERT INTO t3 VALUES(-4194303);
+ INSERT INTO t3 VALUES(-4194304);
+ INSERT INTO t3 VALUES(-4194305);
+ INSERT INTO t3 VALUES(-4194306);
+ INSERT INTO t3 VALUES(8388606);
+ INSERT INTO t3 VALUES(8388607);
+ INSERT INTO t3 VALUES(8388608);
+ INSERT INTO t3 VALUES(8388609);
+ INSERT INTO t3 VALUES(8388610);
+ INSERT INTO t3 VALUES(-8388606);
+ INSERT INTO t3 VALUES(-8388607);
+ INSERT INTO t3 VALUES(-8388608);
+ INSERT INTO t3 VALUES(-8388609);
+ INSERT INTO t3 VALUES(-8388610);
+ INSERT INTO t3 VALUES(16777214);
+ INSERT INTO t3 VALUES(16777215);
+ INSERT INTO t3 VALUES(16777216);
+ INSERT INTO t3 VALUES(16777217);
+ INSERT INTO t3 VALUES(16777218);
+ INSERT INTO t3 VALUES(-16777214);
+ INSERT INTO t3 VALUES(-16777215);
+ INSERT INTO t3 VALUES(-16777216);
+ INSERT INTO t3 VALUES(-16777217);
+ INSERT INTO t3 VALUES(-16777218);
+ INSERT INTO t3 VALUES(33554430);
+ INSERT INTO t3 VALUES(33554431);
+ INSERT INTO t3 VALUES(33554432);
+ INSERT INTO t3 VALUES(33554433);
+ INSERT INTO t3 VALUES(33554434);
+ INSERT INTO t3 VALUES(-33554430);
+ INSERT INTO t3 VALUES(-33554431);
+ INSERT INTO t3 VALUES(-33554432);
+ INSERT INTO t3 VALUES(-33554433);
+ INSERT INTO t3 VALUES(-33554434);
+ INSERT INTO t3 VALUES(67108862);
+ INSERT INTO t3 VALUES(67108863);
+ INSERT INTO t3 VALUES(67108864);
+ INSERT INTO t3 VALUES(67108865);
+ INSERT INTO t3 VALUES(67108866);
+ INSERT INTO t3 VALUES(-67108862);
+ INSERT INTO t3 VALUES(-67108863);
+ INSERT INTO t3 VALUES(-67108864);
+ INSERT INTO t3 VALUES(-67108865);
+ INSERT INTO t3 VALUES(-67108866);
+ INSERT INTO t3 VALUES(134217726);
+ INSERT INTO t3 VALUES(134217727);
+ INSERT INTO t3 VALUES(134217728);
+ INSERT INTO t3 VALUES(134217729);
+ INSERT INTO t3 VALUES(134217730);
+ INSERT INTO t3 VALUES(-134217726);
+ INSERT INTO t3 VALUES(-134217727);
+ INSERT INTO t3 VALUES(-134217728);
+ INSERT INTO t3 VALUES(-134217729);
+ INSERT INTO t3 VALUES(-134217730);
+ INSERT INTO t3 VALUES(268435454);
+ INSERT INTO t3 VALUES(268435455);
+ INSERT INTO t3 VALUES(268435456);
+ INSERT INTO t3 VALUES(268435457);
+ INSERT INTO t3 VALUES(268435458);
+ INSERT INTO t3 VALUES(-268435454);
+ INSERT INTO t3 VALUES(-268435455);
+ INSERT INTO t3 VALUES(-268435456);
+ INSERT INTO t3 VALUES(-268435457);
+ INSERT INTO t3 VALUES(-268435458);
+ INSERT INTO t3 VALUES(536870910);
+ INSERT INTO t3 VALUES(536870911);
+ INSERT INTO t3 VALUES(536870912);
+ INSERT INTO t3 VALUES(536870913);
+ INSERT INTO t3 VALUES(536870914);
+ INSERT INTO t3 VALUES(-536870910);
+ INSERT INTO t3 VALUES(-536870911);
+ INSERT INTO t3 VALUES(-536870912);
+ INSERT INTO t3 VALUES(-536870913);
+ INSERT INTO t3 VALUES(-536870914);
+ INSERT INTO t3 VALUES(1073741822);
+ INSERT INTO t3 VALUES(1073741823);
+ INSERT INTO t3 VALUES(1073741824);
+ INSERT INTO t3 VALUES(1073741825);
+ INSERT INTO t3 VALUES(1073741826);
+ INSERT INTO t3 VALUES(-1073741822);
+ INSERT INTO t3 VALUES(-1073741823);
+ INSERT INTO t3 VALUES(-1073741824);
+ INSERT INTO t3 VALUES(-1073741825);
+ INSERT INTO t3 VALUES(-1073741826);
+ INSERT INTO t3 VALUES(2147483646);
+ INSERT INTO t3 VALUES(2147483647);
+ INSERT INTO t3 VALUES(2147483648);
+ INSERT INTO t3 VALUES(2147483649);
+ INSERT INTO t3 VALUES(2147483650);
+ INSERT INTO t3 VALUES(-2147483646);
+ INSERT INTO t3 VALUES(-2147483647);
+ INSERT INTO t3 VALUES(-2147483648);
+ INSERT INTO t3 VALUES(-2147483649);
+ INSERT INTO t3 VALUES(-2147483650);
+ INSERT INTO t3 VALUES(4294967294);
+ INSERT INTO t3 VALUES(4294967295);
+ INSERT INTO t3 VALUES(4294967296);
+ INSERT INTO t3 VALUES(4294967297);
+ INSERT INTO t3 VALUES(4294967298);
+ INSERT INTO t3 VALUES(-4294967294);
+ INSERT INTO t3 VALUES(-4294967295);
+ INSERT INTO t3 VALUES(-4294967296);
+ INSERT INTO t3 VALUES(-4294967297);
+ INSERT INTO t3 VALUES(-4294967298);
+ INSERT INTO t3 VALUES(8589934590);
+ INSERT INTO t3 VALUES(8589934591);
+ INSERT INTO t3 VALUES(8589934592);
+ INSERT INTO t3 VALUES(8589934593);
+ INSERT INTO t3 VALUES(8589934594);
+ INSERT INTO t3 VALUES(-8589934590);
+ INSERT INTO t3 VALUES(-8589934591);
+ INSERT INTO t3 VALUES(-8589934592);
+ INSERT INTO t3 VALUES(-8589934593);
+ INSERT INTO t3 VALUES(-8589934594);
+ INSERT INTO t3 VALUES(17179869182);
+ INSERT INTO t3 VALUES(17179869183);
+ INSERT INTO t3 VALUES(17179869184);
+ INSERT INTO t3 VALUES(17179869185);
+ INSERT INTO t3 VALUES(17179869186);
+ INSERT INTO t3 VALUES(-17179869182);
+ INSERT INTO t3 VALUES(-17179869183);
+ INSERT INTO t3 VALUES(-17179869184);
+ INSERT INTO t3 VALUES(-17179869185);
+ INSERT INTO t3 VALUES(-17179869186);
+ INSERT INTO t3 VALUES(34359738366);
+ INSERT INTO t3 VALUES(34359738367);
+ INSERT INTO t3 VALUES(34359738368);
+ INSERT INTO t3 VALUES(34359738369);
+ INSERT INTO t3 VALUES(34359738370);
+ INSERT INTO t3 VALUES(-34359738366);
+ INSERT INTO t3 VALUES(-34359738367);
+ INSERT INTO t3 VALUES(-34359738368);
+ INSERT INTO t3 VALUES(-34359738369);
+ INSERT INTO t3 VALUES(-34359738370);
+ INSERT INTO t3 VALUES(68719476734);
+ INSERT INTO t3 VALUES(68719476735);
+ INSERT INTO t3 VALUES(68719476736);
+ INSERT INTO t3 VALUES(68719476737);
+ INSERT INTO t3 VALUES(68719476738);
+ INSERT INTO t3 VALUES(-68719476734);
+ INSERT INTO t3 VALUES(-68719476735);
+ INSERT INTO t3 VALUES(-68719476736);
+ INSERT INTO t3 VALUES(-68719476737);
+ INSERT INTO t3 VALUES(-68719476738);
+ INSERT INTO t3 VALUES(137438953470);
+ INSERT INTO t3 VALUES(137438953471);
+ INSERT INTO t3 VALUES(137438953472);
+ INSERT INTO t3 VALUES(137438953473);
+ INSERT INTO t3 VALUES(137438953474);
+ INSERT INTO t3 VALUES(-137438953470);
+ INSERT INTO t3 VALUES(-137438953471);
+ INSERT INTO t3 VALUES(-137438953472);
+ INSERT INTO t3 VALUES(-137438953473);
+ INSERT INTO t3 VALUES(-137438953474);
+ INSERT INTO t3 VALUES(274877906942);
+ INSERT INTO t3 VALUES(274877906943);
+ INSERT INTO t3 VALUES(274877906944);
+ INSERT INTO t3 VALUES(274877906945);
+ INSERT INTO t3 VALUES(274877906946);
+ INSERT INTO t3 VALUES(-274877906942);
+ INSERT INTO t3 VALUES(-274877906943);
+ INSERT INTO t3 VALUES(-274877906944);
+ INSERT INTO t3 VALUES(-274877906945);
+ INSERT INTO t3 VALUES(-274877906946);
+ INSERT INTO t3 VALUES(549755813886);
+ INSERT INTO t3 VALUES(549755813887);
+ INSERT INTO t3 VALUES(549755813888);
+ INSERT INTO t3 VALUES(549755813889);
+ INSERT INTO t3 VALUES(549755813890);
+ INSERT INTO t3 VALUES(-549755813886);
+ INSERT INTO t3 VALUES(-549755813887);
+ INSERT INTO t3 VALUES(-549755813888);
+ INSERT INTO t3 VALUES(-549755813889);
+ INSERT INTO t3 VALUES(-549755813890);
+ INSERT INTO t3 VALUES(1099511627774);
+ INSERT INTO t3 VALUES(1099511627775);
+ INSERT INTO t3 VALUES(1099511627776);
+ INSERT INTO t3 VALUES(1099511627777);
+ INSERT INTO t3 VALUES(1099511627778);
+ INSERT INTO t3 VALUES(-1099511627774);
+ INSERT INTO t3 VALUES(-1099511627775);
+ INSERT INTO t3 VALUES(-1099511627776);
+ INSERT INTO t3 VALUES(-1099511627777);
+ INSERT INTO t3 VALUES(-1099511627778);
+ INSERT INTO t3 VALUES(2199023255550);
+ INSERT INTO t3 VALUES(2199023255551);
+ INSERT INTO t3 VALUES(2199023255552);
+ INSERT INTO t3 VALUES(2199023255553);
+ INSERT INTO t3 VALUES(2199023255554);
+ INSERT INTO t3 VALUES(-2199023255550);
+ INSERT INTO t3 VALUES(-2199023255551);
+ INSERT INTO t3 VALUES(-2199023255552);
+ INSERT INTO t3 VALUES(-2199023255553);
+ INSERT INTO t3 VALUES(-2199023255554);
+ INSERT INTO t3 VALUES(4398046511102);
+ INSERT INTO t3 VALUES(4398046511103);
+ INSERT INTO t3 VALUES(4398046511104);
+ INSERT INTO t3 VALUES(4398046511105);
+ INSERT INTO t3 VALUES(4398046511106);
+ INSERT INTO t3 VALUES(-4398046511102);
+ INSERT INTO t3 VALUES(-4398046511103);
+ INSERT INTO t3 VALUES(-4398046511104);
+ INSERT INTO t3 VALUES(-4398046511105);
+ INSERT INTO t3 VALUES(-4398046511106);
+ INSERT INTO t3 VALUES(8796093022206);
+ INSERT INTO t3 VALUES(8796093022207);
+ INSERT INTO t3 VALUES(8796093022208);
+ INSERT INTO t3 VALUES(8796093022209);
+ INSERT INTO t3 VALUES(8796093022210);
+ INSERT INTO t3 VALUES(-8796093022206);
+ INSERT INTO t3 VALUES(-8796093022207);
+ INSERT INTO t3 VALUES(-8796093022208);
+ INSERT INTO t3 VALUES(-8796093022209);
+ INSERT INTO t3 VALUES(-8796093022210);
+ INSERT INTO t3 VALUES(17592186044414);
+ INSERT INTO t3 VALUES(17592186044415);
+ INSERT INTO t3 VALUES(17592186044416);
+ INSERT INTO t3 VALUES(17592186044417);
+ INSERT INTO t3 VALUES(17592186044418);
+ INSERT INTO t3 VALUES(-17592186044414);
+ INSERT INTO t3 VALUES(-17592186044415);
+ INSERT INTO t3 VALUES(-17592186044416);
+ INSERT INTO t3 VALUES(-17592186044417);
+ INSERT INTO t3 VALUES(-17592186044418);
+ INSERT INTO t3 VALUES(35184372088830);
+ INSERT INTO t3 VALUES(35184372088831);
+ INSERT INTO t3 VALUES(35184372088832);
+ INSERT INTO t3 VALUES(35184372088833);
+ INSERT INTO t3 VALUES(35184372088834);
+ INSERT INTO t3 VALUES(-35184372088830);
+ INSERT INTO t3 VALUES(-35184372088831);
+ INSERT INTO t3 VALUES(-35184372088832);
+ INSERT INTO t3 VALUES(-35184372088833);
+ INSERT INTO t3 VALUES(-35184372088834);
+ INSERT INTO t3 VALUES(70368744177662);
+ INSERT INTO t3 VALUES(70368744177663);
+ INSERT INTO t3 VALUES(70368744177664);
+ INSERT INTO t3 VALUES(70368744177665);
+ INSERT INTO t3 VALUES(70368744177666);
+ INSERT INTO t3 VALUES(-70368744177662);
+ INSERT INTO t3 VALUES(-70368744177663);
+ INSERT INTO t3 VALUES(-70368744177664);
+ INSERT INTO t3 VALUES(-70368744177665);
+ INSERT INTO t3 VALUES(-70368744177666);
+ INSERT INTO t3 VALUES(140737488355326);
+ INSERT INTO t3 VALUES(140737488355327);
+ INSERT INTO t3 VALUES(140737488355328);
+ INSERT INTO t3 VALUES(140737488355329);
+ INSERT INTO t3 VALUES(140737488355330);
+ INSERT INTO t3 VALUES(-140737488355326);
+ INSERT INTO t3 VALUES(-140737488355327);
+ INSERT INTO t3 VALUES(-140737488355328);
+ INSERT INTO t3 VALUES(-140737488355329);
+ INSERT INTO t3 VALUES(-140737488355330);
+ INSERT INTO t3 VALUES(281474976710654);
+ INSERT INTO t3 VALUES(281474976710655);
+ INSERT INTO t3 VALUES(281474976710656);
+ INSERT INTO t3 VALUES(281474976710657);
+ INSERT INTO t3 VALUES(281474976710658);
+ INSERT INTO t3 VALUES(-281474976710654);
+ INSERT INTO t3 VALUES(-281474976710655);
+ INSERT INTO t3 VALUES(-281474976710656);
+ INSERT INTO t3 VALUES(-281474976710657);
+ INSERT INTO t3 VALUES(-281474976710658);
+ INSERT INTO t3 VALUES(562949953421310);
+ INSERT INTO t3 VALUES(562949953421311);
+ INSERT INTO t3 VALUES(562949953421312);
+ INSERT INTO t3 VALUES(562949953421313);
+ INSERT INTO t3 VALUES(562949953421314);
+ INSERT INTO t3 VALUES(-562949953421310);
+ INSERT INTO t3 VALUES(-562949953421311);
+ INSERT INTO t3 VALUES(-562949953421312);
+ INSERT INTO t3 VALUES(-562949953421313);
+ INSERT INTO t3 VALUES(-562949953421314);
+ INSERT INTO t3 VALUES(1125899906842622);
+ INSERT INTO t3 VALUES(1125899906842623);
+ INSERT INTO t3 VALUES(1125899906842624);
+ INSERT INTO t3 VALUES(1125899906842625);
+ INSERT INTO t3 VALUES(1125899906842626);
+ INSERT INTO t3 VALUES(-1125899906842622);
+ INSERT INTO t3 VALUES(-1125899906842623);
+ INSERT INTO t3 VALUES(-1125899906842624);
+ INSERT INTO t3 VALUES(-1125899906842625);
+ INSERT INTO t3 VALUES(-1125899906842626);
+ INSERT INTO t3 VALUES(2251799813685246);
+ INSERT INTO t3 VALUES(2251799813685247);
+ INSERT INTO t3 VALUES(2251799813685248);
+ INSERT INTO t3 VALUES(2251799813685249);
+ INSERT INTO t3 VALUES(2251799813685250);
+ INSERT INTO t3 VALUES(-2251799813685246);
+ INSERT INTO t3 VALUES(-2251799813685247);
+ INSERT INTO t3 VALUES(-2251799813685248);
+ INSERT INTO t3 VALUES(-2251799813685249);
+ INSERT INTO t3 VALUES(-2251799813685250);
+ INSERT INTO t3 VALUES(4503599627370494);
+ INSERT INTO t3 VALUES(4503599627370495);
+ INSERT INTO t3 VALUES(4503599627370496);
+ INSERT INTO t3 VALUES(4503599627370497);
+ INSERT INTO t3 VALUES(4503599627370498);
+ INSERT INTO t3 VALUES(-4503599627370494);
+ INSERT INTO t3 VALUES(-4503599627370495);
+ INSERT INTO t3 VALUES(-4503599627370496);
+ INSERT INTO t3 VALUES(-4503599627370497);
+ INSERT INTO t3 VALUES(-4503599627370498);
+ INSERT INTO t3 VALUES(9007199254740990);
+ INSERT INTO t3 VALUES(9007199254740991);
+ INSERT INTO t3 VALUES(9007199254740992);
+ INSERT INTO t3 VALUES(9007199254740993);
+ INSERT INTO t3 VALUES(9007199254740994);
+ INSERT INTO t3 VALUES(-9007199254740990);
+ INSERT INTO t3 VALUES(-9007199254740991);
+ INSERT INTO t3 VALUES(-9007199254740992);
+ INSERT INTO t3 VALUES(-9007199254740993);
+ INSERT INTO t3 VALUES(-9007199254740994);
+ INSERT INTO t3 VALUES(18014398509481982);
+ INSERT INTO t3 VALUES(18014398509481983);
+ INSERT INTO t3 VALUES(18014398509481984);
+ INSERT INTO t3 VALUES(18014398509481985);
+ INSERT INTO t3 VALUES(18014398509481986);
+ INSERT INTO t3 VALUES(-18014398509481982);
+ INSERT INTO t3 VALUES(-18014398509481983);
+ INSERT INTO t3 VALUES(-18014398509481984);
+ INSERT INTO t3 VALUES(-18014398509481985);
+ INSERT INTO t3 VALUES(-18014398509481986);
+ INSERT INTO t3 VALUES(36028797018963966);
+ INSERT INTO t3 VALUES(36028797018963967);
+ INSERT INTO t3 VALUES(36028797018963968);
+ INSERT INTO t3 VALUES(36028797018963969);
+ INSERT INTO t3 VALUES(36028797018963970);
+ INSERT INTO t3 VALUES(-36028797018963966);
+ INSERT INTO t3 VALUES(-36028797018963967);
+ INSERT INTO t3 VALUES(-36028797018963968);
+ INSERT INTO t3 VALUES(-36028797018963969);
+ INSERT INTO t3 VALUES(-36028797018963970);
+ INSERT INTO t3 VALUES(72057594037927934);
+ INSERT INTO t3 VALUES(72057594037927935);
+ INSERT INTO t3 VALUES(72057594037927936);
+ INSERT INTO t3 VALUES(72057594037927937);
+ INSERT INTO t3 VALUES(72057594037927938);
+ INSERT INTO t3 VALUES(-72057594037927934);
+ INSERT INTO t3 VALUES(-72057594037927935);
+ INSERT INTO t3 VALUES(-72057594037927936);
+ INSERT INTO t3 VALUES(-72057594037927937);
+ INSERT INTO t3 VALUES(-72057594037927938);
+ INSERT INTO t3 VALUES(144115188075855870);
+ INSERT INTO t3 VALUES(144115188075855871);
+ INSERT INTO t3 VALUES(144115188075855872);
+ INSERT INTO t3 VALUES(144115188075855873);
+ INSERT INTO t3 VALUES(144115188075855874);
+ INSERT INTO t3 VALUES(-144115188075855870);
+ INSERT INTO t3 VALUES(-144115188075855871);
+ INSERT INTO t3 VALUES(-144115188075855872);
+ INSERT INTO t3 VALUES(-144115188075855873);
+ INSERT INTO t3 VALUES(-144115188075855874);
+ INSERT INTO t3 VALUES(288230376151711742);
+ INSERT INTO t3 VALUES(288230376151711743);
+ INSERT INTO t3 VALUES(288230376151711744);
+ INSERT INTO t3 VALUES(288230376151711745);
+ INSERT INTO t3 VALUES(288230376151711746);
+ INSERT INTO t3 VALUES(-288230376151711742);
+ INSERT INTO t3 VALUES(-288230376151711743);
+ INSERT INTO t3 VALUES(-288230376151711744);
+ INSERT INTO t3 VALUES(-288230376151711745);
+ INSERT INTO t3 VALUES(-288230376151711746);
+ INSERT INTO t3 VALUES(576460752303423486);
+ INSERT INTO t3 VALUES(576460752303423487);
+ INSERT INTO t3 VALUES(576460752303423488);
+ INSERT INTO t3 VALUES(576460752303423489);
+ INSERT INTO t3 VALUES(576460752303423490);
+ INSERT INTO t3 VALUES(-576460752303423486);
+ INSERT INTO t3 VALUES(-576460752303423487);
+ INSERT INTO t3 VALUES(-576460752303423488);
+ INSERT INTO t3 VALUES(-576460752303423489);
+ INSERT INTO t3 VALUES(-576460752303423490);
+ INSERT INTO t3 VALUES(1152921504606846974);
+ INSERT INTO t3 VALUES(1152921504606846975);
+ INSERT INTO t3 VALUES(1152921504606846976);
+ INSERT INTO t3 VALUES(1152921504606846977);
+ INSERT INTO t3 VALUES(1152921504606846978);
+ INSERT INTO t3 VALUES(-1152921504606846974);
+ INSERT INTO t3 VALUES(-1152921504606846975);
+ INSERT INTO t3 VALUES(-1152921504606846976);
+ INSERT INTO t3 VALUES(-1152921504606846977);
+ INSERT INTO t3 VALUES(-1152921504606846978);
+ INSERT INTO t3 VALUES(2305843009213693950);
+ INSERT INTO t3 VALUES(2305843009213693951);
+ INSERT INTO t3 VALUES(2305843009213693952);
+ INSERT INTO t3 VALUES(2305843009213693953);
+ INSERT INTO t3 VALUES(2305843009213693954);
+ INSERT INTO t3 VALUES(-2305843009213693950);
+ INSERT INTO t3 VALUES(-2305843009213693951);
+ INSERT INTO t3 VALUES(-2305843009213693952);
+ INSERT INTO t3 VALUES(-2305843009213693953);
+ INSERT INTO t3 VALUES(-2305843009213693954);
+ INSERT INTO t3 VALUES(4611686018427387902);
+ INSERT INTO t3 VALUES(4611686018427387903);
+ INSERT INTO t3 VALUES(4611686018427387904);
+ INSERT INTO t3 VALUES(4611686018427387905);
+ INSERT INTO t3 VALUES(4611686018427387906);
+ INSERT INTO t3 VALUES(-4611686018427387902);
+ INSERT INTO t3 VALUES(-4611686018427387903);
+ INSERT INTO t3 VALUES(-4611686018427387904);
+ INSERT INTO t3 VALUES(-4611686018427387905);
+ INSERT INTO t3 VALUES(-4611686018427387906);
+ INSERT INTO t3 VALUES(9223372036854775806);
+ INSERT INTO t3 VALUES(9223372036854775807);
+ INSERT INTO t3 VALUES(-9223372036854775806);
+ INSERT INTO t3 VALUES(-9223372036854775807);
+ INSERT INTO t3 VALUES(-9223372036854775808);
+ SELECT x FROM t3 ORDER BY x;
+ }
+} {-9223372036854775808\
+-9223372036854775807\
+-9223372036854775806\
+-4611686018427387906\
+-4611686018427387905\
+-4611686018427387904\
+-4611686018427387903\
+-4611686018427387902\
+-2305843009213693954\
+-2305843009213693953\
+-2305843009213693952\
+-2305843009213693951\
+-2305843009213693950\
+-1152921504606846978\
+-1152921504606846977\
+-1152921504606846976\
+-1152921504606846975\
+-1152921504606846974\
+-576460752303423490\
+-576460752303423489\
+-576460752303423488\
+-576460752303423487\
+-576460752303423486\
+-288230376151711746\
+-288230376151711745\
+-288230376151711744\
+-288230376151711743\
+-288230376151711742\
+-144115188075855874\
+-144115188075855873\
+-144115188075855872\
+-144115188075855871\
+-144115188075855870\
+-72057594037927938\
+-72057594037927937\
+-72057594037927936\
+-72057594037927935\
+-72057594037927934\
+-36028797018963970\
+-36028797018963969\
+-36028797018963968\
+-36028797018963967\
+-36028797018963966\
+-18014398509481986\
+-18014398509481985\
+-18014398509481984\
+-18014398509481983\
+-18014398509481982\
+-9007199254740994\
+-9007199254740993\
+-9007199254740992\
+-9007199254740991\
+-9007199254740990\
+-4503599627370498\
+-4503599627370497\
+-4503599627370496\
+-4503599627370495\
+-4503599627370494\
+-2251799813685250\
+-2251799813685249\
+-2251799813685248\
+-2251799813685247\
+-2251799813685246\
+-1125899906842626\
+-1125899906842625\
+-1125899906842624\
+-1125899906842623\
+-1125899906842622\
+-562949953421314\
+-562949953421313\
+-562949953421312\
+-562949953421311\
+-562949953421310\
+-281474976710658\
+-281474976710657\
+-281474976710656\
+-281474976710655\
+-281474976710654\
+-140737488355330\
+-140737488355329\
+-140737488355328\
+-140737488355327\
+-140737488355326\
+-70368744177666\
+-70368744177665\
+-70368744177664\
+-70368744177663\
+-70368744177662\
+-35184372088834\
+-35184372088833\
+-35184372088832\
+-35184372088831\
+-35184372088830\
+-17592186044418\
+-17592186044417\
+-17592186044416\
+-17592186044415\
+-17592186044414\
+-8796093022210\
+-8796093022209\
+-8796093022208\
+-8796093022207\
+-8796093022206\
+-4398046511106\
+-4398046511105\
+-4398046511104\
+-4398046511103\
+-4398046511102\
+-2199023255554\
+-2199023255553\
+-2199023255552\
+-2199023255551\
+-2199023255550\
+-1099511627778\
+-1099511627777\
+-1099511627776\
+-1099511627775\
+-1099511627774\
+-549755813890\
+-549755813889\
+-549755813888\
+-549755813887\
+-549755813886\
+-274877906946\
+-274877906945\
+-274877906944\
+-274877906943\
+-274877906942\
+-137438953474\
+-137438953473\
+-137438953472\
+-137438953471\
+-137438953470\
+-68719476738\
+-68719476737\
+-68719476736\
+-68719476735\
+-68719476734\
+-34359738370\
+-34359738369\
+-34359738368\
+-34359738367\
+-34359738366\
+-17179869186\
+-17179869185\
+-17179869184\
+-17179869183\
+-17179869182\
+-8589934594\
+-8589934593\
+-8589934592\
+-8589934591\
+-8589934590\
+-4294967298\
+-4294967297\
+-4294967296\
+-4294967295\
+-4294967294\
+-2147483650\
+-2147483649\
+-2147483648\
+-2147483647\
+-2147483646\
+-1073741826\
+-1073741825\
+-1073741824\
+-1073741823\
+-1073741822\
+-536870914\
+-536870913\
+-536870912\
+-536870911\
+-536870910\
+-268435458\
+-268435457\
+-268435456\
+-268435455\
+-268435454\
+-134217730\
+-134217729\
+-134217728\
+-134217727\
+-134217726\
+-67108866\
+-67108865\
+-67108864\
+-67108863\
+-67108862\
+-33554434\
+-33554433\
+-33554432\
+-33554431\
+-33554430\
+-16777218\
+-16777217\
+-16777216\
+-16777215\
+-16777214\
+-8388610\
+-8388609\
+-8388608\
+-8388607\
+-8388606\
+-4194306\
+-4194305\
+-4194304\
+-4194303\
+-4194302\
+-2097154\
+-2097153\
+-2097152\
+-2097151\
+-2097150\
+-1048578\
+-1048577\
+-1048576\
+-1048575\
+-1048574\
+-524290\
+-524289\
+-524288\
+-524287\
+-524286\
+-262146\
+-262145\
+-262144\
+-262143\
+-262142\
+-131074\
+-131073\
+-131072\
+-131071\
+-131070\
+-65538\
+-65537\
+-65536\
+-65535\
+-65534\
+-32770\
+-32769\
+-32768\
+-32767\
+-32766\
+-16386\
+-16385\
+-16384\
+-16383\
+-16382\
+-8194\
+-8193\
+-8192\
+-8191\
+-8190\
+-4098\
+-4097\
+-4096\
+-4095\
+-4094\
+-2050\
+-2049\
+-2048\
+-2047\
+-2046\
+-1026\
+-1025\
+-1024\
+-1023\
+-1022\
+-514\
+-513\
+-512\
+-511\
+-510\
+-258\
+-257\
+-256\
+-255\
+-254\
+-130\
+-129\
+-128\
+-127\
+-126\
+-66\
+-65\
+-64\
+-63\
+-62\
+-34\
+-33\
+-32\
+-31\
+-30\
+-18\
+-17\
+-16\
+-15\
+-14\
+-13\
+-12\
+-11\
+-10\
+-9\
+-8\
+-7\
+-6\
+-5\
+-4\
+-3\
+-2\
+-1\
+0\
+1\
+2\
+3\
+4\
+5\
+6\
+7\
+8\
+9\
+10\
+11\
+12\
+13\
+14\
+15\
+16\
+17\
+18\
+30\
+31\
+32\
+33\
+34\
+62\
+63\
+64\
+65\
+66\
+126\
+127\
+128\
+129\
+130\
+254\
+255\
+256\
+257\
+258\
+510\
+511\
+512\
+513\
+514\
+1022\
+1023\
+1024\
+1025\
+1026\
+2046\
+2047\
+2048\
+2049\
+2050\
+4094\
+4095\
+4096\
+4097\
+4098\
+8190\
+8191\
+8192\
+8193\
+8194\
+16382\
+16383\
+16384\
+16385\
+16386\
+32766\
+32767\
+32768\
+32769\
+32770\
+65534\
+65535\
+65536\
+65537\
+65538\
+131070\
+131071\
+131072\
+131073\
+131074\
+262142\
+262143\
+262144\
+262145\
+262146\
+524286\
+524287\
+524288\
+524289\
+524290\
+1048574\
+1048575\
+1048576\
+1048577\
+1048578\
+2097150\
+2097151\
+2097152\
+2097153\
+2097154\
+4194302\
+4194303\
+4194304\
+4194305\
+4194306\
+8388606\
+8388607\
+8388608\
+8388609\
+8388610\
+16777214\
+16777215\
+16777216\
+16777217\
+16777218\
+33554430\
+33554431\
+33554432\
+33554433\
+33554434\
+67108862\
+67108863\
+67108864\
+67108865\
+67108866\
+134217726\
+134217727\
+134217728\
+134217729\
+134217730\
+268435454\
+268435455\
+268435456\
+268435457\
+268435458\
+536870910\
+536870911\
+536870912\
+536870913\
+536870914\
+1073741822\
+1073741823\
+1073741824\
+1073741825\
+1073741826\
+2147483646\
+2147483647\
+2147483648\
+2147483649\
+2147483650\
+4294967294\
+4294967295\
+4294967296\
+4294967297\
+4294967298\
+8589934590\
+8589934591\
+8589934592\
+8589934593\
+8589934594\
+17179869182\
+17179869183\
+17179869184\
+17179869185\
+17179869186\
+34359738366\
+34359738367\
+34359738368\
+34359738369\
+34359738370\
+68719476734\
+68719476735\
+68719476736\
+68719476737\
+68719476738\
+137438953470\
+137438953471\
+137438953472\
+137438953473\
+137438953474\
+274877906942\
+274877906943\
+274877906944\
+274877906945\
+274877906946\
+549755813886\
+549755813887\
+549755813888\
+549755813889\
+549755813890\
+1099511627774\
+1099511627775\
+1099511627776\
+1099511627777\
+1099511627778\
+2199023255550\
+2199023255551\
+2199023255552\
+2199023255553\
+2199023255554\
+4398046511102\
+4398046511103\
+4398046511104\
+4398046511105\
+4398046511106\
+8796093022206\
+8796093022207\
+8796093022208\
+8796093022209\
+8796093022210\
+17592186044414\
+17592186044415\
+17592186044416\
+17592186044417\
+17592186044418\
+35184372088830\
+35184372088831\
+35184372088832\
+35184372088833\
+35184372088834\
+70368744177662\
+70368744177663\
+70368744177664\
+70368744177665\
+70368744177666\
+140737488355326\
+140737488355327\
+140737488355328\
+140737488355329\
+140737488355330\
+281474976710654\
+281474976710655\
+281474976710656\
+281474976710657\
+281474976710658\
+562949953421310\
+562949953421311\
+562949953421312\
+562949953421313\
+562949953421314\
+1125899906842622\
+1125899906842623\
+1125899906842624\
+1125899906842625\
+1125899906842626\
+2251799813685246\
+2251799813685247\
+2251799813685248\
+2251799813685249\
+2251799813685250\
+4503599627370494\
+4503599627370495\
+4503599627370496\
+4503599627370497\
+4503599627370498\
+9007199254740990\
+9007199254740991\
+9007199254740992\
+9007199254740993\
+9007199254740994\
+18014398509481982\
+18014398509481983\
+18014398509481984\
+18014398509481985\
+18014398509481986\
+36028797018963966\
+36028797018963967\
+36028797018963968\
+36028797018963969\
+36028797018963970\
+72057594037927934\
+72057594037927935\
+72057594037927936\
+72057594037927937\
+72057594037927938\
+144115188075855870\
+144115188075855871\
+144115188075855872\
+144115188075855873\
+144115188075855874\
+288230376151711742\
+288230376151711743\
+288230376151711744\
+288230376151711745\
+288230376151711746\
+576460752303423486\
+576460752303423487\
+576460752303423488\
+576460752303423489\
+576460752303423490\
+1152921504606846974\
+1152921504606846975\
+1152921504606846976\
+1152921504606846977\
+1152921504606846978\
+2305843009213693950\
+2305843009213693951\
+2305843009213693952\
+2305843009213693953\
+2305843009213693954\
+4611686018427387902\
+4611686018427387903\
+4611686018427387904\
+4611686018427387905\
+4611686018427387906\
+9223372036854775806\
+9223372036854775807}
+
+finish_test
diff --git a/third_party/sqlite/test/misc6.test b/third_party/sqlite/test/misc6.test
new file mode 100755
index 0000000..9840dd9
--- /dev/null
+++ b/third_party/sqlite/test/misc6.test
@@ -0,0 +1,48 @@
+# 2006 September 4
+#
+# 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.
+#
+# This file implements tests to make sure sqlite3_value_text()
+# always returns a null-terminated string.
+#
+# $Id: misc6.test,v 1.3 2007/04/23 23:56:32 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test misc6-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ sqlite3_create_function $DB
+ set STMT [sqlite3_prepare $DB {SELECT hex8(?)} -1 DUMMY]
+ set sqlite_static_bind_value {0123456789}
+ set sqlite_static_bind_nbyte 5
+ sqlite_bind $STMT 1 {} static-nbytes
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test misc6-1.2 {
+ sqlite3_column_text $STMT 0
+} {3031323334}
+ifcapable utf16 {
+ do_test misc6-1.3 {
+ sqlite3_finalize $STMT
+ set STMT [sqlite3_prepare $DB {SELECT hex16(?)} -1 DUMMY]
+ set sqlite_static_bind_value {0123456789}
+ set sqlite_static_bind_nbyte 5
+ sqlite_bind $STMT 1 {} static-nbytes
+ sqlite3_step $STMT
+ } SQLITE_ROW
+ do_test misc6-1.4 {
+ sqlite3_column_text $STMT 0
+ } {00300031003200330034}
+}
+sqlite3_finalize $STMT
+
+finish_test
diff --git a/third_party/sqlite/test/misc7.test b/third_party/sqlite/test/misc7.test
new file mode 100755
index 0000000..111d802
--- /dev/null
+++ b/third_party/sqlite/test/misc7.test
@@ -0,0 +1,475 @@
+# 2006 September 4
+#
+# 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.
+#
+# $Id: misc7.test,v 1.23 2008/08/02 03:50:40 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test misc7-1-misuse {
+ c_misuse_test
+} {}
+
+do_test misc7-2 {
+ c_realloc_test
+} {}
+
+do_test misc7-3 {
+ c_collation_test
+} {}
+
+# Try to open a directory:
+#
+do_test misc7-4 {
+ file delete mydir
+ file mkdir mydir
+ set rc [catch {
+ sqlite3 db2 ./mydir
+ } msg]
+ list $rc $msg
+} {1 {unable to open database file}}
+
+# Try to open a file with a directory where its journal file should be.
+#
+do_test misc7-5 {
+ file delete mydir
+ file mkdir mydir-journal
+ sqlite3 db2 ./mydir
+ catchsql {
+ CREATE TABLE abc(a, b, c);
+ } db2
+} {1 {unable to open database file}}
+db2 close
+
+#--------------------------------------------------------------------
+# The following tests, misc7-6.* test the libraries behaviour when
+# it cannot open a file. To force this condition, we use up all the
+# file-descriptors before running sqlite. This probably only works
+# on unix.
+#
+
+proc use_up_files {} {
+ set ret [list]
+ catch {
+ while 1 { lappend ret [open test.db] }
+ }
+ return $ret
+}
+
+proc do_fileopen_test {prefix sql} {
+ set fd_list [use_up_files]
+ set ::go 1
+ set ::n 1
+ set ::sql $sql
+ while {$::go} {
+ catch {db close}
+ do_test ${prefix}.${::n} {
+ set rc [catch {
+ sqlite db test.db
+ db eval $::sql
+ } msg]
+ if {$rc == 0} {set ::go 0}
+
+ expr {$rc == 0 || ($rc == 1 && [string first unable $msg]==0)}
+ } 1
+
+ close [lindex $fd_list 0]
+ set fd_list [lrange $fd_list 1 end]
+ incr ::n
+ }
+ foreach fd $fd_list {
+ close $fd
+ }
+ db close
+}
+
+execsql { CREATE TABLE abc(a PRIMARY KEY, b, c); }
+db close
+
+if {$tcl_platform(platform)!="windows"} {
+ do_fileopen_test misc7-6.1 {
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(2, 3, 4);
+ INSERT INTO abc SELECT a+2, b, c FROM abc;
+ COMMIT;
+ }
+
+ do_fileopen_test misc7-6.2 {
+ PRAGMA temp.cache_size = 1000;
+ }
+}
+
+#
+# End of tests for out-of-file-descriptors condition.
+#--------------------------------------------------------------------
+
+sqlite3 db test.db
+execsql {
+ DELETE FROM abc;
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(2, 3, 4);
+ INSERT INTO abc SELECT a+2, b, c FROM abc;
+}
+
+
+#--------------------------------------------------------------------
+# Test that the sqlite3_busy_timeout call seems to delay approximately
+# the right amount of time.
+#
+do_test misc7-7.0 {
+ sqlite3 db2 test.db
+ sqlite3_busy_timeout [sqlite3_connection_pointer db] 2000
+ execsql {
+ BEGIN EXCLUSIVE;
+ } db2
+
+ # Now db2 has an exclusive lock on the database file, and db has
+ # a busy-timeout of 2000 milliseconds. So check that trying to
+ # access the database using connection db delays for at least 1500 ms.
+ #
+ set tm [time {
+ set result [catchsql {
+ SELECT * FROM sqlite_master;
+ } db]
+ }]
+ set delay [lindex $tm 0] ;# In microseconds
+ lappend result [expr {$delay>1500000 && $delay<4000000}]
+} {1 {database is locked} 1}
+db2 close
+
+#--------------------------------------------------------------------
+# Test that nothing goes horribly wrong when attaching a database
+# after the omit_readlock pragma has been exercised.
+#
+do_test misc7-7.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ PRAGMA omit_readlock = 1;
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.hello(world);
+ SELECT name FROM aux.sqlite_master;
+ }
+} {hello}
+do_test misc7-7.2 {
+ execsql {
+ DETACH aux;
+ }
+} {}
+
+# Test the UTF-16 version of the "out of memory" message (used when
+# malloc fails during sqlite3_open() ).
+#
+ifcapable utf16 {
+ do_test misc7-8 {
+ encoding convertfrom unicode [sqlite3_errmsg16 0x00000000]
+ } {out of memory}
+}
+
+do_test misc7-9 {
+ execsql {
+ SELECT *
+ FROM (SELECT name+1 AS one FROM sqlite_master LIMIT 1 OFFSET 1)
+ WHERE one LIKE 'hello%';
+ }
+} {}
+
+#--------------------------------------------------------------------
+# Improve coverage for vtab code.
+#
+ifcapable vtab {
+ # Run some debug code to improve reported coverage
+ #
+
+ # set sqlite_where_trace 1
+ do_test misc7-10 {
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING echo(abc);
+ SELECT a FROM t1 WHERE a = 1 ORDER BY b;
+ }
+ } {1}
+ set sqlite_where_trace 0
+
+ # Specify an ORDER BY clause that cannot be indexed.
+ do_test misc7-11 {
+ execsql {
+ SELECT t1.a, t2.a FROM t1, t1 AS t2 ORDER BY 2 LIMIT 1;
+ }
+ } {1 1}
+
+ # The whole point of this is to test an error code other than
+ # SQLITE_NOMEM from the vtab xBestIndex callback.
+ #
+ do_ioerr_test misc7-12 -tclprep {
+ sqlite3 db2 test.db
+ register_echo_module [sqlite3_connection_pointer db2]
+ db2 eval {
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ CREATE VIRTUAL TABLE t1 USING echo(abc);
+ }
+ db2 close
+ } -tclbody {
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {SELECT * FROM t1 WHERE a = 1;}
+ }
+
+ # The case where the virtual table module returns a very large number
+ # as the cost of a scan (greater than SQLITE_BIG_DOUBLE in the code).
+ #
+ do_test misc7-13 {
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+ set ::echo_module_cost 2.0e+99
+ execsql {SELECT * FROM t1 WHERE a = 1;}
+ } {1 2 3}
+ unset ::echo_module_cost
+}
+
+db close
+file delete -force test.db
+file delete -force test.db-journal
+sqlite3 db test.db
+
+ifcapable explain {
+ do_test misc7-14.1 {
+ execsql {
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ }
+ execsql {
+ EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid = 1;
+ }
+ } {0 0 {TABLE abc AS t2 USING PRIMARY KEY}}
+ do_test misc7-14.2 {
+ execsql {
+ EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE a = 1;
+ }
+ } {0 0 {TABLE abc AS t2 WITH INDEX sqlite_autoindex_abc_1}}
+ do_test misc7-14.3 {
+ execsql {
+ EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 ORDER BY a;
+ }
+ } {0 0 {TABLE abc AS t2 WITH INDEX sqlite_autoindex_abc_1 ORDER BY}}
+}
+
+db close
+file delete -force test.db
+file delete -force test.db-journal
+sqlite3 db test.db
+
+#--------------------------------------------------------------------
+# This is all to force the pager_remove_from_stmt_list() function
+# (inside pager.c) to remove a pager from the middle of the
+# statement-list.
+#
+do_test misc7-15.1 {
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ INSERT INTO abc
+ VALUES(randstr(100,100), randstr(100,100), randstr(100,100));
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ COMMIT;
+ }
+ expr {[file size test.db]>10240}
+} {1}
+do_test misc7-15.2 {
+ execsql {
+ DELETE FROM abc WHERE rowid > 12;
+ INSERT INTO abc SELECT
+ randstr(100,100), randstr(100,100), randstr(100,100) FROM abc;
+ }
+} {}
+
+db close
+file delete -force test.db
+file delete -force test.db-journal
+sqlite3 db test.db
+
+do_ioerr_test misc7-16 -sqlprep {
+ PRAGMA cache_size = 10;
+ PRAGMA default_cache_size = 10;
+ CREATE TABLE t3(a, b, UNIQUE(a, b));
+ INSERT INTO t3 VALUES( randstr(100, 100), randstr(100, 100) );
+ INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+ INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+ INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+ INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+ INSERT INTO t3 SELECT randstr(100, 100), randstr(100, 100) FROM t3;
+ UPDATE t3
+ SET b = 'hello world'
+ WHERE rowid >= (SELECT max(rowid)-1 FROM t3);
+} -tclbody {
+ set rc [catch {db eval {
+ BEGIN;
+ PRAGMA cache_size = 10;
+ INSERT INTO t3 VALUES( randstr(100, 100), randstr(100, 100) );
+ UPDATE t3 SET a = b;
+ COMMIT;
+ }} msg]
+
+ if {!$rc || ($rc && [string first "columns" $msg]==0)} {
+ set msg
+ } else {
+ error $msg
+ }
+}
+
+sqlite3 db test.db
+
+do_test misc7-16.X {
+ execsql {
+ SELECT count(*) FROM t3;
+ }
+} {32}
+
+set sqlite_pager_n_sort_bucket 4
+do_test misc7-17 {
+ execsql {
+ PRAGMA integrity_check;
+ VACUUM;
+ PRAGMA integrity_check;
+ }
+} {ok ok}
+set sqlite_pager_n_sort_bucket 0
+
+#----------------------------------------------------------------------
+# Test the situation where a hot-journal is discovered but write-access
+# to it is denied. This should return SQLITE_BUSY.
+#
+# These tests do not work on windows due to restrictions in the
+# windows file system.
+#
+if {$tcl_platform(platform)!="windows"} {
+
+ # Some network filesystems (ex: AFP) do not support setting read-only
+ # permissions. Only run these tests if full unix permission setting
+ # capabilities are supported.
+ #
+ file attributes test.db -permissions rw-r--r--
+ if {[file attributes test.db -permissions]==0644} {
+
+ do_test misc7-17.1 {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE (oid%3)==0;
+ }
+ copy_file test.db bak.db
+ copy_file test.db-journal bak.db-journal
+ execsql {
+ COMMIT;
+ }
+
+ db close
+ copy_file bak.db test.db
+ copy_file bak.db-journal test.db-journal
+ sqlite3 db test.db
+
+ catch {file attributes test.db-journal -permissions r--------}
+ catch {file attributes test.db-journal -readonly 1}
+ catchsql {
+ SELECT count(*) FROM t3;
+ }
+ } {1 {database is locked}}
+ do_test misc7-17.2 {
+ # Note that the -readonly flag must be cleared before the -permissions
+ # are set. Otherwise, when using tcl 8.5 on mac, the fact that the
+ # -readonly flag is set causes the attempt to set the permissions
+ # to fail.
+ catch {file attributes test.db-journal -readonly 0}
+ catch {file attributes test.db-journal -permissions rw-------}
+ catchsql {
+ SELECT count(*) FROM t3;
+ }
+ } {0 32}
+
+ set ::pending_byte_page [expr ($::sqlite_pending_byte / 1024) + 1]
+ do_test misc7-17.3 {
+ db eval {
+ pragma writable_schema = true;
+ UPDATE sqlite_master
+ SET rootpage = $pending_byte_page
+ WHERE type = 'table' AND name = 't3';
+ }
+ execsql {
+ SELECT rootpage FROM sqlite_master WHERE type = 'table' AND name = 't3';
+ }
+ } $::pending_byte_page
+
+ do_test misc7-17.4 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT count(*) FROM t3;
+ }
+ } {1 {database disk image is malformed}}
+ }
+}
+
+# Ticket #2470
+#
+do_test misc7-18.1 {
+ execsql {
+ CREATE TABLE table_1 (col_10);
+ CREATE TABLE table_2 (
+ col_1, col_2, col_3, col_4, col_5,
+ col_6, col_7, col_8, col_9, col_10
+ );
+ SELECT a.col_10
+ FROM
+ (SELECT table_1.col_10 AS col_10 FROM table_1) a,
+ (SELECT table_1.col_10, table_2.col_9 AS qcol_9
+ FROM table_1, table_2
+ GROUP BY table_1.col_10, qcol_9);
+ }
+} {}
+
+# Testing boundary conditions on sqlite3_status()
+#
+do_test misc7-19.1 {
+ sqlite3_status -1 0
+} {21 0 0}
+do_test misc7-19.2 {
+ sqlite3_status 1000 0
+} {21 0 0}
+
+
+# sqlite3_global_recover() is a no-op. But we might as well test it
+# if only to get the test coverage.
+#
+do_test misc7-20.1 {
+ sqlite3_global_recover
+} {SQLITE_OK}
+
+
+db close
+file delete -force test.db
+
+finish_test
diff --git a/third_party/sqlite/test/misuse.test b/third_party/sqlite/test/misuse.test
new file mode 100755
index 0000000..3734aa0
--- /dev/null
+++ b/third_party/sqlite/test/misuse.test
@@ -0,0 +1,207 @@
+# 2002 May 10
+#
+# 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.
+#
+# This file implements tests for the SQLITE_MISUSE detection logic.
+# This test file leaks memory and file descriptors.
+#
+# $Id: misuse.test,v 1.11 2006/01/03 00:33:50 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc catchsql2 {sql} {
+ set r [
+ catch {
+ set res [list]
+ db eval $sql data {
+ if { $res==[list] } {
+ foreach f $data(*) {lappend res $f}
+ }
+ foreach f $data(*) {lappend res $data($f)}
+ }
+ set res
+ } msg
+ ]
+ lappend r $msg
+}
+
+
+# Make sure the test logic works
+#
+do_test misuse-1.1 {
+ db close
+ catch {file delete -force test2.db}
+ catch {file delete -force test2.db-journal}
+ sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ }
+ catchsql2 {
+ SELECT * FROM t1
+ }
+} {0 {a b 1 2}}
+do_test misuse-1.2 {
+ catchsql2 {
+ SELECT x_coalesce(NULL,a) AS 'xyz' FROM t1
+ }
+} {1 {no such function: x_coalesce}}
+do_test misuse-1.3 {
+ sqlite3_create_function $::DB
+ catchsql2 {
+ SELECT x_coalesce(NULL,a) AS 'xyz' FROM t1
+ }
+} {0 {xyz 1}}
+
+# Use the x_sqlite_exec() SQL function to simulate the effect of two
+# threads trying to use the same database at the same time.
+#
+# It used to be prohibited to invoke sqlite_exec() from within a function,
+# but that has changed. The following tests used to cause errors but now
+# they do not.
+#
+ifcapable {utf16} {
+ do_test misuse-1.4 {
+ catchsql2 {
+ SELECT x_sqlite_exec('SELECT * FROM t1') AS xyz;
+ }
+ } {0 {xyz {1 2}}}
+}
+do_test misuse-1.5 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+do_test misuse-1.6 {
+ catchsql {
+ SELECT * FROM t1
+ }
+} {0 {1 2}}
+
+# Attempt to register a new SQL function while an sqlite_exec() is active.
+#
+do_test misuse-2.1 {
+ db close
+ sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ SELECT * FROM t1
+ }
+} {1 2}
+do_test misuse-2.2 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+
+# We used to disallow creating new function from within an exec().
+# But now this is acceptable.
+do_test misuse-2.3 {
+ set v [catch {
+ db eval {SELECT * FROM t1} {} {
+ sqlite3_create_function $::DB
+ }
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test misuse-2.4 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+do_test misuse-2.5 {
+ catchsql {
+ SELECT * FROM t1
+ }
+} {0 {1 2}}
+
+# Attempt to register a new SQL aggregate while an sqlite_exec() is active.
+#
+do_test misuse-3.1 {
+ db close
+ sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ SELECT * FROM t1
+ }
+} {1 2}
+do_test misuse-3.2 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+
+# We used to disallow creating new function from within an exec().
+# But now this is acceptable.
+do_test misuse-3.3 {
+ set v [catch {
+ db eval {SELECT * FROM t1} {} {
+ sqlite3_create_aggregate $::DB
+ }
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test misuse-3.4 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+do_test misuse-3.5 {
+ catchsql {
+ SELECT * FROM t1
+ }
+} {0 {1 2}}
+
+# Attempt to close the database from an sqlite_exec callback.
+#
+# Update for v3: The db cannot be closed because there are active
+# VMs. The sqlite3_close call would return SQLITE_BUSY.
+do_test misuse-4.1 {
+ db close
+ sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ SELECT * FROM t1
+ }
+} {1 2}
+do_test misuse-4.2 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+do_test misuse-4.3 {
+ set v [catch {
+ db eval {SELECT * FROM t1} {} {
+ set r [sqlite3_close $::DB]
+ }
+ } msg]
+ lappend v $msg $r
+} {0 {} SQLITE_BUSY}
+do_test misuse-4.4 {
+ # Flush the TCL statement cache here, otherwise the sqlite3_close() will
+ # fail because there are still un-finalized() VDBEs.
+ db cache flush
+ sqlite3_close $::DB
+ catchsql2 {SELECT * FROM t1}
+} {1 {library routine called out of sequence}}
+do_test misuse-4.5 {
+ catchsql {
+ SELECT * FROM t1
+ }
+} {1 {library routine called out of sequence}}
+
+# Attempt to use a database after it has been closed.
+#
+do_test misuse-5.1 {
+ db close
+ sqlite3 db test2.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ SELECT * FROM t1
+ }
+} {1 2}
+do_test misuse-5.2 {
+ catchsql2 {SELECT * FROM t1}
+} {0 {a b 1 2}}
+do_test misuse-5.3 {
+ db close
+ set r [catch {
+ sqlite3_prepare $::DB {SELECT * FROM t1} -1 TAIL
+ } msg]
+ lappend r $msg
+} {1 {(21) library routine called out of sequence}}
+
+finish_test
diff --git a/third_party/sqlite/test/mutex1.test b/third_party/sqlite/test/mutex1.test
new file mode 100755
index 0000000..f52d4fe
--- /dev/null
+++ b/third_party/sqlite/test/mutex1.test
@@ -0,0 +1,163 @@
+# 2008 June 17
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: mutex1.test,v 1.10 2008/07/15 00:27:35 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {[info exists tester_do_binarylog]} {
+ finish_test
+ return
+}
+
+sqlite3_reset_auto_extension
+
+proc mutex_counters {varname} {
+ upvar $varname var
+ set var(total) 0
+ foreach {name value} [read_mutex_counters] {
+ set var($name) $value
+ incr var(total) $value
+ }
+}
+
+#-------------------------------------------------------------------------
+# Tests mutex1-1.* test that sqlite3_config() returns SQLITE_MISUSE if
+# is called at the wrong time. And that the first time sqlite3_initialize
+# is called it obtains the 'static_master' mutex 3 times and a recursive
+# mutex (sqlite3Config.pInitMutex) twice. Subsequent calls are no-ops
+# that do not require any mutexes.
+#
+do_test mutex1-1.0 {
+ install_mutex_counters 1
+} {SQLITE_MISUSE}
+
+do_test mutex1-1.1 {
+ db close
+ install_mutex_counters 1
+} {SQLITE_MISUSE}
+
+do_test mutex1-1.2 {
+ sqlite3_shutdown
+ install_mutex_counters 1
+} {SQLITE_OK}
+
+do_test mutex1-1.3 {
+ install_mutex_counters 0
+} {SQLITE_OK}
+
+do_test mutex1-1.4 {
+ install_mutex_counters 1
+} {SQLITE_OK}
+
+do_test mutex1-1.5 {
+ mutex_counters counters
+ set counters(total)
+} {0}
+
+do_test mutex1-1.6 {
+ sqlite3_initialize
+} {SQLITE_OK}
+
+do_test mutex1-1.7 {
+ mutex_counters counters
+ # list $counters(total) $counters(static_master)
+ expr {$counters(total)>0}
+} {1}
+
+do_test mutex1-1.8 {
+ clear_mutex_counters
+ sqlite3_initialize
+} {SQLITE_OK}
+
+do_test mutex1-1.9 {
+ mutex_counters counters
+ list $counters(total) $counters(static_master)
+} {0 0}
+
+#-------------------------------------------------------------------------
+# Tests mutex1-2.* test the three thread-safety related modes that
+# can be selected using sqlite3_config:
+#
+# * Serialized mode,
+# * Multi-threaded mode,
+# * Single-threaded mode.
+#
+set enable_shared_cache [sqlite3_enable_shared_cache 1]
+ifcapable threadsafe {
+ foreach {mode mutexes} {
+ singlethread {}
+ multithread {fast static_master static_mem static_prng}
+ serialized {fast recursive static_master static_mem static_prng}
+ } {
+ ifcapable memorymanage {
+ if {$mode ne "singlethread"} {
+ lappend mutexes static_lru static_lru2 static_mem2
+ }
+ }
+
+ do_test mutex1.2.$mode.1 {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config $mode
+ } SQLITE_OK
+
+ do_test mutex1.2.$mode.2 {
+ sqlite3_initialize
+ clear_mutex_counters
+ sqlite3 db test.db
+ catchsql { CREATE TABLE abc(a, b, c) }
+ db eval {
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+ } {}
+
+ do_test mutex1.2.$mode.3 {
+ mutex_counters counters
+
+ set res [list]
+ foreach {key value} [array get counters] {
+ if {$key ne "total" && $value > 0} {
+ lappend res $key
+ }
+ }
+ lsort $res
+ } [lsort $mutexes]
+ }
+}
+sqlite3_enable_shared_cache $enable_shared_cache
+
+# Open and use a connection in "nomutex" mode. Test that no recursive
+# mutexes are obtained.
+ifcapable threadsafe {
+ do_test mutex1.3.1 {
+ catch {db close}
+ clear_mutex_counters
+ sqlite3 db test.db -nomutex 1
+ execsql { SELECT * FROM abc }
+ } {1 2 3 1 2 3 1 2 3}
+ do_test mutex1.3.2 {
+ mutex_counters counters
+ set counters(recursive)
+ } {0}
+}
+
+do_test mutex1-X {
+ catch {db close}
+ sqlite3_shutdown
+ clear_mutex_counters
+ install_mutex_counters 0
+ sqlite3_initialize
+} {SQLITE_OK}
+
+autoinstall_test_functions
+finish_test
diff --git a/third_party/sqlite/test/mutex2.test b/third_party/sqlite/test/mutex2.test
new file mode 100755
index 0000000..6aa6715
--- /dev/null
+++ b/third_party/sqlite/test/mutex2.test
@@ -0,0 +1,101 @@
+# 2008 July 7
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test scripts for deliberate failures of mutex routines.
+#
+# $Id: mutex2.test,v 1.8 2008/07/19 13:43:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# deinitialize
+#
+catch {db close}
+sqlite3_reset_auto_extension
+sqlite3_shutdown
+install_mutex_counters 1
+
+# Fix the mutex subsystem so that it will not initialize. In other words,
+# make it so that sqlite3_initialize() always fails.
+#
+do_test mutex2-1.1 {
+ set ::disable_mutex_init 10
+ sqlite3_initialize
+} {SQLITE_IOERR}
+do_test mutex2-1.1 {
+ set ::disable_mutex_init 7
+ sqlite3_initialize
+} {SQLITE_NOMEM}
+
+proc utf16 {str} {
+ set r [encoding convertto unicode $str]
+ append r "\x00\x00"
+ return $r
+}
+
+# Now that sqlite3_initialize() is failing, try to run various APIs that
+# require that SQLite be initialized. Verify that they fail.
+#
+do_test mutex2-2.1 {
+ set ::disable_mutex_init 7
+ set rc [catch {sqlite db test.db} msg]
+ lappend rc $msg
+} {1 {}}
+ifcapable utf16 {
+ do_test mutex2-2.2 {
+ set db2 [sqlite3_open16 [utf16 test.db] {}]
+ } {0}
+ do_test mutex2-2.3 {
+ sqlite3_complete16 [utf16 {SELECT * FROM t1;}]
+ } {7}
+}
+do_test mutex2-2.4 {
+ sqlite3_mprintf_int {This is a test %d,%d,%d} 1 2 3
+} {}
+ifcapable load_ext {
+ do_test mutex2-2.5 {
+ sqlite3_auto_extension_sqr
+ } {7}
+}
+do_test mutex2-2.6 {
+ sqlite3_reset_auto_extension
+} {}
+do_test mutex2-2.7 {
+ sqlite3_malloc 10000
+} {0}
+do_test mutex2-2.8 {
+ sqlite3_realloc 0 10000
+} {0}
+ifcapable threadsafe {
+ do_test mutex2-2.9 {
+ alloc_dealloc_mutex
+ } {0}
+}
+do_test mutex2-2.10 {
+ vfs_initfail_test
+} {}
+
+# Restore the system to a functional state
+#
+install_mutex_counters 0
+set disable_mutex_init 0
+autoinstall_test_functions
+
+# Mutex allocation works now.
+#
+
+do_test mutex2-3.1 {
+ set ptr [alloc_dealloc_mutex]
+ expr {$ptr!=0}
+} {1}
+
+
+finish_test
diff --git a/third_party/sqlite/test/nan.test b/third_party/sqlite/test/nan.test
new file mode 100755
index 0000000..d5645ac
--- /dev/null
+++ b/third_party/sqlite/test/nan.test
@@ -0,0 +1,228 @@
+# 2008 April 28
+#
+# 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.
+#
+#***********************************************************************
+#
+# Ticket #3060
+#
+# Make sure IEEE floating point NaN values are handled properly.
+# SQLite should always convert NaN into NULL.
+#
+# Also verify that the decimal to IEEE754 binary conversion routines
+# correctly generate 0.0, +Inf, and -Inf as appropriate for numbers
+# out of range.
+#
+# $Id: nan.test,v 1.3 2008/05/11 11:07:07 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_test nan-1.1 {
+ db eval {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ CREATE TABLE t1(x FLOAT);
+ }
+ set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL]
+ sqlite3_bind_double $::STMT 1 NaN
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+do_test nan-1.2 {
+ sqlite3_bind_double $::STMT 1 +Inf
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null inf real}
+do_test nan-1.3 {
+ sqlite3_bind_double $::STMT 1 -Inf
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null inf real -inf real}
+do_test nan-1.4 {
+ sqlite3_bind_double $::STMT 1 -NaN
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null inf real -inf real {} null}
+do_test nan-1.5 {
+ sqlite3_bind_double $::STMT 1 NaN0
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null inf real -inf real {} null {} null}
+do_test nan-1.5 {
+ sqlite3_bind_double $::STMT 1 -NaN0
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null inf real -inf real {} null {} null {} null}
+do_test nan-1.6 {
+ db eval {
+ UPDATE t1 SET x=x-x;
+ SELECT x, typeof(x) FROM t1;
+ }
+} {{} null {} null {} null {} null {} null {} null}
+
+do_test nan-2.1 {
+ db eval {
+ DELETE FROM T1;
+ }
+ sqlite3_bind_double $::STMT 1 NaN
+ sqlite3_step $::STMT
+ sqlite3_reset $::STMT
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+sqlite3_finalize $::STMT
+
+# SQLite always converts NaN into NULL so it is not possible to write
+# a NaN value into the database file using SQLite. The following series
+# of tests writes a normal floating point value (0.5) into the database,
+# then writes directly into the database file to change the 0.5 into NaN.
+# Then it reads the value of the database to verify it is converted into
+# NULL.
+#
+do_test nan-3.1 {
+ db eval {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(0.5);
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size=1024;
+ VACUUM;
+ }
+ hexio_read test.db 2040 8
+} {3FE0000000000000}
+do_test nan-3.2 {
+ db eval {
+ SELECT x, typeof(x) FROM t1
+ }
+} {0.5 real}
+do_test nan-3.3 {
+ db close
+ hexio_write test.db 2040 FFF8000000000000
+ sqlite3 db test.db
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+do_test nan-3.4 {
+ db close
+ hexio_write test.db 2040 7FF8000000000000
+ sqlite3 db test.db
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+do_test nan-3.5 {
+ db close
+ hexio_write test.db 2040 FFFFFFFFFFFFFFFF
+ sqlite3 db test.db
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+do_test nan-3.6 {
+ db close
+ hexio_write test.db 2040 7FFFFFFFFFFFFFFF
+ sqlite3 db test.db
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+
+# Verify that the sqlite3AtoF routine is able to handle extreme
+# numbers.
+#
+do_test nan-4.1 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES([string repeat 9 307].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {1e+307 real}
+do_test nan-4.2 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES([string repeat 9 308].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {1e+308 real}
+do_test nan-4.3 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES([string repeat 9 309].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {inf real}
+do_test nan-4.4 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES(-[string repeat 9 307].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-1e+307 real}
+do_test nan-4.5 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES(-[string repeat 9 308].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-1e+308 real}
+do_test nan-4.6 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-inf real}
+do_test nan-4.7 {
+ db eval {DELETE FROM t1}
+ set big -[string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
+ db eval "INSERT INTO t1 VALUES($big)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-1e+308 real}
+do_test nan-4.8 {
+ db eval {DELETE FROM t1}
+ set big [string repeat 0 10000][string repeat 9 308].[string repeat 0 10000]
+ db eval "INSERT INTO t1 VALUES($big)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {1e+308 real}
+
+
+do_test nan-4.10 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES(1234.5[string repeat 0 10000]12345)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {1234.5 real}
+do_test nan-4.11 {
+ db eval {DELETE FROM t1}
+ db eval "INSERT INTO t1 VALUES(-1234.5[string repeat 0 10000]12345)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-1234.5 real}
+do_test nan-4.12 {
+ db eval {DELETE FROM t1}
+ set small [string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
+ db eval "INSERT INTO t1 VALUES($small)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {9.88131291682493e-324 real}
+do_test nan-4.13 {
+ db eval {DELETE FROM t1}
+ set small [string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
+ db eval "INSERT INTO t1 VALUES($small)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {0.0 real}
+do_test nan-4.14 {
+ db eval {DELETE FROM t1}
+ set small \
+ -[string repeat 0 10000].[string repeat 0 323][string repeat 9 10000]
+ db eval "INSERT INTO t1 VALUES($small)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {-9.88131291682493e-324 real}
+do_test nan-4.15 {
+ db eval {DELETE FROM t1}
+ set small \
+ -[string repeat 0 10000].[string repeat 0 324][string repeat 9 10000]
+ db eval "INSERT INTO t1 VALUES($small)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {0.0 real}
+
+do_test nan-4.20 {
+ db eval {DELETE FROM t1}
+ set big [string repeat 9 10000].0e-9000
+ db eval "INSERT INTO t1 VALUES($big)"
+ db eval {SELECT x, typeof(x) FROM t1}
+} {{} null}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/notnull.test b/third_party/sqlite/test/notnull.test
new file mode 100755
index 0000000..5af9940
--- /dev/null
+++ b/third_party/sqlite/test/notnull.test
@@ -0,0 +1,505 @@
+# 2002 January 29
+#
+# 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.
+#
+# This file implements tests for the NOT NULL constraint.
+#
+# $Id: notnull.test,v 1.4 2006/01/17 09:35:02 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !conflict {
+ finish_test
+ return
+}
+
+do_test notnull-1.0 {
+ execsql {
+ CREATE TABLE t1 (
+ a NOT NULL,
+ b NOT NULL DEFAULT 5,
+ c NOT NULL ON CONFLICT REPLACE DEFAULT 6,
+ d NOT NULL ON CONFLICT IGNORE DEFAULT 7,
+ e NOT NULL ON CONFLICT ABORT DEFAULT 8
+ );
+ SELECT * FROM t1;
+ }
+} {}
+do_test notnull-1.1 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-1.2 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-1.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-1.4 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-1.5 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-1.6 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-1.7 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-1.8 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-1.9 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-1.10 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.b may not be NULL}}
+do_test notnull-1.11 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-1.12 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-1.13 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 6 4 5}}
+do_test notnull-1.14 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-1.15 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 6 4 5}}
+do_test notnull-1.16 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.c may not be NULL}}
+do_test notnull-1.17 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,3,null,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.d may not be NULL}}
+do_test notnull-1.18 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,e) VALUES(1,2,3,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 7 5}}
+do_test notnull-1.19 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d) VALUES(1,2,3,4);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 4 8}}
+do_test notnull-1.20 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,null);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.e may not be NULL}}
+do_test notnull-1.21 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(e,d,c,b,a) VALUES(1,2,3,null,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {5 5 3 2 1}}
+
+do_test notnull-2.1 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-2.2 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR REPLACE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-2.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR IGNORE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-2.4 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR ABORT t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-2.5 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET b=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.b may not be NULL}}
+do_test notnull-2.6 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR REPLACE t1 SET b=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 5 3 5 4}}
+do_test notnull-2.7 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR IGNORE t1 SET b=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-2.8 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET c=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 6 5 4}}
+do_test notnull-2.9 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET d=null, a=b, b=a;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-2.10 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET e=null, a=b, b=a;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.e may not be NULL}}
+
+do_test notnull-3.0 {
+ execsql {
+ CREATE INDEX t1a ON t1(a);
+ CREATE INDEX t1b ON t1(b);
+ CREATE INDEX t1c ON t1(c);
+ CREATE INDEX t1d ON t1(d);
+ CREATE INDEX t1e ON t1(e);
+ CREATE INDEX t1abc ON t1(a,b,c);
+ }
+} {}
+do_test notnull-3.1 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-3.2 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-3.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-3.4 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-3.5 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(b,c,d,e) VALUES(2,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-3.6 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-3.7 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-3.8 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-3.9 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,c,d,e) VALUES(1,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-3.10 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.b may not be NULL}}
+do_test notnull-3.11 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-3.12 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 5 3 4 5}}
+do_test notnull-3.13 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 6 4 5}}
+do_test notnull-3.14 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR IGNORE INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {}}
+do_test notnull-3.15 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 6 4 5}}
+do_test notnull-3.16 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.c may not be NULL}}
+do_test notnull-3.17 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,3,null,5);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.d may not be NULL}}
+do_test notnull-3.18 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR ABORT INTO t1(a,b,c,e) VALUES(1,2,3,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 7 5}}
+do_test notnull-3.19 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d) VALUES(1,2,3,4);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {1 2 3 4 8}}
+do_test notnull-3.20 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,null);
+ SELECT * FROM t1 order by a;
+ }
+} {1 {t1.e may not be NULL}}
+do_test notnull-3.21 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT OR REPLACE INTO t1(e,d,c,b,a) VALUES(1,2,3,null,5);
+ SELECT * FROM t1 order by a;
+ }
+} {0 {5 5 3 2 1}}
+
+do_test notnull-4.1 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-4.2 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR REPLACE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-4.3 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR IGNORE t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-4.4 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR ABORT t1 SET a=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.a may not be NULL}}
+do_test notnull-4.5 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET b=null;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.b may not be NULL}}
+do_test notnull-4.6 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR REPLACE t1 SET b=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 5 3 5 4}}
+do_test notnull-4.7 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE OR IGNORE t1 SET b=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-4.8 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET c=null, d=e, e=d;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 6 5 4}}
+do_test notnull-4.9 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET d=null, a=b, b=a;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {0 {1 2 3 4 5}}
+do_test notnull-4.10 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2,3,4,5);
+ UPDATE t1 SET e=null, a=b, b=a;
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 {t1.e may not be NULL}}
+
+finish_test
diff --git a/third_party/sqlite/test/null.test b/third_party/sqlite/test/null.test
new file mode 100755
index 0000000..f3782a7
--- /dev/null
+++ b/third_party/sqlite/test/null.test
@@ -0,0 +1,283 @@
+# 2001 September 15
+#
+# 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.
+#
+# This file implements tests for proper treatment of the special
+# value NULL.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table and some data to work with.
+#
+do_test null-1.0 {
+ execsql {
+ begin;
+ create table t1(a,b,c);
+ insert into t1 values(1,0,0);
+ insert into t1 values(2,0,1);
+ insert into t1 values(3,1,0);
+ insert into t1 values(4,1,1);
+ insert into t1 values(5,null,0);
+ insert into t1 values(6,null,1);
+ insert into t1 values(7,null,null);
+ commit;
+ select * from t1;
+ }
+} {1 0 0 2 0 1 3 1 0 4 1 1 5 {} 0 6 {} 1 7 {} {}}
+
+# Check for how arithmetic expressions handle NULL
+#
+do_test null-1.1 {
+ execsql {
+ select ifnull(a+b,99) from t1;
+ }
+} {1 2 4 5 99 99 99}
+do_test null-1.2 {
+ execsql {
+ select ifnull(b*c,99) from t1;
+ }
+} {0 0 0 1 99 99 99}
+
+# Check to see how the CASE expression handles NULL values. The
+# first WHEN for which the test expression is TRUE is selected.
+# FALSE and UNKNOWN test expressions are skipped.
+#
+do_test null-2.1 {
+ execsql {
+ select ifnull(case when b<>0 then 1 else 0 end, 99) from t1;
+ }
+} {0 0 1 1 0 0 0}
+do_test null-2.2 {
+ execsql {
+ select ifnull(case when not b<>0 then 1 else 0 end, 99) from t1;
+ }
+} {1 1 0 0 0 0 0}
+do_test null-2.3 {
+ execsql {
+ select ifnull(case when b<>0 and c<>0 then 1 else 0 end, 99) from t1;
+ }
+} {0 0 0 1 0 0 0}
+do_test null-2.4 {
+ execsql {
+ select ifnull(case when not (b<>0 and c<>0) then 1 else 0 end, 99) from t1;
+ }
+} {1 1 1 0 1 0 0}
+do_test null-2.5 {
+ execsql {
+ select ifnull(case when b<>0 or c<>0 then 1 else 0 end, 99) from t1;
+ }
+} {0 1 1 1 0 1 0}
+do_test null-2.6 {
+ execsql {
+ select ifnull(case when not (b<>0 or c<>0) then 1 else 0 end, 99) from t1;
+ }
+} {1 0 0 0 0 0 0}
+do_test null-2.7 {
+ execsql {
+ select ifnull(case b when c then 1 else 0 end, 99) from t1;
+ }
+} {1 0 0 1 0 0 0}
+do_test null-2.8 {
+ execsql {
+ select ifnull(case c when b then 1 else 0 end, 99) from t1;
+ }
+} {1 0 0 1 0 0 0}
+
+# Check to see that NULL values are ignored in aggregate functions.
+#
+do_test null-3.1 {
+ execsql {
+ select count(*), count(b), count(c), sum(b), sum(c),
+ avg(b), avg(c), min(b), max(b) from t1;
+ }
+} {7 4 6 2 3 0.5 0.5 0 1}
+
+# The sum of zero entries is a NULL, but the total of zero entries is 0.
+#
+do_test null-3.2 {
+ execsql {
+ SELECT sum(b), total(b) FROM t1 WHERE b<0
+ }
+} {{} 0.0}
+
+# Check to see how WHERE clauses handle NULL values. A NULL value
+# is the same as UNKNOWN. The WHERE clause should only select those
+# rows that are TRUE. FALSE and UNKNOWN rows are rejected.
+#
+do_test null-4.1 {
+ execsql {
+ select a from t1 where b<10
+ }
+} {1 2 3 4}
+do_test null-4.2 {
+ execsql {
+ select a from t1 where not b>10
+ }
+} {1 2 3 4}
+do_test null-4.3 {
+ execsql {
+ select a from t1 where b<10 or c=1;
+ }
+} {1 2 3 4 6}
+do_test null-4.4 {
+ execsql {
+ select a from t1 where b<10 and c=1;
+ }
+} {2 4}
+do_test null-4.5 {
+ execsql {
+ select a from t1 where not (b<10 and c=1);
+ }
+} {1 3 5}
+
+# The DISTINCT keyword on a SELECT statement should treat NULL values
+# as distinct
+#
+do_test null-5.1 {
+ execsql {
+ select distinct b from t1 order by b;
+ }
+} {{} 0 1}
+
+# A UNION to two queries should treat NULL values
+# as distinct.
+#
+# (Later:) We also take this opportunity to test the ability
+# of an ORDER BY clause to bind to either SELECT of a UNION.
+# The left-most SELECT is preferred. In standard SQL, only
+# the left SELECT can be used. The ability to match an ORDER
+# BY term to the right SELECT is an SQLite extension.
+#
+ifcapable compound {
+ do_test null-6.1 {
+ execsql {
+ select b from t1 union select c from t1 order by b;
+ }
+ } {{} 0 1}
+ do_test null-6.2 {
+ execsql {
+ select b from t1 union select c from t1 order by 1;
+ }
+ } {{} 0 1}
+ do_test null-6.3 {
+ execsql {
+ select b from t1 union select c from t1 order by t1.b;
+ }
+ } {{} 0 1}
+ do_test null-6.4 {
+ execsql {
+ select b from t1 union select c from t1 order by main.t1.b;
+ }
+ } {{} 0 1}
+ do_test null-6.5 {
+ catchsql {
+ select b from t1 union select c from t1 order by t1.a;
+ }
+ } {1 {1st ORDER BY term does not match any column in the result set}}
+ do_test null-6.6 {
+ catchsql {
+ select b from t1 union select c from t1 order by main.t1.a;
+ }
+ } {1 {1st ORDER BY term does not match any column in the result set}}
+} ;# ifcapable compound
+
+# The UNIQUE constraint only applies to non-null values
+#
+ifcapable conflict {
+do_test null-7.1 {
+ execsql {
+ create table t2(a, b unique on conflict ignore);
+ insert into t2 values(1,1);
+ insert into t2 values(2,null);
+ insert into t2 values(3,null);
+ insert into t2 values(4,1);
+ select a from t2;
+ }
+ } {1 2 3}
+ do_test null-7.2 {
+ execsql {
+ create table t3(a, b, c, unique(b,c) on conflict ignore);
+ insert into t3 values(1,1,1);
+ insert into t3 values(2,null,1);
+ insert into t3 values(3,null,1);
+ insert into t3 values(4,1,1);
+ select a from t3;
+ }
+ } {1 2 3}
+}
+
+# Ticket #461 - Make sure nulls are handled correctly when doing a
+# lookup using an index.
+#
+do_test null-8.1 {
+ execsql {
+ CREATE TABLE t4(x,y);
+ INSERT INTO t4 VALUES(1,11);
+ INSERT INTO t4 VALUES(2,NULL);
+ SELECT x FROM t4 WHERE y=NULL;
+ }
+} {}
+ifcapable subquery {
+ do_test null-8.2 {
+ execsql {
+ SELECT x FROM t4 WHERE y IN (33,NULL);
+ }
+ } {}
+}
+do_test null-8.3 {
+ execsql {
+ SELECT x FROM t4 WHERE y<33 ORDER BY x;
+ }
+} {1}
+do_test null-8.4 {
+ execsql {
+ SELECT x FROM t4 WHERE y>6 ORDER BY x;
+ }
+} {1}
+do_test null-8.5 {
+ execsql {
+ SELECT x FROM t4 WHERE y!=33 ORDER BY x;
+ }
+} {1}
+do_test null-8.11 {
+ execsql {
+ CREATE INDEX t4i1 ON t4(y);
+ SELECT x FROM t4 WHERE y=NULL;
+ }
+} {}
+ifcapable subquery {
+ do_test null-8.12 {
+ execsql {
+ SELECT x FROM t4 WHERE y IN (33,NULL);
+ }
+ } {}
+}
+do_test null-8.13 {
+ execsql {
+ SELECT x FROM t4 WHERE y<33 ORDER BY x;
+ }
+} {1}
+do_test null-8.14 {
+ execsql {
+ SELECT x FROM t4 WHERE y>6 ORDER BY x;
+ }
+} {1}
+do_test null-8.15 {
+ execsql {
+ SELECT x FROM t4 WHERE y!=33 ORDER BY x;
+ }
+} {1}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/openv2.test b/third_party/sqlite/test/openv2.test
new file mode 100755
index 0000000..637edc6
--- /dev/null
+++ b/third_party/sqlite/test/openv2.test
@@ -0,0 +1,41 @@
+# 2007 Sep 3
+#
+# 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.
+#
+#***********************************************************************
+#
+# Tests on the sqlite3_open_v2() interface.
+#
+# $Id: openv2.test,v 1.1 2007/09/03 15:19:36 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+db close
+file delete -force test.db test.db-journal
+do_test openv2-1.1 {
+ set rc [catch {sqlite3 db test.db -create 0} msg]
+ lappend rc $msg
+} {1 {unable to open database file}}
+do_test openv2-1.2 {
+ info commands db
+} {}
+do_test openv2-1.3 {
+ sqlite3 db test.db
+ db eval {CREATE TABLE t1(x)}
+ db close
+ sqlite3 db test.db -readonly 1
+ db eval {SELECT name FROM sqlite_master}
+} {t1}
+do_test openv2-1.4 {
+ catchsql {
+ INSERT INTO t1 VALUES(123)
+ }
+} {1 {attempt to write a readonly database}}
+
+finish_test
diff --git a/third_party/sqlite/test/pager.test b/third_party/sqlite/test/pager.test
new file mode 100755
index 0000000..bb92617
--- /dev/null
+++ b/third_party/sqlite/test/pager.test
@@ -0,0 +1,571 @@
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: pager.test,v 1.30 2007/08/24 16:29:24 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {[info commands pager_open]!=""} {
+db close
+
+# Basic sanity check. Open and close a pager.
+#
+do_test pager-1.0 {
+ catch {file delete -force ptf1.db}
+ catch {file delete -force ptf1.db-journal}
+ set v [catch {
+ set ::p1 [pager_open ptf1.db 10]
+ } msg]
+} {0}
+do_test pager-1.1 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager-1.2 {
+ pager_pagecount $::p1
+} {0}
+do_test pager-1.3 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager-1.4 {
+ pager_close $::p1
+} {}
+
+# Try to write a few pages.
+#
+do_test pager-2.1 {
+ set v [catch {
+ set ::p1 [pager_open ptf1.db 10]
+ } msg]
+} {0}
+#do_test pager-2.2 {
+# set v [catch {
+# set ::g1 [page_get $::p1 0]
+# } msg]
+# lappend v $msg
+#} {1 SQLITE_ERROR}
+do_test pager-2.3.1 {
+ set ::gx [page_lookup $::p1 1]
+} {}
+do_test pager-2.3.2 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager-2.3.3 {
+ set v [catch {
+ set ::g1 [page_get $::p1 1]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager-2.3.3 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.3.4 {
+ set ::gx [page_lookup $::p1 1]
+ expr {$::gx!=""}
+} {1}
+do_test pager-2.3.5 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.3.6 {
+ expr {$::g1==$::gx}
+} {1}
+do_test pager-2.3.7 {
+ page_unref $::gx
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.4 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.5 {
+ pager_pagecount $::p1
+} {0}
+do_test pager-2.6 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.7 {
+ page_number $::g1
+} {1}
+do_test pager-2.8 {
+ page_read $::g1
+} {}
+do_test pager-2.9 {
+ page_unref $::g1
+} {}
+
+# Update 24/03/2007: Even though the ref-count has dropped to zero, the
+# pager-cache still contains some pages. Previously, it was always true
+# that if there were no references to a pager it was empty.
+do_test pager-2.10 {
+ pager_stats $::p1
+} {ref 0 page 1 max 10 size -1 state 0 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.11 {
+ set ::g1 [page_get $::p1 1]
+ expr {$::g1!=0}
+} {1}
+do_test pager-2.12 {
+ page_number $::g1
+} {1}
+do_test pager-2.13 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 1 miss 1 ovfl 0}
+do_test pager-2.14 {
+ set v [catch {
+ page_write $::g1 "Page-One"
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager-2.15 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 1 state 2 err 0 hit 1 miss 1 ovfl 0}
+do_test pager-2.16 {
+ page_read $::g1
+} {Page-One}
+do_test pager-2.17 {
+ set v [catch {
+ pager_commit $::p1
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager-2.20 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size -1 state 1 err 0 hit 2 miss 1 ovfl 0}
+do_test pager-2.19 {
+ pager_pagecount $::p1
+} {1}
+do_test pager-2.21 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 1 state 1 err 0 hit 2 miss 1 ovfl 0}
+do_test pager-2.22 {
+ page_unref $::g1
+} {}
+do_test pager-2.23 {
+ pager_stats $::p1
+} {ref 0 page 1 max 10 size -1 state 0 err 0 hit 2 miss 1 ovfl 0}
+do_test pager-2.24 {
+ set v [catch {
+ page_get $::p1 1
+ } ::g1]
+ if {$v} {lappend v $::g1}
+ set v
+} {0}
+do_test pager-2.25 {
+ page_read $::g1
+} {Page-One}
+do_test pager-2.26 {
+ set v [catch {
+ page_write $::g1 {page-one}
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager-2.27 {
+ page_read $::g1
+} {page-one}
+do_test pager-2.28 {
+ set v [catch {
+ pager_rollback $::p1
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager-2.29 {
+ page_unref $::g1
+ set ::g1 [page_get $::p1 1]
+ page_read $::g1
+} {Page-One}
+do_test pager-2.99 {
+ pager_close $::p1
+} {}
+
+do_test pager-3.1 {
+ set v [catch {
+ set ::p1 [pager_open ptf1.db 15]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager-3.2 {
+ pager_pagecount $::p1
+} {1}
+do_test pager-3.3 {
+ set v [catch {
+ set ::g(1) [page_get $::p1 1]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager-3.4 {
+ page_read $::g(1)
+} {Page-One}
+do_test pager-3.5 {
+ for {set i 2} {$i<=20} {incr i} {
+ set gx [page_get $::p1 $i]
+ page_write $gx "Page-$i"
+ page_unref $gx
+ }
+ pager_commit $::p1
+} {}
+for {set i 2} {$i<=20} {incr i} {
+ do_test pager-3.6.[expr {$i-1}] [subst {
+ set gx \[page_get $::p1 $i\]
+ set v \[page_read \$gx\]
+ page_unref \$gx
+ set v
+ }] "Page-$i"
+}
+for {set i 1} {$i<=20} {incr i} {
+ regsub -all CNT {
+ set ::g1 [page_get $::p1 CNT]
+ set ::g2 [page_get $::p1 CNT]
+ set ::vx [page_read $::g2]
+ expr {$::g1==$::g2}
+ } $i body;
+ do_test pager-3.7.$i.1 $body {1}
+ regsub -all CNT {
+ page_unref $::g2
+ set vy [page_read $::g1]
+ expr {$vy==$::vx}
+ } $i body;
+ do_test pager-3.7.$i.2 $body {1}
+ regsub -all CNT {
+ page_unref $::g1
+ set gx [page_get $::p1 CNT]
+ set vy [page_read $gx]
+ page_unref $gx
+ expr {$vy==$::vx}
+ } $i body;
+ do_test pager-3.7.$i.3 $body {1}
+}
+do_test pager-3.99 {
+ pager_close $::p1
+} {}
+
+# tests of the checkpoint mechanism and api
+#
+do_test pager-4.0 {
+ set v [catch {
+ file delete -force ptf1.db
+ set ::p1 [pager_open ptf1.db 15]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager-4.1 {
+ set g1 [page_get $::p1 1]
+ page_write $g1 "Page-1 v0"
+ for {set i 2} {$i<=20} {incr i} {
+ set gx [page_get $::p1 $i]
+ page_write $gx "Page-$i v0"
+ page_unref $gx
+ }
+ pager_commit $::p1
+} {}
+for {set i 1} {$i<=20} {incr i} {
+ do_test pager-4.2.$i {
+ set gx [page_get $p1 $i]
+ set v [page_read $gx]
+ page_unref $gx
+ set v
+ } "Page-$i v0"
+}
+do_test pager-4.3 {
+ lrange [pager_stats $::p1] 0 1
+} {ref 1}
+do_test pager-4.4 {
+ lrange [pager_stats $::p1] 8 9
+} {state 1}
+
+for {set i 1} {$i<20} {incr i} {
+ do_test pager-4.5.$i.0 {
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager-4.5.$i.1 {
+ page_write $g1 "Page-1 v$i"
+ lrange [pager_stats $p1] 8 9
+ } {state 2}
+ do_test pager-4.5.$i.2 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager-4.5.$i.3 {
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v$i"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager-4.5.$i.4 {
+ pager_rollback $p1
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager-4.5.$i.5 {
+ page_write $g1 "Page-1 v$i"
+ lrange [pager_stats $p1] 8 9
+ } {state 2}
+ do_test pager-4.5.$i.6 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager-4.5.$i.7 {
+ pager_stmt_rollback $p1
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ if {$j<=$i || $i==1} {
+ set shouldbe "Page-$j v$i"
+ } else {
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ }
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager-4.5.$i.8 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager-4.5.$i.9 {
+ pager_stmt_commit $p1
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v$i"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager-4.5.$i.10 {
+ pager_commit $p1
+ lrange [pager_stats $p1] 8 9
+ } {state 1}
+}
+
+# Test that nothing bad happens when sqlite3pager_set_cachesize() is
+# called with a negative argument.
+do_test pager-4.6.1 {
+ pager_close [pager_open ptf2.db -15]
+} {}
+
+# Test truncate on an in-memory database is Ok.
+ifcapable memorydb {
+ do_test pager-4.6.2 {
+ set ::p2 [pager_open :memory: 10]
+ pager_truncate $::p2 5
+ } {}
+ do_test pager-4.6.3 {
+ for {set i 1} {$i<5} {incr i} {
+ set p [page_get $::p2 $i]
+ page_write $p "Page $i"
+ pager_commit $::p2
+ page_unref $p
+ }
+ # pager_truncate $::p2 3
+ } {}
+ do_test pager-4.6.4 {
+ pager_close $::p2
+ } {}
+}
+
+do_test pager-4.99 {
+ pager_close $::p1
+} {}
+
+
+
+ file delete -force ptf1.db
+
+} ;# end if( not mem: and has pager_open command );
+
+if 0 {
+# Ticket #615: an assertion fault inside the pager. It is a benign
+# fault, but we might as well test for it.
+#
+do_test pager-5.1 {
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x);
+ PRAGMA synchronous=off;
+ COMMIT;
+ }
+} {}
+}
+
+# The following tests cover rolling back hot journal files.
+# They can't be run on windows because the windows version of
+# SQLite holds a mandatory exclusive lock on journal files it has open.
+#
+if {$tcl_platform(platform)!="windows"} {
+do_test pager-6.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ sqlite3 db2 test2.db
+ execsql {
+ PRAGMA synchronous = 0;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ INSERT INTO abc VALUES(1, 2, randstr(200,200));
+ BEGIN;
+ UPDATE abc SET c = randstr(200,200);
+ } db2
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ seek $f [expr [file size test.db-journal] - 1032] start
+ puts -nonewline $f "\00\00\00\00"
+ close $f
+
+ sqlite3 db test.db
+ execsql {
+ SELECT sql FROM sqlite_master
+ }
+} {{CREATE TABLE abc(a, b, c)}}
+
+do_test pager-6.2 {
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ seek $f [expr [file size test.db-journal] - 1032] start
+ puts -nonewline $f "\00\00\00\FF"
+ close $f
+
+ sqlite3 db test.db
+ execsql {
+ SELECT sql FROM sqlite_master
+ }
+} {{CREATE TABLE abc(a, b, c)}}
+
+do_test pager-6.3 {
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ seek $f [expr [file size test.db-journal] - 4] start
+ puts -nonewline $f "\00\00\00\00"
+ close $f
+
+ sqlite3 db test.db
+ execsql {
+ SELECT sql FROM sqlite_master
+ }
+} {{CREATE TABLE abc(a, b, c)}}
+
+do_test pager-6.4.1 {
+ execsql {
+ BEGIN;
+ SELECT sql FROM sqlite_master;
+ }
+ copy_file test2.db-journal test.db-journal;
+ sqlite3 db3 test.db
+ catchsql {
+ BEGIN;
+ SELECT sql FROM sqlite_master;
+ } db3;
+} {1 {database is locked}}
+do_test pager-6.4.2 {
+ file delete -force test.db-journal
+ catchsql {
+ SELECT sql FROM sqlite_master;
+ } db3;
+} {0 {{CREATE TABLE abc(a, b, c)}}}
+do_test pager-6.4.3 {
+ db3 close
+ execsql {
+ COMMIT;
+ }
+} {}
+
+do_test pager-6.5 {
+ copy_file test2.db test.db
+ copy_file test2.db-journal test.db-journal
+
+ set f [open test.db-journal a]
+ fconfigure $f -encoding binary
+ puts -nonewline $f "hello"
+ puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04"
+ puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
+ close $f
+
+ sqlite3 db test.db
+ execsql {
+ SELECT sql FROM sqlite_master
+ }
+} {{CREATE TABLE abc(a, b, c)}}
+
+do_test pager-6.5 {
+ db2 close
+} {}
+}
+finish_test
diff --git a/third_party/sqlite/test/pager2.test b/third_party/sqlite/test/pager2.test
new file mode 100755
index 0000000..52dfe73
--- /dev/null
+++ b/third_party/sqlite/test/pager2.test
@@ -0,0 +1,408 @@
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: pager2.test,v 1.6 2007/03/23 18:12:07 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Don't run this test file if the pager test interface [pager_open] is not
+# available, or the library was compiled without in-memory database support.
+#
+if {[info commands pager_open]!=""} {
+ifcapable memorydb {
+db close
+
+# Basic sanity check. Open and close a pager.
+#
+do_test pager2-1.0 {
+ set v [catch {
+ set ::p1 [pager_open :memory: 10]
+ } msg]
+} {0}
+do_test pager2-1.1 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size 0 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager2-1.2 {
+ pager_pagecount $::p1
+} {0}
+do_test pager2-1.3 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size 0 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager2-1.4 {
+ pager_close $::p1
+} {}
+
+# Try to write a few pages.
+#
+do_test pager2-2.1 {
+ set v [catch {
+ set ::p1 [pager_open :memory: 10]
+ } msg]
+} {0}
+#do_test pager2-2.2 {
+# set v [catch {
+# set ::g1 [page_get $::p1 0]
+# } msg]
+# lappend v $msg
+#} {1 SQLITE_ERROR}
+do_test pager2-2.3.1 {
+ set ::gx [page_lookup $::p1 1]
+} {}
+do_test pager2-2.3.2 {
+ pager_stats $::p1
+} {ref 0 page 0 max 10 size 0 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager2-2.3.3 {
+ set v [catch {
+ set ::g1 [page_get $::p1 1]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager2-2.3.3 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.3.4 {
+ set ::gx [page_lookup $::p1 1]
+ expr {$::gx!=""}
+} {1}
+do_test pager2-2.3.5 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.3.6 {
+ expr {$::g1==$::gx}
+} {1}
+do_test pager2-2.3.7 {
+ page_unref $::gx
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.4 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.5 {
+ pager_pagecount $::p1
+} {0}
+do_test pager2-2.6 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.7 {
+ page_number $::g1
+} {1}
+do_test pager2-2.8 {
+ page_read $::g1
+} {}
+do_test pager2-2.9 {
+ page_unref $::g1
+} {}
+do_test pager2-2.10 {
+ pager_stats $::p1
+} {ref 0 page 1 max 10 size 0 state 0 err 0 hit 0 miss 1 ovfl 0}
+do_test pager2-2.11 {
+ set ::g1 [page_get $::p1 1]
+ expr {$::g1!=0}
+} {1}
+do_test pager2-2.12 {
+ page_number $::g1
+} {1}
+do_test pager2-2.13 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 1 miss 1 ovfl 0}
+do_test pager2-2.14 {
+ set v [catch {
+ page_write $::g1 "Page-One"
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager2-2.15 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0}
+do_test pager2-2.16 {
+ page_read $::g1
+} {Page-One}
+do_test pager2-2.17 {
+ set v [catch {
+ pager_commit $::p1
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager2-2.20 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 1 state 1 err 0 hit 1 miss 1 ovfl 0}
+do_test pager2-2.19 {
+ pager_pagecount $::p1
+} {1}
+do_test pager2-2.21 {
+ pager_stats $::p1
+} {ref 1 page 1 max 10 size 1 state 1 err 0 hit 1 miss 1 ovfl 0}
+do_test pager2-2.22 {
+ page_unref $::g1
+} {}
+do_test pager2-2.23 {
+ pager_stats $::p1
+} {ref 0 page 1 max 10 size 1 state 0 err 0 hit 1 miss 1 ovfl 0}
+do_test pager2-2.24 {
+ set v [catch {
+ page_get $::p1 1
+ } ::g1]
+ if {$v} {lappend v $::g1}
+ set v
+} {0}
+do_test pager2-2.25 {
+ page_read $::g1
+} {Page-One}
+do_test pager2-2.26 {
+ set v [catch {
+ page_write $::g1 {page-one}
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager2-2.27 {
+ page_read $::g1
+} {page-one}
+do_test pager2-2.28 {
+ set v [catch {
+ pager_rollback $::p1
+ } msg]
+ lappend v $msg
+} {0 {}}
+do_test pager2-2.29 {
+ page_unref $::g1
+ set ::g1 [page_get $::p1 1]
+ page_read $::g1
+} {Page-One}
+#do_test pager2-2.99 {
+# pager_close $::p1
+#} {}
+
+#do_test pager2-3.1 {
+# set v [catch {
+# set ::p1 [pager_open :memory: 15]
+# } msg]
+# if {$v} {lappend v $msg}
+# set v
+#} {0}
+do_test pager2-3.2 {
+ pager_pagecount $::p1
+} {1}
+do_test pager2-3.3 {
+ set v [catch {
+ set ::g(1) [page_get $::p1 1]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager2-3.4 {
+ page_read $::g(1)
+} {Page-One}
+do_test pager2-3.5 {
+ for {set i 2} {$i<=20} {incr i} {
+ set gx [page_get $::p1 $i]
+ page_write $gx "Page-$i"
+ page_unref $gx
+ }
+ pager_commit $::p1
+} {}
+for {set i 2} {$i<=20} {incr i} {
+ do_test pager2-3.6.[expr {$i-1}] [subst {
+ set gx \[page_get $::p1 $i\]
+ set v \[page_read \$gx\]
+ page_unref \$gx
+ set v
+ }] "Page-$i"
+}
+for {set i 1} {$i<=20} {incr i} {
+ regsub -all CNT {
+ set ::g1 [page_get $::p1 CNT]
+ set ::g2 [page_get $::p1 CNT]
+ set ::vx [page_read $::g2]
+ expr {$::g1==$::g2}
+ } $i body;
+ do_test pager2-3.7.$i.1 $body {1}
+ regsub -all CNT {
+ page_unref $::g2
+ set vy [page_read $::g1]
+ expr {$vy==$::vx}
+ } $i body;
+ do_test pager2-3.7.$i.2 $body {1}
+ regsub -all CNT {
+ page_unref $::g1
+ set gx [page_get $::p1 CNT]
+ set vy [page_read $gx]
+ page_unref $gx
+ expr {$vy==$::vx}
+ } $i body;
+ do_test pager2-3.7.$i.3 $body {1}
+}
+do_test pager2-3.99 {
+ pager_close $::p1
+} {}
+
+# tests of the checkpoint mechanism and api
+#
+do_test pager2-4.0 {
+ set v [catch {
+ set ::p1 [pager_open :memory: 15]
+ } msg]
+ if {$v} {lappend v $msg}
+ set v
+} {0}
+do_test pager2-4.1 {
+ set g1 [page_get $::p1 1]
+ page_write $g1 "Page-1 v0"
+ for {set i 2} {$i<=20} {incr i} {
+ set gx [page_get $::p1 $i]
+ page_write $gx "Page-$i v0"
+ page_unref $gx
+ }
+ pager_commit $::p1
+} {}
+for {set i 1} {$i<=20} {incr i} {
+ do_test pager2-4.2.$i {
+ set gx [page_get $p1 $i]
+ set v [page_read $gx]
+ page_unref $gx
+ set v
+ } "Page-$i v0"
+}
+do_test pager2-4.3 {
+ lrange [pager_stats $::p1] 0 1
+} {ref 1}
+do_test pager2-4.4 {
+ lrange [pager_stats $::p1] 8 9
+} {state 1}
+
+for {set i 1} {$i<20} {incr i} {
+ do_test pager2-4.5.$i.0 {
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager2-4.5.$i.1 {
+ page_write $g1 "Page-1 v$i"
+ lrange [pager_stats $p1] 8 9
+ } {state 4}
+ do_test pager2-4.5.$i.2 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager2-4.5.$i.3 {
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v$i"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager2-4.5.$i.4 {
+ pager_rollback $p1
+ set res {}
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager2-4.5.$i.5 {
+ page_write $g1 "Page-1 v$i"
+ lrange [pager_stats $p1] 8 9
+ } {state 4}
+ do_test pager2-4.5.$i.6 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager2-4.5.$i.7 {
+ pager_stmt_rollback $p1
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ if {$j<=$i || $i==1} {
+ set shouldbe "Page-$j v$i"
+ } else {
+ set shouldbe "Page-$j v[expr {$i-1}]"
+ }
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager2-4.5.$i.8 {
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ page_write $gx "Page-$j v$i"
+ page_unref $gx
+ if {$j==$i} {
+ pager_stmt_begin $p1
+ }
+ }
+ } {}
+ do_test pager2-4.5.$i.9 {
+ pager_stmt_commit $p1
+ for {set j 2} {$j<=20} {incr j} {
+ set gx [page_get $p1 $j]
+ set value [page_read $gx]
+ page_unref $gx
+ set shouldbe "Page-$j v$i"
+ if {$value!=$shouldbe} {
+ lappend res $value $shouldbe
+ }
+ }
+ set res
+ } {}
+ do_test pager2-4.5.$i.10 {
+ pager_commit $p1
+ lrange [pager_stats $p1] 8 9
+ } {state 1}
+}
+
+do_test pager2-4.99 {
+ pager_close $::p1
+} {}
+
+} ;# ifcapable inmemory
+} ;# end if( has pager_open command );
+
+
+finish_test
diff --git a/third_party/sqlite/test/pager3.test b/third_party/sqlite/test/pager3.test
new file mode 100755
index 0000000..59a97c5
--- /dev/null
+++ b/third_party/sqlite/test/pager3.test
@@ -0,0 +1,73 @@
+# 2001 September 15
+#
+# 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 page cache subsystem.
+#
+# $Id: pager3.test,v 1.3 2005/03/29 03:11:00 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This test makes sure the database file is truncated back to the correct
+# length on a rollback.
+#
+# After some preliminary setup, a transaction is start at NOTE (1).
+# The create table on the following line allocates an additional page
+# at the end of the database file. But that page is not written because
+# the database still has a RESERVED lock, not an EXCLUSIVE lock. The
+# new page is held in memory and the size of the file is unchanged.
+# The insert at NOTE (2) begins adding additional pages. Then it hits
+# a constraint error and aborts. The abort causes sqlite3OsTruncate()
+# to be called to restore the file to the same length as it was after
+# the create table. But the create table results had not yet been
+# written so the file is actually lengthened by this truncate. Finally,
+# the rollback at NOTE (3) is called to undo all the changes since the
+# begin. This rollback should truncate the database again.
+#
+# This test was added because the second truncate at NOTE (3) was not
+# occurring on early versions of SQLite 3.0.
+#
+ifcapable tempdb {
+ do_test pager3-1.1 {
+ execsql {
+ create table t1(a unique, b);
+ insert into t1 values(1, 'abcdefghijklmnopqrstuvwxyz');
+ insert into t1 values(2, 'abcdefghijklmnopqrstuvwxyz');
+ update t1 set b=b||a||b;
+ update t1 set b=b||a||b;
+ update t1 set b=b||a||b;
+ update t1 set b=b||a||b;
+ update t1 set b=b||a||b;
+ update t1 set b=b||a||b;
+ create temp table t2 as select * from t1;
+ begin; ------- NOTE (1)
+ create table t3(x);
+ }
+ catchsql {
+ insert into t1 select 4-a, b from t2; ----- NOTE (2)
+ }
+ execsql {
+ rollback; ------- NOTE (3)
+ }
+ db close
+ sqlite3 db test.db
+ set r ok
+ ifcapable {integrityck} {
+ set r [execsql {
+ pragma integrity_check;
+ }]
+ }
+ set r
+ } ok
+}
+
+finish_test
diff --git a/third_party/sqlite/test/pageropt.test b/third_party/sqlite/test/pageropt.test
new file mode 100755
index 0000000..3ab1ff5
--- /dev/null
+++ b/third_party/sqlite/test/pageropt.test
@@ -0,0 +1,201 @@
+# 2007 April 12
+#
+# 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 the tests in this file are to verify that the
+# pager optimizations implemented in version 3.3.14 work.
+#
+# $Id: pageropt.test,v 1.4 2008/04/14 01:00:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas||secure_delete} {
+ finish_test
+ return
+}
+
+# Run the SQL statement supplied by the argument and return
+# the results. Prepend four integers to the beginning of the
+# result which are
+#
+# (1) The number of page reads from the database
+# (2) The number of page writes to the database
+# (3) The number of page writes to the journal
+# (4) The number of cache pages freed
+#
+proc pagercount_sql {sql {db db}} {
+ global sqlite3_pager_readdb_count
+ global sqlite3_pager_writedb_count
+ global sqlite3_pager_writej_count
+ global sqlite3_pager_pgfree_count
+ set sqlite3_pager_readdb_count 0
+ set sqlite3_pager_writedb_count 0
+ set sqlite3_pager_writej_count 0
+ set sqlite3_pager_pgfree_count 0
+ set r [$db eval $sql]
+ set cnt [list $sqlite3_pager_readdb_count \
+ $sqlite3_pager_writedb_count \
+ $sqlite3_pager_writej_count \
+ $sqlite3_pager_pgfree_count]
+ return [concat $cnt $r]
+}
+
+# Setup the test database
+#
+do_test pageropt-1.1 {
+ sqlite3_soft_heap_limit 0
+ execsql {
+ PRAGMA auto_vacuum = OFF;
+ PRAGMA page_size = 1024;
+ }
+ pagercount_sql {
+ CREATE TABLE t1(x);
+ }
+} {0 2 0 0}
+do_test pageropt-1.2 {
+ pagercount_sql {
+ INSERT INTO t1 VALUES(randomblob(5000));
+ }
+} {0 6 2 0}
+
+# Verify that values remain in cache on for subsequent reads.
+# We should not have to go back to disk.
+#
+do_test pageropt-1.3 {
+ pagercount_sql {
+ SELECT length(x) FROM t1
+ }
+} {0 0 0 0 5000}
+
+# If another thread reads the database, the original cache
+# remains valid.
+#
+sqlite3 db2 test.db
+set blobcontent [db2 one {SELECT hex(x) FROM t1}]
+do_test pageropt-1.4 {
+ pagercount_sql {
+ SELECT hex(x) FROM t1
+ }
+} [list 0 0 0 0 $blobcontent]
+
+# But if the other thread modifies the database, then the cache
+# must refill.
+#
+do_test pageropt-1.5 {
+ db2 eval {CREATE TABLE t2(y)}
+ pagercount_sql {
+ SELECT hex(x) FROM t1
+ }
+} [list 6 0 0 6 $blobcontent]
+do_test pageropt-1.6 {
+ pagercount_sql {
+ SELECT hex(x) FROM t1
+ }
+} [list 0 0 0 0 $blobcontent]
+
+# Verify that the last page of an overflow chain is not read from
+# disk when deleting a row. The one row of t1(x) has four pages
+# of overflow. So deleting that row from t1 should involve reading
+# the sqlite_master table (1 page) the main page of t1 (1 page) and
+# the three overflow pages of t1 for a total of 5 pages.
+#
+# Pages written are page 1 (for the freelist pointer), the root page
+# of the table, and one of the overflow chain pointers because it
+# becomes the trunk of the freelist. Total 3.
+#
+do_test pageropt-2.1 {
+ db close
+ sqlite3 db test.db
+ pagercount_sql {
+ DELETE FROM t1 WHERE rowid=1
+ }
+} {5 3 3 0}
+
+# When pulling pages off of the freelist, there is no reason
+# to actually bring in the old content.
+#
+do_test pageropt-2.2 {
+ db close
+ sqlite3 db test.db
+ pagercount_sql {
+ INSERT INTO t1 VALUES(randomblob(1500));
+ }
+} {3 4 3 0}
+do_test pageropt-2.3 {
+ pagercount_sql {
+ INSERT INTO t1 VALUES(randomblob(1500));
+ }
+} {0 4 3 0}
+
+# Note the new optimization that when pulling the very last page off of the
+# freelist we do not read the content of that page.
+#
+do_test pageropt-2.4 {
+ pagercount_sql {
+ INSERT INTO t1 VALUES(randomblob(1500));
+ }
+} {0 5 3 0}
+
+# Appending a large quantity of data does not involve writing much
+# to the journal file.
+#
+do_test pageropt-3.1 {
+ pagercount_sql {
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+} {1 7 2 0}
+
+# Once again, we do not need to read the last page of an overflow chain
+# while deleting.
+#
+do_test pageropt-3.2 {
+ pagercount_sql {
+ DROP TABLE t2;
+ }
+} {0 2 3 0}
+do_test pageropt-3.3 {
+ pagercount_sql {
+ DELETE FROM t1;
+ }
+} {0 3 3 0}
+
+# There are now 11 pages on the freelist. Move them all into an
+# overflow chain by inserting a single large record. Starting from
+# a cold cache, only page 1, the root page of table t1, and the trunk
+# of the freelist need to be read (3 pages). And only those three
+# pages need to be journalled. But 13 pages need to be written:
+# page1, the root page of table t1, and an 11 page overflow chain.
+#
+do_test pageropt-4.1 {
+ db close
+ sqlite3 db test.db
+ pagercount_sql {
+ INSERT INTO t1 VALUES(randomblob(11300))
+ }
+} {3 13 3 0}
+
+# Now we delete that big entries starting from a cold cache and an
+# empty freelist. The first 10 of the 11 pages overflow chain have
+# to be read, together with page1 and the root of the t1 table. 12
+# reads total. But only page1, the t1 root, and the trunk of the
+# freelist need to be journalled and written back.
+#
+do_test pageropt-4.2 {
+ db close
+ sqlite3 db test.db
+ pagercount_sql {
+ DELETE FROM t1
+ }
+} {12 3 3 0}
+
+sqlite3_soft_heap_limit $soft_limit
+catch {db2 close}
+finish_test
diff --git a/third_party/sqlite/test/pagesize.test b/third_party/sqlite/test/pagesize.test
new file mode 100755
index 0000000..a598b9f
--- /dev/null
+++ b/third_party/sqlite/test/pagesize.test
@@ -0,0 +1,182 @@
+# 2004 September 2
+#
+# 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.
+# This file implements tests for the page_size PRAGMA.
+#
+# $Id: pagesize.test,v 1.12 2007/04/06 21:42:22 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This test script depends entirely on "PRAGMA page_size". So if this
+# pragma is not available, omit the whole file.
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+do_test pagesize-1.1 {
+ execsql {PRAGMA page_size}
+} 1024
+ifcapable {explain} {
+ do_test pagesize-1.2 {
+ catch {execsql {EXPLAIN PRAGMA page_size}}
+ } 0
+}
+do_test pagesize-1.3 {
+ execsql {
+ CREATE TABLE t1(a);
+ PRAGMA page_size=2048;
+ PRAGMA page_size;
+ }
+} 1024
+
+do_test pagesize-1.4 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ execsql {
+ PRAGMA page_size=511;
+ PRAGMA page_size;
+ }
+} 1024
+do_test pagesize-1.5 {
+ execsql {
+ PRAGMA page_size=512;
+ PRAGMA page_size;
+ }
+} 512
+if {![info exists SQLITE_MAX_PAGE_SIZE] || $SQLITE_MAX_PAGE_SIZE>=8192} {
+ do_test pagesize-1.6 {
+ execsql {
+ PRAGMA page_size=8192;
+ PRAGMA page_size;
+ }
+ } 8192
+ do_test pagesize-1.7 {
+ execsql {
+ PRAGMA page_size=65537;
+ PRAGMA page_size;
+ }
+ } 8192
+ do_test pagesize-1.8 {
+ execsql {
+ PRAGMA page_size=1234;
+ PRAGMA page_size
+ }
+ } 8192
+}
+foreach PGSZ {512 2048 4096 8192} {
+ if {[info exists SQLITE_MAX_PAGE_SIZE]
+ && $SQLITE_MAX_PAGE_SIZE<$PGSZ} continue
+ ifcapable memorydb {
+ do_test pagesize-2.$PGSZ.0 {
+ db close
+ sqlite3 db :memory:
+ execsql "PRAGMA page_size=$PGSZ;"
+ execsql {PRAGMA page_size}
+ } 1024
+ }
+ do_test pagesize-2.$PGSZ.1 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ execsql "PRAGMA page_size=$PGSZ"
+ execsql {
+ CREATE TABLE t1(x);
+ PRAGMA page_size;
+ }
+ } $PGSZ
+ do_test pagesize-2.$PGSZ.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA page_size
+ }
+ } $PGSZ
+ do_test pagesize-2.$PGSZ.3 {
+ file size test.db
+ } [expr {$PGSZ*($AUTOVACUUM?3:2)}]
+ ifcapable {vacuum} {
+ do_test pagesize-2.$PGSZ.4 {
+ execsql {VACUUM}
+ } {}
+ }
+ integrity_check pagesize-2.$PGSZ.5
+ do_test pagesize-2.$PGSZ.6 {
+ db close
+ sqlite3 db test.db
+ execsql {PRAGMA page_size}
+ } $PGSZ
+ do_test pagesize-2.$PGSZ.7 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10,9000));
+ INSERT INTO t1 VALUES(randstr(10,9000));
+ INSERT INTO t1 VALUES(randstr(10,9000));
+ BEGIN;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ SELECT count(*) FROM t1;
+ }
+ } 48
+ do_test pagesize-2.$PGSZ.8 {
+ execsql {
+ ROLLBACK;
+ SELECT count(*) FROM t1;
+ }
+ } 3
+ integrity_check pagesize-2.$PGSZ.9
+ do_test pagesize-2.$PGSZ.10 {
+ db close
+ sqlite3 db test.db
+ execsql {PRAGMA page_size}
+ } $PGSZ
+ do_test pagesize-2.$PGSZ.11 {
+ execsql {
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ INSERT INTO t1 SELECT x||x FROM t1;
+ SELECT count(*) FROM t1;
+ }
+ } 192
+ do_test pagesize-2.$PGSZ.12 {
+ execsql {
+ BEGIN;
+ DELETE FROM t1 WHERE rowid%5!=0;
+ SELECT count(*) FROM t1;
+ }
+ } 38
+ do_test pagesize-2.$PGSZ.13 {
+ execsql {
+ ROLLBACK;
+ SELECT count(*) FROM t1;
+ }
+ } 192
+ integrity_check pagesize-2.$PGSZ.14
+ do_test pagesize-2.$PGSZ.15 {
+ execsql {DELETE FROM t1 WHERE rowid%5!=0}
+ ifcapable {vacuum} {execsql VACUUM}
+ execsql {SELECT count(*) FROM t1}
+ } 38
+ do_test pagesize-2.$PGSZ.16 {
+ execsql {DROP TABLE t1}
+ ifcapable {vacuum} {execsql VACUUM}
+ } {}
+ integrity_check pagesize-2.$PGSZ.17
+}
+
+finish_test
diff --git a/third_party/sqlite/test/permutations.test b/third_party/sqlite/test/permutations.test
new file mode 100755
index 0000000..8aee99f
--- /dev/null
+++ b/third_party/sqlite/test/permutations.test
@@ -0,0 +1,547 @@
+# 2008 June 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.
+#
+#***********************************************************************
+#
+# $Id: permutations.test,v 1.20 2008/08/01 18:47:02 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Argument processing.
+#
+set ::testmode [lindex $argv 0]
+set ::testfile [lindex $argv 1]
+set argv [lrange $argv 2 end]
+
+set ::permutations_presql ""
+set ::permutations_test_prefix ""
+
+if {$::testmode eq "veryquick"} {
+ set ::testmode [list persistent_journal no_journal]
+ set ISQUICK 1
+}
+if {$::testmode eq "quick"} {
+ set ::testmode [list persistent_journal no_journal autovacuum_ioerr]
+ set ISQUICK 1
+}
+if {$::testmode eq "all"} {
+ set ::testmode {
+ memsubsys1 memsubsys2 singlethread multithread onefile utf16 exclusive
+ persistent_journal persistent_journal_error no_journal no_journal_error
+ autovacuum_ioerr no_mutex_try
+ }
+}
+if {$::testmode eq "targets"} {
+ puts ""
+ puts -nonewline "veryquick "
+ puts "Same as persistent_journal and no_journal"
+ puts -nonewline "quick "
+ puts "Same as persistent_journal, no_journal and autovacuum_ioerr"
+ puts -nonewline "all "
+ puts "Everything except autovacuum_crash"
+}
+
+set EXCLUDE {
+ all.test in2.test onefile.test
+ async2.test incrvacuum_ioerr.test permutations.test
+ async.test jrnlmode2.test quick.test
+ autovacuum_crash.test jrnlmode3.test shared_err.test
+ autovacuum_ioerr.test jrnlmode4.test soak.test
+ btree8.test loadext.test speed1p.test
+ corrupt.test malloc2.test speed1.test
+ crash2.test malloc3.test speed2.test
+ crash3.test malloc4.test speed3.test
+ crash4.test mallocAll.test speed4p.test
+ crash6.test malloc.test speed4.test
+ crash7.test memleak.test sqllimits1.test
+ crash.test memsubsys1.test thread001.test
+ exclusive3.test memsubsys2.test thread002.test
+ fts3.test misc7.test utf16.test
+ fuzz_malloc.test misuse.test veryquick.test
+ fuzz.test mutex2.test vtab_err.test
+ lookaside.test
+}
+set ALLTESTS [list]
+foreach filename [glob $testdir/*.test] {
+ set filename [file tail $filename]
+ if {[lsearch $EXCLUDE $filename] < 0} { lappend ALLTESTS $filename }
+}
+
+rename finish_test really_finish_test2
+proc finish_test {} {}
+
+rename do_test really_do_test
+
+proc do_test {name args} {
+ eval really_do_test [list "perm-$::permutations_test_prefix.$name"] $args
+}
+
+# Overload the [sqlite3] command
+rename sqlite3 really_sqlite3
+proc sqlite3 {args} {
+ set r [eval really_sqlite3 $args]
+ if { [llength $args] == 2 && $::permutations_presql ne "" } {
+ [lindex $args 0] eval $::permutations_presql
+ }
+ set r
+}
+
+# run_tests OPTIONS
+#
+# where available options are:
+#
+# -initialize SCRIPT (default "")
+# -shutdown SCRIPT (default "")
+# -include LIST-OF-FILES (default $::ALLTESTS)
+# -exclude LIST-OF-FILES (default "")
+# -presql SQL (default "")
+# -description TITLE (default "")
+#
+proc run_tests {name args} {
+ set ::permutations_test_prefix $name
+ set options(-shutdown) ""
+ set options(-initialize) ""
+ set options(-exclude) ""
+ set options(-include) $::ALLTESTS
+ set options(-presql) ""
+ set options(-description) "no description supplied (fixme)"
+ array set options $args
+
+ if {$::testmode eq "targets"} {
+ puts [format "% -20s %s" $name [string trim $options(-description)]]
+ return
+ }
+ if {$::testmode ne "" && [lsearch $::testmode $name]<0} return
+
+ uplevel $options(-initialize)
+ set ::permutations_presql $options(-presql)
+
+ foreach file $options(-include) {
+ if {[lsearch $options(-exclude) $file] < 0 && (
+ $::testfile eq "" || $::testfile eq $file || "$::testfile.test" eq $file
+ ) } {
+ uplevel source $::testdir/$file
+ }
+ }
+
+ uplevel $options(-shutdown)
+}
+
+#############################################################################
+# Start of tests
+
+# Run some tests using pre-allocated page and scratch blocks.
+#
+run_tests "memsubsys1" -description {
+ Tests using pre-allocated page and scratch blocks
+} -initialize {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_pagecache 4096 24
+ sqlite3_config_scratch 25000 1
+ sqlite3_initialize
+} -shutdown {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_pagecache 0 0
+ sqlite3_config_scratch 0 0
+ sqlite3_initialize
+}
+
+# Run some tests using pre-allocated page and scratch blocks. This time
+# the allocations are too small to use in most cases.
+#
+run_tests "memsubsys2" -description {
+ Tests using small pre-allocated page and scratch blocks
+} -initialize {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_pagecache 512 5
+ sqlite3_config_scratch 1000 1
+ sqlite3_initialize
+} -shutdown {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_pagecache 0 0
+ sqlite3_config_scratch 0 0
+ sqlite3_initialize
+}
+
+# Run all tests with the lookaside allocator disabled.
+#
+run_tests "memsubsys3" -description {
+ OOM tests with lookaside disabled
+} -include {
+ malloc.test
+ malloc3.test
+ malloc4.test
+ malloc5.test
+ malloc6.test
+ malloc7.test
+ malloc8.test
+ malloc9.test
+ mallocA.test
+ mallocB.test
+ mallocC.test
+ mallocD.test
+ mallocE.test
+ mallocF.test
+ mallocG.test
+} -initialize {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_lookaside 0 0
+ sqlite3_initialize
+} -shutdown {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_lookaside 100 500
+ sqlite3_initialize
+}
+
+# Run some tests in SQLITE_CONFIG_SINGLETHREAD mode.
+#
+run_tests "singlethread" -description {
+ Tests run in SQLITE_CONFIG_SINGLETHREAD mode
+} -initialize {
+ do_test mutex2-singlethread.0 {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config singlethread
+ } SQLITE_OK
+} -include {
+ delete.test delete2.test insert.test rollback.test select1.test
+ select2.test trans.test update.test vacuum.test types.test
+ types2.test types3.test
+} -shutdown {
+ do_test mutex2-X {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config serialized
+ } SQLITE_OK
+}
+
+run_tests "nomutex" -description {
+ Tests run with the SQLITE_OPEN_SINGLETHREADED flag passed to sqlite3_open().
+} -initialize {
+ rename sqlite3 sqlite3_nomutex
+ proc sqlite3 {args} {
+ if {[string range [lindex $args 0] 0 0] ne "-"} {
+ lappend args -nomutex 1
+ }
+ uplevel [concat sqlite3_nomutex $args]
+ }
+} -include {
+ delete.test delete2.test insert.test rollback.test select1.test
+ select2.test trans.test update.test vacuum.test types.test
+ types2.test types3.test
+} -shutdown {
+ rename sqlite3 {}
+ rename sqlite3_nomutex sqlite3
+}
+
+# Run some tests in SQLITE_CONFIG_MULTITHREAD mode.
+#
+run_tests "multithread" -description {
+ Tests run in SQLITE_CONFIG_MULTITHREAD mode
+} -initialize {
+ do_test mutex2-multithread.0 {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config multithread
+ } SQLITE_OK
+} -include {
+ delete.test delete2.test insert.test rollback.test select1.test
+ select2.test trans.test update.test vacuum.test types.test
+ types2.test types3.test
+} -shutdown {
+ do_test mutex2-X {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config serialized
+ } SQLITE_OK
+}
+
+# Run some tests using the "onefile" demo.
+#
+run_tests "onefile" -description {
+ Run some tests using the "test_onefile.c" demo
+} -initialize {
+ rename sqlite3 sqlite3_onefile
+ proc sqlite3 {args} {
+ if {[string range [lindex $args 0] 0 0] ne "-"} {
+ lappend args -vfs fs
+ }
+ uplevel [concat sqlite3_onefile $args]
+ }
+} -include {
+ conflict.test insert.test insert2.test insert3.test
+ rollback.test select1.test select2.test select3.test
+ temptable.test
+} -shutdown {
+ rename sqlite3 {}
+ rename sqlite3_onefile sqlite3
+}
+
+# Run some tests using UTF-16 databases.
+#
+run_tests "utf16" -description {
+ Run tests using UTF-16 databases
+} -presql {
+ pragma encoding = 'UTF-16'
+} -include {
+ alter.test alter3.test
+ auth.test bind.test blob.test capi2.test capi3.test collate1.test
+ collate2.test collate3.test collate4.test collate5.test collate6.test
+ conflict.test date.test delete.test expr.test fkey1.test func.test
+ hook.test index.test insert2.test insert.test interrupt.test in.test
+ intpkey.test ioerr.test join2.test join.test lastinsert.test
+ laststmtchanges.test limit.test lock2.test lock.test main.test
+ memdb.test minmax.test misc1.test misc2.test misc3.test notnull.test
+ null.test progress.test quote.test rowid.test select1.test select2.test
+ select3.test select4.test select5.test select6.test sort.test
+ subselect.test tableapi.test table.test temptable.test
+ trace.test trigger1.test trigger2.test trigger3.test
+ trigger4.test types2.test types.test unique.test update.test
+ vacuum.test view.test where.test
+}
+
+# Run some tests in exclusive locking mode.
+#
+run_tests "exclusive" -description {
+ Run tests in exclusive locking mode.
+} -presql {
+ pragma locking_mode = 'exclusive'
+} -include {
+ rollback.test select1.test select2.test
+ malloc.test ioerr.test
+}
+
+# Run some tests in persistent journal mode.
+#
+run_tests "persistent_journal" -description {
+ Run tests in persistent-journal mode.
+} -presql {
+ pragma journal_mode = persist
+} -include {
+ delete.test delete2.test insert.test rollback.test select1.test
+ select2.test trans.test update.test vacuum.test
+}
+
+# Run some error tests in persistent journal mode.
+#
+run_tests "persistent_journal_error" -description {
+ Run malloc.test and ioerr.test in persistent-journal mode.
+} -presql {
+ pragma journal_mode = persist
+} -include {
+ malloc.test ioerr.test
+}
+
+# Run some tests in no journal mode.
+#
+run_tests "no_journal" -description {
+ Run tests in no-journal mode.
+} -presql {
+ pragma journal_mode = persist
+} -include {
+ delete.test delete2.test insert.test rollback.test select1.test
+ select2.test trans.test update.test vacuum.test
+}
+
+# Run some error tests in no journal mode.
+#
+run_tests "no_journal_error" -description {
+ Run malloc.test and ioerr.test in no-journal mode.
+} -presql {
+ pragma journal_mode = persist
+} -include {
+ malloc.test ioerr.test
+}
+
+# Run some crash-tests in autovacuum mode.
+#
+run_tests "autovacuum_crash" -description {
+ Run crash.test in autovacuum mode.
+} -presql {
+ pragma auto_vacuum = 1
+} -include crash.test
+
+# Run some ioerr-tests in autovacuum mode.
+#
+run_tests "autovacuum_ioerr" -description {
+ Run ioerr.test in autovacuum mode.
+} -presql {
+ pragma auto_vacuum = 1
+} -include ioerr.test
+
+ifcapable mem3 {
+ run_tests "memsys3" -description {
+ Run tests using the allocator in mem3.c.
+ } -exclude {
+ autovacuum.test delete3.test manydb.test
+ bigrow.test incrblob2.test memdb.test
+ bitvec.test index2.test memsubsys1.test
+ capi3c.test ioerr.test memsubsys2.test
+ capi3.test join3.test pagesize.test
+ collate5.test limit.test
+ } -initialize {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 25000000 0
+ sqlite3_config_lookaside 0 0
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ autoinstall_test_functions
+ } -shutdown {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 0 0
+ sqlite3_config_lookaside 100 500
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ }
+}
+
+ifcapable mem5 {
+ run_tests "memsys5" -description {
+ Run tests using the allocator in mem5.c.
+ } -exclude {
+ autovacuum.test delete3.test manydb.test
+ bigrow.test incrblob2.test memdb.test
+ bitvec.test index2.test memsubsys1.test
+ capi3c.test ioerr.test memsubsys2.test
+ capi3.test join3.test pagesize.test
+ collate5.test limit.test
+ } -initialize {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 25000000 64
+ sqlite3_config_lookaside 0 0
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ autoinstall_test_functions
+ } -shutdown {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 0 0
+ sqlite3_config_lookaside 100 500
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ }
+
+ run_tests "memsys5-2" -description {
+ Run tests using the allocator in mem5.c in a different configuration.
+ } -include {
+ select1.test
+ } -initialize {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 40000000 16
+ sqlite3_config_lookaside 0 0
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ autoinstall_test_functions
+ } -shutdown {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 0 0
+ sqlite3_config_lookaside 100 500
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ }
+}
+
+ifcapable threadsafe {
+ run_tests "no_mutex_try" -description {
+ The sqlite3_mutex_try() interface always fails
+ } -initialize {
+ catch {db close}
+ sqlite3_shutdown
+ install_mutex_counters 1
+ set ::disable_mutex_try 1
+ } -shutdown {
+ catch {db close}
+ sqlite3_shutdown
+ install_mutex_counters 0
+ }
+}
+
+run_tests "memsys6" -description {
+ Run tests using the allocator in mem6.c.
+} -exclude {
+ capi3.test capi3c.test
+} -initialize {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_chunkalloc 0
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ autoinstall_test_functions
+} -shutdown {
+ catch {db close}
+ sqlite3_reset_auto_extension
+ sqlite3_shutdown
+ sqlite3_config_heap 0 0
+ install_malloc_faultsim 1
+ sqlite3_initialize
+}
+
+# run_tests "crash_safe_append" -description {
+# Run crash.test with persistent journals on a SAFE_APPEND file-system.
+# } -initialize {
+# rename crashsql sa_crashsql
+# proc crashsql {args} {
+# set options [lrange $args 0 [expr {[llength $args]-2}]]
+# lappend options -char safe_append
+# set sql [lindex $args end]
+# lappend options "
+# PRAGMA journal_mode=persistent;
+# $sql
+# "
+# set fd [open test.db-journal w]
+# puts $fd [string repeat 1234567890 100000]
+# close $fd
+# eval sa_crashsql $options
+# }
+# } -shutdown {
+# rename crashsql {}
+# rename sa_crashsql crashsql
+# } -include crash.test
+
+
+# End of tests
+#############################################################################
+
+if {$::testmode eq "targets"} { puts "" ; exit }
+
+# Restore the [sqlite3] command.
+#
+rename sqlite3 {}
+rename really_sqlite3 sqlite3
+
+# Restore the [finish_test] command.
+#
+rename finish_test ""
+rename really_finish_test2 finish_test
+
+# Restore the [do_test] command.
+#
+rename do_test ""
+rename really_do_test do_test
+
+finish_test
diff --git a/third_party/sqlite/test/pragma.test b/third_party/sqlite/test/pragma.test
new file mode 100755
index 0000000..2857cfe
--- /dev/null
+++ b/third_party/sqlite/test/pragma.test
@@ -0,0 +1,1206 @@
+# 2002 March 6
+#
+# 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.
+#
+# This file implements tests for the PRAGMA command.
+#
+# $Id: pragma.test,v 1.64 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Test organization:
+#
+# pragma-1.*: Test cache_size, default_cache_size and synchronous on main db.
+# pragma-2.*: Test synchronous on attached db.
+# pragma-3.*: Test detection of table/index inconsistency by integrity_check.
+# pragma-4.*: Test cache_size and default_cache_size on attached db.
+# pragma-5.*: Test that pragma synchronous may not be used inside of a
+# transaction.
+# pragma-6.*: Test schema-query pragmas.
+# pragma-7.*: Miscellaneous tests.
+# pragma-8.*: Test user_version and schema_version pragmas.
+# pragma-9.*: Test temp_store and temp_store_directory.
+# pragma-10.*: Test the count_changes pragma in the presence of triggers.
+# pragma-11.*: Test the collation_list pragma.
+# pragma-14.*: Test the page_count pragma.
+# pragma-15.*: Test that the value set using the cache_size pragma is not
+# reset when the schema is reloaded.
+#
+
+ifcapable !pragma {
+ finish_test
+ return
+}
+
+# Delete the preexisting database to avoid the special setup
+# that the "all.test" script does.
+#
+db close
+file delete test.db test.db-journal
+file delete test3.db test3.db-journal
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+
+
+ifcapable pager_pragmas {
+set DFLT_CACHE_SZ [db one {PRAGMA default_cache_size}]
+set TEMP_CACHE_SZ [db one {PRAGMA temp.default_cache_size}]
+do_test pragma-1.1 {
+ execsql {
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ 2]
+do_test pragma-1.2 {
+ execsql {
+ PRAGMA synchronous=OFF;
+ PRAGMA cache_size=1234;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list 1234 $DFLT_CACHE_SZ 0]
+do_test pragma-1.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ 2]
+do_test pragma-1.4 {
+ execsql {
+ PRAGMA synchronous=OFF;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ 0]
+do_test pragma-1.5 {
+ execsql {
+ PRAGMA cache_size=4321;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list 4321 $DFLT_CACHE_SZ 0]
+do_test pragma-1.6 {
+ execsql {
+ PRAGMA synchronous=ON;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list 4321 $DFLT_CACHE_SZ 1]
+do_test pragma-1.7 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ 2]
+do_test pragma-1.8 {
+ execsql {
+ PRAGMA default_cache_size=123;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} {123 123 2}
+do_test pragma-1.9.1 {
+ db close
+ sqlite3 db test.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} {123 123 2}
+ifcapable vacuum {
+ do_test pragma-1.9.2 {
+ execsql {
+ VACUUM;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+ } {123 123 2}
+}
+do_test pragma-1.10 {
+ execsql {
+ PRAGMA synchronous=NORMAL;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} {123 123 1}
+do_test pragma-1.11 {
+ execsql {
+ PRAGMA synchronous=FULL;
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} {123 123 2}
+do_test pragma-1.12 {
+ db close
+ sqlite3 db test.db; set ::DB [sqlite3_connection_pointer db]
+ execsql {
+ PRAGMA cache_size;
+ PRAGMA default_cache_size;
+ PRAGMA synchronous;
+ }
+} {123 123 2}
+
+# Make sure the pragma handler understands numeric values in addition
+# to keywords like "off" and "full".
+#
+do_test pragma-1.13 {
+ execsql {
+ PRAGMA synchronous=0;
+ PRAGMA synchronous;
+ }
+} {0}
+do_test pragma-1.14 {
+ execsql {
+ PRAGMA synchronous=2;
+ PRAGMA synchronous;
+ }
+} {2}
+} ;# ifcapable pager_pragmas
+
+# Test turning "flag" pragmas on and off.
+#
+ifcapable debug {
+ # Pragma "vdbe_listing" is only available if compiled with SQLITE_DEBUG
+ #
+ do_test pragma-1.15 {
+ execsql {
+ PRAGMA vdbe_listing=YES;
+ PRAGMA vdbe_listing;
+ }
+ } {1}
+ do_test pragma-1.16 {
+ execsql {
+ PRAGMA vdbe_listing=NO;
+ PRAGMA vdbe_listing;
+ }
+ } {0}
+}
+
+do_test pragma-1.17 {
+ execsql {
+ PRAGMA parser_trace=ON;
+ PRAGMA parser_trace=OFF;
+ }
+} {}
+do_test pragma-1.18 {
+ execsql {
+ PRAGMA bogus = -1234; -- Parsing of negative values
+ }
+} {}
+
+# Test modifying the safety_level of an attached database.
+ifcapable pager_pragmas&&attach {
+ do_test pragma-2.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ } {}
+ do_test pragma-2.2 {
+ execsql {
+ pragma aux.synchronous;
+ }
+ } {2}
+ do_test pragma-2.3 {
+ execsql {
+ pragma aux.synchronous = OFF;
+ pragma aux.synchronous;
+ pragma synchronous;
+ }
+ } {0 2}
+ do_test pragma-2.4 {
+ execsql {
+ pragma aux.synchronous = ON;
+ pragma synchronous;
+ pragma aux.synchronous;
+ }
+ } {2 1}
+} ;# ifcapable pager_pragmas
+
+# Construct a corrupted index and make sure the integrity_check
+# pragma finds it.
+#
+# These tests won't work if the database is encrypted
+#
+do_test pragma-3.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ BEGIN;
+ CREATE TABLE t2(a,b,c);
+ CREATE INDEX i2 ON t2(a);
+ INSERT INTO t2 VALUES(11,2,3);
+ INSERT INTO t2 VALUES(22,3,4);
+ COMMIT;
+ SELECT rowid, * from t2;
+ }
+} {1 11 2 3 2 22 3 4}
+ifcapable attach {
+ if {![sqlite3 -has-codec] && $sqlite_options(integrityck)} {
+ do_test pragma-3.2 {
+ db eval {SELECT rootpage FROM sqlite_master WHERE name='i2'} break
+ set pgsz [db eval {PRAGMA page_size}]
+ # overwrite the header on the rootpage of the index in order to
+ # make the index appear to be empty.
+ #
+ set offset [expr {$pgsz*($rootpage-1)}]
+ hexio_write test.db $offset 0a00000000040000000000
+ db close
+ sqlite3 db test.db
+ execsql {PRAGMA integrity_check}
+ } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+ do_test pragma-3.3 {
+ execsql {PRAGMA integrity_check=1}
+ } {{rowid 1 missing from index i2}}
+ do_test pragma-3.4 {
+ execsql {
+ ATTACH DATABASE 'test.db' AS t2;
+ PRAGMA integrity_check
+ }
+ } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+ do_test pragma-3.5 {
+ execsql {
+ PRAGMA integrity_check=4
+ }
+ } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2}}
+ do_test pragma-3.6 {
+ execsql {
+ PRAGMA integrity_check=xyz
+ }
+ } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+ do_test pragma-3.7 {
+ execsql {
+ PRAGMA integrity_check=0
+ }
+ } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+
+ # Add additional corruption by appending unused pages to the end of
+ # the database file testerr.db
+ #
+ do_test pragma-3.8 {
+ execsql {DETACH t2}
+ file delete -force testerr.db testerr.db-journal
+ set out [open testerr.db w]
+ fconfigure $out -translation binary
+ set in [open test.db r]
+ fconfigure $in -translation binary
+ puts -nonewline $out [read $in]
+ seek $in 0
+ puts -nonewline $out [read $in]
+ close $in
+ close $out
+ execsql {REINDEX t2}
+ execsql {PRAGMA integrity_check}
+ } {ok}
+ do_test pragma-3.8.1 {
+ execsql {PRAGMA quick_check}
+ } {ok}
+ do_test pragma-3.9 {
+ execsql {
+ ATTACH 'testerr.db' AS t2;
+ PRAGMA integrity_check
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+ do_test pragma-3.10 {
+ execsql {
+ PRAGMA integrity_check=1
+ }
+ } {{*** in database t2 ***
+Page 4 is never used}}
+ do_test pragma-3.11 {
+ execsql {
+ PRAGMA integrity_check=5
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2}}
+ do_test pragma-3.12 {
+ execsql {
+ PRAGMA integrity_check=4
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2}}
+ do_test pragma-3.13 {
+ execsql {
+ PRAGMA integrity_check=3
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used}}
+ do_test pragma-3.14 {
+ execsql {
+ PRAGMA integrity_check(2)
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used}}
+ do_test pragma-3.15 {
+ execsql {
+ ATTACH 'testerr.db' AS t3;
+ PRAGMA integrity_check
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+ do_test pragma-3.16 {
+ execsql {
+ PRAGMA integrity_check(10)
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2}}
+ do_test pragma-3.17 {
+ execsql {
+ PRAGMA integrity_check=8
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 4 is never used
+Page 5 is never used}}
+ do_test pragma-3.18 {
+ execsql {
+ PRAGMA integrity_check=4
+ }
+ } {{*** in database t2 ***
+Page 4 is never used
+Page 5 is never used
+Page 6 is never used} {rowid 1 missing from index i2}}
+ }
+ do_test pragma-3.99 {
+ catchsql {DETACH t3}
+ catchsql {DETACH t2}
+ file delete -force testerr.db testerr.db-journal
+ catchsql {DROP INDEX i2}
+ } {0 {}}
+}
+
+# Test modifying the cache_size of an attached database.
+ifcapable pager_pragmas&&attach {
+do_test pragma-4.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ pragma aux.cache_size;
+ pragma aux.default_cache_size;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ]
+do_test pragma-4.2 {
+ execsql {
+ pragma aux.cache_size = 50;
+ pragma aux.cache_size;
+ pragma aux.default_cache_size;
+ }
+} [list 50 $DFLT_CACHE_SZ]
+do_test pragma-4.3 {
+ execsql {
+ pragma aux.default_cache_size = 456;
+ pragma aux.cache_size;
+ pragma aux.default_cache_size;
+ }
+} {456 456}
+do_test pragma-4.4 {
+ execsql {
+ pragma cache_size;
+ pragma default_cache_size;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ]
+do_test pragma-4.5 {
+ execsql {
+ DETACH aux;
+ ATTACH 'test3.db' AS aux;
+ pragma aux.cache_size;
+ pragma aux.default_cache_size;
+ }
+} [list $DFLT_CACHE_SZ $DFLT_CACHE_SZ]
+do_test pragma-4.6 {
+ execsql {
+ DETACH aux;
+ ATTACH 'test2.db' AS aux;
+ pragma aux.cache_size;
+ pragma aux.default_cache_size;
+ }
+} {456 456}
+} ;# ifcapable pager_pragmas
+
+# Test that modifying the sync-level in the middle of a transaction is
+# disallowed.
+ifcapable pager_pragmas {
+do_test pragma-5.0 {
+ execsql {
+ pragma synchronous;
+ }
+} {2}
+do_test pragma-5.1 {
+ catchsql {
+ BEGIN;
+ pragma synchronous = OFF;
+ }
+} {1 {Safety level may not be changed inside a transaction}}
+do_test pragma-5.2 {
+ execsql {
+ pragma synchronous;
+ }
+} {2}
+catchsql {COMMIT;}
+} ;# ifcapable pager_pragmas
+
+# Test schema-query pragmas
+#
+ifcapable schema_pragmas {
+ifcapable tempdb&&attach {
+ do_test pragma-6.1 {
+ set res {}
+ execsql {SELECT * FROM sqlite_temp_master}
+ foreach {idx name file} [execsql {pragma database_list}] {
+ lappend res $idx $name
+ }
+ set res
+ } {0 main 1 temp 2 aux}
+}
+do_test pragma-6.2 {
+ execsql {
+ pragma table_info(t2)
+ }
+} {0 a {} 0 {} 0 1 b {} 0 {} 0 2 c {} 0 {} 0}
+db nullvalue <<NULL>>
+do_test pragma-6.2.2 {
+ execsql {
+ CREATE TABLE t5(
+ a TEXT DEFAULT CURRENT_TIMESTAMP,
+ b DEFAULT (5+3),
+ c TEXT,
+ d INTEGER DEFAULT NULL,
+ e TEXT DEFAULT ''
+ );
+ PRAGMA table_info(t5);
+ }
+} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <<NULL>> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0}
+db nullvalue {}
+ifcapable {foreignkey} {
+ do_test pragma-6.3 {
+ execsql {
+ CREATE TABLE t3(a int references t2(b), b UNIQUE);
+ pragma foreign_key_list(t3);
+ }
+ } {0 0 t2 a b}
+ do_test pragma-6.4 {
+ execsql {
+ pragma index_list(t3);
+ }
+ } {0 sqlite_autoindex_t3_1 1}
+}
+ifcapable {!foreignkey} {
+ execsql {CREATE TABLE t3(a,b UNIQUE)}
+}
+do_test pragma-6.5 {
+ execsql {
+ CREATE INDEX t3i1 ON t3(a,b);
+ pragma index_info(t3i1);
+ }
+} {0 0 a 1 1 b}
+} ;# ifcapable schema_pragmas
+# Miscellaneous tests
+#
+ifcapable schema_pragmas {
+do_test pragma-7.1 {
+ # Make sure a pragma knows to read the schema if it needs to
+ db close
+ sqlite3 db test.db
+ execsql {
+ pragma index_list(t3);
+ }
+} {0 t3i1 0 1 sqlite_autoindex_t3_1 1}
+} ;# ifcapable schema_pragmas
+ifcapable {utf16} {
+ do_test pragma-7.2 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ pragma encoding=bogus;
+ }
+ } {1 {unsupported encoding: bogus}}
+}
+ifcapable tempdb {
+ do_test pragma-7.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ pragma lock_status;
+ }
+ } {main unlocked temp closed}
+} else {
+ do_test pragma-7.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ pragma lock_status;
+ }
+ } {main unlocked}
+}
+
+
+#----------------------------------------------------------------------
+# Test cases pragma-8.* test the "PRAGMA schema_version" and "PRAGMA
+# user_version" statements.
+#
+# pragma-8.1: PRAGMA schema_version
+# pragma-8.2: PRAGMA user_version
+#
+
+ifcapable schema_version {
+
+# First check that we can set the schema version and then retrieve the
+# same value.
+do_test pragma-8.1.1 {
+ execsql {
+ PRAGMA schema_version = 105;
+ }
+} {}
+do_test pragma-8.1.2 {
+ execsql2 {
+ PRAGMA schema_version;
+ }
+} {schema_version 105}
+do_test pragma-8.1.3 {
+ execsql {
+ PRAGMA schema_version = 106;
+ }
+} {}
+do_test pragma-8.1.4 {
+ execsql {
+ PRAGMA schema_version;
+ }
+} 106
+
+# Check that creating a table modifies the schema-version (this is really
+# to verify that the value being read is in fact the schema version).
+do_test pragma-8.1.5 {
+ execsql {
+ CREATE TABLE t4(a, b, c);
+ INSERT INTO t4 VALUES(1, 2, 3);
+ SELECT * FROM t4;
+ }
+} {1 2 3}
+do_test pragma-8.1.6 {
+ execsql {
+ PRAGMA schema_version;
+ }
+} 107
+
+# Now open a second connection to the database. Ensure that changing the
+# schema-version using the first connection forces the second connection
+# to reload the schema. This has to be done using the C-API test functions,
+# because the TCL API accounts for SCHEMA_ERROR and retries the query.
+do_test pragma-8.1.7 {
+ sqlite3 db2 test.db; set ::DB2 [sqlite3_connection_pointer db2]
+ execsql {
+ SELECT * FROM t4;
+ } db2
+} {1 2 3}
+do_test pragma-8.1.8 {
+ execsql {
+ PRAGMA schema_version = 108;
+ }
+} {}
+do_test pragma-8.1.9 {
+ set ::STMT [sqlite3_prepare $::DB2 "SELECT * FROM t4" -1 DUMMY]
+ sqlite3_step $::STMT
+} SQLITE_ERROR
+do_test pragma-8.1.10 {
+ sqlite3_finalize $::STMT
+} SQLITE_SCHEMA
+
+# Make sure the schema-version can be manipulated in an attached database.
+file delete -force test2.db
+file delete -force test2.db-journal
+ifcapable attach {
+ do_test pragma-8.1.11 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.t1(a, b, c);
+ PRAGMA aux.schema_version = 205;
+ }
+ } {}
+ do_test pragma-8.1.12 {
+ execsql {
+ PRAGMA aux.schema_version;
+ }
+ } 205
+}
+do_test pragma-8.1.13 {
+ execsql {
+ PRAGMA schema_version;
+ }
+} 108
+
+# And check that modifying the schema-version in an attached database
+# forces the second connection to reload the schema.
+ifcapable attach {
+ do_test pragma-8.1.14 {
+ sqlite3 db2 test.db; set ::DB2 [sqlite3_connection_pointer db2]
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ SELECT * FROM aux.t1;
+ } db2
+ } {}
+ do_test pragma-8.1.15 {
+ execsql {
+ PRAGMA aux.schema_version = 206;
+ }
+ } {}
+ do_test pragma-8.1.16 {
+ set ::STMT [sqlite3_prepare $::DB2 "SELECT * FROM aux.t1" -1 DUMMY]
+ sqlite3_step $::STMT
+ } SQLITE_ERROR
+ do_test pragma-8.1.17 {
+ sqlite3_finalize $::STMT
+ } SQLITE_SCHEMA
+ do_test pragma-8.1.18 {
+ db2 close
+ } {}
+}
+
+# Now test that the user-version can be read and written (and that we aren't
+# accidentally manipulating the schema-version instead).
+do_test pragma-8.2.1 {
+ execsql2 {
+ PRAGMA user_version;
+ }
+} {user_version 0}
+do_test pragma-8.2.2 {
+ execsql {
+ PRAGMA user_version = 2;
+ }
+} {}
+do_test pragma-8.2.3.1 {
+ execsql2 {
+ PRAGMA user_version;
+ }
+} {user_version 2}
+do_test pragma-8.2.3.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA user_version;
+ }
+} {2}
+do_test pragma-8.2.4.1 {
+ execsql {
+ PRAGMA schema_version;
+ }
+} {108}
+ifcapable vacuum {
+ do_test pragma-8.2.4.2 {
+ execsql {
+ VACUUM;
+ PRAGMA user_version;
+ }
+ } {2}
+ do_test pragma-8.2.4.3 {
+ execsql {
+ PRAGMA schema_version;
+ }
+ } {109}
+}
+
+ifcapable attach {
+ db eval {ATTACH 'test2.db' AS aux}
+
+ # Check that the user-version in the auxilary database can be manipulated (
+ # and that we aren't accidentally manipulating the same in the main db).
+ do_test pragma-8.2.5 {
+ execsql {
+ PRAGMA aux.user_version;
+ }
+ } {0}
+ do_test pragma-8.2.6 {
+ execsql {
+ PRAGMA aux.user_version = 3;
+ }
+ } {}
+ do_test pragma-8.2.7 {
+ execsql {
+ PRAGMA aux.user_version;
+ }
+ } {3}
+ do_test pragma-8.2.8 {
+ execsql {
+ PRAGMA main.user_version;
+ }
+ } {2}
+
+ # Now check that a ROLLBACK resets the user-version if it has been modified
+ # within a transaction.
+ do_test pragma-8.2.9 {
+ execsql {
+ BEGIN;
+ PRAGMA aux.user_version = 10;
+ PRAGMA user_version = 11;
+ }
+ } {}
+ do_test pragma-8.2.10 {
+ execsql {
+ PRAGMA aux.user_version;
+ }
+ } {10}
+ do_test pragma-8.2.11 {
+ execsql {
+ PRAGMA main.user_version;
+ }
+ } {11}
+ do_test pragma-8.2.12 {
+ execsql {
+ ROLLBACK;
+ PRAGMA aux.user_version;
+ }
+ } {3}
+ do_test pragma-8.2.13 {
+ execsql {
+ PRAGMA main.user_version;
+ }
+ } {2}
+}
+
+# Try a negative value for the user-version
+do_test pragma-8.2.14 {
+ execsql {
+ PRAGMA user_version = -450;
+ }
+} {}
+do_test pragma-8.2.15 {
+ execsql {
+ PRAGMA user_version;
+ }
+} {-450}
+} ; # ifcapable schema_version
+
+# Check to see if TEMP_STORE is memory or disk. Return strings
+# "memory" or "disk" as appropriate.
+#
+proc check_temp_store {} {
+ db eval {CREATE TEMP TABLE IF NOT EXISTS a(b)}
+ db eval {PRAGMA database_list} {
+ if {$name=="temp"} {
+ set bt [btree_from_db db 1]
+ if {[btree_ismemdb $bt]} {
+ return "memory"
+ }
+ return "disk"
+ }
+ }
+ return "unknown"
+}
+
+
+# Test temp_store and temp_store_directory pragmas
+#
+ifcapable pager_pragmas {
+do_test pragma-9.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA temp_store;
+ }
+} {0}
+if {$TEMP_STORE<=1} {
+ do_test pragma-9.1.1 {
+ check_temp_store
+ } {disk}
+} else {
+ do_test pragma-9.1.1 {
+ check_temp_store
+ } {memory}
+}
+
+do_test pragma-9.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA temp_store=file;
+ PRAGMA temp_store;
+ }
+} {1}
+if {$TEMP_STORE==3} {
+ # When TEMP_STORE is 3, always use memory regardless of pragma settings.
+ do_test pragma-9.2.1 {
+ check_temp_store
+ } {memory}
+} else {
+ do_test pragma-9.2.1 {
+ check_temp_store
+ } {disk}
+}
+
+do_test pragma-9.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA temp_store=memory;
+ PRAGMA temp_store;
+ }
+} {2}
+if {$TEMP_STORE==0} {
+ # When TEMP_STORE is 0, always use the disk regardless of pragma settings.
+ do_test pragma-9.3.1 {
+ check_temp_store
+ } {disk}
+} else {
+ do_test pragma-9.3.1 {
+ check_temp_store
+ } {memory}
+}
+
+do_test pragma-9.4 {
+ execsql {
+ PRAGMA temp_store_directory;
+ }
+} {}
+do_test pragma-9.5 {
+ set pwd [string map {' ''} [pwd]]
+ execsql "
+ PRAGMA temp_store_directory='$pwd';
+ "
+} {}
+do_test pragma-9.6 {
+ execsql {
+ PRAGMA temp_store_directory;
+ }
+} [list [pwd]]
+do_test pragma-9.7 {
+ catchsql {
+ PRAGMA temp_store_directory='/NON/EXISTENT/PATH/FOOBAR';
+ }
+} {1 {not a writable directory}}
+do_test pragma-9.8 {
+ execsql {
+ PRAGMA temp_store_directory='';
+ }
+} {}
+if {![info exists TEMP_STORE] || $TEMP_STORE<=1} {
+ ifcapable tempdb {
+ do_test pragma-9.9 {
+ execsql {
+ PRAGMA temp_store_directory;
+ PRAGMA temp_store=FILE;
+ CREATE TEMP TABLE temp_store_directory_test(a integer);
+ INSERT INTO temp_store_directory_test values (2);
+ SELECT * FROM temp_store_directory_test;
+ }
+ } {2}
+ do_test pragma-9.10 {
+ catchsql "
+ PRAGMA temp_store_directory='$pwd';
+ SELECT * FROM temp_store_directory_test;
+ "
+ } {1 {no such table: temp_store_directory_test}}
+ }
+}
+do_test pragma-9.11 {
+ execsql {
+ PRAGMA temp_store = 0;
+ PRAGMA temp_store;
+ }
+} {0}
+do_test pragma-9.12 {
+ execsql {
+ PRAGMA temp_store = 1;
+ PRAGMA temp_store;
+ }
+} {1}
+do_test pragma-9.13 {
+ execsql {
+ PRAGMA temp_store = 2;
+ PRAGMA temp_store;
+ }
+} {2}
+do_test pragma-9.14 {
+ execsql {
+ PRAGMA temp_store = 3;
+ PRAGMA temp_store;
+ }
+} {0}
+do_test pragma-9.15 {
+ catchsql {
+ BEGIN EXCLUSIVE;
+ CREATE TEMP TABLE temp_table(t);
+ INSERT INTO temp_table VALUES('valuable data');
+ PRAGMA temp_store = 1;
+ }
+} {1 {temporary storage cannot be changed from within a transaction}}
+do_test pragma-9.16 {
+ execsql {
+ SELECT * FROM temp_table;
+ COMMIT;
+ }
+} {{valuable data}}
+
+do_test pragma-9.17 {
+ execsql {
+ INSERT INTO temp_table VALUES('valuable data II');
+ SELECT * FROM temp_table;
+ }
+} {{valuable data} {valuable data II}}
+
+do_test pragma-9.18 {
+ set rc [catch {
+ db eval {SELECT t FROM temp_table} {
+ execsql {pragma temp_store = 1}
+ }
+ } msg]
+ list $rc $msg
+} {1 {temporary storage cannot be changed from within a transaction}}
+
+} ;# ifcapable pager_pragmas
+
+ifcapable trigger {
+
+do_test pragma-10.0 {
+ catchsql {
+ DROP TABLE main.t1;
+ }
+ execsql {
+ PRAGMA count_changes = 1;
+
+ CREATE TABLE t1(a PRIMARY KEY);
+ CREATE TABLE t1_mirror(a);
+ CREATE TABLE t1_mirror2(a);
+ CREATE TRIGGER t1_bi BEFORE INSERT ON t1 BEGIN
+ INSERT INTO t1_mirror VALUES(new.a);
+ END;
+ CREATE TRIGGER t1_ai AFTER INSERT ON t1 BEGIN
+ INSERT INTO t1_mirror2 VALUES(new.a);
+ END;
+ CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 BEGIN
+ UPDATE t1_mirror SET a = new.a WHERE a = old.a;
+ END;
+ CREATE TRIGGER t1_au AFTER UPDATE ON t1 BEGIN
+ UPDATE t1_mirror2 SET a = new.a WHERE a = old.a;
+ END;
+ CREATE TRIGGER t1_bd BEFORE DELETE ON t1 BEGIN
+ DELETE FROM t1_mirror WHERE a = old.a;
+ END;
+ CREATE TRIGGER t1_ad AFTER DELETE ON t1 BEGIN
+ DELETE FROM t1_mirror2 WHERE a = old.a;
+ END;
+ }
+} {}
+
+do_test pragma-10.1 {
+ execsql {
+ INSERT INTO t1 VALUES(randstr(10,10));
+ }
+} {1}
+do_test pragma-10.2 {
+ execsql {
+ UPDATE t1 SET a = randstr(10,10);
+ }
+} {1}
+do_test pragma-10.3 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {1}
+
+} ;# ifcapable trigger
+
+ifcapable schema_pragmas {
+ do_test pragma-11.1 {
+ execsql2 {
+ pragma collation_list;
+ }
+ } {seq 0 name NOCASE seq 1 name RTRIM seq 2 name BINARY}
+ do_test pragma-11.2 {
+ db collate New_Collation blah...
+ execsql {
+ pragma collation_list;
+ }
+ } {0 New_Collation 1 NOCASE 2 RTRIM 3 BINARY}
+}
+
+ifcapable schema_pragmas&&tempdb {
+ do_test pragma-12.1 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA temp.table_info('abc');
+ } db2
+ } {}
+ db2 close
+
+ do_test pragma-12.2 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA temp.default_cache_size = 200;
+ PRAGMA temp.default_cache_size;
+ } db2
+ } {200}
+ db2 close
+
+ do_test pragma-12.3 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA temp.cache_size = 400;
+ PRAGMA temp.cache_size;
+ } db2
+ } {400}
+ db2 close
+}
+
+ifcapable bloblit {
+
+do_test pragma-13.1 {
+ execsql {
+ DROP TABLE IF EXISTS t4;
+ PRAGMA vdbe_trace=on;
+ PRAGMA vdbe_listing=on;
+ PRAGMA sql_trace=on;
+ CREATE TABLE t4(a INTEGER PRIMARY KEY,b);
+ INSERT INTO t4(b) VALUES(x'0123456789abcdef0123456789abcdef0123456789');
+ INSERT INTO t4(b) VALUES(randstr(30,30));
+ INSERT INTO t4(b) VALUES(1.23456);
+ INSERT INTO t4(b) VALUES(NULL);
+ INSERT INTO t4(b) VALUES(0);
+ INSERT INTO t4(b) SELECT b||b||b||b FROM t4;
+ SELECT * FROM t4;
+ }
+ execsql {
+ PRAGMA vdbe_trace=off;
+ PRAGMA vdbe_listing=off;
+ PRAGMA sql_trace=off;
+ }
+} {}
+
+} ;# ifcapable bloblit
+
+ifcapable pager_pragmas {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+
+ do_test pragma-14.1 {
+ execsql { pragma auto_vacuum = 0 }
+ execsql { pragma page_count }
+ } {0}
+
+ do_test pragma-14.2 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ PRAGMA page_count;
+ }
+ } {2}
+
+ do_test pragma-14.3 {
+ execsql {
+ BEGIN;
+ CREATE TABLE def(a, b, c);
+ PRAGMA page_count;
+ }
+ } {3}
+
+ do_test pragma-14.4 {
+ set page_size [db one {pragma page_size}]
+ expr [file size test.db] / $page_size
+ } {2}
+
+ do_test pragma-14.5 {
+ execsql {
+ ROLLBACK;
+ PRAGMA page_count;
+ }
+ } {2}
+
+ do_test pragma-14.6 {
+ file delete -force test2.db
+ sqlite3 db2 test2.db
+ execsql {
+ PRAGMA auto_vacuum = 0;
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+ CREATE TABLE t3(a, b, c);
+ CREATE TABLE t4(a, b, c);
+ } db2
+ db2 close
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.page_count;
+ }
+ } {5}
+}
+
+# Test that the value set using the cache_size pragma is not reset when the
+# schema is reloaded.
+#
+ifcapable pager_pragmas {
+ db close
+ sqlite3 db test.db
+ do_test pragma-15.1 {
+ execsql {
+ PRAGMA cache_size=59;
+ PRAGMA cache_size;
+ }
+ } {59}
+ do_test pragma-15.2 {
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE newtable(a, b, c);
+ } db2
+ db2 close
+ } {}
+ do_test pragma-15.3 {
+ # Evaluating this statement will cause the schema to be reloaded (because
+ # the schema was changed by another connection in pragma-15.2). At one
+ # point there was a bug that reset the cache_size to its default value
+ # when this happened.
+ execsql { SELECT * FROM sqlite_master }
+ execsql { PRAGMA cache_size }
+ } {59}
+}
+
+# Reset the sqlite3_temp_directory variable for the next run of tests:
+sqlite3 dbX :memory:
+dbX eval {PRAGMA temp_store_directory = ""}
+dbX close
+
+finish_test
diff --git a/third_party/sqlite/test/pragma2.test b/third_party/sqlite/test/pragma2.test
new file mode 100755
index 0000000..87c3c5d
--- /dev/null
+++ b/third_party/sqlite/test/pragma2.test
@@ -0,0 +1,119 @@
+# 2002 March 6
+#
+# 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.
+#
+# This file implements tests for the PRAGMA command.
+#
+# $Id: pragma2.test,v 1.4 2007/10/09 08:29:33 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Test organization:
+#
+# pragma2-1.*: Test freelist_count pragma on the main database.
+# pragma2-2.*: Test freelist_count pragma on an attached database.
+# pragma2-3.*: Test trying to write to the freelist_count is a no-op.
+#
+
+ifcapable !pragma||!schema_pragmas {
+ finish_test
+ return
+}
+
+# Delete the preexisting database to avoid the special setup
+# that the "all.test" script does.
+#
+db close
+file delete test.db test.db-journal
+file delete test3.db test3.db-journal
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+db eval {PRAGMA auto_vacuum=0}
+
+do_test pragma2-1.1 {
+ execsql {
+ PRAGMA freelist_count;
+ }
+} {0}
+do_test pragma2-1.2 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ PRAGMA freelist_count;
+ }
+} {0}
+do_test pragma2-1.3 {
+ execsql {
+ DROP TABLE abc;
+ PRAGMA freelist_count;
+ }
+} {1}
+do_test pragma2-1.4 {
+ execsql {
+ PRAGMA main.freelist_count;
+ }
+} {1}
+
+file delete -force test2.db
+file delete -force test2.db-journal
+
+ifcapable attach {
+ do_test pragma2-2.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.auto_vacuum=OFF;
+ PRAGMA aux.freelist_count;
+ }
+ } {0}
+ do_test pragma2-2.2 {
+ execsql {
+ CREATE TABLE aux.abc(a, b, c);
+ PRAGMA aux.freelist_count;
+ }
+ } {0}
+ do_test pragma2-2.3 {
+ set ::val [string repeat 0123456789 1000]
+ execsql {
+ INSERT INTO aux.abc VALUES(1, 2, $::val);
+ PRAGMA aux.freelist_count;
+ }
+ } {0}
+ do_test pragma2-2.4 {
+ expr {[file size test2.db] / 1024}
+ } {11}
+ do_test pragma2-2.5 {
+ execsql {
+ DELETE FROM aux.abc;
+ PRAGMA aux.freelist_count;
+ }
+ } {9}
+
+ do_test pragma2-3.1 {
+ execsql {
+ PRAGMA aux.freelist_count;
+ PRAGMA main.freelist_count;
+ PRAGMA freelist_count;
+ }
+ } {9 1 1}
+ do_test pragma2-3.2 {
+ execsql {
+ PRAGMA freelist_count = 500;
+ PRAGMA freelist_count;
+ }
+ } {1 1}
+ do_test pragma2-3.3 {
+ execsql {
+ PRAGMA aux.freelist_count = 500;
+ PRAGMA aux.freelist_count;
+ }
+ } {9 9}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/printf.test b/third_party/sqlite/test/printf.test
new file mode 100755
index 0000000..346992d
--- /dev/null
+++ b/third_party/sqlite/test/printf.test
@@ -0,0 +1,3718 @@
+# 2001 September 15
+#
+# 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 file is testing the sqlite_*_printf() interface.
+#
+# $Id: printf.test,v 1.30 2008/07/09 16:51:52 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_test printf-1.1.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 1 1 1
+} {abc: 1 1 1 :xyz}
+do_test printf-1.1.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 1 1 1
+} {abc: ( 1) ( 1) ( 1) :xyz}
+do_test printf-1.1.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 1 1 1
+} {abc: (1 ) (1 ) (1 ) :xyz}
+do_test printf-1.1.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 1 1 1
+} {abc: ( +1) ( 1) ( 1) :xyz}
+do_test printf-1.1.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 1 1 1
+} {abc: (000001) (000001) (000001) :xyz}
+do_test printf-1.1.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 1 1 1
+} {abc: ( 1) ( 1) ( 1) :xyz}
+do_test printf-1.1.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 1 1 1
+} {abc: ( 1) ( 0x1) ( 01) :xyz}
+do_test printf-1.2.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 2 2 2
+} {abc: 2 2 2 :xyz}
+do_test printf-1.2.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 2 2 2
+} {abc: ( 2) ( 2) ( 2) :xyz}
+do_test printf-1.2.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 2 2 2
+} {abc: (2 ) (2 ) (2 ) :xyz}
+do_test printf-1.2.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 2 2 2
+} {abc: ( +2) ( 2) ( 2) :xyz}
+do_test printf-1.2.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 2 2 2
+} {abc: (000002) (000002) (000002) :xyz}
+do_test printf-1.2.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 2 2 2
+} {abc: ( 2) ( 2) ( 2) :xyz}
+do_test printf-1.2.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 2 2 2
+} {abc: ( 2) ( 0x2) ( 02) :xyz}
+do_test printf-1.3.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 5 5 5
+} {abc: 5 5 5 :xyz}
+do_test printf-1.3.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 5 5 5
+} {abc: ( 5) ( 5) ( 5) :xyz}
+do_test printf-1.3.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 5 5 5
+} {abc: (5 ) (5 ) (5 ) :xyz}
+do_test printf-1.3.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 5 5 5
+} {abc: ( +5) ( 5) ( 5) :xyz}
+do_test printf-1.3.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 5 5 5
+} {abc: (000005) (000005) (000005) :xyz}
+do_test printf-1.3.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 5 5 5
+} {abc: ( 5) ( 5) ( 5) :xyz}
+do_test printf-1.3.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 5 5 5
+} {abc: ( 5) ( 0x5) ( 05) :xyz}
+do_test printf-1.4.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 10 10 10
+} {abc: 10 a 12 :xyz}
+do_test printf-1.4.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 10 10 10
+} {abc: ( 10) ( a) ( 12) :xyz}
+do_test printf-1.4.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 10 10 10
+} {abc: (10 ) (a ) (12 ) :xyz}
+do_test printf-1.4.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 10 10 10
+} {abc: ( +10) ( a) ( 12) :xyz}
+do_test printf-1.4.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 10 10 10
+} {abc: (000010) (00000a) (000012) :xyz}
+do_test printf-1.4.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 10 10 10
+} {abc: ( 10) ( a) ( 12) :xyz}
+do_test printf-1.4.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 10 10 10
+} {abc: ( 10) ( 0xa) ( 012) :xyz}
+do_test printf-1.5.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 99 99 99
+} {abc: 99 63 143 :xyz}
+do_test printf-1.5.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 99 99 99
+} {abc: ( 99) ( 63) ( 143) :xyz}
+do_test printf-1.5.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 99 99 99
+} {abc: (99 ) (63 ) (143 ) :xyz}
+do_test printf-1.5.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 99 99 99
+} {abc: ( +99) ( 63) ( 143) :xyz}
+do_test printf-1.5.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 99 99 99
+} {abc: (000099) (000063) (000143) :xyz}
+do_test printf-1.5.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 99 99 99
+} {abc: ( 99) ( 63) ( 143) :xyz}
+do_test printf-1.5.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 99 99 99
+} {abc: ( 99) ( 0x63) ( 0143) :xyz}
+do_test printf-1.6.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 100 100 100
+} {abc: 100 64 144 :xyz}
+do_test printf-1.6.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 100 100 100
+} {abc: ( 100) ( 64) ( 144) :xyz}
+do_test printf-1.6.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 100 100 100
+} {abc: (100 ) (64 ) (144 ) :xyz}
+do_test printf-1.6.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 100 100 100
+} {abc: ( +100) ( 64) ( 144) :xyz}
+do_test printf-1.6.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 100 100 100
+} {abc: (000100) (000064) (000144) :xyz}
+do_test printf-1.6.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 100 100 100
+} {abc: ( 100) ( 64) ( 144) :xyz}
+do_test printf-1.6.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 100 100 100
+} {abc: ( 100) ( 0x64) ( 0144) :xyz}
+do_test printf-1.7.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 1000000 1000000 1000000
+} {abc: 1000000 f4240 3641100 :xyz}
+do_test printf-1.7.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: (1000000) ( f4240) (3641100) :xyz}
+do_test printf-1.7.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: (1000000) (f4240 ) (3641100) :xyz}
+do_test printf-1.7.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: (+1000000) ( f4240) (3641100) :xyz}
+do_test printf-1.7.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: (1000000) (0f4240) (3641100) :xyz}
+do_test printf-1.7.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: ( 1000000) ( f4240) (3641100) :xyz}
+do_test printf-1.7.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 1000000 1000000 1000000
+} {abc: (1000000) (0xf4240) (03641100) :xyz}
+do_test printf-1.8.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 999999999 999999999 999999999
+} {abc: 999999999 3b9ac9ff 7346544777 :xyz}
+do_test printf-1.8.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: (999999999) (3b9ac9ff) (7346544777) :xyz}
+do_test printf-1.8.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: (999999999) (3b9ac9ff) (7346544777) :xyz}
+do_test printf-1.8.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: (+999999999) (3b9ac9ff) (7346544777) :xyz}
+do_test printf-1.8.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: (999999999) (3b9ac9ff) (7346544777) :xyz}
+do_test printf-1.8.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: ( 999999999) (3b9ac9ff) (7346544777) :xyz}
+do_test printf-1.8.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 999999999 999999999 999999999
+} {abc: (999999999) (0x3b9ac9ff) (07346544777) :xyz}
+do_test printf-1.9.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0 0 0
+} {abc: 0 0 0 :xyz}
+do_test printf-1.9.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0 0 0
+} {abc: ( 0) ( 0) ( 0) :xyz}
+do_test printf-1.9.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0 0 0
+} {abc: (0 ) (0 ) (0 ) :xyz}
+do_test printf-1.9.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0 0 0
+} {abc: ( +0) ( 0) ( 0) :xyz}
+do_test printf-1.9.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0 0 0
+} {abc: (000000) (000000) (000000) :xyz}
+do_test printf-1.9.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0 0 0
+} {abc: ( 0) ( 0) ( 0) :xyz}
+do_test printf-1.9.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0 0 0
+} {abc: ( 0) ( 0) ( 0) :xyz}
+# 0xffffffff == -1
+do_test printf-1.10.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: -1 ffffffff 37777777777 :xyz}
+do_test printf-1.10.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: ( -1) (ffffffff) (37777777777) :xyz}
+do_test printf-1.10.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: (-1 ) (ffffffff) (37777777777) :xyz}
+do_test printf-1.10.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: ( -1) (ffffffff) (37777777777) :xyz}
+do_test printf-1.10.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: (-00001) (ffffffff) (37777777777) :xyz}
+do_test printf-1.10.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: ( -1) (ffffffff) (37777777777) :xyz}
+do_test printf-1.10.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xffffffff 0xffffffff 0xffffffff
+} {abc: ( -1) (0xffffffff) (037777777777) :xyz}
+# 0xfffffffe == -2
+do_test printf-1.11.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: -2 fffffffe 37777777776 :xyz}
+do_test printf-1.11.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: ( -2) (fffffffe) (37777777776) :xyz}
+do_test printf-1.11.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: (-2 ) (fffffffe) (37777777776) :xyz}
+do_test printf-1.11.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: ( -2) (fffffffe) (37777777776) :xyz}
+do_test printf-1.11.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: (-00002) (fffffffe) (37777777776) :xyz}
+do_test printf-1.11.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: ( -2) (fffffffe) (37777777776) :xyz}
+do_test printf-1.11.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xfffffffe 0xfffffffe 0xfffffffe
+} {abc: ( -2) (0xfffffffe) (037777777776) :xyz}
+# 0xfffffffb == -5
+do_test printf-1.12.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: -5 fffffffb 37777777773 :xyz}
+do_test printf-1.12.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: ( -5) (fffffffb) (37777777773) :xyz}
+do_test printf-1.12.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: (-5 ) (fffffffb) (37777777773) :xyz}
+do_test printf-1.12.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: ( -5) (fffffffb) (37777777773) :xyz}
+do_test printf-1.12.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: (-00005) (fffffffb) (37777777773) :xyz}
+do_test printf-1.12.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: ( -5) (fffffffb) (37777777773) :xyz}
+do_test printf-1.12.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xfffffffb 0xfffffffb 0xfffffffb
+} {abc: ( -5) (0xfffffffb) (037777777773) :xyz}
+# 0xfffffff6 == -10
+do_test printf-1.13.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: -10 fffffff6 37777777766 :xyz}
+do_test printf-1.13.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: ( -10) (fffffff6) (37777777766) :xyz}
+do_test printf-1.13.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: (-10 ) (fffffff6) (37777777766) :xyz}
+do_test printf-1.13.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: ( -10) (fffffff6) (37777777766) :xyz}
+do_test printf-1.13.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: (-00010) (fffffff6) (37777777766) :xyz}
+do_test printf-1.13.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: ( -10) (fffffff6) (37777777766) :xyz}
+do_test printf-1.13.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xfffffff6 0xfffffff6 0xfffffff6
+} {abc: ( -10) (0xfffffff6) (037777777766) :xyz}
+# 0xffffff9d == -99
+do_test printf-1.14.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: -99 ffffff9d 37777777635 :xyz}
+do_test printf-1.14.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: ( -99) (ffffff9d) (37777777635) :xyz}
+do_test printf-1.14.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: (-99 ) (ffffff9d) (37777777635) :xyz}
+do_test printf-1.14.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: ( -99) (ffffff9d) (37777777635) :xyz}
+do_test printf-1.14.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: (-00099) (ffffff9d) (37777777635) :xyz}
+do_test printf-1.14.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: ( -99) (ffffff9d) (37777777635) :xyz}
+do_test printf-1.14.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xffffff9d 0xffffff9d 0xffffff9d
+} {abc: ( -99) (0xffffff9d) (037777777635) :xyz}
+# 0xffffff9c == -100
+do_test printf-1.15.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: -100 ffffff9c 37777777634 :xyz}
+do_test printf-1.15.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: ( -100) (ffffff9c) (37777777634) :xyz}
+do_test printf-1.15.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: (-100 ) (ffffff9c) (37777777634) :xyz}
+do_test printf-1.15.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: ( -100) (ffffff9c) (37777777634) :xyz}
+do_test printf-1.15.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: (-00100) (ffffff9c) (37777777634) :xyz}
+do_test printf-1.15.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: ( -100) (ffffff9c) (37777777634) :xyz}
+do_test printf-1.15.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xffffff9c 0xffffff9c 0xffffff9c
+} {abc: ( -100) (0xffffff9c) (037777777634) :xyz}
+# 0xff676981 == -9999999
+do_test printf-1.16.1 {
+ sqlite3_mprintf_int {abc: %d %x %o :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: -9999999 ff676981 37731664601 :xyz}
+do_test printf-1.16.2 {
+ sqlite3_mprintf_int {abc: (%6d) (%6x) (%6o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (ff676981) (37731664601) :xyz}
+do_test printf-1.16.3 {
+ sqlite3_mprintf_int {abc: (%-6d) (%-6x) (%-6o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (ff676981) (37731664601) :xyz}
+do_test printf-1.16.4 {
+ sqlite3_mprintf_int {abc: (%+6d) (%+6x) (%+6o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (ff676981) (37731664601) :xyz}
+do_test printf-1.16.5 {
+ sqlite3_mprintf_int {abc: (%06d) (%06x) (%06o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (ff676981) (37731664601) :xyz}
+do_test printf-1.16.6 {
+ sqlite3_mprintf_int {abc: (% 6d) (% 6x) (% 6o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (ff676981) (37731664601) :xyz}
+do_test printf-1.16.7 {
+ sqlite3_mprintf_int {abc: (%#6d) (%#6x) (%#6o) :xyz}\
+ 0xff676981 0xff676981 0xff676981
+} {abc: (-9999999) (0xff676981) (037731664601) :xyz}
+do_test printf-2.1.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 0.001
+} {abc: (0.0) :xyz}
+do_test printf-2.1.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 0.001
+} {abc: (1.0e-03) :xyz}
+do_test printf-2.1.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.1.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 0.001
+} {abc: 1 1 (0.001) :xyz}
+do_test printf-2.1.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 0.001
+} {abc: 1 1 (0.00100000) :xyz}
+do_test printf-2.1.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 0.001
+} {abc: 1 1 (000000.001) :xyz}
+do_test printf-2.1.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 0.001
+} {abc: 1 1 (0.0) :xyz}
+do_test printf-2.1.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 0.001
+} {abc: 1 1 (1.0e-03) :xyz}
+do_test printf-2.1.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 0.001
+} {abc: 1 1 (0.001) :xyz}
+do_test printf-2.1.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 1.0e-20
+} {abc: (0.0) :xyz}
+do_test printf-2.1.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 1.0e-20
+} {abc: (1.0e-20) :xyz}
+do_test printf-2.1.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.1.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (1e-20) :xyz}
+do_test printf-2.1.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (1.00000e-20) :xyz}
+do_test printf-2.1.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (000001e-20) :xyz}
+do_test printf-2.1.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (0.0) :xyz}
+do_test printf-2.1.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (1.0e-20) :xyz}
+do_test printf-2.1.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 1.0e-20
+} {abc: 1 1 (1e-20) :xyz}
+do_test printf-2.1.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 1.0
+} {abc: (1.0) :xyz}
+do_test printf-2.1.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 1.0
+} {abc: (1.0e+00) :xyz}
+do_test printf-2.1.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 1.0
+} {abc: (1) :xyz}
+do_test printf-2.1.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 1.0
+} {abc: 1 1 (1) :xyz}
+do_test printf-2.1.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 1.0
+} {abc: 1 1 (1.00000) :xyz}
+do_test printf-2.1.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 1.0
+} {abc: 1 1 (0000000001) :xyz}
+do_test printf-2.1.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 1.0
+} {abc: 1 1 (1.0) :xyz}
+do_test printf-2.1.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 1.0
+} {abc: 1 1 (1.0e+00) :xyz}
+do_test printf-2.1.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 1.0
+} {abc: 1 1 (1) :xyz}
+do_test printf-2.1.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 0.0
+} {abc: (0.0) :xyz}
+do_test printf-2.1.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 0.0
+} {abc: (0.0e+00) :xyz}
+do_test printf-2.1.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 0.0
+} {abc: (0) :xyz}
+do_test printf-2.1.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 0.0
+} {abc: 1 1 (0) :xyz}
+do_test printf-2.1.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 0.0
+} {abc: 1 1 (0.00000) :xyz}
+do_test printf-2.1.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 0.0
+} {abc: 1 1 (0000000000) :xyz}
+do_test printf-2.1.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 0.0
+} {abc: 1 1 (0.0) :xyz}
+do_test printf-2.1.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 0.0
+} {abc: 1 1 (0.0e+00) :xyz}
+do_test printf-2.1.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 0.0
+} {abc: 1 1 (0) :xyz}
+do_test printf-2.1.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 100.0
+} {abc: (100.0) :xyz}
+do_test printf-2.1.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 100.0
+} {abc: (1.0e+02) :xyz}
+do_test printf-2.1.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 100.0
+} {abc: (1e+02) :xyz}
+do_test printf-2.1.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 100.0
+} {abc: 1 1 (100) :xyz}
+do_test printf-2.1.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 100.0
+} {abc: 1 1 (100.000) :xyz}
+do_test printf-2.1.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 100.0
+} {abc: 1 1 (0000000100) :xyz}
+do_test printf-2.1.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 100.0
+} {abc: 1 1 (100.0) :xyz}
+do_test printf-2.1.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 100.0
+} {abc: 1 1 (1.0e+02) :xyz}
+do_test printf-2.1.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 100.0
+} {abc: 1 1 (1e+02) :xyz}
+do_test printf-2.1.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 9.99999
+} {abc: (10.0) :xyz}
+do_test printf-2.1.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 9.99999
+} {abc: (1.0e+01) :xyz}
+do_test printf-2.1.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 9.99999
+} {abc: (1e+01) :xyz}
+do_test printf-2.1.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 9.99999
+} {abc: 1 1 (9.99999) :xyz}
+do_test printf-2.1.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 9.99999
+} {abc: 1 1 (9.99999) :xyz}
+do_test printf-2.1.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 9.99999
+} {abc: 1 1 (0009.99999) :xyz}
+do_test printf-2.1.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 9.99999
+} {abc: 1 1 (10.0) :xyz}
+do_test printf-2.1.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 9.99999
+} {abc: 1 1 (1.0e+01) :xyz}
+do_test printf-2.1.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 9.99999
+} {abc: 1 1 (1e+01) :xyz}
+do_test printf-2.1.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 -0.00543
+} {abc: (-0.0) :xyz}
+do_test printf-2.1.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 -0.00543
+} {abc: (-5.4e-03) :xyz}
+do_test printf-2.1.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 -0.00543
+} {abc: (-0.005) :xyz}
+do_test printf-2.1.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-0.00543) :xyz}
+do_test printf-2.1.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-0.00543000) :xyz}
+do_test printf-2.1.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-000.00543) :xyz}
+do_test printf-2.1.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-0.0) :xyz}
+do_test printf-2.1.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-5.4e-03) :xyz}
+do_test printf-2.1.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 -0.00543
+} {abc: 1 1 (-0.005) :xyz}
+do_test printf-2.1.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 -1.0
+} {abc: (-1.0) :xyz}
+do_test printf-2.1.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 -1.0
+} {abc: (-1.0e+00) :xyz}
+do_test printf-2.1.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 -1.0
+} {abc: (-1) :xyz}
+do_test printf-2.1.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 -1.0
+} {abc: 1 1 (-1) :xyz}
+do_test printf-2.1.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 -1.0
+} {abc: 1 1 (-1.00000) :xyz}
+do_test printf-2.1.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 -1.0
+} {abc: 1 1 (-000000001) :xyz}
+do_test printf-2.1.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 -1.0
+} {abc: 1 1 (-1.0) :xyz}
+do_test printf-2.1.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 -1.0
+} {abc: 1 1 (-1.0e+00) :xyz}
+do_test printf-2.1.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 -1.0
+} {abc: 1 1 (-1) :xyz}
+do_test printf-2.1.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 -99.99999
+} {abc: (-100.0) :xyz}
+do_test printf-2.1.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 -99.99999
+} {abc: (-1.0e+02) :xyz}
+do_test printf-2.1.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 -99.99999
+} {abc: (-1e+02) :xyz}
+do_test printf-2.1.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-100) :xyz}
+do_test printf-2.1.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-100.000) :xyz}
+do_test printf-2.1.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-000000100) :xyz}
+do_test printf-2.1.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-100.0) :xyz}
+do_test printf-2.1.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-1.0e+02) :xyz}
+do_test printf-2.1.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 -99.99999
+} {abc: 1 1 (-1e+02) :xyz}
+do_test printf-2.1.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 3.14e+9
+} {abc: (3140000000.0) :xyz}
+do_test printf-2.1.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 3.14e+9
+} {abc: (3.1e+09) :xyz}
+do_test printf-2.1.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 3.14e+9
+} {abc: (3e+09) :xyz}
+do_test printf-2.1.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (3.14e+09) :xyz}
+do_test printf-2.1.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (3.14000e+09) :xyz}
+do_test printf-2.1.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (003.14e+09) :xyz}
+do_test printf-2.1.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (3140000000.0) :xyz}
+do_test printf-2.1.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (3.1e+09) :xyz}
+do_test printf-2.1.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 3.14e+9
+} {abc: 1 1 (3e+09) :xyz}
+do_test printf-2.1.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 -4.72732e+88
+} {abc: (-4.7e+88) :xyz}
+do_test printf-2.1.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 -4.72732e+88
+} {abc: (-5e+88) :xyz}
+do_test printf-2.1.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 -4.72732e+88
+} {abc: 1 1 (-4.72732e+88) :xyz}
+do_test printf-2.1.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 -4.72732e+88
+} {abc: 1 1 (-4.72732e+88) :xyz}
+do_test printf-2.1.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 -4.72732e+88
+} {abc: 1 1 (-4.72732e+88) :xyz}
+do_test printf-2.1.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 -4.72732e+88
+} {abc: 1 1 (-4.7e+88) :xyz}
+do_test printf-2.1.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 -4.72732e+88
+} {abc: 1 1 (-5e+88) :xyz}
+do_test printf-2.1.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 9.87991e+143
+} {abc: (9.9e+143) :xyz}
+do_test printf-2.1.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 9.87991e+143
+} {abc: (1e+144) :xyz}
+do_test printf-2.1.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 9.87991e+143
+} {abc: 1 1 (9.87991e+143) :xyz}
+do_test printf-2.1.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 9.87991e+143
+} {abc: 1 1 (9.87991e+143) :xyz}
+do_test printf-2.1.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 9.87991e+143
+} {abc: 1 1 (9.87991e+143) :xyz}
+do_test printf-2.1.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 9.87991e+143
+} {abc: 1 1 (9.9e+143) :xyz}
+do_test printf-2.1.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 9.87991e+143
+} {abc: 1 1 (1e+144) :xyz}
+do_test printf-2.1.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 -6.287291e-9
+} {abc: (-0.0) :xyz}
+do_test printf-2.1.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 -6.287291e-9
+} {abc: (-6.3e-09) :xyz}
+do_test printf-2.1.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 -6.287291e-9
+} {abc: (-6e-09) :xyz}
+do_test printf-2.1.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-6.28729e-09) :xyz}
+do_test printf-2.1.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-6.28729e-09) :xyz}
+do_test printf-2.1.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-6.28729e-09) :xyz}
+do_test printf-2.1.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-0.0) :xyz}
+do_test printf-2.1.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-6.3e-09) :xyz}
+do_test printf-2.1.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 -6.287291e-9
+} {abc: 1 1 (-6e-09) :xyz}
+do_test printf-2.1.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 3.38826392e-110
+} {abc: (0.0) :xyz}
+do_test printf-2.1.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 1 1 3.38826392e-110
+} {abc: (3.4e-110) :xyz}
+do_test printf-2.1.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 1 1 3.38826392e-110
+} {abc: (3e-110) :xyz}
+do_test printf-2.1.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (3.38826e-110) :xyz}
+do_test printf-2.1.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (3.38826e-110) :xyz}
+do_test printf-2.1.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (3.38826e-110) :xyz}
+do_test printf-2.1.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1f) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (0.0) :xyz}
+do_test printf-2.1.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1e) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (3.4e-110) :xyz}
+do_test printf-2.1.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 3.38826392e-110
+} {abc: 1 1 (3e-110) :xyz}
+do_test printf-2.2.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 0.001
+} {abc: (0.00100) :xyz}
+do_test printf-2.2.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 0.001
+} {abc: (1.00000e-03) :xyz}
+do_test printf-2.2.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.2.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 0.001
+} {abc: 5 5 (0.001) :xyz}
+do_test printf-2.2.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 0.001
+} {abc: 5 5 (0.00100000) :xyz}
+do_test printf-2.2.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 0.001
+} {abc: 5 5 (000000.001) :xyz}
+do_test printf-2.2.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 0.001
+} {abc: 5 5 (0.00100) :xyz}
+do_test printf-2.2.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 0.001
+} {abc: 5 5 (1.00000e-03) :xyz}
+do_test printf-2.2.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 0.001
+} {abc: 5 5 (0.001) :xyz}
+do_test printf-2.2.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 1.0e-20
+} {abc: (0.00000) :xyz}
+do_test printf-2.2.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 1.0e-20
+} {abc: (1.00000e-20) :xyz}
+do_test printf-2.2.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.2.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (1e-20) :xyz}
+do_test printf-2.2.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (1.00000e-20) :xyz}
+do_test printf-2.2.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (000001e-20) :xyz}
+do_test printf-2.2.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (0.00000) :xyz}
+do_test printf-2.2.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (1.00000e-20) :xyz}
+do_test printf-2.2.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 1.0e-20
+} {abc: 5 5 (1e-20) :xyz}
+do_test printf-2.2.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 1.0
+} {abc: (1.00000) :xyz}
+do_test printf-2.2.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 1.0
+} {abc: (1.00000e+00) :xyz}
+do_test printf-2.2.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.2.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 1.0
+} {abc: 5 5 (1) :xyz}
+do_test printf-2.2.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 1.0
+} {abc: 5 5 (1.00000) :xyz}
+do_test printf-2.2.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 1.0
+} {abc: 5 5 (0000000001) :xyz}
+do_test printf-2.2.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 1.0
+} {abc: 5 5 (1.00000) :xyz}
+do_test printf-2.2.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 1.0
+} {abc: 5 5 (1.00000e+00) :xyz}
+do_test printf-2.2.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 1.0
+} {abc: 5 5 ( 1) :xyz}
+do_test printf-2.2.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 0.0
+} {abc: (0.00000) :xyz}
+do_test printf-2.2.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 0.0
+} {abc: (0.00000e+00) :xyz}
+do_test printf-2.2.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.2.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 0.0
+} {abc: 5 5 (0) :xyz}
+do_test printf-2.2.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 0.0
+} {abc: 5 5 (0.00000) :xyz}
+do_test printf-2.2.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 0.0
+} {abc: 5 5 (0000000000) :xyz}
+do_test printf-2.2.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 0.0
+} {abc: 5 5 (0.00000) :xyz}
+do_test printf-2.2.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 0.0
+} {abc: 5 5 (0.00000e+00) :xyz}
+do_test printf-2.2.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 0.0
+} {abc: 5 5 ( 0) :xyz}
+do_test printf-2.2.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 100.0
+} {abc: (100.00000) :xyz}
+do_test printf-2.2.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 100.0
+} {abc: (1.00000e+02) :xyz}
+do_test printf-2.2.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 100.0
+} {abc: ( 100) :xyz}
+do_test printf-2.2.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 100.0
+} {abc: 5 5 (100) :xyz}
+do_test printf-2.2.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 100.0
+} {abc: 5 5 (100.000) :xyz}
+do_test printf-2.2.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 100.0
+} {abc: 5 5 (0000000100) :xyz}
+do_test printf-2.2.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 100.0
+} {abc: 5 5 (100.00000) :xyz}
+do_test printf-2.2.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 100.0
+} {abc: 5 5 (1.00000e+02) :xyz}
+do_test printf-2.2.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 100.0
+} {abc: 5 5 ( 100) :xyz}
+do_test printf-2.2.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 9.99999
+} {abc: (9.99999) :xyz}
+do_test printf-2.2.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 9.99999
+} {abc: (9.99999e+00) :xyz}
+do_test printf-2.2.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 9.99999
+} {abc: ( 10) :xyz}
+do_test printf-2.2.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 9.99999
+} {abc: 5 5 (9.99999) :xyz}
+do_test printf-2.2.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 9.99999
+} {abc: 5 5 (9.99999) :xyz}
+do_test printf-2.2.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 9.99999
+} {abc: 5 5 (0009.99999) :xyz}
+do_test printf-2.2.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 9.99999
+} {abc: 5 5 (9.99999) :xyz}
+do_test printf-2.2.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 9.99999
+} {abc: 5 5 (9.99999e+00) :xyz}
+do_test printf-2.2.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 9.99999
+} {abc: 5 5 ( 10) :xyz}
+do_test printf-2.2.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 -0.00543
+} {abc: (-0.00543) :xyz}
+do_test printf-2.2.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 -0.00543
+} {abc: (-5.43000e-03) :xyz}
+do_test printf-2.2.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 -0.00543
+} {abc: (-0.00543) :xyz}
+do_test printf-2.2.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-0.00543) :xyz}
+do_test printf-2.2.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-0.00543000) :xyz}
+do_test printf-2.2.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-000.00543) :xyz}
+do_test printf-2.2.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-0.00543) :xyz}
+do_test printf-2.2.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-5.43000e-03) :xyz}
+do_test printf-2.2.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 -0.00543
+} {abc: 5 5 (-0.00543) :xyz}
+do_test printf-2.2.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 -1.0
+} {abc: (-1.00000) :xyz}
+do_test printf-2.2.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 -1.0
+} {abc: (-1.00000e+00) :xyz}
+do_test printf-2.2.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 -1.0
+} {abc: ( -1) :xyz}
+do_test printf-2.2.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 -1.0
+} {abc: 5 5 (-1) :xyz}
+do_test printf-2.2.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 -1.0
+} {abc: 5 5 (-1.00000) :xyz}
+do_test printf-2.2.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 -1.0
+} {abc: 5 5 (-000000001) :xyz}
+do_test printf-2.2.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 -1.0
+} {abc: 5 5 (-1.00000) :xyz}
+do_test printf-2.2.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 -1.0
+} {abc: 5 5 (-1.00000e+00) :xyz}
+do_test printf-2.2.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 -1.0
+} {abc: 5 5 ( -1) :xyz}
+do_test printf-2.2.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 -99.99999
+} {abc: (-99.99999) :xyz}
+do_test printf-2.2.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 -99.99999
+} {abc: (-1.00000e+02) :xyz}
+do_test printf-2.2.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 -99.99999
+} {abc: ( -100) :xyz}
+do_test printf-2.2.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 -99.99999
+} {abc: 5 5 (-100) :xyz}
+do_test printf-2.2.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 -99.99999
+} {abc: 5 5 (-100.000) :xyz}
+do_test printf-2.2.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 -99.99999
+} {abc: 5 5 (-000000100) :xyz}
+do_test printf-2.2.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 -99.99999
+} {abc: 5 5 (-99.99999) :xyz}
+do_test printf-2.2.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 -99.99999
+} {abc: 5 5 (-1.00000e+02) :xyz}
+do_test printf-2.2.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 -99.99999
+} {abc: 5 5 ( -100) :xyz}
+do_test printf-2.2.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 3.14e+9
+} {abc: (3140000000.00000) :xyz}
+do_test printf-2.2.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 3.14e+9
+} {abc: (3.14000e+09) :xyz}
+do_test printf-2.2.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 3.14e+9
+} {abc: (3.14e+09) :xyz}
+do_test printf-2.2.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (3.14e+09) :xyz}
+do_test printf-2.2.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (3.14000e+09) :xyz}
+do_test printf-2.2.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (003.14e+09) :xyz}
+do_test printf-2.2.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (3140000000.00000) :xyz}
+do_test printf-2.2.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (3.14000e+09) :xyz}
+do_test printf-2.2.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 3.14e+9
+} {abc: 5 5 (3.14e+09) :xyz}
+do_test printf-2.2.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 -4.72732e+88
+} {abc: (-4.72732e+88) :xyz}
+do_test printf-2.2.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 -4.72732e+88
+} {abc: (-4.7273e+88) :xyz}
+do_test printf-2.2.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 -4.72732e+88
+} {abc: 5 5 (-4.72732e+88) :xyz}
+do_test printf-2.2.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 -4.72732e+88
+} {abc: 5 5 (-4.72732e+88) :xyz}
+do_test printf-2.2.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 -4.72732e+88
+} {abc: 5 5 (-4.72732e+88) :xyz}
+do_test printf-2.2.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 -4.72732e+88
+} {abc: 5 5 (-4.72732e+88) :xyz}
+do_test printf-2.2.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 -4.72732e+88
+} {abc: 5 5 (-4.7273e+88) :xyz}
+do_test printf-2.2.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 9.87991e+143
+} {abc: (9.87991e+143) :xyz}
+do_test printf-2.2.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 9.87991e+143
+} {abc: (9.8799e+143) :xyz}
+do_test printf-2.2.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 9.87991e+143
+} {abc: 5 5 (9.87991e+143) :xyz}
+do_test printf-2.2.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 9.87991e+143
+} {abc: 5 5 (9.87991e+143) :xyz}
+do_test printf-2.2.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 9.87991e+143
+} {abc: 5 5 (9.87991e+143) :xyz}
+do_test printf-2.2.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 9.87991e+143
+} {abc: 5 5 (9.87991e+143) :xyz}
+do_test printf-2.2.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 9.87991e+143
+} {abc: 5 5 (9.8799e+143) :xyz}
+do_test printf-2.2.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 -6.287291e-9
+} {abc: (-0.00000) :xyz}
+do_test printf-2.2.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 -6.287291e-9
+} {abc: (-6.28729e-09) :xyz}
+do_test printf-2.2.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 -6.287291e-9
+} {abc: (-6.2873e-09) :xyz}
+do_test printf-2.2.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-6.28729e-09) :xyz}
+do_test printf-2.2.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-6.28729e-09) :xyz}
+do_test printf-2.2.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-6.28729e-09) :xyz}
+do_test printf-2.2.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-0.00000) :xyz}
+do_test printf-2.2.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-6.28729e-09) :xyz}
+do_test printf-2.2.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 -6.287291e-9
+} {abc: 5 5 (-6.2873e-09) :xyz}
+do_test printf-2.2.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 5 5 3.38826392e-110
+} {abc: (0.00000) :xyz}
+do_test printf-2.2.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 5 5 3.38826392e-110
+} {abc: (3.38826e-110) :xyz}
+do_test printf-2.2.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 5 5 3.38826392e-110
+} {abc: (3.3883e-110) :xyz}
+do_test printf-2.2.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (3.38826e-110) :xyz}
+do_test printf-2.2.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (3.38826e-110) :xyz}
+do_test printf-2.2.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (3.38826e-110) :xyz}
+do_test printf-2.2.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5f) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (0.00000) :xyz}
+do_test printf-2.2.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5e) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (3.38826e-110) :xyz}
+do_test printf-2.2.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%5.5g) :xyz} 5 5 3.38826392e-110
+} {abc: 5 5 (3.3883e-110) :xyz}
+do_test printf-2.3.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 0.001
+} {abc: (0.0010000000) :xyz}
+do_test printf-2.3.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 0.001
+} {abc: (1.0000000000e-03) :xyz}
+do_test printf-2.3.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 0.001
+} {abc: ( 0.001) :xyz}
+do_test printf-2.3.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 0.001
+} {abc: 10 10 (0.001) :xyz}
+do_test printf-2.3.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 0.001
+} {abc: 10 10 (0.00100000) :xyz}
+do_test printf-2.3.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 0.001
+} {abc: 10 10 (000000.001) :xyz}
+do_test printf-2.3.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 0.001
+} {abc: 10 10 (0.0010000000) :xyz}
+do_test printf-2.3.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 0.001
+} {abc: 10 10 (1.0000000000e-03) :xyz}
+do_test printf-2.3.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 0.001
+} {abc: 10 10 ( 0.001) :xyz}
+do_test printf-2.3.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 1.0e-20
+} {abc: (0.0000000000) :xyz}
+do_test printf-2.3.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 1.0e-20
+} {abc: (1.0000000000e-20) :xyz}
+do_test printf-2.3.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 1.0e-20
+} {abc: ( 1e-20) :xyz}
+do_test printf-2.3.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 1.0e-20
+} {abc: 10 10 (1e-20) :xyz}
+do_test printf-2.3.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 1.0e-20
+} {abc: 10 10 (1.00000e-20) :xyz}
+do_test printf-2.3.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 1.0e-20
+} {abc: 10 10 (000001e-20) :xyz}
+do_test printf-2.3.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 1.0e-20
+} {abc: 10 10 (0.0000000000) :xyz}
+do_test printf-2.3.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 1.0e-20
+} {abc: 10 10 (1.0000000000e-20) :xyz}
+do_test printf-2.3.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 1.0e-20
+} {abc: 10 10 ( 1e-20) :xyz}
+do_test printf-2.3.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 1.0
+} {abc: (1.0000000000) :xyz}
+do_test printf-2.3.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 1.0
+} {abc: (1.0000000000e+00) :xyz}
+do_test printf-2.3.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.3.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 1.0
+} {abc: 10 10 (1) :xyz}
+do_test printf-2.3.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 1.0
+} {abc: 10 10 (1.00000) :xyz}
+do_test printf-2.3.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 1.0
+} {abc: 10 10 (0000000001) :xyz}
+do_test printf-2.3.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 1.0
+} {abc: 10 10 (1.0000000000) :xyz}
+do_test printf-2.3.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 1.0
+} {abc: 10 10 (1.0000000000e+00) :xyz}
+do_test printf-2.3.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 1.0
+} {abc: 10 10 ( 1) :xyz}
+do_test printf-2.3.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 0.0
+} {abc: (0.0000000000) :xyz}
+do_test printf-2.3.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 0.0
+} {abc: (0.0000000000e+00) :xyz}
+do_test printf-2.3.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.3.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 0.0
+} {abc: 10 10 (0) :xyz}
+do_test printf-2.3.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 0.0
+} {abc: 10 10 (0.00000) :xyz}
+do_test printf-2.3.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 0.0
+} {abc: 10 10 (0000000000) :xyz}
+do_test printf-2.3.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 0.0
+} {abc: 10 10 (0.0000000000) :xyz}
+do_test printf-2.3.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 0.0
+} {abc: 10 10 (0.0000000000e+00) :xyz}
+do_test printf-2.3.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 0.0
+} {abc: 10 10 ( 0) :xyz}
+do_test printf-2.3.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 100.0
+} {abc: (100.0000000000) :xyz}
+do_test printf-2.3.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 100.0
+} {abc: (1.0000000000e+02) :xyz}
+do_test printf-2.3.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 100.0
+} {abc: ( 100) :xyz}
+do_test printf-2.3.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 100.0
+} {abc: 10 10 (100) :xyz}
+do_test printf-2.3.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 100.0
+} {abc: 10 10 (100.000) :xyz}
+do_test printf-2.3.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 100.0
+} {abc: 10 10 (0000000100) :xyz}
+do_test printf-2.3.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 100.0
+} {abc: 10 10 (100.0000000000) :xyz}
+do_test printf-2.3.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 100.0
+} {abc: 10 10 (1.0000000000e+02) :xyz}
+do_test printf-2.3.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 100.0
+} {abc: 10 10 ( 100) :xyz}
+do_test printf-2.3.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 9.99999
+} {abc: (9.9999900000) :xyz}
+do_test printf-2.3.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 9.99999
+} {abc: (9.9999900000e+00) :xyz}
+do_test printf-2.3.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 9.99999
+} {abc: ( 9.99999) :xyz}
+do_test printf-2.3.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 9.99999
+} {abc: 10 10 (9.99999) :xyz}
+do_test printf-2.3.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 9.99999
+} {abc: 10 10 (9.99999) :xyz}
+do_test printf-2.3.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 9.99999
+} {abc: 10 10 (0009.99999) :xyz}
+do_test printf-2.3.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 9.99999
+} {abc: 10 10 (9.9999900000) :xyz}
+do_test printf-2.3.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 9.99999
+} {abc: 10 10 (9.9999900000e+00) :xyz}
+do_test printf-2.3.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 9.99999
+} {abc: 10 10 ( 9.99999) :xyz}
+do_test printf-2.3.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 -0.00543
+} {abc: (-0.0054300000) :xyz}
+do_test printf-2.3.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 -0.00543
+} {abc: (-5.4300000000e-03) :xyz}
+do_test printf-2.3.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 -0.00543
+} {abc: ( -0.00543) :xyz}
+do_test printf-2.3.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 -0.00543
+} {abc: 10 10 (-0.00543) :xyz}
+do_test printf-2.3.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 -0.00543
+} {abc: 10 10 (-0.00543000) :xyz}
+do_test printf-2.3.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 -0.00543
+} {abc: 10 10 (-000.00543) :xyz}
+do_test printf-2.3.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 -0.00543
+} {abc: 10 10 (-0.0054300000) :xyz}
+do_test printf-2.3.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 -0.00543
+} {abc: 10 10 (-5.4300000000e-03) :xyz}
+do_test printf-2.3.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 -0.00543
+} {abc: 10 10 ( -0.00543) :xyz}
+do_test printf-2.3.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 -1.0
+} {abc: (-1.0000000000) :xyz}
+do_test printf-2.3.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 -1.0
+} {abc: (-1.0000000000e+00) :xyz}
+do_test printf-2.3.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 -1.0
+} {abc: ( -1) :xyz}
+do_test printf-2.3.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 -1.0
+} {abc: 10 10 (-1) :xyz}
+do_test printf-2.3.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 -1.0
+} {abc: 10 10 (-1.00000) :xyz}
+do_test printf-2.3.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 -1.0
+} {abc: 10 10 (-000000001) :xyz}
+do_test printf-2.3.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 -1.0
+} {abc: 10 10 (-1.0000000000) :xyz}
+do_test printf-2.3.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 -1.0
+} {abc: 10 10 (-1.0000000000e+00) :xyz}
+do_test printf-2.3.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 -1.0
+} {abc: 10 10 ( -1) :xyz}
+do_test printf-2.3.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 -99.99999
+} {abc: (-99.9999900000) :xyz}
+do_test printf-2.3.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 -99.99999
+} {abc: (-9.9999990000e+01) :xyz}
+do_test printf-2.3.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 -99.99999
+} {abc: ( -99.99999) :xyz}
+do_test printf-2.3.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 -99.99999
+} {abc: 10 10 (-100) :xyz}
+do_test printf-2.3.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 -99.99999
+} {abc: 10 10 (-100.000) :xyz}
+do_test printf-2.3.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 -99.99999
+} {abc: 10 10 (-000000100) :xyz}
+do_test printf-2.3.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 -99.99999
+} {abc: 10 10 (-99.9999900000) :xyz}
+do_test printf-2.3.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 -99.99999
+} {abc: 10 10 (-9.9999990000e+01) :xyz}
+do_test printf-2.3.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 -99.99999
+} {abc: 10 10 ( -99.99999) :xyz}
+do_test printf-2.3.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 3.14e+9
+} {abc: (3140000000.0000000000) :xyz}
+do_test printf-2.3.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 3.14e+9
+} {abc: (3.1400000000e+09) :xyz}
+do_test printf-2.3.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 3.14e+9
+} {abc: (3140000000) :xyz}
+do_test printf-2.3.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (3.14e+09) :xyz}
+do_test printf-2.3.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (3.14000e+09) :xyz}
+do_test printf-2.3.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (003.14e+09) :xyz}
+do_test printf-2.3.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (3140000000.0000000000) :xyz}
+do_test printf-2.3.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (3.1400000000e+09) :xyz}
+do_test printf-2.3.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 3.14e+9
+} {abc: 10 10 (3140000000) :xyz}
+do_test printf-2.3.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 -4.72732e+88
+} {abc: (-4.7273200000e+88) :xyz}
+do_test printf-2.3.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 -4.72732e+88
+} {abc: (-4.72732e+88) :xyz}
+do_test printf-2.3.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 -4.72732e+88
+} {abc: 10 10 (-4.72732e+88) :xyz}
+do_test printf-2.3.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 -4.72732e+88
+} {abc: 10 10 (-4.72732e+88) :xyz}
+do_test printf-2.3.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 -4.72732e+88
+} {abc: 10 10 (-4.72732e+88) :xyz}
+do_test printf-2.3.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 -4.72732e+88
+} {abc: 10 10 (-4.7273200000e+88) :xyz}
+do_test printf-2.3.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 -4.72732e+88
+} {abc: 10 10 (-4.72732e+88) :xyz}
+do_test printf-2.3.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 9.87991e+143
+} {abc: (9.8799100000e+143) :xyz}
+do_test printf-2.3.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 9.87991e+143
+} {abc: (9.87991e+143) :xyz}
+do_test printf-2.3.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 9.87991e+143
+} {abc: 10 10 (9.87991e+143) :xyz}
+do_test printf-2.3.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 9.87991e+143
+} {abc: 10 10 (9.87991e+143) :xyz}
+do_test printf-2.3.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 9.87991e+143
+} {abc: 10 10 (9.87991e+143) :xyz}
+do_test printf-2.3.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 9.87991e+143
+} {abc: 10 10 (9.8799100000e+143) :xyz}
+do_test printf-2.3.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 9.87991e+143
+} {abc: 10 10 (9.87991e+143) :xyz}
+do_test printf-2.3.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 -6.287291e-9
+} {abc: (-0.0000000063) :xyz}
+do_test printf-2.3.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 -6.287291e-9
+} {abc: (-6.2872910000e-09) :xyz}
+do_test printf-2.3.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 -6.287291e-9
+} {abc: (-6.287291e-09) :xyz}
+do_test printf-2.3.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-6.28729e-09) :xyz}
+do_test printf-2.3.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-6.28729e-09) :xyz}
+do_test printf-2.3.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-6.28729e-09) :xyz}
+do_test printf-2.3.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-0.0000000063) :xyz}
+do_test printf-2.3.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-6.2872910000e-09) :xyz}
+do_test printf-2.3.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 -6.287291e-9
+} {abc: 10 10 (-6.287291e-09) :xyz}
+do_test printf-2.3.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 10 3.38826392e-110
+} {abc: (0.0000000000) :xyz}
+do_test printf-2.3.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 10 3.38826392e-110
+} {abc: (3.3882639200e-110) :xyz}
+do_test printf-2.3.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 10 3.38826392e-110
+} {abc: (3.38826392e-110) :xyz}
+do_test printf-2.3.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (3.38826e-110) :xyz}
+do_test printf-2.3.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (3.38826e-110) :xyz}
+do_test printf-2.3.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (3.38826e-110) :xyz}
+do_test printf-2.3.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10f) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (0.0000000000) :xyz}
+do_test printf-2.3.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10e) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (3.3882639200e-110) :xyz}
+do_test printf-2.3.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.10g) :xyz} 10 10 3.38826392e-110
+} {abc: 10 10 (3.38826392e-110) :xyz}
+do_test printf-2.4.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 0.001
+} {abc: ( 0.00100) :xyz}
+do_test printf-2.4.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 0.001
+} {abc: (1.00000e-03) :xyz}
+do_test printf-2.4.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 0.001
+} {abc: ( 0.001) :xyz}
+do_test printf-2.4.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 0.001
+} {abc: 10 5 (0.001) :xyz}
+do_test printf-2.4.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 0.001
+} {abc: 10 5 (0.00100000) :xyz}
+do_test printf-2.4.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 0.001
+} {abc: 10 5 (000000.001) :xyz}
+do_test printf-2.4.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 0.001
+} {abc: 10 5 ( 0.00100) :xyz}
+do_test printf-2.4.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 0.001
+} {abc: 10 5 (1.00000e-03) :xyz}
+do_test printf-2.4.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 0.001
+} {abc: 10 5 ( 0.001) :xyz}
+do_test printf-2.4.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 1.0e-20
+} {abc: ( 0.00000) :xyz}
+do_test printf-2.4.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 1.0e-20
+} {abc: (1.00000e-20) :xyz}
+do_test printf-2.4.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 1.0e-20
+} {abc: ( 1e-20) :xyz}
+do_test printf-2.4.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 1.0e-20
+} {abc: 10 5 (1e-20) :xyz}
+do_test printf-2.4.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 1.0e-20
+} {abc: 10 5 (1.00000e-20) :xyz}
+do_test printf-2.4.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 1.0e-20
+} {abc: 10 5 (000001e-20) :xyz}
+do_test printf-2.4.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 1.0e-20
+} {abc: 10 5 ( 0.00000) :xyz}
+do_test printf-2.4.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 1.0e-20
+} {abc: 10 5 (1.00000e-20) :xyz}
+do_test printf-2.4.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 1.0e-20
+} {abc: 10 5 ( 1e-20) :xyz}
+do_test printf-2.4.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 1.0
+} {abc: ( 1.00000) :xyz}
+do_test printf-2.4.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 1.0
+} {abc: (1.00000e+00) :xyz}
+do_test printf-2.4.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.4.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 1.0
+} {abc: 10 5 (1) :xyz}
+do_test printf-2.4.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 1.0
+} {abc: 10 5 (1.00000) :xyz}
+do_test printf-2.4.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 1.0
+} {abc: 10 5 (0000000001) :xyz}
+do_test printf-2.4.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 1.0
+} {abc: 10 5 ( 1.00000) :xyz}
+do_test printf-2.4.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 1.0
+} {abc: 10 5 (1.00000e+00) :xyz}
+do_test printf-2.4.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 1.0
+} {abc: 10 5 ( 1) :xyz}
+do_test printf-2.4.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 0.0
+} {abc: ( 0.00000) :xyz}
+do_test printf-2.4.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 0.0
+} {abc: (0.00000e+00) :xyz}
+do_test printf-2.4.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.4.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 0.0
+} {abc: 10 5 (0) :xyz}
+do_test printf-2.4.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 0.0
+} {abc: 10 5 (0.00000) :xyz}
+do_test printf-2.4.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 0.0
+} {abc: 10 5 (0000000000) :xyz}
+do_test printf-2.4.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 0.0
+} {abc: 10 5 ( 0.00000) :xyz}
+do_test printf-2.4.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 0.0
+} {abc: 10 5 (0.00000e+00) :xyz}
+do_test printf-2.4.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 0.0
+} {abc: 10 5 ( 0) :xyz}
+do_test printf-2.4.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 100.0
+} {abc: ( 100.00000) :xyz}
+do_test printf-2.4.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 100.0
+} {abc: (1.00000e+02) :xyz}
+do_test printf-2.4.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 100.0
+} {abc: ( 100) :xyz}
+do_test printf-2.4.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 100.0
+} {abc: 10 5 (100) :xyz}
+do_test printf-2.4.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 100.0
+} {abc: 10 5 (100.000) :xyz}
+do_test printf-2.4.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 100.0
+} {abc: 10 5 (0000000100) :xyz}
+do_test printf-2.4.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 100.0
+} {abc: 10 5 ( 100.00000) :xyz}
+do_test printf-2.4.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 100.0
+} {abc: 10 5 (1.00000e+02) :xyz}
+do_test printf-2.4.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 100.0
+} {abc: 10 5 ( 100) :xyz}
+do_test printf-2.4.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 9.99999
+} {abc: ( 9.99999) :xyz}
+do_test printf-2.4.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 9.99999
+} {abc: (9.99999e+00) :xyz}
+do_test printf-2.4.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 9.99999
+} {abc: ( 10) :xyz}
+do_test printf-2.4.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 9.99999
+} {abc: 10 5 (9.99999) :xyz}
+do_test printf-2.4.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 9.99999
+} {abc: 10 5 (9.99999) :xyz}
+do_test printf-2.4.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 9.99999
+} {abc: 10 5 (0009.99999) :xyz}
+do_test printf-2.4.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 9.99999
+} {abc: 10 5 ( 9.99999) :xyz}
+do_test printf-2.4.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 9.99999
+} {abc: 10 5 (9.99999e+00) :xyz}
+do_test printf-2.4.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 9.99999
+} {abc: 10 5 ( 10) :xyz}
+do_test printf-2.4.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 -0.00543
+} {abc: ( -0.00543) :xyz}
+do_test printf-2.4.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 -0.00543
+} {abc: (-5.43000e-03) :xyz}
+do_test printf-2.4.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 -0.00543
+} {abc: ( -0.00543) :xyz}
+do_test printf-2.4.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 -0.00543
+} {abc: 10 5 (-0.00543) :xyz}
+do_test printf-2.4.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 -0.00543
+} {abc: 10 5 (-0.00543000) :xyz}
+do_test printf-2.4.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 -0.00543
+} {abc: 10 5 (-000.00543) :xyz}
+do_test printf-2.4.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 -0.00543
+} {abc: 10 5 ( -0.00543) :xyz}
+do_test printf-2.4.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 -0.00543
+} {abc: 10 5 (-5.43000e-03) :xyz}
+do_test printf-2.4.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 -0.00543
+} {abc: 10 5 ( -0.00543) :xyz}
+do_test printf-2.4.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 -1.0
+} {abc: ( -1.00000) :xyz}
+do_test printf-2.4.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 -1.0
+} {abc: (-1.00000e+00) :xyz}
+do_test printf-2.4.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 -1.0
+} {abc: ( -1) :xyz}
+do_test printf-2.4.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 -1.0
+} {abc: 10 5 (-1) :xyz}
+do_test printf-2.4.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 -1.0
+} {abc: 10 5 (-1.00000) :xyz}
+do_test printf-2.4.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 -1.0
+} {abc: 10 5 (-000000001) :xyz}
+do_test printf-2.4.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 -1.0
+} {abc: 10 5 ( -1.00000) :xyz}
+do_test printf-2.4.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 -1.0
+} {abc: 10 5 (-1.00000e+00) :xyz}
+do_test printf-2.4.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 -1.0
+} {abc: 10 5 ( -1) :xyz}
+do_test printf-2.4.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 -99.99999
+} {abc: ( -99.99999) :xyz}
+do_test printf-2.4.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 -99.99999
+} {abc: (-1.00000e+02) :xyz}
+do_test printf-2.4.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 -99.99999
+} {abc: ( -100) :xyz}
+do_test printf-2.4.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 -99.99999
+} {abc: 10 5 (-100) :xyz}
+do_test printf-2.4.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 -99.99999
+} {abc: 10 5 (-100.000) :xyz}
+do_test printf-2.4.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 -99.99999
+} {abc: 10 5 (-000000100) :xyz}
+do_test printf-2.4.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 -99.99999
+} {abc: 10 5 ( -99.99999) :xyz}
+do_test printf-2.4.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 -99.99999
+} {abc: 10 5 (-1.00000e+02) :xyz}
+do_test printf-2.4.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 -99.99999
+} {abc: 10 5 ( -100) :xyz}
+do_test printf-2.4.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 3.14e+9
+} {abc: (3140000000.00000) :xyz}
+do_test printf-2.4.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 3.14e+9
+} {abc: (3.14000e+09) :xyz}
+do_test printf-2.4.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 3.14e+9
+} {abc: ( 3.14e+09) :xyz}
+do_test printf-2.4.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 3.14e+9
+} {abc: 10 5 (3.14e+09) :xyz}
+do_test printf-2.4.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 3.14e+9
+} {abc: 10 5 (3.14000e+09) :xyz}
+do_test printf-2.4.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 3.14e+9
+} {abc: 10 5 (003.14e+09) :xyz}
+do_test printf-2.4.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 3.14e+9
+} {abc: 10 5 (3140000000.00000) :xyz}
+do_test printf-2.4.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 3.14e+9
+} {abc: 10 5 (3.14000e+09) :xyz}
+do_test printf-2.4.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 3.14e+9
+} {abc: 10 5 ( 3.14e+09) :xyz}
+do_test printf-2.4.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 -4.72732e+88
+} {abc: (-4.72732e+88) :xyz}
+do_test printf-2.4.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 -4.72732e+88
+} {abc: (-4.7273e+88) :xyz}
+do_test printf-2.4.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 -4.72732e+88
+} {abc: 10 5 (-4.72732e+88) :xyz}
+do_test printf-2.4.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 -4.72732e+88
+} {abc: 10 5 (-4.72732e+88) :xyz}
+do_test printf-2.4.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 -4.72732e+88
+} {abc: 10 5 (-4.72732e+88) :xyz}
+do_test printf-2.4.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 -4.72732e+88
+} {abc: 10 5 (-4.72732e+88) :xyz}
+do_test printf-2.4.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 -4.72732e+88
+} {abc: 10 5 (-4.7273e+88) :xyz}
+do_test printf-2.4.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 9.87991e+143
+} {abc: (9.87991e+143) :xyz}
+do_test printf-2.4.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 9.87991e+143
+} {abc: (9.8799e+143) :xyz}
+do_test printf-2.4.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 9.87991e+143
+} {abc: 10 5 (9.87991e+143) :xyz}
+do_test printf-2.4.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 9.87991e+143
+} {abc: 10 5 (9.87991e+143) :xyz}
+do_test printf-2.4.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 9.87991e+143
+} {abc: 10 5 (9.87991e+143) :xyz}
+do_test printf-2.4.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 9.87991e+143
+} {abc: 10 5 (9.87991e+143) :xyz}
+do_test printf-2.4.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 9.87991e+143
+} {abc: 10 5 (9.8799e+143) :xyz}
+do_test printf-2.4.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 -6.287291e-9
+} {abc: ( -0.00000) :xyz}
+do_test printf-2.4.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 -6.287291e-9
+} {abc: (-6.28729e-09) :xyz}
+do_test printf-2.4.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 -6.287291e-9
+} {abc: (-6.2873e-09) :xyz}
+do_test printf-2.4.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 (-6.28729e-09) :xyz}
+do_test printf-2.4.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 (-6.28729e-09) :xyz}
+do_test printf-2.4.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 (-6.28729e-09) :xyz}
+do_test printf-2.4.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 ( -0.00000) :xyz}
+do_test printf-2.4.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 (-6.28729e-09) :xyz}
+do_test printf-2.4.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 -6.287291e-9
+} {abc: 10 5 (-6.2873e-09) :xyz}
+do_test printf-2.4.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 10 5 3.38826392e-110
+} {abc: ( 0.00000) :xyz}
+do_test printf-2.4.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 10 5 3.38826392e-110
+} {abc: (3.38826e-110) :xyz}
+do_test printf-2.4.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 10 5 3.38826392e-110
+} {abc: (3.3883e-110) :xyz}
+do_test printf-2.4.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 (3.38826e-110) :xyz}
+do_test printf-2.4.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 (3.38826e-110) :xyz}
+do_test printf-2.4.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 (3.38826e-110) :xyz}
+do_test printf-2.4.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5f) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 ( 0.00000) :xyz}
+do_test printf-2.4.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5e) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 (3.38826e-110) :xyz}
+do_test printf-2.4.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%10.5g) :xyz} 10 5 3.38826392e-110
+} {abc: 10 5 (3.3883e-110) :xyz}
+do_test printf-2.5.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 0.001
+} {abc: (0.00) :xyz}
+do_test printf-2.5.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 0.001
+} {abc: (1.00e-03) :xyz}
+do_test printf-2.5.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.5.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 0.001
+} {abc: 2 2 (0.001) :xyz}
+do_test printf-2.5.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 0.001
+} {abc: 2 2 (0.00100000) :xyz}
+do_test printf-2.5.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 0.001
+} {abc: 2 2 (000000.001) :xyz}
+do_test printf-2.5.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 0.001
+} {abc: 2 2 (0.00) :xyz}
+do_test printf-2.5.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 0.001
+} {abc: 2 2 (1.00e-03) :xyz}
+do_test printf-2.5.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 0.001
+} {abc: 2 2 (0.001) :xyz}
+do_test printf-2.5.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 1.0e-20
+} {abc: (0.00) :xyz}
+do_test printf-2.5.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 1.0e-20
+} {abc: (1.00e-20) :xyz}
+do_test printf-2.5.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.5.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (1e-20) :xyz}
+do_test printf-2.5.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (1.00000e-20) :xyz}
+do_test printf-2.5.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (000001e-20) :xyz}
+do_test printf-2.5.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (0.00) :xyz}
+do_test printf-2.5.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (1.00e-20) :xyz}
+do_test printf-2.5.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 1.0e-20
+} {abc: 2 2 (1e-20) :xyz}
+do_test printf-2.5.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 1.0
+} {abc: (1.00) :xyz}
+do_test printf-2.5.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 1.0
+} {abc: (1.00e+00) :xyz}
+do_test printf-2.5.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.5.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 1.0
+} {abc: 2 2 (1) :xyz}
+do_test printf-2.5.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 1.0
+} {abc: 2 2 (1.00000) :xyz}
+do_test printf-2.5.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 1.0
+} {abc: 2 2 (0000000001) :xyz}
+do_test printf-2.5.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 1.0
+} {abc: 2 2 (1.00) :xyz}
+do_test printf-2.5.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 1.0
+} {abc: 2 2 (1.00e+00) :xyz}
+do_test printf-2.5.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 1.0
+} {abc: 2 2 ( 1) :xyz}
+do_test printf-2.5.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 0.0
+} {abc: (0.00) :xyz}
+do_test printf-2.5.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 0.0
+} {abc: (0.00e+00) :xyz}
+do_test printf-2.5.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.5.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 0.0
+} {abc: 2 2 (0) :xyz}
+do_test printf-2.5.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 0.0
+} {abc: 2 2 (0.00000) :xyz}
+do_test printf-2.5.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 0.0
+} {abc: 2 2 (0000000000) :xyz}
+do_test printf-2.5.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 0.0
+} {abc: 2 2 (0.00) :xyz}
+do_test printf-2.5.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 0.0
+} {abc: 2 2 (0.00e+00) :xyz}
+do_test printf-2.5.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 0.0
+} {abc: 2 2 ( 0) :xyz}
+do_test printf-2.5.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 100.0
+} {abc: (100.00) :xyz}
+do_test printf-2.5.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 100.0
+} {abc: (1.00e+02) :xyz}
+do_test printf-2.5.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 100.0
+} {abc: (1e+02) :xyz}
+do_test printf-2.5.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 100.0
+} {abc: 2 2 (100) :xyz}
+do_test printf-2.5.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 100.0
+} {abc: 2 2 (100.000) :xyz}
+do_test printf-2.5.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 100.0
+} {abc: 2 2 (0000000100) :xyz}
+do_test printf-2.5.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 100.0
+} {abc: 2 2 (100.00) :xyz}
+do_test printf-2.5.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 100.0
+} {abc: 2 2 (1.00e+02) :xyz}
+do_test printf-2.5.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 100.0
+} {abc: 2 2 (1e+02) :xyz}
+do_test printf-2.5.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 9.99999
+} {abc: (10.00) :xyz}
+do_test printf-2.5.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 9.99999
+} {abc: (1.00e+01) :xyz}
+do_test printf-2.5.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 9.99999
+} {abc: (10) :xyz}
+do_test printf-2.5.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 9.99999
+} {abc: 2 2 (9.99999) :xyz}
+do_test printf-2.5.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 9.99999
+} {abc: 2 2 (9.99999) :xyz}
+do_test printf-2.5.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 9.99999
+} {abc: 2 2 (0009.99999) :xyz}
+do_test printf-2.5.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 9.99999
+} {abc: 2 2 (10.00) :xyz}
+do_test printf-2.5.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 9.99999
+} {abc: 2 2 (1.00e+01) :xyz}
+do_test printf-2.5.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 9.99999
+} {abc: 2 2 (10) :xyz}
+do_test printf-2.5.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 -0.00543
+} {abc: (-0.01) :xyz}
+do_test printf-2.5.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 -0.00543
+} {abc: (-5.43e-03) :xyz}
+do_test printf-2.5.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 -0.00543
+} {abc: (-0.0054) :xyz}
+do_test printf-2.5.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-0.00543) :xyz}
+do_test printf-2.5.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-0.00543000) :xyz}
+do_test printf-2.5.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-000.00543) :xyz}
+do_test printf-2.5.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-0.01) :xyz}
+do_test printf-2.5.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-5.43e-03) :xyz}
+do_test printf-2.5.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 -0.00543
+} {abc: 2 2 (-0.0054) :xyz}
+do_test printf-2.5.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 -1.0
+} {abc: (-1.00) :xyz}
+do_test printf-2.5.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 -1.0
+} {abc: (-1.00e+00) :xyz}
+do_test printf-2.5.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 -1.0
+} {abc: (-1) :xyz}
+do_test printf-2.5.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 -1.0
+} {abc: 2 2 (-1) :xyz}
+do_test printf-2.5.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 -1.0
+} {abc: 2 2 (-1.00000) :xyz}
+do_test printf-2.5.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 -1.0
+} {abc: 2 2 (-000000001) :xyz}
+do_test printf-2.5.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 -1.0
+} {abc: 2 2 (-1.00) :xyz}
+do_test printf-2.5.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 -1.0
+} {abc: 2 2 (-1.00e+00) :xyz}
+do_test printf-2.5.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 -1.0
+} {abc: 2 2 (-1) :xyz}
+do_test printf-2.5.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 -99.99999
+} {abc: (-100.00) :xyz}
+do_test printf-2.5.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 -99.99999
+} {abc: (-1.00e+02) :xyz}
+do_test printf-2.5.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 -99.99999
+} {abc: (-1e+02) :xyz}
+do_test printf-2.5.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-100) :xyz}
+do_test printf-2.5.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-100.000) :xyz}
+do_test printf-2.5.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-000000100) :xyz}
+do_test printf-2.5.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-100.00) :xyz}
+do_test printf-2.5.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-1.00e+02) :xyz}
+do_test printf-2.5.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 -99.99999
+} {abc: 2 2 (-1e+02) :xyz}
+do_test printf-2.5.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 3.14e+9
+} {abc: (3140000000.00) :xyz}
+do_test printf-2.5.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 3.14e+9
+} {abc: (3.14e+09) :xyz}
+do_test printf-2.5.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 3.14e+9
+} {abc: (3.1e+09) :xyz}
+do_test printf-2.5.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (3.14e+09) :xyz}
+do_test printf-2.5.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (3.14000e+09) :xyz}
+do_test printf-2.5.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (003.14e+09) :xyz}
+do_test printf-2.5.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (3140000000.00) :xyz}
+do_test printf-2.5.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (3.14e+09) :xyz}
+do_test printf-2.5.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 3.14e+9
+} {abc: 2 2 (3.1e+09) :xyz}
+do_test printf-2.5.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 -4.72732e+88
+} {abc: (-4.73e+88) :xyz}
+do_test printf-2.5.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 -4.72732e+88
+} {abc: (-4.7e+88) :xyz}
+do_test printf-2.5.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 -4.72732e+88
+} {abc: 2 2 (-4.72732e+88) :xyz}
+do_test printf-2.5.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 -4.72732e+88
+} {abc: 2 2 (-4.72732e+88) :xyz}
+do_test printf-2.5.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 -4.72732e+88
+} {abc: 2 2 (-4.72732e+88) :xyz}
+do_test printf-2.5.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 -4.72732e+88
+} {abc: 2 2 (-4.73e+88) :xyz}
+do_test printf-2.5.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 -4.72732e+88
+} {abc: 2 2 (-4.7e+88) :xyz}
+do_test printf-2.5.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 9.87991e+143
+} {abc: (9.88e+143) :xyz}
+do_test printf-2.5.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 9.87991e+143
+} {abc: (9.9e+143) :xyz}
+do_test printf-2.5.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 9.87991e+143
+} {abc: 2 2 (9.87991e+143) :xyz}
+do_test printf-2.5.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 9.87991e+143
+} {abc: 2 2 (9.87991e+143) :xyz}
+do_test printf-2.5.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 9.87991e+143
+} {abc: 2 2 (9.87991e+143) :xyz}
+do_test printf-2.5.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 9.87991e+143
+} {abc: 2 2 (9.88e+143) :xyz}
+do_test printf-2.5.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 9.87991e+143
+} {abc: 2 2 (9.9e+143) :xyz}
+do_test printf-2.5.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 -6.287291e-9
+} {abc: (-0.00) :xyz}
+do_test printf-2.5.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 -6.287291e-9
+} {abc: (-6.29e-09) :xyz}
+do_test printf-2.5.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 -6.287291e-9
+} {abc: (-6.3e-09) :xyz}
+do_test printf-2.5.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-6.28729e-09) :xyz}
+do_test printf-2.5.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-6.28729e-09) :xyz}
+do_test printf-2.5.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-6.28729e-09) :xyz}
+do_test printf-2.5.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-0.00) :xyz}
+do_test printf-2.5.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-6.29e-09) :xyz}
+do_test printf-2.5.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 -6.287291e-9
+} {abc: 2 2 (-6.3e-09) :xyz}
+do_test printf-2.5.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 2 3.38826392e-110
+} {abc: (0.00) :xyz}
+do_test printf-2.5.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 2 3.38826392e-110
+} {abc: (3.39e-110) :xyz}
+do_test printf-2.5.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 2 3.38826392e-110
+} {abc: (3.4e-110) :xyz}
+do_test printf-2.5.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (3.38826e-110) :xyz}
+do_test printf-2.5.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (3.38826e-110) :xyz}
+do_test printf-2.5.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (3.38826e-110) :xyz}
+do_test printf-2.5.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2f) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (0.00) :xyz}
+do_test printf-2.5.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2e) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (3.39e-110) :xyz}
+do_test printf-2.5.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.2g) :xyz} 2 2 3.38826392e-110
+} {abc: 2 2 (3.4e-110) :xyz}
+do_test printf-2.6.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.6.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 0.001
+} {abc: (1.000e-03) :xyz}
+do_test printf-2.6.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.6.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 0.001
+} {abc: 2 3 (0.001) :xyz}
+do_test printf-2.6.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 0.001
+} {abc: 2 3 (0.00100000) :xyz}
+do_test printf-2.6.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 0.001
+} {abc: 2 3 (000000.001) :xyz}
+do_test printf-2.6.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 0.001
+} {abc: 2 3 (0.001) :xyz}
+do_test printf-2.6.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 0.001
+} {abc: 2 3 (1.000e-03) :xyz}
+do_test printf-2.6.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 0.001
+} {abc: 2 3 (0.001) :xyz}
+do_test printf-2.6.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 1.0e-20
+} {abc: (0.000) :xyz}
+do_test printf-2.6.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 1.0e-20
+} {abc: (1.000e-20) :xyz}
+do_test printf-2.6.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.6.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (1e-20) :xyz}
+do_test printf-2.6.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (1.00000e-20) :xyz}
+do_test printf-2.6.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (000001e-20) :xyz}
+do_test printf-2.6.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (0.000) :xyz}
+do_test printf-2.6.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (1.000e-20) :xyz}
+do_test printf-2.6.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 1.0e-20
+} {abc: 2 3 (1e-20) :xyz}
+do_test printf-2.6.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 1.0
+} {abc: (1.000) :xyz}
+do_test printf-2.6.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 1.0
+} {abc: (1.000e+00) :xyz}
+do_test printf-2.6.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.6.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 1.0
+} {abc: 2 3 (1) :xyz}
+do_test printf-2.6.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 1.0
+} {abc: 2 3 (1.00000) :xyz}
+do_test printf-2.6.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 1.0
+} {abc: 2 3 (0000000001) :xyz}
+do_test printf-2.6.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 1.0
+} {abc: 2 3 (1.000) :xyz}
+do_test printf-2.6.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 1.0
+} {abc: 2 3 (1.000e+00) :xyz}
+do_test printf-2.6.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 1.0
+} {abc: 2 3 ( 1) :xyz}
+do_test printf-2.6.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 0.0
+} {abc: (0.000) :xyz}
+do_test printf-2.6.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 0.0
+} {abc: (0.000e+00) :xyz}
+do_test printf-2.6.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.6.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 0.0
+} {abc: 2 3 (0) :xyz}
+do_test printf-2.6.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 0.0
+} {abc: 2 3 (0.00000) :xyz}
+do_test printf-2.6.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 0.0
+} {abc: 2 3 (0000000000) :xyz}
+do_test printf-2.6.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 0.0
+} {abc: 2 3 (0.000) :xyz}
+do_test printf-2.6.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 0.0
+} {abc: 2 3 (0.000e+00) :xyz}
+do_test printf-2.6.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 0.0
+} {abc: 2 3 ( 0) :xyz}
+do_test printf-2.6.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 100.0
+} {abc: (100.000) :xyz}
+do_test printf-2.6.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 100.0
+} {abc: (1.000e+02) :xyz}
+do_test printf-2.6.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 100.0
+} {abc: (100) :xyz}
+do_test printf-2.6.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 100.0
+} {abc: 2 3 (100) :xyz}
+do_test printf-2.6.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 100.0
+} {abc: 2 3 (100.000) :xyz}
+do_test printf-2.6.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 100.0
+} {abc: 2 3 (0000000100) :xyz}
+do_test printf-2.6.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 100.0
+} {abc: 2 3 (100.000) :xyz}
+do_test printf-2.6.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 100.0
+} {abc: 2 3 (1.000e+02) :xyz}
+do_test printf-2.6.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 100.0
+} {abc: 2 3 (100) :xyz}
+do_test printf-2.6.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 9.99999
+} {abc: (10.000) :xyz}
+do_test printf-2.6.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 9.99999
+} {abc: (1.000e+01) :xyz}
+do_test printf-2.6.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 9.99999
+} {abc: (10) :xyz}
+do_test printf-2.6.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 9.99999
+} {abc: 2 3 (9.99999) :xyz}
+do_test printf-2.6.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 9.99999
+} {abc: 2 3 (9.99999) :xyz}
+do_test printf-2.6.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 9.99999
+} {abc: 2 3 (0009.99999) :xyz}
+do_test printf-2.6.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 9.99999
+} {abc: 2 3 (10.000) :xyz}
+do_test printf-2.6.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 9.99999
+} {abc: 2 3 (1.000e+01) :xyz}
+do_test printf-2.6.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 9.99999
+} {abc: 2 3 (10) :xyz}
+do_test printf-2.6.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 -0.00543
+} {abc: (-0.005) :xyz}
+do_test printf-2.6.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 -0.00543
+} {abc: (-5.430e-03) :xyz}
+do_test printf-2.6.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 -0.00543
+} {abc: (-0.00543) :xyz}
+do_test printf-2.6.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-0.00543) :xyz}
+do_test printf-2.6.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-0.00543000) :xyz}
+do_test printf-2.6.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-000.00543) :xyz}
+do_test printf-2.6.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-0.005) :xyz}
+do_test printf-2.6.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-5.430e-03) :xyz}
+do_test printf-2.6.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 -0.00543
+} {abc: 2 3 (-0.00543) :xyz}
+do_test printf-2.6.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 -1.0
+} {abc: (-1.000) :xyz}
+do_test printf-2.6.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 -1.0
+} {abc: (-1.000e+00) :xyz}
+do_test printf-2.6.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 -1.0
+} {abc: (-1) :xyz}
+do_test printf-2.6.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 -1.0
+} {abc: 2 3 (-1) :xyz}
+do_test printf-2.6.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 -1.0
+} {abc: 2 3 (-1.00000) :xyz}
+do_test printf-2.6.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 -1.0
+} {abc: 2 3 (-000000001) :xyz}
+do_test printf-2.6.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 -1.0
+} {abc: 2 3 (-1.000) :xyz}
+do_test printf-2.6.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 -1.0
+} {abc: 2 3 (-1.000e+00) :xyz}
+do_test printf-2.6.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 -1.0
+} {abc: 2 3 (-1) :xyz}
+do_test printf-2.6.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 -99.99999
+} {abc: (-100.000) :xyz}
+do_test printf-2.6.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 -99.99999
+} {abc: (-1.000e+02) :xyz}
+do_test printf-2.6.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 -99.99999
+} {abc: (-100) :xyz}
+do_test printf-2.6.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-100) :xyz}
+do_test printf-2.6.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-100.000) :xyz}
+do_test printf-2.6.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-000000100) :xyz}
+do_test printf-2.6.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-100.000) :xyz}
+do_test printf-2.6.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-1.000e+02) :xyz}
+do_test printf-2.6.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 -99.99999
+} {abc: 2 3 (-100) :xyz}
+do_test printf-2.6.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 3.14e+9
+} {abc: (3140000000.000) :xyz}
+do_test printf-2.6.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 3.14e+9
+} {abc: (3.140e+09) :xyz}
+do_test printf-2.6.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 3.14e+9
+} {abc: (3.14e+09) :xyz}
+do_test printf-2.6.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (3.14e+09) :xyz}
+do_test printf-2.6.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (3.14000e+09) :xyz}
+do_test printf-2.6.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (003.14e+09) :xyz}
+do_test printf-2.6.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (3140000000.000) :xyz}
+do_test printf-2.6.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (3.140e+09) :xyz}
+do_test printf-2.6.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 3.14e+9
+} {abc: 2 3 (3.14e+09) :xyz}
+do_test printf-2.6.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 -4.72732e+88
+} {abc: (-4.727e+88) :xyz}
+do_test printf-2.6.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 -4.72732e+88
+} {abc: (-4.73e+88) :xyz}
+do_test printf-2.6.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 -4.72732e+88
+} {abc: 2 3 (-4.72732e+88) :xyz}
+do_test printf-2.6.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 -4.72732e+88
+} {abc: 2 3 (-4.72732e+88) :xyz}
+do_test printf-2.6.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 -4.72732e+88
+} {abc: 2 3 (-4.72732e+88) :xyz}
+do_test printf-2.6.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 -4.72732e+88
+} {abc: 2 3 (-4.727e+88) :xyz}
+do_test printf-2.6.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 -4.72732e+88
+} {abc: 2 3 (-4.73e+88) :xyz}
+do_test printf-2.6.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 9.87991e+143
+} {abc: (9.880e+143) :xyz}
+do_test printf-2.6.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 9.87991e+143
+} {abc: (9.88e+143) :xyz}
+do_test printf-2.6.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 9.87991e+143
+} {abc: 2 3 (9.87991e+143) :xyz}
+do_test printf-2.6.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 9.87991e+143
+} {abc: 2 3 (9.87991e+143) :xyz}
+do_test printf-2.6.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 9.87991e+143
+} {abc: 2 3 (9.87991e+143) :xyz}
+do_test printf-2.6.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 9.87991e+143
+} {abc: 2 3 (9.880e+143) :xyz}
+do_test printf-2.6.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 9.87991e+143
+} {abc: 2 3 (9.88e+143) :xyz}
+do_test printf-2.6.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 -6.287291e-9
+} {abc: (-0.000) :xyz}
+do_test printf-2.6.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 -6.287291e-9
+} {abc: (-6.287e-09) :xyz}
+do_test printf-2.6.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 -6.287291e-9
+} {abc: (-6.29e-09) :xyz}
+do_test printf-2.6.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-6.28729e-09) :xyz}
+do_test printf-2.6.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-6.28729e-09) :xyz}
+do_test printf-2.6.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-6.28729e-09) :xyz}
+do_test printf-2.6.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-0.000) :xyz}
+do_test printf-2.6.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-6.287e-09) :xyz}
+do_test printf-2.6.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 -6.287291e-9
+} {abc: 2 3 (-6.29e-09) :xyz}
+do_test printf-2.6.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 2 3 3.38826392e-110
+} {abc: (0.000) :xyz}
+do_test printf-2.6.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 2 3 3.38826392e-110
+} {abc: (3.388e-110) :xyz}
+do_test printf-2.6.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 2 3 3.38826392e-110
+} {abc: (3.39e-110) :xyz}
+do_test printf-2.6.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (3.38826e-110) :xyz}
+do_test printf-2.6.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (3.38826e-110) :xyz}
+do_test printf-2.6.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (3.38826e-110) :xyz}
+do_test printf-2.6.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3f) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (0.000) :xyz}
+do_test printf-2.6.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3e) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (3.388e-110) :xyz}
+do_test printf-2.6.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%2.3g) :xyz} 2 3 3.38826392e-110
+} {abc: 2 3 (3.39e-110) :xyz}
+do_test printf-2.7.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.7.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 0.001
+} {abc: (1.000e-03) :xyz}
+do_test printf-2.7.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.7.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 0.001
+} {abc: 3 3 (0.001) :xyz}
+do_test printf-2.7.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 0.001
+} {abc: 3 3 (0.00100000) :xyz}
+do_test printf-2.7.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 0.001
+} {abc: 3 3 (000000.001) :xyz}
+do_test printf-2.7.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 0.001
+} {abc: 3 3 (0.001) :xyz}
+do_test printf-2.7.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 0.001
+} {abc: 3 3 (1.000e-03) :xyz}
+do_test printf-2.7.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 0.001
+} {abc: 3 3 (0.001) :xyz}
+do_test printf-2.7.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 1.0e-20
+} {abc: (0.000) :xyz}
+do_test printf-2.7.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 1.0e-20
+} {abc: (1.000e-20) :xyz}
+do_test printf-2.7.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.7.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (1e-20) :xyz}
+do_test printf-2.7.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (1.00000e-20) :xyz}
+do_test printf-2.7.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (000001e-20) :xyz}
+do_test printf-2.7.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (0.000) :xyz}
+do_test printf-2.7.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (1.000e-20) :xyz}
+do_test printf-2.7.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 1.0e-20
+} {abc: 3 3 (1e-20) :xyz}
+do_test printf-2.7.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 1.0
+} {abc: (1.000) :xyz}
+do_test printf-2.7.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 1.0
+} {abc: (1.000e+00) :xyz}
+do_test printf-2.7.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.7.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 1.0
+} {abc: 3 3 (1) :xyz}
+do_test printf-2.7.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 1.0
+} {abc: 3 3 (1.00000) :xyz}
+do_test printf-2.7.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 1.0
+} {abc: 3 3 (0000000001) :xyz}
+do_test printf-2.7.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 1.0
+} {abc: 3 3 (1.000) :xyz}
+do_test printf-2.7.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 1.0
+} {abc: 3 3 (1.000e+00) :xyz}
+do_test printf-2.7.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 1.0
+} {abc: 3 3 ( 1) :xyz}
+do_test printf-2.7.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 0.0
+} {abc: (0.000) :xyz}
+do_test printf-2.7.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 0.0
+} {abc: (0.000e+00) :xyz}
+do_test printf-2.7.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.7.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 0.0
+} {abc: 3 3 (0) :xyz}
+do_test printf-2.7.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 0.0
+} {abc: 3 3 (0.00000) :xyz}
+do_test printf-2.7.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 0.0
+} {abc: 3 3 (0000000000) :xyz}
+do_test printf-2.7.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 0.0
+} {abc: 3 3 (0.000) :xyz}
+do_test printf-2.7.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 0.0
+} {abc: 3 3 (0.000e+00) :xyz}
+do_test printf-2.7.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 0.0
+} {abc: 3 3 ( 0) :xyz}
+do_test printf-2.7.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 100.0
+} {abc: (100.000) :xyz}
+do_test printf-2.7.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 100.0
+} {abc: (1.000e+02) :xyz}
+do_test printf-2.7.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 100.0
+} {abc: (100) :xyz}
+do_test printf-2.7.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 100.0
+} {abc: 3 3 (100) :xyz}
+do_test printf-2.7.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 100.0
+} {abc: 3 3 (100.000) :xyz}
+do_test printf-2.7.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 100.0
+} {abc: 3 3 (0000000100) :xyz}
+do_test printf-2.7.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 100.0
+} {abc: 3 3 (100.000) :xyz}
+do_test printf-2.7.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 100.0
+} {abc: 3 3 (1.000e+02) :xyz}
+do_test printf-2.7.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 100.0
+} {abc: 3 3 (100) :xyz}
+do_test printf-2.7.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 9.99999
+} {abc: (10.000) :xyz}
+do_test printf-2.7.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 9.99999
+} {abc: (1.000e+01) :xyz}
+do_test printf-2.7.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 9.99999
+} {abc: ( 10) :xyz}
+do_test printf-2.7.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 9.99999
+} {abc: 3 3 (9.99999) :xyz}
+do_test printf-2.7.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 9.99999
+} {abc: 3 3 (9.99999) :xyz}
+do_test printf-2.7.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 9.99999
+} {abc: 3 3 (0009.99999) :xyz}
+do_test printf-2.7.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 9.99999
+} {abc: 3 3 (10.000) :xyz}
+do_test printf-2.7.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 9.99999
+} {abc: 3 3 (1.000e+01) :xyz}
+do_test printf-2.7.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 9.99999
+} {abc: 3 3 ( 10) :xyz}
+do_test printf-2.7.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 -0.00543
+} {abc: (-0.005) :xyz}
+do_test printf-2.7.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 -0.00543
+} {abc: (-5.430e-03) :xyz}
+do_test printf-2.7.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 -0.00543
+} {abc: (-0.00543) :xyz}
+do_test printf-2.7.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-0.00543) :xyz}
+do_test printf-2.7.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-0.00543000) :xyz}
+do_test printf-2.7.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-000.00543) :xyz}
+do_test printf-2.7.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-0.005) :xyz}
+do_test printf-2.7.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-5.430e-03) :xyz}
+do_test printf-2.7.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 -0.00543
+} {abc: 3 3 (-0.00543) :xyz}
+do_test printf-2.7.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 -1.0
+} {abc: (-1.000) :xyz}
+do_test printf-2.7.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 -1.0
+} {abc: (-1.000e+00) :xyz}
+do_test printf-2.7.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 -1.0
+} {abc: ( -1) :xyz}
+do_test printf-2.7.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 -1.0
+} {abc: 3 3 (-1) :xyz}
+do_test printf-2.7.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 -1.0
+} {abc: 3 3 (-1.00000) :xyz}
+do_test printf-2.7.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 -1.0
+} {abc: 3 3 (-000000001) :xyz}
+do_test printf-2.7.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 -1.0
+} {abc: 3 3 (-1.000) :xyz}
+do_test printf-2.7.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 -1.0
+} {abc: 3 3 (-1.000e+00) :xyz}
+do_test printf-2.7.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 -1.0
+} {abc: 3 3 ( -1) :xyz}
+do_test printf-2.7.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 -99.99999
+} {abc: (-100.000) :xyz}
+do_test printf-2.7.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 -99.99999
+} {abc: (-1.000e+02) :xyz}
+do_test printf-2.7.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 -99.99999
+} {abc: (-100) :xyz}
+do_test printf-2.7.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-100) :xyz}
+do_test printf-2.7.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-100.000) :xyz}
+do_test printf-2.7.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-000000100) :xyz}
+do_test printf-2.7.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-100.000) :xyz}
+do_test printf-2.7.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-1.000e+02) :xyz}
+do_test printf-2.7.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 -99.99999
+} {abc: 3 3 (-100) :xyz}
+do_test printf-2.7.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 3.14e+9
+} {abc: (3140000000.000) :xyz}
+do_test printf-2.7.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 3.14e+9
+} {abc: (3.140e+09) :xyz}
+do_test printf-2.7.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 3.14e+9
+} {abc: (3.14e+09) :xyz}
+do_test printf-2.7.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (3.14e+09) :xyz}
+do_test printf-2.7.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (3.14000e+09) :xyz}
+do_test printf-2.7.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (003.14e+09) :xyz}
+do_test printf-2.7.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (3140000000.000) :xyz}
+do_test printf-2.7.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (3.140e+09) :xyz}
+do_test printf-2.7.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 3.14e+9
+} {abc: 3 3 (3.14e+09) :xyz}
+do_test printf-2.7.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 -4.72732e+88
+} {abc: (-4.727e+88) :xyz}
+do_test printf-2.7.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 -4.72732e+88
+} {abc: (-4.73e+88) :xyz}
+do_test printf-2.7.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 -4.72732e+88
+} {abc: 3 3 (-4.72732e+88) :xyz}
+do_test printf-2.7.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 -4.72732e+88
+} {abc: 3 3 (-4.72732e+88) :xyz}
+do_test printf-2.7.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 -4.72732e+88
+} {abc: 3 3 (-4.72732e+88) :xyz}
+do_test printf-2.7.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 -4.72732e+88
+} {abc: 3 3 (-4.727e+88) :xyz}
+do_test printf-2.7.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 -4.72732e+88
+} {abc: 3 3 (-4.73e+88) :xyz}
+do_test printf-2.7.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 9.87991e+143
+} {abc: (9.880e+143) :xyz}
+do_test printf-2.7.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 9.87991e+143
+} {abc: (9.88e+143) :xyz}
+do_test printf-2.7.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 9.87991e+143
+} {abc: 3 3 (9.87991e+143) :xyz}
+do_test printf-2.7.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 9.87991e+143
+} {abc: 3 3 (9.87991e+143) :xyz}
+do_test printf-2.7.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 9.87991e+143
+} {abc: 3 3 (9.87991e+143) :xyz}
+do_test printf-2.7.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 9.87991e+143
+} {abc: 3 3 (9.880e+143) :xyz}
+do_test printf-2.7.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 9.87991e+143
+} {abc: 3 3 (9.88e+143) :xyz}
+do_test printf-2.7.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 -6.287291e-9
+} {abc: (-0.000) :xyz}
+do_test printf-2.7.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 -6.287291e-9
+} {abc: (-6.287e-09) :xyz}
+do_test printf-2.7.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 -6.287291e-9
+} {abc: (-6.29e-09) :xyz}
+do_test printf-2.7.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-6.28729e-09) :xyz}
+do_test printf-2.7.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-6.28729e-09) :xyz}
+do_test printf-2.7.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-6.28729e-09) :xyz}
+do_test printf-2.7.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-0.000) :xyz}
+do_test printf-2.7.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-6.287e-09) :xyz}
+do_test printf-2.7.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 -6.287291e-9
+} {abc: 3 3 (-6.29e-09) :xyz}
+do_test printf-2.7.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 3 3.38826392e-110
+} {abc: (0.000) :xyz}
+do_test printf-2.7.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 3 3.38826392e-110
+} {abc: (3.388e-110) :xyz}
+do_test printf-2.7.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 3 3.38826392e-110
+} {abc: (3.39e-110) :xyz}
+do_test printf-2.7.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (3.38826e-110) :xyz}
+do_test printf-2.7.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (3.38826e-110) :xyz}
+do_test printf-2.7.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (3.38826e-110) :xyz}
+do_test printf-2.7.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3f) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (0.000) :xyz}
+do_test printf-2.7.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3e) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (3.388e-110) :xyz}
+do_test printf-2.7.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.3g) :xyz} 3 3 3.38826392e-110
+} {abc: 3 3 (3.39e-110) :xyz}
+do_test printf-2.8.1.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 0.001
+} {abc: (0.00) :xyz}
+do_test printf-2.8.1.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 0.001
+} {abc: (1.00e-03) :xyz}
+do_test printf-2.8.1.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 0.001
+} {abc: (0.001) :xyz}
+do_test printf-2.8.1.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 0.001
+} {abc: 3 2 (0.001) :xyz}
+do_test printf-2.8.1.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 0.001
+} {abc: 3 2 (0.00100000) :xyz}
+do_test printf-2.8.1.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 0.001
+} {abc: 3 2 (000000.001) :xyz}
+do_test printf-2.8.1.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 0.001
+} {abc: 3 2 (0.00) :xyz}
+do_test printf-2.8.1.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 0.001
+} {abc: 3 2 (1.00e-03) :xyz}
+do_test printf-2.8.1.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 0.001
+} {abc: 3 2 (0.001) :xyz}
+do_test printf-2.8.2.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 1.0e-20
+} {abc: (0.00) :xyz}
+do_test printf-2.8.2.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 1.0e-20
+} {abc: (1.00e-20) :xyz}
+do_test printf-2.8.2.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 1.0e-20
+} {abc: (1e-20) :xyz}
+do_test printf-2.8.2.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (1e-20) :xyz}
+do_test printf-2.8.2.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (1.00000e-20) :xyz}
+do_test printf-2.8.2.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (000001e-20) :xyz}
+do_test printf-2.8.2.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (0.00) :xyz}
+do_test printf-2.8.2.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (1.00e-20) :xyz}
+do_test printf-2.8.2.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 1.0e-20
+} {abc: 3 2 (1e-20) :xyz}
+do_test printf-2.8.3.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 1.0
+} {abc: (1.00) :xyz}
+do_test printf-2.8.3.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 1.0
+} {abc: (1.00e+00) :xyz}
+do_test printf-2.8.3.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 1.0
+} {abc: ( 1) :xyz}
+do_test printf-2.8.3.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 1.0
+} {abc: 3 2 (1) :xyz}
+do_test printf-2.8.3.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 1.0
+} {abc: 3 2 (1.00000) :xyz}
+do_test printf-2.8.3.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 1.0
+} {abc: 3 2 (0000000001) :xyz}
+do_test printf-2.8.3.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 1.0
+} {abc: 3 2 (1.00) :xyz}
+do_test printf-2.8.3.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 1.0
+} {abc: 3 2 (1.00e+00) :xyz}
+do_test printf-2.8.3.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 1.0
+} {abc: 3 2 ( 1) :xyz}
+do_test printf-2.8.4.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 0.0
+} {abc: (0.00) :xyz}
+do_test printf-2.8.4.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 0.0
+} {abc: (0.00e+00) :xyz}
+do_test printf-2.8.4.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 0.0
+} {abc: ( 0) :xyz}
+do_test printf-2.8.4.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 0.0
+} {abc: 3 2 (0) :xyz}
+do_test printf-2.8.4.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 0.0
+} {abc: 3 2 (0.00000) :xyz}
+do_test printf-2.8.4.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 0.0
+} {abc: 3 2 (0000000000) :xyz}
+do_test printf-2.8.4.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 0.0
+} {abc: 3 2 (0.00) :xyz}
+do_test printf-2.8.4.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 0.0
+} {abc: 3 2 (0.00e+00) :xyz}
+do_test printf-2.8.4.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 0.0
+} {abc: 3 2 ( 0) :xyz}
+do_test printf-2.8.5.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 100.0
+} {abc: (100.00) :xyz}
+do_test printf-2.8.5.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 100.0
+} {abc: (1.00e+02) :xyz}
+do_test printf-2.8.5.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 100.0
+} {abc: (1e+02) :xyz}
+do_test printf-2.8.5.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 100.0
+} {abc: 3 2 (100) :xyz}
+do_test printf-2.8.5.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 100.0
+} {abc: 3 2 (100.000) :xyz}
+do_test printf-2.8.5.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 100.0
+} {abc: 3 2 (0000000100) :xyz}
+do_test printf-2.8.5.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 100.0
+} {abc: 3 2 (100.00) :xyz}
+do_test printf-2.8.5.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 100.0
+} {abc: 3 2 (1.00e+02) :xyz}
+do_test printf-2.8.5.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 100.0
+} {abc: 3 2 (1e+02) :xyz}
+do_test printf-2.8.6.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 9.99999
+} {abc: (10.00) :xyz}
+do_test printf-2.8.6.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 9.99999
+} {abc: (1.00e+01) :xyz}
+do_test printf-2.8.6.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 9.99999
+} {abc: ( 10) :xyz}
+do_test printf-2.8.6.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 9.99999
+} {abc: 3 2 (9.99999) :xyz}
+do_test printf-2.8.6.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 9.99999
+} {abc: 3 2 (9.99999) :xyz}
+do_test printf-2.8.6.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 9.99999
+} {abc: 3 2 (0009.99999) :xyz}
+do_test printf-2.8.6.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 9.99999
+} {abc: 3 2 (10.00) :xyz}
+do_test printf-2.8.6.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 9.99999
+} {abc: 3 2 (1.00e+01) :xyz}
+do_test printf-2.8.6.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 9.99999
+} {abc: 3 2 ( 10) :xyz}
+do_test printf-2.8.7.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 -0.00543
+} {abc: (-0.01) :xyz}
+do_test printf-2.8.7.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 -0.00543
+} {abc: (-5.43e-03) :xyz}
+do_test printf-2.8.7.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 -0.00543
+} {abc: (-0.0054) :xyz}
+do_test printf-2.8.7.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-0.00543) :xyz}
+do_test printf-2.8.7.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-0.00543000) :xyz}
+do_test printf-2.8.7.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-000.00543) :xyz}
+do_test printf-2.8.7.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-0.01) :xyz}
+do_test printf-2.8.7.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-5.43e-03) :xyz}
+do_test printf-2.8.7.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 -0.00543
+} {abc: 3 2 (-0.0054) :xyz}
+do_test printf-2.8.8.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 -1.0
+} {abc: (-1.00) :xyz}
+do_test printf-2.8.8.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 -1.0
+} {abc: (-1.00e+00) :xyz}
+do_test printf-2.8.8.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 -1.0
+} {abc: ( -1) :xyz}
+do_test printf-2.8.8.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 -1.0
+} {abc: 3 2 (-1) :xyz}
+do_test printf-2.8.8.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 -1.0
+} {abc: 3 2 (-1.00000) :xyz}
+do_test printf-2.8.8.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 -1.0
+} {abc: 3 2 (-000000001) :xyz}
+do_test printf-2.8.8.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 -1.0
+} {abc: 3 2 (-1.00) :xyz}
+do_test printf-2.8.8.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 -1.0
+} {abc: 3 2 (-1.00e+00) :xyz}
+do_test printf-2.8.8.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 -1.0
+} {abc: 3 2 ( -1) :xyz}
+do_test printf-2.8.9.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 -99.99999
+} {abc: (-100.00) :xyz}
+do_test printf-2.8.9.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 -99.99999
+} {abc: (-1.00e+02) :xyz}
+do_test printf-2.8.9.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 -99.99999
+} {abc: (-1e+02) :xyz}
+do_test printf-2.8.9.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-100) :xyz}
+do_test printf-2.8.9.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-100.000) :xyz}
+do_test printf-2.8.9.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-000000100) :xyz}
+do_test printf-2.8.9.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-100.00) :xyz}
+do_test printf-2.8.9.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-1.00e+02) :xyz}
+do_test printf-2.8.9.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 -99.99999
+} {abc: 3 2 (-1e+02) :xyz}
+do_test printf-2.8.10.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 3.14e+9
+} {abc: (3140000000.00) :xyz}
+do_test printf-2.8.10.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 3.14e+9
+} {abc: (3.14e+09) :xyz}
+do_test printf-2.8.10.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 3.14e+9
+} {abc: (3.1e+09) :xyz}
+do_test printf-2.8.10.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (3.14e+09) :xyz}
+do_test printf-2.8.10.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (3.14000e+09) :xyz}
+do_test printf-2.8.10.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (003.14e+09) :xyz}
+do_test printf-2.8.10.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (3140000000.00) :xyz}
+do_test printf-2.8.10.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (3.14e+09) :xyz}
+do_test printf-2.8.10.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 3.14e+9
+} {abc: 3 2 (3.1e+09) :xyz}
+do_test printf-2.8.11.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 -4.72732e+88
+} {abc: (-4.73e+88) :xyz}
+do_test printf-2.8.11.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 -4.72732e+88
+} {abc: (-4.7e+88) :xyz}
+do_test printf-2.8.11.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 -4.72732e+88
+} {abc: 3 2 (-4.72732e+88) :xyz}
+do_test printf-2.8.11.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 -4.72732e+88
+} {abc: 3 2 (-4.72732e+88) :xyz}
+do_test printf-2.8.11.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 -4.72732e+88
+} {abc: 3 2 (-4.72732e+88) :xyz}
+do_test printf-2.8.11.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 -4.72732e+88
+} {abc: 3 2 (-4.73e+88) :xyz}
+do_test printf-2.8.11.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 -4.72732e+88
+} {abc: 3 2 (-4.7e+88) :xyz}
+do_test printf-2.8.12.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 9.87991e+143
+} {abc: (9.88e+143) :xyz}
+do_test printf-2.8.12.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 9.87991e+143
+} {abc: (9.9e+143) :xyz}
+do_test printf-2.8.12.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 9.87991e+143
+} {abc: 3 2 (9.87991e+143) :xyz}
+do_test printf-2.8.12.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 9.87991e+143
+} {abc: 3 2 (9.87991e+143) :xyz}
+do_test printf-2.8.12.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 9.87991e+143
+} {abc: 3 2 (9.87991e+143) :xyz}
+do_test printf-2.8.12.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 9.87991e+143
+} {abc: 3 2 (9.88e+143) :xyz}
+do_test printf-2.8.12.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 9.87991e+143
+} {abc: 3 2 (9.9e+143) :xyz}
+do_test printf-2.8.13.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 -6.287291e-9
+} {abc: (-0.00) :xyz}
+do_test printf-2.8.13.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 -6.287291e-9
+} {abc: (-6.29e-09) :xyz}
+do_test printf-2.8.13.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 -6.287291e-9
+} {abc: (-6.3e-09) :xyz}
+do_test printf-2.8.13.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-6.28729e-09) :xyz}
+do_test printf-2.8.13.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-6.28729e-09) :xyz}
+do_test printf-2.8.13.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-6.28729e-09) :xyz}
+do_test printf-2.8.13.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-0.00) :xyz}
+do_test printf-2.8.13.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-6.29e-09) :xyz}
+do_test printf-2.8.13.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 -6.287291e-9
+} {abc: 3 2 (-6.3e-09) :xyz}
+do_test printf-2.8.14.1 {
+ sqlite3_mprintf_double {abc: (%*.*f) :xyz} 3 2 3.38826392e-110
+} {abc: (0.00) :xyz}
+do_test printf-2.8.14.2 {
+ sqlite3_mprintf_double {abc: (%*.*e) :xyz} 3 2 3.38826392e-110
+} {abc: (3.39e-110) :xyz}
+do_test printf-2.8.14.3 {
+ sqlite3_mprintf_double {abc: (%*.*g) :xyz} 3 2 3.38826392e-110
+} {abc: (3.4e-110) :xyz}
+do_test printf-2.8.14.4 {
+ sqlite3_mprintf_double {abc: %d %d (%g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (3.38826e-110) :xyz}
+do_test printf-2.8.14.5 {
+ sqlite3_mprintf_double {abc: %d %d (%#g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (3.38826e-110) :xyz}
+do_test printf-2.8.14.6 {
+ sqlite3_mprintf_double {abc: %d %d (%010g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (3.38826e-110) :xyz}
+do_test printf-2.8.14.7 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2f) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (0.00) :xyz}
+do_test printf-2.8.14.8 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2e) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (3.39e-110) :xyz}
+do_test printf-2.8.14.9 {
+ sqlite3_mprintf_double {abc: %d %d (%3.2g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 (3.4e-110) :xyz}
+do_test printf-2.8.15.1 {
+ sqlite3_mprintf_double {abc: (% *.*f) :xyz} 3 2 3.38826392e-110
+} {abc: ( 0.00) :xyz}
+do_test printf-2.8.15.2 {
+ sqlite3_mprintf_double {abc: (% *.*e) :xyz} 3 2 3.38826392e-110
+} {abc: ( 3.39e-110) :xyz}
+do_test printf-2.8.15.3 {
+ sqlite3_mprintf_double {abc: (% *.*g) :xyz} 3 2 3.38826392e-110
+} {abc: ( 3.4e-110) :xyz}
+do_test printf-2.8.15.4 {
+ sqlite3_mprintf_double {abc: %d %d (% g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 3.38826e-110) :xyz}
+do_test printf-2.8.15.5 {
+ sqlite3_mprintf_double {abc: %d %d (% #g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 3.38826e-110) :xyz}
+do_test printf-2.8.15.6 {
+ sqlite3_mprintf_double {abc: %d %d (%0 10g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 3.38826e-110) :xyz}
+do_test printf-2.8.15.7 {
+ sqlite3_mprintf_double {abc: %d %d (% 3.2f) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 0.00) :xyz}
+do_test printf-2.8.15.8 {
+ sqlite3_mprintf_double {abc: %d %d (% 3.2e) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 3.39e-110) :xyz}
+do_test printf-2.8.15.9 {
+ sqlite3_mprintf_double {abc: %d %d (% 3.2g) :xyz} 3 2 3.38826392e-110
+} {abc: 3 2 ( 3.4e-110) :xyz}
+
+do_test printf-2.9.1 {
+ sqlite3_mprintf_double {abc: %d %d (%5.0g) :xyz} 0 0 1.234
+} {abc: 0 0 ( 1) :xyz}
+do_test printf-2.9.2 {
+ sqlite3_mprintf_double {abc: %d %d (%+5.0g) :xyz} 0 0 1.234
+} {abc: 0 0 ( +1) :xyz}
+do_test printf-2.9.3 {
+ sqlite3_mprintf_double {abc: %d %d (%+-5.0g) :xyz} 0 0 1.234
+} {abc: 0 0 (+1 ) :xyz}
+
+do_test printf-2.10.1 {
+ sqlite3_mprintf_double {abc: %d %d (%-010.5f) :xyz} 0 0 1.234
+} {abc: 0 0 (1.23400 ) :xyz}
+do_test printf-2.10.2 {
+ sqlite3_mprintf_double {abc: %d %d (%010.5f) :xyz} 0 0 1.234
+} {abc: 0 0 (0001.23400) :xyz}
+do_test printf-2.10.3 {
+ sqlite3_mprintf_double {abc: %d %d (%+010.5f) :xyz} 0 0 1.234
+} {abc: 0 0 (+001.23400) :xyz}
+
+do_test printf-3.1 {
+ sqlite3_mprintf_str {A String: (%*.*s)} 10 10 {This is the string}
+} [format {A String: (%*.*s)} 10 10 {This is the string}]
+do_test printf-3.2 {
+ sqlite3_mprintf_str {A String: (%*.*s)} 10 5 {This is the string}
+} [format {A String: (%*.*s)} 10 5 {This is the string}]
+do_test printf-3.3 {
+ sqlite3_mprintf_str {A String: (%*.*s)} -10 5 {This is the string}
+} [format {A String: (%*.*s)} -10 5 {This is the string}]
+do_test printf-3.4 {
+ sqlite3_mprintf_str {%d %d A String: (%s)} 1 2 {This is the string}
+} [format {%d %d A String: (%s)} 1 2 {This is the string}]
+do_test printf-3.5 {
+ sqlite3_mprintf_str {%d %d A String: (%30s)} 1 2 {This is the string}
+} [format {%d %d A String: (%30s)} 1 2 {This is the string}]
+do_test printf-3.6 {
+ sqlite3_mprintf_str {%d %d A String: (%-30s)} 1 2 {This is the string}
+} [format {%d %d A String: (%-30s)} 1 2 {This is the string}]
+do_test snprintf-3.11 {
+ sqlite3_snprintf_str 2 {x%d %d %s} 10 10 {This is the string}
+} {x}
+do_test snprintf-3.12 {
+ sqlite3_snprintf_str 3 {x%d %d %s} 10 10 {This is the string}
+} {x1}
+do_test snprintf-3.13 {
+ sqlite3_snprintf_str 4 {x%d %d %s} 10 10 {This is the string}
+} {x10}
+do_test snprintf-3.14 {
+ sqlite3_snprintf_str 5 {x%d %d %s} 10 10 {This is the string}
+} {x10 }
+do_test snprintf-3.15 {
+ sqlite3_snprintf_str 6 {x%d %d %s} 10 10 {This is the string}
+} {x10 1}
+do_test snprintf-3.16 {
+ sqlite3_snprintf_str 7 {x%d %d %s} 10 10 {This is the string}
+} {x10 10}
+do_test snprintf-3.17 {
+ sqlite3_snprintf_str 8 {x%d %d %s} 10 10 {This is the string}
+} {x10 10 }
+do_test snprintf-3.18 {
+ sqlite3_snprintf_str 9 {x%d %d %s} 10 10 {This is the string}
+} {x10 10 T}
+do_test snprintf-3.19 {
+ sqlite3_snprintf_str 100 {x%d %d %s} 10 10 {This is the string}
+} {x10 10 This is the string}
+
+do_test printf-4.1 {
+ sqlite3_mprintf_str {%d %d A quoted string: '%q'} 1 2 {Hi Y'all}
+} {1 2 A quoted string: 'Hi Y''all'}
+do_test printf-4.2 {
+ sqlite3_mprintf_str {%d %d A NULL pointer in %%q: '%q'} 1 2
+} {1 2 A NULL pointer in %q: '(NULL)'}
+do_test printf-4.3 {
+ sqlite3_mprintf_str {%d %d A quoted string: %Q} 1 2 {Hi Y'all}
+} {1 2 A quoted string: 'Hi Y''all'}
+do_test printf-4.4 {
+ sqlite3_mprintf_str {%d %d A NULL pointer in %%Q: %Q} 1 2
+} {1 2 A NULL pointer in %Q: NULL}
+
+do_test printf-5.1 {
+ set x [sqlite3_mprintf_str {%d %d %100000s} 0 0 {Hello}]
+ string length $x
+} {344}
+do_test printf-5.2 {
+ sqlite3_mprintf_str {%d %d (%-10.10s) %} -9 -10 {HelloHelloHello}
+} {-9 -10 (HelloHello) %}
+
+do_test printf-6.1 {
+ sqlite3_mprintf_z_test , one two three four five six
+} {,one,two,three,four,five,six}
+
+
+do_test printf-7.1 {
+ sqlite3_mprintf_scaled {A double: %g} 1.0e307 1.0
+} {A double: 1e+307}
+do_test printf-7.2 {
+ sqlite3_mprintf_scaled {A double: %g} 1.0e307 10.0
+} {A double: 1e+308}
+do_test printf-7.3 {
+ sqlite3_mprintf_scaled {A double: %g} 1.0e307 100.0
+} {A double: Inf}
+do_test printf-7.4 {
+ sqlite3_mprintf_scaled {A double: %g} -1.0e307 100.0
+} {A double: -Inf}
+do_test printf-7.5 {
+ sqlite3_mprintf_scaled {A double: %+g} 1.0e307 100.0
+} {A double: +Inf}
+
+do_test printf-8.1 {
+ sqlite3_mprintf_int {%u %u %u} 0x7fffffff 0x80000000 0xffffffff
+} {2147483647 2147483648 4294967295}
+do_test printf-8.2 {
+ sqlite3_mprintf_int {%lu %lu %lu} 0x7fffffff 0x80000000 0xffffffff
+} {2147483647 2147483648 4294967295}
+do_test printf-8.3 {
+ sqlite3_mprintf_int64 {%llu %llu %llu} 2147483647 2147483648 4294967296
+} {2147483647 2147483648 4294967296}
+do_test printf-8.4 {
+ sqlite3_mprintf_int64 {%lld %lld %lld} 2147483647 2147483648 4294967296
+} {2147483647 2147483648 4294967296}
+do_test printf-8.5 {
+ sqlite3_mprintf_int64 {%llx %llx %llx} 2147483647 2147483648 4294967296
+} {7fffffff 80000000 100000000}
+do_test printf-8.6 {
+ sqlite3_mprintf_int64 {%llx %llo %lld} -1 -1 -1
+} {ffffffffffffffff 1777777777777777777777 -1}
+do_test printf-8.7 {
+ sqlite3_mprintf_int64 {%llx %llx %llx} +2147483647 +2147483648 +4294967296
+} {7fffffff 80000000 100000000}
+
+do_test printf-9.1 {
+ sqlite3_mprintf_int {%*.*c} 4 4 65
+} {AAAA}
+do_test printf-9.2 {
+ sqlite3_mprintf_int {%*.*c} -4 1 66
+} {B }
+do_test printf-9.3 {
+ sqlite3_mprintf_int {%*.*c} 4 1 67
+} { C}
+do_test printf-9.4 {
+ sqlite3_mprintf_int {%d %d %c} 4 1 67
+} {4 1 C}
+set ten { }
+set fifty $ten$ten$ten$ten$ten
+do_test printf-9.5 {
+ sqlite3_mprintf_int {%d %*c} 1 -201 67
+} "1 C$fifty$fifty$fifty$fifty"
+do_test printf-9.6 {
+ sqlite3_mprintf_int {hi%12345.12346yhello} 0 0 0
+} {hi}
+
+# Ticket #812
+#
+do_test printf-10.1 {
+ sqlite3_mprintf_stronly %s {}
+} {}
+
+# Ticket #831
+#
+do_test printf-10.2 {
+ sqlite3_mprintf_stronly %q {}
+} {}
+
+# Ticket #1340: Test for loss of precision on large positive exponents
+#
+do_test printf-10.3 {
+ sqlite3_mprintf_double {%d %d %f} 1 1 1e300
+} {1 1 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000}
+
+# The non-standard '!' flag on a 'g' conversion forces a decimal point
+# and at least one digit on either side of the decimal point.
+#
+do_test printf-11.1 {
+ sqlite3_mprintf_double {%d %d %!g} 1 1 1
+} {1 1 1.0}
+do_test printf-11.2 {
+ sqlite3_mprintf_double {%d %d %!g} 1 1 123
+} {1 1 123.0}
+do_test printf-11.3 {
+ sqlite3_mprintf_double {%d %d %!g} 1 1 12.3
+} {1 1 12.3}
+do_test printf-11.4 {
+ sqlite3_mprintf_double {%d %d %!g} 1 1 0.123
+} {1 1 0.123}
+do_test printf-11.5 {
+ sqlite3_mprintf_double {%d %d %!.15g} 1 1 1
+} {1 1 1.0}
+do_test printf-11.6 {
+ sqlite3_mprintf_double {%d %d %!.15g} 1 1 1e10
+} {1 1 10000000000.0}
+do_test printf-11.7 {
+ sqlite3_mprintf_double {%d %d %!.15g} 1 1 1e300
+} {1 1 1.0e+300}
+
+# Additional tests for coverage
+#
+do_test printf-12.1 {
+ sqlite3_mprintf_double {%d %d %.2000g} 1 1 1.0
+} {1 1 1}
+
+# Floating point boundary cases
+#
+do_test printf-13.1 {
+ sqlite3_mprintf_hexdouble %.20f 4024000000000000
+} {10.00000000000000000000}
+do_test printf-13.2 {
+ sqlite3_mprintf_hexdouble %.20f 4197d78400000000
+} {100000000.00000000000000000000}
+do_test printf-13.3 {
+ sqlite3_mprintf_hexdouble %.20f 4693b8b5b5056e17
+} {100000000000000000000000000000000.00000000000000000000}
+do_test printf-13.4 {
+ sqlite3_mprintf_hexdouble %.20f 7ff0000000000000
+} {Inf}
+do_test printf-13.5 {
+ sqlite3_mprintf_hexdouble %.20f fff0000000000000
+} {-Inf}
+do_test printf-13.6 {
+ sqlite3_mprintf_hexdouble %.20f fff8000000000000
+} {NaN}
+
+do_test printf-14.1 {
+ sqlite3_mprintf_str {abc-%y-123} 0 0 {not used}
+} {abc-}
+do_test printf-14.2 {
+ sqlite3_mprintf_n_test {xyzzy}
+} 5
+do_test printf-14.3 {
+ sqlite3_mprintf_str {abc-%T-123} 0 0 {not used}
+} {abc-}
+do_test printf-14.4 {
+ sqlite3_mprintf_str {abc-%#} 0 0 {not used}
+} {abc-}
+do_test printf-14.5 {
+ sqlite3_mprintf_str {abc-%*.*s-xyz} 10 -10 {a_very_long_string}
+} {abc-a_very_lon-xyz}
+do_test printf-14.6 {
+ sqlite3_mprintf_str {abc-%5.10/} 0 0 {not used}
+} {abc-}
+do_test printf-14.7 {
+ sqlite3_mprintf_str {abc-%05.5d} 123 0 {not used}
+} {abc-00123}
+do_test printf-14.8 {
+ sqlite3_mprintf_str {abc-%05.5d} 1234567 0 {not used}
+} {abc-1234567}
+
+for {set i 2} {$i<200} {incr i} {
+ set res [string repeat { } [expr {$i-1}]]x
+ do_test printf-14.90.$i "
+ sqlite3_mprintf_str {%*.*s} $i 500 x
+ " $res
+}
+
+do_test printf-15.1 {
+ sqlite3_snprintf_int 5 {12345} 0
+} {1234}
+do_test printf-15.2 {
+ sqlite3_snprintf_int 5 {} 0
+} {}
+do_test printf-15.3 {
+ sqlite3_snprintf_int 0 {} 0
+} {abcdefghijklmnopqrstuvwxyz}
+
+# Now test malloc() failure within a sqlite3_mprintf():
+#
+ifcapable memdebug {
+ foreach var {a b c d} {
+ set $var [string repeat $var 400]
+ }
+ set str1 "[string repeat A 360]%d%d%s"
+ set str2 [string repeat B 5000]
+ set zSuccess "[string repeat A 360]11[string repeat B 5000]"
+ foreach ::iRepeat {0 1} {
+ set nTestNum 1
+ while {1} {
+ sqlite3_memdebug_fail $nTestNum -repeat $::iRepeat
+ set z [sqlite3_mprintf_str $str1 1 1 $str2]
+ set nFail [sqlite3_memdebug_fail -1 -benign nBenign]
+ do_test printf-malloc-$::iRepeat.$nTestNum {
+ expr {($nFail>0 && $z eq "") || ($nFail==$nBenign && $z eq $zSuccess)}
+ } {1}
+ if {$nFail == 0} break
+ incr nTestNum
+ }
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/progress.test b/third_party/sqlite/test/progress.test
new file mode 100755
index 0000000..b25a100
--- /dev/null
+++ b/third_party/sqlite/test/progress.test
@@ -0,0 +1,177 @@
+# 2001 September 15
+#
+# 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 file is testing the 'progress callback'.
+#
+# $Id: progress.test,v 1.8 2007/06/15 14:53:53 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If the progress callback is not available in this build, skip this
+# whole file.
+ifcapable !progress {
+ finish_test
+ return
+}
+
+# Build some test data
+#
+execsql {
+ BEGIN;
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ INSERT INTO t1 VALUES(5);
+ INSERT INTO t1 VALUES(6);
+ INSERT INTO t1 VALUES(7);
+ INSERT INTO t1 VALUES(8);
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(10);
+ COMMIT;
+}
+
+
+# Test that the progress callback is invoked.
+do_test progress-1.0 {
+ set counter 0
+ db progress 1 "[namespace code {incr counter}] ; expr 0"
+ execsql {
+ SELECT * FROM t1
+ }
+ expr $counter > 1
+} 1
+do_test progress-1.0.1 {
+ db progress
+} {::namespace inscope :: {incr counter} ; expr 0}
+do_test progress-1.0.2 {
+ set v [catch {db progress xyz bogus} msg]
+ lappend v $msg
+} {1 {expected integer but got "xyz"}}
+
+# Test that the query is abandoned when the progress callback returns non-zero
+do_test progress-1.1 {
+ set counter 0
+ db progress 1 "[namespace code {incr counter}] ; expr 1"
+ set rc [catch {execsql {
+ SELECT * FROM t1
+ }}]
+ list $counter $rc
+} {1 1}
+
+# Test that the query is rolled back when the progress callback returns
+# non-zero.
+do_test progress-1.2 {
+
+ # This figures out how many opcodes it takes to copy 5 extra rows into t1.
+ db progress 1 "[namespace code {incr five_rows}] ; expr 0"
+ set five_rows 0
+ execsql {
+ INSERT INTO t1 SELECT a+10 FROM t1 WHERE a < 6
+ }
+ db progress 0 ""
+ execsql {
+ DELETE FROM t1 WHERE a > 10
+ }
+
+ # Now set up the progress callback to abandon the query after the number of
+ # opcodes to copy 5 rows. That way, when we try to copy 6 rows, we know
+ # some data will have been inserted into the table by the time the progress
+ # callback abandons the query.
+ db progress $five_rows "expr 1"
+ catchsql {
+ INSERT INTO t1 SELECT a+10 FROM t1 WHERE a < 9
+ }
+ execsql {
+ SELECT count(*) FROM t1
+ }
+} 10
+
+# Test that an active transaction remains active and not rolled back
+# after the progress query abandons a query.
+#
+# UPDATE: It is now recognised that this is a sure route to database
+# corruption. So the transaction is rolled back.
+do_test progress-1.3 {
+
+ db progress 0 ""
+ execsql BEGIN
+ execsql {
+ INSERT INTO t1 VALUES(11)
+ }
+ db progress 1 "expr 1"
+ catchsql {
+ INSERT INTO t1 VALUES(12)
+ }
+ db progress 0 ""
+ catchsql COMMIT
+} {1 {cannot commit - no transaction is active}}
+do_test progress-1.3.1 {
+ execsql {
+ SELECT count(*) FROM t1
+ }
+} 10
+
+# Check that a value of 0 for N means no progress callback
+do_test progress-1.4 {
+ set counter 0
+ db progress 0 "[namespace code {incr counter}] ; expr 0"
+ execsql {
+ SELECT * FROM t1;
+ }
+ set counter
+} 0
+
+db progress 0 ""
+
+# Make sure other queries can be run from within the progress
+# handler. Ticket #1827
+#
+do_test progress-1.5 {
+ set rx 0
+ proc set_rx {args} {
+ db progress 0 {}
+ set ::rx [db eval {SELECT count(*) FROM t1}]
+ return [expr 0]
+ }
+ db progress 10 set_rx
+ db eval {
+ SELECT sum(a) FROM t1
+ }
+} {55}
+do_test progress-1.6 {
+ set ::rx
+} {10}
+
+# Check that abandoning a query using the progress handler does
+# not cause other queries to abort. Ticket #2415.
+do_test progress-1.7 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ INSERT INTO abc VALUES(7, 8, 9);
+ }
+
+ set ::res [list]
+ db eval {SELECT a, b, c FROM abc} {
+ lappend ::res $a $b $c
+ db progress 10 "expr 1"
+ catch {db eval {SELECT a, b, c FROM abc} { }} msg
+ lappend ::res $msg
+ }
+
+ set ::res
+} {1 2 3 interrupted 4 5 6 interrupted 7 8 9 interrupted}
+
+finish_test
diff --git a/third_party/sqlite/test/ptrchng.test b/third_party/sqlite/test/ptrchng.test
new file mode 100755
index 0000000..75525f9
--- /dev/null
+++ b/third_party/sqlite/test/ptrchng.test
@@ -0,0 +1,223 @@
+# 2007 April 27
+#
+# 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 the tests in this file are to verify that the
+# underlying TEXT or BLOB representation of an sqlite3_value
+# changes appropriately when APIs from the following set are
+# called:
+#
+# sqlite3_value_text()
+# sqlite3_value_text16()
+# sqlite3_value_blob()
+# sqlite3_value_bytes()
+# sqlite3_value_bytes16()
+#
+# $Id: ptrchng.test,v 1.5 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !bloblit {
+ finish_test
+ return
+}
+
+# Register the "pointer_change" SQL function.
+#
+sqlite3_create_function db
+
+do_test ptrchng-1.1 {
+ execsql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y BLOB);
+ INSERT INTO t1 VALUES(1, 'abc');
+ INSERT INTO t1 VALUES(2,
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234356789');
+ INSERT INTO t1 VALUES(3, x'626c6f62');
+ INSERT INTO t1 VALUES(4,
+ x'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324'
+ );
+ SELECT count(*) FROM t1;
+ }
+} {4}
+
+# For the short entries that fit in the Mem.zBuf[], the pointer should
+# never change regardless of what type conversions occur.
+#
+# UPDATE: No longer true, as Mem.zBuf[] has been removed.
+#
+do_test ptrchng-2.1 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=1
+ }
+} {0}
+do_test ptrchng-2.2 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=1
+ }
+} {0}
+ifcapable utf16 {
+ do_test ptrchng-2.3 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=1
+ }
+ } {1}
+ do_test ptrchng-2.4 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=1
+ }
+ } {1}
+ do_test ptrchng-2.5 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=1
+ }
+ } {0}
+ do_test ptrchng-2.6 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=1
+ }
+ } {1}
+}
+do_test ptrchng-2.11 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=3
+ }
+} {0}
+do_test ptrchng-2.12 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=3
+ }
+} {0}
+ifcapable utf16 {
+ do_test ptrchng-2.13 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=3
+ }
+ } {1}
+ do_test ptrchng-2.14 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=3
+ }
+ } {1}
+ do_test ptrchng-2.15 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=3
+ }
+ } {0}
+ do_test ptrchng-2.16 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=3
+ }
+ } {1}
+}
+
+# For the long entries that do not fit in the Mem.zBuf[], the pointer
+# should change sometimes.
+#
+do_test ptrchng-3.1 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=2
+ }
+} {0}
+do_test ptrchng-3.2 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=2
+ }
+} {0}
+ifcapable utf16 {
+ do_test ptrchng-3.3 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=2
+ }
+ } {1}
+ do_test ptrchng-3.4 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=2
+ }
+ } {1}
+ do_test ptrchng-3.5 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=2
+ }
+ } {0}
+ do_test ptrchng-3.6 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=2
+ }
+ } {1}
+}
+do_test ptrchng-3.11 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=4
+ }
+} {0}
+do_test ptrchng-3.12 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=4
+ }
+} {0}
+ifcapable utf16 {
+ do_test ptrchng-3.13 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=4
+ }
+ } {1}
+ do_test ptrchng-3.14 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=4
+ }
+ } {1}
+ do_test ptrchng-3.15 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=4
+ }
+ } {0}
+ do_test ptrchng-3.16 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=4
+ }
+ } {1}
+}
+
+# A call to _bytes() should never reformat a _text() or _blob().
+#
+do_test ptrchng-4.1 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'bytes', 'text') FROM t1
+ }
+} {0 0 0 0}
+do_test ptrchng-4.2 {
+ execsql {
+ SELECT pointer_change(y, 'blob', 'bytes', 'blob') FROM t1
+ }
+} {0 0 0 0}
+
+# A call to _blob() should never trigger a reformat
+#
+do_test ptrchng-5.1 {
+ execsql {
+ SELECT pointer_change(y, 'text', 'bytes', 'blob') FROM t1
+ }
+} {0 0 0 0}
+ifcapable utf16 {
+ do_test ptrchng-5.2 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1
+ }
+ } {0 0 0 0}
+ do_test ptrchng-5.3 {
+ execsql {
+ SELECT pointer_change(y, 'text16', 'bytes16', 'blob') FROM t1
+ }
+ } {0 0 0 0}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/quick.test b/third_party/sqlite/test/quick.test
new file mode 100755
index 0000000..b2b731e
--- /dev/null
+++ b/third_party/sqlite/test/quick.test
@@ -0,0 +1,134 @@
+#
+# 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 runs all tests.
+#
+# $Id: quick.test,v 1.84 2008/06/26 08:29:35 danielk1977 Exp $
+
+proc lshift {lvar} {
+ upvar $lvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+while {[set arg [lshift argv]] != ""} {
+ switch -- $arg {
+ -sharedpagercache {
+ sqlite3_enable_shared_cache 1
+ }
+ -soak {
+ set SOAKTEST 1
+ }
+ -start {
+ set STARTAT "[lshift argv]*"
+ }
+ default {
+ set argv [linsert $argv 0 $arg]
+ break
+ }
+ }
+}
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {}
+set ISQUICK 1
+
+set EXCLUDE {
+ all.test
+ async.test
+ async2.test
+ async3.test
+ corrupt.test
+ crash.test
+ crash2.test
+ crash3.test
+ crash4.test
+ crash5.test
+ crash6.test
+ crash7.test
+ delete3.test
+ fts3.test
+ fts.test
+ fts1.test
+ fts2.test
+ fuzz.test
+ fuzz_malloc.test
+ in2.test
+ loadext.test
+ memleak.test
+ misc7.test
+ misuse.test
+ mutex2.test
+ onefile.test
+ permutations.test
+ quick.test
+ select9.test
+ soak.test
+ speed1.test
+ speed1p.test
+ speed2.test
+ speed3.test
+ speed4.test
+ speed4p.test
+ sqllimits1.test
+ tkt2686.test
+ thread001.test
+ thread002.test
+ vacuum3.test
+
+ incrvacuum_ioerr.test
+ autovacuum_crash.test
+ btree8.test
+ shared_err.test
+ vtab_err.test
+ veryquick.test
+ mallocAll.test
+}
+
+if {[sqlite3 -has-codec]} {
+ # lappend EXCLUDE \
+ # conflict.test
+}
+
+
+# Files to include in the test. If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
+foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
+ # If this is "veryquick.test", do not run any of the malloc or
+ # IO error simulations.
+ if {[info exists ISVERYQUICK] && (
+ [string match *malloc* $testfile] || [string match *ioerr* $testfile]
+ ) } {
+ continue
+ }
+
+ set tail [file tail $testfile]
+ if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+ if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
+ if {[info exists STARTAT] && [string match $STARTAT $tail]} {unset STARTAT}
+ if {[info exists STARTAT]} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+}
+#set argv quick
+#source $testdir/permutations.test
+#set argv ""
+source $testdir/misuse.test
+
+set sqlite_open_file_count 0
+really_finish_test
diff --git a/third_party/sqlite/test/quote.test b/third_party/sqlite/test/quote.test
new file mode 100755
index 0000000..f13d6f9
--- /dev/null
+++ b/third_party/sqlite/test/quote.test
@@ -0,0 +1,89 @@
+# 2001 September 15
+#
+# 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 file is the ability to specify table and column names
+# as quoted strings.
+#
+# $Id: quote.test,v 1.7 2007/04/25 11:32:30 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table with a strange name and with strange column names.
+#
+do_test quote-1.0 {
+ catchsql {CREATE TABLE '@abc' ( '#xyz' int, '!pqr' text );}
+} {0 {}}
+
+# Insert, update and query the table.
+#
+do_test quote-1.1 {
+ catchsql {INSERT INTO '@abc' VALUES(5,'hello')}
+} {0 {}}
+do_test quote-1.2.1 {
+ catchsql {SELECT * FROM '@abc'}
+} {0 {5 hello}}
+do_test quote-1.2.2 {
+ catchsql {SELECT * FROM [@abc]} ;# SqlServer compatibility
+} {0 {5 hello}}
+do_test quote-1.2.3 {
+ catchsql {SELECT * FROM `@abc`} ;# MySQL compatibility
+} {0 {5 hello}}
+do_test quote-1.3 {
+ catchsql {
+ SELECT '@abc'.'!pqr', '@abc'.'#xyz'+5 FROM '@abc'
+ }
+} {0 {hello 10}}
+do_test quote-1.3.1 {
+ catchsql {
+ SELECT '!pqr', '#xyz'+5 FROM '@abc'
+ }
+} {0 {!pqr 5}}
+do_test quote-1.3.2 {
+ catchsql {
+ SELECT "!pqr", "#xyz"+5 FROM '@abc'
+ }
+} {0 {hello 10}}
+do_test quote-1.3.3 {
+ catchsql {
+ SELECT [!pqr], `#xyz`+5 FROM '@abc'
+ }
+} {0 {hello 10}}
+do_test quote-1.3.4 {
+ set r [catch {
+ execsql {SELECT '@abc'.'!pqr', '@abc'.'#xyz'+5 FROM '@abc'}
+ } msg ]
+ lappend r $msg
+} {0 {hello 10}}
+do_test quote-1.4 {
+ set r [catch {
+ execsql {UPDATE '@abc' SET '#xyz'=11}
+ } msg ]
+ lappend r $msg
+} {0 {}}
+do_test quote-1.5 {
+ set r [catch {
+ execsql {SELECT '@abc'.'!pqr', '@abc'.'#xyz'+5 FROM '@abc'}
+ } msg ]
+ lappend r $msg
+} {0 {hello 16}}
+
+# Drop the table with the strange name.
+#
+do_test quote-1.6 {
+ set r [catch {
+ execsql {DROP TABLE '@abc'}
+ } msg ]
+ lappend r $msg
+} {0 {}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/rdonly.test b/third_party/sqlite/test/rdonly.test
new file mode 100755
index 0000000..a975cef
--- /dev/null
+++ b/third_party/sqlite/test/rdonly.test
@@ -0,0 +1,78 @@
+# 2007 April 24
+#
+# 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.
+#
+# This file implements tests to make sure SQLite treats a database
+# as readonly if its write version is set to high.
+#
+# $Id: rdonly.test,v 1.2 2008/07/08 10:19:58 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Create a database.
+#
+do_test rdonly-1.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ SELECT * FROM t1;
+ }
+} {1}
+
+# Changes the write version from 1 to 2. Verify that the database
+# can be read but not written.
+#
+do_test rdonly-1.2 {
+ db close
+ hexio_get_int [hexio_read test.db 18 1]
+} 1
+do_test rdonly-1.3 {
+ hexio_write test.db 18 02
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM t1;
+ }
+} {1}
+do_test rdonly-1.4 {
+ catchsql {
+ INSERT INTO t1 VALUES(2)
+ }
+} {1 {attempt to write a readonly database}}
+
+# Change the write version back to 1. Verify that the database
+# is read-write again.
+#
+do_test rdonly-1.5 {
+ db close
+ hexio_write test.db 18 01
+ sqlite3 db test.db
+ catchsql {
+ INSERT INTO t1 VALUES(2);
+ SELECT * FROM t1;
+ }
+} {0 {1 2}}
+
+# Now, after connection [db] has loaded the database schema, modify the
+# write-version of the file (and the change-counter, so that the
+# write-version is reloaded). This way, SQLite does not discover that
+# the database is read-only until after it is locked.
+#
+do_test rdonly-1.6 {
+ hexio_write test.db 18 02 ; # write-version
+ hexio_write test.db 24 11223344 ; # change-counter
+ catchsql {
+ INSERT INTO t1 VALUES(2);
+ }
+} {1 {attempt to write a readonly database}}
+
+finish_test
diff --git a/third_party/sqlite/test/reindex.test b/third_party/sqlite/test/reindex.test
new file mode 100755
index 0000000..9d5ecd9
--- /dev/null
+++ b/third_party/sqlite/test/reindex.test
@@ -0,0 +1,171 @@
+# 2004 November 5
+#
+# 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.
+# This file implements tests for the REINDEX command.
+#
+# $Id: reindex.test,v 1.4 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# There is nothing to test if REINDEX is disable for this build.
+#
+ifcapable {!reindex} {
+ finish_test
+ return
+}
+
+# Basic sanity checks.
+#
+do_test reindex-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ CREATE INDEX i1 ON t1(a);
+ REINDEX;
+ }
+} {}
+integrity_check reindex-1.2
+do_test reindex-1.3 {
+ execsql {
+ REINDEX t1;
+ }
+} {}
+integrity_check reindex-1.4
+do_test reindex-1.5 {
+ execsql {
+ REINDEX i1;
+ }
+} {}
+integrity_check reindex-1.6
+do_test reindex-1.7 {
+ execsql {
+ REINDEX main.t1;
+ }
+} {}
+do_test reindex-1.8 {
+ execsql {
+ REINDEX main.i1;
+ }
+} {}
+do_test reindex-1.9 {
+ catchsql {
+ REINDEX bogus
+ }
+} {1 {unable to identify the object to be reindexed}}
+
+# Set up a table for testing that includes several different collating
+# sequences including some that we can modify.
+#
+do_test reindex-2.1 {
+ proc c1 {a b} {
+ return [expr {-[string compare $a $b]}]
+ }
+ proc c2 {a b} {
+ return [expr {-[string compare [string tolower $a] [string tolower $b]]}]
+ }
+ db collate c1 c1
+ db collate c2 c2
+ execsql {
+ CREATE TABLE t2(
+ a TEXT PRIMARY KEY COLLATE c1,
+ b TEXT UNIQUE COLLATE c2,
+ c TEXT COLLATE nocase,
+ d TEST COLLATE binary
+ );
+ INSERT INTO t2 VALUES('abc','abc','abc','abc');
+ INSERT INTO t2 VALUES('ABCD','ABCD','ABCD','ABCD');
+ INSERT INTO t2 VALUES('bcd','bcd','bcd','bcd');
+ INSERT INTO t2 VALUES('BCDE','BCDE','BCDE','BCDE');
+ SELECT a FROM t2 ORDER BY a;
+ }
+} {bcd abc BCDE ABCD}
+do_test reindex-2.2 {
+ execsql {
+ SELECT b FROM t2 ORDER BY b;
+ }
+} {BCDE bcd ABCD abc}
+do_test reindex-2.3 {
+ execsql {
+ SELECT c FROM t2 ORDER BY c;
+ }
+} {abc ABCD bcd BCDE}
+do_test reindex-2.4 {
+ execsql {
+ SELECT d FROM t2 ORDER BY d;
+ }
+} {ABCD BCDE abc bcd}
+
+# Change a collating sequence function. Verify that REINDEX rebuilds
+# the index.
+#
+do_test reindex-2.5 {
+ proc c1 {a b} {
+ return [string compare $a $b]
+ }
+ execsql {
+ SELECT a FROM t2 ORDER BY a;
+ }
+} {bcd abc BCDE ABCD}
+ifcapable {integrityck} {
+ do_test reindex-2.5.1 {
+ string equal ok [execsql {PRAGMA integrity_check}]
+ } {0}
+}
+do_test reindex-2.6 {
+ execsql {
+ REINDEX c2;
+ SELECT a FROM t2 ORDER BY a;
+ }
+} {bcd abc BCDE ABCD}
+do_test reindex-2.7 {
+ execsql {
+ REINDEX t1;
+ SELECT a FROM t2 ORDER BY a;
+ }
+} {bcd abc BCDE ABCD}
+do_test reindex-2.8 {
+ execsql {
+ REINDEX c1;
+ SELECT a FROM t2 ORDER BY a;
+ }
+} {ABCD BCDE abc bcd}
+integrity_check reindex-2.8.1
+
+# Try to REINDEX an index for which the collation sequence is not available.
+#
+do_test reindex-3.1 {
+ sqlite3 db2 test.db
+ catchsql {
+ REINDEX c1;
+ } db2
+} {1 {no such collation sequence: c1}}
+do_test reindex-3.2 {
+ proc need_collate {collation} {
+ db2 collate c1 c1
+ }
+ db2 collation_needed need_collate
+ catchsql {
+ REINDEX c1;
+ } db2
+} {0 {}}
+do_test reindex-3.3 {
+ catchsql {
+ REINDEX;
+ } db2
+} {1 {no such collation sequence: c2}}
+
+do_test reindex-3.99 {
+ db2 close
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/rollback.test b/third_party/sqlite/test/rollback.test
new file mode 100755
index 0000000..b0047d6
--- /dev/null
+++ b/third_party/sqlite/test/rollback.test
@@ -0,0 +1,82 @@
+# 2004 June 30
+#
+# 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 file is verifying that a rollback in one statement
+# caused by an ON CONFLICT ROLLBACK clause aborts any other pending
+# statements.
+#
+# $Id: rollback.test,v 1.6 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set DB [sqlite3_connection_pointer db]
+
+do_test rollback-1.1 {
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ SELECT * FROM t1;
+ }
+} {1 2 3 4}
+
+ifcapable conflict {
+ do_test rollback-1.2 {
+ execsql {
+ CREATE TABLE t3(a unique on conflict rollback);
+ INSERT INTO t3 SELECT a FROM t1;
+ BEGIN;
+ INSERT INTO t1 SELECT * FROM t1;
+ }
+ } {}
+}
+do_test rollback-1.3 {
+ set STMT [sqlite3_prepare $DB "SELECT a FROM t1" -1 TAIL]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+
+ifcapable conflict {
+ # This causes a ROLLBACK, which deletes the table out from underneath the
+ # SELECT statement.
+ #
+ do_test rollback-1.4 {
+ catchsql {
+ INSERT INTO t3 SELECT a FROM t1;
+ }
+ } {1 {column a is not unique}}
+
+ # Try to continue with the SELECT statement
+ #
+ do_test rollback-1.5 {
+ sqlite3_step $STMT
+ } {SQLITE_ERROR}
+
+ # Restart the SELECT statement
+ #
+ do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_ABORT}
+} else {
+ do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
+}
+
+do_test rollback-1.7 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test rollback-1.8 {
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test rollback-1.9 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+finish_test
diff --git a/third_party/sqlite/test/rowid.test b/third_party/sqlite/test/rowid.test
new file mode 100755
index 0000000..24dcd2a
--- /dev/null
+++ b/third_party/sqlite/test/rowid.test
@@ -0,0 +1,701 @@
+# 2001 September 15
+#
+# 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 file is testing the magic ROWID column that is
+# found on all tables.
+#
+# $Id: rowid.test,v 1.20 2008/01/19 20:11:26 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Basic ROWID functionality tests.
+#
+do_test rowid-1.1 {
+ execsql {
+ CREATE TABLE t1(x int, y int);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ SELECT x FROM t1 ORDER BY y;
+ }
+} {1 3}
+do_test rowid-1.2 {
+ set r [execsql {SELECT rowid FROM t1 ORDER BY x}]
+ global x2rowid rowid2x
+ set x2rowid(1) [lindex $r 0]
+ set x2rowid(3) [lindex $r 1]
+ set rowid2x($x2rowid(1)) 1
+ set rowid2x($x2rowid(3)) 3
+ llength $r
+} {2}
+do_test rowid-1.3 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE rowid==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.4 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE rowid==$x2rowid(3)"
+ execsql $sql
+} {3}
+do_test rowid-1.5 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE oid==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.6 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE OID==$x2rowid(3)"
+ execsql $sql
+} {3}
+do_test rowid-1.7 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE _rowid_==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.7.1 {
+ while 1 {
+ set norow [expr {int(rand()*1000000)}]
+ if {$norow!=$x2rowid(1) && $norow!=$x2rowid(3)} break
+ }
+ execsql "SELECT x FROM t1 WHERE rowid=$norow"
+} {}
+do_test rowid-1.8 {
+ global x2rowid
+ set v [execsql {SELECT x, oid FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+do_test rowid-1.9 {
+ global x2rowid
+ set v [execsql {SELECT x, RowID FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+do_test rowid-1.10 {
+ global x2rowid
+ set v [execsql {SELECT x, _rowid_ FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+
+# We can insert or update the ROWID column.
+#
+do_test rowid-2.1 {
+ catchsql {
+ INSERT INTO t1(rowid,x,y) VALUES(1234,5,6);
+ SELECT rowid, * FROM t1;
+ }
+} {0 {1 1 2 2 3 4 1234 5 6}}
+do_test rowid-2.2 {
+ catchsql {
+ UPDATE t1 SET rowid=12345 WHERE x==1;
+ SELECT rowid, * FROM t1
+ }
+} {0 {2 3 4 1234 5 6 12345 1 2}}
+do_test rowid-2.3 {
+ catchsql {
+ INSERT INTO t1(y,x,oid) VALUES(8,7,1235);
+ SELECT rowid, * FROM t1 WHERE rowid>1000;
+ }
+} {0 {1234 5 6 1235 7 8 12345 1 2}}
+do_test rowid-2.4 {
+ catchsql {
+ UPDATE t1 SET oid=12346 WHERE x==1;
+ SELECT rowid, * FROM t1;
+ }
+} {0 {2 3 4 1234 5 6 1235 7 8 12346 1 2}}
+do_test rowid-2.5 {
+ catchsql {
+ INSERT INTO t1(x,_rowid_,y) VALUES(9,1236,10);
+ SELECT rowid, * FROM t1 WHERE rowid>1000;
+ }
+} {0 {1234 5 6 1235 7 8 1236 9 10 12346 1 2}}
+do_test rowid-2.6 {
+ catchsql {
+ UPDATE t1 SET _rowid_=12347 WHERE x==1;
+ SELECT rowid, * FROM t1 WHERE rowid>1000;
+ }
+} {0 {1234 5 6 1235 7 8 1236 9 10 12347 1 2}}
+
+# But we can use ROWID in the WHERE clause of an UPDATE that does not
+# change the ROWID.
+#
+do_test rowid-2.7 {
+ global x2rowid
+ set sql "UPDATE t1 SET x=2 WHERE OID==$x2rowid(3)"
+ execsql $sql
+ execsql {SELECT x FROM t1 ORDER BY x}
+} {1 2 5 7 9}
+do_test rowid-2.8 {
+ global x2rowid
+ set sql "UPDATE t1 SET x=3 WHERE _rowid_==$x2rowid(3)"
+ execsql $sql
+ execsql {SELECT x FROM t1 ORDER BY x}
+} {1 3 5 7 9}
+
+# We cannot index by ROWID
+#
+do_test rowid-2.9 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(rowid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+do_test rowid-2.10 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(_rowid_)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named _rowid_}}
+do_test rowid-2.11 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(oid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named oid}}
+do_test rowid-2.12 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(x, rowid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+
+# Columns defined in the CREATE statement override the buildin ROWID
+# column names.
+#
+do_test rowid-3.1 {
+ execsql {
+ CREATE TABLE t2(rowid int, x int, y int);
+ INSERT INTO t2 VALUES(0,2,3);
+ INSERT INTO t2 VALUES(4,5,6);
+ INSERT INTO t2 VALUES(7,8,9);
+ SELECT * FROM t2 ORDER BY x;
+ }
+} {0 2 3 4 5 6 7 8 9}
+do_test rowid-3.2 {
+ execsql {SELECT * FROM t2 ORDER BY rowid}
+} {0 2 3 4 5 6 7 8 9}
+do_test rowid-3.3 {
+ execsql {SELECT rowid, x, y FROM t2 ORDER BY rowid}
+} {0 2 3 4 5 6 7 8 9}
+do_test rowid-3.4 {
+ set r1 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY rowid}]
+ foreach {a b c d e f} $r1 {}
+ set r2 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY x DESC}]
+ foreach {u v w x y z} $r2 {}
+ expr {$u==$e && $w==$c && $y==$a}
+} {1}
+# sqlite3 v3 - do_probtest doesn't exist anymore?
+if 0 {
+do_probtest rowid-3.5 {
+ set r1 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY rowid}]
+ foreach {a b c d e f} $r1 {}
+ expr {$a!=$b && $c!=$d && $e!=$f}
+} {1}
+}
+
+# Let's try some more complex examples, including some joins.
+#
+do_test rowid-4.1 {
+ execsql {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ }
+ for {set i 1} {$i<=50} {incr i} {
+ execsql "INSERT INTO t1(x,y) VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {INSERT INTO t2 SELECT _rowid_, x*y, y*y FROM t1}
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.2.1 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.oid==t2.rowid}
+} {256}
+do_test rowid-4.2.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.2.3 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t2.rowid==t1.rowid}
+} {256}
+do_test rowid-4.2.4 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t2.rowid==t1.oid AND t1.x==4}
+} {256}
+do_test rowid-4.2.5 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.2.6 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t2.rowid==t1.rowid}
+} {256}
+do_test rowid-4.2.7 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t2.rowid==t1.oid AND t1.x==4}
+} {256}
+do_test rowid-4.3 {
+ execsql {CREATE INDEX idxt1 ON t1(x)}
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.3.1 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.3.2 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t2.rowid==t1.oid AND 4==t1.x}
+} {256}
+do_test rowid-4.4 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.4.1 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.4.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t2.rowid==t1.oid AND 4==t1.x}
+} {256}
+do_test rowid-4.5 {
+ execsql {CREATE INDEX idxt2 ON t2(y)}
+ set sqlite_search_count 0
+ concat [execsql {
+ SELECT t1.x FROM t2, t1
+ WHERE t2.y==256 AND t1.rowid==t2.rowid
+ }] $sqlite_search_count
+} {4 3}
+do_test rowid-4.5.1 {
+ set sqlite_search_count 0
+ concat [execsql {
+ SELECT t1.x FROM t2, t1
+ WHERE t1.OID==t2.rowid AND t2.y==81
+ }] $sqlite_search_count
+} {3 3}
+do_test rowid-4.6 {
+ execsql {
+ SELECT t1.x FROM t1, t2
+ WHERE t2.y==256 AND t1.rowid==t2.rowid
+ }
+} {4}
+
+do_test rowid-5.1.1 {
+ ifcapable subquery {
+ execsql {DELETE FROM t1 WHERE _rowid_ IN (SELECT oid FROM t1 WHERE x>8)}
+ } else {
+ set oids [execsql {SELECT oid FROM t1 WHERE x>8}]
+ set where "_rowid_ = [join $oids { OR _rowid_ = }]"
+ execsql "DELETE FROM t1 WHERE $where"
+ }
+} {}
+do_test rowid-5.1.2 {
+ execsql {SELECT max(x) FROM t1}
+} {8}
+
+# Make sure a "WHERE rowid=X" clause works when there is no ROWID of X.
+#
+do_test rowid-6.1 {
+ execsql {
+ SELECT x FROM t1
+ }
+} {1 2 3 4 5 6 7 8}
+do_test rowid-6.2 {
+ for {set ::norow 1} {1} {incr ::norow} {
+ if {[execsql "SELECT x FROM t1 WHERE rowid=$::norow"]==""} break
+ }
+ execsql [subst {
+ DELETE FROM t1 WHERE rowid=$::norow
+ }]
+} {}
+do_test rowid-6.3 {
+ execsql {
+ SELECT x FROM t1
+ }
+} {1 2 3 4 5 6 7 8}
+
+# Beginning with version 2.3.4, SQLite computes rowids of new rows by
+# finding the maximum current rowid and adding one. It falls back to
+# the old random algorithm if the maximum rowid is the largest integer.
+# The following tests are for this new behavior.
+#
+do_test rowid-7.0 {
+ execsql {
+ DELETE FROM t1;
+ DROP TABLE t2;
+ DROP INDEX idxt1;
+ INSERT INTO t1 VALUES(1,2);
+ SELECT rowid, * FROM t1;
+ }
+} {1 1 2}
+do_test rowid-7.1 {
+ execsql {
+ INSERT INTO t1 VALUES(99,100);
+ SELECT rowid,* FROM t1
+ }
+} {1 1 2 2 99 100}
+do_test rowid-7.2 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t2(b) VALUES(55);
+ SELECT * FROM t2;
+ }
+} {1 55}
+do_test rowid-7.3 {
+ execsql {
+ INSERT INTO t2(b) VALUES(66);
+ SELECT * FROM t2;
+ }
+} {1 55 2 66}
+do_test rowid-7.4 {
+ execsql {
+ INSERT INTO t2(a,b) VALUES(1000000,77);
+ INSERT INTO t2(b) VALUES(88);
+ SELECT * FROM t2;
+ }
+} {1 55 2 66 1000000 77 1000001 88}
+do_test rowid-7.5 {
+ execsql {
+ INSERT INTO t2(a,b) VALUES(2147483647,99);
+ INSERT INTO t2(b) VALUES(11);
+ SELECT b FROM t2 ORDER BY b;
+ }
+} {11 55 66 77 88 99}
+ifcapable subquery {
+ do_test rowid-7.6 {
+ execsql {
+ SELECT b FROM t2 WHERE a NOT IN(1,2,1000000,1000001,2147483647);
+ }
+ } {11}
+ do_test rowid-7.7 {
+ execsql {
+ INSERT INTO t2(b) VALUES(22);
+ INSERT INTO t2(b) VALUES(33);
+ INSERT INTO t2(b) VALUES(44);
+ INSERT INTO t2(b) VALUES(55);
+ SELECT b FROM t2 WHERE a NOT IN(1,2,1000000,1000001,2147483647)
+ ORDER BY b;
+ }
+ } {11 22 33 44 55}
+}
+do_test rowid-7.8 {
+ execsql {
+ DELETE FROM t2 WHERE a!=2;
+ INSERT INTO t2(b) VALUES(111);
+ SELECT * FROM t2;
+ }
+} {2 66 3 111}
+
+ifcapable {trigger} {
+# Make sure AFTER triggers that do INSERTs do not change the last_insert_rowid.
+# Ticket #290
+#
+do_test rowid-8.1 {
+ execsql {
+ CREATE TABLE t3(a integer primary key);
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES(1);
+ CREATE TRIGGER r3 AFTER INSERT on t3 FOR EACH ROW BEGIN
+ INSERT INTO t4 VALUES(NEW.a+10);
+ END;
+ SELECT * FROM t3;
+ }
+} {}
+do_test rowid-8.2 {
+ execsql {
+ SELECT rowid, * FROM t4;
+ }
+} {1 1}
+do_test rowid-8.3 {
+ execsql {
+ INSERT INTO t3 VALUES(123);
+ SELECT last_insert_rowid();
+ }
+} {123}
+do_test rowid-8.4 {
+ execsql {
+ SELECT * FROM t3;
+ }
+} {123}
+do_test rowid-8.5 {
+ execsql {
+ SELECT rowid, * FROM t4;
+ }
+} {1 1 2 133}
+do_test rowid-8.6 {
+ execsql {
+ INSERT INTO t3 VALUES(NULL);
+ SELECT last_insert_rowid();
+ }
+} {124}
+do_test rowid-8.7 {
+ execsql {
+ SELECT * FROM t3;
+ }
+} {123 124}
+do_test rowid-8.8 {
+ execsql {
+ SELECT rowid, * FROM t4;
+ }
+} {1 1 2 133 3 134}
+} ;# endif trigger
+
+# If triggers are not enable, simulate their effect for the tests that
+# follow.
+ifcapable {!trigger} {
+ execsql {
+ CREATE TABLE t3(a integer primary key);
+ INSERT INTO t3 VALUES(123);
+ INSERT INTO t3 VALUES(124);
+ }
+}
+
+# ticket #377: Comparison between integer primiary key and floating point
+# values.
+#
+do_test rowid-9.1 {
+ execsql {
+ SELECT * FROM t3 WHERE a<123.5
+ }
+} {123}
+do_test rowid-9.2 {
+ execsql {
+ SELECT * FROM t3 WHERE a<124.5
+ }
+} {123 124}
+do_test rowid-9.3 {
+ execsql {
+ SELECT * FROM t3 WHERE a>123.5
+ }
+} {124}
+do_test rowid-9.4 {
+ execsql {
+ SELECT * FROM t3 WHERE a>122.5
+ }
+} {123 124}
+do_test rowid-9.5 {
+ execsql {
+ SELECT * FROM t3 WHERE a==123.5
+ }
+} {}
+do_test rowid-9.6 {
+ execsql {
+ SELECT * FROM t3 WHERE a==123.000
+ }
+} {123}
+do_test rowid-9.7 {
+ execsql {
+ SELECT * FROM t3 WHERE a>100.5 AND a<200.5
+ }
+} {123 124}
+do_test rowid-9.8 {
+ execsql {
+ SELECT * FROM t3 WHERE a>'xyz';
+ }
+} {}
+do_test rowid-9.9 {
+ execsql {
+ SELECT * FROM t3 WHERE a<'xyz';
+ }
+} {123 124}
+do_test rowid-9.10 {
+ execsql {
+ SELECT * FROM t3 WHERE a>=122.9 AND a<=123.1
+ }
+} {123}
+
+# Ticket #567. Comparisons of ROWID or integery primary key against
+# floating point numbers still do not always work.
+#
+do_test rowid-10.1 {
+ execsql {
+ CREATE TABLE t5(a);
+ INSERT INTO t5 VALUES(1);
+ INSERT INTO t5 VALUES(2);
+ INSERT INTO t5 SELECT a+2 FROM t5;
+ INSERT INTO t5 SELECT a+4 FROM t5;
+ SELECT rowid, * FROM t5;
+ }
+} {1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8}
+do_test rowid-10.2 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>=5.5}
+} {6 6 7 7 8 8}
+do_test rowid-10.3 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>=5.0}
+} {5 5 6 6 7 7 8 8}
+do_test rowid-10.4 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>5.5}
+} {6 6 7 7 8 8}
+do_test rowid-10.3.2 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>5.0}
+} {6 6 7 7 8 8}
+do_test rowid-10.5 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5<=rowid}
+} {6 6 7 7 8 8}
+do_test rowid-10.6 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5<rowid}
+} {6 6 7 7 8 8}
+do_test rowid-10.7 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<=5.5}
+} {1 1 2 2 3 3 4 4 5 5}
+do_test rowid-10.8 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<5.5}
+} {1 1 2 2 3 3 4 4 5 5}
+do_test rowid-10.9 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5>=rowid}
+} {1 1 2 2 3 3 4 4 5 5}
+do_test rowid-10.10 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5>rowid}
+} {1 1 2 2 3 3 4 4 5 5}
+do_test rowid-10.11 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>=5.5 ORDER BY rowid DESC}
+} {8 8 7 7 6 6}
+do_test rowid-10.11.2 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>=5.0 ORDER BY rowid DESC}
+} {8 8 7 7 6 6 5 5}
+do_test rowid-10.12 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>5.5 ORDER BY rowid DESC}
+} {8 8 7 7 6 6}
+do_test rowid-10.12.2 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>5.0 ORDER BY rowid DESC}
+} {8 8 7 7 6 6}
+do_test rowid-10.13 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5<=rowid ORDER BY rowid DESC}
+} {8 8 7 7 6 6}
+do_test rowid-10.14 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5<rowid ORDER BY rowid DESC}
+} {8 8 7 7 6 6}
+do_test rowid-10.15 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<=5.5 ORDER BY rowid DESC}
+} {5 5 4 4 3 3 2 2 1 1}
+do_test rowid-10.16 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<5.5 ORDER BY rowid DESC}
+} {5 5 4 4 3 3 2 2 1 1}
+do_test rowid-10.17 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5>=rowid ORDER BY rowid DESC}
+} {5 5 4 4 3 3 2 2 1 1}
+do_test rowid-10.18 {
+ execsql {SELECT rowid, a FROM t5 WHERE 5.5>rowid ORDER BY rowid DESC}
+} {5 5 4 4 3 3 2 2 1 1}
+
+do_test rowid-10.30 {
+ execsql {
+ CREATE TABLE t6(a);
+ INSERT INTO t6(rowid,a) SELECT -a,a FROM t5;
+ SELECT rowid, * FROM t6;
+ }
+} {-8 8 -7 7 -6 6 -5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.31.1 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>=-5.5}
+} {-5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.31.2 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>=-5.0}
+} {-5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.32.1 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>=-5.5 ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4 -5 5}
+do_test rowid-10.32.1 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>=-5.0 ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4 -5 5}
+do_test rowid-10.33 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5<=rowid}
+} {-5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.34 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5<=rowid ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4 -5 5}
+do_test rowid-10.35.1 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>-5.5}
+} {-5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.35.2 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>-5.0}
+} {-4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.36.1 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>-5.5 ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4 -5 5}
+do_test rowid-10.36.2 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid>-5.0 ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4}
+do_test rowid-10.37 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5<rowid}
+} {-5 5 -4 4 -3 3 -2 2 -1 1}
+do_test rowid-10.38 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5<rowid ORDER BY rowid DESC}
+} {-1 1 -2 2 -3 3 -4 4 -5 5}
+do_test rowid-10.39 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid<=-5.5}
+} {-8 8 -7 7 -6 6}
+do_test rowid-10.40 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid<=-5.5 ORDER BY rowid DESC}
+} {-6 6 -7 7 -8 8}
+do_test rowid-10.41 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5>=rowid}
+} {-8 8 -7 7 -6 6}
+do_test rowid-10.42 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5>=rowid ORDER BY rowid DESC}
+} {-6 6 -7 7 -8 8}
+do_test rowid-10.43 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid<-5.5}
+} {-8 8 -7 7 -6 6}
+do_test rowid-10.44 {
+ execsql {SELECT rowid, a FROM t6 WHERE rowid<-5.5 ORDER BY rowid DESC}
+} {-6 6 -7 7 -8 8}
+do_test rowid-10.44 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5>rowid}
+} {-8 8 -7 7 -6 6}
+do_test rowid-10.46 {
+ execsql {SELECT rowid, a FROM t6 WHERE -5.5>rowid ORDER BY rowid DESC}
+} {-6 6 -7 7 -8 8}
+
+# Comparison of rowid against string values.
+#
+do_test rowid-11.1 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>'abc'}
+} {}
+do_test rowid-11.2 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid>='abc'}
+} {}
+do_test rowid-11.3 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<'abc'}
+} {1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8}
+do_test rowid-11.4 {
+ execsql {SELECT rowid, a FROM t5 WHERE rowid<='abc'}
+} {1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8}
+
+# Test the automatic generation of rowids when the table already contains
+# a rowid with the maximum value.
+#
+# Once the the maximum rowid is taken, rowids are normally chosen at
+# random. By by reseting the random number generator, we can cause
+# the rowid guessing loop to collide with prior rowids, and test the
+# loop out to its limit of 100 iterations. After 100 collisions, the
+# rowid guesser gives up and reports SQLITE_FULL.
+#
+do_test rowid-12.1 {
+ execsql {
+ CREATE TABLE t7(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t7 VALUES(9223372036854775807,'a');
+ SELECT y FROM t7;
+ }
+} {a}
+do_test rowid-12.2 {
+ db close
+ sqlite3 db test.db
+ save_prng_state
+ execsql {
+ INSERT INTO t7 VALUES(NULL,'b');
+ SELECT x, y FROM t7;
+ }
+} {1 b 9223372036854775807 a}
+execsql {INSERT INTO t7 VALUES(2,'y');}
+for {set i 1} {$i<=101} {incr i} {
+ do_test rowid-12.3.$i {
+ restore_prng_state
+ execsql {
+ INSERT INTO t7 VALUES(NULL,'x');
+ INSERT OR IGNORE INTO t7 VALUES(last_insert_rowid()+1,'y');
+ SELECT count(*) FROM t7 WHERE y=='x';
+ }
+ } $i
+}
+do_test rowid-12.4 {
+ restore_prng_state
+ catchsql {
+ INSERT INTO t7 VALUES(NULL,'x');
+ }
+} {1 {database or disk is full}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/rtree.test b/third_party/sqlite/test/rtree.test
new file mode 100755
index 0000000..eb880af
--- /dev/null
+++ b/third_party/sqlite/test/rtree.test
@@ -0,0 +1,40 @@
+#
+# 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 runs all rtree related tests.
+#
+# $Id: rtree.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+rename finish_test rtree_finish_test
+proc finish_test {} {}
+
+set RTREE_EXCLUDE { }
+if {[info exists ISQUICK] && $ISQUICK} {
+ set RTREE_EXCLUDE rtree3.test
+}
+
+set rtreedir [file join $testdir .. ext rtree]
+
+foreach testfile [lsort -dictionary [glob $rtreedir/*.test]] {
+ set tail [file tail $testfile]
+ if {[lsearch -exact $RTREE_EXCLUDE $tail]>=0} continue
+ source $testfile
+ catch {db close}
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+}
+
+set sqlite_open_file_count 0
+rtree_finish_test
+rename finish_test {}
+rename rtree_finish_test finish_test
diff --git a/third_party/sqlite/test/safety.test b/third_party/sqlite/test/safety.test
new file mode 100755
index 0000000..9cca57c
--- /dev/null
+++ b/third_party/sqlite/test/safety.test
@@ -0,0 +1,93 @@
+# 2005 January 11
+#
+# 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 file is testing the sqlite3SafetyOn and sqlite3SafetyOff
+# functions. Those routines are not strictly necessary - they are
+# designed to detect misuse of the library.
+#
+# $Id: safety.test,v 1.4 2008/03/18 13:46:53 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !debug {
+ puts "Skipping safety tests since SQLITE_DEBUG is off"
+ finish_test
+ return
+}
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str.
+proc utf8 {str} {
+ # If $str ends in two 0x00 0x00 bytes, knock these off before
+ # converting to UTF-8 using TCL.
+ binary scan $str \c* vals
+ if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+ set str [binary format \c* [lrange $vals 0 end-2]]
+ }
+
+ set r [encoding convertfrom unicode $str]
+ return $r
+}
+
+
+do_test safety-1.1 {
+ set DB [sqlite3_connection_pointer db]
+ db eval {CREATE TABLE t1(a)}
+ sqlite_set_magic $DB SQLITE_MAGIC_BUSY
+ catchsql {
+ SELECT name FROM sqlite_master;
+ }
+} {1 {library routine called out of sequence}}
+do_test safety-1.2 {
+ sqlite_set_magic $DB SQLITE_MAGIC_OPEN
+ catchsql {
+ SELECT name FROM sqlite_master
+ }
+} {0 t1}
+
+do_test safety-2.1 {
+ proc safety_on {} "sqlite_set_magic $DB SQLITE_MAGIC_BUSY"
+ db function safety_on safety_on
+ catchsql {
+ SELECT safety_on(), name FROM sqlite_master
+ }
+} {1 {library routine called out of sequence}}
+ifcapable {utf16} {
+ do_test safety-2.1.1 {
+ utf8 [sqlite3_errmsg16 db]
+ } {library routine called out of sequence}
+}
+do_test safety-2.2 {
+ catchsql {
+ SELECT 'hello'
+ }
+} {1 {library routine called out of sequence}}
+do_test safety-2.3 {
+ sqlite3_close $DB
+} {SQLITE_MISUSE}
+do_test safety-2.4 {
+ sqlite_set_magic $DB SQLITE_MAGIC_OPEN
+ execsql {
+ SELECT name FROM sqlite_master
+ }
+} {t1}
+
+do_test safety-3.1 {
+ set rc [catch {
+ db eval {SELECT name FROM sqlite_master} {
+ sqlite_set_magic $DB SQLITE_MAGIC_BUSY
+ }
+ } msg]
+ lappend rc $msg
+} {1 {library routine called out of sequence}}
+sqlite_set_magic $DB SQLITE_MAGIC_OPEN
+
+finish_test
diff --git a/third_party/sqlite/test/schema.test b/third_party/sqlite/test/schema.test
new file mode 100755
index 0000000..25779a1
--- /dev/null
+++ b/third_party/sqlite/test/schema.test
@@ -0,0 +1,367 @@
+# 2005 Jan 24
+#
+# 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.
+#
+# This file tests the various conditions under which an SQLITE_SCHEMA
+# error should be returned.
+#
+# $Id: schema.test,v 1.8 2007/10/09 08:29:33 danielk1977 Exp $
+
+#---------------------------------------------------------------------
+# When any of the following types of SQL statements or actions are
+# executed, all pre-compiled statements are invalidated. An attempt
+# to execute an invalidated statement always returns SQLITE_SCHEMA.
+#
+# CREATE/DROP TABLE...................................schema-1.*
+# CREATE/DROP VIEW....................................schema-2.*
+# CREATE/DROP TRIGGER.................................schema-3.*
+# CREATE/DROP INDEX...................................schema-4.*
+# DETACH..............................................schema-5.*
+# Deleting a user-function............................schema-6.*
+# Deleting a collation sequence.......................schema-7.*
+# Setting or changing the authorization function......schema-8.*
+# Rollback of a DDL statement.........................schema-12.*
+#
+# Test cases schema-9.* and schema-10.* test some specific bugs
+# that came up during development.
+#
+# Test cases schema-11.* test that it is impossible to delete or
+# change a collation sequence or user-function while SQL statements
+# are executing. Adding new collations or functions is allowed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test schema-1.1 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test schema-1.2 {
+ sqlite3_finalize $::STMT
+} {SQLITE_SCHEMA}
+do_test schema-1.3 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP TABLE abc;
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test schema-1.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_SCHEMA}
+
+ifcapable view {
+ do_test schema-2.1 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM sqlite_master;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-2.2 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+ do_test schema-2.3 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP VIEW v1;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-2.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+}
+
+ifcapable trigger {
+ do_test schema-3.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE TRIGGER abc_trig AFTER INSERT ON abc BEGIN
+ SELECT 1, 2, 3;
+ END;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-3.2 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+ do_test schema-3.3 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP TRIGGER abc_trig;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-3.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+}
+
+do_test schema-4.1 {
+ catchsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE INDEX abc_index ON abc(a);
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test schema-4.2 {
+ sqlite3_finalize $::STMT
+} {SQLITE_SCHEMA}
+do_test schema-4.3 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP INDEX abc_index;
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test schema-4.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_SCHEMA}
+
+#---------------------------------------------------------------------
+# Tests 5.1 to 5.4 check that prepared statements are invalidated when
+# a database is DETACHed (but not when one is ATTACHed).
+#
+ifcapable attach {
+ do_test schema-5.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema-5.2 {
+ sqlite3_reset $::STMT
+ } {SQLITE_OK}
+ do_test schema-5.3 {
+ execsql {
+ DETACH aux;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-5.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+}
+
+#---------------------------------------------------------------------
+# Tests 6.* check that prepared statements are invalidated when
+# a user-function is deleted (but not when one is added).
+do_test schema-6.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ db function hello_function {}
+ sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema-6.2 {
+ sqlite3_reset $::STMT
+} {SQLITE_OK}
+do_test schema-6.3 {
+ sqlite_delete_function $::DB hello_function
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test schema-6.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_SCHEMA}
+
+#---------------------------------------------------------------------
+# Tests 7.* check that prepared statements are invalidated when
+# a collation sequence is deleted (but not when one is added).
+#
+ifcapable utf16 {
+ do_test schema-7.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ add_test_collate $::DB 1 1 1
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema-7.2 {
+ sqlite3_reset $::STMT
+ } {SQLITE_OK}
+ do_test schema-7.3 {
+ add_test_collate $::DB 0 0 0
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-7.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+}
+
+#---------------------------------------------------------------------
+# Tests 8.1 and 8.2 check that prepared statements are invalidated when
+# the authorization function is set.
+#
+ifcapable auth {
+ do_test schema-8.1 {
+ set ::STMT [sqlite3_prepare $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ db auth {}
+ sqlite3_step $::STMT
+ } {SQLITE_ERROR}
+ do_test schema-8.3 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_SCHEMA}
+}
+
+#---------------------------------------------------------------------
+# schema-9.1: Test that if a table is dropped by one database connection,
+# other database connections are aware of the schema change.
+# schema-9.2: Test that if a view is dropped by one database connection,
+# other database connections are aware of the schema change.
+#
+do_test schema-9.1 {
+ sqlite3 db2 test.db
+ execsql {
+ DROP TABLE abc;
+ } db2
+ db2 close
+ catchsql {
+ SELECT * FROM abc;
+ }
+} {1 {no such table: abc}}
+execsql {
+ CREATE TABLE abc(a, b, c);
+}
+ifcapable view {
+ do_test schema-9.2 {
+ execsql {
+ CREATE VIEW abcview AS SELECT * FROM abc;
+ }
+ sqlite3 db2 test.db
+ execsql {
+ DROP VIEW abcview;
+ } db2
+ db2 close
+ catchsql {
+ SELECT * FROM abcview;
+ }
+ } {1 {no such table: abcview}}
+}
+
+#---------------------------------------------------------------------
+# Test that if a CREATE TABLE statement fails because there are other
+# btree cursors open on the same database file it does not corrupt
+# the sqlite_master table.
+#
+# 2007-05-02: These tests have been overcome by events. Open btree
+# cursors no longer block CREATE TABLE. But there is no reason not
+# to keep the tests in the test suite.
+#
+do_test schema-10.1 {
+ execsql {
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema-10.2 {
+ catchsql {
+ CREATE TABLE t2(a, b, c);
+ }
+} {0 {}}
+do_test schema-10.3 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema-10.4 {
+ sqlite3 db2 test.db
+ execsql {
+ SELECT * FROM abc
+ } db2
+} {1 2 3}
+do_test schema-10.5 {
+ db2 close
+} {}
+
+#---------------------------------------------------------------------
+# Attempting to delete or replace a user-function or collation sequence
+# while there are active statements returns an SQLITE_BUSY error.
+#
+# schema-11.1 - 11.4: User function.
+# schema-11.5 - 11.8: Collation sequence.
+#
+do_test schema-11.1 {
+ db function tstfunc {}
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema-11.2 {
+ sqlite_delete_function $::DB tstfunc
+} {SQLITE_BUSY}
+do_test schema-11.3 {
+ set rc [catch {
+ db function tstfunc {}
+ } msg]
+ list $rc $msg
+} {1 {Unable to delete/modify user-function due to active statements}}
+do_test schema-11.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema-11.5 {
+ db collate tstcollate {}
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema-11.6 {
+ sqlite_delete_collation $::DB tstcollate
+} {SQLITE_BUSY}
+do_test schema-11.7 {
+ set rc [catch {
+ db collate tstcollate {}
+ } msg]
+ list $rc $msg
+} {1 {Unable to delete/modify collation sequence due to active statements}}
+do_test schema-11.8 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+# The following demonstrates why statements need to be expired whenever
+# there is a rollback (explicit or otherwise).
+#
+do_test schema-12.1 {
+ # Begin a transaction and create a table. This increments
+ # the schema cookie. Then compile an SQL statement, using
+ # the current (incremented) value of the cookie.
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(a, b, c);
+ }
+ set ::STMT [sqlite3_prepare $::DB "CREATE TABLE t4(a,b,c)" -1 TAIL]
+
+ # Rollback the transaction, resetting the schema cookie to the value
+ # it had at the start of this test case. Then create a table,
+ # incrementing the schema cookie.
+ execsql {
+ ROLLBACK;
+ CREATE TABLE t4(a, b, c);
+ }
+
+ # The schema cookie now has the same value as it did when SQL statement
+ # $::STMT was prepared. So unless it has been expired, it would be
+ # possible to run the "CREATE TABLE t4" statement and create a
+ # duplicate table.
+ list [sqlite3_step $::STMT] [sqlite3_finalize $::STMT]
+} {SQLITE_ERROR SQLITE_SCHEMA}
+
+finish_test
diff --git a/third_party/sqlite/test/schema2.test b/third_party/sqlite/test/schema2.test
new file mode 100755
index 0000000..63e58a0
--- /dev/null
+++ b/third_party/sqlite/test/schema2.test
@@ -0,0 +1,340 @@
+# 2006 November 08
+#
+# 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.
+#
+# This file tests the various conditions under which an SQLITE_SCHEMA
+# error should be returned. This is a copy of schema.test that
+# has been altered to use sqlite3_prepare_v2 instead of sqlite3_prepare
+#
+# $Id: schema2.test,v 1.3 2007/10/09 08:29:33 danielk1977 Exp $
+
+#---------------------------------------------------------------------
+# When any of the following types of SQL statements or actions are
+# executed, all pre-compiled statements are invalidated. An attempt
+# to execute an invalidated statement always returns SQLITE_SCHEMA.
+#
+# CREATE/DROP TABLE...................................schema2-1.*
+# CREATE/DROP VIEW....................................schema2-2.*
+# CREATE/DROP TRIGGER.................................schema2-3.*
+# CREATE/DROP INDEX...................................schema2-4.*
+# DETACH..............................................schema2-5.*
+# Deleting a user-function............................schema2-6.*
+# Deleting a collation sequence.......................schema2-7.*
+# Setting or changing the authorization function......schema2-8.*
+#
+# Test cases schema2-9.* and schema2-10.* test some specific bugs
+# that came up during development.
+#
+# Test cases schema2-11.* test that it is impossible to delete or
+# change a collation sequence or user-function while SQL statements
+# are executing. Adding new collations or functions is allowed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test schema2-1.1 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-1.2 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-1.3 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP TABLE abc;
+ }
+ sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-1.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+
+ifcapable view {
+ do_test schema2-2.1 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE VIEW v1 AS SELECT * FROM sqlite_master;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ do_test schema2-2.2 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+ do_test schema2-2.3 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP VIEW v1;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema2-2.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+}
+
+ifcapable trigger {
+ do_test schema2-3.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE TRIGGER abc_trig AFTER INSERT ON abc BEGIN
+ SELECT 1, 2, 3;
+ END;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ do_test schema2-3.2 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+ do_test schema2-3.3 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP TRIGGER abc_trig;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ do_test schema2-3.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+}
+
+do_test schema2-4.1 {
+ catchsql {
+ CREATE TABLE abc(a, b, c);
+ }
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ CREATE INDEX abc_index ON abc(a);
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-4.2 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-4.3 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ execsql {
+ DROP INDEX abc_index;
+ }
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-4.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------
+# Tests 5.1 to 5.4 check that prepared statements are invalidated when
+# a database is DETACHed (but not when one is ATTACHed).
+#
+ifcapable attach {
+ do_test schema2-5.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema2-5.2 {
+ sqlite3_reset $::STMT
+ } {SQLITE_OK}
+ do_test schema2-5.3 {
+ execsql {
+ DETACH aux;
+ }
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema2-5.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+}
+
+#---------------------------------------------------------------------
+# Tests 6.* check that prepared statements are invalidated when
+# a user-function is deleted (but not when one is added).
+do_test schema2-6.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ db function hello_function {}
+ sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-6.2 {
+ sqlite3_reset $::STMT
+} {SQLITE_OK}
+do_test schema2-6.3 {
+ sqlite_delete_function $::DB hello_function
+ sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-6.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------
+# Tests 7.* check that prepared statements are invalidated when
+# a collation sequence is deleted (but not when one is added).
+#
+ifcapable utf16 {
+ do_test schema2-7.1 {
+ set sql {SELECT * FROM abc;}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ add_test_collate $::DB 1 1 1
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema2-7.2 {
+ sqlite3_reset $::STMT
+ } {SQLITE_OK}
+ do_test schema2-7.3 {
+ add_test_collate $::DB 0 0 0
+ sqlite3_step $::STMT
+ } {SQLITE_DONE}
+ do_test schema2-7.4 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+}
+
+#---------------------------------------------------------------------
+# Tests 8.1 and 8.2 check that prepared statements are invalidated when
+# the authorization function is set.
+#
+ifcapable auth {
+ do_test schema2-8.1 {
+ set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+ db auth {}
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ do_test schema2-8.3 {
+ sqlite3_finalize $::STMT
+ } {SQLITE_OK}
+}
+
+#---------------------------------------------------------------------
+# schema2-9.1: Test that if a table is dropped by one database connection,
+# other database connections are aware of the schema change.
+# schema2-9.2: Test that if a view is dropped by one database connection,
+# other database connections are aware of the schema change.
+#
+do_test schema2-9.1 {
+ sqlite3 db2 test.db
+ execsql {
+ DROP TABLE abc;
+ } db2
+ db2 close
+ catchsql {
+ SELECT * FROM abc;
+ }
+} {1 {no such table: abc}}
+execsql {
+ CREATE TABLE abc(a, b, c);
+}
+ifcapable view {
+ do_test schema2-9.2 {
+ execsql {
+ CREATE VIEW abcview AS SELECT * FROM abc;
+ }
+ sqlite3 db2 test.db
+ execsql {
+ DROP VIEW abcview;
+ } db2
+ db2 close
+ catchsql {
+ SELECT * FROM abcview;
+ }
+ } {1 {no such table: abcview}}
+}
+
+#---------------------------------------------------------------------
+# Test that if a CREATE TABLE statement fails because there are other
+# btree cursors open on the same database file it does not corrupt
+# the sqlite_master table.
+#
+# 2007-05-02: These tests have been overcome by events. Open btree
+# cursors no longer block CREATE TABLE. But there is no reason not
+# to keep the tests in the test suite.
+#
+do_test schema2-10.1 {
+ execsql {
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-10.2 {
+ catchsql {
+ CREATE TABLE t2(a, b, c);
+ }
+} {0 {}}
+do_test schema2-10.3 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-10.4 {
+ sqlite3 db2 test.db
+ execsql {
+ SELECT * FROM abc
+ } db2
+} {1 2 3}
+do_test schema2-10.5 {
+ db2 close
+} {}
+
+#---------------------------------------------------------------------
+# Attempting to delete or replace a user-function or collation sequence
+# while there are active statements returns an SQLITE_BUSY error.
+#
+# schema2-11.1 - 11.4: User function.
+# schema2-11.5 - 11.8: Collation sequence.
+#
+do_test schema2-11.1 {
+ db function tstfunc {}
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-11.2 {
+ sqlite_delete_function $::DB tstfunc
+} {SQLITE_BUSY}
+do_test schema2-11.3 {
+ set rc [catch {
+ db function tstfunc {}
+ } msg]
+ list $rc $msg
+} {1 {Unable to delete/modify user-function due to active statements}}
+do_test schema2-11.4 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-11.5 {
+ db collate tstcollate {}
+ set sql {SELECT * FROM abc}
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-11.6 {
+ sqlite_delete_collation $::DB tstcollate
+} {SQLITE_BUSY}
+do_test schema2-11.7 {
+ set rc [catch {
+ db collate tstcollate {}
+ } msg]
+ list $rc $msg
+} {1 {Unable to delete/modify collation sequence due to active statements}}
+do_test schema2-11.8 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+finish_test
diff --git a/third_party/sqlite/test/select1.test b/third_party/sqlite/test/select1.test
new file mode 100755
index 0000000..59c3b58
--- /dev/null
+++ b/third_party/sqlite/test/select1.test
@@ -0,0 +1,1044 @@
+# 2001 September 15
+#
+# 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 file is testing the SELECT statement.
+#
+# $Id: select1.test,v 1.65 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Try to select on a non-existant table.
+#
+do_test select1-1.1 {
+ set v [catch {execsql {SELECT * FROM test1}} msg]
+ lappend v $msg
+} {1 {no such table: test1}}
+
+
+execsql {CREATE TABLE test1(f1 int, f2 int)}
+
+do_test select1-1.2 {
+ set v [catch {execsql {SELECT * FROM test1, test2}} msg]
+ lappend v $msg
+} {1 {no such table: test2}}
+do_test select1-1.3 {
+ set v [catch {execsql {SELECT * FROM test2, test1}} msg]
+ lappend v $msg
+} {1 {no such table: test2}}
+
+execsql {INSERT INTO test1(f1,f2) VALUES(11,22)}
+
+
+# Make sure the columns are extracted correctly.
+#
+do_test select1-1.4 {
+ execsql {SELECT f1 FROM test1}
+} {11}
+do_test select1-1.5 {
+ execsql {SELECT f2 FROM test1}
+} {22}
+do_test select1-1.6 {
+ execsql {SELECT f2, f1 FROM test1}
+} {22 11}
+do_test select1-1.7 {
+ execsql {SELECT f1, f2 FROM test1}
+} {11 22}
+do_test select1-1.8 {
+ execsql {SELECT * FROM test1}
+} {11 22}
+do_test select1-1.8.1 {
+ execsql {SELECT *, * FROM test1}
+} {11 22 11 22}
+do_test select1-1.8.2 {
+ execsql {SELECT *, min(f1,f2), max(f1,f2) FROM test1}
+} {11 22 11 22}
+do_test select1-1.8.3 {
+ execsql {SELECT 'one', *, 'two', * FROM test1}
+} {one 11 22 two 11 22}
+
+execsql {CREATE TABLE test2(r1 real, r2 real)}
+execsql {INSERT INTO test2(r1,r2) VALUES(1.1,2.2)}
+
+do_test select1-1.9 {
+ execsql {SELECT * FROM test1, test2}
+} {11 22 1.1 2.2}
+do_test select1-1.9.1 {
+ execsql {SELECT *, 'hi' FROM test1, test2}
+} {11 22 1.1 2.2 hi}
+do_test select1-1.9.2 {
+ execsql {SELECT 'one', *, 'two', * FROM test1, test2}
+} {one 11 22 1.1 2.2 two 11 22 1.1 2.2}
+do_test select1-1.10 {
+ execsql {SELECT test1.f1, test2.r1 FROM test1, test2}
+} {11 1.1}
+do_test select1-1.11 {
+ execsql {SELECT test1.f1, test2.r1 FROM test2, test1}
+} {11 1.1}
+do_test select1-1.11.1 {
+ execsql {SELECT * FROM test2, test1}
+} {1.1 2.2 11 22}
+do_test select1-1.11.2 {
+ execsql {SELECT * FROM test1 AS a, test1 AS b}
+} {11 22 11 22}
+do_test select1-1.12 {
+ execsql {SELECT max(test1.f1,test2.r1), min(test1.f2,test2.r2)
+ FROM test2, test1}
+} {11 2.2}
+do_test select1-1.13 {
+ execsql {SELECT min(test1.f1,test2.r1), max(test1.f2,test2.r2)
+ FROM test1, test2}
+} {1.1 22}
+
+set long {This is a string that is too big to fit inside a NBFS buffer}
+do_test select1-2.0 {
+ execsql "
+ DROP TABLE test2;
+ DELETE FROM test1;
+ INSERT INTO test1 VALUES(11,22);
+ INSERT INTO test1 VALUES(33,44);
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES('abc',NULL);
+ INSERT INTO t3 VALUES(NULL,'xyz');
+ INSERT INTO t3 SELECT * FROM test1;
+ CREATE TABLE t4(a,b);
+ INSERT INTO t4 VALUES(NULL,'$long');
+ SELECT * FROM t3;
+ "
+} {abc {} {} xyz 11 22 33 44}
+
+# Error messges from sqliteExprCheck
+#
+do_test select1-2.1 {
+ set v [catch {execsql {SELECT count(f1,f2) FROM test1}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function count()}}
+do_test select1-2.2 {
+ set v [catch {execsql {SELECT count(f1) FROM test1}} msg]
+ lappend v $msg
+} {0 2}
+do_test select1-2.3 {
+ set v [catch {execsql {SELECT Count() FROM test1}} msg]
+ lappend v $msg
+} {0 2}
+do_test select1-2.4 {
+ set v [catch {execsql {SELECT COUNT(*) FROM test1}} msg]
+ lappend v $msg
+} {0 2}
+do_test select1-2.5 {
+ set v [catch {execsql {SELECT COUNT(*)+1 FROM test1}} msg]
+ lappend v $msg
+} {0 3}
+do_test select1-2.5.1 {
+ execsql {SELECT count(*),count(a),count(b) FROM t3}
+} {4 3 3}
+do_test select1-2.5.2 {
+ execsql {SELECT count(*),count(a),count(b) FROM t4}
+} {1 0 1}
+do_test select1-2.5.3 {
+ execsql {SELECT count(*),count(a),count(b) FROM t4 WHERE b=5}
+} {0 0 0}
+do_test select1-2.6 {
+ set v [catch {execsql {SELECT min(*) FROM test1}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function min()}}
+do_test select1-2.7 {
+ set v [catch {execsql {SELECT Min(f1) FROM test1}} msg]
+ lappend v $msg
+} {0 11}
+do_test select1-2.8 {
+ set v [catch {execsql {SELECT MIN(f1,f2) FROM test1}} msg]
+ lappend v [lsort $msg]
+} {0 {11 33}}
+do_test select1-2.8.1 {
+ execsql {SELECT coalesce(min(a),'xyzzy') FROM t3}
+} {11}
+do_test select1-2.8.2 {
+ execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3}
+} {11}
+do_test select1-2.8.3 {
+ execsql {SELECT min(b), min(b) FROM t4}
+} [list $long $long]
+do_test select1-2.9 {
+ set v [catch {execsql {SELECT MAX(*) FROM test1}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function MAX()}}
+do_test select1-2.10 {
+ set v [catch {execsql {SELECT Max(f1) FROM test1}} msg]
+ lappend v $msg
+} {0 33}
+do_test select1-2.11 {
+ set v [catch {execsql {SELECT max(f1,f2) FROM test1}} msg]
+ lappend v [lsort $msg]
+} {0 {22 44}}
+do_test select1-2.12 {
+ set v [catch {execsql {SELECT MAX(f1,f2)+1 FROM test1}} msg]
+ lappend v [lsort $msg]
+} {0 {23 45}}
+do_test select1-2.13 {
+ set v [catch {execsql {SELECT MAX(f1)+1 FROM test1}} msg]
+ lappend v $msg
+} {0 34}
+do_test select1-2.13.1 {
+ execsql {SELECT coalesce(max(a),'xyzzy') FROM t3}
+} {abc}
+do_test select1-2.13.2 {
+ execsql {SELECT max(coalesce(a,'xyzzy')) FROM t3}
+} {xyzzy}
+do_test select1-2.14 {
+ set v [catch {execsql {SELECT SUM(*) FROM test1}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function SUM()}}
+do_test select1-2.15 {
+ set v [catch {execsql {SELECT Sum(f1) FROM test1}} msg]
+ lappend v $msg
+} {0 44}
+do_test select1-2.16 {
+ set v [catch {execsql {SELECT sum(f1,f2) FROM test1}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function sum()}}
+do_test select1-2.17 {
+ set v [catch {execsql {SELECT SUM(f1)+1 FROM test1}} msg]
+ lappend v $msg
+} {0 45}
+do_test select1-2.17.1 {
+ execsql {SELECT sum(a) FROM t3}
+} {44.0}
+do_test select1-2.18 {
+ set v [catch {execsql {SELECT XYZZY(f1) FROM test1}} msg]
+ lappend v $msg
+} {1 {no such function: XYZZY}}
+do_test select1-2.19 {
+ set v [catch {execsql {SELECT SUM(min(f1,f2)) FROM test1}} msg]
+ lappend v $msg
+} {0 44}
+do_test select1-2.20 {
+ set v [catch {execsql {SELECT SUM(min(f1)) FROM test1}} msg]
+ lappend v $msg
+} {1 {misuse of aggregate function min()}}
+
+# Ticket #2526
+#
+do_test select1-2.21 {
+ catchsql {
+ SELECT min(f1) AS m FROM test1 GROUP BY f1 HAVING max(m+5)<10
+ }
+} {1 {misuse of aliased aggregate m}}
+do_test select1-2.22 {
+ catchsql {
+ SELECT coalesce(min(f1)+5,11) AS m FROM test1
+ GROUP BY f1
+ HAVING max(m+5)<10
+ }
+} {1 {misuse of aliased aggregate m}}
+do_test select1-2.23 {
+ execsql {
+ CREATE TABLE tkt2526(a,b,c PRIMARY KEY);
+ INSERT INTO tkt2526 VALUES('x','y',NULL);
+ INSERT INTO tkt2526 VALUES('x','z',NULL);
+ }
+ catchsql {
+ SELECT count(a) AS cn FROM tkt2526 GROUP BY a HAVING cn<max(cn)
+ }
+} {1 {misuse of aliased aggregate cn}}
+
+# WHERE clause expressions
+#
+do_test select1-3.1 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1<11}} msg]
+ lappend v $msg
+} {0 {}}
+do_test select1-3.2 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1<=11}} msg]
+ lappend v $msg
+} {0 11}
+do_test select1-3.3 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1=11}} msg]
+ lappend v $msg
+} {0 11}
+do_test select1-3.4 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1>=11}} msg]
+ lappend v [lsort $msg]
+} {0 {11 33}}
+do_test select1-3.5 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1>11}} msg]
+ lappend v [lsort $msg]
+} {0 33}
+do_test select1-3.6 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE f1!=11}} msg]
+ lappend v [lsort $msg]
+} {0 33}
+do_test select1-3.7 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE min(f1,f2)!=11}} msg]
+ lappend v [lsort $msg]
+} {0 33}
+do_test select1-3.8 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE max(f1,f2)!=11}} msg]
+ lappend v [lsort $msg]
+} {0 {11 33}}
+do_test select1-3.9 {
+ set v [catch {execsql {SELECT f1 FROM test1 WHERE count(f1,f2)!=11}} msg]
+ lappend v $msg
+} {1 {wrong number of arguments to function count()}}
+
+# ORDER BY expressions
+#
+do_test select1-4.1 {
+ set v [catch {execsql {SELECT f1 FROM test1 ORDER BY f1}} msg]
+ lappend v $msg
+} {0 {11 33}}
+do_test select1-4.2 {
+ set v [catch {execsql {SELECT f1 FROM test1 ORDER BY -f1}} msg]
+ lappend v $msg
+} {0 {33 11}}
+do_test select1-4.3 {
+ set v [catch {execsql {SELECT f1 FROM test1 ORDER BY min(f1,f2)}} msg]
+ lappend v $msg
+} {0 {11 33}}
+do_test select1-4.4 {
+ set v [catch {execsql {SELECT f1 FROM test1 ORDER BY min(f1)}} msg]
+ lappend v $msg
+} {1 {misuse of aggregate: min(f1)}}
+
+# The restriction not allowing constants in the ORDER BY clause
+# has been removed. See ticket #1768
+#do_test select1-4.5 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY 8.4;
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+#do_test select1-4.6 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY '8.4';
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+#do_test select1-4.7.1 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY 'xyz';
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+#do_test select1-4.7.2 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY -8.4;
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+#do_test select1-4.7.3 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY +8.4;
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+#do_test select1-4.7.4 {
+# catchsql {
+# SELECT f1 FROM test1 ORDER BY 4294967296; -- constant larger than 32 bits
+# }
+#} {1 {ORDER BY terms must not be non-integer constants}}
+
+do_test select1-4.5 {
+ execsql {
+ SELECT f1 FROM test1 ORDER BY 8.4
+ }
+} {11 33}
+do_test select1-4.6 {
+ execsql {
+ SELECT f1 FROM test1 ORDER BY '8.4'
+ }
+} {11 33}
+
+do_test select1-4.8 {
+ execsql {
+ CREATE TABLE t5(a,b);
+ INSERT INTO t5 VALUES(1,10);
+ INSERT INTO t5 VALUES(2,9);
+ SELECT * FROM t5 ORDER BY 1;
+ }
+} {1 10 2 9}
+do_test select1-4.9.1 {
+ execsql {
+ SELECT * FROM t5 ORDER BY 2;
+ }
+} {2 9 1 10}
+do_test select1-4.9.2 {
+ execsql {
+ SELECT * FROM t5 ORDER BY +2;
+ }
+} {2 9 1 10}
+do_test select1-4.10.1 {
+ catchsql {
+ SELECT * FROM t5 ORDER BY 3;
+ }
+} {1 {1st ORDER BY term out of range - should be between 1 and 2}}
+do_test select1-4.10.2 {
+ catchsql {
+ SELECT * FROM t5 ORDER BY -1;
+ }
+} {1 {1st ORDER BY term out of range - should be between 1 and 2}}
+do_test select1-4.11 {
+ execsql {
+ INSERT INTO t5 VALUES(3,10);
+ SELECT * FROM t5 ORDER BY 2, 1 DESC;
+ }
+} {2 9 3 10 1 10}
+do_test select1-4.12 {
+ execsql {
+ SELECT * FROM t5 ORDER BY 1 DESC, b;
+ }
+} {3 10 2 9 1 10}
+do_test select1-4.13 {
+ execsql {
+ SELECT * FROM t5 ORDER BY b DESC, 1;
+ }
+} {1 10 3 10 2 9}
+
+
+# ORDER BY ignored on an aggregate query
+#
+do_test select1-5.1 {
+ set v [catch {execsql {SELECT max(f1) FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 33}
+
+execsql {CREATE TABLE test2(t1 text, t2 text)}
+execsql {INSERT INTO test2 VALUES('abc','xyz')}
+
+# Check for column naming
+#
+do_test select1-6.1 {
+ set v [catch {execsql2 {SELECT f1 FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1 11 f1 33}}
+do_test select1-6.1.1 {
+ db eval {PRAGMA full_column_names=on}
+ set v [catch {execsql2 {SELECT f1 FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {test1.f1 11 test1.f1 33}}
+do_test select1-6.1.2 {
+ set v [catch {execsql2 {SELECT f1 as 'f1' FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1 11 f1 33}}
+do_test select1-6.1.3 {
+ set v [catch {execsql2 {SELECT * FROM test1 WHERE f1==11}} msg]
+ lappend v $msg
+} {0 {f1 11 f2 22}}
+do_test select1-6.1.4 {
+ set v [catch {execsql2 {SELECT DISTINCT * FROM test1 WHERE f1==11}} msg]
+ db eval {PRAGMA full_column_names=off}
+ lappend v $msg
+} {0 {f1 11 f2 22}}
+do_test select1-6.1.5 {
+ set v [catch {execsql2 {SELECT * FROM test1 WHERE f1==11}} msg]
+ lappend v $msg
+} {0 {f1 11 f2 22}}
+do_test select1-6.1.6 {
+ set v [catch {execsql2 {SELECT DISTINCT * FROM test1 WHERE f1==11}} msg]
+ lappend v $msg
+} {0 {f1 11 f2 22}}
+do_test select1-6.2 {
+ set v [catch {execsql2 {SELECT f1 as xyzzy FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {xyzzy 11 xyzzy 33}}
+do_test select1-6.3 {
+ set v [catch {execsql2 {SELECT f1 as "xyzzy" FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {xyzzy 11 xyzzy 33}}
+do_test select1-6.3.1 {
+ set v [catch {execsql2 {SELECT f1 as 'xyzzy ' FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {{xyzzy } 11 {xyzzy } 33}}
+do_test select1-6.4 {
+ set v [catch {execsql2 {SELECT f1+F2 as xyzzy FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {xyzzy 33 xyzzy 77}}
+do_test select1-6.4a {
+ set v [catch {execsql2 {SELECT f1+F2 FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1+F2 33 f1+F2 77}}
+do_test select1-6.5 {
+ set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {test1.f1+F2 33 test1.f1+F2 77}}
+do_test select1-6.5.1 {
+ execsql2 {PRAGMA full_column_names=on}
+ set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
+ execsql2 {PRAGMA full_column_names=off}
+ lappend v $msg
+} {0 {test1.f1+F2 33 test1.f1+F2 77}}
+do_test select1-6.6 {
+ set v [catch {execsql2 {SELECT test1.f1+F2, t1 FROM test1, test2
+ ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {test1.f1+F2 33 t1 abc test1.f1+F2 77 t1 abc}}
+do_test select1-6.7 {
+ set v [catch {execsql2 {SELECT A.f1, t1 FROM test1 as A, test2
+ ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1 11 t1 abc f1 33 t1 abc}}
+do_test select1-6.8 {
+ set v [catch {execsql2 {SELECT A.f1, f1 FROM test1 as A, test1 as B
+ ORDER BY f2}} msg]
+ lappend v $msg
+} {1 {ambiguous column name: f1}}
+do_test select1-6.8b {
+ set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B
+ ORDER BY f2}} msg]
+ lappend v $msg
+} {1 {ambiguous column name: f2}}
+do_test select1-6.8c {
+ set v [catch {execsql2 {SELECT A.f1, f1 FROM test1 as A, test1 as A
+ ORDER BY f2}} msg]
+ lappend v $msg
+} {1 {ambiguous column name: A.f1}}
+do_test select1-6.9.1 {
+ set v [catch {execsql {SELECT A.f1, B.f1 FROM test1 as A, test1 as B
+ ORDER BY A.f1, B.f1}} msg]
+ lappend v $msg
+} {0 {11 11 11 33 33 11 33 33}}
+do_test select1-6.9.2 {
+ set v [catch {execsql2 {SELECT A.f1, B.f1 FROM test1 as A, test1 as B
+ ORDER BY A.f1, B.f1}} msg]
+ lappend v $msg
+} {0 {f1 11 f1 11 f1 33 f1 33 f1 11 f1 11 f1 33 f1 33}}
+
+do_test select1-6.9.3 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=OFF;
+ }
+ execsql2 {
+ SELECT test1 . f1, test1 . f2 FROM test1 LIMIT 1
+ }
+} {{test1 . f1} 11 {test1 . f2} 22}
+do_test select1-6.9.4 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=ON;
+ }
+ execsql2 {
+ SELECT test1 . f1, test1 . f2 FROM test1 LIMIT 1
+ }
+} {test1.f1 11 test1.f2 22}
+do_test select1-6.9.5 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=ON;
+ }
+ execsql2 {
+ SELECT 123.45;
+ }
+} {123.45 123.45}
+do_test select1-6.9.6 {
+ execsql2 {
+ SELECT * FROM test1 a, test1 b LIMIT 1
+ }
+} {a.f1 11 a.f2 22 b.f1 11 b.f2 22}
+do_test select1-6.9.7 {
+ set x [execsql2 {
+ SELECT * FROM test1 a, (select 5, 6) LIMIT 1
+ }]
+ regsub -all {subquery_[0-9a-fA-F]+_} $x {subquery} x
+ set x
+} {a.f1 11 a.f2 22 sqlite_subquery.5 5 sqlite_subquery.6 6}
+do_test select1-6.9.8 {
+ set x [execsql2 {
+ SELECT * FROM test1 a, (select 5 AS x, 6 AS y) AS b LIMIT 1
+ }]
+ regsub -all {subquery_[0-9a-fA-F]+_} $x {subquery} x
+ set x
+} {a.f1 11 a.f2 22 b.x 5 b.y 6}
+do_test select1-6.9.9 {
+ execsql2 {
+ SELECT a.f1, b.f2 FROM test1 a, test1 b LIMIT 1
+ }
+} {test1.f1 11 test1.f2 22}
+do_test select1-6.9.10 {
+ execsql2 {
+ SELECT f1, t1 FROM test1, test2 LIMIT 1
+ }
+} {test1.f1 11 test2.t1 abc}
+do_test select1-6.9.11 {
+ db eval {
+ PRAGMA short_column_names=ON;
+ PRAGMA full_column_names=ON;
+ }
+ execsql2 {
+ SELECT a.f1, b.f2 FROM test1 a, test1 b LIMIT 1
+ }
+} {test1.f1 11 test1.f2 22}
+do_test select1-6.9.12 {
+ execsql2 {
+ SELECT f1, t1 FROM test1, test2 LIMIT 1
+ }
+} {test1.f1 11 test2.t1 abc}
+do_test select1-6.9.13 {
+ db eval {
+ PRAGMA short_column_names=ON;
+ PRAGMA full_column_names=OFF;
+ }
+ execsql2 {
+ SELECT a.f1, b.f1 FROM test1 a, test1 b LIMIT 1
+ }
+} {f1 11 f1 11}
+do_test select1-6.9.14 {
+ execsql2 {
+ SELECT f1, t1 FROM test1, test2 LIMIT 1
+ }
+} {f1 11 t1 abc}
+do_test select1-6.9.15 {
+ db eval {
+ PRAGMA short_column_names=OFF;
+ PRAGMA full_column_names=ON;
+ }
+ execsql2 {
+ SELECT a.f1, b.f1 FROM test1 a, test1 b LIMIT 1
+ }
+} {test1.f1 11 test1.f1 11}
+do_test select1-6.9.16 {
+ execsql2 {
+ SELECT f1, t1 FROM test1, test2 LIMIT 1
+ }
+} {test1.f1 11 test2.t1 abc}
+
+
+db eval {
+ PRAGMA short_column_names=ON;
+ PRAGMA full_column_names=OFF;
+}
+
+ifcapable compound {
+do_test select1-6.10 {
+ set v [catch {execsql2 {
+ SELECT f1 FROM test1 UNION SELECT f2 FROM test1
+ ORDER BY f2;
+ }} msg]
+ lappend v $msg
+} {0 {f1 11 f1 22 f1 33 f1 44}}
+do_test select1-6.11 {
+ set v [catch {execsql2 {
+ SELECT f1 FROM test1 UNION SELECT f2+100 FROM test1
+ ORDER BY f2+101;
+ }} msg]
+ lappend v $msg
+} {1 {1st ORDER BY term does not match any column in the result set}}
+
+# Ticket #2296
+ifcapable subquery&&compound {
+do_test select1-6.20 {
+ execsql {
+ CREATE TABLE t6(a TEXT, b TEXT);
+ INSERT INTO t6 VALUES('a','0');
+ INSERT INTO t6 VALUES('b','1');
+ INSERT INTO t6 VALUES('c','2');
+ INSERT INTO t6 VALUES('d','3');
+ SELECT a FROM t6 WHERE b IN
+ (SELECT b FROM t6 WHERE a<='b' UNION SELECT '3' AS x
+ ORDER BY 1 LIMIT 1)
+ }
+} {a}
+do_test select1-6.21 {
+ execsql {
+ SELECT a FROM t6 WHERE b IN
+ (SELECT b FROM t6 WHERE a<='b' UNION SELECT '3' AS x
+ ORDER BY 1 DESC LIMIT 1)
+ }
+} {d}
+do_test select1-6.22 {
+ execsql {
+ SELECT a FROM t6 WHERE b IN
+ (SELECT b FROM t6 WHERE a<='b' UNION SELECT '3' AS x
+ ORDER BY b LIMIT 2)
+ ORDER BY a;
+ }
+} {a b}
+do_test select1-6.23 {
+ execsql {
+ SELECT a FROM t6 WHERE b IN
+ (SELECT b FROM t6 WHERE a<='b' UNION SELECT '3' AS x
+ ORDER BY x DESC LIMIT 2)
+ ORDER BY a;
+ }
+} {b d}
+}
+
+} ;#ifcapable compound
+
+do_test select1-7.1 {
+ set v [catch {execsql {
+ SELECT f1 FROM test1 WHERE f2=;
+ }} msg]
+ lappend v $msg
+} {1 {near ";": syntax error}}
+ifcapable compound {
+do_test select1-7.2 {
+ set v [catch {execsql {
+ SELECT f1 FROM test1 UNION SELECT WHERE;
+ }} msg]
+ lappend v $msg
+} {1 {near "WHERE": syntax error}}
+} ;# ifcapable compound
+do_test select1-7.3 {
+ set v [catch {execsql {SELECT f1 FROM test1 as 'hi', test2 as}} msg]
+ lappend v $msg
+} {1 {near "as": syntax error}}
+do_test select1-7.4 {
+ set v [catch {execsql {
+ SELECT f1 FROM test1 ORDER BY;
+ }} msg]
+ lappend v $msg
+} {1 {near ";": syntax error}}
+do_test select1-7.5 {
+ set v [catch {execsql {
+ SELECT f1 FROM test1 ORDER BY f1 desc, f2 where;
+ }} msg]
+ lappend v $msg
+} {1 {near "where": syntax error}}
+do_test select1-7.6 {
+ set v [catch {execsql {
+ SELECT count(f1,f2 FROM test1;
+ }} msg]
+ lappend v $msg
+} {1 {near "FROM": syntax error}}
+do_test select1-7.7 {
+ set v [catch {execsql {
+ SELECT count(f1,f2+) FROM test1;
+ }} msg]
+ lappend v $msg
+} {1 {near ")": syntax error}}
+do_test select1-7.8 {
+ set v [catch {execsql {
+ SELECT f1 FROM test1 ORDER BY f2, f1+;
+ }} msg]
+ lappend v $msg
+} {1 {near ";": syntax error}}
+do_test select1-7.9 {
+ catchsql {
+ SELECT f1 FROM test1 LIMIT 5+3 OFFSET 11 ORDER BY f2;
+ }
+} {1 {near "ORDER": syntax error}}
+
+do_test select1-8.1 {
+ execsql {SELECT f1 FROM test1 WHERE 4.3+2.4 OR 1 ORDER BY f1}
+} {11 33}
+do_test select1-8.2 {
+ execsql {
+ SELECT f1 FROM test1 WHERE ('x' || f1) BETWEEN 'x10' AND 'x20'
+ ORDER BY f1
+ }
+} {11}
+do_test select1-8.3 {
+ execsql {
+ SELECT f1 FROM test1 WHERE 5-3==2
+ ORDER BY f1
+ }
+} {11 33}
+
+# TODO: This test is failing because f1 is now being loaded off the
+# disk as a vdbe integer, not a string. Hence the value of f1/(f1-11)
+# changes because of rounding. Disable the test for now.
+if 0 {
+do_test select1-8.4 {
+ execsql {
+ SELECT coalesce(f1/(f1-11),'x'),
+ coalesce(min(f1/(f1-11),5),'y'),
+ coalesce(max(f1/(f1-33),6),'z')
+ FROM test1 ORDER BY f1
+ }
+} {x y 6 1.5 1.5 z}
+}
+do_test select1-8.5 {
+ execsql {
+ SELECT min(1,2,3), -max(1,2,3)
+ FROM test1 ORDER BY f1
+ }
+} {1 -3 1 -3}
+
+
+# Check the behavior when the result set is empty
+#
+# SQLite v3 always sets r(*).
+#
+# do_test select1-9.1 {
+# catch {unset r}
+# set r(*) {}
+# db eval {SELECT * FROM test1 WHERE f1<0} r {}
+# set r(*)
+# } {}
+do_test select1-9.2 {
+ execsql {PRAGMA empty_result_callbacks=on}
+ catch {unset r}
+ set r(*) {}
+ db eval {SELECT * FROM test1 WHERE f1<0} r {}
+ set r(*)
+} {f1 f2}
+ifcapable subquery {
+ do_test select1-9.3 {
+ set r(*) {}
+ db eval {SELECT * FROM test1 WHERE f1<(select count(*) from test2)} r {}
+ set r(*)
+ } {f1 f2}
+}
+do_test select1-9.4 {
+ set r(*) {}
+ db eval {SELECT * FROM test1 ORDER BY f1} r {}
+ set r(*)
+} {f1 f2}
+do_test select1-9.5 {
+ set r(*) {}
+ db eval {SELECT * FROM test1 WHERE f1<0 ORDER BY f1} r {}
+ set r(*)
+} {f1 f2}
+unset r
+
+# Check for ORDER BY clauses that refer to an AS name in the column list
+#
+do_test select1-10.1 {
+ execsql {
+ SELECT f1 AS x FROM test1 ORDER BY x
+ }
+} {11 33}
+do_test select1-10.2 {
+ execsql {
+ SELECT f1 AS x FROM test1 ORDER BY -x
+ }
+} {33 11}
+do_test select1-10.3 {
+ execsql {
+ SELECT f1-23 AS x FROM test1 ORDER BY abs(x)
+ }
+} {10 -12}
+do_test select1-10.4 {
+ execsql {
+ SELECT f1-23 AS x FROM test1 ORDER BY -abs(x)
+ }
+} {-12 10}
+do_test select1-10.5 {
+ execsql {
+ SELECT f1-22 AS x, f2-22 as y FROM test1
+ }
+} {-11 0 11 22}
+do_test select1-10.6 {
+ execsql {
+ SELECT f1-22 AS x, f2-22 as y FROM test1 WHERE x>0 AND y<50
+ }
+} {11 22}
+do_test select1-10.7 {
+ execsql {
+ SELECT f1 COLLATE nocase AS x FROM test1 ORDER BY x
+ }
+} {11 33}
+
+# Check the ability to specify "TABLE.*" in the result set of a SELECT
+#
+do_test select1-11.1 {
+ execsql {
+ DELETE FROM t3;
+ DELETE FROM t4;
+ INSERT INTO t3 VALUES(1,2);
+ INSERT INTO t4 VALUES(3,4);
+ SELECT * FROM t3, t4;
+ }
+} {1 2 3 4}
+do_test select1-11.2.1 {
+ execsql {
+ SELECT * FROM t3, t4;
+ }
+} {1 2 3 4}
+do_test select1-11.2.2 {
+ execsql2 {
+ SELECT * FROM t3, t4;
+ }
+} {a 3 b 4 a 3 b 4}
+do_test select1-11.4.1 {
+ execsql {
+ SELECT t3.*, t4.b FROM t3, t4;
+ }
+} {1 2 4}
+do_test select1-11.4.2 {
+ execsql {
+ SELECT "t3".*, t4.b FROM t3, t4;
+ }
+} {1 2 4}
+do_test select1-11.5.1 {
+ execsql2 {
+ SELECT t3.*, t4.b FROM t3, t4;
+ }
+} {a 1 b 4 b 4}
+do_test select1-11.6 {
+ execsql2 {
+ SELECT x.*, y.b FROM t3 AS x, t4 AS y;
+ }
+} {a 1 b 4 b 4}
+do_test select1-11.7 {
+ execsql {
+ SELECT t3.b, t4.* FROM t3, t4;
+ }
+} {2 3 4}
+do_test select1-11.8 {
+ execsql2 {
+ SELECT t3.b, t4.* FROM t3, t4;
+ }
+} {b 4 a 3 b 4}
+do_test select1-11.9 {
+ execsql2 {
+ SELECT x.b, y.* FROM t3 AS x, t4 AS y;
+ }
+} {b 4 a 3 b 4}
+do_test select1-11.10 {
+ catchsql {
+ SELECT t5.* FROM t3, t4;
+ }
+} {1 {no such table: t5}}
+do_test select1-11.11 {
+ catchsql {
+ SELECT t3.* FROM t3 AS x, t4;
+ }
+} {1 {no such table: t3}}
+ifcapable subquery {
+ do_test select1-11.12 {
+ execsql2 {
+ SELECT t3.* FROM t3, (SELECT max(a), max(b) FROM t4)
+ }
+ } {a 1 b 2}
+ do_test select1-11.13 {
+ execsql2 {
+ SELECT t3.* FROM (SELECT max(a), max(b) FROM t4), t3
+ }
+ } {a 1 b 2}
+ do_test select1-11.14 {
+ execsql2 {
+ SELECT * FROM t3, (SELECT max(a), max(b) FROM t4) AS 'tx'
+ }
+ } {a 1 b 2 max(a) 3 max(b) 4}
+ do_test select1-11.15 {
+ execsql2 {
+ SELECT y.*, t3.* FROM t3, (SELECT max(a), max(b) FROM t4) AS y
+ }
+ } {max(a) 3 max(b) 4 a 1 b 2}
+}
+do_test select1-11.16 {
+ execsql2 {
+ SELECT y.* FROM t3 as y, t4 as z
+ }
+} {a 1 b 2}
+
+# Tests of SELECT statements without a FROM clause.
+#
+do_test select1-12.1 {
+ execsql2 {
+ SELECT 1+2+3
+ }
+} {1+2+3 6}
+do_test select1-12.2 {
+ execsql2 {
+ SELECT 1,'hello',2
+ }
+} {1 1 'hello' hello 2 2}
+do_test select1-12.3 {
+ execsql2 {
+ SELECT 1 AS 'a','hello' AS 'b',2 AS 'c'
+ }
+} {a 1 b hello c 2}
+do_test select1-12.4 {
+ execsql {
+ DELETE FROM t3;
+ INSERT INTO t3 VALUES(1,2);
+ }
+} {}
+
+ifcapable compound {
+do_test select1-12.5 {
+ execsql {
+ SELECT * FROM t3 UNION SELECT 3 AS 'a', 4 ORDER BY a;
+ }
+} {1 2 3 4}
+
+do_test select1-12.6 {
+ execsql {
+ SELECT 3, 4 UNION SELECT * FROM t3;
+ }
+} {1 2 3 4}
+} ;# ifcapable compound
+
+ifcapable subquery {
+ do_test select1-12.7 {
+ execsql {
+ SELECT * FROM t3 WHERE a=(SELECT 1);
+ }
+ } {1 2}
+ do_test select1-12.8 {
+ execsql {
+ SELECT * FROM t3 WHERE a=(SELECT 2);
+ }
+ } {}
+}
+
+ifcapable {compound && subquery} {
+ do_test select1-12.9 {
+ execsql2 {
+ SELECT x FROM (
+ SELECT a AS x, b AS y FROM t3 UNION SELECT a,b FROM t4 ORDER BY a,b
+ ) ORDER BY x;
+ }
+ } {x 1 x 3}
+ do_test select1-12.10 {
+ execsql2 {
+ SELECT z.x FROM (
+ SELECT a AS x,b AS y FROM t3 UNION SELECT a, b FROM t4 ORDER BY a,b
+ ) AS 'z' ORDER BY x;
+ }
+ } {x 1 x 3}
+} ;# ifcapable compound
+
+
+# Check for a VDBE stack growth problem that existed at one point.
+#
+ifcapable subquery {
+ do_test select1-13.1 {
+ execsql {
+ BEGIN;
+ create TABLE abc(a, b, c, PRIMARY KEY(a, b));
+ INSERT INTO abc VALUES(1, 1, 1);
+ }
+ for {set i 0} {$i<10} {incr i} {
+ execsql {
+ INSERT INTO abc SELECT a+(select max(a) FROM abc),
+ b+(select max(a) FROM abc), c+(select max(a) FROM abc) FROM abc;
+ }
+ }
+ execsql {COMMIT}
+
+ # This used to seg-fault when the problem existed.
+ execsql {
+ SELECT count(
+ (SELECT a FROM abc WHERE a = NULL AND b >= upper.c)
+ ) FROM abc AS upper;
+ }
+ } {0}
+}
+
+db close
+file delete -force test.db
+sqlite3 db test.db
+do_test select1-14.1 {
+ execsql {
+ SELECT * FROM sqlite_master WHERE rowid>10;
+ SELECT * FROM sqlite_master WHERE rowid=10;
+ SELECT * FROM sqlite_master WHERE rowid<10;
+ SELECT * FROM sqlite_master WHERE rowid<=10;
+ SELECT * FROM sqlite_master WHERE rowid>=10;
+ SELECT * FROM sqlite_master;
+ }
+} {}
+do_test select1-14.2 {
+ execsql {
+ SELECT 10 IN (SELECT rowid FROM sqlite_master);
+ }
+} {0}
+
+finish_test
diff --git a/third_party/sqlite/test/select2.test b/third_party/sqlite/test/select2.test
new file mode 100755
index 0000000..3bbfb34
--- /dev/null
+++ b/third_party/sqlite/test/select2.test
@@ -0,0 +1,185 @@
+# 2001 September 15
+#
+# 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 file is testing the SELECT statement.
+#
+# $Id: select2.test,v 1.27 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a table with some data
+#
+execsql {CREATE TABLE tbl1(f1 int, f2 int)}
+execsql {BEGIN}
+for {set i 0} {$i<=30} {incr i} {
+ execsql "INSERT INTO tbl1 VALUES([expr {$i%9}],[expr {$i%10}])"
+}
+execsql {COMMIT}
+
+# Do a second query inside a first.
+#
+do_test select2-1.1 {
+ set sql {SELECT DISTINCT f1 FROM tbl1 ORDER BY f1}
+ set r {}
+ catch {unset data}
+ db eval $sql data {
+ set f1 $data(f1)
+ lappend r $f1:
+ set sql2 "SELECT f2 FROM tbl1 WHERE f1=$f1 ORDER BY f2"
+ db eval $sql2 d2 {
+ lappend r $d2(f2)
+ }
+ }
+ set r
+} {0: 0 7 8 9 1: 0 1 8 9 2: 0 1 2 9 3: 0 1 2 3 4: 2 3 4 5: 3 4 5 6: 4 5 6 7: 5 6 7 8: 6 7 8}
+
+do_test select2-1.2 {
+ set sql {SELECT DISTINCT f1 FROM tbl1 WHERE f1>3 AND f1<5}
+ set r {}
+ db eval $sql data {
+ set f1 $data(f1)
+ lappend r $f1:
+ set sql2 "SELECT f2 FROM tbl1 WHERE f1=$f1 ORDER BY f2"
+ db eval $sql2 d2 {
+ lappend r $d2(f2)
+ }
+ }
+ set r
+} {4: 2 3 4}
+unset data
+
+# Create a largish table. Do this twice, once using the TCL cache and once
+# without. Compare the performance to make sure things go faster with the
+# cache turned on.
+#
+ifcapable tclvar {
+ do_test select2-2.0.1 {
+ set t1 [time {
+ execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int); BEGIN;}
+ for {set i 1} {$i<=30000} {incr i} {
+ set i2 [expr {$i*2}]
+ set i3 [expr {$i*3}]
+ db eval {INSERT INTO tbl2 VALUES($i,$i2,$i3)}
+ }
+ execsql {COMMIT}
+ }]
+ list
+ } {}
+ puts "time with cache: $::t1"
+}
+catch {execsql {DROP TABLE tbl2}}
+do_test select2-2.0.2 {
+ set t2 [time {
+ execsql {CREATE TABLE tbl2(f1 int, f2 int, f3 int); BEGIN;}
+ for {set i 1} {$i<=30000} {incr i} {
+ set i2 [expr {$i*2}]
+ set i3 [expr {$i*3}]
+ execsql "INSERT INTO tbl2 VALUES($i,$i2,$i3)"
+ }
+ execsql {COMMIT}
+ }]
+ list
+} {}
+puts "time without cache: $t2"
+ifcapable tclvar {
+ do_test select2-2.0.3 {
+ expr {[lindex $t1 0]<[lindex $t2 0]}
+ } 1
+}
+
+do_test select2-2.1 {
+ execsql {SELECT count(*) FROM tbl2}
+} {30000}
+do_test select2-2.2 {
+ execsql {SELECT count(*) FROM tbl2 WHERE f2>1000}
+} {29500}
+
+do_test select2-3.1 {
+ execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}
+} {500}
+
+do_test select2-3.2a {
+ execsql {CREATE INDEX idx1 ON tbl2(f2)}
+} {}
+do_test select2-3.2b {
+ execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}
+} {500}
+do_test select2-3.2c {
+ execsql {SELECT f1 FROM tbl2 WHERE f2=1000}
+} {500}
+do_test select2-3.2d {
+ set sqlite_search_count 0
+ execsql {SELECT * FROM tbl2 WHERE 1000=f2}
+ set sqlite_search_count
+} {3}
+do_test select2-3.2e {
+ set sqlite_search_count 0
+ execsql {SELECT * FROM tbl2 WHERE f2=1000}
+ set sqlite_search_count
+} {3}
+
+# Make sure queries run faster with an index than without
+#
+do_test select2-3.3 {
+ execsql {DROP INDEX idx1}
+ set sqlite_search_count 0
+ execsql {SELECT f1 FROM tbl2 WHERE f2==2000}
+ set sqlite_search_count
+} {29999}
+
+# Make sure we can optimize functions in the WHERE clause that
+# use fields from two or more different table. (Bug #6)
+#
+do_test select2-4.1 {
+ execsql {
+ CREATE TABLE aa(a);
+ CREATE TABLE bb(b);
+ INSERT INTO aa VALUES(1);
+ INSERT INTO aa VALUES(3);
+ INSERT INTO bb VALUES(2);
+ INSERT INTO bb VALUES(4);
+ SELECT * FROM aa, bb WHERE max(a,b)>2;
+ }
+} {1 4 3 2 3 4}
+do_test select2-4.2 {
+ execsql {
+ INSERT INTO bb VALUES(0);
+ SELECT * FROM aa, bb WHERE b;
+ }
+} {1 2 1 4 3 2 3 4}
+do_test select2-4.3 {
+ execsql {
+ SELECT * FROM aa, bb WHERE NOT b;
+ }
+} {1 0 3 0}
+do_test select2-4.4 {
+ execsql {
+ SELECT * FROM aa, bb WHERE min(a,b);
+ }
+} {1 2 1 4 3 2 3 4}
+do_test select2-4.5 {
+ execsql {
+ SELECT * FROM aa, bb WHERE NOT min(a,b);
+ }
+} {1 0 3 0}
+do_test select2-4.6 {
+ execsql {
+ SELECT * FROM aa, bb WHERE CASE WHEN a=b-1 THEN 1 END;
+ }
+} {1 2 3 4}
+do_test select2-4.7 {
+ execsql {
+ SELECT * FROM aa, bb WHERE CASE WHEN a=b-1 THEN 0 ELSE 1 END;
+ }
+} {1 4 1 0 3 2 3 0}
+
+finish_test
diff --git a/third_party/sqlite/test/select3.test b/third_party/sqlite/test/select3.test
new file mode 100755
index 0000000..b5dbef5
--- /dev/null
+++ b/third_party/sqlite/test/select3.test
@@ -0,0 +1,264 @@
+# 2001 September 15
+#
+# 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 file is testing aggregate functions and the
+# GROUP BY and HAVING clauses of SELECT statements.
+#
+# $Id: select3.test,v 1.23 2008/01/16 18:20:42 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test select3-1.0 {
+ execsql {
+ CREATE TABLE t1(n int, log int);
+ BEGIN;
+ }
+ for {set i 1} {$i<32} {incr i} {
+ for {set j 0} {(1<<$j)<$i} {incr j} {}
+ execsql "INSERT INTO t1 VALUES($i,$j)"
+ }
+ execsql {
+ COMMIT
+ }
+ execsql {SELECT DISTINCT log FROM t1 ORDER BY log}
+} {0 1 2 3 4 5}
+
+# Basic aggregate functions.
+#
+do_test select3-1.1 {
+ execsql {SELECT count(*) FROM t1}
+} {31}
+do_test select3-1.2 {
+ execsql {
+ SELECT min(n),min(log),max(n),max(log),sum(n),sum(log),avg(n),avg(log)
+ FROM t1
+ }
+} {1 0 31 5 496 124 16.0 4.0}
+do_test select3-1.3 {
+ execsql {SELECT max(n)/avg(n), max(log)/avg(log) FROM t1}
+} {1.9375 1.25}
+
+# Try some basic GROUP BY clauses
+#
+do_test select3-2.1 {
+ execsql {SELECT log, count(*) FROM t1 GROUP BY log ORDER BY log}
+} {0 1 1 1 2 2 3 4 4 8 5 15}
+do_test select3-2.2 {
+ execsql {SELECT log, min(n) FROM t1 GROUP BY log ORDER BY log}
+} {0 1 1 2 2 3 3 5 4 9 5 17}
+do_test select3-2.3.1 {
+ execsql {SELECT log, avg(n) FROM t1 GROUP BY log ORDER BY log}
+} {0 1.0 1 2.0 2 3.5 3 6.5 4 12.5 5 24.0}
+do_test select3-2.3.2 {
+ execsql {SELECT log, avg(n)+1 FROM t1 GROUP BY log ORDER BY log}
+} {0 2.0 1 3.0 2 4.5 3 7.5 4 13.5 5 25.0}
+do_test select3-2.4 {
+ execsql {SELECT log, avg(n)-min(n) FROM t1 GROUP BY log ORDER BY log}
+} {0 0.0 1 0.0 2 0.5 3 1.5 4 3.5 5 7.0}
+do_test select3-2.5 {
+ execsql {SELECT log*2+1, avg(n)-min(n) FROM t1 GROUP BY log ORDER BY log}
+} {1 0.0 3 0.0 5 0.5 7 1.5 9 3.5 11 7.0}
+do_test select3-2.6 {
+ execsql {
+ SELECT log*2+1 as x, count(*) FROM t1 GROUP BY x ORDER BY x
+ }
+} {1 1 3 1 5 2 7 4 9 8 11 15}
+do_test select3-2.7 {
+ execsql {
+ SELECT log*2+1 AS x, count(*) AS y FROM t1 GROUP BY x ORDER BY y, x
+ }
+} {1 1 3 1 5 2 7 4 9 8 11 15}
+do_test select3-2.8 {
+ execsql {
+ SELECT log*2+1 AS x, count(*) AS y FROM t1 GROUP BY x ORDER BY 10-(x+y)
+ }
+} {11 15 9 8 7 4 5 2 3 1 1 1}
+#do_test select3-2.9 {
+# catchsql {
+# SELECT log, count(*) FROM t1 GROUP BY 'x' ORDER BY log;
+# }
+#} {1 {GROUP BY terms must not be non-integer constants}}
+do_test select3-2.10 {
+ catchsql {
+ SELECT log, count(*) FROM t1 GROUP BY 0 ORDER BY log;
+ }
+} {1 {1st GROUP BY term out of range - should be between 1 and 2}}
+do_test select3-2.11 {
+ catchsql {
+ SELECT log, count(*) FROM t1 GROUP BY 3 ORDER BY log;
+ }
+} {1 {1st GROUP BY term out of range - should be between 1 and 2}}
+do_test select3-2.12 {
+ catchsql {
+ SELECT log, count(*) FROM t1 GROUP BY 1 ORDER BY log;
+ }
+} {0 {0 1 1 1 2 2 3 4 4 8 5 15}}
+
+# Cannot have an empty GROUP BY
+do_test select3-2.13 {
+ catchsql {
+ SELECT log, count(*) FROM t1 GROUP BY ORDER BY log;
+ }
+} {1 {near "ORDER": syntax error}}
+do_test select3-2.14 {
+ catchsql {
+ SELECT log, count(*) FROM t1 GROUP BY;
+ }
+} {1 {near ";": syntax error}}
+
+# Cannot have a HAVING without a GROUP BY
+#
+do_test select3-3.1 {
+ set v [catch {execsql {SELECT log, count(*) FROM t1 HAVING log>=4}} msg]
+ lappend v $msg
+} {1 {a GROUP BY clause is required before HAVING}}
+
+# Toss in some HAVING clauses
+#
+do_test select3-4.1 {
+ execsql {SELECT log, count(*) FROM t1 GROUP BY log HAVING log>=4 ORDER BY log}
+} {4 8 5 15}
+do_test select3-4.2 {
+ execsql {
+ SELECT log, count(*) FROM t1
+ GROUP BY log
+ HAVING count(*)>=4
+ ORDER BY log
+ }
+} {3 4 4 8 5 15}
+do_test select3-4.3 {
+ execsql {
+ SELECT log, count(*) FROM t1
+ GROUP BY log
+ HAVING count(*)>=4
+ ORDER BY max(n)+0
+ }
+} {3 4 4 8 5 15}
+do_test select3-4.4 {
+ execsql {
+ SELECT log AS x, count(*) AS y FROM t1
+ GROUP BY x
+ HAVING y>=4
+ ORDER BY max(n)+0
+ }
+} {3 4 4 8 5 15}
+do_test select3-4.5 {
+ execsql {
+ SELECT log AS x FROM t1
+ GROUP BY x
+ HAVING count(*)>=4
+ ORDER BY max(n)+0
+ }
+} {3 4 5}
+
+do_test select3-5.1 {
+ execsql {
+ SELECT log, count(*), avg(n), max(n+log*2) FROM t1
+ GROUP BY log
+ ORDER BY max(n+log*2)+0, avg(n)+0
+ }
+} {0 1 1.0 1 1 1 2.0 4 2 2 3.5 8 3 4 6.5 14 4 8 12.5 24 5 15 24.0 41}
+do_test select3-5.2 {
+ execsql {
+ SELECT log, count(*), avg(n), max(n+log*2) FROM t1
+ GROUP BY log
+ ORDER BY max(n+log*2)+0, min(log,avg(n))+0
+ }
+} {0 1 1.0 1 1 1 2.0 4 2 2 3.5 8 3 4 6.5 14 4 8 12.5 24 5 15 24.0 41}
+
+# Test sorting of GROUP BY results in the presence of an index
+# on the GROUP BY column.
+#
+do_test select3-6.1 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY log;
+ }
+} {0 1 1 2 2 3 3 5 4 9 5 17}
+do_test select3-6.2 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY log DESC;
+ }
+} {5 17 4 9 3 5 2 3 1 2 0 1}
+do_test select3-6.3 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY 1;
+ }
+} {0 1 1 2 2 3 3 5 4 9 5 17}
+do_test select3-6.4 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY 1 DESC;
+ }
+} {5 17 4 9 3 5 2 3 1 2 0 1}
+do_test select3-6.5 {
+ execsql {
+ CREATE INDEX i1 ON t1(log);
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY log;
+ }
+} {0 1 1 2 2 3 3 5 4 9 5 17}
+do_test select3-6.6 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY log DESC;
+ }
+} {5 17 4 9 3 5 2 3 1 2 0 1}
+do_test select3-6.7 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY 1;
+ }
+} {0 1 1 2 2 3 3 5 4 9 5 17}
+do_test select3-6.8 {
+ execsql {
+ SELECT log, min(n) FROM t1 GROUP BY log ORDER BY 1 DESC;
+ }
+} {5 17 4 9 3 5 2 3 1 2 0 1}
+
+# Sometimes an aggregate query can return no rows at all.
+#
+do_test select3-7.1 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT a, sum(b) FROM t2 WHERE b=5 GROUP BY a;
+ }
+} {}
+do_test select3-7.2 {
+ execsql {
+ SELECT a, sum(b) FROM t2 WHERE b=5;
+ }
+} {{} {}}
+
+# If a table column is of type REAL but we are storing integer values
+# in it, the values are stored as integers to take up less space. The
+# values are converted by to REAL as they are read out of the table.
+# Make sure the GROUP BY clause does this conversion correctly.
+# Ticket #2251.
+#
+do_test select3-8.1 {
+ execsql {
+ CREATE TABLE A (
+ A1 DOUBLE,
+ A2 VARCHAR COLLATE NOCASE,
+ A3 DOUBLE
+ );
+ INSERT INTO A VALUES(39136,'ABC',1201900000);
+ INSERT INTO A VALUES(39136,'ABC',1207000000);
+ SELECT typeof(sum(a3)) FROM a;
+ }
+} {real}
+do_test select3-8.2 {
+ execsql {
+ SELECT typeof(sum(a3)) FROM a GROUP BY a1;
+ }
+} {real}
+
+finish_test
diff --git a/third_party/sqlite/test/select4.test b/third_party/sqlite/test/select4.test
new file mode 100755
index 0000000..f369389
--- /dev/null
+++ b/third_party/sqlite/test/select4.test
@@ -0,0 +1,796 @@
+# 2001 September 15
+#
+# 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 file is testing UNION, INTERSECT and EXCEPT operators
+# in SELECT statements.
+#
+# $Id: select4.test,v 1.29 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Most tests in this file depend on compound-select. But there are a couple
+# right at the end that test DISTINCT, so we cannot omit the entire file.
+#
+ifcapable compound {
+
+# Build some test data
+#
+execsql {
+ CREATE TABLE t1(n int, log int);
+ BEGIN;
+}
+for {set i 1} {$i<32} {incr i} {
+ for {set j 0} {(1<<$j)<$i} {incr j} {}
+ execsql "INSERT INTO t1 VALUES($i,$j)"
+}
+execsql {
+ COMMIT;
+}
+
+do_test select4-1.0 {
+ execsql {SELECT DISTINCT log FROM t1 ORDER BY log}
+} {0 1 2 3 4 5}
+
+# Union All operator
+#
+do_test select4-1.1a {
+ lsort [execsql {SELECT DISTINCT log FROM t1}]
+} {0 1 2 3 4 5}
+do_test select4-1.1b {
+ lsort [execsql {SELECT n FROM t1 WHERE log=3}]
+} {5 6 7 8}
+do_test select4-1.1c {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }
+} {0 1 2 3 4 5 5 6 7 8}
+do_test select4-1.1d {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ SELECT * FROM t2;
+ }
+} {0 1 2 3 4 5 5 6 7 8}
+execsql {DROP TABLE t2}
+do_test select4-1.1e {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log DESC;
+ SELECT * FROM t2;
+ }
+} {8 7 6 5 5 4 3 2 1 0}
+execsql {DROP TABLE t2}
+do_test select4-1.1f {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=2
+ }
+} {0 1 2 3 4 5 3 4}
+do_test select4-1.1g {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=2;
+ SELECT * FROM t2;
+ }
+} {0 1 2 3 4 5 3 4}
+execsql {DROP TABLE t2}
+ifcapable subquery {
+ do_test select4-1.2 {
+ execsql {
+ SELECT log FROM t1 WHERE n IN
+ (SELECT DISTINCT log FROM t1 UNION ALL
+ SELECT n FROM t1 WHERE log=3)
+ ORDER BY log;
+ }
+ } {0 1 2 2 3 3 3 3}
+}
+do_test select4-1.3 {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {ORDER BY clause should come after UNION ALL not before}}
+
+# Union operator
+#
+do_test select4-2.1 {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ UNION
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }
+} {0 1 2 3 4 5 6 7 8}
+ifcapable subquery {
+ do_test select4-2.2 {
+ execsql {
+ SELECT log FROM t1 WHERE n IN
+ (SELECT DISTINCT log FROM t1 UNION
+ SELECT n FROM t1 WHERE log=3)
+ ORDER BY log;
+ }
+ } {0 1 2 2 3 3 3 3}
+}
+do_test select4-2.3 {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log
+ UNION
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {ORDER BY clause should come after UNION not before}}
+
+# Except operator
+#
+do_test select4-3.1.1 {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ EXCEPT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }
+} {0 1 2 3 4}
+do_test select4-3.1.2 {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1
+ EXCEPT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ SELECT * FROM t2;
+ }
+} {0 1 2 3 4}
+execsql {DROP TABLE t2}
+do_test select4-3.1.3 {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1
+ EXCEPT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log DESC;
+ SELECT * FROM t2;
+ }
+} {4 3 2 1 0}
+execsql {DROP TABLE t2}
+ifcapable subquery {
+ do_test select4-3.2 {
+ execsql {
+ SELECT log FROM t1 WHERE n IN
+ (SELECT DISTINCT log FROM t1 EXCEPT
+ SELECT n FROM t1 WHERE log=3)
+ ORDER BY log;
+ }
+ } {0 1 2 2}
+}
+do_test select4-3.3 {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log
+ EXCEPT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {ORDER BY clause should come after EXCEPT not before}}
+
+# Intersect operator
+#
+do_test select4-4.1.1 {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }
+} {5}
+
+do_test select4-4.1.2 {
+ execsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT 6
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY t1.log;
+ }
+} {5 6}
+
+do_test select4-4.1.3 {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1 UNION ALL SELECT 6
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ SELECT * FROM t2;
+ }
+} {5 6}
+execsql {DROP TABLE t2}
+do_test select4-4.1.4 {
+ execsql {
+ CREATE TABLE t2 AS
+ SELECT DISTINCT log FROM t1 UNION ALL SELECT 6
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log DESC;
+ SELECT * FROM t2;
+ }
+} {6 5}
+execsql {DROP TABLE t2}
+ifcapable subquery {
+ do_test select4-4.2 {
+ execsql {
+ SELECT log FROM t1 WHERE n IN
+ (SELECT DISTINCT log FROM t1 INTERSECT
+ SELECT n FROM t1 WHERE log=3)
+ ORDER BY log;
+ }
+ } {3}
+}
+do_test select4-4.3 {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {ORDER BY clause should come after INTERSECT not before}}
+
+# Various error messages while processing UNION or INTERSECT
+#
+do_test select4-5.1 {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t2
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {no such table: t2}}
+do_test select4-5.2 {
+ set v [catch {execsql {
+ SELECT DISTINCT log AS "xyzzy" FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY xyzzy;
+ }} msg]
+ lappend v $msg
+} {0 {0 1 2 3 4 5 5 6 7 8}}
+do_test select4-5.2b {
+ set v [catch {execsql {
+ SELECT DISTINCT log AS xyzzy FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY "xyzzy";
+ }} msg]
+ lappend v $msg
+} {0 {0 1 2 3 4 5 5 6 7 8}}
+do_test select4-5.2c {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY "xyzzy";
+ }} msg]
+ lappend v $msg
+} {1 {1st ORDER BY term does not match any column in the result set}}
+do_test select4-5.2d {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1
+ INTERSECT
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY "xyzzy";
+ }} msg]
+ lappend v $msg
+} {1 {1st ORDER BY term does not match any column in the result set}}
+do_test select4-5.2e {
+ set v [catch {execsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY n;
+ }} msg]
+ lappend v $msg
+} {0 {0 1 2 3 4 5 5 6 7 8}}
+do_test select4-5.2f {
+ catchsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }
+} {0 {0 1 2 3 4 5 5 6 7 8}}
+do_test select4-5.2g {
+ catchsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY 1;
+ }
+} {0 {0 1 2 3 4 5 5 6 7 8}}
+do_test select4-5.2h {
+ catchsql {
+ SELECT DISTINCT log FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY 2;
+ }
+} {1 {1st ORDER BY term out of range - should be between 1 and 1}}
+do_test select4-5.2i {
+ catchsql {
+ SELECT DISTINCT 1, log FROM t1
+ UNION ALL
+ SELECT 2, n FROM t1 WHERE log=3
+ ORDER BY 2, 1;
+ }
+} {0 {1 0 1 1 1 2 1 3 1 4 1 5 2 5 2 6 2 7 2 8}}
+do_test select4-5.2j {
+ catchsql {
+ SELECT DISTINCT 1, log FROM t1
+ UNION ALL
+ SELECT 2, n FROM t1 WHERE log=3
+ ORDER BY 1, 2 DESC;
+ }
+} {0 {1 5 1 4 1 3 1 2 1 1 1 0 2 8 2 7 2 6 2 5}}
+do_test select4-5.2k {
+ catchsql {
+ SELECT DISTINCT 1, log FROM t1
+ UNION ALL
+ SELECT 2, n FROM t1 WHERE log=3
+ ORDER BY n, 1;
+ }
+} {0 {1 0 1 1 1 2 1 3 1 4 1 5 2 5 2 6 2 7 2 8}}
+do_test select4-5.3 {
+ set v [catch {execsql {
+ SELECT DISTINCT log, n FROM t1
+ UNION ALL
+ SELECT n FROM t1 WHERE log=3
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+do_test select4-5.4 {
+ set v [catch {execsql {
+ SELECT log FROM t1 WHERE n=2
+ UNION ALL
+ SELECT log FROM t1 WHERE n=3
+ UNION ALL
+ SELECT log FROM t1 WHERE n=4
+ UNION ALL
+ SELECT log FROM t1 WHERE n=5
+ ORDER BY log;
+ }} msg]
+ lappend v $msg
+} {0 {1 2 2 3}}
+
+do_test select4-6.1 {
+ execsql {
+ SELECT log, count(*) as cnt FROM t1 GROUP BY log
+ UNION
+ SELECT log, n FROM t1 WHERE n=7
+ ORDER BY cnt, log;
+ }
+} {0 1 1 1 2 2 3 4 3 7 4 8 5 15}
+do_test select4-6.2 {
+ execsql {
+ SELECT log, count(*) FROM t1 GROUP BY log
+ UNION
+ SELECT log, n FROM t1 WHERE n=7
+ ORDER BY count(*), log;
+ }
+} {0 1 1 1 2 2 3 4 3 7 4 8 5 15}
+
+# NULLs are indistinct for the UNION operator.
+# Make sure the UNION operator recognizes this
+#
+do_test select4-6.3 {
+ execsql {
+ SELECT NULL UNION SELECT NULL UNION
+ SELECT 1 UNION SELECT 2 AS 'x'
+ ORDER BY x;
+ }
+} {{} 1 2}
+do_test select4-6.3.1 {
+ execsql {
+ SELECT NULL UNION ALL SELECT NULL UNION ALL
+ SELECT 1 UNION ALL SELECT 2 AS 'x'
+ ORDER BY x;
+ }
+} {{} {} 1 2}
+
+# Make sure the DISTINCT keyword treats NULLs as indistinct.
+#
+ifcapable subquery {
+ do_test select4-6.4 {
+ execsql {
+ SELECT * FROM (
+ SELECT NULL, 1 UNION ALL SELECT NULL, 1
+ );
+ }
+ } {{} 1 {} 1}
+ do_test select4-6.5 {
+ execsql {
+ SELECT DISTINCT * FROM (
+ SELECT NULL, 1 UNION ALL SELECT NULL, 1
+ );
+ }
+ } {{} 1}
+ do_test select4-6.6 {
+ execsql {
+ SELECT DISTINCT * FROM (
+ SELECT 1,2 UNION ALL SELECT 1,2
+ );
+ }
+ } {1 2}
+}
+
+# Test distinctness of NULL in other ways.
+#
+do_test select4-6.7 {
+ execsql {
+ SELECT NULL EXCEPT SELECT NULL
+ }
+} {}
+
+
+# Make sure column names are correct when a compound select appears as
+# an expression in the WHERE clause.
+#
+do_test select4-7.1 {
+ execsql {
+ CREATE TABLE t2 AS SELECT log AS 'x', count(*) AS 'y' FROM t1 GROUP BY log;
+ SELECT * FROM t2 ORDER BY x;
+ }
+} {0 1 1 1 2 2 3 4 4 8 5 15}
+ifcapable subquery {
+ do_test select4-7.2 {
+ execsql2 {
+ SELECT * FROM t1 WHERE n IN (SELECT n FROM t1 INTERSECT SELECT x FROM t2)
+ ORDER BY n
+ }
+ } {n 1 log 0 n 2 log 1 n 3 log 2 n 4 log 2 n 5 log 3}
+ do_test select4-7.3 {
+ execsql2 {
+ SELECT * FROM t1 WHERE n IN (SELECT n FROM t1 EXCEPT SELECT x FROM t2)
+ ORDER BY n LIMIT 2
+ }
+ } {n 6 log 3 n 7 log 3}
+ do_test select4-7.4 {
+ execsql2 {
+ SELECT * FROM t1 WHERE n IN (SELECT n FROM t1 UNION SELECT x FROM t2)
+ ORDER BY n LIMIT 2
+ }
+ } {n 1 log 0 n 2 log 1}
+} ;# ifcapable subquery
+
+} ;# ifcapable compound
+
+# Make sure DISTINCT works appropriately on TEXT and NUMERIC columns.
+do_test select4-8.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(a text, b float, c text);
+ INSERT INTO t3 VALUES(1, 1.1, '1.1');
+ INSERT INTO t3 VALUES(2, 1.10, '1.10');
+ INSERT INTO t3 VALUES(3, 1.10, '1.1');
+ INSERT INTO t3 VALUES(4, 1.1, '1.10');
+ INSERT INTO t3 VALUES(5, 1.2, '1.2');
+ INSERT INTO t3 VALUES(6, 1.3, '1.3');
+ COMMIT;
+ }
+ execsql {
+ SELECT DISTINCT b FROM t3 ORDER BY c;
+ }
+} {1.1 1.2 1.3}
+do_test select4-8.2 {
+ execsql {
+ SELECT DISTINCT c FROM t3 ORDER BY c;
+ }
+} {1.1 1.10 1.2 1.3}
+
+# Make sure the names of columns are taken from the right-most subquery
+# right in a compound query. Ticket #1721
+#
+ifcapable compound {
+
+do_test select4-9.1 {
+ execsql2 {
+ SELECT x, y FROM t2 UNION SELECT a, b FROM t3 ORDER BY x LIMIT 1
+ }
+} {x 0 y 1}
+do_test select4-9.2 {
+ execsql2 {
+ SELECT x, y FROM t2 UNION ALL SELECT a, b FROM t3 ORDER BY x LIMIT 1
+ }
+} {x 0 y 1}
+do_test select4-9.3 {
+ execsql2 {
+ SELECT x, y FROM t2 EXCEPT SELECT a, b FROM t3 ORDER BY x LIMIT 1
+ }
+} {x 0 y 1}
+do_test select4-9.4 {
+ execsql2 {
+ SELECT x, y FROM t2 INTERSECT SELECT 0 AS a, 1 AS b;
+ }
+} {x 0 y 1}
+do_test select4-9.5 {
+ execsql2 {
+ SELECT 0 AS x, 1 AS y
+ UNION
+ SELECT 2 AS p, 3 AS q
+ UNION
+ SELECT 4 AS a, 5 AS b
+ ORDER BY x LIMIT 1
+ }
+} {x 0 y 1}
+
+ifcapable subquery {
+do_test select4-9.6 {
+ execsql2 {
+ SELECT * FROM (
+ SELECT 0 AS x, 1 AS y
+ UNION
+ SELECT 2 AS p, 3 AS q
+ UNION
+ SELECT 4 AS a, 5 AS b
+ ) ORDER BY 1 LIMIT 1;
+ }
+} {x 0 y 1}
+do_test select4-9.7 {
+ execsql2 {
+ SELECT * FROM (
+ SELECT 0 AS x, 1 AS y
+ UNION
+ SELECT 2 AS p, 3 AS q
+ UNION
+ SELECT 4 AS a, 5 AS b
+ ) ORDER BY x LIMIT 1;
+ }
+} {x 0 y 1}
+} ;# ifcapable subquery
+
+do_test select4-9.8 {
+ execsql {
+ SELECT 0 AS x, 1 AS y
+ UNION
+ SELECT 2 AS y, -3 AS x
+ ORDER BY x LIMIT 1;
+ }
+} {0 1}
+
+do_test select4-9.9.1 {
+ execsql2 {
+ SELECT 1 AS a, 2 AS b UNION ALL SELECT 3 AS b, 4 AS a
+ }
+} {a 1 b 2 a 3 b 4}
+
+ifcapable subquery {
+do_test select4-9.9.2 {
+ execsql2 {
+ SELECT * FROM (SELECT 1 AS a, 2 AS b UNION ALL SELECT 3 AS b, 4 AS a)
+ WHERE b=3
+ }
+} {}
+do_test select4-9.10 {
+ execsql2 {
+ SELECT * FROM (SELECT 1 AS a, 2 AS b UNION ALL SELECT 3 AS b, 4 AS a)
+ WHERE b=2
+ }
+} {a 1 b 2}
+do_test select4-9.11 {
+ execsql2 {
+ SELECT * FROM (SELECT 1 AS a, 2 AS b UNION ALL SELECT 3 AS e, 4 AS b)
+ WHERE b=2
+ }
+} {a 1 b 2}
+do_test select4-9.12 {
+ execsql2 {
+ SELECT * FROM (SELECT 1 AS a, 2 AS b UNION ALL SELECT 3 AS e, 4 AS b)
+ WHERE b>0
+ }
+} {a 1 b 2 a 3 b 4}
+} ;# ifcapable subquery
+
+# Try combining DISTINCT, LIMIT, and OFFSET. Make sure they all work
+# together.
+#
+do_test select4-10.1 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log
+ }
+} {0 1 2 3 4 5}
+do_test select4-10.2 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 4
+ }
+} {0 1 2 3}
+do_test select4-10.3 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 0
+ }
+} {}
+do_test select4-10.4 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT -1
+ }
+} {0 1 2 3 4 5}
+do_test select4-10.5 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT -1 OFFSET 2
+ }
+} {2 3 4 5}
+do_test select4-10.6 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 3 OFFSET 2
+ }
+} {2 3 4}
+do_test select4-10.7 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY +log LIMIT 3 OFFSET 20
+ }
+} {}
+do_test select4-10.8 {
+ execsql {
+ SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 0 OFFSET 3
+ }
+} {}
+do_test select4-10.9 {
+ execsql {
+ SELECT DISTINCT max(n), log FROM t1 ORDER BY +log; -- LIMIT 2 OFFSET 1
+ }
+} {31 5}
+
+# Make sure compound SELECTs with wildly different numbers of columns
+# do not cause assertion faults due to register allocation issues.
+#
+do_test select4-11.1 {
+ catchsql {
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ UNION
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
+do_test select4-11.2 {
+ catchsql {
+ SELECT x FROM t2
+ UNION
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
+do_test select4-11.3 {
+ catchsql {
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+do_test select4-11.4 {
+ catchsql {
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+do_test select4-11.5 {
+ catchsql {
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}}
+do_test select4-11.6 {
+ catchsql {
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ }
+} {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}}
+do_test select4-11.7 {
+ catchsql {
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ INTERSECT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
+do_test select4-11.8 {
+ catchsql {
+ SELECT x FROM t2
+ INTERSECT
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ }
+} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
+
+do_test select4-11.11 {
+ catchsql {
+ SELECT x FROM t2
+ UNION
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ INTERSECT
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ }
+} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
+do_test select4-11.12 {
+ catchsql {
+ SELECT x FROM t2
+ UNION
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}}
+do_test select4-11.13 {
+ catchsql {
+ SELECT x FROM t2
+ UNION
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+do_test select4-11.14 {
+ catchsql {
+ SELECT x FROM t2
+ UNION
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ UNION
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
+do_test select4-11.15 {
+ catchsql {
+ SELECT x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x FROM t2
+ UNION
+ SELECT x FROM t2
+ INTERSECT
+ SELECT x FROM t2
+ UNION ALL
+ SELECT x FROM t2
+ EXCEPT
+ SELECT x FROM t2
+ }
+} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
+
+} ;# ifcapable compound
+
+finish_test
diff --git a/third_party/sqlite/test/select5.test b/third_party/sqlite/test/select5.test
new file mode 100755
index 0000000..08b7309
--- /dev/null
+++ b/third_party/sqlite/test/select5.test
@@ -0,0 +1,201 @@
+# 2001 September 15
+#
+# 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 file is testing aggregate functions and the
+# GROUP BY and HAVING clauses of SELECT statements.
+#
+# $Id: select5.test,v 1.18 2008/08/02 03:50:40 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+execsql {
+ CREATE TABLE t1(x int, y int);
+ BEGIN;
+}
+for {set i 1} {$i<32} {incr i} {
+ for {set j 0} {(1<<$j)<$i} {incr j} {}
+ execsql "INSERT INTO t1 VALUES([expr {32-$i}],[expr {10-$j}])"
+}
+execsql {
+ COMMIT
+}
+
+do_test select5-1.0 {
+ execsql {SELECT DISTINCT y FROM t1 ORDER BY y}
+} {5 6 7 8 9 10}
+
+# Sort by an aggregate function.
+#
+do_test select5-1.1 {
+ execsql {SELECT y, count(*) FROM t1 GROUP BY y ORDER BY y}
+} {5 15 6 8 7 4 8 2 9 1 10 1}
+do_test select5-1.2 {
+ execsql {SELECT y, count(*) FROM t1 GROUP BY y ORDER BY count(*), y}
+} {9 1 10 1 8 2 7 4 6 8 5 15}
+do_test select5-1.3 {
+ execsql {SELECT count(*), y FROM t1 GROUP BY y ORDER BY count(*), y}
+} {1 9 1 10 2 8 4 7 8 6 15 5}
+
+# Some error messages associated with aggregates and GROUP BY
+#
+do_test select5-2.1.1 {
+ catchsql {
+ SELECT y, count(*) FROM t1 GROUP BY z ORDER BY y
+ }
+} {1 {no such column: z}}
+do_test select5-2.1.2 {
+ catchsql {
+ SELECT y, count(*) FROM t1 GROUP BY temp.t1.y ORDER BY y
+ }
+} {1 {no such column: temp.t1.y}}
+do_test select5-2.2 {
+ set v [catch {execsql {
+ SELECT y, count(*) FROM t1 GROUP BY z(y) ORDER BY y
+ }} msg]
+ lappend v $msg
+} {1 {no such function: z}}
+do_test select5-2.3 {
+ set v [catch {execsql {
+ SELECT y, count(*) FROM t1 GROUP BY y HAVING count(*)<3 ORDER BY y
+ }} msg]
+ lappend v $msg
+} {0 {8 2 9 1 10 1}}
+do_test select5-2.4 {
+ set v [catch {execsql {
+ SELECT y, count(*) FROM t1 GROUP BY y HAVING z(y)<3 ORDER BY y
+ }} msg]
+ lappend v $msg
+} {1 {no such function: z}}
+do_test select5-2.5 {
+ set v [catch {execsql {
+ SELECT y, count(*) FROM t1 GROUP BY y HAVING count(*)<z ORDER BY y
+ }} msg]
+ lappend v $msg
+} {1 {no such column: z}}
+
+# Get the Agg function to rehash in vdbe.c
+#
+do_test select5-3.1 {
+ execsql {
+ SELECT x, count(*), avg(y) FROM t1 GROUP BY x HAVING x<4 ORDER BY x
+ }
+} {1 1 5.0 2 1 5.0 3 1 5.0}
+
+# Run various aggregate functions when the count is zero.
+#
+do_test select5-4.1 {
+ execsql {
+ SELECT avg(x) FROM t1 WHERE x>100
+ }
+} {{}}
+do_test select5-4.2 {
+ execsql {
+ SELECT count(x) FROM t1 WHERE x>100
+ }
+} {0}
+do_test select5-4.3 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE x>100
+ }
+} {{}}
+do_test select5-4.4 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE x>100
+ }
+} {{}}
+do_test select5-4.5 {
+ execsql {
+ SELECT sum(x) FROM t1 WHERE x>100
+ }
+} {{}}
+
+# Some tests for queries with a GROUP BY clause but no aggregate functions.
+#
+# Note: The query in test cases 5.1 through 5.5 are not legal SQL. So if the
+# implementation changes in the future and it returns different results,
+# this is not such a big deal.
+#
+do_test select5-5.1 {
+ execsql {
+ CREATE TABLE t2(a, b, c);
+ INSERT INTO t2 VALUES(1, 2, 3);
+ INSERT INTO t2 VALUES(1, 4, 5);
+ INSERT INTO t2 VALUES(6, 4, 7);
+ CREATE INDEX t2_idx ON t2(a);
+ }
+} {}
+do_test select5-5.2 {
+ execsql {
+ SELECT a FROM t2 GROUP BY a;
+ }
+} {1 6}
+do_test select5-5.3 {
+ execsql {
+ SELECT a FROM t2 WHERE a>2 GROUP BY a;
+ }
+} {6}
+do_test select5-5.4 {
+ execsql {
+ SELECT a, b FROM t2 GROUP BY a, b;
+ }
+} {1 2 1 4 6 4}
+do_test select5-5.5 {
+ execsql {
+ SELECT a, b FROM t2 GROUP BY a;
+ }
+} {1 4 6 4}
+
+# Test rendering of columns for the GROUP BY clause.
+#
+do_test select5-5.11 {
+breakpoint
+ execsql {
+ SELECT max(c), b*a, b, a FROM t2 GROUP BY b*a, b, a
+ }
+} {3 2 2 1 5 4 4 1 7 24 4 6}
+
+# NULL compare equal to each other for the purposes of processing
+# the GROUP BY clause.
+#
+do_test select5-6.1 {
+ execsql {
+ CREATE TABLE t3(x,y);
+ INSERT INTO t3 VALUES(1,NULL);
+ INSERT INTO t3 VALUES(2,NULL);
+ INSERT INTO t3 VALUES(3,4);
+ SELECT count(x), y FROM t3 GROUP BY y ORDER BY 1
+ }
+} {1 4 2 {}}
+do_test select5-6.2 {
+ execsql {
+ CREATE TABLE t4(x,y,z);
+ INSERT INTO t4 VALUES(1,2,NULL);
+ INSERT INTO t4 VALUES(2,3,NULL);
+ INSERT INTO t4 VALUES(3,NULL,5);
+ INSERT INTO t4 VALUES(4,NULL,6);
+ INSERT INTO t4 VALUES(4,NULL,6);
+ INSERT INTO t4 VALUES(5,NULL,NULL);
+ INSERT INTO t4 VALUES(5,NULL,NULL);
+ INSERT INTO t4 VALUES(6,7,8);
+ SELECT max(x), count(x), y, z FROM t4 GROUP BY y, z ORDER BY 1
+ }
+} {1 1 2 {} 2 1 3 {} 3 1 {} 5 4 2 {} 6 5 2 {} {} 6 1 7 8}
+
+do_test select5.7.2 {
+ execsql {
+ SELECT count(*), count(x) as cnt FROM t4 GROUP BY y ORDER BY cnt;
+ }
+} {1 1 1 1 1 1 5 5}
+
+finish_test
diff --git a/third_party/sqlite/test/select6.test b/third_party/sqlite/test/select6.test
new file mode 100755
index 0000000..7223b5e
--- /dev/null
+++ b/third_party/sqlite/test/select6.test
@@ -0,0 +1,506 @@
+# 2001 September 15
+#
+# 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 file is testing SELECT statements that contain
+# subqueries in their FROM clause.
+#
+# $Id: select6.test,v 1.27 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Omit this whole file if the library is build without subquery support.
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+do_test select6-1.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1,1);
+ INSERT INTO t1 VALUES(2,2);
+ INSERT INTO t1 VALUES(3,2);
+ INSERT INTO t1 VALUES(4,3);
+ INSERT INTO t1 VALUES(5,3);
+ INSERT INTO t1 VALUES(6,3);
+ INSERT INTO t1 VALUES(7,3);
+ INSERT INTO t1 VALUES(8,4);
+ INSERT INTO t1 VALUES(9,4);
+ INSERT INTO t1 VALUES(10,4);
+ INSERT INTO t1 VALUES(11,4);
+ INSERT INTO t1 VALUES(12,4);
+ INSERT INTO t1 VALUES(13,4);
+ INSERT INTO t1 VALUES(14,4);
+ INSERT INTO t1 VALUES(15,4);
+ INSERT INTO t1 VALUES(16,5);
+ INSERT INTO t1 VALUES(17,5);
+ INSERT INTO t1 VALUES(18,5);
+ INSERT INTO t1 VALUES(19,5);
+ INSERT INTO t1 VALUES(20,5);
+ COMMIT;
+ SELECT DISTINCT y FROM t1 ORDER BY y;
+ }
+} {1 2 3 4 5}
+
+do_test select6-1.1 {
+ execsql2 {SELECT * FROM (SELECT x, y FROM t1 WHERE x<2)}
+} {x 1 y 1}
+do_test select6-1.2 {
+ execsql {SELECT count(*) FROM (SELECT y FROM t1)}
+} {20}
+do_test select6-1.3 {
+ execsql {SELECT count(*) FROM (SELECT DISTINCT y FROM t1)}
+} {5}
+do_test select6-1.4 {
+ execsql {SELECT count(*) FROM (SELECT DISTINCT * FROM (SELECT y FROM t1))}
+} {5}
+do_test select6-1.5 {
+ execsql {SELECT count(*) FROM (SELECT * FROM (SELECT DISTINCT y FROM t1))}
+} {5}
+
+do_test select6-1.6 {
+ execsql {
+ SELECT *
+ FROM (SELECT count(*),y FROM t1 GROUP BY y) AS a,
+ (SELECT max(x),y FROM t1 GROUP BY y) as b
+ WHERE a.y=b.y ORDER BY a.y
+ }
+} {1 1 1 1 2 2 3 2 4 3 7 3 8 4 15 4 5 5 20 5}
+do_test select6-1.7 {
+ execsql {
+ SELECT a.y, a.[count(*)], [max(x)], [count(*)]
+ FROM (SELECT count(*),y FROM t1 GROUP BY y) AS a,
+ (SELECT max(x),y FROM t1 GROUP BY y) as b
+ WHERE a.y=b.y ORDER BY a.y
+ }
+} {1 1 1 1 2 2 3 2 3 4 7 4 4 8 15 8 5 5 20 5}
+do_test select6-1.8 {
+ execsql {
+ SELECT q, p, r
+ FROM (SELECT count(*) as p , y as q FROM t1 GROUP BY y) AS a,
+ (SELECT max(x) as r, y as s FROM t1 GROUP BY y) as b
+ WHERE q=s ORDER BY s
+ }
+} {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20}
+do_test select6-1.9 {
+ execsql {
+ SELECT q, p, r, b.[min(x)+y]
+ FROM (SELECT count(*) as p , y as q FROM t1 GROUP BY y) AS a,
+ (SELECT max(x) as r, y as s, min(x)+y FROM t1 GROUP BY y) as b
+ WHERE q=s ORDER BY s
+ }
+} {1 1 1 2 2 2 3 4 3 4 7 7 4 8 15 12 5 5 20 21}
+
+do_test select6-2.0 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t2 SELECT * FROM t1;
+ SELECT DISTINCT b FROM t2 ORDER BY b;
+ }
+} {1 2 3 4 5}
+do_test select6-2.1 {
+ execsql2 {SELECT * FROM (SELECT a, b FROM t2 WHERE a<2)}
+} {a 1 b 1}
+do_test select6-2.2 {
+ execsql {SELECT count(*) FROM (SELECT b FROM t2)}
+} {20}
+do_test select6-2.3 {
+ execsql {SELECT count(*) FROM (SELECT DISTINCT b FROM t2)}
+} {5}
+do_test select6-2.4 {
+ execsql {SELECT count(*) FROM (SELECT DISTINCT * FROM (SELECT b FROM t2))}
+} {5}
+do_test select6-2.5 {
+ execsql {SELECT count(*) FROM (SELECT * FROM (SELECT DISTINCT b FROM t2))}
+} {5}
+
+do_test select6-2.6 {
+ execsql {
+ SELECT *
+ FROM (SELECT count(*),b FROM t2 GROUP BY b) AS a,
+ (SELECT max(a),b FROM t2 GROUP BY b) as b
+ WHERE a.b=b.b ORDER BY a.b
+ }
+} {1 1 1 1 2 2 3 2 4 3 7 3 8 4 15 4 5 5 20 5}
+do_test select6-2.7 {
+ execsql {
+ SELECT a.b, a.[count(*)], [max(a)], [count(*)]
+ FROM (SELECT count(*),b FROM t2 GROUP BY b) AS a,
+ (SELECT max(a),b FROM t2 GROUP BY b) as b
+ WHERE a.b=b.b ORDER BY a.b
+ }
+} {1 1 1 1 2 2 3 2 3 4 7 4 4 8 15 8 5 5 20 5}
+do_test select6-2.8 {
+ execsql {
+ SELECT q, p, r
+ FROM (SELECT count(*) as p , b as q FROM t2 GROUP BY b) AS a,
+ (SELECT max(a) as r, b as s FROM t2 GROUP BY b) as b
+ WHERE q=s ORDER BY s
+ }
+} {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20}
+do_test select6-2.9 {
+ execsql {
+ SELECT a.q, a.p, b.r
+ FROM (SELECT count(*) as p , b as q FROM t2 GROUP BY q) AS a,
+ (SELECT max(a) as r, b as s FROM t2 GROUP BY s) as b
+ WHERE a.q=b.s ORDER BY a.q
+ }
+} {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20}
+
+do_test select6-3.1 {
+ execsql2 {
+ SELECT * FROM (SELECT * FROM (SELECT * FROM t1 WHERE x=3));
+ }
+} {x 3 y 2}
+do_test select6-3.2 {
+ execsql {
+ SELECT * FROM
+ (SELECT a.q, a.p, b.r
+ FROM (SELECT count(*) as p , b as q FROM t2 GROUP BY q) AS a,
+ (SELECT max(a) as r, b as s FROM t2 GROUP BY s) as b
+ WHERE a.q=b.s ORDER BY a.q)
+ ORDER BY "a.q"
+ }
+} {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20}
+do_test select6-3.3 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
+ }
+} {10.5 3.7 14.2}
+do_test select6-3.4 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
+ }
+} {11.5 4.0 15.5}
+do_test select6-3.5 {
+ execsql {
+ SELECT x,y,x+y FROM (SELECT avg(a) as 'x', avg(b) as 'y' FROM t2 WHERE a=4)
+ }
+} {4.0 3.0 7.0}
+do_test select6-3.6 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
+ WHERE a>10
+ }
+} {10.5 3.7 14.2}
+do_test select6-3.7 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1)
+ WHERE a<10
+ }
+} {}
+do_test select6-3.8 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
+ WHERE a>10
+ }
+} {11.5 4.0 15.5}
+do_test select6-3.9 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1 WHERE y=4)
+ WHERE a<10
+ }
+} {}
+do_test select6-3.10 {
+ execsql {
+ SELECT a,b,a+b FROM (SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b)
+ ORDER BY a
+ }
+} {1.0 1 2.0 2.5 2 4.5 5.5 3 8.5 11.5 4 15.5 18.0 5 23.0}
+do_test select6-3.11 {
+ execsql {
+ SELECT a,b,a+b FROM
+ (SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b)
+ WHERE b<4 ORDER BY a
+ }
+} {1.0 1 2.0 2.5 2 4.5 5.5 3 8.5}
+do_test select6-3.12 {
+ execsql {
+ SELECT a,b,a+b FROM
+ (SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b HAVING a>1)
+ WHERE b<4 ORDER BY a
+ }
+} {2.5 2 4.5 5.5 3 8.5}
+do_test select6-3.13 {
+ execsql {
+ SELECT a,b,a+b FROM
+ (SELECT avg(x) as 'a', y as 'b' FROM t1 GROUP BY b HAVING a>1)
+ ORDER BY a
+ }
+} {2.5 2 4.5 5.5 3 8.5 11.5 4 15.5 18.0 5 23.0}
+do_test select6-3.14 {
+ execsql {
+ SELECT [count(*)],y FROM (SELECT count(*), y FROM t1 GROUP BY y)
+ ORDER BY [count(*)]
+ }
+} {1 1 2 2 4 3 5 5 8 4}
+do_test select6-3.15 {
+ execsql {
+ SELECT [count(*)],y FROM (SELECT count(*), y FROM t1 GROUP BY y)
+ ORDER BY y
+ }
+} {1 1 2 2 4 3 8 4 5 5}
+
+do_test select6-4.1 {
+ execsql {
+ SELECT a,b,c FROM
+ (SELECT x AS 'a', y AS 'b', x+y AS 'c' FROM t1 WHERE y=4)
+ WHERE a<10 ORDER BY a;
+ }
+} {8 4 12 9 4 13}
+do_test select6-4.2 {
+ execsql {
+ SELECT y FROM (SELECT DISTINCT y FROM t1) WHERE y<5 ORDER BY y
+ }
+} {1 2 3 4}
+do_test select6-4.3 {
+ execsql {
+ SELECT DISTINCT y FROM (SELECT y FROM t1) WHERE y<5 ORDER BY y
+ }
+} {1 2 3 4}
+do_test select6-4.4 {
+ execsql {
+ SELECT avg(y) FROM (SELECT DISTINCT y FROM t1) WHERE y<5 ORDER BY y
+ }
+} {2.5}
+do_test select6-4.5 {
+ execsql {
+ SELECT avg(y) FROM (SELECT DISTINCT y FROM t1 WHERE y<5) ORDER BY y
+ }
+} {2.5}
+
+do_test select6-5.1 {
+ execsql {
+ SELECT a,x,b FROM
+ (SELECT x+3 AS 'a', x FROM t1 WHERE y=3) AS 'p',
+ (SELECT x AS 'b' FROM t1 WHERE y=4) AS 'q'
+ WHERE a=b
+ ORDER BY a
+ }
+} {8 5 8 9 6 9 10 7 10}
+do_test select6-5.2 {
+ execsql {
+ SELECT a,x,b FROM
+ (SELECT x+3 AS 'a', x FROM t1 WHERE y=3),
+ (SELECT x AS 'b' FROM t1 WHERE y=4)
+ WHERE a=b
+ ORDER BY a
+ }
+} {8 5 8 9 6 9 10 7 10}
+
+# Tests of compound sub-selects
+#
+do_test select5-6.1 {
+ execsql {
+ DELETE FROM t1 WHERE x>4;
+ SELECT * FROM t1
+ }
+} {1 1 2 2 3 2 4 3}
+ifcapable compound {
+ do_test select6-6.2 {
+ execsql {
+ SELECT * FROM (
+ SELECT x AS 'a' FROM t1 UNION ALL SELECT x+10 AS 'a' FROM t1
+ ) ORDER BY a;
+ }
+ } {1 2 3 4 11 12 13 14}
+ do_test select6-6.3 {
+ execsql {
+ SELECT * FROM (
+ SELECT x AS 'a' FROM t1 UNION ALL SELECT x+1 AS 'a' FROM t1
+ ) ORDER BY a;
+ }
+ } {1 2 2 3 3 4 4 5}
+ do_test select6-6.4 {
+ execsql {
+ SELECT * FROM (
+ SELECT x AS 'a' FROM t1 UNION SELECT x+1 AS 'a' FROM t1
+ ) ORDER BY a;
+ }
+ } {1 2 3 4 5}
+ do_test select6-6.5 {
+ execsql {
+ SELECT * FROM (
+ SELECT x AS 'a' FROM t1 INTERSECT SELECT x+1 AS 'a' FROM t1
+ ) ORDER BY a;
+ }
+ } {2 3 4}
+ do_test select6-6.6 {
+ execsql {
+ SELECT * FROM (
+ SELECT x AS 'a' FROM t1 EXCEPT SELECT x*2 AS 'a' FROM t1
+ ) ORDER BY a;
+ }
+ } {1 3}
+} ;# ifcapable compound
+
+# Subselects with no FROM clause
+#
+do_test select6-7.1 {
+ execsql {
+ SELECT * FROM (SELECT 1)
+ }
+} {1}
+do_test select6-7.2 {
+ execsql {
+ SELECT c,b,a,* FROM (SELECT 1 AS 'a', 2 AS 'b', 'abc' AS 'c')
+ }
+} {abc 2 1 1 2 abc}
+do_test select6-7.3 {
+ execsql {
+ SELECT c,b,a,* FROM (SELECT 1 AS 'a', 2 AS 'b', 'abc' AS 'c' WHERE 0)
+ }
+} {}
+do_test select6-7.4 {
+ execsql2 {
+ SELECT c,b,a,* FROM (SELECT 1 AS 'a', 2 AS 'b', 'abc' AS 'c' WHERE 1)
+ }
+} {c abc b 2 a 1 a 1 b 2 c abc}
+
+# The remaining tests in this file depend on the EXPLAIN keyword.
+# Skip these tests if EXPLAIN is disabled in the current build.
+#
+ifcapable {!explain} {
+ finish_test
+ return
+}
+
+# The following procedure compiles the SQL given as an argument and returns
+# TRUE if that SQL uses any transient tables and returns FALSE if no
+# transient tables are used. This is used to make sure that the
+# sqliteFlattenSubquery() routine in select.c is doing its job.
+#
+proc is_flat {sql} {
+ return [expr 0>[lsearch [execsql "EXPLAIN $sql"] OpenEphemeral]]
+}
+
+# Check that the flattener works correctly for deeply nested subqueries
+# involving joins.
+#
+do_test select6-8.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(p,q);
+ INSERT INTO t3 VALUES(1,11);
+ INSERT INTO t3 VALUES(2,22);
+ CREATE TABLE t4(q,r);
+ INSERT INTO t4 VALUES(11,111);
+ INSERT INTO t4 VALUES(22,222);
+ COMMIT;
+ SELECT * FROM t3 NATURAL JOIN t4;
+ }
+} {1 11 111 2 22 222}
+do_test select6-8.2 {
+ execsql {
+ SELECT y, p, q, r FROM
+ (SELECT t1.y AS y, t2.b AS b FROM t1, t2 WHERE t1.x=t2.a) AS m,
+ (SELECT t3.p AS p, t3.q AS q, t4.r AS r FROM t3 NATURAL JOIN t4) as n
+ WHERE y=p
+ }
+} {1 1 11 111 2 2 22 222 2 2 22 222}
+# If view support is omitted from the build, then so is the query
+# "flattener". So omit this test and test select6-8.6 in that case.
+ifcapable view {
+do_test select6-8.3 {
+ is_flat {
+ SELECT y, p, q, r FROM
+ (SELECT t1.y AS y, t2.b AS b FROM t1, t2 WHERE t1.x=t2.a) AS m,
+ (SELECT t3.p AS p, t3.q AS q, t4.r AS r FROM t3 NATURAL JOIN t4) as n
+ WHERE y=p
+ }
+} {1}
+} ;# ifcapable view
+do_test select6-8.4 {
+ execsql {
+ SELECT DISTINCT y, p, q, r FROM
+ (SELECT t1.y AS y, t2.b AS b FROM t1, t2 WHERE t1.x=t2.a) AS m,
+ (SELECT t3.p AS p, t3.q AS q, t4.r AS r FROM t3 NATURAL JOIN t4) as n
+ WHERE y=p
+ }
+} {1 1 11 111 2 2 22 222}
+do_test select6-8.5 {
+ execsql {
+ SELECT * FROM
+ (SELECT y, p, q, r FROM
+ (SELECT t1.y AS y, t2.b AS b FROM t1, t2 WHERE t1.x=t2.a) AS m,
+ (SELECT t3.p AS p, t3.q AS q, t4.r AS r FROM t3 NATURAL JOIN t4) as n
+ WHERE y=p) AS e,
+ (SELECT r AS z FROM t4 WHERE q=11) AS f
+ WHERE e.r=f.z
+ }
+} {1 1 11 111 111}
+ifcapable view {
+do_test select6-8.6 {
+ is_flat {
+ SELECT * FROM
+ (SELECT y, p, q, r FROM
+ (SELECT t1.y AS y, t2.b AS b FROM t1, t2 WHERE t1.x=t2.a) AS m,
+ (SELECT t3.p AS p, t3.q AS q, t4.r AS r FROM t3 NATURAL JOIN t4) as n
+ WHERE y=p) AS e,
+ (SELECT r AS z FROM t4 WHERE q=11) AS f
+ WHERE e.r=f.z
+ }
+} {1}
+} ;# ifcapable view
+
+# Ticket #1634
+#
+do_test select6-9.1 {
+ execsql {
+ SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b
+ }
+} {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2}
+do_test select6-9.2 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT 2);
+ }
+} {1 2}
+do_test select6-9.3 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT 2 OFFSET 1);
+ }
+} {2 3}
+do_test select6-9.4 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1) LIMIT 2;
+ }
+} {1 2}
+do_test select6-9.5 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1) LIMIT 2 OFFSET 1;
+ }
+} {2 3}
+do_test select6-9.6 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT 2) LIMIT 3;
+ }
+} {1 2}
+do_test select6-9.7 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT -1) LIMIT 3;
+ }
+} {1 2 3}
+do_test select6-9.8 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT -1);
+ }
+} {1 2 3 4}
+do_test select6-9.9 {
+ execsql {
+ SELECT x FROM (SELECT x FROM t1 LIMIT -1 OFFSET 1);
+ }
+} {2 3 4}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/select7.test b/third_party/sqlite/test/select7.test
new file mode 100755
index 0000000..3837c88
--- /dev/null
+++ b/third_party/sqlite/test/select7.test
@@ -0,0 +1,159 @@
+# 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 file is testing compute SELECT statements and nested
+# views.
+#
+# $Id: select7.test,v 1.11 2007/09/12 17:01:45 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable compound {
+
+# A 3-way INTERSECT. Ticket #875
+ifcapable tempdb {
+ do_test select7-1.1 {
+ execsql {
+ create temp table t1(x);
+ insert into t1 values('amx');
+ insert into t1 values('anx');
+ insert into t1 values('amy');
+ insert into t1 values('bmy');
+ select * from t1 where x like 'a__'
+ intersect select * from t1 where x like '_m_'
+ intersect select * from t1 where x like '__x';
+ }
+ } {amx}
+}
+
+
+# Nested views do not handle * properly. Ticket #826.
+#
+ifcapable view {
+do_test select7-2.1 {
+ execsql {
+ CREATE TABLE x(id integer primary key, a TEXT NULL);
+ INSERT INTO x (a) VALUES ('first');
+ CREATE TABLE tempx(id integer primary key, a TEXT NULL);
+ INSERT INTO tempx (a) VALUES ('t-first');
+ CREATE VIEW tv1 AS SELECT x.id, tx.id FROM x JOIN tempx tx ON tx.id=x.id;
+ CREATE VIEW tv1b AS SELECT x.id, tx.id FROM x JOIN tempx tx on tx.id=x.id;
+ CREATE VIEW tv2 AS SELECT * FROM tv1 UNION SELECT * FROM tv1b;
+ SELECT * FROM tv2;
+ }
+} {1 1}
+} ;# ifcapable view
+
+} ;# ifcapable compound
+
+# Do not allow GROUP BY without an aggregate. Ticket #1039.
+#
+# Change: force any query with a GROUP BY clause to be processed as
+# an aggregate query, whether it contains aggregates or not.
+#
+ifcapable subquery {
+ # do_test select7-3.1 {
+ # catchsql {
+ # SELECT * FROM (SELECT * FROM sqlite_master) GROUP BY name
+ # }
+ # } {1 {GROUP BY may only be used on aggregate queries}}
+ do_test select7-3.1 {
+ catchsql {
+ SELECT * FROM (SELECT * FROM sqlite_master) GROUP BY name
+ }
+ } [list 0 [execsql {SELECT * FROM sqlite_master ORDER BY name}]]
+}
+
+# Ticket #2018 - Make sure names are resolved correctly on all
+# SELECT statements of a compound subquery.
+#
+ifcapable {subquery && compound} {
+ do_test select7-4.1 {
+ execsql {
+ CREATE TABLE IF NOT EXISTS photo(pk integer primary key, x);
+ CREATE TABLE IF NOT EXISTS tag(pk integer primary key, fk int, name);
+
+ SELECT P.pk from PHOTO P WHERE NOT EXISTS (
+ SELECT T2.pk from TAG T2 WHERE T2.fk = P.pk
+ EXCEPT
+ SELECT T3.pk from TAG T3 WHERE T3.fk = P.pk AND T3.name LIKE '%foo%'
+ );
+ }
+ } {}
+ do_test select7-4.2 {
+ execsql {
+ INSERT INTO photo VALUES(1,1);
+ INSERT INTO photo VALUES(2,2);
+ INSERT INTO photo VALUES(3,3);
+ INSERT INTO tag VALUES(11,1,'one');
+ INSERT INTO tag VALUES(12,1,'two');
+ INSERT INTO tag VALUES(21,1,'one-b');
+ SELECT P.pk from PHOTO P WHERE NOT EXISTS (
+ SELECT T2.pk from TAG T2 WHERE T2.fk = P.pk
+ EXCEPT
+ SELECT T3.pk from TAG T3 WHERE T3.fk = P.pk AND T3.name LIKE '%foo%'
+ );
+ }
+ } {2 3}
+}
+
+# ticket #2347
+#
+ifcapable {subquery && compound} {
+ do_test select7-5.1 {
+ catchsql {
+ CREATE TABLE t2(a,b);
+ SELECT 5 IN (SELECT a,b FROM t2);
+ }
+ } [list 1 \
+ {only a single result allowed for a SELECT that is part of an expression}]
+ do_test select7-5.2 {
+ catchsql {
+ SELECT 5 IN (SELECT * FROM t2);
+ }
+ } [list 1 \
+ {only a single result allowed for a SELECT that is part of an expression}]
+ do_test select7-5.3 {
+ catchsql {
+ SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
+ }
+ } [list 1 \
+ {only a single result allowed for a SELECT that is part of an expression}]
+ do_test select7-5.4 {
+ catchsql {
+ SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
+ }
+ } [list 1 \
+ {only a single result allowed for a SELECT that is part of an expression}]
+}
+
+# Verify that an error occurs if you have too many terms on a
+# compound select statement.
+#
+ifcapable compound {
+ if {$SQLITE_MAX_COMPOUND_SELECT>0} {
+ set sql {SELECT 0}
+ set result 0
+ for {set i 1} {$i<$SQLITE_MAX_COMPOUND_SELECT} {incr i} {
+ append sql " UNION ALL SELECT $i"
+ lappend result $i
+ }
+ do_test select7-6.1 {
+ catchsql $sql
+ } [list 0 $result]
+ append sql { UNION ALL SELECT 99999999}
+ do_test select7-6.2 {
+ catchsql $sql
+ } {1 {too many terms in compound SELECT}}
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/select8.test b/third_party/sqlite/test/select8.test
new file mode 100755
index 0000000..9862664
--- /dev/null
+++ b/third_party/sqlite/test/select8.test
@@ -0,0 +1,62 @@
+# 2001 September 15
+#
+# 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 file is testing that LIMIT and OFFSET work for
+# unusual combinations SELECT statements.
+#
+# $Id: select8.test,v 1.1 2008/01/12 12:48:09 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+execsql {
+ CREATE TABLE songs(songid, artist, timesplayed);
+ INSERT INTO songs VALUES(1,'one',1);
+ INSERT INTO songs VALUES(2,'one',2);
+ INSERT INTO songs VALUES(3,'two',3);
+ INSERT INTO songs VALUES(4,'three',5);
+ INSERT INTO songs VALUES(5,'one',7);
+ INSERT INTO songs VALUES(6,'two',11);
+}
+set result [execsql {
+ SELECT DISTINCT artist,sum(timesplayed) AS total
+ FROM songs
+ GROUP BY LOWER(artist)
+}]
+puts result=$result
+do_test select8-1.1 {
+ execsql {
+ SELECT DISTINCT artist,sum(timesplayed) AS total
+ FROM songs
+ GROUP BY LOWER(artist)
+ LIMIT 1 OFFSET 1
+ }
+} [lrange $result 2 3]
+do_test select8-1.2 {
+ execsql {
+ SELECT DISTINCT artist,sum(timesplayed) AS total
+ FROM songs
+ GROUP BY LOWER(artist)
+ LIMIT 2 OFFSET 1
+ }
+} [lrange $result 2 5]
+do_test select8-1.3 {
+ execsql {
+ SELECT DISTINCT artist,sum(timesplayed) AS total
+ FROM songs
+ GROUP BY LOWER(artist)
+ LIMIT -1 OFFSET 2
+ }
+} [lrange $result 4 end]
+
+
+finish_test
diff --git a/third_party/sqlite/test/select9.test b/third_party/sqlite/test/select9.test
new file mode 100755
index 0000000..eeadf13
--- /dev/null
+++ b/third_party/sqlite/test/select9.test
@@ -0,0 +1,421 @@
+# 2008 June 24
+#
+# 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.
+#
+# $Id: select9.test,v 1.4 2008/07/01 14:39:35 danielk1977 Exp $
+
+# The tests in this file are focused on test compound SELECT statements
+# that have any or all of an ORDER BY, LIMIT or OFFSET clauses. As of
+# version 3.6.0, SQLite contains code to use SQL indexes where possible
+# to optimize such statements.
+#
+
+# TODO Points:
+#
+# * Are there any "column affinity" issues to consider?
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+#set ISQUICK 1
+
+#-------------------------------------------------------------------------
+# test_compound_select TESTNAME SELECT RESULT
+#
+# This command is used to run multiple LIMIT/OFFSET test cases based on
+# the single SELECT statement passed as the second argument. The SELECT
+# statement may not contain a LIMIT or OFFSET clause. This proc tests
+# many statements of the form:
+#
+# "$SELECT limit $X offset $Y"
+#
+# for various values of $X and $Y.
+#
+# The third argument, $RESULT, should contain the expected result of
+# the command [execsql $SELECT].
+#
+# The first argument, $TESTNAME, is used as the base test case name to
+# pass to [do_test] for each individual LIMIT OFFSET test case.
+#
+proc test_compound_select {testname sql result} {
+
+ set nCol 1
+ db eval $sql A {
+ set nCol [llength $A(*)]
+ break
+ }
+ set nRow [expr {[llength $result] / $nCol}]
+
+ set ::compound_sql $sql
+ do_test $testname {
+ execsql $::compound_sql
+ } $result
+#return
+
+ set iLimitIncr 1
+ set iOffsetIncr 1
+ if {[info exists ::ISQUICK] && $::ISQUICK && $nRow>=5} {
+ set iOffsetIncr [expr $nRow / 5]
+ set iLimitIncr [expr $nRow / 5]
+ }
+
+ set iLimitEnd [expr $nRow+$iLimitIncr]
+ set iOffsetEnd [expr $nRow+$iOffsetIncr]
+
+ for {set iOffset 0} {$iOffset < $iOffsetEnd} {incr iOffset $iOffsetIncr} {
+ for {set iLimit 0} {$iLimit < $iLimitEnd} {incr iLimit} {
+
+ set ::compound_sql "$sql LIMIT $iLimit"
+ if {$iOffset != 0} {
+ append ::compound_sql " OFFSET $iOffset"
+ }
+
+ set iStart [expr {$iOffset*$nCol}]
+ set iEnd [expr {($iOffset*$nCol) + ($iLimit*$nCol) -1}]
+
+ do_test $testname.limit=$iLimit.offset=$iOffset {
+ execsql $::compound_sql
+ } [lrange $result $iStart $iEnd]
+ }
+ }
+}
+
+#-------------------------------------------------------------------------
+# test_compound_select_flippable TESTNAME SELECT RESULT
+#
+# This command is for testing statements of the form:
+#
+# <simple select 1> <compound op> <simple select 2> ORDER BY <order by>
+#
+# where each <simple select> is a simple (non-compound) select statement
+# and <compound op> is one of "INTERSECT", "UNION ALL" or "UNION".
+#
+# This proc calls [test_compound_select] twice, once with the select
+# statement as it is passed to this command, and once with the positions
+# of <select statement 1> and <select statement 2> exchanged.
+#
+proc test_compound_select_flippable {testname sql result} {
+ test_compound_select $testname $sql $result
+
+ set select [string trim $sql]
+ set RE {(.*)(UNION ALL|INTERSECT|UNION)(.*)(ORDER BY.*)}
+ set rc [regexp $RE $select -> s1 op s2 order_by]
+ if {!$rc} {error "Statement is unflippable: $select"}
+
+ set flipsql "$s2 $op $s1 $order_by"
+ test_compound_select $testname.flipped $flipsql $result
+}
+
+#############################################################################
+# Begin tests.
+#
+
+# Create and populate a sample database.
+#
+do_test select9-1.0 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(d, e, f);
+ BEGIN;
+ INSERT INTO t1 VALUES(1, 'one', 'I');
+ INSERT INTO t1 VALUES(3, NULL, NULL);
+ INSERT INTO t1 VALUES(5, 'five', 'V');
+ INSERT INTO t1 VALUES(7, 'seven', 'VII');
+ INSERT INTO t1 VALUES(9, NULL, NULL);
+ INSERT INTO t1 VALUES(2, 'two', 'II');
+ INSERT INTO t1 VALUES(4, 'four', 'IV');
+ INSERT INTO t1 VALUES(6, NULL, NULL);
+ INSERT INTO t1 VALUES(8, 'eight', 'VIII');
+ INSERT INTO t1 VALUES(10, 'ten', 'X');
+
+ INSERT INTO t2 VALUES(1, 'two', 'IV');
+ INSERT INTO t2 VALUES(2, 'four', 'VIII');
+ INSERT INTO t2 VALUES(3, NULL, NULL);
+ INSERT INTO t2 VALUES(4, 'eight', 'XVI');
+ INSERT INTO t2 VALUES(5, 'ten', 'XX');
+ INSERT INTO t2 VALUES(6, NULL, NULL);
+ INSERT INTO t2 VALUES(7, 'fourteen', 'XXVIII');
+ INSERT INTO t2 VALUES(8, 'sixteen', 'XXXII');
+ INSERT INTO t2 VALUES(9, NULL, NULL);
+ INSERT INTO t2 VALUES(10, 'twenty', 'XL');
+
+ COMMIT;
+ }
+} {}
+
+# Each iteration of this loop runs the same tests with a different set
+# of indexes present within the database schema. The data returned by
+# the compound SELECT statements in the test cases should be the same
+# in each case.
+#
+set iOuterLoop 1
+foreach indexes [list {
+ /* Do not create any indexes. */
+} {
+ CREATE INDEX i1 ON t1(a)
+} {
+ CREATE INDEX i2 ON t1(b)
+} {
+ CREATE INDEX i3 ON t2(d)
+} {
+ CREATE INDEX i4 ON t2(e)
+}] {
+
+ do_test select9-1.$iOuterLoop.1 {
+ execsql $indexes
+ } {}
+
+ # Test some 2-way UNION ALL queries. No WHERE clauses.
+ #
+ test_compound_select select9-1.$iOuterLoop.2 {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2
+ } {1 one 3 {} 5 five 7 seven 9 {} 2 two 4 four 6 {} 8 eight 10 ten 1 two 2 four 3 {} 4 eight 5 ten 6 {} 7 fourteen 8 sixteen 9 {} 10 twenty}
+ test_compound_select select9-1.$iOuterLoop.3 {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY 1
+ } {1 one 1 two 2 two 2 four 3 {} 3 {} 4 four 4 eight 5 five 5 ten 6 {} 6 {} 7 seven 7 fourteen 8 eight 8 sixteen 9 {} 9 {} 10 ten 10 twenty}
+ test_compound_select select9-1.$iOuterLoop.4 {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY 2
+ } {3 {} 9 {} 6 {} 3 {} 6 {} 9 {} 8 eight 4 eight 5 five 4 four 2 four 7 fourteen 1 one 7 seven 8 sixteen 10 ten 5 ten 10 twenty 2 two 1 two}
+ test_compound_select_flippable select9-1.$iOuterLoop.5 {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY 1, 2
+ } {1 one 1 two 2 four 2 two 3 {} 3 {} 4 eight 4 four 5 five 5 ten 6 {} 6 {} 7 fourteen 7 seven 8 eight 8 sixteen 9 {} 9 {} 10 ten 10 twenty}
+ test_compound_select_flippable select9-1.$iOuterLoop.6 {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY 2, 1
+ } {3 {} 3 {} 6 {} 6 {} 9 {} 9 {} 4 eight 8 eight 5 five 2 four 4 four 7 fourteen 1 one 7 seven 8 sixteen 5 ten 10 ten 10 twenty 1 two 2 two}
+
+ # Test some 2-way UNION queries.
+ #
+ test_compound_select select9-1.$iOuterLoop.7 {
+ SELECT a, b FROM t1 UNION SELECT d, e FROM t2
+ } {1 one 1 two 2 four 2 two 3 {} 4 eight 4 four 5 five 5 ten 6 {} 7 fourteen 7 seven 8 eight 8 sixteen 9 {} 10 ten 10 twenty}
+
+ test_compound_select select9-1.$iOuterLoop.8 {
+ SELECT a, b FROM t1 UNION SELECT d, e FROM t2 ORDER BY 1
+ } {1 one 1 two 2 four 2 two 3 {} 4 eight 4 four 5 five 5 ten 6 {} 7 fourteen 7 seven 8 eight 8 sixteen 9 {} 10 ten 10 twenty}
+
+ test_compound_select select9-1.$iOuterLoop.9 {
+ SELECT a, b FROM t1 UNION SELECT d, e FROM t2 ORDER BY 2
+ } {3 {} 6 {} 9 {} 4 eight 8 eight 5 five 2 four 4 four 7 fourteen 1 one 7 seven 8 sixteen 5 ten 10 ten 10 twenty 1 two 2 two}
+
+ test_compound_select_flippable select9-1.$iOuterLoop.10 {
+ SELECT a, b FROM t1 UNION SELECT d, e FROM t2 ORDER BY 1, 2
+ } {1 one 1 two 2 four 2 two 3 {} 4 eight 4 four 5 five 5 ten 6 {} 7 fourteen 7 seven 8 eight 8 sixteen 9 {} 10 ten 10 twenty}
+
+ test_compound_select_flippable select9-1.$iOuterLoop.11 {
+ SELECT a, b FROM t1 UNION SELECT d, e FROM t2 ORDER BY 2, 1
+ } {3 {} 6 {} 9 {} 4 eight 8 eight 5 five 2 four 4 four 7 fourteen 1 one 7 seven 8 sixteen 5 ten 10 ten 10 twenty 1 two 2 two}
+
+ # Test some 2-way INTERSECT queries.
+ #
+ test_compound_select select9-1.$iOuterLoop.11 {
+ SELECT a, b FROM t1 INTERSECT SELECT d, e FROM t2
+ } {3 {} 6 {} 9 {}}
+ test_compound_select_flippable select9-1.$iOuterLoop.12 {
+ SELECT a, b FROM t1 INTERSECT SELECT d, e FROM t2 ORDER BY 1
+ } {3 {} 6 {} 9 {}}
+ test_compound_select select9-1.$iOuterLoop.13 {
+ SELECT a, b FROM t1 INTERSECT SELECT d, e FROM t2 ORDER BY 2
+ } {3 {} 6 {} 9 {}}
+ test_compound_select_flippable select9-1.$iOuterLoop.14 {
+ SELECT a, b FROM t1 INTERSECT SELECT d, e FROM t2 ORDER BY 2, 1
+ } {3 {} 6 {} 9 {}}
+ test_compound_select_flippable select9-1.$iOuterLoop.15 {
+ SELECT a, b FROM t1 INTERSECT SELECT d, e FROM t2 ORDER BY 1, 2
+ } {3 {} 6 {} 9 {}}
+
+ # Test some 2-way EXCEPT queries.
+ #
+ test_compound_select select9-1.$iOuterLoop.16 {
+ SELECT a, b FROM t1 EXCEPT SELECT d, e FROM t2
+ } {1 one 2 two 4 four 5 five 7 seven 8 eight 10 ten}
+
+ test_compound_select select9-1.$iOuterLoop.17 {
+ SELECT a, b FROM t1 EXCEPT SELECT d, e FROM t2 ORDER BY 1
+ } {1 one 2 two 4 four 5 five 7 seven 8 eight 10 ten}
+
+ test_compound_select select9-1.$iOuterLoop.18 {
+ SELECT a, b FROM t1 EXCEPT SELECT d, e FROM t2 ORDER BY 2
+ } {8 eight 5 five 4 four 1 one 7 seven 10 ten 2 two}
+
+ test_compound_select select9-1.$iOuterLoop.19 {
+ SELECT a, b FROM t1 EXCEPT SELECT d, e FROM t2 ORDER BY 1, 2
+ } {1 one 2 two 4 four 5 five 7 seven 8 eight 10 ten}
+
+ test_compound_select select9-1.$iOuterLoop.20 {
+ SELECT a, b FROM t1 EXCEPT SELECT d, e FROM t2 ORDER BY 2, 1
+ } {8 eight 5 five 4 four 1 one 7 seven 10 ten 2 two}
+
+ incr iOuterLoop
+}
+
+do_test select9-2.0 {
+ execsql {
+ DROP INDEX i1;
+ DROP INDEX i2;
+ DROP INDEX i3;
+ DROP INDEX i4;
+ }
+} {}
+
+proc reverse {lhs rhs} {
+ return [string compare $rhs $lhs]
+}
+db collate reverse reverse
+
+# This loop is similar to the previous one (test cases select9-1.*)
+# except that the simple select statements have WHERE clauses attached
+# to them. Sometimes the WHERE clause may be satisfied using the same
+# index used for ORDER BY, sometimes not.
+#
+set iOuterLoop 1
+foreach indexes [list {
+ /* Do not create any indexes. */
+} {
+ CREATE INDEX i1 ON t1(a)
+} {
+ DROP INDEX i1;
+ CREATE INDEX i1 ON t1(b, a)
+} {
+ CREATE INDEX i2 ON t2(d DESC, e COLLATE REVERSE ASC);
+} {
+ CREATE INDEX i3 ON t1(a DESC);
+}] {
+ do_test select9-2.$iOuterLoop.1 {
+ execsql $indexes
+ } {}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.2 {
+ SELECT * FROM t1 WHERE a<5 UNION SELECT * FROM t2 WHERE d>=5 ORDER BY 1
+ } {1 one I 2 two II 3 {} {} 4 four IV 5 ten XX 6 {} {} 7 fourteen XXVIII 8 sixteen XXXII 9 {} {} 10 twenty XL}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.2 {
+ SELECT * FROM t1 WHERE a<5 UNION SELECT * FROM t2 WHERE d>=5 ORDER BY 2, 1
+ } {3 {} {} 6 {} {} 9 {} {} 4 four IV 7 fourteen XXVIII 1 one I 8 sixteen XXXII 5 ten XX 10 twenty XL 2 two II}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.3 {
+ SELECT * FROM t1 WHERE a<5 UNION SELECT * FROM t2 WHERE d>=5
+ ORDER BY 2 COLLATE reverse, 1
+ } {3 {} {} 6 {} {} 9 {} {} 2 two II 10 twenty XL 5 ten XX 8 sixteen XXXII 1 one I 7 fourteen XXVIII 4 four IV}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.4 {
+ SELECT * FROM t1 WHERE a<5 UNION ALL SELECT * FROM t2 WHERE d>=5 ORDER BY 1
+ } {1 one I 2 two II 3 {} {} 4 four IV 5 ten XX 6 {} {} 7 fourteen XXVIII 8 sixteen XXXII 9 {} {} 10 twenty XL}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.5 {
+ SELECT * FROM t1 WHERE a<5 UNION ALL SELECT * FROM t2 WHERE d>=5 ORDER BY 2, 1
+ } {3 {} {} 6 {} {} 9 {} {} 4 four IV 7 fourteen XXVIII 1 one I 8 sixteen XXXII 5 ten XX 10 twenty XL 2 two II}
+
+ test_compound_select_flippable select9-2.$iOuterLoop.6 {
+ SELECT * FROM t1 WHERE a<5 UNION ALL SELECT * FROM t2 WHERE d>=5
+ ORDER BY 2 COLLATE reverse, 1
+ } {3 {} {} 6 {} {} 9 {} {} 2 two II 10 twenty XL 5 ten XX 8 sixteen XXXII 1 one I 7 fourteen XXVIII 4 four IV}
+
+ test_compound_select select9-2.$iOuterLoop.4 {
+ SELECT a FROM t1 WHERE a<8 EXCEPT SELECT d FROM t2 WHERE d<=3 ORDER BY 1
+ } {4 5 6 7}
+
+ test_compound_select select9-2.$iOuterLoop.4 {
+ SELECT a FROM t1 WHERE a<8 INTERSECT SELECT d FROM t2 WHERE d<=3 ORDER BY 1
+ } {1 2 3}
+
+}
+
+do_test select9-2.X {
+ execsql {
+ DROP INDEX i1;
+ DROP INDEX i2;
+ DROP INDEX i3;
+ }
+} {}
+
+# This procedure executes the SQL. Then it checks the generated program
+# for the SQL and appends a "nosort" to the result if the program contains the
+# SortCallback opcode. If the program does not contain the SortCallback
+# opcode it appends "sort"
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+# If the right indexes exist, the following query:
+#
+# SELECT t1.a FROM t1 UNION ALL SELECT t2.d FROM t2 ORDER BY 1
+#
+# can use indexes to run without doing a in-memory sort operation.
+# This block of tests (select9-3.*) is used to check if the same
+# is possible with:
+#
+# CREATE VIEW v1 AS SELECT a FROM t1 UNION ALL SELECT d FROM t2
+# SELECT a FROM v1 ORDER BY 1
+#
+# It turns out that it is.
+#
+do_test select9-3.1 {
+ cksort { SELECT a FROM t1 ORDER BY 1 }
+} {1 2 3 4 5 6 7 8 9 10 sort}
+do_test select9-3.2 {
+ execsql { CREATE INDEX i1 ON t1(a) }
+ cksort { SELECT a FROM t1 ORDER BY 1 }
+} {1 2 3 4 5 6 7 8 9 10 nosort}
+do_test select9-3.3 {
+ cksort { SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 LIMIT 5 }
+} {1 1 2 2 3 sort}
+do_test select9-3.4 {
+ execsql { CREATE INDEX i2 ON t2(d) }
+ cksort { SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 LIMIT 5 }
+} {1 1 2 2 3 nosort}
+do_test select9-3.5 {
+ execsql { CREATE VIEW v1 AS SELECT a FROM t1 UNION ALL SELECT d FROM t2 }
+ cksort { SELECT a FROM v1 ORDER BY 1 LIMIT 5 }
+} {1 1 2 2 3 nosort}
+do_test select9-3.X {
+ execsql {
+ DROP INDEX i1;
+ DROP INDEX i2;
+ DROP VIEW v1;
+ }
+} {}
+
+# This block of tests is the same as the preceding one, except that
+# "UNION" is tested instead of "UNION ALL".
+#
+do_test select9-4.1 {
+ cksort { SELECT a FROM t1 ORDER BY 1 }
+} {1 2 3 4 5 6 7 8 9 10 sort}
+do_test select9-4.2 {
+ execsql { CREATE INDEX i1 ON t1(a) }
+ cksort { SELECT a FROM t1 ORDER BY 1 }
+} {1 2 3 4 5 6 7 8 9 10 nosort}
+do_test select9-4.3 {
+ cksort { SELECT a FROM t1 UNION SELECT d FROM t2 ORDER BY 1 LIMIT 5 }
+} {1 2 3 4 5 sort}
+do_test select9-4.4 {
+ execsql { CREATE INDEX i2 ON t2(d) }
+ cksort { SELECT a FROM t1 UNION SELECT d FROM t2 ORDER BY 1 LIMIT 5 }
+} {1 2 3 4 5 nosort}
+do_test select9-4.5 {
+ execsql { CREATE VIEW v1 AS SELECT a FROM t1 UNION SELECT d FROM t2 }
+ cksort { SELECT a FROM v1 ORDER BY 1 LIMIT 5 }
+} {1 2 3 4 5 sort}
+do_test select9-4.X {
+ execsql {
+ DROP INDEX i1;
+ DROP INDEX i2;
+ DROP VIEW v1;
+ }
+} {}
+
+
+finish_test
diff --git a/third_party/sqlite/test/selectA.test b/third_party/sqlite/test/selectA.test
new file mode 100755
index 0000000..8a5e822
--- /dev/null
+++ b/third_party/sqlite/test/selectA.test
@@ -0,0 +1,1278 @@
+# 2008 June 24
+#
+# 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 file is testing the compound-SELECT merge
+# optimization. Or, in other words, making sure that all
+# possible combinations of UNION, UNION ALL, EXCEPT, and
+# INTERSECT work together with an ORDER BY clause (with or w/o
+# explicit sort order and explicit collating secquites) and
+# with and without optional LIMIT and OFFSET clauses.
+#
+# $Id: selectA.test,v 1.5 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+do_test selectA-1.0 {
+ execsql {
+ CREATE TABLE t1(a,b,c COLLATE NOCASE);
+ INSERT INTO t1 VALUES(1,'a','a');
+ INSERT INTO t1 VALUES(9.9, 'b', 'B');
+ INSERT INTO t1 VALUES(NULL, 'C', 'c');
+ INSERT INTO t1 VALUES('hello', 'd', 'D');
+ INSERT INTO t1 VALUES(x'616263', 'e', 'e');
+ SELECT * FROM t1;
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e}
+do_test selectA-1.1 {
+ execsql {
+ CREATE TABLE t2(x,y,z COLLATE NOCASE);
+ INSERT INTO t2 VALUES(NULL,'U','u');
+ INSERT INTO t2 VALUES('mad', 'Z', 'z');
+ INSERT INTO t2 VALUES(x'68617265', 'm', 'M');
+ INSERT INTO t2 VALUES(5.2e6, 'X', 'x');
+ INSERT INTO t2 VALUES(-23, 'Y', 'y');
+ SELECT * FROM t2;
+ }
+} {{} U u mad Z z hare m M 5200000.0 X x -23 Y y}
+do_test selectA-1.2 {
+ execsql {
+ CREATE TABLE t3(a,b,c COLLATE NOCASE);
+ INSERT INTO t3 SELECT * FROM t1;
+ INSERT INTO t3 SELECT * FROM t2;
+ INSERT INTO t3 SELECT * FROM t1;
+ INSERT INTO t3 SELECT * FROM t2;
+ INSERT INTO t3 SELECT * FROM t1;
+ INSERT INTO t3 SELECT * FROM t2;
+ SELECT count(*) FROM t3;
+ }
+} {30}
+
+do_test selectA-2.1 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.2 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.3 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.4 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.5 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.6 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.7 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.8 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.9 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.10 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.11 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.12 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.13 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.14 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.15 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.16 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.17 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.18 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.19 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.20 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.21 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.22 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.23 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.24 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.25 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.26 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.27 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.28 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.29 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.30 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.31 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.32 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.33 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.34 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.35 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.36 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.37 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.38 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.39 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.40 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.41 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-2.42 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-2.43 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-2.44 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-2.45 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-2.46 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-2.47 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-2.48 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-2.49 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-2.50 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-2.51 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-2.52 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-2.53 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY b, a DESC
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-2.54 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY b
+ }
+} {hello d D abc e e}
+do_test selectA-2.55 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY b DESC, c
+ }
+} {abc e e hello d D}
+do_test selectA-2.56 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY b, c DESC, a
+ }
+} {hello d D abc e e}
+do_test selectA-2.57 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY b COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.58 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY b
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-2.59 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY c, a DESC
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.60 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY c
+ }
+} {hello d D abc e e}
+do_test selectA-2.61 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY, b DESC, c, a, b, c, a, b, c
+ }
+} {hello d D abc e e}
+do_test selectA-2.62 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-2.63 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.64 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.65 {
+ execsql {
+ SELECT a,b,c FROM t3 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.66 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.67 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t3 WHERE b<'d'
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-2.68 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-2.69 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.70 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.71 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t1
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ INTERSECT SELECT a,b,c FROM t1
+ EXCEPT SELECT x,y,z FROM t2
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT y,x,z FROM t2
+ INTERSECT SELECT a,b,c FROM t1
+ EXCEPT SELECT c,b,a FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-2.72 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.73 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.74 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.75 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.76 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.77 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.78 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.79 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.80 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.81 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.82 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.83 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-2.84 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-2.85 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-2.86 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.87 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY y COLLATE NOCASE DESC,x,z
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.88 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.89 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-2.90 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.91 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-2.92 {
+ execsql {
+ SELECT x,y,z FROM t2
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ ORDER BY y COLLATE NOCASE DESC,x,z
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-2.93 {
+ execsql {
+ SELECT upper((SELECT c FROM t1 UNION SELECT z FROM t2 ORDER BY 1));
+ }
+} {A}
+do_test selectA-2.94 {
+ execsql {
+ SELECT lower((SELECT c FROM t1 UNION ALL SELECT z FROM t2 ORDER BY 1));
+ }
+} {a}
+do_test selectA-2.95 {
+ execsql {
+ SELECT lower((SELECT c FROM t1 INTERSECT SELECT z FROM t2 ORDER BY 1));
+ }
+} {{}}
+do_test selectA-2.96 {
+ execsql {
+ SELECT lower((SELECT z FROM t2 EXCEPT SELECT c FROM t1 ORDER BY 1));
+ }
+} {m}
+
+
+do_test selectA-3.0 {
+ execsql {
+ CREATE UNIQUE INDEX t1a ON t1(a);
+ CREATE UNIQUE INDEX t1b ON t1(b);
+ CREATE UNIQUE INDEX t1c ON t1(c);
+ CREATE UNIQUE INDEX t2x ON t2(x);
+ CREATE UNIQUE INDEX t2y ON t2(y);
+ CREATE UNIQUE INDEX t2z ON t2(z);
+ SELECT name FROM sqlite_master WHERE type='index'
+ }
+} {t1a t1b t1c t2x t2y t2z}
+do_test selectA-3.1 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.2 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.3 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.4 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.5 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.6 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.7 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.8 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.9 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.10 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION ALL SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.11 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.12 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.13 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.14 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.15 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.16 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.17 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.18 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.19 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.20 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION ALL SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.21 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.22 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.23 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.24 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.25 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.26 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.27 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.28 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.29 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.30 {
+ execsql {
+ SELECT a,b,c FROM t1 UNION SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.31 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.32 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.33 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.34 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.35 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.36 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.37 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.38 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.39 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.40 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.41 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-3.42 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-3.43 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-3.44 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a,b,c
+ }
+} {hello d D abc e e}
+do_test selectA-3.45 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-3.46 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a,b,c
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-3.47 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-3.48 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-3.49 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-3.50 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a DESC
+ }
+} {abc e e hello d D}
+do_test selectA-3.51 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-3.52 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY a DESC
+ }
+} {9.9 b B 1 a a {} C c}
+do_test selectA-3.53 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY b, a DESC
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-3.54 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY b
+ }
+} {hello d D abc e e}
+do_test selectA-3.55 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY b DESC, c
+ }
+} {abc e e hello d D}
+do_test selectA-3.56 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY b, c DESC, a
+ }
+} {hello d D abc e e}
+do_test selectA-3.57 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY b COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.58 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY b
+ }
+} {{} C c 1 a a 9.9 b B}
+do_test selectA-3.59 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY c, a DESC
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.60 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b>='d'
+ ORDER BY c
+ }
+} {hello d D abc e e}
+do_test selectA-3.61 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b>='d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY c COLLATE BINARY, b DESC, c, a, b, c, a, b, c
+ }
+} {hello d D abc e e}
+do_test selectA-3.62 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-3.63 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.64 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.65 {
+ execsql {
+ SELECT a,b,c FROM t3 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.66 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.67 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t3 WHERE b<'d'
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-3.68 {
+ execsql {
+ SELECT a,b,c FROM t1 EXCEPT SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c DESC, a
+ }
+} {abc e e hello d D}
+do_test selectA-3.69 {
+ execsql {
+ SELECT a,b,c FROM t1 INTERSECT SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c COLLATE NOCASE
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.70 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d' INTERSECT SELECT a,b,c FROM t1
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.71 {
+ execsql {
+ SELECT a,b,c FROM t1 WHERE b<'d'
+ INTERSECT SELECT a,b,c FROM t1
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT b,c,a FROM t3
+ INTERSECT SELECT a,b,c FROM t1
+ EXCEPT SELECT x,y,z FROM t2
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT y,x,z FROM t2
+ INTERSECT SELECT a,b,c FROM t1
+ EXCEPT SELECT c,b,a FROM t3
+ ORDER BY c
+ }
+} {1 a a 9.9 b B {} C c}
+do_test selectA-3.72 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.73 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.74 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.75 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.76 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.77 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY b COLLATE NOCASE DESC,a,c
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.78 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.79 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.80 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.81 {
+ execsql {
+ SELECT a,b,c FROM t3 UNION SELECT x,y,z FROM t2
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.82 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a,b,c
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.83 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a DESC,b,c
+ }
+} {hare m M abc e e mad Z z hello d D 5200000.0 X x 9.9 b B 1 a a -23 Y y {} C c {} U u}
+do_test selectA-3.84 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY a,c,b
+ }
+} {{} C c {} U u -23 Y y 1 a a 9.9 b B 5200000.0 X x hello d D mad Z z abc e e hare m M}
+do_test selectA-3.85 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY b,a,c
+ }
+} {{} C c {} U u 5200000.0 X x -23 Y y mad Z z 1 a a 9.9 b B hello d D abc e e hare m M}
+do_test selectA-3.86 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY b COLLATE NOCASE,a,c
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.87 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY y COLLATE NOCASE DESC,x,z
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.88 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c,b,a
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.89 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c,a,b
+ }
+} {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z}
+do_test selectA-3.90 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.91 {
+ execsql {
+ SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3
+ ORDER BY c COLLATE BINARY DESC,a,b
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B}
+do_test selectA-3.92 {
+ execsql {
+ SELECT x,y,z FROM t2
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ ORDER BY y COLLATE NOCASE DESC,x,z
+ }
+} {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a}
+do_test selectA-3.93 {
+ execsql {
+ SELECT upper((SELECT c FROM t1 UNION SELECT z FROM t2 ORDER BY 1));
+ }
+} {A}
+do_test selectA-3.94 {
+ execsql {
+ SELECT lower((SELECT c FROM t1 UNION ALL SELECT z FROM t2 ORDER BY 1));
+ }
+} {a}
+do_test selectA-3.95 {
+ execsql {
+ SELECT lower((SELECT c FROM t1 INTERSECT SELECT z FROM t2 ORDER BY 1));
+ }
+} {{}}
+do_test selectA-3.96 {
+ execsql {
+ SELECT lower((SELECT z FROM t2 EXCEPT SELECT c FROM t1 ORDER BY 1));
+ }
+} {m}
+do_test selectA-3.97 {
+ execsql {
+ SELECT upper((SELECT x FROM (
+ SELECT x,y,z FROM t2
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ INTERSECT SELECT a,b,c FROM t3
+ EXCEPT SELECT c,b,a FROM t1
+ UNION SELECT a,b,c FROM t3
+ ORDER BY y COLLATE NOCASE DESC,x,z)))
+ }
+} {MAD}
+
+finish_test
diff --git a/third_party/sqlite/test/selectB.test b/third_party/sqlite/test/selectB.test
new file mode 100755
index 0000000..9f5261e
--- /dev/null
+++ b/third_party/sqlite/test/selectB.test
@@ -0,0 +1,379 @@
+# 2008 June 24
+#
+# 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.
+#
+# $Id: selectB.test,v 1.9 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+proc test_transform {testname sql1 sql2 results} {
+ set ::vdbe1 [list]
+ set ::vdbe2 [list]
+ db eval "explain $sql1" { lappend ::vdbe1 $opcode }
+ db eval "explain $sql2" { lappend ::vdbe2 $opcode }
+
+ do_test $testname.transform {
+ set ::vdbe1
+ } $::vdbe2
+
+ set ::sql1 $sql1
+ do_test $testname.sql1 {
+ execsql $::sql1
+ } $results
+
+ set ::sql2 $sql2
+ do_test $testname.sql2 {
+ execsql $::sql2
+ } $results
+}
+
+do_test selectB-1.1 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(d, e, f);
+
+ INSERT INTO t1 VALUES( 2, 4, 6);
+ INSERT INTO t1 VALUES( 8, 10, 12);
+ INSERT INTO t1 VALUES(14, 16, 18);
+
+ INSERT INTO t2 VALUES(3, 6, 9);
+ INSERT INTO t2 VALUES(12, 15, 18);
+ INSERT INTO t2 VALUES(21, 24, 27);
+ }
+} {}
+
+for {set ii 1} {$ii <= 2} {incr ii} {
+
+ if {$ii == 2} {
+ do_test selectB-2.1 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t2(d);
+ }
+ } {}
+ }
+
+ test_transform selectB-$ii.2 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2
+ } {2 8 14 3 12 21}
+
+ test_transform selectB-$ii.3 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2) ORDER BY 1
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1
+ } {2 3 8 12 14 21}
+
+ test_transform selectB-$ii.4 {
+ SELECT * FROM
+ (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ WHERE a>10 ORDER BY 1
+ } {
+ SELECT a FROM t1 WHERE a>10 UNION ALL SELECT d FROM t2 WHERE d>10 ORDER BY 1
+ } {12 14 21}
+
+ test_transform selectB-$ii.5 {
+ SELECT * FROM
+ (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ WHERE a>10 ORDER BY a
+ } {
+ SELECT a FROM t1 WHERE a>10
+ UNION ALL
+ SELECT d FROM t2 WHERE d>10
+ ORDER BY a
+ } {12 14 21}
+
+ test_transform selectB-$ii.6 {
+ SELECT * FROM
+ (SELECT a FROM t1 UNION ALL SELECT d FROM t2 WHERE d > 12)
+ WHERE a>10 ORDER BY a
+ } {
+ SELECT a FROM t1 WHERE a>10
+ UNION ALL
+ SELECT d FROM t2 WHERE d>12 AND d>10
+ ORDER BY a
+ } {14 21}
+
+ test_transform selectB-$ii.7 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2) ORDER BY 1
+ LIMIT 2
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 LIMIT 2
+ } {2 3}
+
+ test_transform selectB-$ii.8 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2) ORDER BY 1
+ LIMIT 2 OFFSET 3
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 LIMIT 2 OFFSET 3
+ } {12 14}
+
+ test_transform selectB-$ii.9 {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1
+ )
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1
+ } {2 8 14 3 12 21 6 12 18}
+
+ test_transform selectB-$ii.10 {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1
+ ) ORDER BY 1
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1
+ ORDER BY 1
+ } {2 3 6 8 12 12 14 18 21}
+
+ test_transform selectB-$ii.11 {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1
+ ) WHERE a>=10 ORDER BY 1 LIMIT 3
+ } {
+ SELECT a FROM t1 WHERE a>=10 UNION ALL SELECT d FROM t2 WHERE d>=10
+ UNION ALL SELECT c FROM t1 WHERE c>=10
+ ORDER BY 1 LIMIT 3
+ } {12 12 14}
+
+ test_transform selectB-$ii.12 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 LIMIT 2)
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 LIMIT 2
+ } {2 8}
+
+ test_transform selectB-$ii.13 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a ASC)
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 ASC
+ } {2 3 8 12 14 21}
+
+ test_transform selectB-$ii.14 {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC)
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC
+ } {21 14 12 8 3 2}
+
+ test_transform selectB-$ii.14 {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC
+ ) LIMIT 2 OFFSET 2
+ } {
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC LIMIT 2 OFFSET 2
+ } {12 8}
+
+ test_transform selectB-$ii.15 {
+ SELECT * FROM (
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC
+ )
+ } {
+ SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC
+ } {2 4 3 6 8 10 12 15 14 16 21 24}
+}
+
+do_test selectB-3.0 {
+ execsql {
+ DROP INDEX i1;
+ DROP INDEX i2;
+ }
+} {}
+
+for {set ii 3} {$ii <= 4} {incr ii} {
+
+ if {$ii == 4} {
+ do_test selectB-4.0 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+ CREATE INDEX i3 ON t1(c);
+ CREATE INDEX i4 ON t2(d);
+ CREATE INDEX i5 ON t2(e);
+ CREATE INDEX i6 ON t2(f);
+ }
+ } {}
+ }
+
+ do_test selectB-$ii.1 {
+ execsql {
+ SELECT DISTINCT * FROM
+ (SELECT c FROM t1 UNION ALL SELECT e FROM t2)
+ ORDER BY 1;
+ }
+ } {6 12 15 18 24}
+
+ do_test selectB-$ii.2 {
+ execsql {
+ SELECT c, count(*) FROM
+ (SELECT c FROM t1 UNION ALL SELECT e FROM t2)
+ GROUP BY c ORDER BY 1;
+ }
+ } {6 2 12 1 15 1 18 1 24 1}
+ do_test selectB-$ii.3 {
+ execsql {
+ SELECT c, count(*) FROM
+ (SELECT c FROM t1 UNION ALL SELECT e FROM t2)
+ GROUP BY c HAVING count(*)>1;
+ }
+ } {6 2}
+ do_test selectB-$ii.4 {
+ execsql {
+ SELECT t4.c, t3.a FROM
+ (SELECT c FROM t1 UNION ALL SELECT e FROM t2) AS t4, t1 AS t3
+ WHERE t3.a=14
+ ORDER BY 1
+ }
+ } {6 14 6 14 12 14 15 14 18 14 24 14}
+
+ do_test selectB-$ii.5 {
+ execsql {
+ SELECT d FROM t2
+ EXCEPT
+ SELECT a FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ }
+ } {}
+ do_test selectB-$ii.6 {
+ execsql {
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ EXCEPT
+ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2)
+ }
+ } {}
+ do_test selectB-$ii.7 {
+ execsql {
+ SELECT c FROM t1
+ EXCEPT
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ }
+ } {12}
+ do_test selectB-$ii.8 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ EXCEPT
+ SELECT c FROM t1
+ }
+ } {9 15 24 27}
+ do_test selectB-$ii.9 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ EXCEPT
+ SELECT c FROM t1
+ ORDER BY c DESC
+ }
+ } {27 24 15 9}
+
+ do_test selectB-$ii.10 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ UNION
+ SELECT c FROM t1
+ ORDER BY c DESC
+ }
+ } {27 24 18 15 12 9 6}
+ do_test selectB-$ii.11 {
+ execsql {
+ SELECT c FROM t1
+ UNION
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ ORDER BY c
+ }
+ } {6 9 12 15 18 24 27}
+ do_test selectB-$ii.12 {
+ execsql {
+ SELECT c FROM t1 UNION SELECT e FROM t2 UNION ALL SELECT f FROM t2
+ ORDER BY c
+ }
+ } {6 9 12 15 18 18 24 27}
+ do_test selectB-$ii.13 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ UNION
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ ORDER BY 1
+ }
+ } {6 9 15 18 24 27}
+
+ do_test selectB-$ii.14 {
+ execsql {
+ SELECT c FROM t1
+ INTERSECT
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ ORDER BY 1
+ }
+ } {6 18}
+ do_test selectB-$ii.15 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ INTERSECT
+ SELECT c FROM t1
+ ORDER BY 1
+ }
+ } {6 18}
+ do_test selectB-$ii.16 {
+ execsql {
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ INTERSECT
+ SELECT * FROM (SELECT e FROM t2 UNION ALL SELECT f FROM t2)
+ ORDER BY 1
+ }
+ } {6 9 15 18 24 27}
+
+ do_test selectB-$ii.17 {
+ execsql {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 LIMIT 4
+ ) LIMIT 2
+ }
+ } {2 8}
+
+ do_test selectB-$ii.18 {
+ execsql {
+ SELECT * FROM (
+ SELECT a FROM t1 UNION ALL SELECT d FROM t2 LIMIT 4 OFFSET 2
+ ) LIMIT 2
+ }
+ } {14 3}
+
+ do_test selectB-$ii.19 {
+ execsql {
+ SELECT * FROM (
+ SELECT DISTINCT (a/10) FROM t1 UNION ALL SELECT DISTINCT(d%2) FROM t2
+ )
+ }
+ } {0 1 0 1}
+
+ do_test selectB-$ii.20 {
+ execsql {
+ SELECT DISTINCT * FROM (
+ SELECT DISTINCT (a/10) FROM t1 UNION ALL SELECT DISTINCT(d%2) FROM t2
+ )
+ }
+ } {0 1}
+
+ do_test selectB-$ii.21 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 UNION ALL SELECT * FROM t2) ORDER BY a+b
+ }
+ } {2 4 6 3 6 9 8 10 12 12 15 18 14 16 18 21 24 27}
+
+ do_test selectB-$ii.21 {
+ execsql {
+ SELECT * FROM (SELECT 345 UNION ALL SELECT d FROM t2) ORDER BY 1;
+ }
+ } {3 12 21 345}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/server1.test b/third_party/sqlite/test/server1.test
new file mode 100755
index 0000000..134a9f5
--- /dev/null
+++ b/third_party/sqlite/test/server1.test
@@ -0,0 +1,171 @@
+# 2006 January 09
+#
+# 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 testing the server mode of SQLite.
+#
+# This file is derived from thread1.test
+#
+# $Id: server1.test,v 1.5 2007/08/29 18:20:17 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this whole file if the server testing code is not enabled
+#
+if {[llength [info command client_step]]==0 || [sqlite3 -has-codec]} {
+ finish_test
+ return
+}
+
+# The sample server implementation does not work right when memory
+# management is enabled.
+#
+ifcapable memorymanage {
+ finish_test
+ return
+}
+
+# Create some data to work with
+#
+do_test server1-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,'abcdefgh');
+ INSERT INTO t1 SELECT a+1, b||b FROM t1;
+ INSERT INTO t1 SELECT a+2, b||b FROM t1;
+ INSERT INTO t1 SELECT a+4, b||b FROM t1;
+ SELECT count(*), max(length(b)) FROM t1;
+ }
+} {8 64}
+
+# Interleave two threads on read access. Then make sure a third
+# thread can write the database. In other words:
+#
+# read-lock A
+# read-lock B
+# unlock A
+# unlock B
+# write-lock C
+#
+do_test server1-1.2 {
+ client_create A test.db
+ client_create B test.db
+ client_create C test.db
+ client_compile A {SELECT a FROM t1}
+ client_step A
+ client_result A
+} SQLITE_ROW
+do_test server1-1.3 {
+ client_argc A
+} 1
+do_test server1-1.4 {
+ client_argv A 0
+} 1
+do_test server1-1.5 {
+ client_compile B {SELECT b FROM t1}
+ client_step B
+ client_result B
+} SQLITE_ROW
+do_test server1-1.6 {
+ client_argc B
+} 1
+do_test server1-1.7 {
+ client_argv B 0
+} abcdefgh
+do_test server1-1.8 {
+ client_finalize A
+ client_result A
+} SQLITE_OK
+do_test server1-1.9 {
+ client_finalize B
+ client_result B
+} SQLITE_OK
+do_test server1-1.10 {
+ client_compile C {CREATE TABLE t2(x,y)}
+ client_step C
+ client_result C
+} SQLITE_DONE
+do_test server1-1.11 {
+ client_finalize C
+ client_result C
+} SQLITE_OK
+do_test server1-1.12 {
+ catchsql {SELECT name FROM sqlite_master}
+ execsql {SELECT name FROM sqlite_master}
+} {t1 t2}
+
+
+# Read from table t1. Do not finalize the statement. This
+# will leave the lock pending.
+#
+do_test server1-2.1 {
+ client_halt *
+ client_create A test.db
+ client_compile A {SELECT a FROM t1}
+ client_step A
+ client_result A
+} SQLITE_ROW
+
+# Read from the same table from another thread. This is allows.
+#
+do_test server1-2.2 {
+ client_create B test.db
+ client_compile B {SELECT b FROM t1}
+ client_step B
+ client_result B
+} SQLITE_ROW
+
+# Write to a different table from another thread. This is allowed
+# because in server mode with a shared cache we have table-level locking.
+#
+do_test server1-2.3 {
+ client_create C test.db
+ client_compile C {INSERT INTO t2 VALUES(98,99)}
+ client_step C
+ client_result C
+ client_finalize C
+ client_result C
+} SQLITE_OK
+
+# But we cannot insert into table t1 because threads A and B have it locked.
+#
+do_test server1-2.4 {
+ client_compile C {INSERT INTO t1 VALUES(98,99)}
+ client_step C
+ client_result C
+ client_finalize C
+ client_result C
+} SQLITE_LOCKED
+do_test server1-2.5 {
+ client_finalize B
+ client_wait B
+ client_compile C {INSERT INTO t1 VALUES(98,99)}
+ client_step C
+ client_result C
+ client_finalize C
+ client_result C
+} SQLITE_LOCKED
+
+# Insert into t1 is successful after finishing the other two threads.
+do_test server1-2.6 {
+ client_finalize A
+ client_wait A
+ client_compile C {INSERT INTO t1 VALUES(98,99)}
+ client_step C
+ client_result C
+ client_finalize C
+ client_result C
+} SQLITE_OK
+
+client_halt *
+sqlite3_enable_shared_cache 0
+finish_test
diff --git a/third_party/sqlite/test/shared.test b/third_party/sqlite/test/shared.test
new file mode 100755
index 0000000..61a28c4
--- /dev/null
+++ b/third_party/sqlite/test/shared.test
@@ -0,0 +1,1005 @@
+# 2005 December 30
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: shared.test,v 1.34 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+# These tests cannot be run without the ATTACH command.
+#
+ifcapable !shared_cache||!attach {
+ finish_test
+ return
+}
+
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+foreach av [list 0 1] {
+
+# Open the database connection and execute the auto-vacuum pragma
+file delete -force test.db
+sqlite3 db test.db
+
+ifcapable autovacuum {
+ do_test shared-[expr $av+1].1.0 {
+ execsql "pragma auto_vacuum=$::av"
+ execsql {pragma auto_vacuum}
+ } "$av"
+} else {
+ if {$av} {
+ db close
+ break
+ }
+}
+
+# $av is currently 0 if this loop iteration is to test with auto-vacuum turned
+# off, and 1 if it is turned on. Increment it so that (1 -> no auto-vacuum)
+# and (2 -> auto-vacuum). The sole reason for this is so that it looks nicer
+# when we use this variable as part of test-case names.
+#
+incr av
+
+# Test organization:
+#
+# shared-1.*: Simple test to verify basic sanity of table level locking when
+# two connections share a pager cache.
+# shared-2.*: Test that a read transaction can co-exist with a
+# write-transaction, including a simple test to ensure the
+# external locking protocol is still working.
+# shared-3.*: Simple test of read-uncommitted mode.
+# shared-4.*: Check that the schema is locked and unlocked correctly.
+# shared-5.*: Test that creating/dropping schema items works when databases
+# are attached in different orders to different handles.
+# shared-6.*: Locking, UNION ALL queries and sub-queries.
+# shared-7.*: Autovacuum and shared-cache.
+# shared-8.*: Tests related to the text encoding of shared-cache databases.
+# shared-9.*: TEMP triggers and shared-cache databases.
+# shared-10.*: Tests of sqlite3_close().
+# shared-11.*: Test transaction locking.
+#
+
+do_test shared-$av.1.1 {
+ # Open a second database on the file test.db. It should use the same pager
+ # cache and schema as the original connection. Verify that only 1 file is
+ # opened.
+ sqlite3 db2 test.db
+ set ::sqlite_open_file_count
+} {1}
+do_test shared-$av.1.2 {
+ # Add a table and a single row of data via the first connection.
+ # Ensure that the second connection can see them.
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ } db
+ execsql {
+ SELECT * FROM abc;
+ } db2
+} {1 2 3}
+do_test shared-$av.1.3 {
+ # Have the first connection begin a transaction and obtain a read-lock
+ # on table abc. This should not prevent the second connection from
+ # querying abc.
+ execsql {
+ BEGIN;
+ SELECT * FROM abc;
+ }
+ execsql {
+ SELECT * FROM abc;
+ } db2
+} {1 2 3}
+do_test shared-$av.1.4 {
+ # Try to insert a row into abc via connection 2. This should fail because
+ # of the read-lock connection 1 is holding on table abc (obtained in the
+ # previous test case).
+ catchsql {
+ INSERT INTO abc VALUES(4, 5, 6);
+ } db2
+} {1 {database table is locked: abc}}
+do_test shared-$av.1.5 {
+ # Using connection 2 (the one without the open transaction), try to create
+ # a new table. This should fail because of the open read transaction
+ # held by connection 1.
+ catchsql {
+ CREATE TABLE def(d, e, f);
+ } db2
+} {1 {database table is locked: sqlite_master}}
+do_test shared-$av.1.6 {
+ # Upgrade connection 1's transaction to a write transaction. Create
+ # a new table - def - and insert a row into it. Because the connection 1
+ # transaction modifies the schema, it should not be possible for
+ # connection 2 to access the database at all until the connection 1
+ # has finished the transaction.
+ execsql {
+ CREATE TABLE def(d, e, f);
+ INSERT INTO def VALUES('IV', 'V', 'VI');
+ }
+} {}
+do_test shared-$av.1.7 {
+ # Read from the sqlite_master table with connection 1 (inside the
+ # transaction). Then test that we can not do this with connection 2. This
+ # is because of the schema-modified lock established by connection 1
+ # in the previous test case.
+ execsql {
+ SELECT * FROM sqlite_master;
+ }
+ catchsql {
+ SELECT * FROM sqlite_master;
+ } db2
+} {1 {database schema is locked: main}}
+do_test shared-$av.1.8 {
+ # Commit the connection 1 transaction.
+ execsql {
+ COMMIT;
+ }
+} {}
+
+do_test shared-$av.2.1 {
+ # Open connection db3 to the database. Use a different path to the same
+ # file so that db3 does *not* share the same pager cache as db and db2
+ # (there should be two open file handles).
+ if {$::tcl_platform(platform)=="unix"} {
+ sqlite3 db3 ./test.db
+ } else {
+ sqlite3 db3 TEST.DB
+ }
+ set ::sqlite_open_file_count
+} {2}
+do_test shared-$av.2.2 {
+ # Start read transactions on db and db2 (the shared pager cache). Ensure
+ # db3 cannot write to the database.
+ execsql {
+ BEGIN;
+ SELECT * FROM abc;
+ }
+ execsql {
+ BEGIN;
+ SELECT * FROM abc;
+ } db2
+ catchsql {
+ INSERT INTO abc VALUES(1, 2, 3);
+ } db2
+} {1 {database table is locked: abc}}
+do_test shared-$av.2.3 {
+ # Turn db's transaction into a write-transaction. db3 should still be
+ # able to read from table def (but will not see the new row). Connection
+ # db2 should not be able to read def (because of the write-lock).
+
+# Todo: The failed "INSERT INTO abc ..." statement in the above test
+# has started a write-transaction on db2 (should this be so?). This
+# would prevent connection db from starting a write-transaction. So roll the
+# db2 transaction back and replace it with a new read transaction.
+ execsql {
+ ROLLBACK;
+ BEGIN;
+ SELECT * FROM abc;
+ } db2
+
+ execsql {
+ INSERT INTO def VALUES('VII', 'VIII', 'IX');
+ }
+ concat [
+ catchsql { SELECT * FROM def; } db3
+ ] [
+ catchsql { SELECT * FROM def; } db2
+ ]
+} {0 {IV V VI} 1 {database table is locked: def}}
+do_test shared-$av.2.4 {
+ # Commit the open transaction on db. db2 still holds a read-transaction.
+ # This should prevent db3 from writing to the database, but not from
+ # reading.
+ execsql {
+ COMMIT;
+ }
+ concat [
+ catchsql { SELECT * FROM def; } db3
+ ] [
+ catchsql { INSERT INTO def VALUES('X', 'XI', 'XII'); } db3
+ ]
+} {0 {IV V VI VII VIII IX} 1 {database is locked}}
+
+catchsql COMMIT db2
+
+do_test shared-$av.3.1.1 {
+ # This test case starts a linear scan of table 'seq' using a
+ # read-uncommitted connection. In the middle of the scan, rows are added
+ # to the end of the seq table (ahead of the current cursor position).
+ # The uncommitted rows should be included in the results of the scan.
+ execsql "
+ CREATE TABLE seq(i PRIMARY KEY, x);
+ INSERT INTO seq VALUES(1, '[string repeat X 500]');
+ INSERT INTO seq VALUES(2, '[string repeat X 500]');
+ "
+ execsql {SELECT * FROM sqlite_master} db2
+ execsql {PRAGMA read_uncommitted = 1} db2
+
+ set ret [list]
+ db2 eval {SELECT i FROM seq ORDER BY i} {
+ if {$i < 4} {
+ set max [execsql {SELECT max(i) FROM seq}]
+ db eval {
+ INSERT INTO seq SELECT i + :max, x FROM seq;
+ }
+ }
+ lappend ret $i
+ }
+ set ret
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
+do_test shared-$av.3.1.2 {
+ # Another linear scan through table seq using a read-uncommitted connection.
+ # This time, delete each row as it is read. Should not affect the results of
+ # the scan, but the table should be empty after the scan is concluded
+ # (test 3.1.3 verifies this).
+ set ret [list]
+ db2 eval {SELECT i FROM seq} {
+ db eval {DELETE FROM seq WHERE i = :i}
+ lappend ret $i
+ }
+ set ret
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
+do_test shared-$av.3.1.3 {
+ execsql {
+ SELECT * FROM seq;
+ }
+} {}
+
+catch {db close}
+catch {db2 close}
+catch {db3 close}
+
+#--------------------------------------------------------------------------
+# Tests shared-4.* test that the schema locking rules are applied
+# correctly. i.e.:
+#
+# 1. All transactions require a read-lock on the schemas of databases they
+# access.
+# 2. Transactions that modify a database schema require a write-lock on that
+# schema.
+# 3. It is not possible to compile a statement while another handle has a
+# write-lock on the schema.
+#
+
+# Open two database handles db and db2. Each has a single attach database
+# (as well as main):
+#
+# db.main -> ./test.db
+# db.test2 -> ./test2.db
+# db2.main -> ./test2.db
+# db2.test -> ./test.db
+#
+file delete -force test.db
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db test.db
+sqlite3 db2 test2.db
+do_test shared-$av.4.1.1 {
+ set sqlite_open_file_count
+} {2}
+do_test shared-$av.4.1.2 {
+ execsql {ATTACH 'test2.db' AS test2}
+ set sqlite_open_file_count
+} {2}
+do_test shared-$av.4.1.3 {
+ execsql {ATTACH 'test.db' AS test} db2
+ set sqlite_open_file_count
+} {2}
+
+# Sanity check: Create a table in ./test.db via handle db, and test that handle
+# db2 can "see" the new table immediately. A handle using a seperate pager
+# cache would have to reload the database schema before this were possible.
+#
+do_test shared-$av.4.2.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE def(d, e, f);
+ INSERT INTO abc VALUES('i', 'ii', 'iii');
+ INSERT INTO def VALUES('I', 'II', 'III');
+ }
+} {}
+do_test shared-$av.4.2.2 {
+ execsql {
+ SELECT * FROM test.abc;
+ } db2
+} {i ii iii}
+
+# Open a read-transaction and read from table abc via handle 2. Check that
+# handle 1 can read table abc. Check that handle 1 cannot modify table abc
+# or the database schema. Then check that handle 1 can modify table def.
+#
+do_test shared-$av.4.3.1 {
+ execsql {
+ BEGIN;
+ SELECT * FROM test.abc;
+ } db2
+} {i ii iii}
+do_test shared-$av.4.3.2 {
+ catchsql {
+ INSERT INTO abc VALUES('iv', 'v', 'vi');
+ }
+} {1 {database table is locked: abc}}
+do_test shared-$av.4.3.3 {
+ catchsql {
+ CREATE TABLE ghi(g, h, i);
+ }
+} {1 {database table is locked: sqlite_master}}
+do_test shared-$av.4.3.3 {
+ catchsql {
+ INSERT INTO def VALUES('IV', 'V', 'VI');
+ }
+} {0 {}}
+do_test shared-$av.4.3.4 {
+ # Cleanup: commit the transaction opened by db2.
+ execsql {
+ COMMIT
+ } db2
+} {}
+
+# Open a write-transaction using handle 1 and modify the database schema.
+# Then try to execute a compiled statement to read from the same
+# database via handle 2 (fails to get the lock on sqlite_master). Also
+# try to compile a read of the same database using handle 2 (also fails).
+# Finally, compile a read of the other database using handle 2. This
+# should also fail.
+#
+ifcapable compound {
+ do_test shared-$av.4.4.1.2 {
+ # Sanity check 1: Check that the schema is what we think it is when viewed
+ # via handle 1.
+ execsql {
+ CREATE TABLE test2.ghi(g, h, i);
+ SELECT 'test.db:'||name FROM sqlite_master
+ UNION ALL
+ SELECT 'test2.db:'||name FROM test2.sqlite_master;
+ }
+ } {test.db:abc test.db:def test2.db:ghi}
+ do_test shared-$av.4.4.1.2 {
+ # Sanity check 2: Check that the schema is what we think it is when viewed
+ # via handle 2.
+ execsql {
+ SELECT 'test2.db:'||name FROM sqlite_master
+ UNION ALL
+ SELECT 'test.db:'||name FROM test.sqlite_master;
+ } db2
+ } {test2.db:ghi test.db:abc test.db:def}
+}
+
+do_test shared-$av.4.4.2 {
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set sql {SELECT * FROM abc}
+ set ::STMT1 [sqlite3_prepare $::DB2 $sql -1 DUMMY]
+ execsql {
+ BEGIN;
+ CREATE TABLE jkl(j, k, l);
+ }
+ sqlite3_step $::STMT1
+} {SQLITE_ERROR}
+do_test shared-$av.4.4.3 {
+ sqlite3_finalize $::STMT1
+} {SQLITE_LOCKED}
+do_test shared-$av.4.4.4 {
+ set rc [catch {
+ set ::STMT1 [sqlite3_prepare $::DB2 $sql -1 DUMMY]
+ } msg]
+ list $rc $msg
+} {1 {(6) database schema is locked: test}}
+do_test shared-$av.4.4.5 {
+ set rc [catch {
+ set ::STMT1 [sqlite3_prepare $::DB2 "SELECT * FROM ghi" -1 DUMMY]
+ } msg]
+ list $rc $msg
+} {1 {(6) database schema is locked: test}}
+
+
+catch {db2 close}
+catch {db close}
+
+#--------------------------------------------------------------------------
+# Tests shared-5.*
+#
+foreach db [list test.db test1.db test2.db test3.db] {
+ file delete -force $db ${db}-journal
+}
+do_test shared-$av.5.1.1 {
+ sqlite3 db1 test.db
+ sqlite3 db2 test.db
+ execsql {
+ ATTACH 'test1.db' AS test1;
+ ATTACH 'test2.db' AS test2;
+ ATTACH 'test3.db' AS test3;
+ } db1
+ execsql {
+ ATTACH 'test3.db' AS test3;
+ ATTACH 'test2.db' AS test2;
+ ATTACH 'test1.db' AS test1;
+ } db2
+} {}
+do_test shared-$av.5.1.2 {
+ execsql {
+ CREATE TABLE test1.t1(a, b);
+ CREATE INDEX test1.i1 ON t1(a, b);
+ } db1
+} {}
+ifcapable view {
+ do_test shared-$av.5.1.3 {
+ execsql {
+ CREATE VIEW test1.v1 AS SELECT * FROM t1;
+ } db1
+ } {}
+}
+ifcapable trigger {
+ do_test shared-$av.5.1.4 {
+ execsql {
+ CREATE TRIGGER test1.trig1 AFTER INSERT ON t1 BEGIN
+ INSERT INTO t1 VALUES(new.a, new.b);
+ END;
+ } db1
+ } {}
+}
+do_test shared-$av.5.1.5 {
+ execsql {
+ DROP INDEX i1;
+ } db2
+} {}
+ifcapable view {
+ do_test shared-$av.5.1.6 {
+ execsql {
+ DROP VIEW v1;
+ } db2
+ } {}
+}
+ifcapable trigger {
+ do_test shared-$av.5.1.7 {
+ execsql {
+ DROP TRIGGER trig1;
+ } db2
+ } {}
+}
+do_test shared-$av.5.1.8 {
+ execsql {
+ DROP TABLE t1;
+ } db2
+} {}
+ifcapable compound {
+ do_test shared-$av.5.1.9 {
+ execsql {
+ SELECT * FROM sqlite_master UNION ALL SELECT * FROM test1.sqlite_master
+ } db1
+ } {}
+}
+
+#--------------------------------------------------------------------------
+# Tests shared-6.* test that a query obtains all the read-locks it needs
+# before starting execution of the query. This means that there is no chance
+# some rows of data will be returned before a lock fails and SQLITE_LOCK
+# is returned.
+#
+do_test shared-$av.6.1.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t2 VALUES(3, 4);
+ } db1
+} {}
+ifcapable compound {
+ do_test shared-$av.6.1.2 {
+ execsql {
+ SELECT * FROM t1 UNION ALL SELECT * FROM t2;
+ } db2
+ } {1 2 3 4}
+}
+do_test shared-$av.6.1.3 {
+ # Establish a write lock on table t2 via connection db2. Then make a
+ # UNION all query using connection db1 that first accesses t1, followed
+ # by t2. If the locks are grabbed at the start of the statement (as
+ # they should be), no rows are returned. If (as was previously the case)
+ # they are grabbed as the tables are accessed, the t1 rows will be
+ # returned before the query fails.
+ #
+ execsql {
+ BEGIN;
+ INSERT INTO t2 VALUES(5, 6);
+ } db2
+ set ret [list]
+ catch {
+ db1 eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2} {
+ lappend ret $a $b
+ }
+ }
+ set ret
+} {}
+do_test shared-$av.6.1.4 {
+ execsql {
+ COMMIT;
+ BEGIN;
+ INSERT INTO t1 VALUES(7, 8);
+ } db2
+ set ret [list]
+ catch {
+ db1 eval {
+ SELECT (CASE WHEN a>4 THEN (SELECT a FROM t1) ELSE 0 END) AS d FROM t2;
+ } {
+ lappend ret $d
+ }
+ }
+ set ret
+} {}
+
+catch {db1 close}
+catch {db2 close}
+foreach f [list test.db test2.db] {
+ file delete -force $f ${f}-journal
+}
+
+#--------------------------------------------------------------------------
+# Tests shared-7.* test auto-vacuum does not invalidate cursors from
+# other shared-cache users when it reorganizes the database on
+# COMMIT.
+#
+do_test shared-$av.7.1 {
+ # This test case sets up a test database in auto-vacuum mode consisting
+ # of two tables, t1 and t2. Both have a single index. Table t1 is
+ # populated first (so consists of pages toward the start of the db file),
+ # t2 second (pages toward the end of the file).
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ CREATE TABLE t2(a PRIMARY KEY, b);
+ }
+ set ::contents {}
+ for {set i 0} {$i < 100} {incr i} {
+ set a [string repeat "$i " 20]
+ set b [string repeat "$i " 20]
+ db eval {
+ INSERT INTO t1 VALUES(:a, :b);
+ }
+ lappend ::contents [list [expr $i+1] $a $b]
+ }
+ execsql {
+ INSERT INTO t2 SELECT * FROM t1;
+ COMMIT;
+ }
+} {}
+do_test shared-$av.7.2 {
+ # This test case deletes the contents of table t1 (the one at the start of
+ # the file) while many cursors are open on table t2 and its index. All of
+ # the non-root pages will be moved from the end to the start of the file
+ # when the DELETE is committed - this test verifies that moving the pages
+ # does not disturb the open cursors.
+ #
+
+ proc lockrow {db tbl oids body} {
+ set ret [list]
+ db eval "SELECT oid AS i, a, b FROM $tbl ORDER BY a" {
+ if {$i==[lindex $oids 0]} {
+ set noids [lrange $oids 1 end]
+ if {[llength $noids]==0} {
+ set subret [eval $body]
+ } else {
+ set subret [lockrow $db $tbl $noids $body]
+ }
+ }
+ lappend ret [list $i $a $b]
+ }
+ return [linsert $subret 0 $ret]
+ }
+ proc locktblrows {db tbl body} {
+ set oids [db eval "SELECT oid FROM $tbl"]
+ lockrow $db $tbl $oids $body
+ }
+
+ set scans [locktblrows db t2 {
+ execsql {
+ DELETE FROM t1;
+ } db2
+ }]
+ set error 0
+
+ # Test that each SELECT query returned the expected contents of t2.
+ foreach s $scans {
+ if {[lsort -integer -index 0 $s]!=$::contents} {
+ set error 1
+ }
+ }
+ set error
+} {0}
+
+catch {db close}
+catch {db2 close}
+unset -nocomplain contents
+
+#--------------------------------------------------------------------------
+# The following tests try to trick the shared-cache code into assuming
+# the wrong encoding for a database.
+#
+file delete -force test.db test.db-journal
+ifcapable utf16 {
+ do_test shared-$av.8.1.1 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA encoding = 'UTF-16';
+ SELECT * FROM sqlite_master;
+ }
+ } {}
+ do_test shared-$av.8.1.2 {
+ string range [execsql {PRAGMA encoding;}] 0 end-2
+ } {UTF-16}
+
+ do_test shared-$av.8.1.3 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA encoding = 'UTF-8';
+ CREATE TABLE abc(a, b, c);
+ } db2
+ } {}
+ do_test shared-$av.8.1.4 {
+ execsql {
+ SELECT * FROM sqlite_master;
+ }
+ } "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}"
+ do_test shared-$av.8.1.5 {
+ db2 close
+ execsql {
+ PRAGMA encoding;
+ }
+ } {UTF-8}
+
+ file delete -force test2.db test2.db-journal
+ do_test shared-$av.8.2.1 {
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ SELECT * FROM aux.sqlite_master;
+ }
+ } {}
+ do_test shared-$av.8.2.2 {
+ sqlite3 db2 test2.db
+ execsql {
+ PRAGMA encoding = 'UTF-16';
+ CREATE TABLE def(d, e, f);
+ } db2
+ string range [execsql {PRAGMA encoding;} db2] 0 end-2
+ } {UTF-16}
+
+ catch {db close}
+ catch {db2 close}
+ file delete -force test.db test2.db
+
+ do_test shared-$av.8.3.2 {
+ sqlite3 db test.db
+ execsql { CREATE TABLE def(d, e, f) }
+ execsql { PRAGMA encoding }
+ } {UTF-8}
+ do_test shared-$av.8.3.3 {
+ set zDb16 "[encoding convertto unicode test.db]\x00\x00"
+ set db16 [sqlite3_open16 $zDb16 {}]
+
+ set stmt [sqlite3_prepare $db16 "SELECT sql FROM sqlite_master" -1 DUMMY]
+ sqlite3_step $stmt
+ set sql [sqlite3_column_text $stmt 0]
+ sqlite3_finalize $stmt
+ set sql
+ } {CREATE TABLE def(d, e, f)}
+ do_test shared-$av.8.3.4 {
+ set stmt [sqlite3_prepare $db16 "PRAGMA encoding" -1 DUMMY]
+ sqlite3_step $stmt
+ set enc [sqlite3_column_text $stmt 0]
+ sqlite3_finalize $stmt
+ set enc
+ } {UTF-8}
+
+ sqlite3_close $db16
+
+# Bug #2547 is causing this to fail.
+if 0 {
+ do_test shared-$av.8.2.3 {
+ catchsql {
+ SELECT * FROM aux.sqlite_master;
+ }
+ } {1 {attached databases must use the same text encoding as main database}}
+}
+}
+
+catch {db close}
+catch {db2 close}
+file delete -force test.db test2.db
+
+#---------------------------------------------------------------------------
+# The following tests - shared-9.* - test interactions between TEMP triggers
+# and shared-schemas.
+#
+ifcapable trigger&&tempdb {
+
+do_test shared-$av.9.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE abc_mirror(a, b, c);
+ CREATE TEMP TRIGGER BEFORE INSERT ON abc BEGIN
+ INSERT INTO abc_mirror(a, b, c) VALUES(new.a, new.b, new.c);
+ END;
+ INSERT INTO abc VALUES(1, 2, 3);
+ SELECT * FROM abc_mirror;
+ }
+} {1 2 3}
+do_test shared-$av.9.2 {
+ execsql {
+ INSERT INTO abc VALUES(4, 5, 6);
+ SELECT * FROM abc_mirror;
+ } db2
+} {1 2 3}
+do_test shared-$av.9.3 {
+ db close
+ db2 close
+} {}
+
+} ; # End shared-9.*
+
+#---------------------------------------------------------------------------
+# The following tests - shared-10.* - test that the library behaves
+# correctly when a connection to a shared-cache is closed.
+#
+do_test shared-$av.10.1 {
+ # Create a small sample database with two connections to it (db and db2).
+ file delete -force test.db
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE ab(a PRIMARY KEY, b);
+ CREATE TABLE de(d PRIMARY KEY, e);
+ INSERT INTO ab VALUES('Chiang Mai', 100000);
+ INSERT INTO ab VALUES('Bangkok', 8000000);
+ INSERT INTO de VALUES('Ubon', 120000);
+ INSERT INTO de VALUES('Khon Kaen', 200000);
+ }
+} {}
+do_test shared-$av.10.2 {
+ # Open a read-transaction with the first connection, a write-transaction
+ # with the second.
+ execsql {
+ BEGIN;
+ SELECT * FROM ab;
+ }
+ execsql {
+ BEGIN;
+ INSERT INTO de VALUES('Pataya', 30000);
+ } db2
+} {}
+do_test shared-$av.10.3 {
+ # An external connection should be able to read the database, but not
+ # prepare a write operation.
+ if {$::tcl_platform(platform)=="unix"} {
+ sqlite3 db3 ./test.db
+ } else {
+ sqlite3 db3 TEST.DB
+ }
+ execsql {
+ SELECT * FROM ab;
+ } db3
+ catchsql {
+ BEGIN;
+ INSERT INTO de VALUES('Pataya', 30000);
+ } db3
+} {1 {database is locked}}
+do_test shared-$av.10.4 {
+ # Close the connection with the write-transaction open
+ db2 close
+} {}
+do_test shared-$av.10.5 {
+ # Test that the db2 transaction has been automatically rolled back.
+ # If it has not the ('Pataya', 30000) entry will still be in the table.
+ execsql {
+ SELECT * FROM de;
+ }
+} {Ubon 120000 {Khon Kaen} 200000}
+do_test shared-$av.10.5 {
+ # Closing db2 should have dropped the shared-cache back to a read-lock.
+ # So db3 should be able to prepare a write...
+ catchsql {INSERT INTO de VALUES('Pataya', 30000);} db3
+} {0 {}}
+do_test shared-$av.10.6 {
+ # ... but not commit it.
+ catchsql {COMMIT} db3
+} {1 {database is locked}}
+do_test shared-$av.10.7 {
+ # Commit the (read-only) db transaction. Check via db3 to make sure the
+ # contents of table "de" are still as they should be.
+ execsql {
+ COMMIT;
+ }
+ execsql {
+ SELECT * FROM de;
+ } db3
+} {Ubon 120000 {Khon Kaen} 200000 Pataya 30000}
+do_test shared-$av.10.9 {
+ # Commit the external transaction.
+ catchsql {COMMIT} db3
+} {0 {}}
+integrity_check shared-$av.10.10
+do_test shared-$av.10.11 {
+ db close
+ db3 close
+} {}
+
+do_test shared-$av.11.1 {
+ file delete -force test.db
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE TABLE abc2(a, b, c);
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ }
+} {}
+do_test shared-$av.11.2 {
+ catchsql {BEGIN;} db2
+ catchsql {SELECT * FROM abc;} db2
+} {1 {database table is locked: abc}}
+do_test shared-$av.11.3 {
+ catchsql {BEGIN} db2
+} {1 {cannot start a transaction within a transaction}}
+do_test shared-$av.11.4 {
+ catchsql {SELECT * FROM abc2;} db2
+} {0 {}}
+do_test shared-$av.11.5 {
+ catchsql {INSERT INTO abc2 VALUES(1, 2, 3);} db2
+} {1 {database is locked}}
+do_test shared-$av.11.6 {
+ catchsql {SELECT * FROM abc2}
+} {0 {}}
+do_test shared-$av.11.6 {
+ execsql {
+ ROLLBACK;
+ PRAGMA read_uncommitted = 1;
+ } db2
+} {}
+do_test shared-$av.11.7 {
+ execsql {
+ INSERT INTO abc2 VALUES(4, 5, 6);
+ INSERT INTO abc2 VALUES(7, 8, 9);
+ }
+} {}
+do_test shared-$av.11.8 {
+ set res [list]
+ db2 eval {
+ SELECT abc.a as I, abc2.a as II FROM abc, abc2;
+ } {
+ execsql {
+ DELETE FROM abc WHERE 1;
+ }
+ lappend res $I $II
+ }
+ set res
+} {1 4 {} 7}
+if {[llength [info command sqlite3_shared_cache_report]]==1} {
+ do_test shared-$av.11.9 {
+ string tolower [sqlite3_shared_cache_report]
+ } [string tolower [list [file nativename [file normalize test.db]] 2]]
+}
+
+do_test shared-$av.11.11 {
+ db close
+ db2 close
+} {}
+
+# This tests that if it is impossible to free any pages, SQLite will
+# exceed the limit set by PRAGMA cache_size.
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+ifcapable pager_pragmas {
+ do_test shared-$av.12.1 {
+ execsql {
+ PRAGMA cache_size = 10;
+ PRAGMA cache_size;
+ }
+ } {10}
+}
+do_test shared-$av.12.2 {
+ set ::db_handles [list]
+ for {set i 1} {$i < 15} {incr i} {
+ lappend ::db_handles db$i
+ sqlite3 db$i test.db
+ execsql "CREATE TABLE db${i}(a, b, c)" db$i
+ execsql "INSERT INTO db${i} VALUES(1, 2, 3)"
+ }
+} {}
+proc nested_select {handles} {
+ [lindex $handles 0] eval "SELECT * FROM [lindex $handles 0]" {
+ lappend ::res $a $b $c
+ if {[llength $handles]>1} {
+ nested_select [lrange $handles 1 end]
+ }
+ }
+}
+do_test shared-$av.12.3 {
+ set ::res [list]
+ nested_select $::db_handles
+ set ::res
+} [string range [string repeat "1 2 3 " [llength $::db_handles]] 0 end-1]
+
+do_test shared-$av.12.X {
+ db close
+ foreach h $::db_handles {
+ $h close
+ }
+} {}
+
+# Internally, locks are acquired on shared B-Tree structures in the order
+# that the structures appear in the virtual memory address space. This
+# test case attempts to cause the order of the structures in memory
+# to be different from the order in which they are attached to a given
+# database handle. This covers an extra line or two.
+#
+do_test shared-$av.13.1 {
+ file delete -force test2.db test3.db test4.db test5.db
+ sqlite3 db :memory:
+ execsql {
+ ATTACH 'test2.db' AS aux2;
+ ATTACH 'test3.db' AS aux3;
+ ATTACH 'test4.db' AS aux4;
+ ATTACH 'test5.db' AS aux5;
+ DETACH aux2;
+ DETACH aux3;
+ DETACH aux4;
+ ATTACH 'test2.db' AS aux2;
+ ATTACH 'test3.db' AS aux3;
+ ATTACH 'test4.db' AS aux4;
+ }
+} {}
+do_test shared-$av.13.2 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE aux2.t2(a, b, c);
+ CREATE TABLE aux3.t3(a, b, c);
+ CREATE TABLE aux4.t4(a, b, c);
+ CREATE TABLE aux5.t5(a, b, c);
+ SELECT count(*) FROM
+ aux2.sqlite_master,
+ aux3.sqlite_master,
+ aux4.sqlite_master,
+ aux5.sqlite_master
+ }
+} {1}
+do_test shared-$av.13.3 {
+ db close
+} {}
+
+# Test that nothing horrible happens if a connection to a shared B-Tree
+# structure is closed while some other connection has an open cursor.
+#
+do_test shared-$av.14.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {SELECT name FROM sqlite_master}
+} {db1 db2 db3 db4 db5 db6 db7 db8 db9 db10 db11 db12 db13 db14}
+do_test shared-$av.14.2 {
+ set res [list]
+ db eval {SELECT name FROM sqlite_master} {
+ if {$name eq "db7"} {
+ db2 close
+ }
+ lappend res $name
+ }
+ set res
+} {db1 db2 db3 db4 db5 db6 db7 db8 db9 db10 db11 db12 db13 db14}
+do_test shared-$av.14.3 {
+ db close
+} {}
+
+}
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/shared2.test b/third_party/sqlite/test/shared2.test
new file mode 100755
index 0000000..5f18f8b
--- /dev/null
+++ b/third_party/sqlite/test/shared2.test
@@ -0,0 +1,131 @@
+# 2005 January 19
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: shared2.test,v 1.5 2007/08/23 02:47:54 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+ifcapable !shared_cache {
+ finish_test
+ return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+# Test that if we delete all rows from a table any read-uncommitted
+# cursors are correctly invalidated. Test on both table and index btrees.
+do_test shared2-1.1 {
+ sqlite3 db1 test.db
+ sqlite3 db2 test.db
+
+ # Set up some data. Table "numbers" has 64 rows after this block
+ # is executed.
+ execsql {
+ BEGIN;
+ CREATE TABLE numbers(a PRIMARY KEY, b);
+ INSERT INTO numbers(oid) VALUES(NULL);
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ INSERT INTO numbers(oid) SELECT NULL FROM numbers;
+ UPDATE numbers set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789';
+ COMMIT;
+ } db1
+} {}
+do_test shared2-1.2 {
+ # Put connection 2 in read-uncommitted mode and start a SELECT on table
+ # 'numbers'. Half way through the SELECT, use connection 1 to delete the
+ # contents of this table.
+ execsql {
+ pragma read_uncommitted = 1;
+ } db2
+ set count [execsql {SELECT count(*) FROM numbers} db2]
+ db2 eval {SELECT a FROM numbers ORDER BY oid} {
+ if {$a==32} {
+ execsql {
+ BEGIN;
+ DELETE FROM numbers;
+ } db1
+ }
+ }
+ list $a $count
+} {32 64}
+do_test shared2-1.3 {
+ # Same test as 1.2, except scan using the index this time.
+ execsql {
+ ROLLBACK;
+ } db1
+ set count [execsql {SELECT count(*) FROM numbers} db2]
+ db2 eval {SELECT a, b FROM numbers ORDER BY a} {
+ if {$a==32} {
+ execsql {
+ DELETE FROM numbers;
+ } db1
+ }
+ }
+ list $a $count
+} {32 64}
+
+#---------------------------------------------------------------------------
+# These tests, shared2.2.*, test the outcome when data is added to or
+# removed from a table due to a rollback while a read-uncommitted
+# cursor is scanning it.
+#
+do_test shared2-2.1 {
+ execsql {
+ INSERT INTO numbers VALUES(1, 'Medium length text field');
+ INSERT INTO numbers VALUES(2, 'Medium length text field');
+ INSERT INTO numbers VALUES(3, 'Medium length text field');
+ INSERT INTO numbers VALUES(4, 'Medium length text field');
+ BEGIN;
+ DELETE FROM numbers WHERE (a%2)=0;
+ } db1
+ set res [list]
+ db2 eval {
+ SELECT a FROM numbers ORDER BY a;
+ } {
+ lappend res $a
+ if {$a==3} {
+ execsql {ROLLBACK} db1
+ }
+ }
+ set res
+} {1 3 4}
+do_test shared2-2.2 {
+ execsql {
+ BEGIN;
+ INSERT INTO numbers VALUES(5, 'Medium length text field');
+ INSERT INTO numbers VALUES(6, 'Medium length text field');
+ } db1
+ set res [list]
+ db2 eval {
+ SELECT a FROM numbers ORDER BY a;
+ } {
+ lappend res $a
+ if {$a==5} {
+ execsql {ROLLBACK} db1
+ }
+ }
+ set res
+} {1 2 3 4 5}
+
+db1 close
+db2 close
+
+do_test shared2-3.2 {
+ sqlite3_enable_shared_cache 1
+} {1}
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/shared3.test b/third_party/sqlite/test/shared3.test
new file mode 100755
index 0000000..0f05aac
--- /dev/null
+++ b/third_party/sqlite/test/shared3.test
@@ -0,0 +1,105 @@
+# 2005 January 19
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: shared3.test,v 1.3 2008/06/23 09:50:52 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+ifcapable !shared_cache {
+ finish_test
+ return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+# Ticket #1824
+#
+do_test shared3-1.1 {
+ file delete -force test.db test.db-journal
+ sqlite3 db1 test.db
+ db1 eval {
+ PRAGMA encoding=UTF16;
+ CREATE TABLE t1(x,y);
+ INSERT INTO t1 VALUES('abc','This is a test string');
+ }
+ db1 close
+ sqlite3 db1 test.db
+ db1 eval {SELECT * FROM t1}
+} {abc {This is a test string}}
+do_test shared3-1.2 {
+ sqlite3 db2 test.db
+ db2 eval {SELECT y FROM t1 WHERE x='abc'}
+} {{This is a test string}}
+
+db1 close
+db2 close
+
+do_test shared3-2.1 {
+ sqlite3 db1 test.db
+ execsql {
+ PRAGMA main.cache_size = 10;
+ } db1
+} {}
+do_test shared3-2.2 {
+ execsql { PRAGMA main.cache_size } db1
+} {10}
+do_test shared3-2.3 {
+ sqlite3 db2 test.db
+ execsql { PRAGMA main.cache_size } db1
+} {10}
+do_test shared3-2.4 {
+ execsql { PRAGMA main.cache_size } db2
+} {10}
+do_test shared3-2.5 {
+ execsql { PRAGMA main.cache_size } db1
+} {10}
+
+# The cache-size should now be 10 pages. However at one point there was
+# a bug that caused the cache size to return to the default value when
+# a second connection was opened on the shared-cache (as happened in
+# test case shared3-2.3 above). The goal of the following tests is to
+# ensure that the cache-size really is 10 pages.
+#
+if {$::tcl_platform(platform)=="unix"} {
+ set alternative_name ./test.db
+} else {
+ set alternative_name TEST.DB
+}
+do_test shared3-2.6 {
+ sqlite3 db3 $alternative_name
+ catchsql {select count(*) from sqlite_master} db3
+} {0 1}
+do_test shared3-2.7 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(10, randomblob(5000))
+ } db1
+ catchsql {select count(*) from sqlite_master} db3
+} {0 1}
+do_test shared3-2.8 {
+ execsql {
+ INSERT INTO t1 VALUES(10, randomblob(10000))
+ } db1
+
+ # If the pager-cache is really still limited to 10 pages, then the INSERT
+ # statement above should have caused the pager to grab an exclusive lock
+ # on the database file so that the cache could be spilled.
+ #
+ catchsql {select count(*) from sqlite_master} db3
+} {1 {database is locked}}
+
+db1 close
+db2 close
+db3 close
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/shared4.test b/third_party/sqlite/test/shared4.test
new file mode 100755
index 0000000..f9f0173
--- /dev/null
+++ b/third_party/sqlite/test/shared4.test
@@ -0,0 +1,237 @@
+# 2008 July 14
+#
+# 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.
+#
+#***********************************************************************
+#
+# Test the btree mutex protocol for shared cache mode.
+#
+# $Id: shared4.test,v 1.2 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+puts hello
+
+# This script is only valid if we are running shared-cache mode in a
+# threadsafe-capable database engine.
+#
+ifcapable !shared_cache||!compound {
+ finish_test
+ return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+# Prepare multiple databases in shared cache mode.
+#
+do_test shared4-1.1 {
+ file delete -force test1.db test1.db-journal
+ file delete -force test2.db test2.db-journal
+ file delete -force test3.db test3.db-journal
+ file delete -force test4.db test4.db-journal
+ sqlite3 db1 test1.db
+ sqlite3 db2 test2.db
+ sqlite3 db3 test3.db
+ sqlite3 db4 test4.db
+ db1 eval {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(111);
+ }
+ db2 eval {
+ CREATE TABLE t2(b);
+ INSERT INTO t2 VALUES(222);
+ }
+ db3 eval {
+ CREATE TABLE t3(c);
+ INSERT INTO t3 VALUES(333);
+ }
+ db4 eval {
+ CREATE TABLE t4(d);
+ INSERT INTO t4 VALUES(444);
+ }
+ db1 eval {
+ ATTACH DATABASE 'test2.db' AS two;
+ ATTACH DATABASE 'test3.db' AS three;
+ ATTACH DATABASE 'test4.db' AS four;
+ }
+ db2 eval {
+ ATTACH DATABASE 'test4.db' AS four;
+ ATTACH DATABASE 'test3.db' AS three;
+ ATTACH DATABASE 'test1.db' AS one;
+ }
+ db3 eval {
+ ATTACH DATABASE 'test1.db' AS one;
+ ATTACH DATABASE 'test2.db' AS two;
+ ATTACH DATABASE 'test4.db' AS four;
+ }
+ db4 eval {
+ ATTACH DATABASE 'test3.db' AS three;
+ ATTACH DATABASE 'test2.db' AS two;
+ ATTACH DATABASE 'test1.db' AS one;
+ }
+ db1 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4;
+ }
+} {111 222 333 444}
+do_test shared4-1.2 {
+ db2 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT c FROM t3;
+ }
+} {111 222 444 333}
+do_test shared4-1.3 {
+ db3 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT d FROM t4;
+ }
+} {111 333 222 444}
+do_test shared4-1.4 {
+ db4 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT b FROM t2;
+ }
+} {111 333 444 222}
+do_test shared4-1.5 {
+ db3 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT c FROM t3;
+ }
+} {111 444 222 333}
+do_test shared4-1.6 {
+ db4 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT b FROM t2;
+ }
+} {111 444 333 222}
+do_test shared4-1.7 {
+ db1 eval {
+ SELECT b FROM t2 UNION ALL
+ SELECT a FROM t1 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4;
+ }
+} {222 111 333 444}
+do_test shared4-1.8 {
+ db2 eval {
+ SELECT b FROM t2 UNION ALL
+ SELECT a FROM t1 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT c FROM t3;
+ }
+} {222 111 444 333}
+do_test shared4-1.9 {
+ db3 eval {
+ SELECT b FROM t2 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT a FROM t1 UNION ALL
+ SELECT d FROM t4;
+ }
+} {222 333 111 444}
+do_test shared4-1.10 {
+ db4 eval {
+ SELECT b FROM t2 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT a FROM t1;
+ }
+} {222 333 444 111}
+do_test shared4-1.11 {
+ db1 eval {
+ SELECT c FROM t3 UNION ALL
+ SELECT a FROM t1 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT d FROM t4;
+ }
+} {333 111 222 444}
+do_test shared4-1.12 {
+ db2 eval {
+ SELECT c FROM t3 UNION ALL
+ SELECT a FROM t1 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT b FROM t2;
+ }
+} {333 111 444 222}
+
+do_test shared4-2.1 {
+ db1 eval {
+ UPDATE t1 SET a=a+1000;
+ UPDATE t2 SET b=b+2000;
+ UPDATE t3 SET c=c+3000;
+ UPDATE t4 SET d=d+4000;
+ }
+ db2 eval {
+ UPDATE t1 SET a=a+10000;
+ UPDATE t2 SET b=b+20000;
+ UPDATE t3 SET c=c+30000;
+ UPDATE t4 SET d=d+40000;
+ }
+ db3 eval {
+ UPDATE t1 SET a=a+100000;
+ UPDATE t2 SET b=b+200000;
+ UPDATE t3 SET c=c+300000;
+ UPDATE t4 SET d=d+400000;
+ }
+ db4 eval {
+ UPDATE t1 SET a=a+1000000;
+ UPDATE t2 SET b=b+2000000;
+ UPDATE t3 SET c=c+3000000;
+ UPDATE t4 SET d=d+4000000;
+ }
+ db1 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4;
+ }
+} {1111111 2222222 3333333 4444444}
+do_test shared4-2.2 {
+ db2 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT c FROM t3;
+ }
+} {1111111 2222222 4444444 3333333}
+do_test shared4-2.3 {
+ db3 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT b FROM t2 UNION ALL
+ SELECT d FROM t4;
+ }
+} {1111111 3333333 2222222 4444444}
+do_test shared4-2.4 {
+ db4 eval {
+ SELECT a FROM t1 UNION ALL
+ SELECT c FROM t3 UNION ALL
+ SELECT d FROM t4 UNION ALL
+ SELECT b FROM t2;
+ }
+} {1111111 3333333 4444444 2222222}
+
+
+db1 close
+db2 close
+db3 close
+db4 close
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/shared_err.test b/third_party/sqlite/test/shared_err.test
new file mode 100755
index 0000000..95e8c90
--- /dev/null
+++ b/third_party/sqlite/test/shared_err.test
@@ -0,0 +1,501 @@
+# 2005 December 30
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of the tests in this file are IO errors that occur in a shared
+# cache context. What happens to connection B if one connection A encounters
+# an IO-error whilst reading or writing the file-system?
+#
+# $Id: shared_err.test,v 1.21 2008/07/07 17:55:29 danielk1977 Exp $
+
+proc skip {args} {}
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+db close
+
+ifcapable !shared_cache||!subquery {
+ finish_test
+ return
+}
+
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+do_ioerr_test shared_ioerr-1 -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ CREATE TABLE t1(a,b,c);
+ BEGIN;
+ SELECT * FROM sqlite_master;
+ } db2
+} -sqlbody {
+ SELECT * FROM sqlite_master;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(4,5,6);
+ ROLLBACK;
+ SELECT * FROM t1;
+ BEGIN TRANSACTION;
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(4,5,6);
+ COMMIT;
+ SELECT * FROM t1;
+ DELETE FROM t1 WHERE a<100;
+} -cleanup {
+ do_test shared_ioerr-1.$n.cleanup.1 {
+ set res [catchsql {
+ SELECT * FROM t1;
+ } db2]
+ set possible_results [list \
+ "1 {disk I/O error}" \
+ "0 {1 2 3}" \
+ "0 {1 2 3 1 2 3 4 5 6}" \
+ "0 {1 2 3 1 2 3 4 5 6 1 2 3 4 5 6}" \
+ "0 {}" \
+ ]
+ set rc [expr [lsearch -exact $possible_results $res] >= 0]
+ if {$rc != 1} {
+ puts ""
+ puts "Result: $res"
+ }
+ set rc
+ } {1}
+ db2 close
+}
+
+do_ioerr_test shared_ioerr-2 -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1(oid) VALUES(NULL);
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ INSERT INTO t1(oid) SELECT NULL FROM t1;
+ UPDATE t1 set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789';
+ CREATE INDEX i1 ON t1(a);
+ COMMIT;
+ BEGIN;
+ SELECT * FROM sqlite_master;
+ } db2
+} -tclbody {
+ set ::residx 0
+ execsql {DELETE FROM t1 WHERE 0 = (a % 2);}
+ incr ::residx
+
+ # When this transaction begins the table contains 512 entries. The
+ # two statements together add 512+146 more if it succeeds.
+ # (1024/7==146)
+ execsql {BEGIN;}
+ execsql {INSERT INTO t1 SELECT a+1, b FROM t1;}
+ execsql {INSERT INTO t1 SELECT 'string' || a, b FROM t1 WHERE 0 = (a%7);}
+ execsql {COMMIT;}
+
+ incr ::residx
+} -cleanup {
+ catchsql ROLLBACK
+ do_test shared_ioerr-2.$n.cleanup.1 {
+ set res [catchsql {
+ SELECT max(a), min(a), count(*) FROM (SELECT a FROM t1 order by a);
+ } db2]
+ set possible_results [list \
+ {0 {1024 1 1024}} \
+ {0 {1023 1 512}} \
+ {0 {string994 1 1170}} \
+ ]
+ set idx [lsearch -exact $possible_results $res]
+ set success [expr {$idx==$::residx || $res=="1 {disk I/O error}"}]
+ if {!$success} {
+ puts ""
+ puts "Result: \"$res\" ($::residx)"
+ }
+ set success
+ } {1}
+ db2 close
+}
+
+# This test is designed to provoke an IO error when a cursor position is
+# "saved" (because another cursor is going to modify the underlying table).
+#
+do_ioerr_test shared_ioerr-3 -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE t1(a, b, UNIQUE(a, b));
+ } db2
+ for {set i 0} {$i < 200} {incr i} {
+ set a [string range [string repeat "[format %03d $i]." 5] 0 end-1]
+
+ set b [string repeat $i 2000]
+ execsql {INSERT INTO t1 VALUES($a, $b)} db2
+ }
+ execsql {COMMIT} db2
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY]
+ sqlite3_step $::STMT ;# Cursor points at 000.000.000.000
+ sqlite3_step $::STMT ;# Cursor points at 001.001.001.001
+
+} -tclbody {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES('201.201.201.201.201', NULL);
+ UPDATE t1 SET a = '202.202.202.202.202' WHERE a LIKE '201%';
+ COMMIT;
+ }
+} -cleanup {
+ set ::steprc [sqlite3_step $::STMT]
+ set ::column [sqlite3_column_text $::STMT 0]
+ set ::finalrc [sqlite3_finalize $::STMT]
+
+ # There are three possible outcomes here (assuming persistent IO errors):
+ #
+ # 1. If the [sqlite3_step] did not require any IO (required pages in
+ # the cache), then the next row ("002...") may be retrieved
+ # successfully.
+ #
+ # 2. If the [sqlite3_step] does require IO, then [sqlite3_step] returns
+ # SQLITE_ERROR and [sqlite3_finalize] returns IOERR.
+ #
+ # 3. If, after the initial IO error, SQLite tried to rollback the
+ # active transaction and a second IO error was encountered, then
+ # statement $::STMT will have been aborted. This means [sqlite3_stmt]
+ # returns SQLITE_ABORT, and the statement cursor does not move. i.e.
+ # [sqlite3_column] still returns the current row ("001...") and
+ # [sqlite3_finalize] returns SQLITE_OK.
+ #
+
+ do_test shared_ioerr-3.$n.cleanup.1 {
+ expr {
+ $::steprc eq "SQLITE_ROW" ||
+ $::steprc eq "SQLITE_ERROR" ||
+ $::steprc eq "SQLITE_ABORT"
+ }
+ } {1}
+ do_test shared_ioerr-3.$n.cleanup.2 {
+ expr {
+ ($::steprc eq "SQLITE_ROW" && $::column eq "002.002.002.002.002") ||
+ ($::steprc eq "SQLITE_ERROR" && $::column eq "") ||
+ ($::steprc eq "SQLITE_ABORT" && $::column eq "001.001.001.001.001")
+ }
+ } {1}
+ do_test shared_ioerr-3.$n.cleanup.3 {
+ expr {
+ ($::steprc eq "SQLITE_ROW" && $::finalrc eq "SQLITE_OK") ||
+ ($::steprc eq "SQLITE_ERROR" && $::finalrc eq "SQLITE_IOERR") ||
+ ($::steprc eq "SQLITE_ERROR" && $::finalrc eq "SQLITE_ABORT")
+ }
+ } {1}
+
+# db2 eval {select * from sqlite_master}
+ db2 close
+}
+
+# This is a repeat of the previous test except that this time we
+# are doing a reverse-order scan of the table when the cursor is
+# "saved".
+#
+do_ioerr_test shared_ioerr-3rev -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ PRAGMA cache_size = 10;
+ BEGIN;
+ CREATE TABLE t1(a, b, UNIQUE(a, b));
+ } db2
+ for {set i 0} {$i < 200} {incr i} {
+ set a [string range [string repeat "[format %03d $i]." 5] 0 end-1]
+
+ set b [string repeat $i 2000]
+ execsql {INSERT INTO t1 VALUES($a, $b)} db2
+ }
+ execsql {COMMIT} db2
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set ::STMT [sqlite3_prepare $::DB2 \
+ "SELECT a FROM t1 ORDER BY a DESC" -1 DUMMY]
+ sqlite3_step $::STMT ;# Cursor points at 199.199.199.199.199
+ sqlite3_step $::STMT ;# Cursor points at 198.198.198.198.198
+
+} -tclbody {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES('201.201.201.201.201', NULL);
+ UPDATE t1 SET a = '202.202.202.202.202' WHERE a LIKE '201%';
+ COMMIT;
+ }
+} -cleanup {
+ set ::steprc [sqlite3_step $::STMT]
+ set ::column [sqlite3_column_text $::STMT 0]
+ set ::finalrc [sqlite3_finalize $::STMT]
+
+ # There are three possible outcomes here (assuming persistent IO errors):
+ #
+ # 1. If the [sqlite3_step] did not require any IO (required pages in
+ # the cache), then the next row ("002...") may be retrieved
+ # successfully.
+ #
+ # 2. If the [sqlite3_step] does require IO, then [sqlite3_step] returns
+ # SQLITE_ERROR and [sqlite3_finalize] returns IOERR.
+ #
+ # 3. If, after the initial IO error, SQLite tried to rollback the
+ # active transaction and a second IO error was encountered, then
+ # statement $::STMT will have been aborted. This means [sqlite3_stmt]
+ # returns SQLITE_ABORT, and the statement cursor does not move. i.e.
+ # [sqlite3_column] still returns the current row ("001...") and
+ # [sqlite3_finalize] returns SQLITE_OK.
+ #
+
+ do_test shared_ioerr-3rev.$n.cleanup.1 {
+ expr {
+ $::steprc eq "SQLITE_ROW" ||
+ $::steprc eq "SQLITE_ERROR" ||
+ $::steprc eq "SQLITE_ABORT"
+ }
+ } {1}
+ do_test shared_ioerr-3rev.$n.cleanup.2 {
+ expr {
+ ($::steprc eq "SQLITE_ROW" && $::column eq "197.197.197.197.197") ||
+ ($::steprc eq "SQLITE_ERROR" && $::column eq "") ||
+ ($::steprc eq "SQLITE_ABORT" && $::column eq "198.198.198.198.198")
+ }
+ } {1}
+ do_test shared_ioerr-3rev.$n.cleanup.3 {
+ expr {
+ ($::steprc eq "SQLITE_ROW" && $::finalrc eq "SQLITE_OK") ||
+ ($::steprc eq "SQLITE_ERROR" && $::finalrc eq "SQLITE_IOERR") ||
+ ($::steprc eq "SQLITE_ERROR" && $::finalrc eq "SQLITE_ABORT")
+ }
+ } {1}
+
+# db2 eval {select * from sqlite_master}
+ db2 close
+}
+
+# Provoke a malloc() failure when a cursor position is being saved. This
+# only happens with index cursors (because they malloc() space to save the
+# current key value). It does not happen with tables, because an integer
+# key does not require a malloc() to store.
+#
+# The library should return an SQLITE_NOMEM to the caller. The query that
+# owns the cursor (the one for which the position is not saved) should
+# continue unaffected.
+#
+do_malloc_test shared_err-4 -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ BEGIN;
+ CREATE TABLE t1(a, b, UNIQUE(a, b));
+ } db2
+ for {set i 0} {$i < 5} {incr i} {
+ set a [string repeat $i 10]
+ set b [string repeat $i 2000]
+ execsql {INSERT INTO t1 VALUES($a, $b)} db2
+ }
+ execsql {COMMIT} db2
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY]
+ sqlite3_step $::STMT ;# Cursor points at 0000000000
+ sqlite3_step $::STMT ;# Cursor points at 1111111111
+} -tclbody {
+ execsql {
+ INSERT INTO t1 VALUES(6, NULL);
+ }
+} -cleanup {
+ do_test shared_malloc-4.$::n.cleanup.1 {
+ set ::rc [sqlite3_step $::STMT]
+ expr {$::rc=="SQLITE_ROW" || $::rc=="SQLITE_ERROR"}
+ } {1}
+ if {$::rc=="SQLITE_ROW"} {
+ do_test shared_malloc-4.$::n.cleanup.2 {
+ sqlite3_column_text $::STMT 0
+ } {2222222222}
+ }
+ do_test shared_malloc-4.$::n.cleanup.3 {
+ set rc [sqlite3_finalize $::STMT]
+ expr {$rc=="SQLITE_OK" || $rc=="SQLITE_ABORT" ||
+ $rc=="SQLITE_NOMEM" || $rc=="SQLITE_IOERR"}
+ } {1}
+# db2 eval {select * from sqlite_master}
+ db2 close
+}
+
+do_malloc_test shared_err-5 -tclbody {
+ db close
+ sqlite3 dbX test.db
+ sqlite3 dbY test.db
+ dbX close
+ dbY close
+} -cleanup {
+ catch {dbX close}
+ catch {dbY close}
+}
+
+do_malloc_test shared_err-6 -tclbody {
+ catch {db close}
+ sqlite3_thread_cleanup
+ sqlite3_enable_shared_cache 0
+} -cleanup {
+ sqlite3_enable_shared_cache 1
+}
+
+# As of 3.5.0, sqlite3_enable_shared_cache can be called at
+# any time and from any thread
+#do_test shared_err-misuse-7.1 {
+# sqlite3 db test.db
+# catch {
+# sqlite3_enable_shared_cache 0
+# } msg
+# set msg
+#} {library routine called out of sequence}
+
+# Again provoke a malloc() failure when a cursor position is being saved,
+# this time during a ROLLBACK operation by some other handle.
+#
+# The library should return an SQLITE_NOMEM to the caller. The query that
+# owns the cursor (the one for which the position is not saved) should
+# be aborted.
+#
+set ::aborted 0
+do_malloc_test shared_err-8 -tclprep {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA read_uncommitted = 1;
+ BEGIN;
+ CREATE TABLE t1(a, b, UNIQUE(a, b));
+ } db2
+ for {set i 0} {$i < 2} {incr i} {
+ set a [string repeat $i 10]
+ set b [string repeat $i 2000]
+ execsql {INSERT INTO t1 VALUES($a, $b)} db2
+ }
+ execsql {COMMIT} db2
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY]
+ sqlite3_step $::STMT ;# Cursor points at 0000000000
+ sqlite3_step $::STMT ;# Cursor points at 1111111111
+} -tclbody {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(6, NULL);
+ ROLLBACK;
+ }
+} -cleanup {
+ do_test shared_malloc-8.$::n.cleanup.1 {
+ set res [catchsql {SELECT a FROM t1} db2]
+ set ans [lindex $res 1]
+ if {[lindex $res 0]} {
+ set r [expr {$ans=="disk I/O error" || $ans=="out of memory"}]
+ } else {
+ set r [expr {[lrange $ans 0 1]=="0000000000 1111111111"}]
+ }
+ } {1}
+ do_test shared_malloc-8.$::n.cleanup.2 {
+ set rc1 [sqlite3_step $::STMT]
+ set rc2 [sqlite3_finalize $::STMT]
+ if {$rc2=="SQLITE_ABORT"} {
+ incr ::aborted
+ }
+ expr {
+ ($rc1=="SQLITE_DONE" && $rc2=="SQLITE_OK") ||
+ ($rc1=="SQLITE_ERROR" && $rc2=="SQLITE_ABORT") ||
+ ($rc1=="SQLITE_ERROR" && $rc2=="SQLITE_NOMEM") ||
+ ($rc1=="SQLITE_ERROR" && $rc2=="SQLITE_IOERR")
+ }
+ } {1}
+ db2 close
+}
+do_test shared_malloc-8.X {
+ # Test that one or more queries were aborted due to the malloc() failure.
+ expr $::aborted>=1
+} {1}
+
+# This test is designed to catch a specific bug that was present during
+# development of 3.5.0. If a malloc() failed while setting the page-size,
+# a buffer (Pager.pTmpSpace) was being freed. This could cause a seg-fault
+# later if another connection tried to use the pager.
+#
+# This test will crash 3.4.2.
+#
+do_malloc_test shared_err-9 -tclprep {
+ sqlite3 db2 test.db
+} -sqlbody {
+ PRAGMA page_size = 4096;
+ PRAGMA page_size = 1024;
+} -cleanup {
+ db2 eval {
+ CREATE TABLE abc(a, b, c);
+ BEGIN;
+ INSERT INTO abc VALUES(1, 2, 3);
+ ROLLBACK;
+ }
+ db2 close
+}
+
+catch {db close}
+catch {db2 close}
+do_malloc_test shared_err-10 -tclprep {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+
+ db eval { SELECT * FROM sqlite_master }
+ db2 eval {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ }
+} -tclbody {
+ catch {db eval {SELECT * FROM sqlite_master}}
+ error 1
+} -cleanup {
+ execsql { SELECT * FROM sqlite_master }
+}
+
+do_malloc_test shared_err-11 -tclprep {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+
+ db eval { SELECT * FROM sqlite_master }
+ db2 eval {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ }
+} -tclbody {
+ catch {db eval {SELECT * FROM sqlite_master}}
+ catch {sqlite3_errmsg16 db}
+ error 1
+} -cleanup {
+ execsql { SELECT * FROM sqlite_master }
+}
+
+catch {db close}
+catch {db2 close}
+
+do_malloc_test shared_err-12 -sqlbody {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+}
+
+catch {db close}
+catch {db2 close}
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/shortread1.test b/third_party/sqlite/test/shortread1.test
new file mode 100755
index 0000000..647f2ff
--- /dev/null
+++ b/third_party/sqlite/test/shortread1.test
@@ -0,0 +1,52 @@
+# 2007 Sep 13
+#
+# 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 attempts to duplicate an error scenario seen on a
+# customer system using version 3.2.2. The problem appears to
+# have been fixed (perhaps by accident) with check-in [3503].
+# These tests will prevent an accidental recurrance.
+#
+# $Id: shortread1.test,v 1.1 2007/09/14 01:48:12 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test shortread1-1.1 {
+ execsql {
+ CREATE TABLE t1(a TEXT);
+ BEGIN;
+ INSERT INTO t1 VALUES(hex(randomblob(5000)));
+ INSERT INTO t1 VALUES(hex(randomblob(100)));
+ PRAGMA freelist_count;
+ }
+} {0}
+do_test shortread1-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE rowid=1;
+ PRAGMA freelist_count;
+ }
+} {11}
+do_test shortread1-1.3 {
+ sqlite3_release_memory [expr {1024*9}]
+ execsql {
+ INSERT INTO t1 VALUES(hex(randomblob(5000)));
+ PRAGMA freelist_count;
+ }
+} {0}
+do_test shortread1-1.4 {
+ execsql {
+ COMMIT;
+ SELECT count(*) FROM t1;
+ }
+} {2}
+
+finish_test
diff --git a/third_party/sqlite/test/sidedelete.test b/third_party/sqlite/test/sidedelete.test
new file mode 100755
index 0000000..04e457d
--- /dev/null
+++ b/third_party/sqlite/test/sidedelete.test
@@ -0,0 +1,92 @@
+# 2007 Dec 12
+#
+# 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 contains test cases for stressing database
+# changes that involve side effects that delete rows from
+# the table being changed. Ticket #2832 shows that in
+# older versions of SQLite that behavior was implemented
+# incorrectly and resulted in corrupt database files.
+#
+# $Id: sidedelete.test,v 1.2 2008/08/04 03:51:24 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# The sequence table is created to store a sequence of integers
+# starting with 1. This is used to reinitialize other tables
+# as part of other tests.
+#
+do_test sidedelete-1.1 {
+ execsql {
+ CREATE TABLE sequence(a INTEGER PRIMARY KEY);
+ INSERT INTO sequence VALUES(1);
+ INSERT INTO sequence VALUES(2);
+ }
+ for {set i 0} {$i<8} {incr i} {
+ execsql {
+ INSERT INTO sequence SELECT a+(SELECT max(a) FROM sequence) FROM sequence;
+ }
+ }
+ execsql {SELECT count(*) FROM sequence}
+} {512}
+
+# Make a series of changes using an UPDATE OR REPLACE and a
+# correlated subquery. This would cause database corruption
+# prior to the fix for ticket #2832.
+#
+do_test sidedelete-2.0 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ CREATE TABLE chng(a PRIMARY KEY, b);
+ SELECT count(*) FROM t1;
+ SELECT count(*) FROM chng;
+ }
+} {0 0}
+for {set i 2} {$i<=100} {incr i} {
+ set n [expr {($i+2)/2}]
+ do_test sidedelete-2.$i.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT a, a FROM sequence WHERE a<=$i;
+ DELETE FROM chng;
+ INSERT INTO chng SELECT a*2, a*2+1 FROM sequence WHERE a<=$i/2;
+ UPDATE OR REPLACE t1 SET a=(SELECT b FROM chng WHERE a=t1.a);
+ SELECT count(*), sum(a) FROM t1;
+ }
+ } [list $n [expr {$n*$n-1}]]
+ integrity_check sidedelete-2.$i.2
+}
+
+# This will cause stacks leaks but not database corruption prior
+# to the #2832 fix.
+#
+do_test sidedelete-3.0 {
+ execsql {
+ DROP TABLE t1;
+ CREATE TABLE t1(a PRIMARY KEY);
+ SELECT * FROM t1;
+ }
+} {}
+for {set i 1} {$i<=100} {incr i} {
+ set n [expr {($i+1)/2}]
+ do_test sidedelete-3.$i.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT a FROM sequence WHERE a<=$i;
+ UPDATE OR REPLACE t1 SET a=a+1;
+ SELECT count(*), sum(a) FROM t1;
+ }
+ } [list $n [expr {$n*($n+1)}]]
+ integrity_check sidedelete-3.$i.2
+}
+
+finish_test
diff --git a/third_party/sqlite/test/soak.test b/third_party/sqlite/test/soak.test
new file mode 100755
index 0000000..0d110cc
--- /dev/null
+++ b/third_party/sqlite/test/soak.test
@@ -0,0 +1,89 @@
+# 2007 May 24
+#
+# 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 is the driver for the "soak" tests. It is a peer of the
+# quick.test and all.test scripts.
+#
+# $Id: soak.test,v 1.3 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+rename finish_test really_finish_test
+proc finish_test {} {}
+
+# By default, guarantee that the tests will run for at least 1 hour.
+#
+set TIMEOUT 3600
+
+# Process command-line arguments.
+#
+if {[llength $argv]>0} {
+ foreach {name value} $argv {
+ switch -- $name {
+ -timeout {
+ set TIMEOUT $value
+ }
+ default {
+ puts stderr "Unknown option: $name"
+ exit
+ }
+ }
+ }
+}
+set argv [list]
+
+# Test plan:
+#
+# The general principle is to run those SQLite tests that use
+# pseudo-random data in some way over and over again for a very
+# long time. The number of tests run depends on the value of
+# global variable $TIMEOUT - tests are run for at least $TIMEOUT
+# seconds.
+#
+# fuzz.test (pseudo-random SQL statements)
+# trans.test (pseudo-random changes to a database followed by rollbacks)
+#
+# fuzzy malloc?
+#
+# Many database changes maintaining some kind of invariant.
+# Storing checksums etc.
+#
+
+# List of test files that are run by this file.
+#
+set SOAKTESTS {
+ fuzz.test
+ fuzz_malloc.test
+ trans.test
+}
+
+set ISQUICK 1
+
+set soak_starttime [clock seconds]
+set soak_finishtime [expr {$soak_starttime + $TIMEOUT}]
+
+# Loop until the timeout is reached or an error occurs.
+#
+for {set iRun 0} {[clock seconds] < $soak_finishtime && $nErr==0} {incr iRun} {
+
+ set iIdx [expr {$iRun % [llength $SOAKTESTS]}]
+ source [file join $testdir [lindex $SOAKTESTS $iIdx]]
+ catch {db close}
+
+ if {$sqlite_open_file_count>0} {
+ puts "$tail did not close all files: $sqlite_open_file_count"
+ incr nErr
+ lappend ::failList $tail
+ set sqlite_open_file_count 0
+ }
+
+}
+
+really_finish_test
diff --git a/third_party/sqlite/test/softheap1.test b/third_party/sqlite/test/softheap1.test
new file mode 100755
index 0000000..5a06095
--- /dev/null
+++ b/third_party/sqlite/test/softheap1.test
@@ -0,0 +1,50 @@
+# 2007 Aug 10
+#
+# 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 test script reproduces the problem reported by ticket #2565,
+# A database corruption bug that occurs in auto_vacuum mode when
+# the soft_heap_limit is set low enough to be triggered.
+#
+# $Id: softheap1.test,v 1.5 2008/07/08 17:13:59 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !integrityck {
+ finish_test
+ return
+}
+
+sqlite3_soft_heap_limit -1
+sqlite3_soft_heap_limit 0
+sqlite3_soft_heap_limit 5000
+do_test softheap1-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(hex(randomblob(1000)));
+ BEGIN;
+ }
+ execsql {
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ }
+ execsql {
+ ROLLBACK;
+ }
+ execsql {
+ PRAGMA integrity_check;
+ }
+} {ok}
+
+sqlite3_soft_heap_limit $soft_limit
+
+finish_test
diff --git a/third_party/sqlite/test/sort.test b/third_party/sqlite/test/sort.test
new file mode 100755
index 0000000..08d496b
--- /dev/null
+++ b/third_party/sqlite/test/sort.test
@@ -0,0 +1,467 @@
+# 2001 September 15.
+#
+# 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 file is testing the CREATE TABLE statement.
+#
+# $Id: sort.test,v 1.25 2005/11/14 22:29:06 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a bunch of data to sort against
+#
+do_test sort-1.0 {
+ execsql {
+ CREATE TABLE t1(
+ n int,
+ v varchar(10),
+ log int,
+ roman varchar(10),
+ flt real
+ );
+ INSERT INTO t1 VALUES(1,'one',0,'I',3.141592653);
+ INSERT INTO t1 VALUES(2,'two',1,'II',2.15);
+ INSERT INTO t1 VALUES(3,'three',1,'III',4221.0);
+ INSERT INTO t1 VALUES(4,'four',2,'IV',-0.0013442);
+ INSERT INTO t1 VALUES(5,'five',2,'V',-11);
+ INSERT INTO t1 VALUES(6,'six',2,'VI',0.123);
+ INSERT INTO t1 VALUES(7,'seven',2,'VII',123.0);
+ INSERT INTO t1 VALUES(8,'eight',3,'VIII',-1.6);
+ }
+ execsql {SELECT count(*) FROM t1}
+} {8}
+
+do_test sort-1.1 {
+ execsql {SELECT n FROM t1 ORDER BY n}
+} {1 2 3 4 5 6 7 8}
+do_test sort-1.1.1 {
+ execsql {SELECT n FROM t1 ORDER BY n ASC}
+} {1 2 3 4 5 6 7 8}
+do_test sort-1.1.1 {
+ execsql {SELECT ALL n FROM t1 ORDER BY n ASC}
+} {1 2 3 4 5 6 7 8}
+do_test sort-1.2 {
+ execsql {SELECT n FROM t1 ORDER BY n DESC}
+} {8 7 6 5 4 3 2 1}
+do_test sort-1.3a {
+ execsql {SELECT v FROM t1 ORDER BY v}
+} {eight five four one seven six three two}
+do_test sort-1.3b {
+ execsql {SELECT n FROM t1 ORDER BY v}
+} {8 5 4 1 7 6 3 2}
+do_test sort-1.4 {
+ execsql {SELECT n FROM t1 ORDER BY v DESC}
+} {2 3 6 7 1 4 5 8}
+do_test sort-1.5 {
+ execsql {SELECT flt FROM t1 ORDER BY flt}
+} {-11.0 -1.6 -0.0013442 0.123 2.15 3.141592653 123.0 4221.0}
+do_test sort-1.6 {
+ execsql {SELECT flt FROM t1 ORDER BY flt DESC}
+} {4221.0 123.0 3.141592653 2.15 0.123 -0.0013442 -1.6 -11.0}
+do_test sort-1.7 {
+ execsql {SELECT roman FROM t1 ORDER BY roman}
+} {I II III IV V VI VII VIII}
+do_test sort-1.8 {
+ execsql {SELECT n FROM t1 ORDER BY log, flt}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.1 {
+ execsql {SELECT n FROM t1 ORDER BY log asc, flt}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.2 {
+ execsql {SELECT n FROM t1 ORDER BY log, flt ASC}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.3 {
+ execsql {SELECT n FROM t1 ORDER BY log ASC, flt asc}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.9 {
+ execsql {SELECT n FROM t1 ORDER BY log, flt DESC}
+} {1 3 2 7 6 4 5 8}
+do_test sort-1.9.1 {
+ execsql {SELECT n FROM t1 ORDER BY log ASC, flt DESC}
+} {1 3 2 7 6 4 5 8}
+do_test sort-1.10 {
+ execsql {SELECT n FROM t1 ORDER BY log DESC, flt}
+} {8 5 4 6 7 2 3 1}
+do_test sort-1.11 {
+ execsql {SELECT n FROM t1 ORDER BY log DESC, flt DESC}
+} {8 7 6 4 5 3 2 1}
+
+# These tests are designed to reach some hard-to-reach places
+# inside the string comparison routines.
+#
+# (Later) The sorting behavior changed in 2.7.0. But we will
+# keep these tests. You can never have too many test cases!
+#
+do_test sort-2.1.1 {
+ execsql {
+ UPDATE t1 SET v='x' || -flt;
+ UPDATE t1 SET v='x-2b' where v=='x-0.123';
+ SELECT v FROM t1 ORDER BY v;
+ }
+} {x-123.0 x-2.15 x-2b x-3.141592653 x-4221.0 x0.0013442 x1.6 x11.0}
+do_test sort-2.1.2 {
+ execsql {
+ SELECT v FROM t1 ORDER BY substr(v,2,999);
+ }
+} {x-123.0 x-2.15 x-2b x-3.141592653 x-4221.0 x0.0013442 x1.6 x11.0}
+do_test sort-2.1.3 {
+ execsql {
+ SELECT v FROM t1 ORDER BY substr(v,2,999)+0.0;
+ }
+} {x-4221.0 x-123.0 x-3.141592653 x-2.15 x-2b x0.0013442 x1.6 x11.0}
+do_test sort-2.1.4 {
+ execsql {
+ SELECT v FROM t1 ORDER BY substr(v,2,999) DESC;
+ }
+} {x11.0 x1.6 x0.0013442 x-4221.0 x-3.141592653 x-2b x-2.15 x-123.0}
+do_test sort-2.1.5 {
+ execsql {
+ SELECT v FROM t1 ORDER BY substr(v,2,999)+0.0 DESC;
+ }
+} {x11.0 x1.6 x0.0013442 x-2b x-2.15 x-3.141592653 x-123.0 x-4221.0}
+
+# This is a bug fix for 2.2.4.
+# Strings are normally mapped to upper-case for a caseless comparison.
+# But this can cause problems for characters in between 'Z' and 'a'.
+#
+do_test sort-3.1 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES('AGLIENTU',1);
+ INSERT INTO t2 VALUES('AGLIE`',2);
+ INSERT INTO t2 VALUES('AGNA',3);
+ SELECT a, b FROM t2 ORDER BY a;
+ }
+} {AGLIENTU 1 AGLIE` 2 AGNA 3}
+do_test sort-3.2 {
+ execsql {
+ SELECT a, b FROM t2 ORDER BY a DESC;
+ }
+} {AGNA 3 AGLIE` 2 AGLIENTU 1}
+do_test sort-3.3 {
+ execsql {
+ DELETE FROM t2;
+ INSERT INTO t2 VALUES('aglientu',1);
+ INSERT INTO t2 VALUES('aglie`',2);
+ INSERT INTO t2 VALUES('agna',3);
+ SELECT a, b FROM t2 ORDER BY a;
+ }
+} {aglie` 2 aglientu 1 agna 3}
+do_test sort-3.4 {
+ execsql {
+ SELECT a, b FROM t2 ORDER BY a DESC;
+ }
+} {agna 3 aglientu 1 aglie` 2}
+
+# Version 2.7.0 testing.
+#
+do_test sort-4.1 {
+ execsql {
+ INSERT INTO t1 VALUES(9,'x2.7',3,'IX',4.0e5);
+ INSERT INTO t1 VALUES(10,'x5.0e10',3,'X',-4.0e5);
+ INSERT INTO t1 VALUES(11,'x-4.0e9',3,'XI',4.1e4);
+ INSERT INTO t1 VALUES(12,'x01234567890123456789',3,'XII',-4.2e3);
+ SELECT n FROM t1 ORDER BY n;
+ }
+} {1 2 3 4 5 6 7 8 9 10 11 12}
+do_test sort-4.2 {
+ execsql {
+ SELECT n||'' FROM t1 ORDER BY 1;
+ }
+} {1 10 11 12 2 3 4 5 6 7 8 9}
+do_test sort-4.3 {
+ execsql {
+ SELECT n+0 FROM t1 ORDER BY 1;
+ }
+} {1 2 3 4 5 6 7 8 9 10 11 12}
+do_test sort-4.4 {
+ execsql {
+ SELECT n||'' FROM t1 ORDER BY 1 DESC;
+ }
+} {9 8 7 6 5 4 3 2 12 11 10 1}
+do_test sort-4.5 {
+ execsql {
+ SELECT n+0 FROM t1 ORDER BY 1 DESC;
+ }
+} {12 11 10 9 8 7 6 5 4 3 2 1}
+do_test sort-4.6 {
+ execsql {
+ SELECT v FROM t1 ORDER BY 1;
+ }
+} {x-123.0 x-2.15 x-2b x-3.141592653 x-4.0e9 x-4221.0 x0.0013442 x01234567890123456789 x1.6 x11.0 x2.7 x5.0e10}
+do_test sort-4.7 {
+ execsql {
+ SELECT v FROM t1 ORDER BY 1 DESC;
+ }
+} {x5.0e10 x2.7 x11.0 x1.6 x01234567890123456789 x0.0013442 x-4221.0 x-4.0e9 x-3.141592653 x-2b x-2.15 x-123.0}
+do_test sort-4.8 {
+ execsql {
+ SELECT substr(v,2,99) FROM t1 ORDER BY 1;
+ }
+} {-123.0 -2.15 -2b -3.141592653 -4.0e9 -4221.0 0.0013442 01234567890123456789 1.6 11.0 2.7 5.0e10}
+#do_test sort-4.9 {
+# execsql {
+# SELECT substr(v,2,99)+0.0 FROM t1 ORDER BY 1;
+# }
+#} {-4000000000 -4221 -123 -3.141592653 -2.15 -2 0.0013442 1.6 2.7 11 50000000000 1.23456789012346e+18}
+
+do_test sort-5.1 {
+ execsql {
+ create table t3(a,b);
+ insert into t3 values(5,NULL);
+ insert into t3 values(6,NULL);
+ insert into t3 values(3,NULL);
+ insert into t3 values(4,'cd');
+ insert into t3 values(1,'ab');
+ insert into t3 values(2,NULL);
+ select a from t3 order by b, a;
+ }
+} {2 3 5 6 1 4}
+do_test sort-5.2 {
+ execsql {
+ select a from t3 order by b, a desc;
+ }
+} {6 5 3 2 1 4}
+do_test sort-5.3 {
+ execsql {
+ select a from t3 order by b desc, a;
+ }
+} {4 1 2 3 5 6}
+do_test sort-5.4 {
+ execsql {
+ select a from t3 order by b desc, a desc;
+ }
+} {4 1 6 5 3 2}
+
+do_test sort-6.1 {
+ execsql {
+ create index i3 on t3(b,a);
+ select a from t3 order by b, a;
+ }
+} {2 3 5 6 1 4}
+do_test sort-6.2 {
+ execsql {
+ select a from t3 order by b, a desc;
+ }
+} {6 5 3 2 1 4}
+do_test sort-6.3 {
+ execsql {
+ select a from t3 order by b desc, a;
+ }
+} {4 1 2 3 5 6}
+do_test sort-6.4 {
+ execsql {
+ select a from t3 order by b desc, a desc;
+ }
+} {4 1 6 5 3 2}
+
+do_test sort-7.1 {
+ execsql {
+ CREATE TABLE t4(
+ a INTEGER,
+ b VARCHAR(30)
+ );
+ INSERT INTO t4 VALUES(1,1);
+ INSERT INTO t4 VALUES(2,2);
+ INSERT INTO t4 VALUES(11,11);
+ INSERT INTO t4 VALUES(12,12);
+ SELECT a FROM t4 ORDER BY 1;
+ }
+} {1 2 11 12}
+do_test sort-7.2 {
+ execsql {
+ SELECT b FROM t4 ORDER BY 1
+ }
+} {1 11 12 2}
+
+# Omit tests sort-7.3 to sort-7.8 if view support was disabled at
+# compilatation time.
+ifcapable view {
+do_test sort-7.3 {
+ execsql {
+ CREATE VIEW v4 AS SELECT * FROM t4;
+ SELECT a FROM v4 ORDER BY 1;
+ }
+} {1 2 11 12}
+do_test sort-7.4 {
+ execsql {
+ SELECT b FROM v4 ORDER BY 1;
+ }
+} {1 11 12 2}
+
+ifcapable compound {
+do_test sort-7.5 {
+ execsql {
+ SELECT a FROM t4 UNION SELECT a FROM v4 ORDER BY 1;
+ }
+} {1 2 11 12}
+do_test sort-7.6 {
+ execsql {
+ SELECT b FROM t4 UNION SELECT a FROM v4 ORDER BY 1;
+ }
+} {1 2 11 12 1 11 12 2} ;# text from t4.b and numeric from v4.a
+do_test sort-7.7 {
+ execsql {
+ SELECT a FROM t4 UNION SELECT b FROM v4 ORDER BY 1;
+ }
+} {1 2 11 12 1 11 12 2} ;# numeric from t4.a and text from v4.b
+do_test sort-7.8 {
+ execsql {
+ SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1;
+ }
+} {1 11 12 2}
+} ;# ifcapable compound
+} ;# ifcapable view
+
+#### Version 3 works differently here:
+#do_test sort-7.9 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE numeric;
+# }
+#} {1 2 11 12}
+#do_test sort-7.10 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE integer;
+# }
+#} {1 2 11 12}
+#do_test sort-7.11 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE text;
+# }
+#} {1 11 12 2}
+#do_test sort-7.12 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE blob;
+# }
+#} {1 11 12 2}
+#do_test sort-7.13 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE clob;
+# }
+#} {1 11 12 2}
+#do_test sort-7.14 {
+# execsql {
+# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE varchar;
+# }
+#} {1 11 12 2}
+
+# Ticket #297
+#
+do_test sort-8.1 {
+ execsql {
+ CREATE TABLE t5(a real, b text);
+ INSERT INTO t5 VALUES(100,'A1');
+ INSERT INTO t5 VALUES(100.0,'A2');
+ SELECT * FROM t5 ORDER BY a, b;
+ }
+} {100.0 A1 100.0 A2}
+
+
+ifcapable {bloblit} {
+# BLOBs should sort after TEXT
+#
+do_test sort-9.1 {
+ execsql {
+ CREATE TABLE t6(x, y);
+ INSERT INTO t6 VALUES(1,1);
+ INSERT INTO t6 VALUES(2,'1');
+ INSERT INTO t6 VALUES(3,x'31');
+ INSERT INTO t6 VALUES(4,NULL);
+ SELECT x FROM t6 ORDER BY y;
+ }
+} {4 1 2 3}
+do_test sort-9.2 {
+ execsql {
+ SELECT x FROM t6 ORDER BY y DESC;
+ }
+} {3 2 1 4}
+do_test sort-9.3 {
+ execsql {
+ SELECT x FROM t6 WHERE y<1
+ }
+} {}
+do_test sort-9.4 {
+ execsql {
+ SELECT x FROM t6 WHERE y<'1'
+ }
+} {1}
+do_test sort-9.5 {
+ execsql {
+ SELECT x FROM t6 WHERE y<x'31'
+ }
+} {1 2}
+do_test sort-9.6 {
+ execsql {
+ SELECT x FROM t6 WHERE y>1
+ }
+} {2 3}
+do_test sort-9.7 {
+ execsql {
+ SELECT x FROM t6 WHERE y>'1'
+ }
+} {3}
+} ;# endif bloblit
+
+# Ticket #1092 - ORDER BY on rowid fields.
+do_test sort-10.1 {
+ execsql {
+ CREATE TABLE t7(c INTEGER PRIMARY KEY);
+ INSERT INTO t7 VALUES(1);
+ INSERT INTO t7 VALUES(2);
+ INSERT INTO t7 VALUES(3);
+ INSERT INTO t7 VALUES(4);
+ }
+} {}
+do_test sort-10.2 {
+ execsql {
+ SELECT c FROM t7 WHERE c<=3 ORDER BY c DESC;
+ }
+} {3 2 1}
+do_test sort-10.3 {
+ execsql {
+ SELECT c FROM t7 WHERE c<3 ORDER BY c DESC;
+ }
+} {2 1}
+
+# ticket #1358. Just because one table in a join gives a unique
+# result does not mean they all do. We cannot disable sorting unless
+# all tables in the join give unique results.
+#
+do_test sort-11.1 {
+ execsql {
+ create table t8(a unique, b, c);
+ insert into t8 values(1,2,3);
+ insert into t8 values(2,3,4);
+ create table t9(x,y);
+ insert into t9 values(2,4);
+ insert into t9 values(2,3);
+ select y from t8, t9 where a=1 order by a, y;
+ }
+} {3 4}
+
+# Trouble reported on the mailing list. Check for overly aggressive
+# (which is to say, incorrect) optimization of order-by with a rowid
+# in a join.
+#
+do_test sort-12.1 {
+ execsql {
+ create table a (id integer primary key);
+ create table b (id integer primary key, aId integer, text);
+ insert into a values (1);
+ insert into b values (2, 1, 'xxx');
+ insert into b values (1, 1, 'zzz');
+ insert into b values (3, 1, 'yyy');
+ select a.id, b.id, b.text from a join b on (a.id = b.aId)
+ order by a.id, b.text;
+ }
+} {1 2 xxx 1 3 yyy 1 1 zzz}
+
+finish_test
diff --git a/third_party/sqlite/test/speed1.test b/third_party/sqlite/test/speed1.test
new file mode 100755
index 0000000..8fec759
--- /dev/null
+++ b/third_party/sqlite/test/speed1.test
@@ -0,0 +1,293 @@
+# 2006 November 23
+#
+# 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 measuring executing speed.
+#
+# $Id: speed1.test,v 1.9 2008/07/30 13:15:46 drh Exp $
+#
+
+sqlite3_shutdown
+#sqlite3_config_scratch 29000 1
+sqlite3_config_lookaside 1000 300
+#sqlite3_config_pagecache 1024 10000
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed1.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Create a database schema.
+#
+do_test speed1-1.0 {
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=8192;
+ PRAGMA locking_mode=EXCLUSIVE;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+ CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ }
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1;
+ }
+} {i2a i2b t1 t2}
+
+
+# 50000 INSERTs on an unindexed table
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');\n"
+}
+db eval BEGIN
+speed_trial speed1-insert1 50000 row $sql
+db eval COMMIT
+
+# 50000 INSERTs on an indexed table
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');\n"
+}
+db eval BEGIN
+speed_trial speed1-insert2 50000 row $sql
+db eval COMMIT
+
+
+
+# 50 SELECTs on an integer comparison. There is no index so
+# a full table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+db eval BEGIN
+speed_trial speed1-select1 [expr {50*50000}] row $sql
+db eval COMMIT
+
+# 50 SELECTs on an LIKE comparison. There is no index so a full
+# table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ append sql \
+ "SELECT count(*), avg(b) FROM t1 WHERE c LIKE '%[number_name $i]%';"
+}
+db eval BEGIN
+speed_trial speed1-select2 [expr {50*50000}] row $sql
+db eval COMMIT
+
+# Create indices
+#
+db eval BEGIN
+speed_trial speed1-createidx 150000 row {
+ CREATE INDEX i1a ON t1(a);
+ CREATE INDEX i1b ON t1(b);
+ CREATE INDEX i1c ON t1(c);
+}
+db eval COMMIT
+
+# 5000 SELECTs on an integer comparison where the integer is
+# indexed.
+#
+set sql {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+db eval BEGIN
+speed_trial speed1-select3 5000 stmt $sql
+db eval COMMIT
+
+# 100000 random SELECTs against rowid.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c FROM t1 WHERE rowid=$id;"
+}
+db eval BEGIN
+speed_trial speed1-select4 100000 row $sql
+db eval COMMIT
+
+# 100000 random SELECTs against a unique indexed column.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c FROM t1 WHERE a=$id;"
+}
+db eval BEGIN
+speed_trial speed1-select5 100000 row $sql
+db eval COMMIT
+
+# 50000 random SELECTs against an indexed column text column
+#
+set sql {}
+db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000} {
+ append sql "SELECT c FROM t1 WHERE c='$c';"
+}
+db eval BEGIN
+speed_trial speed1-select6 50000 row $sql
+db eval COMMIT
+
+
+# Vacuum
+speed_trial speed1-vacuum 100000 row VACUUM
+
+# 5000 updates of ranges where the field being compared is indexed.
+#
+set sql {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*2}]
+ set upr [expr {($i+1)*2}]
+ append sql "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
+}
+db eval BEGIN
+speed_trial speed1-update1 5000 stmt $sql
+db eval COMMIT
+
+# 50000 single-row updates. An index is used to find the row quickly.
+#
+set sql {}
+for {set i 0} {$i<50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "UPDATE t1 SET b=$r WHERE a=$i;"
+}
+db eval BEGIN
+speed_trial speed1-update2 50000 row $sql
+db eval COMMIT
+
+# 1 big text update that touches every row in the table.
+#
+speed_trial speed1-update3 50000 row {
+ UPDATE t1 SET c=a;
+}
+
+# Many individual text updates. Each row in the table is
+# touched through an index.
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "UPDATE t1 SET c='[number_name $r]' WHERE a=$i;"
+}
+db eval BEGIN
+speed_trial speed1-update4 50000 row $sql
+db eval COMMIT
+
+# Delete all content in a table.
+#
+speed_trial speed1-delete1 50000 row {DELETE FROM t1}
+
+# Copy one table into another
+#
+speed_trial speed1-copy1 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Delete all content in a table, one row at a time.
+#
+speed_trial speed1-delete2 50000 row {DELETE FROM t1 WHERE 1}
+
+# Refill the table yet again
+#
+speed_trial speed1-copy2 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Drop the table and recreate it without its indices.
+#
+db eval BEGIN
+speed_trial speed1-drop1 50000 row {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+}
+db eval COMMIT
+
+# Refill the table yet again. This copy should be faster because
+# there are no indices to deal with.
+#
+speed_trial speed1-copy3 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Select 20000 rows from the table at random.
+#
+speed_trial speed1-random1 50000 row {
+ SELECT rowid FROM t1 ORDER BY random() LIMIT 20000
+}
+
+# Delete 20000 random rows from the table.
+#
+speed_trial speed1-random-del1 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1-1.1 {
+ db one {SELECT count(*) FROM t1}
+} 30000
+
+
+# Delete 20000 more rows at random from the table.
+#
+speed_trial speed1-random-del2 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1-1.2 {
+ db one {SELECT count(*) FROM t1}
+} 10000
+speed_trial_summary speed1
+
+finish_test
diff --git a/third_party/sqlite/test/speed1p.explain b/third_party/sqlite/test/speed1p.explain
new file mode 100755
index 0000000..e32fedd
--- /dev/null
+++ b/third_party/sqlite/test/speed1p.explain
@@ -0,0 +1,366 @@
+# 2008 March 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is measuring executing speed.
+#
+# This is a copy of speed1.test modified to user prepared statements.
+#
+# $Id: speed1p.explain,v 1.1 2008/04/16 12:57:48 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed1.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Create a database schema.
+#
+do_test speed1p-1.0 {
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=8192;
+ PRAGMA locking_mode=EXCLUSIVE;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+ CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ }
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1;
+ }
+} {i2a i2b t1 t2}
+
+
+# 50000 INSERTs on an unindexed table
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ set x [number_name $r]
+ lappend list $i $r $x
+}
+set script {
+ foreach {i r x} $::list {
+ db eval {INSERT INTO t1 VALUES($i,$r,$x)}
+ }
+}
+explain {INSERT INTO t1 VALUES($i,$r,$x)}
+db eval BEGIN
+speed_trial_tcl speed1p-insert1 50000 row $script
+db eval COMMIT
+
+# 50000 INSERTs on an indexed table
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ set x [number_name $r]
+ lappend list $i $r $x
+}
+set script {
+ foreach {i r x} $::list {
+ db eval {INSERT INTO t2 VALUES($i,$r,$x)}
+ }
+}
+explain {INSERT INTO t2 VALUES($i,$r,$x)}
+db eval BEGIN
+speed_trial_tcl speed1p-insert2 50000 row $script
+db eval COMMIT
+
+
+
+# 50 SELECTs on an integer comparison. There is no index so
+# a full table scan is required.
+#
+set list {}
+for {set i 0} {$i<50} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+ }
+}
+explain {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+db eval BEGIN
+speed_trial_tcl speed1p-select1 [expr {50*50000}] row $script
+db eval COMMIT
+
+# 50 SELECTs on an LIKE comparison. There is no index so a full
+# table scan is required.
+#
+set list {}
+for {set i 0} {$i<50} {incr i} {
+ lappend list "%[number_name $i]%"
+}
+set script {
+ foreach pattern $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE c LIKE $pattern}
+ }
+}
+explain {SELECT count(*), avg(b) FROM t1 WHERE c LIKE $pattern}
+db eval BEGIN
+speed_trial_tcl speed1p-select2 [expr {50*50000}] row $script
+db eval COMMIT
+
+# Create indices
+#
+explain {CREATE INDEX i1a ON t1(a)}
+explain {CREATE INDEX i1b ON t1(b)}
+db eval BEGIN
+speed_trial speed1p-createidx 150000 row {
+ CREATE INDEX i1a ON t1(a);
+ CREATE INDEX i1b ON t1(b);
+ CREATE INDEX i1c ON t1(c);
+}
+db eval COMMIT
+
+# 5000 SELECTs on an integer comparison where the integer is
+# indexed.
+#
+set list {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+ }
+}
+explain {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+db eval BEGIN
+speed_trial_tcl speed1p-select3 5000 stmt $script
+db eval COMMIT
+
+# 100000 random SELECTs against rowid.
+#
+set list {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ lappend list $id
+}
+set script {
+ foreach id $::list {
+ db eval {SELECT c FROM t1 WHERE rowid=$id}
+ }
+}
+explain {SELECT c FROM t1 WHERE rowid=$id}
+db eval BEGIN
+speed_trial_tcl speed1p-select4 100000 row $script
+db eval COMMIT
+
+# 100000 random SELECTs against a unique indexed column.
+#
+set list {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ lappend list $id
+}
+set script {
+ foreach id $::list {
+ db eval {SELECT c FROM t1 WHERE a=$id}
+ }
+}
+explain {SELECT c FROM t1 WHERE a=$id}
+db eval BEGIN
+speed_trial_tcl speed1p-select5 100000 row $script
+db eval COMMIT
+
+# 50000 random SELECTs against an indexed column text column
+#
+set list [db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000}]
+set script {
+ foreach c $::list {
+ db eval {SELECT c FROM t1 WHERE c=$c}
+ }
+}
+explain {SELECT c FROM t1 WHERE c=$c}
+db eval BEGIN
+speed_trial_tcl speed1p-select6 50000 row $script
+db eval COMMIT
+
+
+# Vacuum
+speed_trial speed1p-vacuum 100000 row VACUUM
+
+# 5000 updates of ranges where the field being compared is indexed.
+#
+set list {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*2}]
+ set upr [expr {($i+1)*2}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr}
+ }
+}
+explain {UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr}
+db eval BEGIN
+speed_trial_tcl speed1p-update1 5000 stmt $script
+db eval COMMIT
+
+# 50000 single-row updates. An index is used to find the row quickly.
+#
+set list {}
+for {set i 0} {$i<50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ lappend list $i $r
+}
+set script {
+ foreach {i r} $::list {
+ db eval {UPDATE t1 SET b=$r WHERE a=$i}
+ }
+}
+explain {UPDATE t1 SET b=$r WHERE a=$i}
+db eval BEGIN
+speed_trial_tcl speed1p-update2 50000 row $script
+db eval COMMIT
+
+# 1 big text update that touches every row in the table.
+#
+explain {UPDATE t1 SET c=a}
+speed_trial speed1p-update3 50000 row {
+ UPDATE t1 SET c=a;
+}
+
+# Many individual text updates. Each row in the table is
+# touched through an index.
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ lappend list $i [number_name $r]
+}
+set script {
+ foreach {i x} $::list {
+ db eval {UPDATE t1 SET c=$x WHERE a=$i}
+ }
+}
+explain {UPDATE t1 SET c=$x WHERE a=$i}
+db eval BEGIN
+speed_trial_tcl speed1p-update4 50000 row $script
+db eval COMMIT
+
+# Delete all content in a table.
+#
+explain {DELETE FROM t1}
+speed_trial speed1p-delete1 50000 row {DELETE FROM t1}
+
+# Copy one table into another
+#
+explain {INSERT INTO t1 SELECT * FROM t2}
+speed_trial speed1p-copy1 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Delete all content in a table, one row at a time.
+#
+explain {DELETE FROM t1 WHERE 1}
+speed_trial speed1p-delete2 50000 row {DELETE FROM t1 WHERE 1}
+
+# Refill the table yet again
+#
+speed_trial speed1p-copy2 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Drop the table and recreate it without its indices.
+#
+explain {DROP TABLE t1}
+explain {CREATE TABLE tX(a INTEGER, b INTEGER, c TEXT)}
+db eval BEGIN
+speed_trial speed1p-drop1 50000 row {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+}
+db eval COMMIT
+
+# Refill the table yet again. This copy should be faster because
+# there are no indices to deal with.
+#
+speed_trial speed1p-copy3 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Select 20000 rows from the table at random.
+#
+explain {SELECT rowid FROM t1 ORDER BY random() LIMIT 20000}
+speed_trial speed1p-random1 50000 row {
+ SELECT rowid FROM t1 ORDER BY random() LIMIT 20000
+}
+
+# Delete 20000 random rows from the table.
+#
+explain {DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)}
+speed_trial speed1p-random-del1 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1p-1.1 {
+ db one {SELECT count(*) FROM t1}
+} 30000
+
+
+# Delete 20000 more rows at random from the table.
+#
+speed_trial speed1p-random-del2 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1p-1.2 {
+ db one {SELECT count(*) FROM t1}
+} 10000
+speed_trial_summary speed1
+
+finish_test
diff --git a/third_party/sqlite/test/speed1p.test b/third_party/sqlite/test/speed1p.test
new file mode 100755
index 0000000..4e14fc9
--- /dev/null
+++ b/third_party/sqlite/test/speed1p.test
@@ -0,0 +1,342 @@
+# 2008 March 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.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is measuring executing speed.
+#
+# This is a copy of speed1.test modified to user prepared statements.
+#
+# $Id: speed1p.test,v 1.5 2008/07/30 13:15:46 drh Exp $
+#
+
+sqlite3_shutdown
+#sqlite3_config_scratch 29000 1
+sqlite3_config_lookaside 2048 300
+#sqlite3_config_pagecache 1024 11000
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Create a database schema.
+#
+do_test speed1p-1.0 {
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=500;
+ PRAGMA locking_mode=EXCLUSIVE;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+ CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ }
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1;
+ }
+} {i2a i2b t1 t2}
+
+
+# 50000 INSERTs on an unindexed table
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ set x [number_name $r]
+ lappend list $i $r $x
+}
+set script {
+ foreach {i r x} $::list {
+ db eval {INSERT INTO t1 VALUES($i,$r,$x)}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-insert1 50000 row $script
+db eval COMMIT
+
+# 50000 INSERTs on an indexed table
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ set x [number_name $r]
+ lappend list $i $r $x
+}
+set script {
+ foreach {i r x} $::list {
+ db eval {INSERT INTO t2 VALUES($i,$r,$x)}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-insert2 50000 row $script
+db eval COMMIT
+
+
+
+# 50 SELECTs on an integer comparison. There is no index so
+# a full table scan is required.
+#
+set list {}
+for {set i 0} {$i<50} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select1 [expr {50*50000}] row $script
+db eval COMMIT
+
+# 50 SELECTs on an LIKE comparison. There is no index so a full
+# table scan is required.
+#
+set list {}
+for {set i 0} {$i<50} {incr i} {
+ lappend list "%[number_name $i]%"
+}
+set script {
+ foreach pattern $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE c LIKE $pattern}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select2 [expr {50*50000}] row $script
+db eval COMMIT
+
+# Create indices
+#
+db eval BEGIN
+speed_trial speed1p-createidx 150000 row {
+ CREATE INDEX i1a ON t1(a);
+ CREATE INDEX i1b ON t1(b);
+ CREATE INDEX i1c ON t1(c);
+}
+db eval COMMIT
+
+# 5000 SELECTs on an integer comparison where the integer is
+# indexed.
+#
+set list {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select3 5000 stmt $script
+db eval COMMIT
+
+# 100000 random SELECTs against rowid.
+#
+set list {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ lappend list $id
+}
+set script {
+ foreach id $::list {
+ db eval {SELECT c FROM t1 WHERE rowid=$id}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select4 100000 row $script
+db eval COMMIT
+
+# 100000 random SELECTs against a unique indexed column.
+#
+set list {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ lappend list $id
+}
+set script {
+ foreach id $::list {
+ db eval {SELECT c FROM t1 WHERE a=$id}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select5 100000 row $script
+db eval COMMIT
+
+# 50000 random SELECTs against an indexed column text column
+#
+set list [db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000}]
+set script {
+ foreach c $::list {
+ db eval {SELECT c FROM t1 WHERE c=$c}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-select6 50000 row $script
+db eval COMMIT
+
+
+# Vacuum
+speed_trial speed1p-vacuum 100000 row VACUUM
+
+# 5000 updates of ranges where the field being compared is indexed.
+#
+set list {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*2}]
+ set upr [expr {($i+1)*2}]
+ lappend list $lwr $upr
+}
+set script {
+ foreach {lwr upr} $::list {
+ db eval {UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-update1 5000 stmt $script
+db eval COMMIT
+
+# 50000 single-row updates. An index is used to find the row quickly.
+#
+set list {}
+for {set i 0} {$i<50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ lappend list $i $r
+}
+set script {
+ foreach {i r} $::list {
+ db eval {UPDATE t1 SET b=$r WHERE a=$i}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-update2 50000 row $script
+db eval COMMIT
+
+# 1 big text update that touches every row in the table.
+#
+speed_trial speed1p-update3 50000 row {
+ UPDATE t1 SET c=a;
+}
+
+# Many individual text updates. Each row in the table is
+# touched through an index.
+#
+set list {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ lappend list $i [number_name $r]
+}
+set script {
+ foreach {i x} $::list {
+ db eval {UPDATE t1 SET c=$x WHERE a=$i}
+ }
+}
+db eval BEGIN
+speed_trial_tcl speed1p-update4 50000 row $script
+db eval COMMIT
+
+# Delete all content in a table.
+#
+speed_trial speed1p-delete1 50000 row {DELETE FROM t1}
+
+# Copy one table into another
+#
+speed_trial speed1p-copy1 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Delete all content in a table, one row at a time.
+#
+speed_trial speed1p-delete2 50000 row {DELETE FROM t1 WHERE 1}
+
+# Refill the table yet again
+#
+speed_trial speed1p-copy2 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Drop the table and recreate it without its indices.
+#
+db eval BEGIN
+speed_trial speed1p-drop1 50000 row {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+}
+db eval COMMIT
+
+# Refill the table yet again. This copy should be faster because
+# there are no indices to deal with.
+#
+speed_trial speed1p-copy3 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Select 20000 rows from the table at random.
+#
+speed_trial speed1p-random1 50000 row {
+ SELECT rowid FROM t1 ORDER BY random() LIMIT 20000
+}
+
+# Delete 20000 random rows from the table.
+#
+speed_trial speed1p-random-del1 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1p-1.1 {
+ db one {SELECT count(*) FROM t1}
+} 30000
+
+
+# Delete 20000 more rows at random from the table.
+#
+speed_trial speed1p-random-del2 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed1p-1.2 {
+ db one {SELECT count(*) FROM t1}
+} 10000
+speed_trial_summary speed1
+
+finish_test
diff --git a/third_party/sqlite/test/speed2.test b/third_party/sqlite/test/speed2.test
new file mode 100755
index 0000000..f6d1a4c
--- /dev/null
+++ b/third_party/sqlite/test/speed2.test
@@ -0,0 +1,339 @@
+# 2006 November 23
+#
+# 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 measuring executing speed.
+#
+# $Id: speed2.test,v 1.7 2007/04/16 15:02:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed2
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed2.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Create a database schema.
+#
+do_test speed2-1.0 {
+ execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=8192;
+ PRAGMA locking_mode=EXCLUSIVE;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+ CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ }
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1;
+ }
+} {i2a i2b t1 t2}
+
+
+# 50000 INSERTs on an unindexed table
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');\n"
+}
+db eval BEGIN
+speed_trial speed2-insert1 50000 row $sql
+db eval COMMIT
+
+# 50000 INSERTs on an indexed table
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');\n"
+}
+db eval BEGIN
+speed_trial speed2-insert2 50000 row $sql
+db eval COMMIT
+
+
+
+# 50 SELECTs on an integer comparison. There is no index so
+# a full table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+speed_trial speed2-select1a [expr {50*50000}] row $sql
+
+# 50 SELECTs on an LIKE comparison. There is no index so a full
+# table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ append sql \
+ "SELECT count(*), avg(b) FROM t1 WHERE c LIKE '%[number_name $i]%';"
+}
+speed_trial speed2-select2a [expr {50*50000}] row $sql
+
+# Vacuum
+speed_trial speed2-vacuum1 100000 row VACUUM
+
+# 50 SELECTs on an integer comparison. There is no index so
+# a full table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+speed_trial speed2-select1b [expr {50*50000}] row $sql
+
+# 50 SELECTs on an LIKE comparison. There is no index so a full
+# table scan is required.
+#
+set sql {}
+for {set i 0} {$i<50} {incr i} {
+ append sql \
+ "SELECT count(*), avg(b) FROM t1 WHERE c LIKE '%[number_name $i]%';"
+}
+speed_trial speed2-select2b [expr {50*50000}] row $sql
+
+# Create indices
+#
+db eval BEGIN
+speed_trial speed2-createidx 150000 row {
+ CREATE INDEX i1a ON t1(a);
+ CREATE INDEX i1b ON t1(b);
+ CREATE INDEX i1c ON t1(c);
+}
+db eval COMMIT
+
+# 5000 SELECTs on an integer comparison where the integer is
+# indexed.
+#
+set sql {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+speed_trial speed2-select3a 5000 stmt $sql
+
+# 100000 random SELECTs against rowid.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c=='hi' FROM t1 WHERE rowid=$id;\n"
+}
+speed_trial speed2-select4a 100000 row $sql
+
+# 100000 random SELECTs against a unique indexed column.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c FROM t1 WHERE a=$id;"
+}
+speed_trial speed2-select5a 100000 row $sql
+
+# 50000 random SELECTs against an indexed column text column
+#
+set sql {}
+db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000} {
+ append sql "SELECT c FROM t1 WHERE c='$c';"
+}
+speed_trial speed2-select6a 50000 row $sql
+
+# Vacuum
+speed_trial speed2-vacuum2 100000 row VACUUM
+
+
+# 5000 SELECTs on an integer comparison where the integer is
+# indexed.
+#
+set sql {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
+}
+speed_trial speed2-select3b 5000 stmt $sql
+
+# 100000 random SELECTs against rowid.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c=='hi' FROM t1 WHERE rowid=$id;\n"
+}
+speed_trial speed2-select4b 100000 row $sql
+
+# 100000 random SELECTs against a unique indexed column.
+#
+set sql {}
+for {set i 1} {$i<=100000} {incr i} {
+ set id [expr {int(rand()*50000)+1}]
+ append sql "SELECT c FROM t1 WHERE a=$id;"
+}
+speed_trial speed2-select5b 100000 row $sql
+
+# 50000 random SELECTs against an indexed column text column
+#
+set sql {}
+db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000} {
+ append sql "SELECT c FROM t1 WHERE c='$c';"
+}
+speed_trial speed2-select6b 50000 row $sql
+
+# 5000 updates of ranges where the field being compared is indexed.
+#
+set sql {}
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*2}]
+ set upr [expr {($i+1)*2}]
+ append sql "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
+}
+db eval BEGIN
+speed_trial speed2-update1 5000 stmt $sql
+db eval COMMIT
+
+# 50000 single-row updates. An index is used to find the row quickly.
+#
+set sql {}
+for {set i 0} {$i<50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "UPDATE t1 SET b=$r WHERE a=$i;"
+}
+db eval BEGIN
+speed_trial speed2-update2 50000 row $sql
+db eval COMMIT
+
+# 1 big text update that touches every row in the table.
+#
+speed_trial speed2-update3 50000 row {
+ UPDATE t1 SET c=a;
+}
+
+# Many individual text updates. Each row in the table is
+# touched through an index.
+#
+set sql {}
+for {set i 1} {$i<=50000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ append sql "UPDATE t1 SET c='[number_name $r]' WHERE a=$i;"
+}
+db eval BEGIN
+speed_trial speed2-update4 50000 row $sql
+db eval COMMIT
+
+# Delete all content in a table.
+#
+speed_trial speed2-delete1 50000 row {DELETE FROM t1}
+
+# Copy one table into another
+#
+speed_trial speed2-copy1 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Delete all content in a table, one row at a time.
+#
+speed_trial speed2-delete2 50000 row {DELETE FROM t1 WHERE 1}
+
+# Refill the table yet again
+#
+speed_trial speed2-copy2 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Drop the table and recreate it without its indices.
+#
+db eval BEGIN
+speed_trial speed2-drop1 50000 row {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
+}
+db eval COMMIT
+
+# Refill the table yet again. This copy should be faster because
+# there are no indices to deal with.
+#
+speed_trial speed2-copy3 50000 row {INSERT INTO t1 SELECT * FROM t2}
+
+# Select 20000 rows from the table at random.
+#
+speed_trial speed2-random1 50000 row {
+ SELECT rowid FROM t1 ORDER BY random() LIMIT 20000
+}
+
+# Delete 20000 random rows from the table.
+#
+speed_trial speed2-random-del1 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed2-1.1 {
+ db one {SELECT count(*) FROM t1}
+} 30000
+
+
+# Delete 20000 more rows at random from the table.
+#
+speed_trial speed2-random-del2 20000 row {
+ DELETE FROM t1 WHERE rowid IN
+ (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
+}
+do_test speed2-1.2 {
+ db one {SELECT count(*) FROM t1}
+} 10000
+speed_trial_summary speed2
+
+
+finish_test
diff --git a/third_party/sqlite/test/speed3.test b/third_party/sqlite/test/speed3.test
new file mode 100755
index 0000000..db0d2a9
--- /dev/null
+++ b/third_party/sqlite/test/speed3.test
@@ -0,0 +1,186 @@
+# 2007 May 17
+#
+# 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 testing that the overflow-page related
+# enhancements added after version 3.3.17 speed things up.
+#
+# $Id: speed3.test,v 1.5 2007/10/09 08:29:33 danielk1977 Exp $
+#
+
+#---------------------------------------------------------------------
+# Test plan:
+#
+# If auto-vacuum is enabled for the database, the following cases
+# should show performance improvement with respect to 3.3.17.
+#
+# + When deleting rows that span overflow pages. This is faster
+# because the overflow pages no longer need to be read before
+# they can be moved to the free list (test cases speed3-1.X).
+#
+# + When reading a column value stored on an overflow page that
+# is not the first overflow page for the row. The improvement
+# in this case is because the overflow pages between the tree
+# page and the overflow page containing the value do not have
+# to be read (test cases speed3-2.X).
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tclvar||!attach {
+ finish_test
+ return
+}
+
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set ::NROW 1000
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+proc populate_t1 {db} {
+ $db transaction {
+ for {set ii 0} {$ii < $::NROW} {incr ii} {
+ set N [number_name $ii]
+ set repeats [expr {(10000/[string length $N])+1}]
+ set text [string range [string repeat $N $repeats] 0 10000]
+ $db eval {INSERT INTO main.t1 VALUES($ii, $text, $ii)}
+ }
+ $db eval {INSERT INTO aux.t1 SELECT * FROM main.t1}
+ }
+}
+
+
+proc io_log {db} {
+ db_enter db
+ array set stats1 [btree_pager_stats [btree_from_db db]]
+ array set stats2 [btree_pager_stats [btree_from_db db 2]]
+ db_leave db
+# puts "1: [array get stats1]"
+# puts "2: [array get stats2]"
+ puts "Incrvacuum: Read $stats1(read), wrote $stats1(write)"
+ puts "Normal : Read $stats2(read), wrote $stats2(write)"
+}
+
+proc overflow_report {db} {
+ set bt [btree_from_db db]
+ set csr [btree_cursor $bt 3 0]
+
+ for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} {
+ puts "[btree_ovfl_info $bt $csr]"
+ }
+
+ btree_close_cursor $csr
+
+}
+
+proc reset_db {} {
+ db close
+ sqlite3 db test.db
+ db eval {
+ PRAGMA main.cache_size = 200000;
+ PRAGMA main.auto_vacuum = 'incremental';
+ ATTACH 'test2.db' AS 'aux';
+ PRAGMA aux.auto_vacuum = 'none';
+ }
+}
+
+file delete -force test2.db test2.db-journal
+reset_db
+
+# Set up a database in auto-vacuum mode and create a database schema.
+#
+do_test speed3-0.1 {
+ execsql {
+ CREATE TABLE main.t1(a INTEGER, b TEXT, c INTEGER);
+ }
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1;
+ }
+} {t1}
+do_test speed3-0.2 {
+ execsql {
+ CREATE TABLE aux.t1(a INTEGER, b TEXT, c INTEGER);
+ }
+ execsql {
+ SELECT name FROM aux.sqlite_master ORDER BY 1;
+ }
+} {t1}
+do_test speed3-0.3 {
+ populate_t1 db
+ execsql {
+ SELECT count(*) FROM main.t1;
+ SELECT count(*) FROM aux.t1;
+ }
+} "$::NROW $::NROW"
+do_test speed3-0.4 {
+ execsql {
+ PRAGMA main.auto_vacuum;
+ PRAGMA aux.auto_vacuum;
+ }
+} {2 0}
+
+# Delete all content in a table, one row at a time.
+#
+#io_log db
+#overflow_report db
+reset_db
+speed_trial speed3-1.incrvacuum $::NROW row {DELETE FROM main.t1 WHERE 1}
+speed_trial speed3-1.normal $::NROW row {DELETE FROM aux.t1 WHERE 1}
+io_log db
+
+# Select the "C" column (located at the far end of the overflow
+# chain) from each table row.
+#
+#db eval {PRAGMA incremental_vacuum(500000)}
+populate_t1 db
+#overflow_report db
+reset_db
+speed_trial speed3-2.incrvacuum $::NROW row {SELECT c FROM main.t1}
+speed_trial speed3-2.normal $::NROW row {SELECT c FROM aux.t1}
+io_log db
+
+finish_test
diff --git a/third_party/sqlite/test/speed4.test b/third_party/sqlite/test/speed4.test
new file mode 100755
index 0000000..2ccdaf5
--- /dev/null
+++ b/third_party/sqlite/test/speed4.test
@@ -0,0 +1,231 @@
+# 2007 October 23
+#
+# 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 measuring executing speed. More specifically,
+# the focus is on the speed of:
+#
+# * joins
+# * views
+# * sub-selects
+# * triggers
+#
+# $Id: speed4.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed1.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Summary of tests:
+#
+# speed4-join1: Join three tables using IPK index.
+# speed4-join2: Join three tables using an index.
+# speed4-join3: Join two tables without an index.
+#
+# speed4-view1: Querying a view.
+# speed4-table1: Same queries as in speed4-view1, but run directly against
+# the tables for comparison purposes.
+#
+# speed4-subselect1: A SELECT statement that uses many sub-queries..
+#
+# speed4-trigger1: An INSERT statement that fires a trigger.
+# speed4-trigger2: An UPDATE statement that fires a trigger.
+# speed4-trigger3: A DELETE statement that fires a trigger.
+# speed4-notrigger1: Same operation as trigger1, but without the trigger.
+# speed4-notrigger2: " trigger2 "
+# speed4-notrigger3: " trigger3 "
+#
+
+# Set up the schema. Each of the tables t1, t2 and t3 contain 50,000 rows.
+# This creates a database of around 16MB.
+execsql {
+ BEGIN;
+ CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+
+ CREATE VIEW v1 AS SELECT rowid, i, t FROM t1;
+ CREATE VIEW v2 AS SELECT rowid, i, t FROM t2;
+ CREATE VIEW v3 AS SELECT rowid, i, t FROM t3;
+}
+for {set jj 1} {$jj <= 3} {incr jj} {
+ set stmt [string map "%T% t$jj" {INSERT INTO %T% VALUES(NULL, $i, $t)}]
+ for {set ii 0} {$ii < 50000} {incr ii} {
+ set i [expr {int(rand()*50000)}]
+ set t [number_name $i]
+ execsql $stmt
+ }
+}
+execsql {
+ CREATE INDEX i1 ON t1(t);
+ CREATE INDEX i2 ON t2(t);
+ CREATE INDEX i3 ON t3(t);
+ COMMIT;
+}
+
+# Before running these tests, disable the compiled statement cache built into
+# the Tcl interface. This is because we want to test the speed of SQL
+# compilation as well as execution.
+#
+db cache size 0
+
+# Join t1, t2, t3 on IPK.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid"
+speed_trial speed4-join1 50000 row $sql
+
+# Join t1, t2, t3 on the non-IPK index.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t"
+speed_trial speed4-join2 50000 row $sql
+
+# Run 10000 simple queries against the views.
+set sql ""
+for {set ii 1} {$ii < 10000} {incr ii} {
+ append sql "SELECT * FROM v[expr {($ii%3)+1}] WHERE rowid = [expr {$ii*3}];"
+}
+speed_trial speed4-view1 10000 stmt $sql
+
+# Run the same 10000 simple queries as in the previous test case against
+# the underlying tables. The compiled vdbe programs should be identical, so
+# the only difference in running time is the extra time taken to compile
+# the view definitions.
+#
+set sql ""
+for {set ii 1} {$ii < 10000} {incr ii} {
+ append sql "SELECT t FROM t[expr {($ii%3)+1}] WHERE rowid = [expr {$ii*3}];"
+}
+speed_trial speed4-table1 10000 stmt $sql
+
+# Run a SELECT that uses sub-queries 10000 times. A total of 30000 sub-selects.
+#
+set sql ""
+for {set ii 1} {$ii < 10000} {incr ii} {
+ append sql "
+ SELECT (SELECT t FROM t1 WHERE rowid = [expr {$ii*3}]),
+ (SELECT t FROM t2 WHERE rowid = [expr {$ii*3}]),
+ (SELECT t FROM t3 WHERE rowid = [expr {$ii*3}])
+ ;"
+}
+speed_trial speed4-subselect1 10000 stmt $sql
+
+# The following block tests the speed of some DML statements that cause
+# triggers to fire.
+#
+execsql {
+ CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT);
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN
+ INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN
+ INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN
+ INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t);
+ END;
+ BEGIN;
+}
+set sql ""
+for {set ii 1} {$ii < 10000} {incr ii} {
+ append sql "INSERT INTO t4 VALUES(NULL, $ii, '[number_name $ii]');"
+}
+speed_trial speed4-trigger1 10000 stmt $sql
+set sql ""
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ append sql "
+ UPDATE t4 SET i = $ii2, t = '[number_name $ii2]' WHERE rowid = $ii;
+ "
+}
+speed_trial speed4-trigger2 10000 stmt $sql
+set sql ""
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ append sql "DELETE FROM t4 WHERE rowid = $ii;"
+}
+speed_trial speed4-trigger3 10000 stmt $sql
+execsql {COMMIT}
+
+# The following block contains the same tests as the above block that
+# tests triggers, with one crucial difference: no triggers are defined.
+# So the difference in speed between these tests and the preceding ones
+# is the amount of time taken to compile and execute the trigger programs.
+#
+execsql {
+ DROP TABLE t4;
+ DROP TABLE log;
+ VACUUM;
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ BEGIN;
+}
+set sql ""
+for {set ii 1} {$ii < 10000} {incr ii} {
+ append sql "INSERT INTO t4 VALUES(NULL, $ii, '[number_name $ii]');"
+}
+speed_trial speed4-notrigger1 10000 stmt $sql
+set sql ""
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ append sql "
+ UPDATE t4 SET i = $ii2, t = '[number_name $ii2]' WHERE rowid = $ii;
+ "
+}
+speed_trial speed4-notrigger2 10000 stmt $sql
+set sql ""
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ append sql "DELETE FROM t4 WHERE rowid = $ii;"
+}
+speed_trial speed4-notrigger3 10000 stmt $sql
+execsql {COMMIT}
+
+speed_trial_summary speed4
+finish_test
diff --git a/third_party/sqlite/test/speed4p.explain b/third_party/sqlite/test/speed4p.explain
new file mode 100755
index 0000000..a713955
--- /dev/null
+++ b/third_party/sqlite/test/speed4p.explain
@@ -0,0 +1,283 @@
+# 2007 October 23
+#
+# 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 measuring executing speed. More specifically,
+# the focus is on the speed of:
+#
+# * joins
+# * views
+# * sub-selects
+# * triggers
+#
+# $Id: speed4p.explain,v 1.1 2008/04/16 12:57:48 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed1.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Summary of tests:
+#
+# speed4p-join1: Join three tables using IPK index.
+# speed4p-join2: Join three tables using an index.
+# speed4p-join3: Join two tables without an index.
+#
+# speed4p-view1: Querying a view.
+# speed4p-table1: Same queries as in speed4p-view1, but run directly against
+# the tables for comparison purposes.
+#
+# speed4p-subselect1: A SELECT statement that uses many sub-queries..
+#
+# speed4p-trigger1: An INSERT statement that fires a trigger.
+# speed4p-trigger2: An UPDATE statement that fires a trigger.
+# speed4p-trigger3: A DELETE statement that fires a trigger.
+# speed4p-notrigger1: Same operation as trigger1, but without the trigger.
+# speed4p-notrigger2: " trigger2 "
+# speed4p-notrigger3: " trigger3 "
+#
+
+# Set up the schema. Each of the tables t1, t2 and t3 contain 50,000 rows.
+# This creates a database of around 16MB.
+execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=8192;
+ PRAGMA locking_mode=EXCLUSIVE;
+ BEGIN;
+ CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+
+ CREATE VIEW v1 AS SELECT rowid, i, t FROM t1;
+ CREATE VIEW v2 AS SELECT rowid, i, t FROM t2;
+ CREATE VIEW v3 AS SELECT rowid, i, t FROM t3;
+}
+for {set jj 1} {$jj <= 3} {incr jj} {
+ set stmt [string map "%T% t$jj" {INSERT INTO %T% VALUES(NULL, $i, $t)}]
+ for {set ii 0} {$ii < 50000} {incr ii} {
+ set i [expr {int(rand()*50000)}]
+ set t [number_name $i]
+ execsql $stmt
+ }
+}
+execsql {
+ CREATE INDEX i1 ON t1(t);
+ CREATE INDEX i2 ON t2(t);
+ CREATE INDEX i3 ON t3(t);
+ COMMIT;
+}
+
+# Before running these tests, disable the compiled statement cache built into
+# the Tcl interface. This is because we want to test the speed of SQL
+# compilation as well as execution.
+#
+db cache size 0
+
+# Join t1, t2, t3 on IPK.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid"
+explain $sql
+speed_trial speed4p-join1 50000 row $sql
+
+# Join t1, t2, t3 on the non-IPK index.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t"
+explain $sql
+speed_trial speed4p-join2 50000 row $sql
+
+# Run 10000 simple queries against the views.
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ set t [expr {$ii%3+1}]
+ db eval "SELECT * FROM v$t WHERE rowid = \$v"
+ }
+}
+explain {SELECT * FROm v1 WHERE rowid=$v}
+speed_trial_tcl speed4p-view1 10000 stmt $script
+
+# Run the same 10000 simple queries as in the previous test case against
+# the underlying tables. The compiled vdbe programs should be identical, so
+# the only difference in running time is the extra time taken to compile
+# the view definitions.
+#
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ set t [expr {$ii%3+1}]
+ db eval "SELECT t FROM t$t WHERE rowid = \$v"
+ }
+}
+explain {SELECT * FROM t1 WHERE rowid=$v}
+speed_trial_tcl speed4p-table1 10000 stmt $script
+
+# Run a SELECT that uses sub-queries 10000 times. A total of 30000 sub-selects.
+#
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ db eval {
+ SELECT (SELECT t FROM t1 WHERE rowid = $v),
+ (SELECT t FROM t2 WHERE rowid = $v),
+ (SELECT t FROM t3 WHERE rowid = $v)
+ }
+ }
+}
+explain {
+ SELECT (SELECT t FROM t1 WHERE rowid = $v),
+ (SELECT t FROM t2 WHERE rowid = $v),
+ (SELECT t FROM t3 WHERE rowid = $v)
+}
+speed_trial_tcl speed4p-subselect1 10000 stmt $script
+
+# The following block tests the speed of some DML statements that cause
+# triggers to fire.
+#
+execsql {
+ CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT);
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN
+ INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN
+ INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN
+ INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t);
+ END;
+ BEGIN;
+}
+set list {}
+for {set ii 1} {$ii < 10000} {incr ii} {
+ lappend list $ii [number_name $ii]
+}
+set script {
+ foreach {ii name} $::list {
+ db eval {INSERT INTO t4 VALUES(NULL, $ii, $name)}
+ }
+}
+explain {INSERT INTO t4 VALUES(NULL, $ii, $name)}
+speed_trial_tcl speed4p-trigger1 10000 stmt $script
+
+set list {}
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ lappend list $ii $ii2 [number_name $ii2]
+}
+set script {
+ foreach {ii ii2 name} $::list {
+ db eval {
+ UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii;
+ }
+ }
+}
+explain {UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii}
+speed_trial_tcl speed4p-trigger2 10000 stmt $script
+
+set script {
+ for {set ii 1} {$ii < 20000} {incr ii 2} {
+ db eval {DELETE FROM t4 WHERE rowid = $ii}
+ }
+}
+explain {DELETE FROM t4 WHERE rowid = $ii}
+speed_trial_tcl speed4p-trigger3 10000 stmt $script
+execsql {COMMIT}
+
+# The following block contains the same tests as the above block that
+# tests triggers, with one crucial difference: no triggers are defined.
+# So the difference in speed between these tests and the preceding ones
+# is the amount of time taken to compile and execute the trigger programs.
+#
+execsql {
+ DROP TABLE t4;
+ DROP TABLE log;
+ VACUUM;
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ BEGIN;
+}
+set list {}
+for {set ii 1} {$ii < 10000} {incr ii} {
+ lappend list $ii [number_name $ii]
+}
+set script {
+ foreach {ii name} $::list {
+ db eval {INSERT INTO t4 VALUES(NULL, $ii, $name);}
+ }
+}
+explain {INSERT INTO t4 VALUES(NULL, $ii, $name)}
+speed_trial_tcl speed4p-notrigger1 10000 stmt $script
+
+set list {}
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ lappend list $ii $ii2 [number_name $ii2]
+}
+set script {
+ foreach {ii ii2 name} $::list {
+ db eval {
+ UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii;
+ }
+ }
+}
+explain {UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii}
+speed_trial_tcl speed4p-notrigger2 10000 stmt $script
+
+set script {
+ for {set ii 1} {$ii < 20000} {incr ii 2} {
+ db eval {DELETE FROM t4 WHERE rowid = $ii}
+ }
+}
+explain {DELETE FROM t4 WHERE rowid = $ii}
+speed_trial_tcl speed4p-notrigger3 10000 stmt $script
+execsql {COMMIT}
+
+speed_trial_summary speed4
+finish_test
diff --git a/third_party/sqlite/test/speed4p.test b/third_party/sqlite/test/speed4p.test
new file mode 100755
index 0000000..024232e
--- /dev/null
+++ b/third_party/sqlite/test/speed4p.test
@@ -0,0 +1,292 @@
+# 2007 October 23
+#
+# 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 measuring executing speed. More specifically,
+# the focus is on the speed of:
+#
+# * joins
+# * views
+# * sub-selects
+# * triggers
+#
+# $Id: speed4p.test,v 1.4 2008/04/10 13:32:37 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+speed_trial_init speed1
+
+# Set a uniform random seed
+expr srand(0)
+
+set sqlout [open speed1.txt w]
+proc tracesql {sql} {
+ puts $::sqlout $sql\;
+}
+#db trace tracesql
+
+# The number_name procedure below converts its argment (an integer)
+# into a string which is the English-language name for that number.
+#
+# Example:
+#
+# puts [number_name 123] -> "one hundred twenty three"
+#
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+# Summary of tests:
+#
+# speed4p-join1: Join three tables using IPK index.
+# speed4p-join2: Join three tables using an index.
+# speed4p-join3: Join two tables without an index.
+#
+# speed4p-view1: Querying a view.
+# speed4p-table1: Same queries as in speed4p-view1, but run directly against
+# the tables for comparison purposes.
+#
+# speed4p-subselect1: A SELECT statement that uses many sub-queries..
+#
+# speed4p-trigger1: An INSERT statement that fires a trigger.
+# speed4p-trigger2: An UPDATE statement that fires a trigger.
+# speed4p-trigger3: A DELETE statement that fires a trigger.
+# speed4p-notrigger1: Same operation as trigger1, but without the trigger.
+# speed4p-notrigger2: " trigger2 "
+# speed4p-notrigger3: " trigger3 "
+#
+
+# Set up the schema. Each of the tables t1, t2 and t3 contain 50,000 rows.
+# This creates a database of around 16MB.
+execsql {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=8192;
+ PRAGMA locking_mode=EXCLUSIVE;
+ BEGIN;
+ CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+
+ CREATE VIEW v1 AS SELECT rowid, i, t FROM t1;
+ CREATE VIEW v2 AS SELECT rowid, i, t FROM t2;
+ CREATE VIEW v3 AS SELECT rowid, i, t FROM t3;
+}
+for {set jj 1} {$jj <= 3} {incr jj} {
+ set stmt [string map "%T% t$jj" {INSERT INTO %T% VALUES(NULL, $i, $t)}]
+ for {set ii 0} {$ii < 50000} {incr ii} {
+ set i [expr {int(rand()*50000)}]
+ set t [number_name $i]
+ execsql $stmt
+ }
+}
+execsql {
+ CREATE INDEX i1 ON t1(t);
+ CREATE INDEX i2 ON t2(t);
+ CREATE INDEX i3 ON t3(t);
+ COMMIT;
+}
+
+# Join t1, t2, t3 on IPK.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid"
+speed_trial speed4p-join1 50000 row $sql
+
+# Join t1, t2, t3 on the non-IPK index.
+set sql "SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t"
+speed_trial speed4p-join2 50000 row $sql
+
+# Run 10000 simple queries against the views.
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ set t [expr {$ii%3+1}]
+ db eval "SELECT * FROM v$t WHERE rowid = \$v"
+ }
+}
+speed_trial_tcl speed4p-view1 10000 stmt $script
+
+# Run the same 10000 simple queries as in the previous test case against
+# the underlying tables. The compiled vdbe programs should be identical, so
+# the only difference in running time is the extra time taken to compile
+# the view definitions.
+#
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ set t [expr {$ii%3+1}]
+ db eval "SELECT t FROM t$t WHERE rowid = \$v"
+ }
+}
+speed_trial_tcl speed4p-table1 10000 stmt $script
+
+# Run a SELECT that uses sub-queries 10000 times. A total of 30000 sub-selects.
+#
+set script {
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ db eval {
+ SELECT (SELECT t FROM t1 WHERE rowid = $v),
+ (SELECT t FROM t2 WHERE rowid = $v),
+ (SELECT t FROM t3 WHERE rowid = $v)
+ }
+ }
+}
+speed_trial_tcl speed4p-subselect1 10000 stmt $script
+
+# Single-row updates performance.
+#
+set script {
+ db eval BEGIN
+ for {set ii 1} {$ii < 10000} {incr ii} {
+ set v [expr {$ii*3}]
+ db eval {UPDATE t1 SET i=i+1 WHERE rowid=$ii}
+ }
+ db eval COMMIT
+}
+speed_trial_tcl speed4p-rowid-update 10000 stmt $script
+
+
+db eval {
+ CREATE TABLE t5(t TEXT PRIMARY KEY, i INTEGER);
+}
+speed_trial speed4p-insert-ignore 50000 row {
+ INSERT OR IGNORE INTO t5 SELECT t, i FROM t1;
+}
+
+set list [db eval {SELECT t FROM t5}]
+set script {
+ db eval BEGIN
+ foreach t $::list {
+ db eval {UPDATE t5 SET i=i+1 WHERE t=$t}
+ }
+ db eval COMMIT
+}
+speed_trial_tcl speed4p-unique-update [llength $list] stmt $script
+
+# The following block tests the speed of some DML statements that cause
+# triggers to fire.
+#
+execsql {
+ CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT);
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN
+ INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN
+ INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t);
+ END;
+ CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN
+ INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t);
+ END;
+ BEGIN;
+}
+set list {}
+for {set ii 1} {$ii < 10000} {incr ii} {
+ lappend list $ii [number_name $ii]
+}
+set script {
+ foreach {ii name} $::list {
+ db eval {INSERT INTO t4 VALUES(NULL, $ii, $name)}
+ }
+}
+speed_trial_tcl speed4p-trigger1 10000 stmt $script
+
+set list {}
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ lappend list $ii $ii2 [number_name $ii2]
+}
+set script {
+ foreach {ii ii2 name} $::list {
+ db eval {
+ UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii;
+ }
+ }
+}
+speed_trial_tcl speed4p-trigger2 10000 stmt $script
+
+set script {
+ for {set ii 1} {$ii < 20000} {incr ii 2} {
+ db eval {DELETE FROM t4 WHERE rowid = $ii}
+ }
+}
+speed_trial_tcl speed4p-trigger3 10000 stmt $script
+execsql {COMMIT}
+
+# The following block contains the same tests as the above block that
+# tests triggers, with one crucial difference: no triggers are defined.
+# So the difference in speed between these tests and the preceding ones
+# is the amount of time taken to compile and execute the trigger programs.
+#
+execsql {
+ DROP TABLE t4;
+ DROP TABLE log;
+ VACUUM;
+ CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);
+ BEGIN;
+}
+set list {}
+for {set ii 1} {$ii < 10000} {incr ii} {
+ lappend list $ii [number_name $ii]
+}
+set script {
+ foreach {ii name} $::list {
+ db eval {INSERT INTO t4 VALUES(NULL, $ii, $name);}
+ }
+}
+speed_trial_tcl speed4p-notrigger1 10000 stmt $script
+
+set list {}
+for {set ii 1} {$ii < 20000} {incr ii 2} {
+ set ii2 [expr {$ii*2}]
+ lappend list $ii $ii2 [number_name $ii2]
+}
+set script {
+ foreach {ii ii2 name} $::list {
+ db eval {
+ UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii;
+ }
+ }
+}
+speed_trial_tcl speed4p-notrigger2 10000 stmt $script
+
+set script {
+ for {set ii 1} {$ii < 20000} {incr ii 2} {
+ db eval {DELETE FROM t4 WHERE rowid = $ii}
+ }
+}
+speed_trial_tcl speed4p-notrigger3 10000 stmt $script
+execsql {COMMIT}
+
+speed_trial_summary speed4
+finish_test
diff --git a/third_party/sqlite/test/sqllimits1.test b/third_party/sqlite/test/sqllimits1.test
new file mode 100755
index 0000000..f7f5c2b
--- /dev/null
+++ b/third_party/sqlite/test/sqllimits1.test
@@ -0,0 +1,841 @@
+# 2007 May 8
+#
+# 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 contains tests to verify that the limits defined in
+# sqlite source file limits.h are enforced.
+#
+# $Id: sqllimits1.test,v 1.31 2008/07/15 00:27:35 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Verify that the default per-connection limits are the same as
+# the compile-time hard limits.
+#
+sqlite3 db2 :memory:
+do_test sqllimits1-1.1 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH -1
+} $SQLITE_MAX_LENGTH
+do_test sqllimits1-1.2 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH -1
+} $SQLITE_MAX_SQL_LENGTH
+do_test sqllimits1-1.3 {
+ sqlite3_limit db SQLITE_LIMIT_COLUMN -1
+} $SQLITE_MAX_COLUMN
+do_test sqllimits1-1.4 {
+ sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH -1
+} $SQLITE_MAX_EXPR_DEPTH
+do_test sqllimits1-1.5 {
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT -1
+} $SQLITE_MAX_COMPOUND_SELECT
+do_test sqllimits1-1.6 {
+ sqlite3_limit db SQLITE_LIMIT_VDBE_OP -1
+} $SQLITE_MAX_VDBE_OP
+do_test sqllimits1-1.7 {
+ sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1
+} $SQLITE_MAX_FUNCTION_ARG
+do_test sqllimits1-1.8 {
+ sqlite3_limit db SQLITE_LIMIT_ATTACHED -1
+} $SQLITE_MAX_ATTACHED
+do_test sqllimits1-1.9 {
+ sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH -1
+} $SQLITE_MAX_LIKE_PATTERN_LENGTH
+do_test sqllimits1-1.10 {
+ sqlite3_limit db SQLITE_LIMIT_VARIABLE_NUMBER -1
+} $SQLITE_MAX_VARIABLE_NUMBER
+
+# Limit parameters out of range.
+#
+do_test sqllimits1-1.20 {
+ sqlite3_limit db SQLITE_LIMIT_TOOSMALL 123
+} {-1}
+do_test sqllimits1-1.21 {
+ sqlite3_limit db SQLITE_LIMIT_TOOSMALL 123
+} {-1}
+do_test sqllimits1-1.22 {
+ sqlite3_limit db SQLITE_LIMIT_TOOBIG 123
+} {-1}
+do_test sqllimits1-1.23 {
+ sqlite3_limit db SQLITE_LIMIT_TOOBIG 123
+} {-1}
+
+
+# Decrease all limits by half. Verify that the new limits take.
+#
+if {$SQLITE_MAX_LENGTH>=2} {
+ do_test sqllimits1-2.1.1 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH \
+ [expr {$::SQLITE_MAX_LENGTH/2}]
+ } $SQLITE_MAX_LENGTH
+ do_test sqllimits1-2.1.2 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH -1
+ } [expr {$SQLITE_MAX_LENGTH/2}]
+}
+if {$SQLITE_MAX_SQL_LENGTH>=2} {
+ do_test sqllimits1-2.2.1 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH \
+ [expr {$::SQLITE_MAX_SQL_LENGTH/2}]
+ } $SQLITE_MAX_SQL_LENGTH
+ do_test sqllimits1-2.2.2 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH -1
+ } [expr {$SQLITE_MAX_SQL_LENGTH/2}]
+}
+if {$SQLITE_MAX_COLUMN>=2} {
+ do_test sqllimits1-2.3.1 {
+ sqlite3_limit db SQLITE_LIMIT_COLUMN \
+ [expr {$::SQLITE_MAX_COLUMN/2}]
+ } $SQLITE_MAX_COLUMN
+ do_test sqllimits1-2.3.2 {
+ sqlite3_limit db SQLITE_LIMIT_COLUMN -1
+ } [expr {$SQLITE_MAX_COLUMN/2}]
+}
+if {$SQLITE_MAX_EXPR_DEPTH>=2} {
+ do_test sqllimits1-2.4.1 {
+ sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH \
+ [expr {$::SQLITE_MAX_EXPR_DEPTH/2}]
+ } $SQLITE_MAX_EXPR_DEPTH
+ do_test sqllimits1-2.4.2 {
+ sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH -1
+ } [expr {$SQLITE_MAX_EXPR_DEPTH/2}]
+}
+if {$SQLITE_MAX_COMPOUND_SELECT>=2} {
+ do_test sqllimits1-2.5.1 {
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT \
+ [expr {$::SQLITE_MAX_COMPOUND_SELECT/2}]
+ } $SQLITE_MAX_COMPOUND_SELECT
+ do_test sqllimits1-2.5.2 {
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT -1
+ } [expr {$SQLITE_MAX_COMPOUND_SELECT/2}]
+}
+if {$SQLITE_MAX_VDBE_OP>=2} {
+ do_test sqllimits1-2.6.1 {
+ sqlite3_limit db SQLITE_LIMIT_VDBE_OP \
+ [expr {$::SQLITE_MAX_VDBE_OP/2}]
+ } $SQLITE_MAX_VDBE_OP
+ do_test sqllimits1-2.6.2 {
+ sqlite3_limit db SQLITE_LIMIT_VDBE_OP -1
+ } [expr {$SQLITE_MAX_VDBE_OP/2}]
+}
+if {$SQLITE_MAX_FUNCTION_ARG>=2} {
+ do_test sqllimits1-2.7.1 {
+ sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG \
+ [expr {$::SQLITE_MAX_FUNCTION_ARG/2}]
+ } $SQLITE_MAX_FUNCTION_ARG
+ do_test sqllimits1-2.7.2 {
+ sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1
+ } [expr {$SQLITE_MAX_FUNCTION_ARG/2}]
+}
+if {$SQLITE_MAX_ATTACHED>=2} {
+ do_test sqllimits1-2.8.1 {
+ sqlite3_limit db SQLITE_LIMIT_ATTACHED \
+ [expr {$::SQLITE_MAX_ATTACHED/2}]
+ } $SQLITE_MAX_ATTACHED
+ do_test sqllimits1-2.8.2 {
+ sqlite3_limit db SQLITE_LIMIT_ATTACHED -1
+ } [expr {$SQLITE_MAX_ATTACHED/2}]
+}
+if {$SQLITE_MAX_LIKE_PATTERN_LENGTH>=2} {
+ do_test sqllimits1-2.9.1 {
+ sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH \
+ [expr {$::SQLITE_MAX_LIKE_PATTERN_LENGTH/2}]
+ } $SQLITE_MAX_LIKE_PATTERN_LENGTH
+ do_test sqllimits1-2.9.2 {
+ sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH -1
+ } [expr {$SQLITE_MAX_LIKE_PATTERN_LENGTH/2}]
+}
+if {$SQLITE_MAX_VARIABLE_NUMBER>=2} {
+ do_test sqllimits1-2.10.1 {
+ sqlite3_limit db SQLITE_LIMIT_VARIABLE_NUMBER \
+ [expr {$::SQLITE_MAX_VARIABLE_NUMBER/2}]
+ } $SQLITE_MAX_VARIABLE_NUMBER
+ do_test sqllimits1-2.10.2 {
+ sqlite3_limit db SQLITE_LIMIT_VARIABLE_NUMBER -1
+ } [expr {$SQLITE_MAX_VARIABLE_NUMBER/2}]
+}
+
+# In a separate database connection, verify that the limits are unchanged.
+#
+do_test sqllimits1-3.1 {
+ sqlite3_limit db2 SQLITE_LIMIT_LENGTH -1
+} $SQLITE_MAX_LENGTH
+do_test sqllimits1-3.2 {
+ sqlite3_limit db2 SQLITE_LIMIT_SQL_LENGTH -1
+} $SQLITE_MAX_SQL_LENGTH
+do_test sqllimits1-3.3 {
+ sqlite3_limit db2 SQLITE_LIMIT_COLUMN -1
+} $SQLITE_MAX_COLUMN
+do_test sqllimits1-3.4 {
+ sqlite3_limit db2 SQLITE_LIMIT_EXPR_DEPTH -1
+} $SQLITE_MAX_EXPR_DEPTH
+do_test sqllimits1-3.5 {
+ sqlite3_limit db2 SQLITE_LIMIT_COMPOUND_SELECT -1
+} $SQLITE_MAX_COMPOUND_SELECT
+do_test sqllimits1-3.6 {
+ sqlite3_limit db2 SQLITE_LIMIT_VDBE_OP -1
+} $SQLITE_MAX_VDBE_OP
+do_test sqllimits1-3.7 {
+ sqlite3_limit db2 SQLITE_LIMIT_FUNCTION_ARG -1
+} $SQLITE_MAX_FUNCTION_ARG
+do_test sqllimits1-3.8 {
+ sqlite3_limit db2 SQLITE_LIMIT_ATTACHED -1
+} $SQLITE_MAX_ATTACHED
+do_test sqllimits1-3.9 {
+ sqlite3_limit db2 SQLITE_LIMIT_LIKE_PATTERN_LENGTH -1
+} $SQLITE_MAX_LIKE_PATTERN_LENGTH
+do_test sqllimits1-3.10 {
+ sqlite3_limit db2 SQLITE_LIMIT_VARIABLE_NUMBER -1
+} $SQLITE_MAX_VARIABLE_NUMBER
+db2 close
+
+# Attempt to set all limits to the maximum 32-bit integer. Verify
+# that the limit does not exceed the compile-time upper bound.
+#
+do_test sqllimits1-4.1.1 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_LENGTH -1
+} $SQLITE_MAX_LENGTH
+do_test sqllimits1-4.2.1 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH -1
+} $SQLITE_MAX_SQL_LENGTH
+do_test sqllimits1-4.3.1 {
+ sqlite3_limit db SQLITE_LIMIT_COLUMN 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_COLUMN -1
+} $SQLITE_MAX_COLUMN
+do_test sqllimits1-4.4.1 {
+ sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_EXPR_DEPTH -1
+} $SQLITE_MAX_EXPR_DEPTH
+do_test sqllimits1-4.5.1 {
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT -1
+} $SQLITE_MAX_COMPOUND_SELECT
+do_test sqllimits1-4.6.1 {
+ sqlite3_limit db SQLITE_LIMIT_VDBE_OP 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_VDBE_OP -1
+} $SQLITE_MAX_VDBE_OP
+do_test sqllimits1-4.7.1 {
+ sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1
+} $SQLITE_MAX_FUNCTION_ARG
+do_test sqllimits1-4.8.1 {
+ sqlite3_limit db SQLITE_LIMIT_ATTACHED 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_ATTACHED -1
+} $SQLITE_MAX_ATTACHED
+do_test sqllimits1-4.9.1 {
+ sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH -1
+} $SQLITE_MAX_LIKE_PATTERN_LENGTH
+do_test sqllimits1-4.10.1 {
+ sqlite3_limit db SQLITE_LIMIT_VARIABLE_NUMBER 0x7fffffff
+ sqlite3_limit db SQLITE_LIMIT_VARIABLE_NUMBER -1
+} $SQLITE_MAX_VARIABLE_NUMBER
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-5.* test that the SQLITE_MAX_LENGTH limit
+# is enforced.
+#
+db close
+sqlite3 db test.db
+set LARGESIZE 99999
+set SQLITE_LIMIT_LENGTH 100000
+sqlite3_limit db SQLITE_LIMIT_LENGTH $SQLITE_LIMIT_LENGTH
+
+do_test sqllimits1-5.1.1 {
+ catchsql { SELECT randomblob(2147483647) }
+} {1 {string or blob too big}}
+do_test sqllimits1-5.1.2 {
+ catchsql { SELECT zeroblob(2147483647) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.2 {
+ catchsql { SELECT LENGTH(randomblob($::LARGESIZE)) }
+} [list 0 $LARGESIZE]
+
+do_test sqllimits1-5.3 {
+ catchsql { SELECT quote(randomblob($::LARGESIZE)) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.4 {
+ catchsql { SELECT LENGTH(zeroblob($::LARGESIZE)) }
+} [list 0 $LARGESIZE]
+
+do_test sqllimits1-5.5 {
+ catchsql { SELECT quote(zeroblob($::LARGESIZE)) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.6 {
+ catchsql { SELECT zeroblob(-1) }
+} {0 {{}}}
+
+do_test sqllimits1-5.9 {
+ set ::str [string repeat A 65537]
+ set ::rep [string repeat B 65537]
+ catchsql { SELECT replace($::str, 'A', $::rep) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.10 {
+ set ::str [string repeat %J 2100]
+ catchsql { SELECT strftime($::str, '2003-10-31') }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.11 {
+ set ::str1 [string repeat A [expr {$SQLITE_LIMIT_LENGTH - 10}]]
+ set ::str2 [string repeat B [expr {$SQLITE_LIMIT_LENGTH - 10}]]
+ catchsql { SELECT $::str1 || $::str2 }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.12 {
+ set ::str1 [string repeat ' [expr {$SQLITE_LIMIT_LENGTH - 10}]]
+ catchsql { SELECT quote($::str1) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.13 {
+ set ::str1 [string repeat ' [expr {$SQLITE_LIMIT_LENGTH - 10}]]
+ catchsql { SELECT hex($::str1) }
+} {1 {string or blob too big}}
+
+do_test sqllimits1-5.14.1 {
+ set ::STMT [sqlite3_prepare db "SELECT ?" -1 TAIL]
+ sqlite3_bind_zeroblob $::STMT 1 [expr {$SQLITE_LIMIT_LENGTH + 1}]
+} {}
+do_test sqllimits1-5.14.2 {
+ sqlite3_step $::STMT
+} {SQLITE_ERROR}
+do_test sqllimits1-5.14.3 {
+ sqlite3_reset $::STMT
+} {SQLITE_TOOBIG}
+do_test sqllimits1-5.14.4 {
+ set np1 [expr {$SQLITE_LIMIT_LENGTH + 1}]
+ set ::str1 [string repeat A $np1]
+ catch {sqlite3_bind_text $::STMT 1 $::str1 -1} res
+ set res
+} {SQLITE_TOOBIG}
+do_test sqllimits1-5.14.5 {
+ catch {sqlite3_bind_text16 $::STMT 1 $::str1 -1} res
+ set res
+} {SQLITE_TOOBIG}
+do_test sqllimits1-5.14.6 {
+ catch {sqlite3_bind_text $::STMT 1 $::str1 $np1} res
+ set res
+} {SQLITE_TOOBIG}
+do_test sqllimits1-5.14.7 {
+ catch {sqlite3_bind_text16 $::STMT 1 $::str1 $np1} res
+ set res
+} {SQLITE_TOOBIG}
+do_test sqllimits1-5.14.8 {
+ set n [expr {$np1-1}]
+ catch {sqlite3_bind_text $::STMT 1 $::str1 $n} res
+ set res
+} {}
+do_test sqllimits1-5.14.9 {
+ catch {sqlite3_bind_text16 $::STMT 1 $::str1 $n} res
+ set res
+} {}
+sqlite3_finalize $::STMT
+
+do_test sqllimits1-5.15 {
+ execsql {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES(1);
+ INSERT INTO t4 VALUES(2);
+ INSERT INTO t4 SELECT 2+x FROM t4;
+ }
+ catchsql {
+ SELECT group_concat(hex(randomblob(20000))) FROM t4;
+ }
+} {1 {string or blob too big}}
+db eval {DROP TABLE t4}
+
+sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH 0x7fffffff
+set strvalue [string repeat A $::SQLITE_LIMIT_LENGTH]
+do_test sqllimits1-5.16 {
+ catchsql "SELECT '$strvalue'"
+} [list 0 $strvalue]
+do_test sqllimits1-5.17.1 {
+ catchsql "SELECT 'A$strvalue'"
+} [list 1 {string or blob too big}]
+do_test sqllimits1-5.17.2 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH 0x7fffffff
+ catchsql {SELECT 'A' || $::strvalue}
+} [list 0 A$strvalue]
+do_test sqllimits1-5.17.3 {
+ sqlite3_limit db SQLITE_LIMIT_LENGTH $SQLITE_LIMIT_LENGTH
+ catchsql {SELECT 'A' || $::strvalue}
+} [list 1 {string or blob too big}]
+set blobvalue [string repeat 41 $::SQLITE_LIMIT_LENGTH]
+do_test sqllimits1-5.18 {
+ catchsql "SELECT x'$blobvalue'"
+} [list 0 $strvalue]
+do_test sqllimits1-5.19 {
+ catchsql "SELECT '41$blobvalue'"
+} [list 1 {string or blob too big}]
+unset blobvalue
+
+ifcapable datetime {
+ set strvalue [string repeat D [expr {$SQLITE_LIMIT_LENGTH-12}]]
+ do_test sqllimits1-5.20 {
+ catchsql {SELECT strftime('%Y ' || $::strvalue, '2008-01-02')}
+ } [list 0 [list "2008 $strvalue"]]
+ do_test sqllimits1-5.21 {
+ catchsql {SELECT strftime('%Y-%m-%d ' || $::strvalue, '2008-01-02')}
+ } {1 {string or blob too big}}
+}
+unset strvalue
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-6.* test that the SQLITE_MAX_SQL_LENGTH limit
+# is enforced.
+#
+do_test sqllimits1-6.1 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH 50000
+ set sql "SELECT 1 WHERE 1==1"
+ set tail " /* A comment to take up space in order to make the string\
+ longer without increasing the expression depth */\
+ AND 1 == 1"
+ set N [expr {(50000 / [string length $tail])+1}]
+ append sql [string repeat $tail $N]
+ catchsql $sql
+} {1 {String or BLOB exceeded size limit}}
+do_test sqllimits1-6.3 {
+ sqlite3_limit db SQLITE_LIMIT_SQL_LENGTH 50000
+ set sql "SELECT 1 WHERE 1==1"
+ set tail " /* A comment to take up space in order to make the string\
+ longer without increasing the expression depth */\
+ AND 1 == 1"
+ set N [expr {(50000 / [string length $tail])+1}]
+ append sql [string repeat $tail $N]
+ set nbytes [string length $sql]
+ append sql { AND 0}
+ set rc [catch {sqlite3_prepare db $sql $nbytes TAIL} STMT]
+ lappend rc $STMT
+} {1 {(18) statement too long}}
+do_test sqllimits1-6.4 {
+ sqlite3_errmsg db
+} {statement too long}
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-7.* test that the limit set using the
+# max_page_count pragma.
+#
+do_test sqllimits1-7.1 {
+ execsql {
+ PRAGMA max_page_count = 1000;
+ }
+} {1000}
+do_test sqllimits1-7.2 {
+ execsql { CREATE TABLE trig (a INTEGER, b INTEGER); }
+
+ # Set up a tree of triggers to fire when a row is inserted
+ # into table "trig".
+ #
+ # INSERT -> insert_b -> update_b -> insert_a -> update_a (chain 1)
+ # -> update_a -> insert_a -> update_b (chain 2)
+ # -> insert_a -> update_b -> insert_b -> update_a (chain 3)
+ # -> update_a -> insert_b -> update_b (chain 4)
+ #
+ # Table starts with N rows.
+ #
+ # Chain 1: insert_b (update N rows)
+ # -> update_b (insert 1 rows)
+ # -> insert_a (update N rows)
+ # -> update_a (insert 1 rows)
+ #
+ # chains 2, 3 and 4 are similar. Each inserts more than N^2 rows, where
+ # N is the number of rows at the conclusion of the previous chain.
+ #
+ # Therefore, a single insert adds (N^16 plus some) rows to the database.
+ # A really long loop...
+ #
+ execsql {
+ CREATE TRIGGER update_b BEFORE UPDATE ON trig
+ FOR EACH ROW BEGIN
+ INSERT INTO trig VALUES (65, 'update_b');
+ END;
+
+ CREATE TRIGGER update_a AFTER UPDATE ON trig
+ FOR EACH ROW BEGIN
+ INSERT INTO trig VALUES (65, 'update_a');
+ END;
+
+ CREATE TRIGGER insert_b BEFORE INSERT ON trig
+ FOR EACH ROW BEGIN
+ UPDATE trig SET a = 1;
+ END;
+
+ CREATE TRIGGER insert_a AFTER INSERT ON trig
+ FOR EACH ROW BEGIN
+ UPDATE trig SET a = 1;
+ END;
+ }
+} {}
+
+do_test sqllimits1-7.3 {
+ execsql {
+ INSERT INTO trig VALUES (1,1);
+ }
+} {}
+
+do_test sqllimits1-7.4 {
+ execsql {
+ SELECT COUNT(*) FROM trig;
+ }
+} {7}
+
+# This tries to insert so many rows it fills up the database (limited
+# to 1MB, so not that noteworthy an achievement).
+#
+do_test sqllimits1-7.5 {
+ catchsql {
+ INSERT INTO trig VALUES (1,10);
+ }
+} {1 {database or disk is full}}
+
+do_test sqllimits1-7.6 {
+ catchsql {
+ SELECT COUNT(*) FROM trig;
+ }
+} {0 7}
+
+# Now check the response of the library to opening a file larger than
+# the current max_page_count value. The response is to change the
+# internal max_page_count value to match the actual size of the file.
+if {[db eval {PRAGMA auto_vacuum}]} {
+ set fsize 1700
+} else {
+ set fsize 1691
+}
+do_test sqllimits1-7.7.1 {
+ execsql {
+ PRAGMA max_page_count = 1000000;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a||b||c, b||c||a, c||a||b FROM abc;
+ INSERT INTO abc SELECT a, b, c FROM abc;
+ INSERT INTO abc SELECT b, a, c FROM abc;
+ INSERT INTO abc SELECT c, b, a FROM abc;
+ }
+ expr [file size test.db] / 1024
+} $fsize
+do_test sqllimits1-7.7.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ PRAGMA max_page_count = 1000;
+ }
+ execsql {
+ SELECT count(*) FROM sqlite_master;
+ }
+} {6}
+do_test sqllimits1-7.7.3 {
+ execsql {
+ PRAGMA max_page_count;
+ }
+} $fsize
+do_test sqllimits1-7.7.4 {
+ execsql {
+ DROP TABLE abc;
+ }
+} {}
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-8.* test the SQLITE_MAX_COLUMN limit.
+#
+set SQLITE_LIMIT_COLUMN 200
+sqlite3_limit db SQLITE_LIMIT_COLUMN $SQLITE_LIMIT_COLUMN
+do_test sqllimits1-8.1 {
+ # Columns in a table.
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "c$i"
+ }
+ catchsql "CREATE TABLE t([join $cols ,])"
+} {1 {too many columns on t}}
+
+do_test sqllimits1-8.2 {
+ # Columns in the result-set of a SELECT.
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "sql AS sql$i"
+ }
+ catchsql "SELECT [join $cols ,] FROM sqlite_master"
+} {1 {too many columns in result set}}
+
+do_test sqllimits1-8.3 {
+ # Columns in the result-set of a sub-SELECT.
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "sql AS sql$i"
+ }
+ catchsql "SELECT sql4 FROM (SELECT [join $cols ,] FROM sqlite_master)"
+} {1 {too many columns in result set}}
+
+do_test sqllimits1-8.4 {
+ # Columns in an index.
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols c
+ }
+ set sql1 "CREATE TABLE t1(c);"
+ set sql2 "CREATE INDEX i1 ON t1([join $cols ,]);"
+ catchsql "$sql1 ; $sql2"
+} {1 {too many columns in index}}
+
+do_test sqllimits1-8.5 {
+ # Columns in a GROUP BY clause.
+ catchsql "SELECT * FROM t1 GROUP BY [join $cols ,]"
+} {1 {too many terms in GROUP BY clause}}
+
+do_test sqllimits1-8.6 {
+ # Columns in an ORDER BY clause.
+ catchsql "SELECT * FROM t1 ORDER BY [join $cols ,]"
+} {1 {too many terms in ORDER BY clause}}
+
+do_test sqllimits1-8.7 {
+ # Assignments in an UPDATE statement.
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "c = 1"
+ }
+ catchsql "UPDATE t1 SET [join $cols ,];"
+} {1 {too many columns in set list}}
+
+do_test sqllimits1-8.8 {
+ # Columns in a view definition:
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "c$i"
+ }
+ catchsql "CREATE VIEW v1 AS SELECT [join $cols ,] FROM t1;"
+} {1 {too many columns in result set}}
+
+do_test sqllimits1-8.9 {
+ # Columns in a view definition (testing * expansion):
+ set cols [list]
+ for {set i 0} {$i < $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols "c$i"
+ }
+ catchsql "CREATE TABLE t2([join $cols ,])"
+ catchsql "CREATE VIEW v1 AS SELECT *, c1 AS o FROM t2;"
+} {1 {too many columns in result set}}
+do_test sqllimits1-8.10 {
+ # ORDER BY columns
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols c
+ }
+ set sql "SELECT c FROM t1 ORDER BY [join $cols ,]"
+ catchsql $sql
+} {1 {too many terms in ORDER BY clause}}
+do_test sqllimits1-8.11 {
+ # ORDER BY columns
+ set cols [list]
+ for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
+ lappend cols [expr {$i%3 + 1}]
+ }
+ set sql "SELECT c, c+1, c+2 FROM t1 UNION SELECT c-1, c-2, c-3 FROM t1"
+ append sql " ORDER BY [join $cols ,]"
+ catchsql $sql
+} {1 {too many terms in ORDER BY clause}}
+
+
+#--------------------------------------------------------------------
+# These tests - sqllimits1-9.* - test that the SQLITE_LIMIT_EXPR_DEPTH
+# limit is enforced. The limit refers to the number of terms in
+# the expression.
+#
+if {$SQLITE_MAX_EXPR_DEPTH==0} {
+ puts -nonewline stderr "WARNING: Compile with -DSQLITE_MAX_EXPR_DEPTH to run "
+ puts stderr "tests sqllimits1-9.X"
+} else {
+ do_test sqllimits1-9.1 {
+ set max $::SQLITE_MAX_EXPR_DEPTH
+ set expr "(1 [string repeat {AND 1 } $max])"
+ catchsql [subst {
+ SELECT $expr
+ }]
+ } "1 {Expression tree is too large (maximum depth $::SQLITE_MAX_EXPR_DEPTH)}"
+
+ # Attempting to beat the expression depth limit using nested SELECT
+ # queries causes a parser stack overflow.
+ do_test sqllimits1-9.2 {
+ set max $::SQLITE_MAX_EXPR_DEPTH
+ set expr "SELECT 1"
+ for {set i 0} {$i <= $max} {incr i} {
+ set expr "SELECT ($expr)"
+ }
+ catchsql [subst { $expr }]
+ } "1 {parser stack overflow}"
+
+if 0 {
+ do_test sqllimits1-9.3 {
+ execsql {
+ PRAGMA max_page_count = 1000000; -- 1 GB
+ CREATE TABLE v0(a);
+ INSERT INTO v0 VALUES(1);
+ }
+ db transaction {
+ for {set i 1} {$i < 200} {incr i} {
+ set expr "(a [string repeat {AND 1 } 50]) AS a"
+ execsql [subst {
+ CREATE VIEW v${i} AS SELECT $expr FROM v[expr {$i-1}]
+ }]
+ }
+ }
+ } {}
+
+ do_test sqllimits1-9.4 {
+ catchsql {
+ SELECT a FROM v199
+ }
+ } "1 {Expression tree is too large (maximum depth $::SQLITE_MAX_EXPR_DEPTH)}"
+}
+}
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-10.* test that the SQLITE_MAX_VDBE_OP
+# limit works as expected. The limit refers to the number of opcodes
+# in a single VDBE program.
+#
+# TODO
+
+#--------------------------------------------------------------------
+# Test the SQLITE_LIMIT_FUNCTION_ARG limit works. Test case names
+# match the pattern "sqllimits1-11.*".
+#
+do_test sqllimits1-11.1 {
+ set max $::SQLITE_MAX_FUNCTION_ARG
+ set vals [list]
+ for {set i 0} {$i < $SQLITE_MAX_FUNCTION_ARG} {incr i} {
+ lappend vals $i
+ }
+ catchsql "SELECT max([join $vals ,])"
+} "0 [expr {$::SQLITE_MAX_FUNCTION_ARG - 1}]"
+do_test sqllimits1-11.2 {
+ set max $::SQLITE_MAX_FUNCTION_ARG
+ set vals [list]
+ for {set i 0} {$i <= $SQLITE_MAX_FUNCTION_ARG} {incr i} {
+ lappend vals $i
+ }
+ catchsql "SELECT max([join $vals ,])"
+} {1 {too many arguments on function max}}
+
+# Test that it is SQLite, and not the implementation of the
+# user function that is throwing the error.
+proc myfunc {args} {error "I don't like to be called!"}
+do_test sqllimits1-11.2 {
+ db function myfunc myfunc
+ set max $::SQLITE_MAX_FUNCTION_ARG
+ set vals [list]
+ for {set i 0} {$i <= $SQLITE_MAX_FUNCTION_ARG} {incr i} {
+ lappend vals $i
+ }
+ catchsql "SELECT myfunc([join $vals ,])"
+} {1 {too many arguments on function myfunc}}
+
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-12.*: Test the SQLITE_MAX_ATTACHED limit.
+#
+ifcapable attach {
+ do_test sqllimits1-12.1 {
+ set max $::SQLITE_MAX_ATTACHED
+ for {set i 0} {$i < ($max)} {incr i} {
+ file delete -force test${i}.db test${i}.db-journal
+ }
+ for {set i 0} {$i < ($max)} {incr i} {
+ execsql "ATTACH 'test${i}.db' AS aux${i}"
+ }
+ catchsql "ATTACH 'test${i}.db' AS aux${i}"
+ } "1 {too many attached databases - max $::SQLITE_MAX_ATTACHED}"
+ do_test sqllimits1-12.2 {
+ set max $::SQLITE_MAX_ATTACHED
+ for {set i 0} {$i < ($max)} {incr i} {
+ execsql "DETACH aux${i}"
+ }
+ } {}
+}
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-13.*: Check that the SQLITE_MAX_VARIABLE_NUMBER
+# limit works.
+#
+do_test sqllimits1-13.1 {
+ set max $::SQLITE_MAX_VARIABLE_NUMBER
+ catchsql "SELECT ?[expr {$max+1}] FROM t1"
+} "1 {variable number must be between ?1 and ?$::SQLITE_MAX_VARIABLE_NUMBER}"
+do_test sqllimits1-13.2 {
+ set max $::SQLITE_MAX_VARIABLE_NUMBER
+ set vals [list]
+ for {set i 0} {$i < ($max+3)} {incr i} {
+ lappend vals ?
+ }
+ catchsql "SELECT [join $vals ,] FROM t1"
+} "1 {too many SQL variables}"
+
+
+#--------------------------------------------------------------------
+# Test cases sqllimits1-15.* verify that the
+# SQLITE_MAX_LIKE_PATTERN_LENGTH limit is enforced. This limit only
+# applies to the built-in LIKE operator, supplying an external
+# implementation by overriding the like() scalar function bypasses
+# this limitation.
+#
+# These tests check that the limit is not incorrectly applied to
+# the left-hand-side of the LIKE operator (the string being tested
+# against the pattern).
+#
+set SQLITE_LIMIT_LIKE_PATTERN 1000
+sqlite3_limit db SQLITE_LIMIT_LIKE_PATTERN_LENGTH $SQLITE_LIMIT_LIKE_PATTERN
+do_test sqllimits1-15.1 {
+ set max $::SQLITE_LIMIT_LIKE_PATTERN
+ set ::pattern [string repeat "A%" [expr $max/2]]
+ set ::string [string repeat "A" [expr {$max*2}]]
+ execsql {
+ SELECT $::string LIKE $::pattern;
+ }
+} {1}
+do_test sqllimits1-15.2 {
+ set max $::SQLITE_LIMIT_LIKE_PATTERN
+ set ::pattern [string repeat "A%" [expr {($max/2) + 1}]]
+ set ::string [string repeat "A" [expr {$max*2}]]
+ catchsql {
+ SELECT $::string LIKE $::pattern;
+ }
+} {1 {LIKE or GLOB pattern too complex}}
+
+#--------------------------------------------------------------------
+# This test case doesn't really belong with the other limits tests.
+# It is in this file because it is taxing to run, like the limits tests.
+#
+do_test sqllimits1-16.1 {
+ set ::N [expr int(([expr pow(2,32)]/50) + 1)]
+ expr (($::N*50) & 0xffffffff)<55
+} {1}
+do_test sqllimits1-16.2 {
+ set ::format "[string repeat A 60][string repeat "%J" $::N]"
+ catchsql {
+ SELECT strftime($::format, 1);
+ }
+} {1 {string or blob too big}}
+
+
+foreach {key value} [array get saved] {
+ catch {set $key $value}
+}
+finish_test
diff --git a/third_party/sqlite/test/subquery.test b/third_party/sqlite/test/subquery.test
new file mode 100755
index 0000000..c6416fc9
--- /dev/null
+++ b/third_party/sqlite/test/subquery.test
@@ -0,0 +1,494 @@
+# 2005 January 19
+#
+# 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 testing correlated subqueries
+#
+# $Id: subquery.test,v 1.16 2008/07/10 00:32:42 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+do_test subquery-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ INSERT INTO t1 VALUES(5,6);
+ INSERT INTO t1 VALUES(7,8);
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,1);
+ INSERT INTO t2 VALUES(3,9);
+ INSERT INTO t2 VALUES(5,25);
+ INSERT INTO t2 VALUES(7,49);
+ COMMIT;
+ }
+ execsql {
+ SELECT a, (SELECT y FROM t2 WHERE x=a) FROM t1 WHERE b<8
+ }
+} {1 1 3 9 5 25}
+do_test subquery-1.2 {
+ execsql {
+ UPDATE t1 SET b=b+(SELECT y FROM t2 WHERE x=a);
+ SELECT * FROM t1;
+ }
+} {1 3 3 13 5 31 7 57}
+
+do_test subquery-1.3 {
+ execsql {
+ SELECT b FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE y=a)
+ }
+} {3}
+do_test subquery-1.4 {
+ execsql {
+ SELECT b FROM t1 WHERE NOT EXISTS(SELECT * FROM t2 WHERE y=a)
+ }
+} {13 31 57}
+
+# Simple tests to make sure correlated subqueries in WHERE clauses
+# are used by the query optimizer correctly.
+do_test subquery-1.5 {
+ execsql {
+ SELECT a, x FROM t1, t2 WHERE t1.a = (SELECT x);
+ }
+} {1 1 3 3 5 5 7 7}
+do_test subquery-1.6 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ SELECT a, x FROM t1, t2 WHERE t1.a = (SELECT x);
+ }
+} {1 1 3 3 5 5 7 7}
+do_test subquery-1.7 {
+ execsql {
+ SELECT a, x FROM t2, t1 WHERE t1.a = (SELECT x);
+ }
+} {1 1 3 3 5 5 7 7}
+
+# Try an aggregate in both the subquery and the parent query.
+do_test subquery-1.8 {
+ execsql {
+ SELECT count(*) FROM t1 WHERE a > (SELECT count(*) FROM t2);
+ }
+} {2}
+
+# Test a correlated subquery disables the "only open the index" optimization.
+do_test subquery-1.9.1 {
+ execsql {
+ SELECT (y*2)>b FROM t1, t2 WHERE a=x;
+ }
+} {0 1 1 1}
+do_test subquery-1.9.2 {
+ execsql {
+ SELECT a FROM t1 WHERE (SELECT (y*2)>b FROM t2 WHERE a=x);
+ }
+} {3 5 7}
+
+# Test that the flattening optimization works with subquery expressions.
+do_test subquery-1.10.1 {
+ execsql {
+ SELECT (SELECT a), b FROM t1;
+ }
+} {1 3 3 13 5 31 7 57}
+do_test subquery-1.10.2 {
+ execsql {
+ SELECT * FROM (SELECT (SELECT a), b FROM t1);
+ }
+} {1 3 3 13 5 31 7 57}
+do_test subquery-1.10.3 {
+ execsql {
+ SELECT * FROM (SELECT (SELECT sum(a) FROM t1));
+ }
+} {16}
+do_test subquery-1.10.4 {
+ execsql {
+ CREATE TABLE t5 (val int, period text PRIMARY KEY);
+ INSERT INTO t5 VALUES(5, '2001-3');
+ INSERT INTO t5 VALUES(10, '2001-4');
+ INSERT INTO t5 VALUES(15, '2002-1');
+ INSERT INTO t5 VALUES(5, '2002-2');
+ INSERT INTO t5 VALUES(10, '2002-3');
+ INSERT INTO t5 VALUES(15, '2002-4');
+ INSERT INTO t5 VALUES(10, '2003-1');
+ INSERT INTO t5 VALUES(5, '2003-2');
+ INSERT INTO t5 VALUES(25, '2003-3');
+ INSERT INTO t5 VALUES(5, '2003-4');
+
+ SELECT period, vsum
+ FROM (SELECT
+ a.period,
+ (select sum(val) from t5 where period between a.period and '2002-4') vsum
+ FROM t5 a where a.period between '2002-1' and '2002-4')
+ WHERE vsum < 45 ;
+ }
+} {2002-2 30 2002-3 25 2002-4 15}
+do_test subquery-1.10.5 {
+ execsql {
+ SELECT period, vsum from
+ (select a.period,
+ (select sum(val) from t5 where period between a.period and '2002-4') vsum
+ FROM t5 a where a.period between '2002-1' and '2002-4')
+ WHERE vsum < 45 ;
+ }
+} {2002-2 30 2002-3 25 2002-4 15}
+do_test subquery-1.10.6 {
+ execsql {
+ DROP TABLE t5;
+ }
+} {}
+
+
+
+#------------------------------------------------------------------
+# The following test cases - subquery-2.* - are not logically
+# organized. They're here largely because they were failing during
+# one stage of development of sub-queries.
+#
+do_test subquery-2.1 {
+ execsql {
+ SELECT (SELECT 10);
+ }
+} {10}
+do_test subquery-2.2.1 {
+ execsql {
+ CREATE TABLE t3(a PRIMARY KEY, b);
+ INSERT INTO t3 VALUES(1, 2);
+ INSERT INTO t3 VALUES(3, 1);
+ }
+} {}
+do_test subquery-2.2.2 {
+ execsql {
+ SELECT * FROM t3 WHERE a IN (SELECT b FROM t3);
+ }
+} {1 2}
+do_test subquery-2.2.3 {
+ execsql {
+ DROP TABLE t3;
+ }
+} {}
+do_test subquery-2.3.1 {
+ execsql {
+ CREATE TABLE t3(a TEXT);
+ INSERT INTO t3 VALUES('10');
+ }
+} {}
+do_test subquery-2.3.2 {
+ execsql {
+ SELECT a IN (10.0, 20) FROM t3;
+ }
+} {0}
+do_test subquery-2.3.3 {
+ execsql {
+ DROP TABLE t3;
+ }
+} {}
+do_test subquery-2.4.1 {
+ execsql {
+ CREATE TABLE t3(a TEXT);
+ INSERT INTO t3 VALUES('XX');
+ }
+} {}
+do_test subquery-2.4.2 {
+ execsql {
+ SELECT count(*) FROM t3 WHERE a IN (SELECT 'XX')
+ }
+} {1}
+do_test subquery-2.4.3 {
+ execsql {
+ DROP TABLE t3;
+ }
+} {}
+do_test subquery-2.5.1 {
+ execsql {
+ CREATE TABLE t3(a INTEGER);
+ INSERT INTO t3 VALUES(10);
+
+ CREATE TABLE t4(x TEXT);
+ INSERT INTO t4 VALUES('10.0');
+ }
+} {}
+do_test subquery-2.5.2 {
+ # In the expr "x IN (SELECT a FROM t3)" the RHS of the IN operator
+ # has text affinity and the LHS has integer affinity. The rule is
+ # that we try to convert both sides to an integer before doing the
+ # comparision. Hence, the integer value 10 in t3 will compare equal
+ # to the string value '10.0' in t4 because the t4 value will be
+ # converted into an integer.
+ execsql {
+ SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
+ }
+} {10.0}
+do_test subquery-2.5.3.1 {
+ # The t4i index cannot be used to resolve the "x IN (...)" constraint
+ # because the constraint has integer affinity but t4i has text affinity.
+ execsql {
+ CREATE INDEX t4i ON t4(x);
+ SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
+ }
+} {10.0}
+do_test subquery-2.5.3.2 {
+ # Verify that the t4i index was not used in the previous query
+ set ::sqlite_query_plan
+} {t4 {}}
+do_test subquery-2.5.4 {
+ execsql {
+ DROP TABLE t3;
+ DROP TABLE t4;
+ }
+} {}
+
+#------------------------------------------------------------------
+# The following test cases - subquery-3.* - test tickets that
+# were raised during development of correlated subqueries.
+#
+
+# Ticket 1083
+ifcapable view {
+ do_test subquery-3.1 {
+ catchsql { DROP TABLE t1; }
+ catchsql { DROP TABLE t2; }
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE VIEW v1 AS SELECT b FROM t1 WHERE a>0;
+ CREATE TABLE t2(p,q);
+ INSERT INTO t2 VALUES(2,9);
+ SELECT * FROM v1 WHERE EXISTS(SELECT * FROM t2 WHERE p=v1.b);
+ }
+ } {2}
+} else {
+ catchsql { DROP TABLE t1; }
+ catchsql { DROP TABLE t2; }
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE t2(p,q);
+ INSERT INTO t2 VALUES(2,9);
+ }
+}
+
+# Ticket 1084
+do_test subquery-3.2 {
+ catchsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ }
+ execsql {
+ SELECT (SELECT t1.a) FROM t1;
+ }
+} {1}
+
+# Test Cases subquery-3.3.* test correlated subqueries where the
+# parent query is an aggregate query. Ticket #1105 is an example
+# of such a query.
+#
+do_test subquery-3.3.1 {
+ execsql {
+ SELECT a, (SELECT b) FROM t1 GROUP BY a;
+ }
+} {1 2}
+do_test subquery-3.3.2 {
+ catchsql {DROP TABLE t2}
+ execsql {
+ CREATE TABLE t2(c, d);
+ INSERT INTO t2 VALUES(1, 'one');
+ INSERT INTO t2 VALUES(2, 'two');
+ SELECT a, (SELECT d FROM t2 WHERE a=c) FROM t1 GROUP BY a;
+ }
+} {1 one}
+do_test subquery-3.3.3 {
+ execsql {
+ INSERT INTO t1 VALUES(2, 4);
+ SELECT max(a), (SELECT d FROM t2 WHERE a=c) FROM t1;
+ }
+} {2 two}
+do_test subquery-3.3.4 {
+ execsql {
+ SELECT a, (SELECT (SELECT d FROM t2 WHERE a=c)) FROM t1 GROUP BY a;
+ }
+} {1 one 2 two}
+do_test subquery-3.3.5 {
+ execsql {
+ SELECT a, (SELECT count(*) FROM t2 WHERE a=c) FROM t1;
+ }
+} {1 1 2 1}
+
+#------------------------------------------------------------------
+# These tests - subquery-4.* - use the TCL statement cache to try
+# and expose bugs to do with re-using statements that have been
+# passed to sqlite3_reset().
+#
+# One problem was that VDBE memory cells were not being initialised
+# to NULL on the second and subsequent executions.
+#
+do_test subquery-4.1.1 {
+ execsql {
+ SELECT (SELECT a FROM t1);
+ }
+} {1}
+do_test subquery-4.2 {
+ execsql {
+ DELETE FROM t1;
+ SELECT (SELECT a FROM t1);
+ }
+} {{}}
+do_test subquery-4.2.1 {
+ execsql {
+ CREATE TABLE t3(a PRIMARY KEY);
+ INSERT INTO t3 VALUES(10);
+ }
+ execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
+} {}
+do_test subquery-4.2.2 {
+ execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
+} {}
+
+#------------------------------------------------------------------
+# The subquery-5.* tests make sure string literals in double-quotes
+# are handled efficiently. Double-quote literals are first checked
+# to see if they match any column names. If there is not column name
+# match then those literals are used a string constants. When a
+# double-quoted string appears, we want to make sure that the search
+# for a matching column name did not cause an otherwise static subquery
+# to become a dynamic (correlated) subquery.
+#
+do_test subquery-5.1 {
+ proc callcntproc {n} {
+ incr ::callcnt
+ return $n
+ }
+ set callcnt 0
+ db function callcnt callcntproc
+ execsql {
+ CREATE TABLE t4(x,y);
+ INSERT INTO t4 VALUES('one',1);
+ INSERT INTO t4 VALUES('two',2);
+ INSERT INTO t4 VALUES('three',3);
+ INSERT INTO t4 VALUES('four',4);
+ CREATE TABLE t5(a,b);
+ INSERT INTO t5 VALUES(1,11);
+ INSERT INTO t5 VALUES(2,22);
+ INSERT INTO t5 VALUES(3,33);
+ INSERT INTO t5 VALUES(4,44);
+ SELECT b FROM t5 WHERE a IN
+ (SELECT callcnt(y)+0 FROM t4 WHERE x="two")
+ }
+} {22}
+do_test subquery-5.2 {
+ # This is the key test. The subquery should have only run once. If
+ # The double-quoted identifier "two" were causing the subquery to be
+ # processed as a correlated subquery, then it would have run 4 times.
+ set callcnt
+} {1}
+
+
+# Ticket #1380. Make sure correlated subqueries on an IN clause work
+# correctly when the left-hand side of the IN operator is constant.
+#
+do_test subquery-6.1 {
+ set callcnt 0
+ execsql {
+ SELECT x FROM t4 WHERE 1 IN (SELECT callcnt(count(*)) FROM t5 WHERE a=y)
+ }
+} {one two three four}
+do_test subquery-6.2 {
+ set callcnt
+} {4}
+do_test subquery-6.3 {
+ set callcnt 0
+ execsql {
+ SELECT x FROM t4 WHERE 1 IN (SELECT callcnt(count(*)) FROM t5 WHERE a=1)
+ }
+} {one two three four}
+do_test subquery-6.4 {
+ set callcnt
+} {1}
+
+if 0 { ############# disable until we get #2652 fixed
+# Ticket #2652. Allow aggregate functions of outer queries inside
+# a non-aggregate subquery.
+#
+do_test subquery-7.1 {
+ execsql {
+ CREATE TABLE t7(c7);
+ INSERT INTO t7 VALUES(1);
+ INSERT INTO t7 VALUES(2);
+ INSERT INTO t7 VALUES(3);
+ CREATE TABLE t8(c8);
+ INSERT INTO t8 VALUES(100);
+ INSERT INTO t8 VALUES(200);
+ INSERT INTO t8 VALUES(300);
+ CREATE TABLE t9(c9);
+ INSERT INTO t9 VALUES(10000);
+ INSERT INTO t9 VALUES(20000);
+ INSERT INTO t9 VALUES(30000);
+
+ SELECT (SELECT c7+c8 FROM t7) FROM t8;
+ }
+} {101 201 301}
+do_test subquery-7.2 {
+ execsql {
+ SELECT (SELECT max(c7)+c8 FROM t7) FROM t8;
+ }
+} {103 203 303}
+do_test subquery-7.3 {
+ execsql {
+ SELECT (SELECT c7+max(c8) FROM t8) FROM t7
+ }
+} {301}
+do_test subquery-7.4 {
+ execsql {
+ SELECT (SELECT max(c7)+max(c8) FROM t8) FROM t7
+ }
+} {303}
+do_test subquery-7.5 {
+ execsql {
+ SELECT (SELECT c8 FROM t8 WHERE rowid=max(c7)) FROM t7
+ }
+} {300}
+do_test subquery-7.6 {
+ execsql {
+ SELECT (SELECT (SELECT max(c7+c8+c9) FROM t9) FROM t8) FROM t7
+ }
+} {30101 30102 30103}
+do_test subquery-7.7 {
+ execsql {
+ SELECT (SELECT (SELECT c7+max(c8+c9) FROM t9) FROM t8) FROM t7
+ }
+} {30101 30102 30103}
+do_test subquery-7.8 {
+ execsql {
+ SELECT (SELECT (SELECT max(c7)+c8+c9 FROM t9) FROM t8) FROM t7
+ }
+} {10103}
+do_test subquery-7.9 {
+ execsql {
+ SELECT (SELECT (SELECT c7+max(c8)+c9 FROM t9) FROM t8) FROM t7
+ }
+} {10301 10302 10303}
+do_test subquery-7.10 {
+ execsql {
+ SELECT (SELECT (SELECT c7+c8+max(c9) FROM t9) FROM t8) FROM t7
+ }
+} {30101 30102 30103}
+do_test subquery-7.11 {
+ execsql {
+ SELECT (SELECT (SELECT max(c7)+max(c8)+max(c9) FROM t9) FROM t8) FROM t7
+ }
+} {30303}
+} ;############# Disabled
+
+finish_test
diff --git a/third_party/sqlite/test/subselect.test b/third_party/sqlite/test/subselect.test
new file mode 100755
index 0000000..247f68e
--- /dev/null
+++ b/third_party/sqlite/test/subselect.test
@@ -0,0 +1,210 @@
+# 2001 September 15
+#
+# 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 file is testing SELECT statements that are part of
+# expressions.
+#
+# $Id: subselect.test,v 1.16 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Omit this whole file if the library is build without subquery support.
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+# Basic sanity checking. Try a simple subselect.
+#
+do_test subselect-1.1 {
+ execsql {
+ CREATE TABLE t1(a int, b int);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ INSERT INTO t1 VALUES(5,6);
+ }
+ execsql {SELECT * FROM t1 WHERE a = (SELECT count(*) FROM t1)}
+} {3 4}
+
+# Try a select with more than one result column.
+#
+do_test subselect-1.2 {
+ set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
+ lappend v $msg
+} {1 {only a single result allowed for a SELECT that is part of an expression}}
+
+# A subselect without an aggregate.
+#
+do_test subselect-1.3a {
+ execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=2)}
+} {2}
+do_test subselect-1.3b {
+ execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=4)}
+} {4}
+do_test subselect-1.3c {
+ execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=6)}
+} {6}
+do_test subselect-1.3d {
+ execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=8)}
+} {}
+ifcapable compound {
+ do_test subselect-1.3e {
+ execsql {
+ SELECT b FROM t1
+ WHERE a = (SELECT a FROM t1 UNION SELECT b FROM t1 ORDER BY 1);
+ }
+ } {2}
+}
+
+# What if the subselect doesn't return any value. We should get
+# NULL as the result. Check it out.
+#
+do_test subselect-1.4 {
+ execsql {SELECT b from t1 where a = coalesce((SELECT a FROM t1 WHERE b=5),1)}
+} {2}
+
+# Try multiple subselects within a single expression.
+#
+do_test subselect-1.5 {
+ execsql {
+ CREATE TABLE t2(x int, y int);
+ INSERT INTO t2 VALUES(1,2);
+ INSERT INTO t2 VALUES(2,4);
+ INSERT INTO t2 VALUES(3,8);
+ INSERT INTO t2 VALUES(4,16);
+ }
+ execsql {
+ SELECT y from t2
+ WHERE x = (SELECT sum(b) FROM t1 where a notnull) - (SELECT sum(a) FROM t1)
+ }
+} {8}
+
+# Try something useful. Delete every entry from t2 where the
+# x value is less than half of the maximum.
+#
+do_test subselect-1.6 {
+ execsql {DELETE FROM t2 WHERE x < 0.5*(SELECT max(x) FROM t2)}
+ execsql {SELECT x FROM t2 ORDER BY x}
+} {2 3 4}
+
+# Make sure sorting works for SELECTs there used as a scalar expression.
+#
+do_test subselect-2.1 {
+ execsql {
+ SELECT (SELECT a FROM t1 ORDER BY a), (SELECT a FROM t1 ORDER BY a DESC)
+ }
+} {1 5}
+do_test subselect-2.2 {
+ execsql {
+ SELECT 1 IN (SELECT a FROM t1 ORDER BY a);
+ }
+} {1}
+do_test subselect-2.3 {
+ execsql {
+ SELECT 2 IN (SELECT a FROM t1 ORDER BY a DESC);
+ }
+} {0}
+
+# Verify that the ORDER BY clause is honored in a subquery.
+#
+ifcapable compound {
+do_test subselect-3.1 {
+ execsql {
+ CREATE TABLE t3(x int);
+ INSERT INTO t3 SELECT a FROM t1 UNION ALL SELECT b FROM t1;
+ SELECT * FROM t3 ORDER BY x;
+ }
+} {1 2 3 4 5 6}
+} ;# ifcapable compound
+ifcapable !compound {
+do_test subselect-3.1 {
+ execsql {
+ CREATE TABLE t3(x int);
+ INSERT INTO t3 SELECT a FROM t1;
+ INSERT INTO t3 SELECT b FROM t1;
+ SELECT * FROM t3 ORDER BY x;
+ }
+} {1 2 3 4 5 6}
+} ;# ifcapable !compound
+
+do_test subselect-3.2 {
+ execsql {
+ SELECT sum(x) FROM (SELECT x FROM t3 ORDER BY x LIMIT 2);
+ }
+} {3}
+do_test subselect-3.3 {
+ execsql {
+ SELECT sum(x) FROM (SELECT x FROM t3 ORDER BY x DESC LIMIT 2);
+ }
+} {11}
+do_test subselect-3.4 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x);
+ }
+} {1}
+do_test subselect-3.5 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x DESC);
+ }
+} {6}
+do_test subselect-3.6 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x LIMIT 1);
+ }
+} {1}
+do_test subselect-3.7 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x DESC LIMIT 1);
+ }
+} {6}
+do_test subselect-3.8 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x LIMIT 1 OFFSET 2);
+ }
+} {3}
+do_test subselect-3.9 {
+ execsql {
+ SELECT (SELECT x FROM t3 ORDER BY x DESC LIMIT 1 OFFSET 2);
+ }
+} {4}
+do_test subselect-3.10 {
+ execsql {
+ SELECT x FROM t3 WHERE x IN
+ (SELECT x FROM t3 ORDER BY x DESC LIMIT 1 OFFSET 2);
+ }
+} {4}
+
+# Ticket #2295.
+# Make sure type affinities work correctly on subqueries with
+# an ORDER BY clause.
+#
+do_test subselect-4.1 {
+ execsql {
+ CREATE TABLE t4(a TEXT, b TEXT);
+ INSERT INTO t4 VALUES('a','1');
+ INSERT INTO t4 VALUES('b','2');
+ INSERT INTO t4 VALUES('c','3');
+ SELECT a FROM t4 WHERE b IN (SELECT b FROM t4 ORDER BY b);
+ }
+} {a b c}
+do_test subselect-4.2 {
+ execsql {
+ SELECT a FROM t4 WHERE b IN (SELECT b FROM t4 ORDER BY b LIMIT 1);
+ }
+} {a}
+do_test subselect-4.3 {
+ execsql {
+ SELECT a FROM t4 WHERE b IN (SELECT b FROM t4 ORDER BY b DESC LIMIT 1);
+ }
+} {c}
+
+finish_test
diff --git a/third_party/sqlite/test/substr.test b/third_party/sqlite/test/substr.test
new file mode 100755
index 0000000..055b8fd
--- /dev/null
+++ b/third_party/sqlite/test/substr.test
@@ -0,0 +1,130 @@
+# 2007 May 14
+#
+# 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 file is testing the built-in SUBSTR() functions.
+#
+# $Id: substr.test,v 1.3 2007/10/12 19:11:55 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tclvar {
+ finish_test
+ return
+}
+
+# Create a table to work with.
+#
+execsql {
+ CREATE TABLE t1(t text, b blob)
+}
+proc substr-test {id string i1 i2 result} {
+ db eval {
+ DELETE FROM t1;
+ INSERT INTO t1(t) VALUES($string)
+ }
+ do_test substr-$id.1 [subst {
+ execsql {
+ SELECT substr(t, $i1, $i2) FROM t1
+ }
+ }] [list $result]
+ set qstr '[string map {' ''} $string]'
+ do_test substr-$id.2 [subst {
+ execsql {
+ SELECT substr($qstr, $i1, $i2)
+ }
+ }] [list $result]
+}
+proc subblob-test {id hex i1 i2 hexresult} {
+ db eval "
+ DELETE FROM t1;
+ INSERT INTO t1(b) VALUES(x'$hex')
+ "
+ do_test substr-$id.1 [subst {
+ execsql {
+ SELECT hex(substr(b, $i1, $i2)) FROM t1
+ }
+ }] [list $hexresult]
+ do_test substr-$id.2 [subst {
+ execsql {
+ SELECT hex(substr(x'$hex', $i1, $i2))
+ }
+ }] [list $hexresult]
+}
+
+# Basic SUBSTR functionality
+#
+substr-test 1.1 abcdefg 1 1 a
+substr-test 1.2 abcdefg 2 1 b
+substr-test 1.3 abcdefg 1 2 ab
+substr-test 1.4 abcdefg 1 100 abcdefg
+substr-test 1.5 abcdefg 0 1 a
+substr-test 1.6 abcdefg -1 1 g
+substr-test 1.7 abcdefg -1 10 g
+substr-test 1.8 abcdefg -5 3 cde
+substr-test 1.9 abcdefg -7 3 abc
+substr-test 1.10 abcdefg -100 98 abcde
+
+# Make sure everything works with long unicode characters
+#
+substr-test 2.1 \u1234\u2345\u3456 1 1 \u1234
+substr-test 2.2 \u1234\u2345\u3456 2 1 \u2345
+substr-test 2.3 \u1234\u2345\u3456 1 2 \u1234\u2345
+substr-test 2.4 \u1234\u2345\u3456 -1 1 \u3456
+substr-test 2.5 a\u1234b\u2345c\u3456c -5 3 b\u2345c
+
+# Basic functionality for BLOBs
+#
+subblob-test 3.1 61626364656667 1 1 61
+subblob-test 3.2 61626364656667 2 1 62
+subblob-test 3.3 61626364656667 1 2 6162
+subblob-test 3.4 61626364656667 1 100 61626364656667
+subblob-test 3.5 61626364656667 0 1 61
+subblob-test 3.6 61626364656667 -1 1 67
+subblob-test 3.7 61626364656667 -1 10 67
+subblob-test 3.8 61626364656667 -5 3 636465
+subblob-test 3.9 61626364656667 -7 3 616263
+subblob-test 3.10 61626364656667 -100 98 6162636465
+
+# If these blobs were strings, then they would contain multi-byte
+# characters. But since they are blobs, the substr indices refer
+# to bytes.
+#
+subblob-test 4.1 61E188B462E28D8563E3919663 1 1 61
+subblob-test 4.2 61E188B462E28D8563E3919663 2 1 E1
+subblob-test 4.3 61E188B462E28D8563E3919663 1 2 61E1
+subblob-test 4.4 61E188B462E28D8563E3919663 -2 1 96
+subblob-test 4.5 61E188B462E28D8563E3919663 -5 4 63E39196
+subblob-test 4.6 61E188B462E28D8563E3919663 -100 98 61E188B462E28D8563E391
+
+# Two-argument SUBSTR
+#
+proc substr-2-test {id string idx result} {
+ db eval {
+ DELETE FROM t1;
+ INSERT INTO t1(t) VALUES($string)
+ }
+ do_test substr-$id.1 [subst {
+ execsql {
+ SELECT substr(t, $idx) FROM t1
+ }
+ }] [list $result]
+ set qstr '[string map {' ''} $string]'
+ do_test substr-$id.2 [subst {
+ execsql {
+ SELECT substr($qstr, $idx)
+ }
+ }] [list $result]
+}
+substr-2-test 5.1 abcdefghijklmnop 5 efghijklmnop
+substr-2-test 5.2 abcdef -5 bcdef
+
+finish_test
diff --git a/third_party/sqlite/test/sync.test b/third_party/sqlite/test/sync.test
new file mode 100755
index 0000000..98aa773
--- /dev/null
+++ b/third_party/sqlite/test/sync.test
@@ -0,0 +1,98 @@
+# 2005 August 28
+#
+# 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.
+#
+# This file implements tests to verify that fsync is disabled when
+# pragma synchronous=off even for multi-database commits.
+#
+# $Id: sync.test,v 1.6 2007/10/09 08:29:33 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+#
+# These tests are only applicable on unix when pager pragma are
+# enabled. Also, since every test uses an ATTACHed database, they
+# are only run when ATTACH is enabled.
+#
+if {$::tcl_platform(platform)!="unix"} {
+ finish_test
+ return
+}
+ifcapable !pager_pragmas||!attach {
+ finish_test
+ return
+}
+
+do_test sync-1.1 {
+ set sqlite_sync_count 0
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ PRAGMA fullfsync=OFF;
+ CREATE TABLE t1(a,b);
+ ATTACH DATABASE 'test2.db' AS db2;
+ CREATE TABLE db2.t2(x,y);
+ }
+ ifcapable !dirsync {
+ incr sqlite_sync_count 2
+ }
+ set sqlite_sync_count
+} 8
+ifcapable pager_pragmas {
+ do_test sync-1.2 {
+ set sqlite_sync_count 0
+ execsql {
+ PRAGMA main.synchronous=on;
+ PRAGMA db2.synchronous=on;
+ BEGIN;
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t2 VALUES(3,4);
+ COMMIT;
+ }
+ ifcapable !dirsync {
+ incr sqlite_sync_count 3
+ }
+ set sqlite_sync_count
+ } 8
+}
+do_test sync-1.3 {
+ set sqlite_sync_count 0
+ execsql {
+ PRAGMA main.synchronous=full;
+ PRAGMA db2.synchronous=full;
+ BEGIN;
+ INSERT INTO t1 VALUES(3,4);
+ INSERT INTO t2 VALUES(5,6);
+ COMMIT;
+ }
+ ifcapable !dirsync {
+ incr sqlite_sync_count 3
+ }
+ set sqlite_sync_count
+} 10
+ifcapable pager_pragmas {
+ do_test sync-1.4 {
+ set sqlite_sync_count 0
+ execsql {
+ PRAGMA main.synchronous=off;
+ PRAGMA db2.synchronous=off;
+ BEGIN;
+ INSERT INTO t1 VALUES(5,6);
+ INSERT INTO t2 VALUES(7,8);
+ COMMIT;
+ }
+ set sqlite_sync_count
+ } 0
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/table.test b/third_party/sqlite/test/table.test
new file mode 100755
index 0000000..02fc909
--- /dev/null
+++ b/third_party/sqlite/test/table.test
@@ -0,0 +1,676 @@
+# 2001 September 15
+#
+# 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 file is testing the CREATE TABLE statement.
+#
+# $Id: table.test,v 1.48 2007/10/09 08:29:33 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a basic table and verify it is added to sqlite_master
+#
+do_test table-1.1 {
+ execsql {
+ CREATE TABLE test1 (
+ one varchar(10),
+ two text
+ )
+ }
+ execsql {
+ SELECT sql FROM sqlite_master WHERE type!='meta'
+ }
+} {{CREATE TABLE test1 (
+ one varchar(10),
+ two text
+ )}}
+
+
+# Verify the other fields of the sqlite_master file.
+#
+do_test table-1.3 {
+ execsql {SELECT name, tbl_name, type FROM sqlite_master WHERE type!='meta'}
+} {test1 test1 table}
+
+# Close and reopen the database. Verify that everything is
+# still the same.
+#
+do_test table-1.4 {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT name, tbl_name, type from sqlite_master WHERE type!='meta'}
+} {test1 test1 table}
+
+# Drop the database and make sure it disappears.
+#
+do_test table-1.5 {
+ execsql {DROP TABLE test1}
+ execsql {SELECT * FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Close and reopen the database. Verify that the table is
+# still gone.
+#
+do_test table-1.6 {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Repeat the above steps, but this time quote the table name.
+#
+do_test table-1.10 {
+ execsql {CREATE TABLE "create" (f1 int)}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {create}
+do_test table-1.11 {
+ execsql {DROP TABLE "create"}
+ execsql {SELECT name FROM "sqlite_master" WHERE type!='meta'}
+} {}
+do_test table-1.12 {
+ execsql {CREATE TABLE test1("f1 ho" int)}
+ execsql {SELECT name as "X" FROM sqlite_master WHERE type!='meta'}
+} {test1}
+do_test table-1.13 {
+ execsql {DROP TABLE "TEST1"}
+ execsql {SELECT name FROM "sqlite_master" WHERE type!='meta'}
+} {}
+
+
+
+# Verify that we cannot make two tables with the same name
+#
+do_test table-2.1 {
+ execsql {CREATE TABLE TEST2(one text)}
+ catchsql {CREATE TABLE test2(two text default 'hi')}
+} {1 {table test2 already exists}}
+do_test table-2.1.1 {
+ catchsql {CREATE TABLE "test2" (two)}
+} {1 {table "test2" already exists}}
+do_test table-2.1b {
+ set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
+ lappend v $msg
+} {1 {object name reserved for internal use: sqlite_master}}
+do_test table-2.1c {
+ db close
+ sqlite3 db test.db
+ set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
+ lappend v $msg
+} {1 {object name reserved for internal use: sqlite_master}}
+do_test table-2.1d {
+ catchsql {CREATE TABLE IF NOT EXISTS test2(x,y)}
+} {0 {}}
+do_test table-2.1e {
+ catchsql {CREATE TABLE IF NOT EXISTS test2(x UNIQUE, y TEXT PRIMARY KEY)}
+} {0 {}}
+do_test table-2.1f {
+ execsql {DROP TABLE test2; SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Verify that we cannot make a table with the same name as an index
+#
+do_test table-2.2a {
+ execsql {CREATE TABLE test2(one text); CREATE INDEX test3 ON test2(one)}
+ set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
+ lappend v $msg
+} {1 {there is already an index named test3}}
+do_test table-2.2b {
+ db close
+ sqlite3 db test.db
+ set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
+ lappend v $msg
+} {1 {there is already an index named test3}}
+do_test table-2.2c {
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {test2 test3}
+do_test table-2.2d {
+ execsql {DROP INDEX test3}
+ set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
+ lappend v $msg
+} {0 {}}
+do_test table-2.2e {
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {test2 test3}
+do_test table-2.2f {
+ execsql {DROP TABLE test2; DROP TABLE test3}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {}
+
+# Create a table with many field names
+#
+set big_table \
+{CREATE TABLE big(
+ f1 varchar(20),
+ f2 char(10),
+ f3 varchar(30) primary key,
+ f4 text,
+ f5 text,
+ f6 text,
+ f7 text,
+ f8 text,
+ f9 text,
+ f10 text,
+ f11 text,
+ f12 text,
+ f13 text,
+ f14 text,
+ f15 text,
+ f16 text,
+ f17 text,
+ f18 text,
+ f19 text,
+ f20 text
+)}
+do_test table-3.1 {
+ execsql $big_table
+ execsql {SELECT sql FROM sqlite_master WHERE type=='table'}
+} \{$big_table\}
+do_test table-3.2 {
+ set v [catch {execsql {CREATE TABLE BIG(xyz foo)}} msg]
+ lappend v $msg
+} {1 {table BIG already exists}}
+do_test table-3.3 {
+ set v [catch {execsql {CREATE TABLE biG(xyz foo)}} msg]
+ lappend v $msg
+} {1 {table biG already exists}}
+do_test table-3.4 {
+ set v [catch {execsql {CREATE TABLE bIg(xyz foo)}} msg]
+ lappend v $msg
+} {1 {table bIg already exists}}
+do_test table-3.5 {
+ db close
+ sqlite3 db test.db
+ set v [catch {execsql {CREATE TABLE Big(xyz foo)}} msg]
+ lappend v $msg
+} {1 {table Big already exists}}
+do_test table-3.6 {
+ execsql {DROP TABLE big}
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Try creating large numbers of tables
+#
+set r {}
+for {set i 1} {$i<=100} {incr i} {
+ lappend r [format test%03d $i]
+}
+do_test table-4.1 {
+ for {set i 1} {$i<=100} {incr i} {
+ set sql "CREATE TABLE [format test%03d $i] ("
+ for {set k 1} {$k<$i} {incr k} {
+ append sql "field$k text,"
+ }
+ append sql "last_field text)"
+ execsql $sql
+ }
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} $r
+do_test table-4.1b {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} $r
+
+# Drop the even numbered tables
+#
+set r {}
+for {set i 1} {$i<=100} {incr i 2} {
+ lappend r [format test%03d $i]
+}
+do_test table-4.2 {
+ for {set i 2} {$i<=100} {incr i 2} {
+ # if {$i==38} {execsql {pragma vdbe_trace=on}}
+ set sql "DROP TABLE [format TEST%03d $i]"
+ execsql $sql
+ }
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} $r
+#exit
+
+# Drop the odd number tables
+#
+do_test table-4.3 {
+ for {set i 1} {$i<=100} {incr i 2} {
+ set sql "DROP TABLE [format test%03d $i]"
+ execsql $sql
+ }
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
+} {}
+
+# Try to drop a table that does not exist
+#
+do_test table-5.1.1 {
+ catchsql {DROP TABLE test009}
+} {1 {no such table: test009}}
+do_test table-5.1.2 {
+ catchsql {DROP TABLE IF EXISTS test009}
+} {0 {}}
+
+# Try to drop sqlite_master
+#
+do_test table-5.2 {
+ catchsql {DROP TABLE IF EXISTS sqlite_master}
+} {1 {table sqlite_master may not be dropped}}
+
+# Make sure an EXPLAIN does not really create a new table
+#
+do_test table-5.3 {
+ ifcapable {explain} {
+ execsql {EXPLAIN CREATE TABLE test1(f1 int)}
+ }
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {}
+
+# Make sure an EXPLAIN does not really drop an existing table
+#
+do_test table-5.4 {
+ execsql {CREATE TABLE test1(f1 int)}
+ ifcapable {explain} {
+ execsql {EXPLAIN DROP TABLE test1}
+ }
+ execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
+} {test1}
+
+# Create a table with a goofy name
+#
+#do_test table-6.1 {
+# execsql {CREATE TABLE 'Spaces In This Name!'(x int)}
+# execsql {INSERT INTO 'spaces in this name!' VALUES(1)}
+# set list [glob -nocomplain testdb/spaces*.tbl]
+#} {testdb/spaces+in+this+name+.tbl}
+
+# Try using keywords as table names or column names.
+#
+do_test table-7.1 {
+ set v [catch {execsql {
+ CREATE TABLE weird(
+ desc text,
+ asc text,
+ key int,
+ [14_vac] boolean,
+ fuzzy_dog_12 varchar(10),
+ begin blob,
+ end clob
+ )
+ }} msg]
+ lappend v $msg
+} {0 {}}
+do_test table-7.2 {
+ execsql {
+ INSERT INTO weird VALUES('a','b',9,0,'xyz','hi','y''all');
+ SELECT * FROM weird;
+ }
+} {a b 9 0 xyz hi y'all}
+do_test table-7.3 {
+ execsql2 {
+ SELECT * FROM weird;
+ }
+} {desc a asc b key 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all}
+
+# Try out the CREATE TABLE AS syntax
+#
+do_test table-8.1 {
+ execsql2 {
+ CREATE TABLE t2 AS SELECT * FROM weird;
+ SELECT * FROM t2;
+ }
+} {desc a asc b key 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all}
+do_test table-8.1.1 {
+ execsql {
+ SELECT sql FROM sqlite_master WHERE name='t2';
+ }
+} {{CREATE TABLE t2(
+ "desc" text,
+ "asc" text,
+ "key" int,
+ "14_vac" boolean,
+ fuzzy_dog_12 varchar(10),
+ "begin" blob,
+ "end" clob
+)}}
+do_test table-8.2 {
+ execsql {
+ CREATE TABLE "t3""xyz"(a,b,c);
+ INSERT INTO [t3"xyz] VALUES(1,2,3);
+ SELECT * FROM [t3"xyz];
+ }
+} {1 2 3}
+do_test table-8.3 {
+ execsql2 {
+ CREATE TABLE [t4"abc] AS SELECT count(*) as cnt, max(b+c) FROM [t3"xyz];
+ SELECT * FROM [t4"abc];
+ }
+} {cnt 1 max(b+c) 5}
+
+# Update for v3: The declaration type of anything except a column is now a
+# NULL pointer, so the created table has no column types. (Changed result
+# from {{CREATE TABLE 't4"abc'(cnt NUMERIC,"max(b+c)" NUMERIC)}}).
+do_test table-8.3.1 {
+ execsql {
+ SELECT sql FROM sqlite_master WHERE name='t4"abc'
+ }
+} {{CREATE TABLE "t4""abc"(cnt,"max(b+c)")}}
+
+ifcapable tempdb {
+ do_test table-8.4 {
+ execsql2 {
+ CREATE TEMPORARY TABLE t5 AS SELECT count(*) AS [y'all] FROM [t3"xyz];
+ SELECT * FROM t5;
+ }
+ } {y'all 1}
+}
+
+do_test table-8.5 {
+ db close
+ sqlite3 db test.db
+ execsql2 {
+ SELECT * FROM [t4"abc];
+ }
+} {cnt 1 max(b+c) 5}
+do_test table-8.6 {
+ execsql2 {
+ SELECT * FROM t2;
+ }
+} {desc a asc b key 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all}
+do_test table-8.7 {
+ catchsql {
+ SELECT * FROM t5;
+ }
+} {1 {no such table: t5}}
+do_test table-8.8 {
+ catchsql {
+ CREATE TABLE t5 AS SELECT * FROM no_such_table;
+ }
+} {1 {no such table: no_such_table}}
+
+# Make sure we cannot have duplicate column names within a table.
+#
+do_test table-9.1 {
+ catchsql {
+ CREATE TABLE t6(a,b,a);
+ }
+} {1 {duplicate column name: a}}
+do_test table-9.2 {
+ catchsql {
+ CREATE TABLE t6(a varchar(100), b blob, a integer);
+ }
+} {1 {duplicate column name: a}}
+
+# Check the foreign key syntax.
+#
+ifcapable {foreignkey} {
+do_test table-10.1 {
+ catchsql {
+ CREATE TABLE t6(a REFERENCES t4(a) NOT NULL);
+ INSERT INTO t6 VALUES(NULL);
+ }
+} {1 {t6.a may not be NULL}}
+do_test table-10.2 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a REFERENCES t4(a) MATCH PARTIAL);
+ }
+} {0 {}}
+do_test table-10.3 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a REFERENCES t4 MATCH FULL ON DELETE SET NULL NOT NULL);
+ }
+} {0 {}}
+do_test table-10.4 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a REFERENCES t4 MATCH FULL ON UPDATE SET DEFAULT DEFAULT 1);
+ }
+} {0 {}}
+do_test table-10.5 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a NOT NULL NOT DEFERRABLE INITIALLY IMMEDIATE);
+ }
+} {0 {}}
+do_test table-10.6 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a NOT NULL DEFERRABLE INITIALLY DEFERRED);
+ }
+} {0 {}}
+do_test table-10.7 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a,
+ FOREIGN KEY (a) REFERENCES t4(b) DEFERRABLE INITIALLY DEFERRED
+ );
+ }
+} {0 {}}
+do_test table-10.8 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a,b,c,
+ FOREIGN KEY (b,c) REFERENCES t4(x,y) MATCH PARTIAL
+ ON UPDATE SET NULL ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
+ );
+ }
+} {0 {}}
+do_test table-10.9 {
+ catchsql {
+ DROP TABLE t6;
+ CREATE TABLE t6(a,b,c,
+ FOREIGN KEY (b,c) REFERENCES t4(x)
+ );
+ }
+} {1 {number of columns in foreign key does not match the number of columns in the referenced table}}
+do_test table-10.10 {
+ catchsql {DROP TABLE t6}
+ catchsql {
+ CREATE TABLE t6(a,b,c,
+ FOREIGN KEY (b,c) REFERENCES t4(x,y,z)
+ );
+ }
+} {1 {number of columns in foreign key does not match the number of columns in the referenced table}}
+do_test table-10.11 {
+ catchsql {DROP TABLE t6}
+ catchsql {
+ CREATE TABLE t6(a,b, c REFERENCES t4(x,y));
+ }
+} {1 {foreign key on c should reference only one column of table t4}}
+do_test table-10.12 {
+ catchsql {DROP TABLE t6}
+ catchsql {
+ CREATE TABLE t6(a,b,c,
+ FOREIGN KEY (b,x) REFERENCES t4(x,y)
+ );
+ }
+} {1 {unknown column "x" in foreign key definition}}
+do_test table-10.13 {
+ catchsql {DROP TABLE t6}
+ catchsql {
+ CREATE TABLE t6(a,b,c,
+ FOREIGN KEY (x,b) REFERENCES t4(x,y)
+ );
+ }
+} {1 {unknown column "x" in foreign key definition}}
+} ;# endif foreignkey
+
+# Test for the "typeof" function. More tests for the
+# typeof() function are found in bind.test and types.test.
+#
+do_test table-11.1 {
+ execsql {
+ CREATE TABLE t7(
+ a integer primary key,
+ b number(5,10),
+ c character varying (8),
+ d VARCHAR(9),
+ e clob,
+ f BLOB,
+ g Text,
+ h
+ );
+ INSERT INTO t7(a) VALUES(1);
+ SELECT typeof(a), typeof(b), typeof(c), typeof(d),
+ typeof(e), typeof(f), typeof(g), typeof(h)
+ FROM t7 LIMIT 1;
+ }
+} {integer null null null null null null null}
+do_test table-11.2 {
+ execsql {
+ SELECT typeof(a+b), typeof(a||b), typeof(c+d), typeof(c||d)
+ FROM t7 LIMIT 1;
+ }
+} {null null null null}
+
+# Test that when creating a table using CREATE TABLE AS, column types are
+# assigned correctly for (SELECT ...) and 'x AS y' expressions.
+do_test table-12.1 {
+ ifcapable subquery {
+ execsql {
+ CREATE TABLE t8 AS SELECT b, h, a as i, (SELECT f FROM t7) as j FROM t7;
+ }
+ } else {
+ execsql {
+ CREATE TABLE t8 AS SELECT b, h, a as i, f as j FROM t7;
+ }
+ }
+} {}
+do_test table-12.2 {
+ execsql {
+ SELECT sql FROM sqlite_master WHERE tbl_name = 't8'
+ }
+} {{CREATE TABLE t8(b number(5,10),h,i integer,j BLOB)}}
+
+#--------------------------------------------------------------------
+# Test cases table-13.*
+#
+# Test the ability to have default values of CURRENT_TIME, CURRENT_DATE
+# and CURRENT_TIMESTAMP.
+#
+do_test table-13.1 {
+ execsql {
+ CREATE TABLE tablet8(
+ a integer primary key,
+ tm text DEFAULT CURRENT_TIME,
+ dt text DEFAULT CURRENT_DATE,
+ dttm text DEFAULT CURRENT_TIMESTAMP
+ );
+ SELECT * FROM tablet8;
+ }
+} {}
+set i 0
+foreach {date time seconds} {
+ 1976-07-04 12:00:00 205329600
+ 1994-04-16 14:00:00 766504800
+ 2000-01-01 00:00:00 946684800
+ 2003-12-31 12:34:56 1072874096
+} {
+ incr i
+ set sqlite_current_time $seconds
+ do_test table-13.2.$i {
+ execsql "
+ INSERT INTO tablet8(a) VALUES($i);
+ SELECT tm, dt, dttm FROM tablet8 WHERE a=$i;
+ "
+ } [list $time $date [list $date $time]]
+}
+set sqlite_current_time 0
+
+#--------------------------------------------------------------------
+# Test cases table-14.*
+#
+# Test that a table cannot be created or dropped while other virtual
+# machines are active. This is required because otherwise when in
+# auto-vacuum mode the btree-layer may need to move the root-pages of
+# a table for which there is an open cursor.
+#
+# 2007-05-02: A open btree cursor no longer blocks CREATE TABLE.
+# But DROP TABLE is still prohibited because we do not want to
+# delete a table out from under a running query.
+#
+
+# db eval {
+# pragma vdbe_trace = 0;
+# }
+# Try to create a table from within a callback:
+unset -nocomplain result
+do_test table-14.1 {
+ set rc [
+ catch {
+ db eval {SELECT * FROM tablet8 LIMIT 1} {} {
+ db eval {CREATE TABLE t9(a, b, c)}
+ }
+ } msg
+ ]
+ set result [list $rc $msg]
+} {0 {}}
+
+# Try to drop a table from within a callback:
+do_test table-14.2 {
+ set rc [
+ catch {
+ db eval {SELECT * FROM tablet8 LIMIT 1} {} {
+ db eval {DROP TABLE t9;}
+ }
+ } msg
+ ]
+ set result [list $rc $msg]
+} {1 {database table is locked}}
+
+ifcapable attach {
+ # Now attach a database and ensure that a table can be created in the
+ # attached database whilst in a callback from a query on the main database.
+ do_test table-14.3 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' as aux;
+ }
+ db eval {SELECT * FROM tablet8 LIMIT 1} {} {
+ db eval {CREATE TABLE aux.t1(a, b, c)}
+ }
+ } {}
+
+ # On the other hand, it should be impossible to drop a table when any VMs
+ # are active. This is because VerifyCookie instructions may have already
+ # been executed, and btree root-pages may not move after this (which a
+ # delete table might do).
+ do_test table-14.4 {
+ set rc [
+ catch {
+ db eval {SELECT * FROM tablet8 LIMIT 1} {} {
+ db eval {DROP TABLE aux.t1;}
+ }
+ } msg
+ ]
+ set result [list $rc $msg]
+ } {1 {database table is locked}}
+}
+
+# Create and drop 2000 tables. This is to check that the balance_shallow()
+# routine works correctly on the sqlite_master table. At one point it
+# contained a bug that would prevent the right-child pointer of the
+# child page from being copied to the root page.
+#
+do_test table-15.1 {
+ execsql {BEGIN}
+ for {set i 0} {$i<2000} {incr i} {
+ execsql "CREATE TABLE tbl$i (a, b, c)"
+ }
+ execsql {COMMIT}
+} {}
+do_test table-15.2 {
+ execsql {BEGIN}
+ for {set i 0} {$i<2000} {incr i} {
+ execsql "DROP TABLE tbl$i"
+ }
+ execsql {COMMIT}
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/tableapi.test b/third_party/sqlite/test/tableapi.test
new file mode 100755
index 0000000..1437663
--- /dev/null
+++ b/third_party/sqlite/test/tableapi.test
@@ -0,0 +1,252 @@
+# 2001 September 15
+#
+# 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 file is testing the sqlite_exec_printf() and
+# sqlite_get_table_printf() APIs.
+#
+# $Id: tableapi.test,v 1.20 2008/07/31 02:05:05 shane Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable memdebug {
+ source $testdir/malloc_common.tcl
+}
+
+do_test tableapi-1.0 {
+ set ::dbx [sqlite3_open test.db]
+ catch {sqlite_exec_printf $::dbx {DROP TABLE xyz} {}}
+ sqlite3_exec_printf $::dbx {CREATE TABLE %s(a int, b text)} xyz
+} {0 {}}
+do_test tableapi-1.1 {
+ sqlite3_exec_printf $::dbx {
+ INSERT INTO xyz VALUES(1,'%q')
+ } {Hi Y'all}
+} {0 {}}
+do_test tableapi-1.2 {
+ sqlite3_exec_printf $::dbx {SELECT * FROM xyz} {}
+} {0 {a b 1 {Hi Y'all}}}
+
+ifcapable gettable {
+
+do_test tableapi-2.1 {
+ sqlite3_get_table_printf $::dbx {
+ BEGIN TRANSACTION;
+ SELECT * FROM xyz WHERE b='%q'
+ } {Hi Y'all}
+} {0 1 2 a b 1 {Hi Y'all}}
+do_test tableapi-2.2 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz
+ } {}
+} {0 1 2 a b 1 {Hi Y'all}}
+do_test tableapi-2.3 {
+ for {set i 2} {$i<=50} {incr i} {
+ sqlite3_get_table_printf $::dbx \
+ "INSERT INTO xyz VALUES($i,'(%s)')" $i
+ }
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz ORDER BY a
+ } {}
+} {0 50 2 a b 1 {Hi Y'all} 2 (2) 3 (3) 4 (4) 5 (5) 6 (6) 7 (7) 8 (8) 9 (9) 10 (10) 11 (11) 12 (12) 13 (13) 14 (14) 15 (15) 16 (16) 17 (17) 18 (18) 19 (19) 20 (20) 21 (21) 22 (22) 23 (23) 24 (24) 25 (25) 26 (26) 27 (27) 28 (28) 29 (29) 30 (30) 31 (31) 32 (32) 33 (33) 34 (34) 35 (35) 36 (36) 37 (37) 38 (38) 39 (39) 40 (40) 41 (41) 42 (42) 43 (43) 44 (44) 45 (45) 46 (46) 47 (47) 48 (48) 49 (49) 50 (50)}
+do_test tableapi-2.3.1 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>49 ORDER BY a
+ } {}
+} {0 1 2 a b 50 (50)}
+do_test tableapi-2.3.2 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>47 ORDER BY a
+ } {}
+} {0 3 2 a b 48 (48) 49 (49) 50 (50)}
+do_test tableapi-2.3.3 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>47 ORDER BY a; invalid
+ } {}
+} {1 {near "invalid": syntax error}}
+do_test tableapi-2.3.4 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>47 ORDER BY a
+ } {} 8
+} {0 a b 48 (48) 49 (49) 50 (50)}
+do_test tableapi-2.4 {
+ set manyquote ''''''''
+ append manyquote $manyquote
+ append manyquote $manyquote
+ append manyquote $manyquote
+ append manyquote $manyquote
+ append manyquote $manyquote
+ append manyquote $manyquote
+ set ::big_str "$manyquote Hello $manyquote"
+ sqlite3_get_table_printf $::dbx {
+ INSERT INTO xyz VALUES(51,'%q')
+ } $::big_str
+} {0 0 0}
+do_test tableapi-2.5 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>49 ORDER BY a;
+ } {}
+} "0 2 2 a b 50 (50) 51 \173$::big_str\175"
+do_test tableapi-2.6 {
+ sqlite3_get_table_printf $::dbx {
+ INSERT INTO xyz VALUES(52,NULL)
+ } {}
+ ifcapable subquery {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a IN (42,50,52) ORDER BY a DESC
+ } {}
+ } else {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a=42 OR a=50 OR a=52 ORDER BY a DESC
+ } {}
+ }
+} {0 3 2 a b 52 NULL 50 (50) 42 (42)}
+do_test tableapi-2.7 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>1000
+ } {}
+} {0 0 0}
+
+}; # end ifcapable gettable
+
+
+# Repeat all tests with the empty_result_callbacks pragma turned on
+#
+do_test tableapi-3.1 {
+ sqlite3_get_table_printf $::dbx {
+ ROLLBACK;
+ PRAGMA empty_result_callbacks = ON;
+ SELECT * FROM xyz WHERE b='%q'
+ } {Hi Y'all}
+} {0 1 2 a b 1 {Hi Y'all}}
+do_test tableapi-3.2 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz
+ } {}
+} {0 1 2 a b 1 {Hi Y'all}}
+do_test tableapi-3.3 {
+ for {set i 2} {$i<=50} {incr i} {
+ sqlite3_get_table_printf $::dbx \
+ "INSERT INTO xyz VALUES($i,'(%s)')" $i
+ }
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz ORDER BY a
+ } {}
+} {0 50 2 a b 1 {Hi Y'all} 2 (2) 3 (3) 4 (4) 5 (5) 6 (6) 7 (7) 8 (8) 9 (9) 10 (10) 11 (11) 12 (12) 13 (13) 14 (14) 15 (15) 16 (16) 17 (17) 18 (18) 19 (19) 20 (20) 21 (21) 22 (22) 23 (23) 24 (24) 25 (25) 26 (26) 27 (27) 28 (28) 29 (29) 30 (30) 31 (31) 32 (32) 33 (33) 34 (34) 35 (35) 36 (36) 37 (37) 38 (38) 39 (39) 40 (40) 41 (41) 42 (42) 43 (43) 44 (44) 45 (45) 46 (46) 47 (47) 48 (48) 49 (49) 50 (50)}
+do_test tableapi-3.3.1 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>49 ORDER BY a
+ } {}
+} {0 1 2 a b 50 (50)}
+do_test tableapi-3.3.2 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>47 ORDER BY a
+ } {}
+} {0 3 2 a b 48 (48) 49 (49) 50 (50)}
+do_test tableapi-3.4 {
+ sqlite3_get_table_printf $::dbx {
+ INSERT INTO xyz VALUES(51,'%q')
+ } $::big_str
+} {0 0 0}
+do_test tableapi-3.5 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>49 ORDER BY a;
+ } {}
+} "0 2 2 a b 50 (50) 51 \173$::big_str\175"
+do_test tableapi-3.6 {
+ sqlite3_get_table_printf $::dbx {
+ INSERT INTO xyz VALUES(52,NULL)
+ } {}
+ ifcapable subquery {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a IN (42,50,52) ORDER BY a DESC
+ } {}
+ } else {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a=42 OR a=50 OR a=52 ORDER BY a DESC
+ } {}
+ }
+} {0 3 2 a b 52 NULL 50 (50) 42 (42)}
+do_test tableapi-3.7 {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz WHERE a>1000
+ } {}
+} {0 0 2 a b}
+
+do_test tableapi-4.1 {
+ set rc [catch {
+ sqlite3_get_table_printf $::dbx {
+ SELECT * FROM xyz; SELECT * FROM sqlite_master
+ } {}
+ } msg]
+ concat $rc $msg
+} {0 1 {sqlite3_get_table() called with two or more incompatible queries}}
+
+# A report on the mailing list says that the sqlite_get_table() api fails
+# on queries involving more than 40 columns. The following code attempts
+# to test that complaint
+#
+do_test tableapi-5.1 {
+ set sql "CREATE TABLE t2("
+ set sep ""
+ for {set i 1} {$i<=100} {incr i} {
+ append sql ${sep}x$i
+ set sep ,
+ }
+ append sql )
+ sqlite3_get_table_printf $::dbx $sql {}
+ set sql "INSERT INTO t2 VALUES("
+ set sep ""
+ for {set i 1} {$i<=100} {incr i} {
+ append sql ${sep}$i
+ set sep ,
+ }
+ append sql )
+ sqlite3_get_table_printf $::dbx $sql {}
+ sqlite3_get_table_printf $::dbx {SELECT * FROM t2} {}
+} {0 1 100 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 x40 x41 x42 x43 x44 x45 x46 x47 x48 x49 x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 x61 x62 x63 x64 x65 x66 x67 x68 x69 x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 x80 x81 x82 x83 x84 x85 x86 x87 x88 x89 x90 x91 x92 x93 x94 x95 x96 x97 x98 x99 x100 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100}
+do_test tableapi-5.2 {
+ set sql "INSERT INTO t2 VALUES("
+ set sep ""
+ for {set i 1} {$i<=100} {incr i} {
+ append sql ${sep}[expr {$i+1000}]
+ set sep ,
+ }
+ append sql )
+ sqlite3_get_table_printf $::dbx $sql {}
+ sqlite3_get_table_printf $::dbx {SELECT * FROM t2} {}
+} {0 2 100 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 x40 x41 x42 x43 x44 x45 x46 x47 x48 x49 x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 x61 x62 x63 x64 x65 x66 x67 x68 x69 x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 x80 x81 x82 x83 x84 x85 x86 x87 x88 x89 x90 x91 x92 x93 x94 x95 x96 x97 x98 x99 x100 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100}
+
+ifcapable schema_pragmas {
+ do_test tableapi-6.1 {
+ sqlite3_get_table_printf $::dbx {PRAGMA user_version} {}
+ } {0 1 1 user_version 0}
+}
+
+ifcapable memdebug {
+ do_malloc_test tableapi-7 -sqlprep {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ INSERT INTO t1 SELECT a+4, b+4 FROM t1;
+ INSERT INTO t1 SELECT a+8, b+8 FROM t1;
+ } -tclbody {
+ set r [sqlite3_get_table_printf db {SELECT rowid, a, b FROM t1} {}]
+ if {[llength $r]<26} {error "out of memory"}
+ }
+}
+
+do_test tableapi-99.0 {
+ sqlite3_close $::dbx
+} {SQLITE_OK}
+
+finish_test
diff --git a/third_party/sqlite/test/tclsqlite.test b/third_party/sqlite/test/tclsqlite.test
new file mode 100755
index 0000000..c4c9c37
--- /dev/null
+++ b/third_party/sqlite/test/tclsqlite.test
@@ -0,0 +1,504 @@
+# 2001 September 15
+#
+# 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 TCL interface to the
+# SQLite library.
+#
+# Actually, all tests are based on the TCL interface, so the main
+# interface is pretty well tested. This file contains some addition
+# tests for fringe issues that the main test suite does not cover.
+#
+# $Id: tclsqlite.test,v 1.65 2008/07/10 17:52:49 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Check the error messages generated by tclsqlite
+#
+if {[sqlite3 -has-codec]} {
+ set r "sqlite_orig HANDLE FILENAME ?-key CODEC-KEY?"
+} else {
+ set r "sqlite3 HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN?"
+}
+do_test tcl-1.1 {
+ set v [catch {sqlite3 bogus} msg]
+ lappend v $msg
+} [list 1 "wrong # args: should be \"$r\""]
+do_test tcl-1.2 {
+ set v [catch {db bogus} msg]
+ lappend v $msg
+} {1 {bad option "bogus": must be authorizer, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, rollback_hook, timeout, total_changes, trace, transaction, update_hook, or version}}
+do_test tcl-1.2.1 {
+ set v [catch {db cache bogus} msg]
+ lappend v $msg
+} {1 {bad option "bogus": must be flush or size}}
+do_test tcl-1.2.2 {
+ set v [catch {db cache} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db cache option ?arg?"}}
+do_test tcl-1.3 {
+ execsql {CREATE TABLE t1(a int, b int)}
+ execsql {INSERT INTO t1 VALUES(10,20)}
+ set v [catch {
+ db eval {SELECT * FROM t1} data {
+ error "The error message"
+ }
+ } msg]
+ lappend v $msg
+} {1 {The error message}}
+do_test tcl-1.4 {
+ set v [catch {
+ db eval {SELECT * FROM t2} data {
+ error "The error message"
+ }
+ } msg]
+ lappend v $msg
+} {1 {no such table: t2}}
+do_test tcl-1.5 {
+ set v [catch {
+ db eval {SELECT * FROM t1} data {
+ break
+ }
+ } msg]
+ lappend v $msg
+} {0 {}}
+catch {expr x*} msg
+do_test tcl-1.6 {
+ set v [catch {
+ db eval {SELECT * FROM t1} data {
+ expr x*
+ }
+ } msg]
+ lappend v $msg
+} [list 1 $msg]
+do_test tcl-1.7 {
+ set v [catch {db} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db SUBCOMMAND ..."}}
+if {[catch {db auth {}}]==0} {
+ do_test tcl-1.8 {
+ set v [catch {db authorizer 1 2 3} msg]
+ lappend v $msg
+ } {1 {wrong # args: should be "db authorizer ?CALLBACK?"}}
+}
+do_test tcl-1.9 {
+ set v [catch {db busy 1 2 3} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db busy CALLBACK"}}
+do_test tcl-1.10 {
+ set v [catch {db progress 1} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db progress N CALLBACK"}}
+do_test tcl-1.11 {
+ set v [catch {db changes xyz} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db changes "}}
+do_test tcl-1.12 {
+ set v [catch {db commit_hook a b c} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db commit_hook ?CALLBACK?"}}
+ifcapable {complete} {
+ do_test tcl-1.13 {
+ set v [catch {db complete} msg]
+ lappend v $msg
+ } {1 {wrong # args: should be "db complete SQL"}}
+}
+do_test tcl-1.14 {
+ set v [catch {db eval} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db eval SQL ?ARRAY-NAME? ?SCRIPT?"}}
+do_test tcl-1.15 {
+ set v [catch {db function} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db function NAME SCRIPT"}}
+do_test tcl-1.16 {
+ set v [catch {db last_insert_rowid xyz} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db last_insert_rowid "}}
+do_test tcl-1.17 {
+ set v [catch {db rekey} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db rekey KEY"}}
+do_test tcl-1.18 {
+ set v [catch {db timeout} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db timeout MILLISECONDS"}}
+do_test tcl-1.19 {
+ set v [catch {db collate} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db collate NAME SCRIPT"}}
+do_test tcl-1.20 {
+ set v [catch {db collation_needed} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db collation_needed SCRIPT"}}
+do_test tcl-1.21 {
+ set v [catch {db total_changes xyz} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db total_changes "}}
+do_test tcl-1.20 {
+ set v [catch {db copy} msg]
+ lappend v $msg
+} {1 {wrong # args: should be "db copy CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"}}
+do_test tcl-1.21 {
+ set v [catch {sqlite3 db2 test.db -vfs nosuchvfs} msg]
+ lappend v $msg
+} {1 {no such vfs: nosuchvfs}}
+
+catch {unset ::result}
+do_test tcl-2.1 {
+ execsql "CREATE TABLE t\u0123x(a int, b\u1235 float)"
+} {}
+ifcapable schema_pragmas {
+ do_test tcl-2.2 {
+ execsql "PRAGMA table_info(t\u0123x)"
+ } "0 a int 0 {} 0 1 b\u1235 float 0 {} 0"
+}
+do_test tcl-2.3 {
+ execsql "INSERT INTO t\u0123x VALUES(1,2.3)"
+ db eval "SELECT * FROM t\u0123x" result break
+ set result(*)
+} "a b\u1235"
+
+
+# Test the onecolumn method
+#
+do_test tcl-3.1 {
+ execsql {
+ INSERT INTO t1 SELECT a*2, b*2 FROM t1;
+ INSERT INTO t1 SELECT a*2+1, b*2+1 FROM t1;
+ INSERT INTO t1 SELECT a*2+3, b*2+3 FROM t1;
+ }
+ set rc [catch {db onecolumn {SELECT * FROM t1 ORDER BY a}} msg]
+ lappend rc $msg
+} {0 10}
+do_test tcl-3.2 {
+ db onecolumn {SELECT * FROM t1 WHERE a<0}
+} {}
+do_test tcl-3.3 {
+ set rc [catch {db onecolumn} errmsg]
+ lappend rc $errmsg
+} {1 {wrong # args: should be "db onecolumn SQL"}}
+do_test tcl-3.4 {
+ set rc [catch {db onecolumn {SELECT bogus}} errmsg]
+ lappend rc $errmsg
+} {1 {no such column: bogus}}
+ifcapable {tclvar} {
+ do_test tcl-3.5 {
+ set b 50
+ set rc [catch {db one {SELECT * FROM t1 WHERE b>$b}} msg]
+ lappend rc $msg
+ } {0 41}
+ do_test tcl-3.6 {
+ set b 500
+ set rc [catch {db one {SELECT * FROM t1 WHERE b>$b}} msg]
+ lappend rc $msg
+ } {0 {}}
+ do_test tcl-3.7 {
+ set b 500
+ set rc [catch {db one {
+ INSERT INTO t1 VALUES(99,510);
+ SELECT * FROM t1 WHERE b>$b
+ }} msg]
+ lappend rc $msg
+ } {0 99}
+}
+ifcapable {!tclvar} {
+ execsql {INSERT INTO t1 VALUES(99,510)}
+}
+
+# Turn the busy handler on and off
+#
+do_test tcl-4.1 {
+ proc busy_callback {cnt} {
+ break
+ }
+ db busy busy_callback
+ db busy
+} {busy_callback}
+do_test tcl-4.2 {
+ db busy {}
+ db busy
+} {}
+
+ifcapable {tclvar} {
+ # Parsing of TCL variable names within SQL into bound parameters.
+ #
+ do_test tcl-5.1 {
+ execsql {CREATE TABLE t3(a,b,c)}
+ catch {unset x}
+ set x(1) 5
+ set x(2) 7
+ execsql {
+ INSERT INTO t3 VALUES($::x(1),$::x(2),$::x(3));
+ SELECT * FROM t3
+ }
+ } {5 7 {}}
+ do_test tcl-5.2 {
+ execsql {
+ SELECT typeof(a), typeof(b), typeof(c) FROM t3
+ }
+ } {text text null}
+ do_test tcl-5.3 {
+ catch {unset x}
+ set x [binary format h12 686900686f00]
+ execsql {
+ UPDATE t3 SET a=$::x;
+ }
+ db eval {
+ SELECT a FROM t3
+ } break
+ binary scan $a h12 adata
+ set adata
+ } {686900686f00}
+ do_test tcl-5.4 {
+ execsql {
+ SELECT typeof(a), typeof(b), typeof(c) FROM t3
+ }
+ } {blob text null}
+}
+
+# Operation of "break" and "continue" within row scripts
+#
+do_test tcl-6.1 {
+ db eval {SELECT * FROM t1} {
+ break
+ }
+ lappend a $b
+} {10 20}
+do_test tcl-6.2 {
+ set cnt 0
+ db eval {SELECT * FROM t1} {
+ if {$a>40} continue
+ incr cnt
+ }
+ set cnt
+} {4}
+do_test tcl-6.3 {
+ set cnt 0
+ db eval {SELECT * FROM t1} {
+ if {$a<40} continue
+ incr cnt
+ }
+ set cnt
+} {5}
+do_test tcl-6.4 {
+ proc return_test {x} {
+ db eval {SELECT * FROM t1} {
+ if {$a==$x} {return $b}
+ }
+ }
+ return_test 10
+} 20
+do_test tcl-6.5 {
+ return_test 20
+} 40
+do_test tcl-6.6 {
+ return_test 99
+} 510
+do_test tcl-6.7 {
+ return_test 0
+} {}
+
+do_test tcl-7.1 {
+ db version
+ expr 0
+} {0}
+
+# modify and reset the NULL representation
+#
+do_test tcl-8.1 {
+ db nullvalue NaN
+ execsql {INSERT INTO t1 VALUES(30,NULL)}
+ db eval {SELECT * FROM t1 WHERE b IS NULL}
+} {30 NaN}
+do_test tcl-8.2 {
+ db nullvalue NULL
+ db nullvalue
+} {NULL}
+do_test tcl-8.3 {
+ db nullvalue {}
+ db eval {SELECT * FROM t1 WHERE b IS NULL}
+} {30 {}}
+
+# Test the return type of user-defined functions
+#
+do_test tcl-9.1 {
+ db function ret_str {return "hi"}
+ execsql {SELECT typeof(ret_str())}
+} {text}
+do_test tcl-9.2 {
+ db function ret_dbl {return [expr {rand()*0.5}]}
+ execsql {SELECT typeof(ret_dbl())}
+} {real}
+do_test tcl-9.3 {
+ db function ret_int {return [expr {int(rand()*200)}]}
+ execsql {SELECT typeof(ret_int())}
+} {integer}
+
+# Recursive calls to the same user-defined function
+#
+ifcapable tclvar {
+ do_test tcl-9.10 {
+ proc userfunc_r1 {n} {
+ if {$n<=0} {return 0}
+ set nm1 [expr {$n-1}]
+ return [expr {[db eval {SELECT r1($nm1)}]+$n}]
+ }
+ db function r1 userfunc_r1
+ execsql {SELECT r1(10)}
+ } {55}
+ do_test tcl-9.11 {
+ execsql {SELECT r1(100)}
+ } {5050}
+}
+
+# Tests for the new transaction method
+#
+do_test tcl-10.1 {
+ db transaction {}
+} {}
+do_test tcl-10.2 {
+ db transaction deferred {}
+} {}
+do_test tcl-10.3 {
+ db transaction immediate {}
+} {}
+do_test tcl-10.4 {
+ db transaction exclusive {}
+} {}
+do_test tcl-10.5 {
+ set rc [catch {db transaction xyzzy {}} msg]
+ lappend rc $msg
+} {1 {bad transaction type "xyzzy": must be deferred, exclusive, or immediate}}
+do_test tcl-10.6 {
+ set rc [catch {db transaction {error test-error}} msg]
+ lappend rc $msg
+} {1 test-error}
+do_test tcl-10.7 {
+ db transaction {
+ db eval {CREATE TABLE t4(x)}
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(1)}
+ }
+ }
+ db eval {SELECT * FROM t4}
+} 1
+do_test tcl-10.8 {
+ catch {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(2)}
+ db eval {INSERT INTO t4 VALUES(3)}
+ db eval {INSERT INTO t4 VALUES(4)}
+ error test-error
+ }
+ }
+ db eval {SELECT * FROM t4}
+} 1
+do_test tcl-10.9 {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(2)}
+ catch {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(3)}
+ db eval {INSERT INTO t4 VALUES(4)}
+ error test-error
+ }
+ }
+ }
+ db eval {SELECT * FROM t4}
+} {1 2 3 4}
+do_test tcl-10.10 {
+ for {set i 0} {$i<1} {incr i} {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(5)}
+ continue
+ }
+ }
+ db eval {SELECT * FROM t4}
+} {1 2 3 4 5}
+do_test tcl-10.11 {
+ for {set i 0} {$i<10} {incr i} {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(6)}
+ break
+ }
+ }
+ db eval {SELECT * FROM t4}
+} {1 2 3 4 5 6}
+do_test tcl-10.12 {
+ set rc [catch {
+ for {set i 0} {$i<10} {incr i} {
+ db transaction {
+ db eval {INSERT INTO t4 VALUES(7)}
+ return
+ }
+ }
+ }]
+} {2}
+do_test tcl-10.13 {
+ db eval {SELECT * FROM t4}
+} {1 2 3 4 5 6 7}
+
+do_test tcl-11.1 {
+ db exists {SELECT x,x*2,x+x FROM t4 WHERE x==4}
+} {1}
+do_test tcl-11.2 {
+ db exists {SELECT 0 FROM t4 WHERE x==4}
+} {1}
+do_test tcl-11.3 {
+ db exists {SELECT 1 FROM t4 WHERE x==8}
+} {0}
+
+do_test tcl-12.1 {
+ unset -nocomplain a b c version
+ set version [db version]
+ scan $version "%d.%d.%d" a b c
+ expr $a*1000000 + $b*1000 + $c
+} [sqlite3_libversion_number]
+
+
+# Check to see that when bindings of the form @aaa are used instead
+# of $aaa, that objects are treated as bytearray and are inserted
+# as BLOBs.
+#
+ifcapable tclvar {
+ do_test tcl-13.1 {
+ db eval {CREATE TABLE t5(x BLOB)}
+ set x abc123
+ db eval {INSERT INTO t5 VALUES($x)}
+ db eval {SELECT typeof(x) FROM t5}
+ } {text}
+ do_test tcl-13.2 {
+ binary scan $x H notUsed
+ db eval {
+ DELETE FROM t5;
+ INSERT INTO t5 VALUES($x);
+ SELECT typeof(x) FROM t5;
+ }
+ } {text}
+ do_test tcl-13.3 {
+ db eval {
+ DELETE FROM t5;
+ INSERT INTO t5 VALUES(@x);
+ SELECT typeof(x) FROM t5;
+ }
+ } {blob}
+ do_test tcl-13.4 {
+ set y 1234
+ db eval {
+ DELETE FROM t5;
+ INSERT INTO t5 VALUES(@y);
+ SELECT hex(x), typeof(x) FROM t5
+ }
+ } {31323334 blob}
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tempdb.test b/third_party/sqlite/test/tempdb.test
new file mode 100755
index 0000000..5e4663f
--- /dev/null
+++ b/third_party/sqlite/test/tempdb.test
@@ -0,0 +1,55 @@
+# 2008 April 14
+#
+# 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 file is in making sure that rolling back
+# a statement journal works correctly.
+#
+# $Id: tempdb.test,v 1.1 2008/04/15 00:02:00 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Use a temporary database.
+#
+db close
+sqlite3 db {}
+
+# Force a statement journal rollback on a database file that
+# has never been opened.
+#
+do_test tempdb-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(x UNIQUE);
+ CREATE TABLE t2(y);
+ INSERT INTO t2 VALUES('hello');
+ INSERT INTO t2 VALUES(NULL);
+ }
+ # Because of the transaction, the temporary database file
+ # has not even been opened yet. The following statement
+ # will cause a statement journal rollback on this non-existant
+ # file.
+ catchsql {
+ INSERT INTO t1
+ SELECT CASE WHEN y IS NULL THEN test_error('oops', 11) ELSE y END
+ FROM t2;
+ }
+} {1 oops}
+
+# Verify that no writes occurred in t1.
+#
+do_test tempdb-1.2 {
+ execsql {
+ SELECT * FROM t1
+ }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/temptable.test b/third_party/sqlite/test/temptable.test
new file mode 100755
index 0000000..5fc40c4
--- /dev/null
+++ b/third_party/sqlite/test/temptable.test
@@ -0,0 +1,422 @@
+# 2001 October 7
+#
+# 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.
+#
+# This file implements tests for temporary tables and indices.
+#
+# $Id: temptable.test,v 1.19 2007/10/09 08:29:33 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tempdb {
+ finish_test
+ return
+}
+
+# Create an alternative connection to the database
+#
+do_test temptable-1.0 {
+ sqlite3 db2 ./test.db
+ set dummy {}
+} {}
+
+# Create a permanent table.
+#
+do_test temptable-1.1 {
+ execsql {CREATE TABLE t1(a,b,c);}
+ execsql {INSERT INTO t1 VALUES(1,2,3);}
+ execsql {SELECT * FROM t1}
+} {1 2 3}
+do_test temptable-1.2 {
+ catch {db2 eval {SELECT * FROM sqlite_master}}
+ db2 eval {SELECT * FROM t1}
+} {1 2 3}
+do_test temptable-1.3 {
+ execsql {SELECT name FROM sqlite_master}
+} {t1}
+do_test temptable-1.4 {
+ db2 eval {SELECT name FROM sqlite_master}
+} {t1}
+
+# Create a temporary table. Verify that only one of the two
+# processes can see it.
+#
+do_test temptable-1.5 {
+ db2 eval {
+ CREATE TEMP TABLE t2(x,y,z);
+ INSERT INTO t2 VALUES(4,5,6);
+ }
+ db2 eval {SELECT * FROM t2}
+} {4 5 6}
+do_test temptable-1.6 {
+ catch {execsql {SELECT * FROM sqlite_master}}
+ catchsql {SELECT * FROM t2}
+} {1 {no such table: t2}}
+do_test temptable-1.7 {
+ catchsql {INSERT INTO t2 VALUES(8,9,0);}
+} {1 {no such table: t2}}
+do_test temptable-1.8 {
+ db2 eval {INSERT INTO t2 VALUES(8,9,0);}
+ db2 eval {SELECT * FROM t2 ORDER BY x}
+} {4 5 6 8 9 0}
+do_test temptable-1.9 {
+ db2 eval {DELETE FROM t2 WHERE x==8}
+ db2 eval {SELECT * FROM t2 ORDER BY x}
+} {4 5 6}
+do_test temptable-1.10 {
+ db2 eval {DELETE FROM t2}
+ db2 eval {SELECT * FROM t2}
+} {}
+do_test temptable-1.11 {
+ db2 eval {
+ INSERT INTO t2 VALUES(7,6,5);
+ INSERT INTO t2 VALUES(4,3,2);
+ SELECT * FROM t2 ORDER BY x;
+ }
+} {4 3 2 7 6 5}
+do_test temptable-1.12 {
+ db2 eval {DROP TABLE t2;}
+ set r [catch {db2 eval {SELECT * FROM t2}} msg]
+ lappend r $msg
+} {1 {no such table: t2}}
+
+# Make sure temporary tables work with transactions
+#
+do_test temptable-2.1 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TEMPORARY TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+} {1 2}
+do_test temptable-2.2 {
+ execsql {ROLLBACK}
+ catchsql {SELECT * FROM t2}
+} {1 {no such table: t2}}
+do_test temptable-2.3 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TEMPORARY TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+} {1 2}
+do_test temptable-2.4 {
+ execsql {COMMIT}
+ catchsql {SELECT * FROM t2}
+} {0 {1 2}}
+do_test temptable-2.5 {
+ set r [catch {db2 eval {SELECT * FROM t2}} msg]
+ lappend r $msg
+} {1 {no such table: t2}}
+
+# Make sure indices on temporary tables are also temporary.
+#
+do_test temptable-3.1 {
+ execsql {
+ CREATE INDEX i2 ON t2(x);
+ SELECT name FROM sqlite_master WHERE type='index';
+ }
+} {}
+do_test temptable-3.2 {
+ execsql {
+ SELECT y FROM t2 WHERE x=1;
+ }
+} {2}
+do_test temptable-3.3 {
+ execsql {
+ DROP INDEX i2;
+ SELECT y FROM t2 WHERE x=1;
+ }
+} {2}
+do_test temptable-3.4 {
+ execsql {
+ CREATE INDEX i2 ON t2(x);
+ DROP TABLE t2;
+ }
+ catchsql {DROP INDEX i2}
+} {1 {no such index: i2}}
+
+# Check for correct name collision processing. A name collision can
+# occur when process A creates a temporary table T then process B
+# creates a permanent table also named T. The temp table in process A
+# hides the existance of the permanent table.
+#
+do_test temptable-4.1 {
+ execsql {
+ CREATE TEMP TABLE t2(x,y);
+ INSERT INTO t2 VALUES(10,20);
+ SELECT * FROM t2;
+ } db2
+} {10 20}
+do_test temptable-4.2 {
+ execsql {
+ CREATE TABLE t2(x,y,z);
+ INSERT INTO t2 VALUES(9,8,7);
+ SELECT * FROM t2;
+ }
+} {9 8 7}
+do_test temptable-4.3 {
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {0 {10 20}}
+do_test temptable-4.4.1 {
+ catchsql {
+ SELECT * FROM temp.t2;
+ } db2
+} {0 {10 20}}
+do_test temptable-4.4.2 {
+ catchsql {
+ SELECT * FROM main.t2;
+ } db2
+} {1 {no such table: main.t2}}
+#do_test temptable-4.4.3 {
+# catchsql {
+# SELECT name FROM main.sqlite_master WHERE type='table';
+# } db2
+#} {1 {database schema has changed}}
+do_test temptable-4.4.4 {
+ catchsql {
+ SELECT name FROM main.sqlite_master WHERE type='table';
+ } db2
+} {0 {t1 t2}}
+do_test temptable-4.4.5 {
+ catchsql {
+ SELECT * FROM main.t2;
+ } db2
+} {0 {9 8 7}}
+do_test temptable-4.4.6 {
+ # TEMP takes precedence over MAIN
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {0 {10 20}}
+do_test temptable-4.5 {
+ catchsql {
+ DROP TABLE t2; -- should drop TEMP
+ SELECT * FROM t2; -- data should be from MAIN
+ } db2
+} {0 {9 8 7}}
+do_test temptable-4.6 {
+ db2 close
+ sqlite3 db2 ./test.db
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {0 {9 8 7}}
+do_test temptable-4.7 {
+ catchsql {
+ DROP TABLE t2;
+ SELECT * FROM t2;
+ }
+} {1 {no such table: t2}}
+do_test temptable-4.8 {
+ db2 close
+ sqlite3 db2 ./test.db
+ execsql {
+ CREATE TEMP TABLE t2(x unique,y);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT * FROM t2;
+ } db2
+} {1 2}
+do_test temptable-4.9 {
+ execsql {
+ CREATE TABLE t2(x unique, y);
+ INSERT INTO t2 VALUES(3,4);
+ SELECT * FROM t2;
+ }
+} {3 4}
+do_test temptable-4.10.1 {
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {0 {1 2}}
+# Update: The schema is reloaded in test temptable-4.10.1. And tclsqlite.c
+# handles it and retries the query anyway.
+# do_test temptable-4.10.2 {
+# catchsql {
+# SELECT name FROM sqlite_master WHERE type='table'
+# } db2
+# } {1 {database schema has changed}}
+do_test temptable-4.10.3 {
+ catchsql {
+ SELECT name FROM sqlite_master WHERE type='table'
+ } db2
+} {0 {t1 t2}}
+do_test temptable-4.11 {
+ execsql {
+ SELECT * FROM t2;
+ } db2
+} {1 2}
+do_test temptable-4.12 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {3 4}
+do_test temptable-4.13 {
+ catchsql {
+ DROP TABLE t2; -- drops TEMP.T2
+ SELECT * FROM t2; -- uses MAIN.T2
+ } db2
+} {0 {3 4}}
+do_test temptable-4.14 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {3 4}
+do_test temptable-4.15 {
+ db2 close
+ sqlite3 db2 ./test.db
+ execsql {
+ SELECT * FROM t2;
+ } db2
+} {3 4}
+
+# Now create a temporary table in db2 and a permanent index in db. The
+# temporary table in db2 should mask the name of the permanent index,
+# but the permanent index should still be accessible and should still
+# be updated when its corresponding table changes.
+#
+do_test temptable-5.1 {
+ execsql {
+ CREATE TEMP TABLE mask(a,b,c)
+ } db2
+ execsql {
+ CREATE INDEX mask ON t2(x);
+ SELECT * FROM t2;
+ }
+} {3 4}
+#do_test temptable-5.2 {
+# catchsql {
+# SELECT * FROM t2;
+# } db2
+#} {1 {database schema has changed}}
+do_test temptable-5.3 {
+ catchsql {
+ SELECT * FROM t2;
+ } db2
+} {0 {3 4}}
+do_test temptable-5.4 {
+ execsql {
+ SELECT y FROM t2 WHERE x=3
+ }
+} {4}
+do_test temptable-5.5 {
+ execsql {
+ SELECT y FROM t2 WHERE x=3
+ } db2
+} {4}
+do_test temptable-5.6 {
+ execsql {
+ INSERT INTO t2 VALUES(1,2);
+ SELECT y FROM t2 WHERE x=1;
+ } db2
+} {2}
+do_test temptable-5.7 {
+ execsql {
+ SELECT y FROM t2 WHERE x=3
+ } db2
+} {4}
+do_test temptable-5.8 {
+ execsql {
+ SELECT y FROM t2 WHERE x=1;
+ }
+} {2}
+do_test temptable-5.9 {
+ execsql {
+ SELECT y FROM t2 WHERE x=3
+ }
+} {4}
+
+db2 close
+
+# Test for correct operation of read-only databases
+#
+do_test temptable-6.1 {
+ execsql {
+ CREATE TABLE t8(x);
+ INSERT INTO t8 VALUES('xyzzy');
+ SELECT * FROM t8;
+ }
+} {xyzzy}
+do_test temptable-6.2 {
+ db close
+ catch {file attributes test.db -permissions 0444}
+ catch {file attributes test.db -readonly 1}
+ sqlite3 db test.db
+ if {[file writable test.db]} {
+ error "Unable to make the database file test.db readonly - rerun this test as an unprivileged user"
+ }
+ execsql {
+ SELECT * FROM t8;
+ }
+} {xyzzy}
+do_test temptable-6.3 {
+ if {[file writable test.db]} {
+ error "Unable to make the database file test.db readonly - rerun this test as an unprivileged user"
+ }
+ catchsql {
+ CREATE TABLE t9(x,y);
+ }
+} {1 {attempt to write a readonly database}}
+do_test temptable-6.4 {
+ catchsql {
+ CREATE TEMP TABLE t9(x,y);
+ }
+} {0 {}}
+do_test temptable-6.5 {
+ catchsql {
+ INSERT INTO t9 VALUES(1,2);
+ SELECT * FROM t9;
+ }
+} {0 {1 2}}
+do_test temptable-6.6 {
+ if {[file writable test.db]} {
+ error "Unable to make the database file test.db readonly - rerun this test as an unprivileged user"
+ }
+ catchsql {
+ INSERT INTO t8 VALUES('hello');
+ SELECT * FROM t8;
+ }
+} {1 {attempt to write a readonly database}}
+do_test temptable-6.7 {
+ catchsql {
+ SELECT * FROM t8,t9;
+ }
+} {0 {xyzzy 1 2}}
+do_test temptable-6.8 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM t8,t9;
+ }
+} {1 {no such table: t9}}
+
+file delete -force test2.db test2.db-journal
+ifcapable attach {
+ do_test temptable-7.1 {
+ catchsql {
+ ATTACH 'test2.db' AS two;
+ CREATE TEMP TABLE two.abc(x,y);
+ }
+ } {1 {temporary table name must be unqualified}}
+}
+
+# Need to do the following for tcl 8.5 on mac. On that configuration, the
+# -readonly flag is taken so seriously that a subsequent [file delete -force]
+# (required before the next test file can be executed) will fail.
+#
+catch {file attributes test.db -readonly 0}
+
+finish_test
diff --git a/third_party/sqlite/test/tester.tcl b/third_party/sqlite/test/tester.tcl
new file mode 100755
index 0000000..5a42196
--- /dev/null
+++ b/third_party/sqlite/test/tester.tcl
@@ -0,0 +1,940 @@
+# 2001 September 15
+#
+# 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 some common TCL routines used for regression
+# testing the SQLite library
+#
+# $Id: tester.tcl,v 1.134 2008/08/05 17:53:24 drh Exp $
+
+#
+# What for user input before continuing. This gives an opportunity
+# to connect profiling tools to the process.
+#
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[regexp {^-+pause$} [lindex $argv $i] all value]} {
+ puts -nonewline "Press RETURN to begin..."
+ flush stdout
+ gets stdin
+ set argv [lreplace $argv $i $i]
+ }
+}
+
+set tcl_precision 15
+set sqlite_pending_byte 0x0010000
+
+#
+# Check the command-line arguments for a default soft-heap-limit.
+# Store this default value in the global variable ::soft_limit and
+# update the soft-heap-limit each time this script is run. In that
+# way if an individual test file changes the soft-heap-limit, it
+# will be reset at the start of the next test file.
+#
+if {![info exists soft_limit]} {
+ set soft_limit 0
+ for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[regexp {^--soft-heap-limit=(.+)$} [lindex $argv $i] all value]} {
+ if {$value!="off"} {
+ set soft_limit $value
+ }
+ set argv [lreplace $argv $i $i]
+ }
+ }
+}
+sqlite3_soft_heap_limit $soft_limit
+
+#
+# Check the command-line arguments to set the memory debugger
+# backtrace depth.
+#
+# See the sqlite3_memdebug_backtrace() function in mem2.c or
+# test_malloc.c for additional information.
+#
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[lindex $argv $i] eq "--malloctrace"} {
+ set argv [lreplace $argv $i $i]
+ sqlite3_memdebug_backtrace 10
+ sqlite3_memdebug_log start
+ set tester_do_malloctrace 1
+ }
+}
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[regexp {^--backtrace=(\d+)$} [lindex $argv $i] all value]} {
+ sqlite3_memdebug_backtrace $value
+ set argv [lreplace $argv $i $i]
+ }
+}
+
+
+proc ostrace_call {zCall nClick zFile i32 i64} {
+ set s "INSERT INTO ostrace VALUES('$zCall', $nClick, '$zFile', $i32, $i64);"
+ puts $::ostrace_fd $s
+}
+
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[lindex $argv $i] eq "--ossummary" || [lindex $argv $i] eq "--ostrace"} {
+ sqlite3_instvfs create -default ostrace
+ set tester_do_ostrace 1
+ set ostrace_fd [open ostrace.sql w]
+ puts $ostrace_fd "BEGIN;"
+ if {[lindex $argv $i] eq "--ostrace"} {
+ set s "CREATE TABLE ostrace"
+ append s "(method TEXT, clicks INT, file TEXT, i32 INT, i64 INT);"
+ puts $ostrace_fd $s
+ sqlite3_instvfs configure ostrace ostrace_call
+ sqlite3_instvfs configure ostrace ostrace_call
+ }
+ set argv [lreplace $argv $i $i]
+ }
+ if {[lindex $argv $i] eq "--binarylog"} {
+ set tester_do_binarylog 1
+ set argv [lreplace $argv $i $i]
+ }
+}
+
+#
+# Check the command-line arguments to set the maximum number of
+# errors tolerated before halting.
+#
+if {![info exists maxErr]} {
+ set maxErr 1000
+}
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ if {[regexp {^--maxerror=(\d+)$} [lindex $argv $i] all maxErr]} {
+ set argv [lreplace $argv $i $i]
+ }
+}
+#puts "Max error = $maxErr"
+
+
+# Use the pager codec if it is available
+#
+if {[sqlite3 -has-codec] && [info command sqlite_orig]==""} {
+ rename sqlite3 sqlite_orig
+ proc sqlite3 {args} {
+ if {[llength $args]==2 && [string index [lindex $args 0] 0]!="-"} {
+ lappend args -key {xyzzy}
+ }
+ uplevel 1 sqlite_orig $args
+ }
+}
+
+
+# Create a test database
+#
+if {![info exists nTest]} {
+ sqlite3_shutdown
+ install_malloc_faultsim 1
+ sqlite3_initialize
+ if {[info exists tester_do_binarylog]} {
+ sqlite3_instvfs binarylog -default binarylog ostrace.bin
+ sqlite3_instvfs marker binarylog "$argv0 $argv"
+ }
+}
+catch {db close}
+file delete -force test.db
+file delete -force test.db-journal
+sqlite3 db ./test.db
+set ::DB [sqlite3_connection_pointer db]
+if {[info exists ::SETUP_SQL]} {
+ db eval $::SETUP_SQL
+}
+
+# Abort early if this script has been run before.
+#
+if {[info exists nTest]} return
+
+# Set the test counters to zero
+#
+set nErr 0
+set nTest 0
+set skip_test 0
+set failList {}
+set omitList {}
+if {![info exists speedTest]} {
+ set speedTest 0
+}
+
+# Record the fact that a sequence of tests were omitted.
+#
+proc omit_test {name reason} {
+ global omitList
+ lappend omitList [list $name $reason]
+}
+
+# Invoke the do_test procedure to run a single test
+#
+proc do_test {name cmd expected} {
+ global argv nErr nTest skip_test maxErr
+ sqlite3_memdebug_settitle $name
+ if {[info exists ::tester_do_binarylog]} {
+ sqlite3_instvfs marker binarylog "Start of $name"
+ }
+ if {$skip_test} {
+ set skip_test 0
+ return
+ }
+ if {[llength $argv]==0} {
+ set go 1
+ } else {
+ set go 0
+ foreach pattern $argv {
+ if {[string match $pattern $name]} {
+ set go 1
+ break
+ }
+ }
+ }
+ if {!$go} return
+ incr nTest
+ puts -nonewline $name...
+ flush stdout
+ if {[catch {uplevel #0 "$cmd;\n"} result]} {
+ puts "\nError: $result"
+ incr nErr
+ lappend ::failList $name
+ if {$nErr>$maxErr} {puts "*** Giving up..."; finalize_testing}
+ } elseif {[string compare $result $expected]} {
+ puts "\nExpected: \[$expected\]\n Got: \[$result\]"
+ incr nErr
+ lappend ::failList $name
+ if {$nErr>=$maxErr} {puts "*** Giving up..."; finalize_testing}
+ } else {
+ puts " Ok"
+ }
+ flush stdout
+ if {[info exists ::tester_do_binarylog]} {
+ sqlite3_instvfs marker binarylog "End of $name"
+ }
+}
+
+# Run an SQL script.
+# Return the number of microseconds per statement.
+#
+proc speed_trial {name numstmt units sql} {
+ puts -nonewline [format {%-21.21s } $name...]
+ flush stdout
+ set speed [time {sqlite3_exec_nr db $sql}]
+ set tm [lindex $speed 0]
+ if {$tm == 0} {
+ set rate [format %20s "many"]
+ } else {
+ set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
+ }
+ set u2 $units/s
+ puts [format {%12d uS %s %s} $tm $rate $u2]
+ global total_time
+ set total_time [expr {$total_time+$tm}]
+}
+proc speed_trial_tcl {name numstmt units script} {
+ puts -nonewline [format {%-21.21s } $name...]
+ flush stdout
+ set speed [time {eval $script}]
+ set tm [lindex $speed 0]
+ if {$tm == 0} {
+ set rate [format %20s "many"]
+ } else {
+ set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
+ }
+ set u2 $units/s
+ puts [format {%12d uS %s %s} $tm $rate $u2]
+ global total_time
+ set total_time [expr {$total_time+$tm}]
+}
+proc speed_trial_init {name} {
+ global total_time
+ set total_time 0
+}
+proc speed_trial_summary {name} {
+ global total_time
+ puts [format {%-21.21s %12d uS TOTAL} $name $total_time]
+}
+
+# Run this routine last
+#
+proc finish_test {} {
+ finalize_testing
+}
+proc finalize_testing {} {
+ global nTest nErr sqlite_open_file_count omitList
+
+ catch {db close}
+ catch {db2 close}
+ catch {db3 close}
+
+ vfs_unlink_test
+ sqlite3 db {}
+ # sqlite3_clear_tsd_memdebug
+ db close
+ sqlite3_reset_auto_extension
+ set heaplimit [sqlite3_soft_heap_limit]
+ if {$heaplimit!=$::soft_limit} {
+ puts "soft-heap-limit changed by this script\
+ from $::soft_limit to $heaplimit"
+ } elseif {$heaplimit!="" && $heaplimit>0} {
+ puts "soft-heap-limit set to $heaplimit"
+ }
+ sqlite3_soft_heap_limit 0
+ incr nTest
+ puts "$nErr errors out of $nTest tests"
+ if {$nErr>0} {
+ puts "Failures on these tests: $::failList"
+ }
+ if {[llength $omitList]>0} {
+ puts "Omitted test cases:"
+ set prec {}
+ foreach {rec} [lsort $omitList] {
+ if {$rec==$prec} continue
+ set prec $rec
+ puts [format { %-12s %s} [lindex $rec 0] [lindex $rec 1]]
+ }
+ }
+ if {$nErr>0 && ![working_64bit_int]} {
+ puts "******************************************************************"
+ puts "N.B.: The version of TCL that you used to build this test harness"
+ puts "is defective in that it does not support 64-bit integers. Some or"
+ puts "all of the test failures above might be a result from this defect"
+ puts "in your TCL build."
+ puts "******************************************************************"
+ }
+ if {[info exists ::tester_do_binarylog]} {
+ sqlite3_instvfs destroy binarylog
+ }
+ if {$sqlite_open_file_count} {
+ puts "$sqlite_open_file_count files were left open"
+ incr nErr
+ }
+ if {[info exists ::tester_do_ostrace]} {
+ puts "Writing ostrace.sql..."
+ set fd $::ostrace_fd
+
+ puts -nonewline $fd "CREATE TABLE ossummary"
+ puts $fd "(method TEXT, clicks INTEGER, count INTEGER);"
+ foreach row [sqlite3_instvfs report ostrace] {
+ foreach {method count clicks} $row break
+ puts $fd "INSERT INTO ossummary VALUES('$method', $clicks, $count);"
+ }
+ puts $fd "COMMIT;"
+ close $fd
+ sqlite3_instvfs destroy ostrace
+ }
+ if {[sqlite3_memory_used]>0} {
+ puts "Unfreed memory: [sqlite3_memory_used] bytes"
+ incr nErr
+ ifcapable memdebug||mem5||(mem3&&debug) {
+ puts "Writing unfreed memory log to \"./memleak.txt\""
+ sqlite3_memdebug_dump ./memleak.txt
+ }
+ } else {
+ puts "All memory allocations freed - no leaks"
+ ifcapable memdebug||mem5 {
+ sqlite3_memdebug_dump ./memusage.txt
+ }
+ }
+ show_memstats
+ puts "Maximum memory usage: [sqlite3_memory_highwater 1] bytes"
+ puts "Current memory usage: [sqlite3_memory_highwater] bytes"
+ if {[info commands sqlite3_memdebug_malloc_count] ne ""} {
+ puts "Number of malloc() : [sqlite3_memdebug_malloc_count] calls"
+ }
+ if {[info exists ::tester_do_malloctrace]} {
+ puts "Writing mallocs.sql..."
+ memdebug_log_sql
+ sqlite3_memdebug_log stop
+ sqlite3_memdebug_log clear
+
+ if {[sqlite3_memory_used]>0} {
+ puts "Writing leaks.sql..."
+ sqlite3_memdebug_log sync
+ memdebug_log_sql leaks.sql
+ }
+ }
+ foreach f [glob -nocomplain test.db-*-journal] {
+ file delete -force $f
+ }
+ foreach f [glob -nocomplain test.db-mj*] {
+ file delete -force $f
+ }
+ exit [expr {$nErr>0}]
+}
+
+# Display memory statistics for analysis and debugging purposes.
+#
+proc show_memstats {} {
+ set x [sqlite3_status SQLITE_STATUS_MEMORY_USED 0]
+ set y [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
+ set val [format {now %10d max %10d max-size %10d} \
+ [lindex $x 1] [lindex $x 2] [lindex $y 2]]
+ puts "Memory used: $val"
+ set x [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0]
+ set y [sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 0]
+ set val [format {now %10d max %10d max-size %10d} \
+ [lindex $x 1] [lindex $x 2] [lindex $y 2]]
+ puts "Page-cache used: $val"
+ set x [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0]
+ set val [format {now %10d max %10d} [lindex $x 1] [lindex $x 2]]
+ puts "Page-cache overflow: $val"
+ set x [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0]
+ set val [format {now %10d max %10d} [lindex $x 1] [lindex $x 2]]
+ puts "Scratch memory used: $val"
+ set x [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0]
+ set y [sqlite3_status SQLITE_STATUS_SCRATCH_SIZE 0]
+ set val [format {now %10d max %10d max-size %10d} \
+ [lindex $x 1] [lindex $x 2] [lindex $y 2]]
+ puts "Scratch overflow: $val"
+ ifcapable yytrackmaxstackdepth {
+ set x [sqlite3_status SQLITE_STATUS_PARSER_STACK 0]
+ set val [format { max %10d} [lindex $x 2]]
+ puts "Parser stack depth: $val"
+ }
+}
+
+# A procedure to execute SQL
+#
+proc execsql {sql {db db}} {
+ # puts "SQL = $sql"
+ uplevel [list $db eval $sql]
+}
+
+# Execute SQL and catch exceptions.
+#
+proc catchsql {sql {db db}} {
+ # puts "SQL = $sql"
+ set r [catch {$db eval $sql} msg]
+ lappend r $msg
+ return $r
+}
+
+# Do an VDBE code dump on the SQL given
+#
+proc explain {sql {db db}} {
+ puts ""
+ puts "addr opcode p1 p2 p3 p4 p5 #"
+ puts "---- ------------ ------ ------ ------ --------------- -- -"
+ $db eval "explain $sql" {} {
+ puts [format {%-4d %-12.12s %-6d %-6d %-6d % -17s %s %s} \
+ $addr $opcode $p1 $p2 $p3 $p4 $p5 $comment
+ ]
+ }
+}
+
+# Show the VDBE program for an SQL statement but omit the Trace
+# opcode at the beginning. This procedure can be used to prove
+# that different SQL statements generate exactly the same VDBE code.
+#
+proc explain_no_trace {sql} {
+ set tr [db eval "EXPLAIN $sql"]
+ return [lrange $tr 7 end]
+}
+
+# Another procedure to execute SQL. This one includes the field
+# names in the returned list.
+#
+proc execsql2 {sql} {
+ set result {}
+ db eval $sql data {
+ foreach f $data(*) {
+ lappend result $f $data($f)
+ }
+ }
+ return $result
+}
+
+# Use the non-callback API to execute multiple SQL statements
+#
+proc stepsql {dbptr sql} {
+ set sql [string trim $sql]
+ set r 0
+ while {[string length $sql]>0} {
+ if {[catch {sqlite3_prepare $dbptr $sql -1 sqltail} vm]} {
+ return [list 1 $vm]
+ }
+ set sql [string trim $sqltail]
+# while {[sqlite_step $vm N VAL COL]=="SQLITE_ROW"} {
+# foreach v $VAL {lappend r $v}
+# }
+ while {[sqlite3_step $vm]=="SQLITE_ROW"} {
+ for {set i 0} {$i<[sqlite3_data_count $vm]} {incr i} {
+ lappend r [sqlite3_column_text $vm $i]
+ }
+ }
+ if {[catch {sqlite3_finalize $vm} errmsg]} {
+ return [list 1 $errmsg]
+ }
+ }
+ return $r
+}
+
+# Delete a file or directory
+#
+proc forcedelete {filename} {
+ if {[catch {file delete -force $filename}]} {
+ exec rm -rf $filename
+ }
+}
+
+# Do an integrity check of the entire database
+#
+proc integrity_check {name} {
+ ifcapable integrityck {
+ do_test $name {
+ execsql {PRAGMA integrity_check}
+ } {ok}
+ }
+}
+
+proc fix_ifcapable_expr {expr} {
+ set ret ""
+ set state 0
+ for {set i 0} {$i < [string length $expr]} {incr i} {
+ set char [string range $expr $i $i]
+ set newstate [expr {[string is alnum $char] || $char eq "_"}]
+ if {$newstate && !$state} {
+ append ret {$::sqlite_options(}
+ }
+ if {!$newstate && $state} {
+ append ret )
+ }
+ append ret $char
+ set state $newstate
+ }
+ if {$state} {append ret )}
+ return $ret
+}
+
+# Evaluate a boolean expression of capabilities. If true, execute the
+# code. Omit the code if false.
+#
+proc ifcapable {expr code {else ""} {elsecode ""}} {
+ #regsub -all {[a-z_0-9]+} $expr {$::sqlite_options(&)} e2
+ set e2 [fix_ifcapable_expr $expr]
+ if ($e2) {
+ set c [catch {uplevel 1 $code} r]
+ } else {
+ set c [catch {uplevel 1 $elsecode} r]
+ }
+ return -code $c $r
+}
+
+# This proc execs a seperate process that crashes midway through executing
+# the SQL script $sql on database test.db.
+#
+# The crash occurs during a sync() of file $crashfile. When the crash
+# occurs a random subset of all unsynced writes made by the process are
+# written into the files on disk. Argument $crashdelay indicates the
+# number of file syncs to wait before crashing.
+#
+# The return value is a list of two elements. The first element is a
+# boolean, indicating whether or not the process actually crashed or
+# reported some other error. The second element in the returned list is the
+# error message. This is "child process exited abnormally" if the crash
+# occured.
+#
+# crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql
+#
+proc crashsql {args} {
+ if {$::tcl_platform(platform)!="unix"} {
+ error "crashsql should only be used on unix"
+ }
+
+ set blocksize ""
+ set crashdelay 1
+ set prngseed 0
+ set tclbody {}
+ set crashfile ""
+ set dc ""
+ set sql [lindex $args end]
+
+ for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} {
+ set z [lindex $args $ii]
+ set n [string length $z]
+ set z2 [lindex $args [expr $ii+1]]
+
+ if {$n>1 && [string first $z -delay]==0} {set crashdelay $z2} \
+ elseif {$n>1 && [string first $z -seed]==0} {set prngseed $z2} \
+ elseif {$n>1 && [string first $z -file]==0} {set crashfile $z2} \
+ elseif {$n>1 && [string first $z -tclbody]==0} {set tclbody $z2} \
+ elseif {$n>1 && [string first $z -blocksize]==0} {set blocksize "-s $z2" } \
+ elseif {$n>1 && [string first $z -characteristics]==0} {set dc "-c {$z2}" } \
+ else { error "Unrecognized option: $z" }
+ }
+
+ if {$crashfile eq ""} {
+ error "Compulsory option -file missing"
+ }
+
+ set cfile [file join [pwd] $crashfile]
+
+ set f [open crash.tcl w]
+ puts $f "sqlite3_crash_enable 1"
+ puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
+ puts $f "set sqlite_pending_byte $::sqlite_pending_byte"
+ puts $f "sqlite3 db test.db -vfs crash"
+
+ # This block sets the cache size of the main database to 10
+ # pages. This is done in case the build is configured to omit
+ # "PRAGMA cache_size".
+ puts $f {db eval {SELECT * FROM sqlite_master;}}
+ puts $f {set bt [btree_from_db db]}
+ puts $f {btree_set_cache_size $bt 10}
+ if {$prngseed} {
+ set seed [expr {$prngseed%10007+1}]
+ # puts seed=$seed
+ puts $f "db eval {SELECT randomblob($seed)}"
+ }
+
+ if {[string length $tclbody]>0} {
+ puts $f $tclbody
+ }
+ if {[string length $sql]>0} {
+ puts $f "db eval {"
+ puts $f "$sql"
+ puts $f "}"
+ }
+ close $f
+
+ set r [catch {
+ exec [info nameofexec] crash.tcl >@stdout
+ } msg]
+ lappend r $msg
+}
+
+# Usage: do_ioerr_test <test number> <options...>
+#
+# This proc is used to implement test cases that check that IO errors
+# are correctly handled. The first argument, <test number>, is an integer
+# used to name the tests executed by this proc. Options are as follows:
+#
+# -tclprep TCL script to run to prepare test.
+# -sqlprep SQL script to run to prepare test.
+# -tclbody TCL script to run with IO error simulation.
+# -sqlbody TCL script to run with IO error simulation.
+# -exclude List of 'N' values not to test.
+# -erc Use extended result codes
+# -persist Make simulated I/O errors persistent
+# -start Value of 'N' to begin with (default 1)
+#
+# -cksum Boolean. If true, test that the database does
+# not change during the execution of the test case.
+#
+proc do_ioerr_test {testname args} {
+
+ set ::ioerropts(-start) 1
+ set ::ioerropts(-cksum) 0
+ set ::ioerropts(-erc) 0
+ set ::ioerropts(-count) 100000000
+ set ::ioerropts(-persist) 1
+ set ::ioerropts(-ckrefcount) 0
+ set ::ioerropts(-restoreprng) 1
+ array set ::ioerropts $args
+
+ # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
+ # a couple of obscure IO errors that do not return them.
+ set ::ioerropts(-erc) 0
+
+ set ::go 1
+ #reset_prng_state
+ save_prng_state
+ for {set n $::ioerropts(-start)} {$::go && $n<200} {incr n} {
+ set ::TN $n
+ incr ::ioerropts(-count) -1
+ if {$::ioerropts(-count)<0} break
+
+ # Skip this IO error if it was specified with the "-exclude" option.
+ if {[info exists ::ioerropts(-exclude)]} {
+ if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
+ }
+ if {$::ioerropts(-restoreprng)} {
+ restore_prng_state
+ }
+
+ # Delete the files test.db and test2.db, then execute the TCL and
+ # SQL (in that order) to prepare for the test case.
+ do_test $testname.$n.1 {
+ set ::sqlite_io_error_pending 0
+ catch {db close}
+ catch {file delete -force test.db}
+ catch {file delete -force test.db-journal}
+ catch {file delete -force test2.db}
+ catch {file delete -force test2.db-journal}
+ set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
+ sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
+ if {[info exists ::ioerropts(-tclprep)]} {
+ eval $::ioerropts(-tclprep)
+ }
+ if {[info exists ::ioerropts(-sqlprep)]} {
+ execsql $::ioerropts(-sqlprep)
+ }
+ expr 0
+ } {0}
+
+ # Read the 'checksum' of the database.
+ if {$::ioerropts(-cksum)} {
+ set checksum [cksum]
+ }
+
+ # Set the Nth IO error to fail.
+ do_test $testname.$n.2 [subst {
+ set ::sqlite_io_error_persist $::ioerropts(-persist)
+ set ::sqlite_io_error_pending $n
+ }] $n
+
+ # Create a single TCL script from the TCL and SQL specified
+ # as the body of the test.
+ set ::ioerrorbody {}
+ if {[info exists ::ioerropts(-tclbody)]} {
+ append ::ioerrorbody "$::ioerropts(-tclbody)\n"
+ }
+ if {[info exists ::ioerropts(-sqlbody)]} {
+ append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
+ }
+
+ # Execute the TCL Script created in the above block. If
+ # there are at least N IO operations performed by SQLite as
+ # a result of the script, the Nth will fail.
+ do_test $testname.$n.3 {
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_hardhit 0
+ set r [catch $::ioerrorbody msg]
+ set ::errseen $r
+ set rc [sqlite3_errcode $::DB]
+ if {$::ioerropts(-erc)} {
+ # If we are in extended result code mode, make sure all of the
+ # IOERRs we get back really do have their extended code values.
+ # If an extended result code is returned, the sqlite3_errcode
+ # TCLcommand will return a string of the form: SQLITE_IOERR+nnnn
+ # where nnnn is a number
+ if {[regexp {^SQLITE_IOERR} $rc] && ![regexp {IOERR\+\d} $rc]} {
+ return $rc
+ }
+ } else {
+ # If we are not in extended result code mode, make sure no
+ # extended error codes are returned.
+ if {[regexp {\+\d} $rc]} {
+ return $rc
+ }
+ }
+ # The test repeats as long as $::go is non-zero. $::go starts out
+ # as 1. When a test runs to completion without hitting an I/O
+ # error, that means there is no point in continuing with this test
+ # case so set $::go to zero.
+ #
+ if {$::sqlite_io_error_pending>0} {
+ set ::go 0
+ set q 0
+ set ::sqlite_io_error_pending 0
+ } else {
+ set q 1
+ }
+
+ set s [expr $::sqlite_io_error_hit==0]
+ if {$::sqlite_io_error_hit>$::sqlite_io_error_hardhit && $r==0} {
+ set r 1
+ }
+ set ::sqlite_io_error_hit 0
+
+ # One of two things must have happened. either
+ # 1. We never hit the IO error and the SQL returned OK
+ # 2. An IO error was hit and the SQL failed
+ #
+ expr { ($s && !$r && !$q) || (!$s && $r && $q) }
+ } {1}
+
+ set ::sqlite_io_error_hit 0
+ set ::sqlite_io_error_pending 0
+
+ # Check that no page references were leaked. There should be
+ # a single reference if there is still an active transaction,
+ # or zero otherwise.
+ #
+ # UPDATE: If the IO error occurs after a 'BEGIN' but before any
+ # locks are established on database files (i.e. if the error
+ # occurs while attempting to detect a hot-journal file), then
+ # there may 0 page references and an active transaction according
+ # to [sqlite3_get_autocommit].
+ #
+ if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} {
+ do_test $testname.$n.4 {
+ set bt [btree_from_db db]
+ db_enter db
+ array set stats [btree_pager_stats $bt]
+ db_leave db
+ set nRef $stats(ref)
+ expr {$nRef == 0 || ([sqlite3_get_autocommit db]==0 && $nRef == 1)}
+ } {1}
+ }
+
+ # If there is an open database handle and no open transaction,
+ # and the pager is not running in exclusive-locking mode,
+ # check that the pager is in "unlocked" state. Theoretically,
+ # if a call to xUnlock() failed due to an IO error the underlying
+ # file may still be locked.
+ #
+ ifcapable pragma {
+ if { [info commands db] ne ""
+ && $::ioerropts(-ckrefcount)
+ && [db one {pragma locking_mode}] eq "normal"
+ && [sqlite3_get_autocommit db]
+ } {
+ do_test $testname.$n.5 {
+ set bt [btree_from_db db]
+ db_enter db
+ array set stats [btree_pager_stats $bt]
+ db_leave db
+ set stats(state)
+ } 0
+ }
+ }
+
+ # If an IO error occured, then the checksum of the database should
+ # be the same as before the script that caused the IO error was run.
+ #
+ if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
+ do_test $testname.$n.6 {
+ catch {db close}
+ catch {db2 close}
+ set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
+ cksum
+ } $checksum
+ }
+
+ set ::sqlite_io_error_hardhit 0
+ set ::sqlite_io_error_pending 0
+ if {[info exists ::ioerropts(-cleanup)]} {
+ catch $::ioerropts(-cleanup)
+ }
+ }
+ set ::sqlite_io_error_pending 0
+ set ::sqlite_io_error_persist 0
+ unset ::ioerropts
+}
+
+# Return a checksum based on the contents of the main database associated
+# with connection $db
+#
+proc cksum {{db db}} {
+ set txt [$db eval {
+ SELECT name, type, sql FROM sqlite_master order by name
+ }]\n
+ foreach tbl [$db eval {
+ SELECT name FROM sqlite_master WHERE type='table' order by name
+ }] {
+ append txt [$db eval "SELECT * FROM $tbl"]\n
+ }
+ foreach prag {default_synchronous default_cache_size} {
+ append txt $prag-[$db eval "PRAGMA $prag"]\n
+ }
+ set cksum [string length $txt]-[md5 $txt]
+ # puts $cksum-[file size test.db]
+ return $cksum
+}
+
+# Generate a checksum based on the contents of the main and temp tables
+# database $db. If the checksum of two databases is the same, and the
+# integrity-check passes for both, the two databases are identical.
+#
+proc allcksum {{db db}} {
+ set ret [list]
+ ifcapable tempdb {
+ set sql {
+ SELECT name FROM sqlite_master WHERE type = 'table' UNION
+ SELECT name FROM sqlite_temp_master WHERE type = 'table' UNION
+ SELECT 'sqlite_master' UNION
+ SELECT 'sqlite_temp_master' ORDER BY 1
+ }
+ } else {
+ set sql {
+ SELECT name FROM sqlite_master WHERE type = 'table' UNION
+ SELECT 'sqlite_master' ORDER BY 1
+ }
+ }
+ set tbllist [$db eval $sql]
+ set txt {}
+ foreach tbl $tbllist {
+ append txt [$db eval "SELECT * FROM $tbl"]
+ }
+ foreach prag {default_cache_size} {
+ append txt $prag-[$db eval "PRAGMA $prag"]\n
+ }
+ # puts txt=$txt
+ return [md5 $txt]
+}
+
+proc memdebug_log_sql {{filename mallocs.sql}} {
+
+ set data [sqlite3_memdebug_log dump]
+ set nFrame [expr [llength [lindex $data 0]]-2]
+ if {$nFrame < 0} { return "" }
+
+ set database temp
+
+ set tbl "CREATE TABLE ${database}.malloc(nCall, nByte"
+ for {set ii 1} {$ii <= $nFrame} {incr ii} {
+ append tbl ", f${ii}"
+ }
+ append tbl ");\n"
+
+ set sql ""
+ foreach e $data {
+ append sql "INSERT INTO ${database}.malloc VALUES([join $e ,]);\n"
+ foreach f [lrange $e 2 end] {
+ set frames($f) 1
+ }
+ }
+
+ set tbl2 "CREATE TABLE ${database}.frame(frame INTEGER PRIMARY KEY, line);\n"
+ set tbl3 "CREATE TABLE ${database}.file(name PRIMARY KEY, content);\n"
+
+ foreach f [array names frames] {
+ set addr [format %x $f]
+ set cmd "addr2line -e [info nameofexec] $addr"
+ set line [eval exec $cmd]
+ append sql "INSERT INTO ${database}.frame VALUES($f, '$line');\n"
+
+ set file [lindex [split $line :] 0]
+ set files($file) 1
+ }
+
+ foreach f [array names files] {
+ set contents ""
+ catch {
+ set fd [open $f]
+ set contents [read $fd]
+ close $fd
+ }
+ set contents [string map {' ''} $contents]
+ append sql "INSERT INTO ${database}.file VALUES('$f', '$contents');\n"
+ }
+
+ set fd [open $filename w]
+ puts $fd "BEGIN; ${tbl}${tbl2}${tbl3}${sql} ; COMMIT;"
+ close $fd
+}
+
+# Copy file $from into $to. This is used because some versions of
+# TCL for windows (notably the 8.4.1 binary package shipped with the
+# current mingw release) have a broken "file copy" command.
+#
+proc copy_file {from to} {
+ if {$::tcl_platform(platform)=="unix"} {
+ file copy -force $from $to
+ } else {
+ set f [open $from]
+ fconfigure $f -translation binary
+ set t [open $to w]
+ fconfigure $t -translation binary
+ puts -nonewline $t [read $f [file size $from]]
+ close $t
+ close $f
+ }
+}
+
+# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
+# to non-zero, then set the global variable $AUTOVACUUM to 1.
+set AUTOVACUUM $sqlite_options(default_autovacuum)
diff --git a/third_party/sqlite/test/thread001.test b/third_party/sqlite/test/thread001.test
new file mode 100755
index 0000000..8a2cbb5
--- /dev/null
+++ b/third_party/sqlite/test/thread001.test
@@ -0,0 +1,138 @@
+# 2007 September 7
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: thread001.test,v 1.5 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+
+source $testdir/tester.tcl
+source $testdir/thread_common.tcl
+if {[info commands sqlthread] eq ""} {
+ return
+}
+
+set ::NTHREAD 10
+
+# Run this test three times:
+#
+# 1) All threads use the same database handle.
+# 2) All threads use their own database handles.
+# 3) All threads use their own database handles, shared-cache is enabled.
+#
+foreach {tn same_db shared_cache} [list \
+ 1 1 0 \
+ 2 0 0 \
+ 3 0 1 \
+] {
+ # Empty the database.
+ #
+ catchsql { DROP TABLE ab; }
+
+ do_test thread001.$tn.0 {
+ db close
+ sqlite3_enable_shared_cache $shared_cache
+ sqlite3_enable_shared_cache $shared_cache
+ } $shared_cache
+ sqlite3 db test.db
+
+ set dbconfig ""
+ if {$same_db} {
+ set dbconfig [list set ::DB [sqlite3_connection_pointer db]]
+ }
+
+ # Set up a database and a schema. The database contains a single
+ # table with two columns. The first column ("a") is an INTEGER PRIMARY
+ # KEY. The second contains the md5sum of all rows in the table with
+ # a smaller value stored in column "a".
+ #
+ do_test thread001.$tn.1 {
+ execsql {
+ CREATE TABLE ab(a INTEGER PRIMARY KEY, b);
+ CREATE INDEX ab_i ON ab(b);
+ INSERT INTO ab SELECT NULL, md5sum(a, b) FROM ab;
+ SELECT count(*) FROM ab;
+ }
+ } {1}
+ do_test thread001.$tn.2 {
+ execsql {
+ SELECT
+ (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
+ (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
+ }
+ } {1}
+ do_test thread001.$tn.3 {
+ execsql { PRAGMA integrity_check }
+ } {ok}
+
+ set thread_program {
+ set needToClose 0
+ if {![info exists ::DB]} {
+ set ::DB [sqlthread open test.db]
+ set needToClose 1
+ }
+
+ for {set i 0} {$i < 100} {incr i} {
+ # Test that the invariant is true.
+ do_test t1 {
+ execsql {
+ SELECT
+ (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
+ (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
+ }
+ } {1}
+
+ # Add another row to the database.
+ execsql { INSERT INTO ab SELECT NULL, md5sum(a, b) FROM ab }
+ }
+
+ if {$needToClose} {
+ sqlite3_close $::DB
+ }
+
+ list OK
+ }
+
+ # Kick off $::NTHREAD threads:
+ #
+ array unset finished
+ for {set i 0} {$i < $::NTHREAD} {incr i} {
+ thread_spawn finished($i) $dbconfig $thread_procs $thread_program
+ }
+
+ # Wait for all threads to finish, then check they all returned "OK".
+ #
+ for {set i 0} {$i < $::NTHREAD} {incr i} {
+ if {![info exists finished($i)]} {
+ vwait finished($i)
+ }
+ do_test thread001.$tn.4.$i {
+ set ::finished($i)
+ } OK
+ }
+
+ # Check the database still looks Ok.
+ #
+ do_test thread001.$tn.5 {
+ execsql { SELECT count(*) FROM ab; }
+ } [expr {1 + $::NTHREAD*100}]
+ do_test thread001.$tn.6 {
+ execsql {
+ SELECT
+ (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
+ (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
+ }
+ } {1}
+ do_test thread001.$tn.7 {
+ execsql { PRAGMA integrity_check }
+ } {ok}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/thread002.test b/third_party/sqlite/test/thread002.test
new file mode 100755
index 0000000..c31e3c4
--- /dev/null
+++ b/third_party/sqlite/test/thread002.test
@@ -0,0 +1,109 @@
+# 2007 September 10
+#
+# 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 test attempts to deadlock SQLite in shared-cache mode.
+#
+#
+# $Id: thread002.test,v 1.3 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+
+source $testdir/tester.tcl
+source $testdir/thread_common.tcl
+if {[info commands sqlthread] eq ""} {
+ finish_test
+ return
+}
+ifcapable !attach {
+ finish_test
+ return
+}
+
+db close
+sqlite3_enable_shared_cache 1
+
+set ::NTHREAD 10
+
+do_test thread002.1 {
+ # Create 3 databases with identical schemas:
+ for {set ii 0} {$ii < 3} {incr ii} {
+ file delete -force test${ii}.db
+ sqlite3 db test${ii}.db
+ execsql {
+ CREATE TABLE t1(k, v);
+ CREATE INDEX t1_i ON t1(v);
+ INSERT INTO t1(v) VALUES(1.0);
+ }
+ db close
+ }
+} {}
+
+set thread_program {
+ set ::DB [sqlite3_open test.db]
+ for {set ii 1} {$ii <= 3} {incr ii} {
+ set T [lindex $order [expr $ii-1]]
+ execsql "ATTACH 'test${T}.db' AS aux${ii}"
+ }
+
+ for {set ii 0} {$ii < 100} {incr ii} {
+ execsql { SELECT * FROM aux1.t1 }
+ execsql { INSERT INTO aux1.t1(v) SELECT sum(v) FROM aux2.t1 }
+
+ execsql { SELECT * FROM aux2.t1 }
+ execsql { INSERT INTO aux2.t1(v) SELECT sum(v) FROM aux3.t1 }
+
+ execsql { SELECT * FROM aux3.t1 }
+ execsql { INSERT INTO aux3.t1(v) SELECT sum(v) FROM aux1.t1 }
+
+ execsql { CREATE TABLE aux1.t2(a,b) }
+ execsql { DROP TABLE aux1.t2 }
+
+ # if {($ii%10)==0} {puts -nonewline . ; flush stdout}
+ puts -nonewline . ; flush stdout
+ }
+
+ sqlite3_close $::DB
+ list OK
+}
+
+set order_list [list {0 1 2} {0 2 1} {1 0 2} {1 2 0} {2 0 1} {2 1 0}]
+
+array unset finished
+for {set ii 0} {$ii < $::NTHREAD} {incr ii} {
+ set order [lindex $order_list [expr $ii%6]]
+ thread_spawn finished($ii) $thread_procs "set order {$order}" $thread_program
+}
+
+# Wait for all threads to finish, then check they all returned "OK".
+#
+for {set i 0} {$i < $::NTHREAD} {incr i} {
+ if {![info exists finished($i)]} {
+ vwait finished($i)
+ }
+ do_test thread001.2.$i {
+ set ::finished($i)
+ } OK
+}
+
+# Check all three databases are Ok.
+for {set ii 0} {$ii < 3} {incr ii} {
+ do_test thread002.3.$ii {
+ sqlite3 db test${ii}.db
+ set res [list \
+ [execsql {SELECT count(*) FROM t1}] \
+ [execsql {PRAGMA integrity_check}] \
+ ]
+ db close
+ set res
+ } [list [expr 1 + $::NTHREAD*100] ok]
+}
+
+finish_test
diff --git a/third_party/sqlite/test/thread1.test b/third_party/sqlite/test/thread1.test
new file mode 100755
index 0000000..c50d245
--- /dev/null
+++ b/third_party/sqlite/test/thread1.test
@@ -0,0 +1,172 @@
+# 2003 December 18
+#
+# 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 multithreading behavior
+#
+# $Id: thread1.test,v 1.7 2004/06/19 00:16:31 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this whole file if the thread testing code is not enabled
+#
+if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} {
+ finish_test
+ return
+}
+
+# Create some data to work with
+#
+do_test thread1-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,'abcdefgh');
+ INSERT INTO t1 SELECT a+1, b||b FROM t1;
+ INSERT INTO t1 SELECT a+2, b||b FROM t1;
+ INSERT INTO t1 SELECT a+4, b||b FROM t1;
+ SELECT count(*), max(length(b)) FROM t1;
+ }
+} {8 64}
+
+# Interleave two threads on read access. Then make sure a third
+# thread can write the database. In other words:
+#
+# read-lock A
+# read-lock B
+# unlock A
+# unlock B
+# write-lock C
+#
+# At one point, the write-lock of C would fail on Linux.
+#
+do_test thread1-1.2 {
+ thread_create A test.db
+ thread_create B test.db
+ thread_create C test.db
+ thread_compile A {SELECT a FROM t1}
+ thread_step A
+ thread_result A
+} SQLITE_ROW
+do_test thread1-1.3 {
+ thread_argc A
+} 1
+do_test thread1-1.4 {
+ thread_argv A 0
+} 1
+do_test thread1-1.5 {
+ thread_compile B {SELECT b FROM t1}
+ thread_step B
+ thread_result B
+} SQLITE_ROW
+do_test thread1-1.6 {
+ thread_argc B
+} 1
+do_test thread1-1.7 {
+ thread_argv B 0
+} abcdefgh
+do_test thread1-1.8 {
+ thread_finalize A
+ thread_result A
+} SQLITE_OK
+do_test thread1-1.9 {
+ thread_finalize B
+ thread_result B
+} SQLITE_OK
+do_test thread1-1.10 {
+ thread_compile C {CREATE TABLE t2(x,y)}
+ thread_step C
+ thread_result C
+} SQLITE_DONE
+do_test thread1-1.11 {
+ thread_finalize C
+ thread_result C
+} SQLITE_OK
+do_test thread1-1.12 {
+ catchsql {SELECT name FROM sqlite_master}
+ execsql {SELECT name FROM sqlite_master}
+} {t1 t2}
+
+
+#
+# The following tests - thread1-2.* - test the following scenario:
+#
+# 1: Read-lock thread A
+# 2: Read-lock thread B
+# 3: Attempt to write in thread C -> SQLITE_BUSY
+# 4: Check db write failed from main thread.
+# 5: Unlock from thread A.
+# 6: Attempt to write in thread C -> SQLITE_BUSY
+# 7: Check db write failed from main thread.
+# 8: Unlock from thread B.
+# 9: Attempt to write in thread C -> SQLITE_DONE
+# 10: Finalize the write from thread C
+# 11: Check db write succeeded from main thread.
+#
+do_test thread1-2.1 {
+ thread_halt *
+ thread_create A test.db
+ thread_compile A {SELECT a FROM t1}
+ thread_step A
+ thread_result A
+} SQLITE_ROW
+do_test thread1-2.2 {
+ thread_create B test.db
+ thread_compile B {SELECT b FROM t1}
+ thread_step B
+ thread_result B
+} SQLITE_ROW
+do_test thread1-2.3 {
+ thread_create C test.db
+ thread_compile C {INSERT INTO t2 VALUES(98,99)}
+ thread_step C
+ thread_result C
+ thread_finalize C
+ thread_result C
+} SQLITE_BUSY
+
+do_test thread1-2.4 {
+ execsql {SELECT * FROM t2}
+} {}
+
+do_test thread1-2.5 {
+ thread_finalize A
+ thread_result A
+} SQLITE_OK
+do_test thread1-2.6 {
+ thread_compile C {INSERT INTO t2 VALUES(98,99)}
+ thread_step C
+ thread_result C
+ thread_finalize C
+ thread_result C
+} SQLITE_BUSY
+do_test thread1-2.7 {
+ execsql {SELECT * FROM t2}
+} {}
+do_test thread1-2.8 {
+ thread_finalize B
+ thread_result B
+} SQLITE_OK
+do_test thread1-2.9 {
+ thread_compile C {INSERT INTO t2 VALUES(98,99)}
+ thread_step C
+ thread_result C
+} SQLITE_DONE
+do_test thread1-2.10 {
+ thread_finalize C
+ thread_result C
+} SQLITE_OK
+do_test thread1-2.11 {
+ execsql {SELECT * FROM t2}
+} {98 99}
+
+thread_halt *
+finish_test
diff --git a/third_party/sqlite/test/thread2.test b/third_party/sqlite/test/thread2.test
new file mode 100755
index 0000000..1d9a208
--- /dev/null
+++ b/third_party/sqlite/test/thread2.test
@@ -0,0 +1,246 @@
+# 2006 January 14
+#
+# 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 multithreading behavior
+#
+# $Id: thread2.test,v 1.2 2006/01/18 18:33:42 danielk1977 Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This file swaps database connections between threads. This
+# is illegal if memory-management is enabled, so skip this file
+# in that case.
+ifcapable memorymanage {
+ finish_test
+ return
+}
+
+
+# Skip this whole file if the thread testing code is not enabled
+#
+if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} {
+ finish_test
+ return
+}
+if {![info exists threadsOverrideEachOthersLocks]} {
+ finish_test
+ return
+}
+
+# Create some data to work with
+#
+do_test thread1-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,'abcdefgh');
+ INSERT INTO t1 SELECT a+1, b||b FROM t1;
+ INSERT INTO t1 SELECT a+2, b||b FROM t1;
+ INSERT INTO t1 SELECT a+4, b||b FROM t1;
+ SELECT count(*), max(length(b)) FROM t1;
+ }
+} {8 64}
+
+# Use the thread_swap command to move the database connections between
+# threads, then verify that they still work.
+#
+do_test thread2-1.2 {
+ db close
+ thread_create A test.db
+ thread_create B test.db
+ thread_swap A B
+ thread_compile A {SELECT a FROM t1 LIMIT 1}
+ thread_result A
+} {SQLITE_OK}
+do_test thread2-1.3 {
+ thread_step A
+ thread_result A
+} {SQLITE_ROW}
+do_test thread2-1.4 {
+ thread_argv A 0
+} {1}
+do_test thread2-1.5 {
+ thread_finalize A
+ thread_result A
+} {SQLITE_OK}
+do_test thread2-1.6 {
+ thread_compile B {SELECT a FROM t1 LIMIT 1}
+ thread_result B
+} {SQLITE_OK}
+do_test thread2-1.7 {
+ thread_step B
+ thread_result B
+} {SQLITE_ROW}
+do_test thread2-1.8 {
+ thread_argv B 0
+} {1}
+do_test thread2-1.9 {
+ thread_finalize B
+ thread_result B
+} {SQLITE_OK}
+
+# Swap them again.
+#
+do_test thread2-2.2 {
+ thread_swap A B
+ thread_compile A {SELECT a FROM t1 LIMIT 1}
+ thread_result A
+} {SQLITE_OK}
+do_test thread2-2.3 {
+ thread_step A
+ thread_result A
+} {SQLITE_ROW}
+do_test thread2-2.4 {
+ thread_argv A 0
+} {1}
+do_test thread2-2.5 {
+ thread_finalize A
+ thread_result A
+} {SQLITE_OK}
+do_test thread2-2.6 {
+ thread_compile B {SELECT a FROM t1 LIMIT 1}
+ thread_result B
+} {SQLITE_OK}
+do_test thread2-2.7 {
+ thread_step B
+ thread_result B
+} {SQLITE_ROW}
+do_test thread2-2.8 {
+ thread_argv B 0
+} {1}
+do_test thread2-2.9 {
+ thread_finalize B
+ thread_result B
+} {SQLITE_OK}
+thread_halt A
+thread_halt B
+
+# Save the original (correct) value of threadsOverrideEachOthersLocks
+# so that it can be restored. If this value is left set incorrectly, lots
+# of things will go wrong in future tests.
+#
+set orig_threadOverride $threadsOverrideEachOthersLocks
+
+# Pretend we are on a system (like RedHat9) were threads do not
+# override each others locks.
+#
+set threadsOverrideEachOthersLocks 0
+
+# Verify that we can move database connections between threads as
+# long as no locks are held.
+#
+do_test thread2-3.1 {
+ thread_create A test.db
+ set DB [thread_db_get A]
+ thread_halt A
+} {}
+do_test thread2-3.2 {
+ set STMT [sqlite3_prepare $DB {SELECT a FROM t1 LIMIT 1} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test thread2-3.3 {
+ sqlite3_column_int $STMT 0
+} 1
+do_test thread2-3.4 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+do_test thread2-3.5 {
+ set STMT [sqlite3_prepare $DB {SELECT max(a) FROM t1} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test thread2-3.6 {
+ sqlite3_column_int $STMT 0
+} 8
+do_test thread2-3.7 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+do_test thread2-3.8 {
+ sqlite3_close $DB
+} {SQLITE_OK}
+
+do_test thread2-3.10 {
+ thread_create A test.db
+ thread_compile A {SELECT a FROM t1 LIMIT 1}
+ thread_step A
+ thread_finalize A
+ set DB [thread_db_get A]
+ thread_halt A
+} {}
+do_test thread2-3.11 {
+ set STMT [sqlite3_prepare $DB {SELECT a FROM t1 LIMIT 1} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test thread2-3.12 {
+ sqlite3_column_int $STMT 0
+} 1
+do_test thread2-3.13 {
+ sqlite3_finalize $STMT
+} SQLITE_OK
+do_test thread2-3.14 {
+ sqlite3_close $DB
+} SQLITE_OK
+
+do_test thread2-3.20 {
+ thread_create A test.db
+ thread_compile A {SELECT a FROM t1 LIMIT 3}
+ thread_step A
+ set STMT [thread_stmt_get A]
+ set DB [thread_db_get A]
+ thread_halt A
+} {}
+do_test thread2-3.21 {
+ sqlite3_step $STMT
+} SQLITE_ROW
+do_test thread2-3.22 {
+ sqlite3_column_int $STMT 0
+} 2
+do_test thread2-3.23 {
+ # The unlock fails here. But because we never check the return
+ # code from sqlite3OsUnlock (because we cannot do anything about it
+ # if it fails) we do not realize that an error has occurred.
+ sqlite3_finalize $STMT
+} SQLITE_OK
+do_test thread2-3.25 {
+ sqlite3_close $DB
+} SQLITE_OK
+
+do_test thread2-3.30 {
+ thread_create A test.db
+ thread_compile A {BEGIN}
+ thread_step A
+ thread_finalize A
+ thread_compile A {SELECT a FROM t1 LIMIT 1}
+ thread_step A
+ thread_finalize A
+ set DB [thread_db_get A]
+ thread_halt A
+} {}
+do_test thread2-3.31 {
+ set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(99,'error')} -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ERROR
+do_test thread2-3.32 {
+ sqlite3_finalize $STMT
+} SQLITE_MISUSE
+do_test thread2-3.33 {
+ sqlite3_close $DB
+} SQLITE_OK
+
+# VERY important to set the override flag back to its true value.
+#
+set threadsOverrideEachOthersLocks $orig_threadOverride
+
+# Also important to halt the worker threads, which are using spin
+# locks and eating away CPU cycles.
+#
+thread_halt *
+finish_test
diff --git a/third_party/sqlite/test/thread_common.tcl b/third_party/sqlite/test/thread_common.tcl
new file mode 100755
index 0000000..5e34b17
--- /dev/null
+++ b/third_party/sqlite/test/thread_common.tcl
@@ -0,0 +1,88 @@
+# 2007 September 10
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: thread_common.tcl,v 1.2 2007/09/10 10:53:02 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {[info commands sqlthread] eq ""} {
+ puts -nonewline "Skipping thread-safety tests - "
+ puts " not running a threadsafe sqlite/tcl build"
+ puts -nonewline "Both SQLITE_THREADSAFE and TCL_THREADS must be defined when"
+ puts " building testfixture"
+ finish_test
+ return
+}
+
+# The following script is sourced by every thread spawned using
+# [sqlthread spawn]:
+set thread_procs {
+
+ # Execute the supplied SQL using database handle $::DB.
+ #
+ proc execsql {sql} {
+
+ set rc SQLITE_LOCKED
+ while {$rc eq "SQLITE_LOCKED"
+ || $rc eq "SQLITE_BUSY"
+ || $rc eq "SQLITE_SCHEMA"} {
+ set res [list]
+
+ set err [catch {
+ set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 dummy_tail]
+ } msg]
+
+ if {$err == 0} {
+ while {[set rc [sqlite3_step $::STMT]] eq "SQLITE_ROW"} {
+ for {set i 0} {$i < [sqlite3_column_count $::STMT]} {incr i} {
+ lappend res [sqlite3_column_text $::STMT 0]
+ }
+ }
+ set rc [sqlite3_finalize $::STMT]
+ } else {
+ if {[string first (6) $msg]} {
+ set rc SQLITE_LOCKED
+ } else {
+ set rc SQLITE_ERROR
+ }
+ }
+
+ if {[string first locked [sqlite3_errmsg $::DB]]>=0} {
+ set rc SQLITE_LOCKED
+ }
+
+ if {$rc eq "SQLITE_LOCKED" || $rc eq "SQLITE_BUSY"} {
+ #puts -nonewline "([sqlthread id] $rc)"
+ #flush stdout
+ after 20
+ }
+ }
+
+ if {$rc ne "SQLITE_OK"} {
+ error "$rc - [sqlite3_errmsg $::DB]"
+ }
+ set res
+ }
+
+ proc do_test {name script result} {
+ set res [eval $script]
+ if {$res ne $result} {
+ error "$name failed: expected \"$result\" got \"$res\""
+ }
+ }
+}
+
+proc thread_spawn {varname args} {
+ sqlthread spawn $varname [join $args ;]
+}
+
+return 0
diff --git a/third_party/sqlite/test/threadtest1.c b/third_party/sqlite/test/threadtest1.c
new file mode 100755
index 0000000..56fcce3
--- /dev/null
+++ b/third_party/sqlite/test/threadtest1.c
@@ -0,0 +1,289 @@
+/*
+** 2002 January 15
+**
+** 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 a simple standalone program used to test whether
+** or not the SQLite library is threadsafe.
+**
+** Testing the thread safety of SQLite is difficult because there are very
+** few places in the code that are even potentially unsafe, and those
+** places execute for very short periods of time. So even if the library
+** is compiled with its mutexes disabled, it is likely to work correctly
+** in a multi-threaded program most of the time.
+**
+** This file is NOT part of the standard SQLite library. It is used for
+** testing only.
+*/
+#include "sqlite.h"
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+** Enable for tracing
+*/
+static int verbose = 0;
+
+/*
+** Come here to die.
+*/
+static void Exit(int rc){
+ exit(rc);
+}
+
+extern char *sqlite3_mprintf(const char *zFormat, ...);
+extern char *sqlite3_vmprintf(const char *zFormat, va_list);
+
+/*
+** When a lock occurs, yield.
+*/
+static int db_is_locked(void *NotUsed, int iCount){
+ /* sched_yield(); */
+ if( verbose ) printf("BUSY %s #%d\n", (char*)NotUsed, iCount);
+ usleep(100);
+ return iCount<25;
+}
+
+/*
+** Used to accumulate query results by db_query()
+*/
+struct QueryResult {
+ const char *zFile; /* Filename - used for error reporting */
+ int nElem; /* Number of used entries in azElem[] */
+ int nAlloc; /* Number of slots allocated for azElem[] */
+ char **azElem; /* The result of the query */
+};
+
+/*
+** The callback function for db_query
+*/
+static int db_query_callback(
+ void *pUser, /* Pointer to the QueryResult structure */
+ int nArg, /* Number of columns in this result row */
+ char **azArg, /* Text of data in all columns */
+ char **NotUsed /* Names of the columns */
+){
+ struct QueryResult *pResult = (struct QueryResult*)pUser;
+ int i;
+ if( pResult->nElem + nArg >= pResult->nAlloc ){
+ if( pResult->nAlloc==0 ){
+ pResult->nAlloc = nArg+1;
+ }else{
+ pResult->nAlloc = pResult->nAlloc*2 + nArg + 1;
+ }
+ pResult->azElem = realloc( pResult->azElem, pResult->nAlloc*sizeof(char*));
+ if( pResult->azElem==0 ){
+ fprintf(stdout,"%s: malloc failed\n", pResult->zFile);
+ return 1;
+ }
+ }
+ if( azArg==0 ) return 0;
+ for(i=0; i<nArg; i++){
+ pResult->azElem[pResult->nElem++] =
+ sqlite3_mprintf("%s",azArg[i] ? azArg[i] : "");
+ }
+ return 0;
+}
+
+/*
+** Execute a query against the database. NULL values are returned
+** as an empty string. The list is terminated by a single NULL pointer.
+*/
+char **db_query(sqlite *db, const char *zFile, const char *zFormat, ...){
+ char *zSql;
+ int rc;
+ char *zErrMsg = 0;
+ va_list ap;
+ struct QueryResult sResult;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ memset(&sResult, 0, sizeof(sResult));
+ sResult.zFile = zFile;
+ if( verbose ) printf("QUERY %s: %s\n", zFile, zSql);
+ rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
+ if( rc==SQLITE_SCHEMA ){
+ if( zErrMsg ) free(zErrMsg);
+ rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
+ }
+ if( verbose ) printf("DONE %s %s\n", zFile, zSql);
+ if( zErrMsg ){
+ fprintf(stdout,"%s: query failed: %s - %s\n", zFile, zSql, zErrMsg);
+ free(zErrMsg);
+ free(zSql);
+ Exit(1);
+ }
+ sqlite3_free(zSql);
+ if( sResult.azElem==0 ){
+ db_query_callback(&sResult, 0, 0, 0);
+ }
+ sResult.azElem[sResult.nElem] = 0;
+ return sResult.azElem;
+}
+
+/*
+** Execute an SQL statement.
+*/
+void db_execute(sqlite *db, const char *zFile, const char *zFormat, ...){
+ char *zSql;
+ int rc;
+ char *zErrMsg = 0;
+ va_list ap;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ if( verbose ) printf("EXEC %s: %s\n", zFile, zSql);
+ do{
+ rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
+ }while( rc==SQLITE_BUSY );
+ if( verbose ) printf("DONE %s: %s\n", zFile, zSql);
+ if( zErrMsg ){
+ fprintf(stdout,"%s: command failed: %s - %s\n", zFile, zSql, zErrMsg);
+ free(zErrMsg);
+ sqlite3_free(zSql);
+ Exit(1);
+ }
+ sqlite3_free(zSql);
+}
+
+/*
+** Free the results of a db_query() call.
+*/
+void db_query_free(char **az){
+ int i;
+ for(i=0; az[i]; i++){
+ sqlite3_free(az[i]);
+ }
+ free(az);
+}
+
+/*
+** Check results
+*/
+void db_check(const char *zFile, const char *zMsg, char **az, ...){
+ va_list ap;
+ int i;
+ char *z;
+ va_start(ap, az);
+ for(i=0; (z = va_arg(ap, char*))!=0; i++){
+ if( az[i]==0 || strcmp(az[i],z)!=0 ){
+ fprintf(stdout,"%s: %s: bad result in column %d: %s\n",
+ zFile, zMsg, i+1, az[i]);
+ db_query_free(az);
+ Exit(1);
+ }
+ }
+ va_end(ap);
+ db_query_free(az);
+}
+
+pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t sig = PTHREAD_COND_INITIALIZER;
+int thread_cnt = 0;
+
+static void *worker_bee(void *pArg){
+ const char *zFilename = (char*)pArg;
+ char *azErr;
+ int i, cnt;
+ int t = atoi(zFilename);
+ char **az;
+ sqlite *db;
+
+ pthread_mutex_lock(&lock);
+ thread_cnt++;
+ pthread_mutex_unlock(&lock);
+ printf("%s: START\n", zFilename);
+ fflush(stdout);
+ for(cnt=0; cnt<10; cnt++){
+ sqlite3_open(&zFilename[2], &db);
+ if( db==0 ){
+ fprintf(stdout,"%s: can't open\n", zFilename);
+ Exit(1);
+ }
+ sqlite3_busy_handler(db, db_is_locked, zFilename);
+ db_execute(db, zFilename, "CREATE TABLE t%d(a,b,c);", t);
+ for(i=1; i<=100; i++){
+ db_execute(db, zFilename, "INSERT INTO t%d VALUES(%d,%d,%d);",
+ t, i, i*2, i*i);
+ }
+ az = db_query(db, zFilename, "SELECT count(*) FROM t%d", t);
+ db_check(zFilename, "tX size", az, "100", 0);
+ az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
+ db_check(zFilename, "tX avg", az, "101", 0);
+ db_execute(db, zFilename, "DELETE FROM t%d WHERE a>50", t);
+ az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
+ db_check(zFilename, "tX avg2", az, "51", 0);
+ for(i=1; i<=50; i++){
+ char z1[30], z2[30];
+ az = db_query(db, zFilename, "SELECT b, c FROM t%d WHERE a=%d", t, i);
+ sprintf(z1, "%d", i*2);
+ sprintf(z2, "%d", i*i);
+ db_check(zFilename, "readback", az, z1, z2, 0);
+ }
+ db_execute(db, zFilename, "DROP TABLE t%d;", t);
+ sqlite3_close(db);
+ }
+ printf("%s: END\n", zFilename);
+ /* unlink(zFilename); */
+ fflush(stdout);
+ pthread_mutex_lock(&lock);
+ thread_cnt--;
+ if( thread_cnt<=0 ){
+ pthread_cond_signal(&sig);
+ }
+ pthread_mutex_unlock(&lock);
+ return 0;
+}
+
+int main(int argc, char **argv){
+ char *zFile;
+ int i, n;
+ pthread_t id;
+ if( argc>2 && strcmp(argv[1], "-v")==0 ){
+ verbose = 1;
+ argc--;
+ argv++;
+ }
+ if( argc<2 || (n=atoi(argv[1]))<1 ) n = 10;
+ for(i=0; i<n; i++){
+ char zBuf[200];
+ sprintf(zBuf, "testdb-%d", (i+1)/2);
+ unlink(zBuf);
+ }
+ for(i=0; i<n; i++){
+ zFile = sqlite3_mprintf("%d.testdb-%d", i%2+1, (i+2)/2);
+ if( (i%2)==0 ){
+ /* Remove both the database file and any old journal for the file
+ ** being used by this thread and the next one. */
+ char *zDb = &zFile[2];
+ char *zJournal = sqlite3_mprintf("%s-journal", zDb);
+ unlink(zDb);
+ unlink(zJournal);
+ free(zJournal);
+ }
+
+ pthread_create(&id, 0, worker_bee, (void*)zFile);
+ pthread_detach(id);
+ }
+ pthread_mutex_lock(&lock);
+ while( thread_cnt>0 ){
+ pthread_cond_wait(&sig, &lock);
+ }
+ pthread_mutex_unlock(&lock);
+ for(i=0; i<n; i++){
+ char zBuf[200];
+ sprintf(zBuf, "testdb-%d", (i+1)/2);
+ unlink(zBuf);
+ }
+ return 0;
+}
diff --git a/third_party/sqlite/test/threadtest2.c b/third_party/sqlite/test/threadtest2.c
new file mode 100755
index 0000000..25b1d90
--- /dev/null
+++ b/third_party/sqlite/test/threadtest2.c
@@ -0,0 +1,133 @@
+/*
+** 2004 January 13
+**
+** 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 a simple standalone program used to test whether
+** or not the SQLite library is threadsafe.
+**
+** This file is NOT part of the standard SQLite library. It is used for
+** testing only.
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include "sqlite.h"
+
+/*
+** Name of the database
+*/
+#define DB_FILE "test.db"
+
+/*
+** When this variable becomes non-zero, all threads stop
+** what they are doing.
+*/
+volatile int all_stop = 0;
+
+/*
+** Callback from the integrity check. If the result is anything other
+** than "ok" it means the integrity check has failed. Set the "all_stop"
+** global variable to stop all other activity. Print the error message
+** or print OK if the string "ok" is seen.
+*/
+int check_callback(void *pid, int argc, char **argv, char **notUsed2){
+ int id = (int)pid;
+ if( strcmp(argv[0],"ok") ){
+ all_stop = 1;
+ fprintf(stderr,"id: %s\n", id, argv[0]);
+ }else{
+ /* fprintf(stderr,"%d: OK\n", id); */
+ }
+ return 0;
+}
+
+/*
+** Do an integrity check on the database. If the first integrity check
+** fails, try it a second time.
+*/
+int integrity_check(sqlite *db, int id){
+ int rc;
+ if( all_stop ) return 0;
+ /* fprintf(stderr,"%d: CHECK\n", id); */
+ rc = sqlite3_exec(db, "pragma integrity_check", check_callback, 0, 0);
+ if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
+ fprintf(stderr,"%d, Integrity check returns %d\n", id, rc);
+ }
+ if( all_stop ){
+ sqlite3_exec(db, "pragma integrity_check", check_callback, 0, 0);
+ }
+ return 0;
+}
+
+/*
+** This is the worker thread
+*/
+void *worker(void *workerArg){
+ sqlite *db;
+ int id = (int)workerArg;
+ int rc;
+ int cnt = 0;
+ fprintf(stderr, "Starting worker %d\n", id);
+ while( !all_stop && cnt++<10000 ){
+ if( cnt%100==0 ) printf("%d: %d\n", id, cnt);
+ while( (sqlite3_open(DB_FILE, &db))!=SQLITE_OK ) sched_yield();
+ sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
+ /* integrity_check(db, id); */
+ if( all_stop ){ sqlite3_close(db); break; }
+ /* fprintf(stderr, "%d: BEGIN\n", id); */
+ rc = sqlite3_exec(db, "INSERT INTO t1 VALUES('bogus data')", 0, 0, 0);
+ /* fprintf(stderr, "%d: END rc=%d\n", id, rc); */
+ sqlite3_close(db);
+ }
+ fprintf(stderr, "Worker %d finished\n", id);
+ return 0;
+}
+
+/*
+** Initialize the database and start the threads
+*/
+int main(int argc, char **argv){
+ sqlite *db;
+ int i, rc;
+ pthread_t aThread[5];
+
+ if( strcmp(DB_FILE,":memory:") ){
+ char *zJournal = sqlite3_mprintf("%s-journal", DB_FILE);
+ unlink(DB_FILE);
+ unlink(zJournal);
+ sqlite3_free(zJournal);
+ }
+ sqlite3_open(DB_FILE, &db);
+ if( db==0 ){
+ fprintf(stderr,"unable to initialize database\n");
+ exit(1);
+ }
+ rc = sqlite3_exec(db, "CREATE TABLE t1(x);", 0,0,0);
+ if( rc ){
+ fprintf(stderr,"cannot create table t1: %d\n", rc);
+ exit(1);
+ }
+ sqlite3_close(db);
+ for(i=0; i<sizeof(aThread)/sizeof(aThread[0]); i++){
+ pthread_create(&aThread[i], 0, worker, (void*)i);
+ }
+ for(i=0; i<sizeof(aThread)/sizeof(aThread[i]); i++){
+ pthread_join(aThread[i], 0);
+ }
+ if( !all_stop ){
+ printf("Everything seems ok.\n");
+ return 0;
+ }else{
+ printf("We hit an error.\n");
+ return 1;
+ }
+}
diff --git a/third_party/sqlite/test/tkt1435.test b/third_party/sqlite/test/tkt1435.test
new file mode 100755
index 0000000..1f62147
--- /dev/null
+++ b/third_party/sqlite/test/tkt1435.test
@@ -0,0 +1,111 @@
+# 2005 September 17
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1435 has been
+# fixed.
+#
+#
+# $Id: tkt1435.test,v 1.2 2006/01/17 09:35:02 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !memorydb {
+ finish_test
+ return
+}
+
+# Construct the sample database.
+#
+do_test tkt1435-1.0 {
+ sqlite3 db :memory:
+ execsql {
+ CREATE TABLE Instances(
+ instanceId INTEGER PRIMARY KEY,
+ troveName STR,
+ versionId INT,
+ flavorId INT,
+ timeStamps STR,
+ isPresent INT,
+ pinned BOOLEAN
+ );
+ INSERT INTO "Instances"
+ VALUES(1, 'libhello:runtime', 1, 1, 1126929880.094, 1, 1);
+ INSERT INTO "Instances"
+ VALUES(2, 'libhello:user', 1, 1, 1126929880.094, 1, 0);
+ INSERT INTO "Instances"
+ VALUES(3, 'libhello:script', 1, 1, 1126929880.094, 1, 0);
+ INSERT INTO "Instances"
+ VALUES(4, 'libhello', 1, 1, 1126929880.094, 1, 0);
+
+ CREATE TABLE Versions(versionId INTEGER PRIMARY KEY,version STR UNIQUE);
+ INSERT INTO "Versions" VALUES(0, NULL);
+ INSERT INTO "Versions" VALUES(1, '/localhost@rpl:linux/0-1-1');
+
+ CREATE TABLE Flavors(flavorId integer primary key, flavor str unique);
+ INSERT INTO "Flavors" VALUES(0, NULL);
+ INSERT INTO "Flavors" VALUES(1, '1#x86');
+
+ CREATE TEMPORARY TABLE tlList (
+ row INTEGER PRIMARY KEY,
+ name STRING,
+ version STRING,
+ flavor STRING
+ );
+
+ INSERT INTO tlList
+ values(NULL, 'libhello:script', '/localhost@rpl:linux/0-1-1', '1#x86');
+ INSERT INTO tlList
+ values(NULL, 'libhello:user', '/localhost@rpl:linux/0-1-1', '1#x86');
+ INSERT INTO tlList
+ values(NULL, 'libhello:runtime', '/localhost@rpl:linux/0-1-1', '1#x86');
+ }
+} {}
+
+# Run the query with an index
+#
+do_test tkt1435-1.1 {
+ execsql {
+ select row, pinned from tlList, Instances, Versions, Flavors
+ where
+ Instances.troveName = tlList.name
+ and Versions.version = tlList.version
+ and Instances.versionId = Versions.versionId
+ and ( Flavors.flavor = tlList.flavor or Flavors.flavor is NULL
+ and tlList.flavor = '')
+ and Instances.flavorId = Flavors.flavorId
+ order by row asc;
+ }
+} {1 0 2 0 3 1}
+
+# Create a indices, analyze and rerun the query.
+# Verify that the results are the same
+#
+do_test tkt1435-1.2 {
+ execsql {
+ CREATE INDEX InstancesNameIdx ON Instances(troveName);
+ CREATE UNIQUE INDEX InstancesIdx
+ ON Instances(troveName, versionId, flavorId);
+ ANALYZE;
+ select row, pinned from tlList, Instances, Versions, Flavors
+ where
+ Instances.troveName = tlList.name
+ and Versions.version = tlList.version
+ and Instances.versionId = Versions.versionId
+ and ( Flavors.flavor = tlList.flavor or Flavors.flavor is NULL
+ and tlList.flavor = '')
+ and Instances.flavorId = Flavors.flavorId
+ order by row asc;
+ }
+} {1 0 2 0 3 1}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1443.test b/third_party/sqlite/test/tkt1443.test
new file mode 100755
index 0000000..0f55437
--- /dev/null
+++ b/third_party/sqlite/test/tkt1443.test
@@ -0,0 +1,180 @@
+# 2005 September 17
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1433 has been
+# fixed.
+#
+# The problem in ticket #1433 was that the dependencies on the right-hand
+# side of an IN operator were not being checked correctly. So in an
+# expression of the form:
+#
+# t1.x IN (1,t2.b,3)
+#
+# the optimizer was missing the fact that the right-hand side of the IN
+# depended on table t2. It was checking dependencies based on the
+# Expr.pRight field rather than Expr.pList and Expr.pSelect.
+#
+# Such a bug could be verifed using a less elaborate test case. But
+# this test case (from the original bug poster) exercises so many different
+# parts of the system all at once, that it seemed like a good one to
+# include in the test suite.
+#
+# NOTE: Yes, in spite of the name of this file (tkt1443.test) this
+# test is for ticket #1433 not #1443. I mistyped the name when I was
+# creating the file and I had already checked in the file by the wrong
+# name be the time I noticed the error. With CVS it is a really hassle
+# to change filenames, so I'll just leave it as is. No harm done.
+#
+# $Id: tkt1443.test,v 1.4 2006/01/17 09:35:02 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery||!memorydb {
+ finish_test
+ return
+}
+
+# Construct the sample database.
+#
+do_test tkt1443-1.0 {
+ sqlite3 db :memory:
+ execsql {
+ CREATE TABLE Items(
+ itemId integer primary key,
+ item str unique
+ );
+ INSERT INTO "Items" VALUES(0, 'ALL');
+ INSERT INTO "Items" VALUES(1, 'double:source');
+ INSERT INTO "Items" VALUES(2, 'double');
+ INSERT INTO "Items" VALUES(3, 'double:runtime');
+ INSERT INTO "Items" VALUES(4, '.*:runtime');
+
+ CREATE TABLE Labels(
+ labelId INTEGER PRIMARY KEY,
+ label STR UNIQUE
+ );
+ INSERT INTO "Labels" VALUES(0, 'ALL');
+ INSERT INTO "Labels" VALUES(1, 'localhost@rpl:linux');
+ INSERT INTO "Labels" VALUES(2, 'localhost@rpl:branch');
+
+ CREATE TABLE LabelMap(
+ itemId INTEGER,
+ labelId INTEGER,
+ branchId integer
+ );
+ INSERT INTO "LabelMap" VALUES(1, 1, 1);
+ INSERT INTO "LabelMap" VALUES(2, 1, 1);
+ INSERT INTO "LabelMap" VALUES(3, 1, 1);
+ INSERT INTO "LabelMap" VALUES(1, 2, 2);
+ INSERT INTO "LabelMap" VALUES(2, 2, 3);
+ INSERT INTO "LabelMap" VALUES(3, 2, 3);
+
+ CREATE TABLE Users (
+ userId INTEGER PRIMARY KEY,
+ user STRING UNIQUE,
+ salt BINARY,
+ password STRING
+ );
+ INSERT INTO "Users" VALUES(1, 'test', 'Šæ$d',
+ '43ba0f45014306bd6df529551ffdb3df');
+ INSERT INTO "Users" VALUES(2, 'limited', 'ªš>S',
+ 'cf07c8348fdf675cc1f7696b7d45191b');
+ CREATE TABLE UserGroups (
+ userGroupId INTEGER PRIMARY KEY,
+ userGroup STRING UNIQUE
+ );
+ INSERT INTO "UserGroups" VALUES(1, 'test');
+ INSERT INTO "UserGroups" VALUES(2, 'limited');
+
+ CREATE TABLE UserGroupMembers (
+ userGroupId INTEGER,
+ userId INTEGER
+ );
+ INSERT INTO "UserGroupMembers" VALUES(1, 1);
+ INSERT INTO "UserGroupMembers" VALUES(2, 2);
+
+ CREATE TABLE Permissions (
+ userGroupId INTEGER,
+ labelId INTEGER NOT NULL,
+ itemId INTEGER NOT NULL,
+ write INTEGER,
+ capped INTEGER,
+ admin INTEGER
+ );
+ INSERT INTO "Permissions" VALUES(1, 0, 0, 1, 0, 1);
+ INSERT INTO "Permissions" VALUES(2, 2, 4, 0, 0, 0);
+ }
+} {}
+
+# Run the query with an index
+#
+do_test tkt1443-1.1 {
+ execsql {
+ select distinct
+ Items.Item as trove, UP.pattern as pattern
+ from
+ ( select
+ Permissions.labelId as labelId,
+ PerItems.item as pattern
+ from
+ Users, UserGroupMembers, Permissions
+ left outer join Items as PerItems
+ on Permissions.itemId = PerItems.itemId
+ where
+ Users.user = 'limited'
+ and Users.userId = UserGroupMembers.userId
+ and UserGroupMembers.userGroupId = Permissions.userGroupId
+ ) as UP join LabelMap on ( UP.labelId = 0 or
+ UP.labelId = LabelMap.labelId ),
+ Labels, Items
+ where
+ Labels.label = 'localhost@rpl:branch'
+ and Labels.labelId = LabelMap.labelId
+ and LabelMap.itemId = Items.itemId
+ ORDER BY +trove, +pattern
+ }
+} {double .*:runtime double:runtime .*:runtime double:source .*:runtime}
+
+# Create an index and rerun the query.
+# Verify that the results are the same
+#
+do_test tkt1443-1.2 {
+ execsql {
+ CREATE UNIQUE INDEX PermissionsIdx
+ ON Permissions(userGroupId, labelId, itemId);
+ select distinct
+ Items.Item as trove, UP.pattern as pattern
+ from
+ ( select
+ Permissions.labelId as labelId,
+ PerItems.item as pattern
+ from
+ Users, UserGroupMembers, Permissions
+ left outer join Items as PerItems
+ on Permissions.itemId = PerItems.itemId
+ where
+ Users.user = 'limited'
+ and Users.userId = UserGroupMembers.userId
+ and UserGroupMembers.userGroupId = Permissions.userGroupId
+ ) as UP join LabelMap on ( UP.labelId = 0 or
+ UP.labelId = LabelMap.labelId ),
+ Labels, Items
+ where
+ Labels.label = 'localhost@rpl:branch'
+ and Labels.labelId = LabelMap.labelId
+ and LabelMap.itemId = Items.itemId
+ ORDER BY +trove, +pattern
+ }
+} {double .*:runtime double:runtime .*:runtime double:source .*:runtime}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1444.test b/third_party/sqlite/test/tkt1444.test
new file mode 100755
index 0000000..13870db
--- /dev/null
+++ b/third_party/sqlite/test/tkt1444.test
@@ -0,0 +1,56 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1444 has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound||!view {
+ finish_test
+ return
+}
+
+# The use of a VIEW that contained an ORDER BY clause within a UNION ALL
+# was causing problems. See ticket #1444.
+#
+do_test tkt1444-1.1 {
+ execsql {
+ CREATE TABLE DemoTable (x INTEGER, TextKey TEXT, DKey Real);
+ CREATE INDEX DemoTableIdx ON DemoTable (TextKey);
+ INSERT INTO DemoTable VALUES(9,8,7);
+ INSERT INTO DemoTable VALUES(1,2,3);
+ CREATE VIEW DemoView AS SELECT * FROM DemoTable ORDER BY TextKey;
+ SELECT * FROM DemoTable UNION ALL SELECT * FROM DemoView ORDER BY 1;
+ }
+} {1 2 3.0 1 2 3.0 9 8 7.0 9 8 7.0}
+do_test tkt1444-1.2 {
+ execsql {
+ SELECT * FROM DemoTable UNION ALL SELECT * FROM DemoView;
+ }
+} {9 8 7.0 1 2 3.0 1 2 3.0 9 8 7.0}
+do_test tkt1444-1.3 {
+ execsql {
+ DROP VIEW DemoView;
+ CREATE VIEW DemoView AS SELECT * FROM DemoTable;
+ SELECT * FROM DemoTable UNION ALL SELECT * FROM DemoView ORDER BY 1;
+ }
+} {1 2 3.0 1 2 3.0 9 8 7.0 9 8 7.0}
+do_test tkt1444-1.4 {
+ execsql {
+ SELECT * FROM DemoTable UNION ALL SELECT * FROM DemoView;
+ }
+} {9 8 7.0 1 2 3.0 9 8 7.0 1 2 3.0}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1449.test b/third_party/sqlite/test/tkt1449.test
new file mode 100755
index 0000000..5f27ee7
--- /dev/null
+++ b/third_party/sqlite/test/tkt1449.test
@@ -0,0 +1,262 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1449 has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Somewhere in tkt1449-1.1 is a VIEW definition that uses a subquery and
+# a compound SELECT. So we cannot run this file if any of these features
+# are not available.
+ifcapable !subquery||!compound||!view {
+ finish_test
+ return
+}
+
+# The following schema generated problems in ticket #1449. We've retained
+# the original schema here because it is some unbelievably complex, it seemed
+# like a good test case for SQLite.
+#
+do_test tkt1449-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE ACLS(ISSUEID text(50) not null, OBJECTID text(50) not null, PARTICIPANTID text(50) not null, PERMISSIONBITS int not null, constraint PK_ACLS primary key (ISSUEID, OBJECTID, PARTICIPANTID));
+ CREATE TABLE ACTIONITEMSTATUSES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, FRIENDLYNAME text(100) not null, REVISION int not null, SHORTNAME text(30) not null, LONGNAME text(200) not null, ATTACHMENTHANDLING int not null, RESULT int not null, NOTIFYCREATOR text(1) null, NOTIFYASSIGNEE text(1) null, NOTIFYFYI text(1) null, NOTIFYCLOSURETEAM text(1) null, NOTIFYCOORDINATORS text(1) null, COMMENTREQUIRED text(1) not null, constraint PK_ACTIONITEMSTATUSES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE ACTIONITEMTYPES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, REVISION int not null, LABEL text(200) not null, INSTRUCTIONS text not null, EMAILINSTRUCTIONS text null, ALLOWEDSTATUSES text not null, INITIALSTATUS text(100) not null, COMMENTREQUIRED text(1) not null, ATTACHMENTHANDLING int not null, constraint PK_ACTIONITEMTYPES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE ATTACHMENTS(TQUNID text(36) not null, OBJECTID text(50) null, ISSUEID text(50) null, DATASTREAM blob not null, CONTENTENCODING text(50) null, CONTENTCHARSET text(50) null, CONTENTTYPE text(100) null, CONTENTID text(100) null, CONTENTLOCATION text(100) null, CONTENTNAME text(100) not null, constraint PK_ATTACHMENTS primary key (TQUNID));
+ CREATE TABLE COMPLIANCEPOLICIES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, BODY text null, constraint PK_COMPLIANCEPOLICIES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE DBHISTORY(DATETIME text(25) not null, OPERATION text(20) not null, KUBIVERSION text(100) not null, FROMVERSION int null, TOVERSION int null);
+ CREATE TABLE DBINFO(FINGERPRINT text(32) not null, VERSION int not null);
+ CREATE TABLE DETACHEDATTACHMENTS (TQUNID text(36) not null, ISSUEID text(50) not null, OBJECTID text(50) not null, PATH text(300) not null, DETACHEDFILELASTMODTIMESTAMP text(25) null, CONTENTID text(100) not null, constraint PK_DETACHEDATTACHMENTS primary key (TQUNID));
+ CREATE TABLE DOCREFERENCES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, REFERENCEDOCUMENTID text(50) null, constraint PK_DOCREFERENCES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE DQ (TQUNID text(36) not null, ISSUEID text(50) not null, DEPENDSID text(50) null, DEPENDSTYPE int null, DEPENDSCOMMANDSTREAM blob null, DEPENDSNODEIDSEQNOKEY text(100) null, DEPENDSACLVERSION int null, constraint PK_DQ primary key (TQUNID));
+ CREATE TABLE EMAILQ(TIMEQUEUED int not null, NODEID text(50) not null, MIME blob not null, TQUNID text(36) not null);
+ CREATE TABLE ENTERPRISEDATA(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, DATE1 text(25) null, DATE2 text(25) null, DATE3 text(25) null, DATE4 text(25) null, DATE5 text(25) null, DATE6 text(25) null, DATE7 text(25) null, DATE8 text(25) null, DATE9 text(25) null, DATE10 text(25) null, VALUE1 int null, VALUE2 int null, VALUE3 int null, VALUE4 int null, VALUE5 int null, VALUE6 int null, VALUE7 int null, VALUE8 int null, VALUE9 int null, VALUE10 int null, VALUE11 int null, VALUE12 int null, VALUE13 int null, VALUE14 int null, VALUE15 int null, VALUE16 int null, VALUE17 int null, VALUE18 int null, VALUE19 int null, VALUE20 int null, STRING1 text(300) null, STRING2 text(300) null, STRING3 text(300) null, STRING4 text(300) null, STRING5 text(300) null, STRING6 text(300) null, STRING7 text(300) null, STRING8 text(300) null, STRING9 text(300) null, STRING10 text(300) null, LONGSTRING1 text null, LONGSTRING2 text null, LONGSTRING3 text null, LONGSTRING4 text null, LONGSTRING5 text null, LONGSTRING6 text null, LONGSTRING7 text null, LONGSTRING8 text null, LONGSTRING9 text null, LONGSTRING10 text null, constraint PK_ENTERPRISEDATA primary key (ISSUEID, OBJECTID));
+ CREATE TABLE FILEMORGUE(TQUNID text(36) not null, PATH text(300) not null, DELETEFOLDERWHENEMPTY text(1) null, constraint PK_FILEMORGUE primary key (TQUNID));
+ CREATE TABLE FILES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARENTENTITYID text(50) null, BODY text null, BODYCONTENTTYPE text(100) null, ISOBSOLETE text(1) null, FILENAME text(300) not null, VISIBLENAME text(300) not null, VERSIONSTRING text(300) not null, DOCUMENTHASH text(40) not null, ISFINAL text(1) null, DOCREFERENCEID text(50) not null, constraint PK_FILES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE FOLDERS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, CONTAINERNAME text(300) null, CONTAINERACLSETTINGS text null, constraint PK_FOLDERS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE GLOBALSETTINGS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, SINGULARPROJECTLABEL text(30) not null, PLURALPROJECTLABEL text(30) not null, PROJECTREQUIRED text(1) not null, CUSTOMPROJECTSALLOWED text(1) not null, ACTIONITEMSPECXML text null, PROJECTLISTXML text null, ENTERPRISEDATALABELS text null, ENTERPRISEDATATABXSL text null, constraint PK_GLOBALSETTINGS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE GLOBALSTRINGPROPERTIES(ID int not null, VALUE text(300) not null, constraint PK_GLOBALSTRINGPROPERTIES primary key (ID));
+ CREATE TABLE IMQ(TQUNID text(36) not null, DATETIMEQUEUED text(25) not null, ISSUEID text(50) not null, KUBIBUILD text(30) not null, FAILCOUNT int not null, LASTRUN text(25) null, ENVELOPESTREAM blob not null, PAYLOADSTREAM blob not null, constraint PK_IMQ primary key (TQUNID));
+ CREATE TABLE INVITATIONNODES(INVITATIONID text(50) not null, RECIPIENTNODEID text(50) not null, DATECREATED text(25) not null, constraint PK_INVITATIONNODES primary key (INVITATIONID, RECIPIENTNODEID));
+ CREATE TABLE INVITATIONS (INVITATIONID text(50) not null, SENDERNODEID text(50) not null, RECIPIENTEMAILADDR text(200) not null, RECIPIENTUSERID text(50) null, RECIPIENTNODES text null, ISSUEID text(50) not null, ENVELOPE text not null, MESSAGEBLOB blob not null, INVITATIONSTATE int not null, TQUNID text(36) not null, DATECREATED text(25) not null);
+ CREATE TABLE ISSUES (CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, CONTAINERNAME text(300) null, CONTAINERACLSETTINGS text null, ISINITIALIZED text(1) null, BLINDINVITES text null, ISSYSTEMISSUE text(1) not null, ISSUETYPE int not null, ACTIVITYTYPEID text(50) null, ISINCOMPLETE text(1) not null, constraint PK_ISSUES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE ISSUESETTINGS (CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, ISSUENAME text(300) not null, ISSUEACLSETTINGS text not null, ISSUEDUEDATE text(25) null, ISSUEPRIORITY int null, ISSUESTATUS int null, DESCRIPTION text null, PROJECTID text(100) null, PROJECTNAME text null, PROJECTNAMEISCUSTOM text(1) null, ISSYSTEMISSUE text(1) not null, ACTIONITEMREVNUM int not null, constraint PK_ISSUESETTINGS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE KMTPMSG (MSGID integer not null, SENDERID text(50) null, RECIPIENTIDLIST text not null, ISSUEID text(50) null, MESSAGETYPE int not null, ENVELOPE text null, MESSAGEBLOB blob not null, RECEIVEDDATE text(25) not null, constraint PK_KMTPMSG primary key (MSGID));
+ CREATE TABLE KMTPNODEQ(NODEID text(50) not null, MSGID int not null, RECEIVEDDATE text(25) not null, SENDCOUNT int not null);
+ CREATE TABLE KMTPQ(MSGID integer not null, SENDERID text(50) null, RECIPIENTIDLIST text not null, ISSUEID text(50) null, MESSAGETYPE int not null, ENVELOPE text null, MESSAGEBLOB blob not null, constraint PK_KMTPQ primary key (MSGID));
+ CREATE TABLE LOGENTRIES(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARENTENTITYID text(50) null, BODY text null, BODYCONTENTTYPE text(100) null, ISOBSOLETE text(1) null, ACTIONTYPE int not null, ASSOCIATEDOBJECTIDS text null, OLDENTITIES text null, NEWENTITIES text null, OTHERENTITIES text null, constraint PK_LOGENTRIES primary key (ISSUEID, OBJECTID));
+ CREATE TABLE LSBI(TQUNID text(36) not null, ISSUEID text(50) not null, TABLEITEMID text(50) null, TABLENODEID text(50) null, TABLECMD int null, TABLECONTAINERID text(50) null, TABLESEQNO int null, DIRTYCONTENT text null, STUBBED text(1) null, ENTITYSTUBDATA text null, UPDATENUMBER int not null, constraint PK_LSBI primary key (TQUNID));
+ CREATE TABLE LSBN(TQUNID text(36) not null, ISSUEID text(50) not null, NODEID text(50) not null, STORESEQNO int not null, SYNCSEQNO int not null, LASTMSGDATE text(25) null, constraint PK_LSBN primary key (TQUNID));
+ CREATE TABLE MMQ(TQUNID text(36) not null, ISSUEID text(50) not null, TABLEREQUESTNODE text(50) null, MMQENTRYINDEX text(60) null, DIRECTION int null, NODEID text(50) null, TABLEFIRSTSEQNO int null, TABLELASTSEQNO int null, NEXTRESENDTIMEOUT text(25) null, TABLETIMEOUTMULTIPLIER int null, constraint PK_MMQ primary key (TQUNID));
+ CREATE TABLE NODEREG(NODEID text(50) not null, USERID text(50) null, CREATETIME text(25) not null, TQUNID text(36) not null);
+ CREATE TABLE NODES (NODEID text(50) not null, USERID text(50) null, NODESTATE int not null, NODECERT text null, KUBIVERSION int not null, KUBIBUILD text(30) not null, TQUNID text(36) not null, LASTBINDDATE text(25) null, LASTUNBINDDATE text(25) null, LASTBINDIP text(15) null, NUMBINDS int not null, NUMSENDS int not null, NUMPOLLS int not null, NUMRECVS int not null);
+ CREATE TABLE PARTICIPANTNODES(ISSUEID text(50) not null, OBJECTID text(50) not null, NODEID text(50) not null, USERID text(50) null, NODESTATE int not null, NODECERT text null, KUBIVERSION int not null, KUBIBUILD text(30) not null, TQUNID text(36) not null);
+ CREATE TABLE PARTICIPANTS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARTICIPANTSTATE int not null, PARTICIPANTROLE int not null, PARTICIPANTTEAM int not null, ISREQUIREDMEMBER text(1) null, USERID text(50) null, ISAGENT text(1) null, NAME text(150) not null, EMAILADDRESS text(200) not null, ISEMAILONLY text(1) not null, INVITATION text null, ACCEPTRESENDCOUNT int null, ACCEPTRESENDTIMEOUT text(25) null, ACCEPTLASTSENTTONODEID text(50) null, constraint PK_PARTICIPANTS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE PARTICIPANTSETTINGS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARTICIPANTID text(50) not null, TASKPIMSYNC text(1) null, MOBILESUPPORT text(1) null, NOTIFYBYEMAIL text(1) null, MARKEDCRITICAL text(1) null, constraint PK_PARTICIPANTSETTINGS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE PARTITIONS(PARTITIONID text(50) not null, NAME text(100) not null, LDAPDN text(300) not null, SERVERNODEID text(50) not null, TQUNID text(36) not null);
+ CREATE TABLE PROJECTS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, NAME text(100) not null, ID text(100) null, constraint PK_PROJECTS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE TASKCOMPLETIONS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARENTENTITYID text(50) null, BODY text null, BODYCONTENTTYPE text(100) null, ISOBSOLETE text(1) null, TASKID text(50) not null, DISPOSITION int not null, STATUSID text(50) not null, SHORTNAME text(30) not null, LONGNAME text(200) not null, constraint PK_TASKCOMPLETIONS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE TASKS(CLASSID int null, SEQNO int not null, LASTMODONNODEID text(50) not null, PREVMODONNODEID text(50) null, ISSUEID text(50) not null, OBJECTID text(50) not null, REVISIONNUM int not null, CONTAINERID text(50) not null, AUTHORID text(50) not null, CREATIONDATE text(25) null, LASTMODIFIEDDATE text(25) null, UPDATENUMBER int null, PREVREVISIONNUM int null, LASTCMD int null, LASTCMDACLVERSION int null, USERDEFINEDFIELD text(300) null, LASTMODIFIEDBYID text(50) null, PARENTENTITYID text(50) null, BODY text null, BODYCONTENTTYPE text(100) null, ISOBSOLETE text(1) null, DUETIME text(25) null, ASSIGNEDTO text(50) not null, TARGETOBJECTIDS text null, RESPONSEID text(50) not null, TYPEID text(50) not null, LABEL text(200) not null, INSTRUCTIONS text not null, ALLOWEDSTATUSES text not null, ISSERIALREVIEW text(1) null, DAYSTOREVIEW int null, REVIEWERIDS text(500) null, REVIEWTYPE int null, REVIEWGROUP text(300) null, constraint PK_TASKS primary key (ISSUEID, OBJECTID));
+ CREATE TABLE USERS (USERID text(50) not null, USERSID text(100) not null, ENTERPRISEUSER text(1) not null, USEREMAILADDRESS text(200) null, EMAILVALIDATED text(1) null, VALIDATIONCOOKIE text(50) null, CREATETIME text(25) not null, TQUNID text(36) not null, PARTITIONID text(50) null);
+ CREATE VIEW CRITICALISSUES as
+
+
+ select
+ USERID, ISSUEID, ISSUENAME, min(DATE1) DATE1
+ from (
+ select p.USERID USERID, p.ISSUEID ISSUEID, iset.ISSUENAME ISSUENAME, t.DUETIME DATE1
+ from PARTICIPANTS p
+ join TASKS t on t.ASSIGNEDTO = p.OBJECTID
+ join TASKCOMPLETIONS tc on tc.TASKID = t.OBJECTID
+ join ISSUESETTINGS iset on iset.ISSUEID = p.ISSUEID
+ where (t.ISOBSOLETE = 'n' or t.ISOBSOLETE is null)
+ and tc.DISPOSITION = 1
+ and iset.ISSUESTATUS = 1
+ union
+ select p.USERID USERID, p.ISSUEID ISSUEID, iset.ISSUENAME ISSUENAME, iset.ISSUEDUEDATE DATE1
+ from PARTICIPANTS p
+ join PARTICIPANTSETTINGS ps on ps.PARTICIPANTID = p.OBJECTID
+ join ISSUESETTINGS iset on iset.ISSUEID = p.ISSUEID
+ where ps.MARKEDCRITICAL = 'y'
+ and iset.ISSUESTATUS = 1
+ ) as CRITICALDATA
+ group by USERID, ISSUEID, ISSUENAME;
+ CREATE VIEW CURRENTFILES as
+
+
+ select
+ d.ISSUEID as ISSUEID,
+ d.REFERENCEDOCUMENTID as OBJECTID,
+ f.VISIBLENAME as VISIBLENAME
+ from
+ DOCREFERENCES d
+ join FILES f on f.OBJECTID = d.REFERENCEDOCUMENTID;
+ CREATE VIEW ISSUEDATA as
+
+
+ select
+ ISSUES.OBJECTID as ISSUEID,
+ ISSUES.CREATIONDATE as CREATIONDATE,
+ ISSUES.AUTHORID as AUTHORID,
+ ISSUES.LASTMODIFIEDDATE as LASTMODIFIEDDATE,
+ ISSUES.LASTMODIFIEDBYID as LASTMODIFIEDBYID,
+ ISSUESETTINGS.ISSUENAME as ISSUENAME,
+ ISSUES.ISINITIALIZED as ISINITIALIZED,
+ ISSUES.ISSYSTEMISSUE as ISSYSTEMISSUE,
+ ISSUES.ISSUETYPE as ISSUETYPE,
+ ISSUES.ISINCOMPLETE as ISINCOMPLETE,
+ ISSUESETTINGS.REVISIONNUM as ISSUESETTINGS_REVISIONNUM,
+ ISSUESETTINGS.LASTMODIFIEDDATE as ISSUESETTINGS_LASTMODIFIEDDATE,
+ ISSUESETTINGS.LASTMODIFIEDBYID as ISSUESETTINGS_LASTMODIFIEDBYID,
+ ISSUESETTINGS.ISSUEDUEDATE as ISSUEDUEDATE,
+ ISSUESETTINGS.ISSUEPRIORITY as ISSUEPRIORITY,
+ ISSUESETTINGS.ISSUESTATUS as ISSUESTATUS,
+ ISSUESETTINGS.DESCRIPTION as DESCRIPTION,
+ ISSUESETTINGS.PROJECTID as PROJECTID,
+ ISSUESETTINGS.PROJECTNAME as PROJECTNAME,
+ ISSUESETTINGS.PROJECTNAMEISCUSTOM as PROJECTNAMEISCUSTOM,
+ ENTERPRISEDATA.REVISIONNUM as ENTERPRISEDATA_REVISIONNUM,
+ ENTERPRISEDATA.CREATIONDATE as ENTERPRISEDATA_CREATIONDATE,
+ ENTERPRISEDATA.AUTHORID as ENTERPRISEDATA_AUTHORID,
+ ENTERPRISEDATA.LASTMODIFIEDDATE as ENTERPRISEDATA_LASTMODIFIEDDATE,
+ ENTERPRISEDATA.LASTMODIFIEDBYID as ENTERPRISEDATA_LASTMODIFIEDBYID,
+ ENTERPRISEDATA.DATE1 as DATE1,
+ ENTERPRISEDATA.DATE2 as DATE2,
+ ENTERPRISEDATA.DATE3 as DATE3,
+ ENTERPRISEDATA.DATE4 as DATE4,
+ ENTERPRISEDATA.DATE5 as DATE5,
+ ENTERPRISEDATA.DATE6 as DATE6,
+ ENTERPRISEDATA.DATE7 as DATE7,
+ ENTERPRISEDATA.DATE8 as DATE8,
+ ENTERPRISEDATA.DATE9 as DATE9,
+ ENTERPRISEDATA.DATE10 as DATE10,
+ ENTERPRISEDATA.VALUE1 as VALUE1,
+ ENTERPRISEDATA.VALUE2 as VALUE2,
+ ENTERPRISEDATA.VALUE3 as VALUE3,
+ ENTERPRISEDATA.VALUE4 as VALUE4,
+ ENTERPRISEDATA.VALUE5 as VALUE5,
+ ENTERPRISEDATA.VALUE6 as VALUE6,
+ ENTERPRISEDATA.VALUE7 as VALUE7,
+ ENTERPRISEDATA.VALUE8 as VALUE8,
+ ENTERPRISEDATA.VALUE9 as VALUE9,
+ ENTERPRISEDATA.VALUE10 as VALUE10,
+ ENTERPRISEDATA.VALUE11 as VALUE11,
+ ENTERPRISEDATA.VALUE12 as VALUE12,
+ ENTERPRISEDATA.VALUE13 as VALUE13,
+ ENTERPRISEDATA.VALUE14 as VALUE14,
+ ENTERPRISEDATA.VALUE15 as VALUE15,
+ ENTERPRISEDATA.VALUE16 as VALUE16,
+ ENTERPRISEDATA.VALUE17 as VALUE17,
+ ENTERPRISEDATA.VALUE18 as VALUE18,
+ ENTERPRISEDATA.VALUE19 as VALUE19,
+ ENTERPRISEDATA.VALUE20 as VALUE20,
+ ENTERPRISEDATA.STRING1 as STRING1,
+ ENTERPRISEDATA.STRING2 as STRING2,
+ ENTERPRISEDATA.STRING3 as STRING3,
+ ENTERPRISEDATA.STRING4 as STRING4,
+ ENTERPRISEDATA.STRING5 as STRING5,
+ ENTERPRISEDATA.STRING6 as STRING6,
+ ENTERPRISEDATA.STRING7 as STRING7,
+ ENTERPRISEDATA.STRING8 as STRING8,
+ ENTERPRISEDATA.STRING9 as STRING9,
+ ENTERPRISEDATA.STRING10 as STRING10,
+ ENTERPRISEDATA.LONGSTRING1 as LONGSTRING1,
+ ENTERPRISEDATA.LONGSTRING2 as LONGSTRING2,
+ ENTERPRISEDATA.LONGSTRING3 as LONGSTRING3,
+ ENTERPRISEDATA.LONGSTRING4 as LONGSTRING4,
+ ENTERPRISEDATA.LONGSTRING5 as LONGSTRING5,
+ ENTERPRISEDATA.LONGSTRING6 as LONGSTRING6,
+ ENTERPRISEDATA.LONGSTRING7 as LONGSTRING7,
+ ENTERPRISEDATA.LONGSTRING8 as LONGSTRING8,
+ ENTERPRISEDATA.LONGSTRING9 as LONGSTRING9,
+ ENTERPRISEDATA.LONGSTRING10 as LONGSTRING10
+ from
+ ISSUES
+ join ISSUESETTINGS on ISSUES.OBJECTID = ISSUESETTINGS.ISSUEID
+ left outer join ENTERPRISEDATA on ISSUES.OBJECTID = ENTERPRISEDATA.ISSUEID;
+ CREATE VIEW ITEMS as
+
+ select 'FILES' as TABLENAME, CLASSID, SEQNO, LASTMODONNODEID, PREVMODONNODEID, ISSUEID, OBJECTID, REVISIONNUM, CONTAINERID, AUTHORID, CREATIONDATE, LASTMODIFIEDDATE, UPDATENUMBER, PREVREVISIONNUM, LASTCMD, LASTCMDACLVERSION, USERDEFINEDFIELD, LASTMODIFIEDBYID, PARENTENTITYID, BODY, BODYCONTENTTYPE, ISOBSOLETE, FILENAME, VISIBLENAME, VERSIONSTRING, DOCUMENTHASH, ISFINAL, DOCREFERENCEID, NULL as ACTIONTYPE, NULL as ASSOCIATEDOBJECTIDS, NULL as OLDENTITIES, NULL as NEWENTITIES, NULL as OTHERENTITIES, NULL as TQUNID, NULL as TABLEITEMID, NULL as TABLENODEID, NULL as TABLECMD, NULL as TABLECONTAINERID, NULL as TABLESEQNO, NULL as DIRTYCONTENT, NULL as STUBBED, NULL as ENTITYSTUBDATA, NULL as PARTICIPANTSTATE, NULL as PARTICIPANTROLE, NULL as PARTICIPANTTEAM, NULL as ISREQUIREDMEMBER, NULL as USERID, NULL as ISAGENT, NULL as NAME, NULL as EMAILADDRESS, NULL as ISEMAILONLY, NULL as INVITATION, NULL as ACCEPTRESENDCOUNT, NULL as ACCEPTRESENDTIMEOUT, NULL as ACCEPTLASTSENTTONODEID, NULL as TASKID, NULL as DISPOSITION, NULL as STATUSID, NULL as SHORTNAME, NULL as LONGNAME, NULL as DUETIME, NULL as ASSIGNEDTO, NULL as TARGETOBJECTIDS, NULL as RESPONSEID, NULL as TYPEID, NULL as LABEL, NULL as INSTRUCTIONS, NULL as ALLOWEDSTATUSES, NULL as ISSERIALREVIEW, NULL as DAYSTOREVIEW, NULL as REVIEWERIDS, NULL as REVIEWTYPE, NULL as REVIEWGROUP from FILES
+ union all
+ select 'LOGENTRIES' as TABLENAME, CLASSID, SEQNO, LASTMODONNODEID, PREVMODONNODEID, ISSUEID, OBJECTID, REVISIONNUM, CONTAINERID, AUTHORID, CREATIONDATE, LASTMODIFIEDDATE, UPDATENUMBER, PREVREVISIONNUM, LASTCMD, LASTCMDACLVERSION, USERDEFINEDFIELD, LASTMODIFIEDBYID, PARENTENTITYID, BODY, BODYCONTENTTYPE, ISOBSOLETE, NULL as FILENAME, NULL as VISIBLENAME, NULL as VERSIONSTRING, NULL as DOCUMENTHASH, NULL as ISFINAL, NULL as DOCREFERENCEID, ACTIONTYPE, ASSOCIATEDOBJECTIDS, OLDENTITIES, NEWENTITIES, OTHERENTITIES, NULL as TQUNID, NULL as TABLEITEMID, NULL as TABLENODEID, NULL as TABLECMD, NULL as TABLECONTAINERID, NULL as TABLESEQNO, NULL as DIRTYCONTENT, NULL as STUBBED, NULL as ENTITYSTUBDATA, NULL as PARTICIPANTSTATE, NULL as PARTICIPANTROLE, NULL as PARTICIPANTTEAM, NULL as ISREQUIREDMEMBER, NULL as USERID, NULL as ISAGENT, NULL as NAME, NULL as EMAILADDRESS, NULL as ISEMAILONLY, NULL as INVITATION, NULL as ACCEPTRESENDCOUNT, NULL as ACCEPTRESENDTIMEOUT, NULL as ACCEPTLASTSENTTONODEID, NULL as TASKID, NULL as DISPOSITION, NULL as STATUSID, NULL as SHORTNAME, NULL as LONGNAME, NULL as DUETIME, NULL as ASSIGNEDTO, NULL as TARGETOBJECTIDS, NULL as RESPONSEID, NULL as TYPEID, NULL as LABEL, NULL as INSTRUCTIONS, NULL as ALLOWEDSTATUSES, NULL as ISSERIALREVIEW, NULL as DAYSTOREVIEW, NULL as REVIEWERIDS, NULL as REVIEWTYPE, NULL as REVIEWGROUP from LOGENTRIES
+ union all
+ select 'LSBI' as TABLENAME, NULL as CLASSID, NULL as SEQNO, NULL as LASTMODONNODEID, NULL as PREVMODONNODEID, ISSUEID, NULL as OBJECTID, NULL as REVISIONNUM, NULL as CONTAINERID, NULL as AUTHORID, NULL as CREATIONDATE, NULL as LASTMODIFIEDDATE, UPDATENUMBER, NULL as PREVREVISIONNUM, NULL as LASTCMD, NULL as LASTCMDACLVERSION, NULL as USERDEFINEDFIELD, NULL as LASTMODIFIEDBYID, NULL as PARENTENTITYID, NULL as BODY, NULL as BODYCONTENTTYPE, NULL as ISOBSOLETE, NULL as FILENAME, NULL as VISIBLENAME, NULL as VERSIONSTRING, NULL as DOCUMENTHASH, NULL as ISFINAL, NULL as DOCREFERENCEID, NULL as ACTIONTYPE, NULL as ASSOCIATEDOBJECTIDS, NULL as OLDENTITIES, NULL as NEWENTITIES, NULL as OTHERENTITIES, TQUNID, TABLEITEMID, TABLENODEID, TABLECMD, TABLECONTAINERID, TABLESEQNO, DIRTYCONTENT, STUBBED, ENTITYSTUBDATA, NULL as PARTICIPANTSTATE, NULL as PARTICIPANTROLE, NULL as PARTICIPANTTEAM, NULL as ISREQUIREDMEMBER, NULL as USERID, NULL as ISAGENT, NULL as NAME, NULL as EMAILADDRESS, NULL as ISEMAILONLY, NULL as INVITATION, NULL as ACCEPTRESENDCOUNT, NULL as ACCEPTRESENDTIMEOUT, NULL as ACCEPTLASTSENTTONODEID, NULL as TASKID, NULL as DISPOSITION, NULL as STATUSID, NULL as SHORTNAME, NULL as LONGNAME, NULL as DUETIME, NULL as ASSIGNEDTO, NULL as TARGETOBJECTIDS, NULL as RESPONSEID, NULL as TYPEID, NULL as LABEL, NULL as INSTRUCTIONS, NULL as ALLOWEDSTATUSES, NULL as ISSERIALREVIEW, NULL as DAYSTOREVIEW, NULL as REVIEWERIDS, NULL as REVIEWTYPE, NULL as REVIEWGROUP from LSBI where TABLECMD=3
+ union all
+ select 'PARTICIPANTS' as TABLENAME, CLASSID, SEQNO, LASTMODONNODEID, PREVMODONNODEID, ISSUEID, OBJECTID, REVISIONNUM, CONTAINERID, AUTHORID, CREATIONDATE, LASTMODIFIEDDATE, UPDATENUMBER, PREVREVISIONNUM, LASTCMD, LASTCMDACLVERSION, USERDEFINEDFIELD, LASTMODIFIEDBYID, NULL as PARENTENTITYID, NULL as BODY, NULL as BODYCONTENTTYPE, NULL as ISOBSOLETE, NULL as FILENAME, NULL as VISIBLENAME, NULL as VERSIONSTRING, NULL as DOCUMENTHASH, NULL as ISFINAL, NULL as DOCREFERENCEID, NULL as ACTIONTYPE, NULL as ASSOCIATEDOBJECTIDS, NULL as OLDENTITIES, NULL as NEWENTITIES, NULL as OTHERENTITIES, NULL as TQUNID, NULL as TABLEITEMID, NULL as TABLENODEID, NULL as TABLECMD, NULL as TABLECONTAINERID, NULL as TABLESEQNO, NULL as DIRTYCONTENT, NULL as STUBBED, NULL as ENTITYSTUBDATA, PARTICIPANTSTATE, PARTICIPANTROLE, PARTICIPANTTEAM, ISREQUIREDMEMBER, USERID, ISAGENT, NAME, EMAILADDRESS, ISEMAILONLY, INVITATION, ACCEPTRESENDCOUNT, ACCEPTRESENDTIMEOUT, ACCEPTLASTSENTTONODEID, NULL as TASKID, NULL as DISPOSITION, NULL as STATUSID, NULL as SHORTNAME, NULL as LONGNAME, NULL as DUETIME, NULL as ASSIGNEDTO, NULL as TARGETOBJECTIDS, NULL as RESPONSEID, NULL as TYPEID, NULL as LABEL, NULL as INSTRUCTIONS, NULL as ALLOWEDSTATUSES, NULL as ISSERIALREVIEW, NULL as DAYSTOREVIEW, NULL as REVIEWERIDS, NULL as REVIEWTYPE, NULL as REVIEWGROUP from PARTICIPANTS
+ union all
+ select 'TASKCOMPLETIONS' as TABLENAME, CLASSID, SEQNO, LASTMODONNODEID, PREVMODONNODEID, ISSUEID, OBJECTID, REVISIONNUM, CONTAINERID, AUTHORID, CREATIONDATE, LASTMODIFIEDDATE, UPDATENUMBER, PREVREVISIONNUM, LASTCMD, LASTCMDACLVERSION, USERDEFINEDFIELD, LASTMODIFIEDBYID, PARENTENTITYID, BODY, BODYCONTENTTYPE, ISOBSOLETE, NULL as FILENAME, NULL as VISIBLENAME, NULL as VERSIONSTRING, NULL as DOCUMENTHASH, NULL as ISFINAL, NULL as DOCREFERENCEID, NULL as ACTIONTYPE, NULL as ASSOCIATEDOBJECTIDS, NULL as OLDENTITIES, NULL as NEWENTITIES, NULL as OTHERENTITIES, NULL as TQUNID, NULL as TABLEITEMID, NULL as TABLENODEID, NULL as TABLECMD, NULL as TABLECONTAINERID, NULL as TABLESEQNO, NULL as DIRTYCONTENT, NULL as STUBBED, NULL as ENTITYSTUBDATA, NULL as PARTICIPANTSTATE, NULL as PARTICIPANTROLE, NULL as PARTICIPANTTEAM, NULL as ISREQUIREDMEMBER, NULL as USERID, NULL as ISAGENT, NULL as NAME, NULL as EMAILADDRESS, NULL as ISEMAILONLY, NULL as INVITATION, NULL as ACCEPTRESENDCOUNT, NULL as ACCEPTRESENDTIMEOUT, NULL as ACCEPTLASTSENTTONODEID, TASKID, DISPOSITION, STATUSID, SHORTNAME, LONGNAME, NULL as DUETIME, NULL as ASSIGNEDTO, NULL as TARGETOBJECTIDS, NULL as RESPONSEID, NULL as TYPEID, NULL as LABEL, NULL as INSTRUCTIONS, NULL as ALLOWEDSTATUSES, NULL as ISSERIALREVIEW, NULL as DAYSTOREVIEW, NULL as REVIEWERIDS, NULL as REVIEWTYPE, NULL as REVIEWGROUP from TASKCOMPLETIONS
+ union all
+ select 'TASKS' as TABLENAME, CLASSID, SEQNO, LASTMODONNODEID, PREVMODONNODEID, ISSUEID, OBJECTID, REVISIONNUM, CONTAINERID, AUTHORID, CREATIONDATE, LASTMODIFIEDDATE, UPDATENUMBER, PREVREVISIONNUM, LASTCMD, LASTCMDACLVERSION, USERDEFINEDFIELD, LASTMODIFIEDBYID, PARENTENTITYID, BODY, BODYCONTENTTYPE, ISOBSOLETE, NULL as FILENAME, NULL as VISIBLENAME, NULL as VERSIONSTRING, NULL as DOCUMENTHASH, NULL as ISFINAL, NULL as DOCREFERENCEID, NULL as ACTIONTYPE, NULL as ASSOCIATEDOBJECTIDS, NULL as OLDENTITIES, NULL as NEWENTITIES, NULL as OTHERENTITIES, NULL as TQUNID, NULL as TABLEITEMID, NULL as TABLENODEID, NULL as TABLECMD, NULL as TABLECONTAINERID, NULL as TABLESEQNO, NULL as DIRTYCONTENT, NULL as STUBBED, NULL as ENTITYSTUBDATA, NULL as PARTICIPANTSTATE, NULL as PARTICIPANTROLE, NULL as PARTICIPANTTEAM, NULL as ISREQUIREDMEMBER, NULL as USERID, NULL as ISAGENT, NULL as NAME, NULL as EMAILADDRESS, NULL as ISEMAILONLY, NULL as INVITATION, NULL as ACCEPTRESENDCOUNT, NULL as ACCEPTRESENDTIMEOUT, NULL as ACCEPTLASTSENTTONODEID, NULL as TASKID, NULL as DISPOSITION, NULL as STATUSID, NULL as SHORTNAME, NULL as LONGNAME, DUETIME, ASSIGNEDTO, TARGETOBJECTIDS, RESPONSEID, TYPEID, LABEL, INSTRUCTIONS, ALLOWEDSTATUSES, ISSERIALREVIEW, DAYSTOREVIEW, REVIEWERIDS, REVIEWTYPE, REVIEWGROUP from TASKS;
+ CREATE VIEW TASKINFO as
+
+
+ select
+ t.ISSUEID as ISSUEID,
+ t.OBJECTID as OBJECTID,
+ t.ASSIGNEDTO as ASSIGNEDTO,
+ t.TARGETOBJECTIDS as TARGETOBJECTIDS,
+ t.DUETIME as DUETIME,
+ t.ISOBSOLETE as ISOBSOLETE,
+ tc.DISPOSITION as DISPOSITION
+ from
+ TASKS t
+ join TASKCOMPLETIONS tc on tc.TASKID = t.OBJECTID;
+ CREATE INDEX DQ_ISSUEID_DEPENDSID on DQ (ISSUEID, DEPENDSID);
+ CREATE INDEX EMAILQ_TIMEQUEUED on EMAILQ (TIMEQUEUED);
+ CREATE INDEX FOLDERS_CONTAINERID_ISSUEID on FOLDERS (CONTAINERID, ISSUEID);
+ CREATE INDEX IMQ_DATETIMEQUEUED on IMQ (DATETIMEQUEUED);
+ CREATE INDEX INVITATIONS_RECIPIENTUSERID_INVITATIONID on INVITATIONS (RECIPIENTUSERID, INVITATIONID);
+ CREATE INDEX INVITATIONS_TQUNID on INVITATIONS (TQUNID);
+ CREATE INDEX ISSUESETTINGS_CONTAINERID on ISSUESETTINGS (CONTAINERID);
+ CREATE INDEX KMTPMSG_RECEIVEDDATE on KMTPMSG (RECEIVEDDATE desc);
+ CREATE INDEX KMTPNODEQ_MSGID on KMTPNODEQ (MSGID);
+ CREATE INDEX KMTPNODEQ_NODEID_MSGID on KMTPNODEQ (NODEID, MSGID);
+ CREATE INDEX KMTPNODEQ_RECEIVEDDATE on KMTPNODEQ (RECEIVEDDATE desc);
+ CREATE INDEX LSBI_ISSUEID_TABLEITEMID on LSBI (ISSUEID, TABLEITEMID);
+ CREATE INDEX LSBN_ISSUEID_NODEID on LSBN (ISSUEID, NODEID);
+ CREATE INDEX MMQ_ISSUEID_MMQENTRYINDEX on MMQ (ISSUEID, MMQENTRYINDEX);
+ CREATE INDEX NODEREG_NODEID_USERID on NODEREG (NODEID, USERID);
+ CREATE INDEX NODEREG_TQUNID on NODEREG (TQUNID);
+ CREATE INDEX NODEREG_USERID_NODEID on NODEREG (USERID, NODEID);
+ CREATE INDEX NODES_NODEID on NODES (NODEID);
+ CREATE INDEX NODES_TQUNID on NODES (TQUNID);
+ CREATE INDEX PARTICIPANTNODES_ISSUEID_OBJECTID_NODEID on PARTICIPANTNODES (ISSUEID, OBJECTID, NODEID);
+ CREATE INDEX PARTICIPANTNODES_TQUNID on PARTICIPANTNODES (TQUNID);
+ CREATE INDEX PARTICIPANTSETTINGS_PARTICIPANTID on PARTICIPANTSETTINGS (PARTICIPANTID);
+ CREATE INDEX PARTITIONS_LDAPDN on PARTITIONS (LDAPDN);
+ CREATE INDEX PARTITIONS_PARTITIONID_SERVERNODEID on PARTITIONS (PARTITIONID, SERVERNODEID);
+ CREATE INDEX PARTITIONS_SERVERNODEID_PARTITIONID on PARTITIONS (SERVERNODEID, PARTITIONID);
+ CREATE INDEX PARTITIONS_TQUNID on PARTITIONS (TQUNID);
+ CREATE INDEX TASKCOMPLETIONS_TASKID on TASKCOMPLETIONS (TASKID);
+ CREATE INDEX TASKS_ASSIGNEDTO on TASKS (ASSIGNEDTO);
+ CREATE INDEX USERS_PARTITIONID_USERID on USERS (PARTITIONID, USERID);
+ CREATE INDEX USERS_TQUNID on USERS (TQUNID);
+ CREATE INDEX USERS_USERID_PARTITIONID on USERS (USERID, PARTITIONID);
+ CREATE INDEX USERS_USERSID_USERID on USERS (USERSID, USERID);
+ COMMIT;
+ }
+} {}
+
+# Given the schema above, the following query was cause an assertion fault
+# do to an uninitialized field in a Select structure.
+#
+do_test tkt1449-1.2 {
+ execsql {
+ select NEWENTITIES from ITEMS where ((ISSUEID = 'x') and (OBJECTID = 'y'))
+ }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1473.test b/third_party/sqlite/test/tkt1473.test
new file mode 100755
index 0000000..3950272
--- /dev/null
+++ b/third_party/sqlite/test/tkt1473.test
@@ -0,0 +1,728 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1473 has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+do_test tkt1473-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1
+ }
+} {1 2 3 4}
+
+do_test tkt1473-1.2 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=0
+ }
+} {1}
+do_test tkt1473-1.3 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=0
+ }
+} {1}
+do_test tkt1473-1.4 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=4
+ }
+} {1 2}
+do_test tkt1473-1.5 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=4
+ }
+} {1 2}
+do_test tkt1473-1.6 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=4
+ }
+} {2}
+do_test tkt1473-1.7 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=4
+ }
+} {2}
+do_test tkt1473-1.8 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=0
+ }
+} {}
+do_test tkt1473-1.9 {
+ execsql {
+ SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=0
+ }
+} {}
+
+# Everything from this point on depends on sub-queries. So skip it
+# if sub-queries are not available.
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+do_test tkt1473-2.2 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=0)
+ }
+} {1}
+do_test tkt1473-2.3 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=0)
+ }
+} {1}
+do_test tkt1473-2.4 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-2.5 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-2.6 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=4)
+ }
+} {2}
+do_test tkt1473-2.7 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=4)
+ }
+} {2}
+do_test tkt1473-2.8 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=0)
+ }
+} {{}}
+do_test tkt1473-2.9 {
+ execsql {
+ SELECT (SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=0)
+ }
+} {{}}
+
+do_test tkt1473-3.2 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=0)
+ }
+} {1}
+do_test tkt1473-3.3 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=0)
+ }
+} {1}
+do_test tkt1473-3.4 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=1 UNION ALL SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-3.5 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=1 UNION SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-3.6 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-3.7 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=4)
+ }
+} {1}
+do_test tkt1473-3.8 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=0 UNION ALL SELECT 2 FROM t1 WHERE b=0)
+ }
+} {0}
+do_test tkt1473-3.9 {
+ execsql {
+ SELECT EXISTS
+ (SELECT 1 FROM t1 WHERE a=0 UNION SELECT 2 FROM t1 WHERE b=0)
+ }
+} {0}
+
+do_test tkt1473-4.1 {
+ execsql {
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,2);
+ INSERT INTO t2 SELECT x+2, y+2 FROM t2;
+ INSERT INTO t2 SELECT x+4, y+4 FROM t2;
+ INSERT INTO t2 SELECT x+8, y+8 FROM t2;
+ INSERT INTO t2 SELECT x+16, y+16 FROM t2;
+ INSERT INTO t2 SELECT x+32, y+32 FROM t2;
+ INSERT INTO t2 SELECT x+64, y+64 FROM t2;
+ SELECT count(*), sum(x), sum(y) FROM t2;
+ }
+} {64 4096 4160}
+do_test tkt1473-4.2 {
+ execsql {
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=3
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ }
+} {2 4 8 10}
+do_test tkt1473-4.3 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=3
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {2}
+do_test tkt1473-4.4 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=3
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {4}
+do_test tkt1473-4.5 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {8}
+do_test tkt1473-4.6 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {10}
+do_test tkt1473-4.7 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {{}}
+
+do_test tkt1473-5.3 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=3
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+do_test tkt1473-5.4 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=3
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+
+do_test tkt1473-5.5 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=2
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {1}
+do_test tkt1473-5.6 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+do_test tkt1473-5.7 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION ALL
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION ALL
+ SELECT 3 FROM t2 WHERE x=2
+ UNION ALL
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION ALL
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION ALL
+ SELECT 7 FROM t2 WHERE y=1
+ UNION ALL
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION ALL
+ SELECT 9 FROM t2 WHERE y=3
+ UNION ALL
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {0}
+
+do_test tkt1473-6.3 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=3
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=2
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+do_test tkt1473-6.4 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=3
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=2
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+
+do_test tkt1473-6.5 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=-1
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=2
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {1}
+do_test tkt1473-6.6 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+do_test tkt1473-6.7 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {0}
+do_test tkt1473-6.8 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=4
+ )
+ }
+} {1}
+do_test tkt1473-6.9 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0
+ UNION
+ SELECT 2 FROM t2 WHERE x=-1
+ UNION
+ SELECT 3 FROM t2 WHERE x=2
+ UNION
+ SELECT 4 FROM t2 WHERE x=-2
+ UNION
+ SELECT 5 FROM t2 WHERE x=4
+ UNION ALL
+ SELECT 6 FROM t2 WHERE y=0
+ UNION
+ SELECT 7 FROM t2 WHERE y=1
+ UNION
+ SELECT 8 FROM t2 WHERE y=-3
+ UNION
+ SELECT 9 FROM t2 WHERE y=3
+ UNION
+ SELECT 10 FROM t2 WHERE y=-4
+ )
+ }
+} {0}
+
+do_test tkt1473-7.1 {
+ execsql {
+ SELECT 1 FROM t2 WHERE x=1 EXCEPT SELECT 2 FROM t2 WHERE y=2
+ }
+} {1}
+do_test tkt1473-7.2 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=1 EXCEPT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {1}
+do_test tkt1473-7.3 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=1 EXCEPT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {1}
+do_test tkt1473-7.4 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0 EXCEPT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {{}}
+do_test tkt1473-7.5 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=0 EXCEPT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {0}
+
+do_test tkt1473-8.1 {
+ execsql {
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 2 FROM t2 WHERE y=2
+ }
+} {}
+do_test tkt1473-8.1 {
+ execsql {
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 1 FROM t2 WHERE y=2
+ }
+} {1}
+do_test tkt1473-8.3 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {{}}
+do_test tkt1473-8.4 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 1 FROM t2 WHERE y=2
+ )
+ }
+} {1}
+do_test tkt1473-8.5 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 2 FROM t2 WHERE y=2
+ )
+ }
+} {0}
+do_test tkt1473-8.6 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 1 FROM t2 WHERE y=2
+ )
+ }
+} {1}
+do_test tkt1473-8.7 {
+ execsql {
+ SELECT (
+ SELECT 1 FROM t2 WHERE x=0 INTERSECT SELECT 1 FROM t2 WHERE y=2
+ )
+ }
+} {{}}
+do_test tkt1473-8.8 {
+ execsql {
+ SELECT EXISTS (
+ SELECT 1 FROM t2 WHERE x=1 INTERSECT SELECT 1 FROM t2 WHERE y=0
+ )
+ }
+} {0}
+
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1501.test b/third_party/sqlite/test/tkt1501.test
new file mode 100755
index 0000000..19ec7f7
--- /dev/null
+++ b/third_party/sqlite/test/tkt1501.test
@@ -0,0 +1,36 @@
+# 2005 November 16
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1501 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+do_test tkt1501-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT a, b, 'abc' FROM t1
+ UNION
+ SELECT b, a, 'xyz' FROM t1
+ ORDER BY 2, 3;
+ }
+} {2 1 xyz 1 2 abc}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1512.test b/third_party/sqlite/test/tkt1512.test
new file mode 100755
index 0000000..a2c39db
--- /dev/null
+++ b/third_party/sqlite/test/tkt1512.test
@@ -0,0 +1,54 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1512 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!vacuum || !autovacuum} {
+ finish_test
+ return
+}
+if {[db one {PRAGMA auto_vacuum}]} {
+ finish_test
+ return
+}
+
+do_test tkt1512-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1
+ }
+} {1 2 3 4}
+do_test tkt1512-1.2 {
+ file size test.db
+} {2048}
+do_test tkt1512-1.3 {
+ execsql {
+ DROP TABLE t1;
+ }
+ file size test.db
+} {2048}
+do_test tkt1512-1.4 {
+ execsql {
+ VACUUM;
+ }
+ file size test.db
+} {1024}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1514.test b/third_party/sqlite/test/tkt1514.test
new file mode 100755
index 0000000..aff37e8
--- /dev/null
+++ b/third_party/sqlite/test/tkt1514.test
@@ -0,0 +1,27 @@
+# 2005 November 16
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1514 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt1514-1.1 {
+ catchsql {
+ CREATE TABLE t1(a,b);
+ SELECT a FROM t1 WHERE max(b)<10 GROUP BY a;
+ }
+} {1 {misuse of aggregate: max(b)}}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1536.test b/third_party/sqlite/test/tkt1536.test
new file mode 100755
index 0000000..46e4619
--- /dev/null
+++ b/third_party/sqlite/test/tkt1536.test
@@ -0,0 +1,38 @@
+# 2005 November 24
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1536 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt1536-1.1 {
+ execsql {
+ CREATE TABLE t1(
+ a INTEGER PRIMARY KEY,
+ b TEXT
+ );
+ INSERT INTO t1 VALUES(1,'01');
+ SELECT typeof(a), typeof(b) FROM t1;
+ }
+} {integer text}
+do_test tkt1536-1.2 {
+ execsql {
+ INSERT INTO t1(b) SELECT b FROM t1;
+ SELECT b FROM t1 WHERE rowid=2;
+ }
+} {01}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1537.test b/third_party/sqlite/test/tkt1537.test
new file mode 100755
index 0000000..633f91f
--- /dev/null
+++ b/third_party/sqlite/test/tkt1537.test
@@ -0,0 +1,122 @@
+# 2005 November 26
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1537 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt1537-1.1 {
+ execsql {
+ CREATE TABLE t1(id, a1, a2);
+ INSERT INTO t1 VALUES(1, NULL, NULL);
+ INSERT INTO t1 VALUES(2, 1, 3);
+ CREATE TABLE t2(id, b);
+ INSERT INTO t2 VALUES(3, 1);
+ INSERT INTO t2 VALUES(4, NULL);
+ SELECT * FROM t1 LEFT JOIN t2 ON a1=b OR a2=+b;
+ }
+} {1 {} {} {} {} 2 1 3 3 1}
+do_test tkt1537-1.2 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON a1=b OR a2=b;
+ }
+} {1 {} {} {} {} 2 1 3 3 1}
+do_test tkt1537-1.3 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON a1=b OR a2=b;
+ }
+} {3 1 2 1 3 4 {} {} {} {}}
+ifcapable subquery {
+ do_test tkt1537-1.4 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b IN (a1,a2);
+ }
+ } {1 {} {} {} {} 2 1 3 3 1}
+ do_test tkt1537-1.5 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON b IN (a2,a1);
+ }
+ } {3 1 2 1 3 4 {} {} {} {}}
+}
+do_test tkt1537-1.6 {
+ execsql {
+ CREATE INDEX t1a1 ON t1(a1);
+ CREATE INDEX t1a2 ON t1(a2);
+ CREATE INDEX t2b ON t2(b);
+ SELECT * FROM t1 LEFT JOIN t2 ON a1=b OR a2=b;
+ }
+} {1 {} {} {} {} 2 1 3 3 1}
+do_test tkt1537-1.7 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON a1=b OR a2=b;
+ }
+} {3 1 2 1 3 4 {} {} {} {}}
+
+ifcapable subquery {
+ do_test tkt1537-1.8 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b IN (a1,a2);
+ }
+ } {1 {} {} {} {} 2 1 3 3 1}
+ do_test tkt1537-1.9 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON b IN (a2,a1);
+ }
+ } {3 1 2 1 3 4 {} {} {} {}}
+}
+
+execsql {
+ DROP INDEX t1a1;
+ DROP INDEX t1a2;
+ DROP INDEX t2b;
+}
+
+do_test tkt1537-2.1 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b BETWEEN a1 AND a2;
+ }
+} {1 {} {} {} {} 2 1 3 3 1}
+do_test tkt1537-2.2 {
+ execsql {
+ CREATE INDEX t2b ON t2(b);
+ SELECT * FROM t1 LEFT JOIN t2 ON b BETWEEN a1 AND a2;
+ }
+} {1 {} {} {} {} 2 1 3 3 1}
+do_test tkt1537-2.3 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON b BETWEEN a1 AND a2;
+ }
+} {3 1 2 1 3 4 {} {} {} {}}
+do_test tkt1537-2.4 {
+ execsql {
+ CREATE INDEX t1a1 ON t1(a1);
+ CREATE INDEX t1a2 ON t1(a2);
+ SELECT * FROM t2 LEFT JOIN t1 ON b BETWEEN a1 AND a2;
+ }
+} {3 1 2 1 3 4 {} {} {} {}}
+
+do_test tkt1537-3.1 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b GLOB 'abc*' WHERE t1.id=1;
+ }
+} {1 {} {} {} {}}
+do_test tkt1537-3.2 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t1 ON a1 GLOB 'abc*' WHERE t2.id=3;
+ }
+} {3 1 {} {} {}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1567.test b/third_party/sqlite/test/tkt1567.test
new file mode 100755
index 0000000..6c4548a
--- /dev/null
+++ b/third_party/sqlite/test/tkt1567.test
@@ -0,0 +1,51 @@
+# 2005 December 19 2005
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1567 is
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt1567-1.1 {
+ execsql {
+ CREATE TABLE t1(a TEXT PRIMARY KEY);
+ }
+ set bigstr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ for {set i 0} {$i<100} {incr i} {
+ set x [format %5d [expr $i*2]]
+ set sql "INSERT INTO t1 VALUES('$x-$bigstr')"
+ execsql $sql
+ }
+} {}
+integrity_check tkt1567-1.2
+
+do_test tkt1567-1.3 {
+ execsql {
+ BEGIN;
+ UPDATE t1 SET a = a||'x' WHERE rowid%2==0;
+ }
+} {}
+do_test tkt1567-1.4 {
+ catchsql {
+ UPDATE t1 SET a = CASE WHEN rowid<90 THEN substr(a,1,10) ELSE '9999' END;
+ }
+} {1 {column a is not unique}}
+do_test tkt1567-1.5 {
+ execsql {
+ COMMIT;
+ }
+} {}
+integrity_check tkt1567-1.6
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1644.test b/third_party/sqlite/test/tkt1644.test
new file mode 100755
index 0000000..aa26a88
--- /dev/null
+++ b/third_party/sqlite/test/tkt1644.test
@@ -0,0 +1,111 @@
+# 2006 January 30
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1644 is
+# fixed. Ticket #1644 complains that precompiled statements
+# are not expired correctly as a result of changes to TEMP
+# views and triggers.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tempdb||!view {
+ finish_test
+ return
+}
+
+# Create two tables T1 and T2 and make V1 point to T1.
+do_test tkt1644-1.1 {
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ CREATE TABLE t2(b);
+ INSERT INTO t2 VALUES(99);
+ CREATE TEMP VIEW v1 AS SELECT * FROM t1;
+ SELECT * FROM v1;
+ }
+} {1}
+
+# The "SELECT * FROM v1" should be in the TCL interface cache below.
+# It will continue to point to T1 unless the cache is invalidated when
+# the view changes.
+#
+do_test tkt1644-1.2 {
+ execsql {
+ DROP VIEW v1;
+ CREATE TEMP VIEW v1 AS SELECT * FROM t2;
+ SELECT * FROM v1;
+ }
+} {99}
+
+# Cache an access to the T1 table.
+#
+do_test tkt1644-1.3 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} {1}
+
+# Create a temp table T1. Make sure the cache is invalidated so that
+# the statement is recompiled and refers to the empty temp table.
+#
+do_test tkt1644-1.4 {
+ execsql {
+ CREATE TEMP TABLE t1(x);
+ }
+ execsql {
+ SELECT * FROM t1;
+ }
+} {}
+
+ifcapable view {
+ do_test tkt1644-2.1 {
+ execsql {
+ CREATE TEMP TABLE temp_t1(a, b);
+ }
+ set ::DB [sqlite3_connection_pointer db]
+ set ::STMT [sqlite3_prepare $::DB "SELECT * FROM temp_t1" -1 DUMMY]
+ execsql {
+ DROP TABLE temp_t1;
+ }
+ list [sqlite3_step $::STMT] [sqlite3_finalize $::STMT]
+ } {SQLITE_ERROR SQLITE_SCHEMA}
+
+ do_test tkt1644-2.2 {
+ execsql {
+ CREATE TABLE real_t1(a, b);
+ CREATE TEMP VIEW temp_v1 AS SELECT * FROM real_t1;
+ }
+ set ::DB [sqlite3_connection_pointer db]
+ set ::STMT [sqlite3_prepare $::DB "SELECT * FROM temp_v1" -1 DUMMY]
+ execsql {
+ DROP VIEW temp_v1;
+ }
+ list [sqlite3_step $::STMT] [sqlite3_finalize $::STMT]
+ } {SQLITE_ERROR SQLITE_SCHEMA}
+
+ do_test tkt1644-2.3 {
+ execsql {
+ CREATE TEMP VIEW temp_v1 AS SELECT * FROM real_t1 LIMIT 10 OFFSET 10;
+ }
+ set ::DB [sqlite3_connection_pointer db]
+ set ::STMT [sqlite3_prepare $::DB "SELECT * FROM temp_v1" -1 DUMMY]
+ execsql {
+ DROP VIEW temp_v1;
+ }
+ list [sqlite3_step $::STMT] [sqlite3_finalize $::STMT]
+ } {SQLITE_ERROR SQLITE_SCHEMA}
+}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1667.test b/third_party/sqlite/test/tkt1667.test
new file mode 100755
index 0000000..1dd78cb
--- /dev/null
+++ b/third_party/sqlite/test/tkt1667.test
@@ -0,0 +1,83 @@
+# 2006 February 10
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1667 has been
+# fixed.
+#
+#
+# $Id: tkt1667.test,v 1.3 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !autovacuum||!tclvar {
+ finish_test
+ return
+}
+
+db close
+file delete -force test.db test.db-journal
+
+# Set the pending byte offset such that the page it is on is
+# the first autovacuum pointer map page in the file (assume a page
+# size of 1024).
+
+set first_ptrmap_page [expr 1024/5 + 3]
+set sqlite_pending_byte [expr 1024 * ($first_ptrmap_page-1)]
+
+sqlite db test.db
+
+do_test tkt1667-1 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ }
+ for {set i 0} {$i < 500} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, randstr(1000, 2000))
+ }
+ }
+ execsql {
+ COMMIT;
+ }
+} {}
+for {set i 0} {$i < 500} {incr i} {
+ do_test tkt1667-2.$i.1 {
+ execsql {
+ DELETE FROM t1 WHERE a = $i;
+ }
+ } {}
+ integrity_check tkt1667-2.$i.2
+}
+
+do_test tkt1667-3 {
+ execsql {
+ BEGIN;
+ }
+ for {set i 0} {$i < 500} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, randstr(1000, 2000))
+ }
+ }
+ execsql {
+ COMMIT;
+ }
+} {}
+do_test tkt1667-4.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+integrity_check tkt1667-4.2
+
+finish_test
diff --git a/third_party/sqlite/test/tkt1873.test b/third_party/sqlite/test/tkt1873.test
new file mode 100755
index 0000000..85a5283
--- /dev/null
+++ b/third_party/sqlite/test/tkt1873.test
@@ -0,0 +1,72 @@
+# 2006 June 27
+#
+# 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.
+#
+# This file implements tests to verify that ticket #1873 has been
+# fixed.
+#
+#
+# $Id: tkt1873.test,v 1.2 2007/10/09 08:29:33 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !attach {
+ finish_test
+ return
+}
+
+file delete -force test2.db test2.db-journal
+
+do_test tkt1873-1.1 {
+ execsql {
+ CREATE TABLE t1(x, y);
+ ATTACH 'test2.db' AS aux;
+ CREATE TABLE aux.t2(x, y);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t2 VALUES(5, 6);
+ INSERT INTO t2 VALUES(7, 8);
+ }
+} {}
+
+do_test tkt1873-1.2 {
+ set rc [catch {
+ db eval {SELECT * FROM t2 LIMIT 1} {
+ db eval {DETACH aux}
+ }
+ } msg]
+ list $rc $msg
+} {1 {database aux is locked}}
+
+do_test tkt1873-1.3 {
+ set rc [catch {
+ db eval {SELECT * FROM t1 LIMIT 1} {
+ db eval {DETACH aux}
+ }
+ } msg]
+ list $rc $msg
+} {0 {}}
+
+do_test tkt1873-1.4 {
+ catchsql {
+ select * from t2;
+ }
+} {1 {no such table: t2}}
+
+do_test tkt1873-1.5 {
+ catchsql {
+ ATTACH 'test2.db' AS aux;
+ select * from t2;
+ }
+} {0 {5 6 7 8}}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2141.test b/third_party/sqlite/test/tkt2141.test
new file mode 100755
index 0000000..a40659f
--- /dev/null
+++ b/third_party/sqlite/test/tkt2141.test
@@ -0,0 +1,61 @@
+# 2007 January 03
+#
+# 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.
+#
+# This file implements tests to verify that ticket #2141 has been
+# fixed.
+#
+#
+# $Id: tkt2141.test,v 1.2 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+do_test tkt2141-1.1 {
+ execsql {
+ CREATE TABLE tab1 (t1_id integer PRIMARY KEY, t1_desc);
+ INSERT INTO tab1 VALUES(1,'rec 1 tab 1');
+ CREATE TABLE tab2 (t2_id integer PRIMARY KEY, t2_id_t1, t2_desc);
+ INSERT INTO tab2 VALUES(1,1,'rec 1 tab 2');
+ CREATE TABLE tab3 (t3_id integer PRIMARY KEY, t3_id_t2, t3_desc);
+ INSERT INTO tab3 VALUES(1,1,'aa');
+ SELECT *
+ FROM tab1 t1 LEFT JOIN tab2 t2 ON t1.t1_id = t2.t2_id_t1
+ WHERE t2.t2_id IN
+ (SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
+ WHERE t3_id IN (1,2) GROUP BY t2_id);
+ }
+} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
+do_test tkt2141-1.2 {
+ execsql {
+ SELECT *
+ FROM tab1 t1 LEFT JOIN tab2 t2 ON t1.t1_id = t2.t2_id_t1
+ WHERE t2.t2_id IN
+ (SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
+ WHERE t3_id IN (1,2));
+ }
+} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
+do_test tkt2141-1.3 {
+ execsql {
+ SELECT *
+ FROM tab1 t1 LEFT JOIN tab2 t2
+ WHERE t2.t2_id IN
+ (SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
+ WHERE t3_id IN (1,2));
+ }
+} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2192.test b/third_party/sqlite/test/tkt2192.test
new file mode 100755
index 0000000..72ef9a2
--- /dev/null
+++ b/third_party/sqlite/test/tkt2192.test
@@ -0,0 +1,140 @@
+# 2007 January 26
+#
+# 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.
+#
+# This file implements tests to verify that ticket #2192 has been
+# fixed.
+#
+#
+# $Id: tkt2192.test,v 1.3 2008/08/04 03:51:24 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !datetime||!compound {
+ finish_test
+ return
+}
+
+do_test tkt2192-1.1 {
+ execsql {
+ -- Raw data (RBS) --------
+
+ create table records (
+ date real,
+ type text,
+ description text,
+ value integer,
+ acc_name text,
+ acc_no text
+ );
+
+ -- Direct Debits ----------------
+ create view direct_debits as
+ select * from records where type = 'D/D';
+
+ create view monthly_direct_debits as
+ select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
+ from direct_debits
+ group by strftime('%Y-%m', date);
+
+ -- Expense Categories ---------------
+ create view energy as
+ select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
+ from direct_debits
+ where description like '%NPOWER%'
+ group by strftime('%Y-%m', date);
+
+ create view phone_internet as
+ select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
+ from direct_debits
+ where description like '%BT DIRECT%'
+ or description like '%SUPANET%'
+ or description like '%ORANGE%'
+ group by strftime('%Y-%m', date);
+
+ create view credit_cards as
+ select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
+ from direct_debits where description like '%VISA%'
+ group by strftime('%Y-%m', date);
+
+ -- Overview ---------------------
+
+ create view expense_overview as
+ select 'Energy' as expense, date, value from energy
+ union
+ select 'Phone/Internet' as expense, date, value from phone_internet
+ union
+ select 'Credit Card' as expense, date, value from credit_cards;
+
+ create view jan as
+ select 'jan', expense, value from expense_overview
+ where date like '%-01';
+
+ create view nov as
+ select 'nov', expense, value from expense_overview
+ where date like '%-11';
+
+ create view summary as
+ select * from jan join nov on (jan.expense = nov.expense);
+ }
+} {}
+do_test tkt2192-1.2 {
+ # set ::sqlite_addop_trace 1
+ execsql {
+ select * from summary;
+ }
+} {}
+do_test tkt2192-2.1 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ CREATE VIEW v1 AS
+ SELECT * FROM t1 WHERE b%7=0 UNION SELECT * FROM t1 WHERE b%5=0;
+ INSERT INTO t1 VALUES(1,7);
+ INSERT INTO t1 VALUES(2,10);
+ INSERT INTO t1 VALUES(3,14);
+ INSERT INTO t1 VALUES(4,15);
+ INSERT INTO t1 VALUES(1,16);
+ INSERT INTO t1 VALUES(2,17);
+ INSERT INTO t1 VALUES(3,20);
+ INSERT INTO t1 VALUES(4,21);
+ INSERT INTO t1 VALUES(1,22);
+ INSERT INTO t1 VALUES(2,24);
+ INSERT INTO t1 VALUES(3,25);
+ INSERT INTO t1 VALUES(4,26);
+ INSERT INTO t1 VALUES(1,27);
+
+ SELECT b FROM v1 ORDER BY b;
+ }
+} {7 10 14 15 20 21 25}
+do_test tkt2192-2.2 {
+ execsql {
+ SELECT * FROM v1 ORDER BY a, b;
+ }
+} {1 7 2 10 3 14 3 20 3 25 4 15 4 21}
+do_test tkt2192-2.3 {
+ execsql {
+ SELECT x.a || '/' || x.b || '/' || y.b
+ FROM v1 AS x JOIN v1 AS y ON x.a=y.a AND x.b<y.b
+ ORDER BY x.a, x.b, y.b
+ }
+} {3/14/20 3/14/25 3/20/25 4/15/21}
+do_test tkt2192-2.4 {
+ execsql {
+ CREATE VIEW v2 AS
+ SELECT x.a || '/' || x.b || '/' || y.b AS z
+ FROM v1 AS x JOIN v1 AS y ON x.a=y.a AND x.b<y.b
+ ORDER BY x.a, x.b, y.b;
+ SELECT * FROM v2;
+ }
+} {3/14/20 3/14/25 3/20/25 4/15/21}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2213.test b/third_party/sqlite/test/tkt2213.test
new file mode 100755
index 0000000..d5a81ab
--- /dev/null
+++ b/third_party/sqlite/test/tkt2213.test
@@ -0,0 +1,29 @@
+# 2007 Febuary 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 implements regression tests for SQLite library.
+#
+# This file implements tests to verify that ticket #2213 has been
+# fixed.
+#
+#
+# $Id: tkt2213.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2213-1 {
+ sqlite3_create_function db
+ catchsql {
+ SELECT tkt2213func(tkt2213func('abcd'));
+ }
+} {0 abcd}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2251.test b/third_party/sqlite/test/tkt2251.test
new file mode 100755
index 0000000..799b7d6
--- /dev/null
+++ b/third_party/sqlite/test/tkt2251.test
@@ -0,0 +1,108 @@
+# 2007 Febuary 24
+#
+# 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.
+#
+# This file implements tests to verify that table column values
+# are pulled out of the database correctly.
+#
+# Long ago, the OP_Column opcode was sufficient to pull out the
+# value of a table column. But then we added the ALTER TABLE ADD COLUMN
+# feature. An added column might not actually exist in every row,
+# and so the OP_Column opcode has to contain a default value. Later
+# still we added a feature whereby a REAL value with no fractional
+# part is stored in the database file as an integer to save space.
+# After extracting the value, we have to call OP_RealAffinity to
+# convert it back to a REAL.
+#
+# The sqlite3ExprCodeGetColumn() routine was added to take care of
+# all of the complications above. The tests in this file attempt
+# to verify that sqlite3ExprCodeGetColumn() is used instead of a
+# raw OP_Column in all places where a table column is extracted from
+# the database.
+#
+# $Id: tkt2251.test,v 1.2 2007/09/12 17:01:45 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+# Create sample data. Verify that the default value and type of an added
+# column is correct for aggregates.
+do_test tkt2251-1.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(2);
+ ALTER TABLE t1 ADD COLUMN b REAL DEFAULT 4.0;
+ SELECT avg(b), typeof(avg(b)) FROM t1;
+ }
+} {4.0 real}
+do_test tkt2251-1.2 {
+ execsql {
+ SELECT sum(b), typeof(sum(b)) FROM t1;
+ }
+} {32.0 real}
+do_test tkt2251-1.3 {
+ execsql {
+ SELECT a, sum(b), typeof(sum(b)) FROM t1 GROUP BY a ORDER BY a;
+ }
+} {1 8.0 real 2 8.0 real 3 4.0 real 9 12.0 real}
+
+# Make sure that the REAL value comes out when values are accessed
+# by index.
+#
+do_test tkt2251-2.1 {
+ execsql {
+ SELECT b, typeof(b) FROM t1 WHERE a=3;
+ }
+} {4.0 real}
+do_test tkt2251-2.2 {
+ execsql {
+ CREATE INDEX t1i1 ON t1(a,b);
+ SELECT b, typeof(b) FROM t1 WHERE a=3;
+ }
+} {4.0 real}
+do_test tkt2251-2.3 {
+ execsql {
+ REINDEX;
+ SELECT b, typeof(b) FROM t1 WHERE a=3;
+ }
+} {4.0 real}
+
+# Make sure the correct REAL value is used when copying from one
+# table to another.
+#
+do_test tkt2251-3.1 {
+ execsql {
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 SELECT * FROM t1;
+ SELECT y, typeof(y) FROM t2 WHERE x=3;
+ }
+} {4.0 real}
+do_test tkt2251-3.2 {
+ execsql {
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ SELECT b, typeof(b) FROM t3 WHERE a=3;
+ }
+} {4.0 real}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2285.test b/third_party/sqlite/test/tkt2285.test
new file mode 100755
index 0000000..f177c22
--- /dev/null
+++ b/third_party/sqlite/test/tkt2285.test
@@ -0,0 +1,56 @@
+# 2005 September 17
+#
+# 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. Specifically.
+# it contains tests to verify that ticket #2285 has been fixed.
+#
+# $Id: tkt2285.test,v 1.2 2008/07/12 14:52:20 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tempdb {
+ finish_test
+ return
+}
+
+do_test tkt2285-1.1 {
+ execsql {
+ PRAGMA locking_mode = EXCLUSIVE;
+ }
+ execsql {
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ ROLLBACK;
+ }
+} {}
+
+do_test tkt2285-1.2 {
+ execsql {
+ SELECT * FROM sqlite_master;
+ }
+} {}
+
+ifcapable tempdb {
+ do_test tkt2285-2.1 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE abc(a, b, c);
+ ROLLBACK;
+ }
+ } {}
+ do_test tkt2285-2.2 {
+ execsql {
+ SELECT * FROM sqlite_temp_master;
+ }
+ } {}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2332.test b/third_party/sqlite/test/tkt2332.test
new file mode 100755
index 0000000..2d09dda
--- /dev/null
+++ b/third_party/sqlite/test/tkt2332.test
@@ -0,0 +1,67 @@
+# 2007 May 3
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: tkt2332.test,v 1.4 2007/09/12 17:01:45 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !incrblob||!tclvar {
+ finish_test
+ return
+}
+
+do_test tkt2332.1 {
+ execsql {
+ CREATE TABLE blobs (k INTEGER PRIMARY KEY, v BLOB);
+ PRAGMA cache_size = 100;
+ }
+} {}
+
+set ::iKey 1
+foreach Len [list 10000 100000 1000000] {
+ do_test tkt2332.$Len.1 {
+ set val "[expr rand()][expr rand()][expr rand()][expr rand()][expr rand()]"
+ set ::blobstr [string range \
+ [string repeat $val [expr ($Len/[string length $val])+1]] 0 [expr $Len-1]
+ ]
+
+ db eval { INSERT INTO blobs VALUES($::iKey, zeroblob($Len)) }
+ } {}
+
+ do_test tkt2332.$Len.2 {
+ execsql {
+ SELECT length(v) FROM blobs WHERE k = $::iKey;
+ }
+ } $Len
+
+ do_test tkt2332.$Len.3 {
+ set ::fd [db incrblob blobs v $::iKey]
+ puts -nonewline $::fd $::blobstr
+ close $::fd
+ } {}
+
+ do_test tkt2332.$Len.4 {
+ execsql { SELECT length(v) FROM blobs WHERE k = $::iKey; }
+ } $Len
+
+ do_test tkt2332.$Len.5 {
+ lindex [execsql {SELECT v FROM blobs WHERE k = $::iKey}] 0
+ } $::blobstr
+
+ incr ::iKey
+}
+
+# Free memory:
+unset ::blobstr
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2339.test b/third_party/sqlite/test/tkt2339.test
new file mode 100755
index 0000000..41acd37
--- /dev/null
+++ b/third_party/sqlite/test/tkt2339.test
@@ -0,0 +1,100 @@
+# 2007 May 6
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: tkt2339.test,v 1.2 2007/09/12 17:01:45 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery||!compound {
+ finish_test
+ return
+}
+
+do_test tkt2339.1 {
+ execsql {
+ create table t1(num int);
+ insert into t1 values (1);
+ insert into t1 values (2);
+ insert into t1 values (3);
+ insert into t1 values (4);
+
+ create table t2(num int);
+ insert into t2 values (11);
+ insert into t2 values (12);
+ insert into t2 values (13);
+ insert into t2 values (14);
+
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ UNION
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC LIMIT 2)
+ }
+} {3 4 13 14}
+do_test tkt2339.2 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ UNION ALL
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC LIMIT 2)
+ }
+} {4 3 14 13}
+do_test tkt2339.3 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC)
+ UNION ALL
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC LIMIT 2)
+ }
+} {4 3 2 1 14 13}
+do_test tkt2339.4 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ UNION ALL
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC)
+ }
+} {4 3 14 13 12 11}
+do_test tkt2339.5 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ UNION
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC)
+ }
+} {3 4 11 12 13 14}
+do_test tkt2339.6 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ EXCEPT
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC)
+ }
+} {3 4}
+do_test tkt2339.7 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 LIMIT 2)
+ UNION
+ SELECT * FROM (SELECT * FROM t2 ORDER BY num DESC LIMIT 2)
+ }
+} {1 2 13 14}
+do_test tkt2339.8 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 LIMIT 2)
+ UNION
+ SELECT * FROM (SELECT * FROM t2 LIMIT 2)
+ }
+} {1 2 11 12}
+do_test tkt2339.9 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t1 ORDER BY num DESC LIMIT 2)
+ UNION
+ SELECT * FROM (SELECT * FROM t2 LIMIT 2)
+ }
+} {3 4 11 12}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2391.test b/third_party/sqlite/test/tkt2391.test
new file mode 100755
index 0000000..d192b85
--- /dev/null
+++ b/third_party/sqlite/test/tkt2391.test
@@ -0,0 +1,49 @@
+#
+# 2007 May 28
+#
+# 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.
+#
+#***********************************************************************
+# $Id: tkt2391.test,v 1.1 2007/05/29 12:11:30 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2391.1 {
+ execsql {
+ CREATE TABLE folders(folderid, parentid, foldername COLLATE binary);
+ INSERT INTO folders VALUES(1, 3, 'FolderA');
+ INSERT INTO folders VALUES(1, 3, 'folderB');
+ INSERT INTO folders VALUES(4, 0, 'FolderC');
+ }
+} {}
+
+do_test tkt2391.2 {
+ execsql {
+ SELECT count(*) FROM folders WHERE foldername < 'FolderC';
+ }
+} {1}
+
+do_test tkt2391.3 {
+ execsql {
+ SELECT count(*) FROM folders WHERE foldername < 'FolderC' COLLATE nocase;
+ }
+} {2}
+
+# This demonstrates the bug. Creating the index causes SQLite to ignore
+# the "COLLATE nocase" clause and use the default collation sequence
+# for column "foldername" instead (happens to be BINARY in this case).
+#
+do_test tkt2391.4 {
+ execsql {
+ CREATE INDEX f_i ON folders(foldername);
+ SELECT count(*) FROM folders WHERE foldername < 'FolderC' COLLATE nocase;
+ }
+} {2}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2409.test b/third_party/sqlite/test/tkt2409.test
new file mode 100755
index 0000000..54e8265
--- /dev/null
+++ b/third_party/sqlite/test/tkt2409.test
@@ -0,0 +1,218 @@
+# 2007 June 13
+#
+# 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.
+#
+# This file implements tests to verify that ticket #2409 has been
+# fixed. More specifically, they verify that if SQLite cannot
+# obtain an EXCLUSIVE lock while trying to spill the cache during
+# any statement other than a COMMIT, an I/O error is returned instead
+# of SQLITE_BUSY.
+#
+# $Id: tkt2409.test,v 1.3 2007/09/12 17:01:45 danielk1977 Exp $
+
+# Test Outline:
+#
+# tkt-2409-1.*: Cause a cache-spill during an INSERT that is within
+# a db transaction but does not start a statement transaction.
+# Verify that the transaction is automatically rolled back
+# and SQLITE_IOERR_BLOCKED is returned
+#
+# tkt-2409-2.*: Cause a cache-spill while updating the change-counter
+# during a database COMMIT. Verify that the transaction is not
+# rolled back and SQLITE_BUSY is returned.
+#
+# tkt-2409-3.*: Similar to 2409-1.*, but using many INSERT statements
+# within a transaction instead of just one.
+#
+# tkt-2409-4.*: Similar to 2409-1.*, but rig it so that the
+# INSERT statement starts a statement transaction. Verify that
+# SQLOTE_BUSY is returned and the transaction is not rolled back.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !pager_pragmas {
+ finish_test
+ return
+}
+
+sqlite3_extended_result_codes $::DB 1
+
+# Aquire a read-lock on the database using handle [db2].
+#
+proc read_lock_db {} {
+ if {$::STMT eq ""} {
+ set ::STMT [sqlite3_prepare db2 {SELECT rowid FROM sqlite_master} -1 TAIL]
+ set rc [sqlite3_step $::STMT]
+ if {$rc eq "SQLITE_ERROR"} {
+ unread_lock_db
+ read_lock_db
+ }
+ }
+}
+
+# Release any read-lock obtained using [read_lock_db]
+#
+proc unread_lock_db {} {
+ if {$::STMT ne ""} {
+ sqlite3_finalize $::STMT
+ set ::STMT ""
+ }
+}
+
+# Open the db handle used by [read_lock_db].
+#
+sqlite3 db2 test.db
+set ::STMT ""
+
+do_test tkt2409-1.1 {
+ execsql {
+ PRAGMA cache_size=10;
+ CREATE TABLE t1(x TEXT UNIQUE NOT NULL, y BLOB);
+ }
+ read_lock_db
+ set ::zShort [string repeat 0123456789 1]
+ set ::zLong [string repeat 0123456789 1500]
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 VALUES($::zShort, $::zLong);
+ }
+} {1 {disk I/O error}}
+
+do_test tkt2409-1.2 {
+ sqlite3_errcode $::DB
+} {SQLITE_IOERR+11}
+
+# Check the integrity of the cache.
+#
+integrity_check tkt2409-1.3
+
+# Check that the transaction was rolled back. Because the INSERT
+# statement in which the "I/O error" occured did not open a statement
+# transaction, SQLite had no choice but to roll back the transaction.
+#
+do_test tkt2409-1.4 {
+ unread_lock_db
+ catchsql { ROLLBACK }
+} {1 {cannot rollback - no transaction is active}}
+
+
+set ::zShort [string repeat 0123456789 1]
+set ::zLong [string repeat 0123456789 1500]
+set ::rc 1
+for {set iCache 10} {$::rc} {incr iCache} {
+ execsql "PRAGMA cache_size = $iCache"
+ do_test tkt2409-2.1.$iCache {
+ read_lock_db
+ set ::rc [catch {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES($::zShort, $::zLong);
+ }
+ } msg]
+ expr {($::rc == 1 && $msg eq "disk I/O error") || $::rc == 0}
+ } {1}
+}
+
+do_test tkt2409-2.2 {
+ catchsql {
+ ROLLBACK;
+ BEGIN;
+ INSERT INTO t1 VALUES($::zShort, $::zLong);
+ COMMIT;
+ }
+} {1 {database is locked}}
+
+do_test tkt2409-2.3 {
+ unread_lock_db
+ catchsql {
+ COMMIT;
+ }
+} {0 {}}
+
+do_test tkt2409-3.1 {
+ db close
+ set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
+ sqlite3_extended_result_codes $::DB 1
+ execsql {
+ PRAGMA cache_size=10;
+ DELETE FROM t1;
+ }
+ read_lock_db
+ set ::zShort [string repeat 0123456789 1]
+ set ::zLong [string repeat 0123456789 1500]
+ catchsql {
+ BEGIN;
+ INSERT INTO t1 SELECT $::zShort, $::zLong;
+ }
+} {1 {database is locked}}
+
+do_test tkt2409-3.2 {
+ sqlite3_errcode $::DB
+} {SQLITE_BUSY}
+
+# Check the integrity of the cache.
+#
+integrity_check tkt2409-3.3
+
+# Check that the transaction was rolled back. Because the INSERT
+# statement in which the "I/O error" occured did not open a statement
+# transaction, SQLite had no choice but to roll back the transaction.
+#
+do_test tkt2409-3.4 {
+ unread_lock_db
+ catchsql { ROLLBACK }
+} {0 {}}
+
+
+do_test tkt2409-4.1 {
+ execsql {
+ PRAGMA cache_size=20;
+ DROP TABLE t1;
+ CREATE TABLE t1 (x TEXT UNIQUE NOT NULL);
+ }
+
+ unset -nocomplain t1
+ array unset t1
+ set t1(0) 1
+ set sql ""
+ for {set i 0} {$i<5000} {incr i} {
+ set r 0
+ while {[info exists t1($r)]} {
+ set r [expr {int(rand()*1000000000)}]
+ }
+ set t1($r) 1
+ append sql "INSERT INTO t1 VALUES('some-text-$r');"
+ }
+
+ read_lock_db
+ execsql BEGIN
+ catchsql $sql
+} {1 {disk I/O error}}
+
+do_test tkt2409-4.2 {
+ sqlite3_errcode $::DB
+} {SQLITE_IOERR+11}
+
+# Check the integrity of the cache.
+#
+integrity_check tkt2409-4.3
+
+do_test tkt2409-4.4 {
+ catchsql { ROLLBACK }
+} {1 {cannot rollback - no transaction is active}}
+
+
+unread_lock_db
+db2 close
+unset -nocomplain t1
+finish_test
diff --git a/third_party/sqlite/test/tkt2450.test b/third_party/sqlite/test/tkt2450.test
new file mode 100755
index 0000000..50f7189
--- /dev/null
+++ b/third_party/sqlite/test/tkt2450.test
@@ -0,0 +1,48 @@
+# 2007 June 24
+#
+# 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 is to test that ticket #2450 has been fixed.
+#
+# $Id: tkt2450.test,v 1.1 2007/06/24 06:32:18 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2450-1 {
+ execsql {
+ CREATE TABLE "t a" ("""cb""");
+ INSERT INTO "t a" ("""cb""") VALUES (1);
+ SELECT """cb""" FROM "t a";
+ }
+} {1}
+
+do_test tkt2450-2 {
+ execsql {
+ SELECT * FROM "t a";
+ }
+} {1}
+
+do_test tkt2450-3 {
+ execsql {
+ SELECT "t a".* FROM "t a";
+ }
+} {1}
+
+do_test tkt2450-3 {
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(2);
+ SELECT * FROM "t a", t1;
+ }
+} {1 2}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2640.test b/third_party/sqlite/test/tkt2640.test
new file mode 100755
index 0000000..13a17e0
--- /dev/null
+++ b/third_party/sqlite/test/tkt2640.test
@@ -0,0 +1,124 @@
+# 2007 Sep 12
+#
+# 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 is to test that ticket #2640 has been fixed.
+#
+# $Id: tkt2640.test,v 1.3 2008/08/04 03:51:24 danielk1977 Exp $
+#
+
+# The problem in ticket #2640 was that the query optimizer was
+# not recognizing all uses of tables within subqueries in the
+# WHERE clause. If the subquery contained a compound SELECT,
+# then tables that were used by terms of the compound other than
+# the last term would not be recognized as dependencies.
+# So if one of the SELECT statements within a compound made
+# use of a table that occurs later in a join, the query
+# optimizer would not recognize this and would try to evaluate
+# the subquery too early, before that tables value had been
+# established.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery||!compound {
+ finish_test
+ return
+}
+
+do_test tkt2640-1.1 {
+ execsql {
+ CREATE TABLE persons(person_id, name);
+ INSERT INTO persons VALUES(1,'fred');
+ INSERT INTO persons VALUES(2,'barney');
+ INSERT INTO persons VALUES(3,'wilma');
+ INSERT INTO persons VALUES(4,'pebbles');
+ INSERT INTO persons VALUES(5,'bambam');
+ CREATE TABLE directors(person_id);
+ INSERT INTO directors VALUES(5);
+ INSERT INTO directors VALUES(3);
+ CREATE TABLE writers(person_id);
+ INSERT INTO writers VALUES(2);
+ INSERT INTO writers VALUES(3);
+ INSERT INTO writers VALUES(4);
+ SELECT DISTINCT p.name
+ FROM persons p, directors d
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=p.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+do_test tkt2640-1.2 {
+ execsql {
+ SELECT DISTINCT p.name
+ FROM persons p CROSS JOIN directors d
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=p.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+do_test tkt2640-1.3 {
+ execsql {
+ SELECT DISTINCT p.name
+ FROM directors d CROSS JOIN persons p
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=p.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+do_test tkt2640-1.4 {
+ execsql {
+ SELECT DISTINCT p.name
+ FROM persons p, directors d
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=d.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+do_test tkt2640-1.5 {
+ execsql {
+ SELECT DISTINCT p.name
+ FROM persons p CROSS JOIN directors d
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=d.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+do_test tkt2640-1.6 {
+ execsql {
+ SELECT DISTINCT p.name
+ FROM directors d CROSS JOIN persons p
+ WHERE d.person_id=p.person_id
+ AND NOT EXISTS (
+ SELECT person_id FROM directors d1 WHERE d1.person_id=d.person_id
+ EXCEPT
+ SELECT person_id FROM writers w
+ );
+ }
+} {wilma}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2643.test b/third_party/sqlite/test/tkt2643.test
new file mode 100755
index 0000000..5c58863
--- /dev/null
+++ b/third_party/sqlite/test/tkt2643.test
@@ -0,0 +1,39 @@
+# 2007 Sep 12
+#
+# 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 is to test that ticket #2643 has been fixed.
+#
+# $Id: tkt2643.test,v 1.1 2007/09/13 17:54:41 drh Exp $
+#
+
+# The problem in ticket #2643 has to do with the query optimizer
+# making bad assumptions about index cost when data from ANALYZE
+# is available.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2643-1.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE, c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(2,3,4);
+ ANALYZE;
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ CREATE INDEX i1 ON t1(c);
+ SELECT count(*) FROM t1 WHERE c IS NOT NULL
+ }
+} {2}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2686.test b/third_party/sqlite/test/tkt2686.test
new file mode 100755
index 0000000..c03c686
--- /dev/null
+++ b/third_party/sqlite/test/tkt2686.test
@@ -0,0 +1,81 @@
+# 2007 Oct 3
+#
+# 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 is to test that ticket #2686 has been fixed.
+#
+# $Id: tkt2686.test,v 1.3 2008/02/02 02:48:52 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+db eval {
+ PRAGMA page_size=1024;
+ PRAGMA max_page_count=50;
+ PRAGMA auto_vacuum=0;
+ CREATE TABLE filler (fill);
+}
+for {set i 1} {$i<2000} {incr i} {
+ do_test tkt2686-$i.1 {
+ db eval BEGIN
+ set rc [catch {
+ while 1 {
+ db eval {INSERT INTO filler (fill) VALUES (randstr(1000, 10000)) }
+ }
+ } msg]
+ lappend rc $msg
+ } {1 {database or disk is full}}
+ do_test tkt2686-$i.2 {
+ execsql {
+ DELETE FROM filler
+ WHERE rowid <= (SELECT MAX(rowid) FROM filler LIMIT 20)
+ }
+ } {}
+ integrity_check tkt2686-$i.3
+ catch {db eval COMMIT}
+}
+
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+
+db eval {
+ PRAGMA page_size=1024;
+ PRAGMA max_page_count=50;
+ PRAGMA auto_vacuum=1;
+ CREATE TABLE filler (fill);
+}
+for {set i 10000} {$i<12000} {incr i} {
+ do_test tkt2686-$i.1 {
+ db eval BEGIN
+ set rc [catch {
+ while 1 {
+ db eval {INSERT INTO filler (fill) VALUES (randstr(1000, 10000)) }
+ }
+ } msg]
+ lappend rc $msg
+ } {1 {database or disk is full}}
+ do_test tkt2686-$i.2 {
+ execsql {
+ DELETE FROM filler
+ WHERE rowid <= (SELECT MAX(rowid) FROM filler LIMIT 20)
+ }
+ } {}
+ integrity_check tkt2686-$i.3
+ catch {db eval COMMIT}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2767.test b/third_party/sqlite/test/tkt2767.test
new file mode 100755
index 0000000..6d642b4
--- /dev/null
+++ b/third_party/sqlite/test/tkt2767.test
@@ -0,0 +1,88 @@
+# 2007 Oct 3
+#
+# 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 is to test that ticket #2767 has been fixed.
+# Ticket #2767 is for a VDBE stack overflow on BEFORE
+# triggers that run RAISE(IGNORE).
+#
+# $Id: tkt2767.test,v 1.2 2008/07/12 14:52:21 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2767-1.1 {
+ execsql {
+ -- Construct a table with many rows of data
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 SELECT x+2 FROM t1;
+ INSERT INTO t1 SELECT x+4 FROM t1;
+ INSERT INTO t1 SELECT x+8 FROM t1;
+ INSERT INTO t1 SELECT x+16 FROM t1;
+
+ -- BEFORE triggers that invoke raise(ignore). The effect of
+ -- these triggers should be to make INSERTs, UPDATEs, and DELETEs
+ -- into no-ops.
+ CREATE TRIGGER r1 BEFORE UPDATE ON t1 BEGIN
+ SELECT raise(ignore);
+ END;
+ CREATE TRIGGER r2 BEFORE DELETE ON t1 BEGIN
+ SELECT raise(ignore);
+ END;
+ CREATE TRIGGER r3 BEFORE INSERT ON t1 BEGIN
+ SELECT raise(ignore);
+ END;
+
+ -- Verify the table content
+ SELECT count(*), sum(x) FROM t1;
+ }
+} {32 528}
+
+# Try to delete all elements of the table. This will invoke the
+# DELETE trigger 32 times, which should overflow the VDBE stack if
+# the problem of #2767 is not fixed. If the problem is fixed, all
+# the deletes should be no-ops so the table should remain unchanged.
+#
+do_test tkt2767-1.2 {
+ execsql {
+ DELETE FROM t1 WHERE x>0;
+ SELECT count(*), sum(x) FROM t1;
+ }
+} {32 528}
+
+# Try to update all elements of the table. This will invoke the
+# UPDATE trigger 32 times, which should overflow the VDBE stack if
+# the problem of #2767 is not fixed. If the problem is fixed, all
+# the updates should be no-ops so the table should remain unchanged.
+#
+do_test tkt2767-1.3 {
+ execsql {
+ UPDATE t1 SET x=x+1;
+ SELECT count(*), sum(x) FROM t1;
+ }
+} {32 528}
+
+# Invoke the insert trigger. The insert trigger was working
+# even prior to the fix of #2767. But it seems good to go ahead
+# and verify that it works.
+#
+do_test tkt2767-1.4 {
+ execsql {
+ INSERT INTO t1 SELECT x+32 FROM t1;
+ SELECT count(*), sum(x) FROM t1;
+ }
+} {32 528}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2817.test b/third_party/sqlite/test/tkt2817.test
new file mode 100755
index 0000000..46b7fe3
--- /dev/null
+++ b/third_party/sqlite/test/tkt2817.test
@@ -0,0 +1,72 @@
+# 2007 December 02
+#
+# 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.
+#
+# Specifically, it tests that bug 2817 is fixed.
+#
+# $Id: tkt2817.test,v 1.2 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2817-1.0 {
+ execsql {
+ CREATE TEMP TABLE tbl(a, b, c);
+ -- INSERT INTO tbl VALUES(1, 'abc', 'def');
+ -- INSERT INTO tbl VALUES(2, 'ghi', 'jkl');
+ }
+} {}
+do_test tkt2817-1.1 {
+ execsql {
+ CREATE TABLE main.tbl(a, b, c);
+ CREATE INDEX main.tbli ON tbl(a, b, c);
+ INSERT INTO main.tbl SELECT a, b, c FROM temp.tbl;
+ }
+} {}
+
+# When bug #2817 existed, this test was failing.
+#
+integrity_check tkt2817-1.2
+
+# So was this one.
+#
+db close
+sqlite3 db test.db
+integrity_check tkt2817-1.3
+
+
+# These tests - tkt2817-2.* - are the same as the previous block, except
+# for the fact that the temp-table and the main table do not share the
+# same name. #2817 did not cause a problem with these tests.
+#
+db close
+file delete -force test.db
+sqlite3 db test.db
+do_test tkt2817-2.0 {
+ execsql {
+ CREATE TEMP TABLE tmp(a, b, c);
+ INSERT INTO tmp VALUES(1, 'abc', 'def');
+ INSERT INTO tmp VALUES(2, 'ghi', 'jkl');
+ }
+} {}
+do_test tkt2817-2.1 {
+ execsql {
+ CREATE TABLE main.tbl(a, b, c);
+ CREATE INDEX main.tbli ON tbl(a, b, c);
+ INSERT INTO main.tbl SELECT a, b, c FROM temp.tmp;
+ }
+} {}
+integrity_check tkt2817-2.2
+db close
+sqlite3 db test.db
+integrity_check tkt2817-2.3
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2820.test b/third_party/sqlite/test/tkt2820.test
new file mode 100755
index 0000000..106c1e5
--- /dev/null
+++ b/third_party/sqlite/test/tkt2820.test
@@ -0,0 +1,94 @@
+# 2007 Dec 4
+#
+# 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 is to test that ticket #2820 has been fixed.
+# Ticket #2820 observes that a DROP TABLE statement that
+# occurs while a query is in process will fail with a
+# "database is locked" error, but the entry in the sqlite_master
+# table will still be removed. This is incorrect. The
+# entry in the sqlite_master table should persist when
+# the DROP fails due to an error.
+#
+# $Id: tkt2820.test,v 1.1 2007/12/04 16:54:53 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc test_schema_change {testid init ddl res} {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ execsql $init
+ do_test tkt2820-$testid.1 {
+ set STMT [sqlite3_prepare db {SELECT * FROM sqlite_master} -1 DUMMY]
+ sqlite3_step $STMT
+ } {SQLITE_ROW}
+#if {$testid==3} {execsql {PRAGMA vdbe_trace=ON}}
+ do_test tkt2820-$testid.2 "catchsql [list $ddl]" \
+ {1 {database table is locked}}
+ do_test tkt2820-$testid.3 {
+ sqlite3_finalize $STMT
+ execsql {SELECT name FROM sqlite_master ORDER BY 1}
+ } $res
+ integrity_check tkt2820-$testid.4
+ db close
+ sqlite3 db test.db
+ integrity_check tkt2820-$testid.5
+}
+
+test_schema_change 1 {
+ CREATE TABLE t1(a);
+} {
+ DROP TABLE t1
+} {t1}
+test_schema_change 2 {
+ CREATE TABLE t1(a);
+ CREATE TABLE t2(b);
+} {
+ DROP TABLE t2
+} {t1 t2}
+test_schema_change 3 {
+ CREATE TABLE t1(a);
+ CREATE INDEX i1 ON t1(a);
+} {
+ DROP INDEX i1
+} {i1 t1}
+
+# We further observe that prior to the fix associated with ticket #2820,
+# no statement journal would be created on an SQL statement that was run
+# while a second statement was active, as long as we are in autocommit
+# mode. This is incorrect.
+#
+do_test tkt2820-4.1 {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ }
+
+ # The INSERT statement within the loop should fail on a
+ # constraint violation on the second inserted row. This
+ # should cause the entire INSERT to rollback using a statement
+ # journal.
+ #
+ db eval {SELECT name FROM sqlite_master} {
+ catch {db eval {
+ INSERT INTO t1 SELECT a+1 FROM t1 ORDER BY a DESC
+ }}
+ }
+ db eval {SELECT a FROM t1 ORDER BY a}
+} {1 2}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2822.test b/third_party/sqlite/test/tkt2822.test
new file mode 100755
index 0000000..fbb3002
--- /dev/null
+++ b/third_party/sqlite/test/tkt2822.test
@@ -0,0 +1,336 @@
+# 2007 Dec 4
+#
+# 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 is to test that the issues surrounding expressions in
+# ORDER BY clauses on compound SELECT statements raised by ticket
+# #2822 have been dealt with.
+#
+# $Id: tkt2822.test,v 1.5 2008/08/04 03:51:24 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+# The ORDER BY matching algorithm is three steps:
+#
+# (1) If the ORDER BY term is an integer constant i, then
+# sort by the i-th column of the result set.
+#
+# (2) If the ORDER BY term is an identifier (not x.y or x.y.z
+# but simply x) then look for a column alias with the same
+# name. If found, then sort by that column.
+#
+# (3) Evaluate the term as an expression and sort by the
+# value of the expression.
+#
+# For a compound SELECT the rules are modified slightly.
+# In the third rule, the expression must exactly match one
+# of the result columns. The sequences of three rules is
+# attempted first on the left-most SELECT. If that doesn't
+# work, we move to the right, one by one.
+#
+# Rule (3) is not in standard SQL - it is an SQLite extension,
+# though one copied from PostgreSQL. The rule for compound
+# queries where a search is made of SELECTs to the right
+# if the left-most SELECT does not match is not a part of
+# standard SQL either. This extension is unique to SQLite
+# as far as we know.
+#
+# Rule (2) was added by the changes ticket #2822. Prior to
+# that changes, SQLite did not support rule (2), making it
+# technically in violation of standard SQL semantics.
+# No body noticed because rule (3) has the same effect as
+# rule (2) except in some obscure cases.
+#
+
+
+# Test plan:
+#
+# tkt2822-1.* - Simple identifier as ORDER BY expression.
+# tkt2822-2.* - More complex ORDER BY expressions.
+
+do_test tkt2822-0.1 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(a, b, c);
+
+ INSERT INTO t1 VALUES(1, 3, 9);
+ INSERT INTO t1 VALUES(3, 9, 27);
+ INSERT INTO t1 VALUES(5, 15, 45);
+
+ INSERT INTO t2 VALUES(2, 6, 18);
+ INSERT INTO t2 VALUES(4, 12, 36);
+ INSERT INTO t2 VALUES(6, 18, 54);
+ }
+} {}
+
+# Test the "ORDER BY <integer>" syntax.
+#
+do_test tkt2822-1.1 {
+ execsql {
+ SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY 1;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+do_test tkt2822-1.2 {
+ execsql {
+ SELECT a, CAST (b AS TEXT), c FROM t1
+ UNION ALL
+ SELECT a, b, c FROM t2
+ ORDER BY 2;
+ }
+} {2 6 18 4 12 36 6 18 54 5 15 45 1 3 9 3 9 27}
+
+# Test the "ORDER BY <identifier>" syntax.
+#
+do_test tkt2822-2.1 {
+ execsql {
+ SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY a;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+
+do_test tkt2822-2.2 {
+ execsql {
+ SELECT a, CAST (b AS TEXT) AS x, c FROM t1
+ UNION ALL
+ SELECT a, b, c FROM t2
+ ORDER BY x;
+ }
+} {2 6 18 4 12 36 6 18 54 5 15 45 1 3 9 3 9 27}
+do_test tkt2822-2.3 {
+ execsql {
+ SELECT t1.a, b, c FROM t1 UNION ALL SELECT t2.a, b, c FROM t2 ORDER BY a;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+
+# Test the "ORDER BY <expression>" syntax.
+#
+do_test tkt2822-3.1 {
+ execsql {
+ SELECT a, CAST (b AS TEXT) AS x, c FROM t1
+ UNION ALL
+ SELECT a, b, c FROM t2
+ ORDER BY CAST (b AS TEXT);
+ }
+} {2 6 18 4 12 36 6 18 54 5 15 45 1 3 9 3 9 27}
+do_test tkt2822-3.2 {
+ execsql {
+ SELECT t1.a, b, c FROM t1 UNION ALL SELECT t2.a, b, c FROM t2 ORDER BY t1.a;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+
+# Test that if a match cannot be found in the leftmost SELECT, an
+# attempt is made to find a match in subsequent SELECT statements.
+#
+do_test tkt2822-3.1 {
+ execsql {
+ SELECT a, b, c FROM t1 UNION ALL SELECT a AS x, b, c FROM t2 ORDER BY x;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+do_test tkt2822-3.2 {
+ # But the leftmost SELECT takes precedence.
+ execsql {
+ SELECT a AS b, CAST (b AS TEXT) AS a, c FROM t1
+ UNION ALL
+ SELECT a, b, c FROM t2
+ ORDER BY a;
+ }
+} {2 6 18 4 12 36 6 18 54 5 15 45 1 3 9 3 9 27}
+do_test tkt2822-3.3 {
+ execsql {
+ SELECT a, b, c FROM t2
+ UNION ALL
+ SELECT a AS b, CAST (b AS TEXT) AS a, c FROM t1
+ ORDER BY a;
+ }
+} {1 3 9 2 6 18 3 9 27 4 12 36 5 15 45 6 18 54}
+
+# Test some error conditions (ORDER BY clauses that match no column).
+#
+do_test tkt2822-4.1 {
+ catchsql {
+ SELECT a, b, c FROM t1 UNION ALL SELECT a, b, c FROM t2 ORDER BY x
+ }
+} {1 {1st ORDER BY term does not match any column in the result set}}
+do_test tkt2822-4.2 {
+ catchsql {
+ SELECT a, CAST (b AS TEXT) AS x, c FROM t1
+ UNION ALL
+ SELECT a, b, c FROM t2
+ ORDER BY CAST (b AS INTEGER);
+ }
+} {1 {1st ORDER BY term does not match any column in the result set}}
+
+# Tests for rule (2).
+#
+# The "ORDER BY b" should match the column alias (rule 2), not the
+# the t3.b value (rule 3).
+#
+do_test tkt2822-5.1 {
+ execsql {
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES(1,8);
+ INSERT INTO t3 VALUES(9,2);
+
+ SELECT a AS b FROM t3 ORDER BY b;
+ }
+} {1 9}
+do_test tkt2822-5.2 {
+ # Case does not matter. b should match B
+ execsql {
+ SELECT a AS b FROM t3 ORDER BY B;
+ }
+} {1 9}
+do_test tkt2822-5.3 {
+ # Quoting should not matter
+ execsql {
+ SELECT a AS 'b' FROM t3 ORDER BY "B";
+ }
+} {1 9}
+do_test tkt2822-5.4 {
+ # Quoting should not matter
+ execsql {
+ SELECT a AS "b" FROM t3 ORDER BY [B];
+ }
+} {1 9}
+
+# In "ORDER BY +b" the term is now an expression rather than
+# a label. It therefore matches by rule (3) instead of rule (2).
+#
+do_test tkt2822-5.5 {
+ execsql {
+ SELECT a AS b FROM t3 ORDER BY +b;
+ }
+} {9 1}
+
+# Tests for rule 2 in compound queries
+#
+do_test tkt2822-6.1 {
+ execsql {
+ CREATE TABLE t6a(p,q);
+ INSERT INTO t6a VALUES(1,8);
+ INSERT INTO t6a VALUES(9,2);
+ CREATE TABLE t6b(x,y);
+ INSERT INTO t6b VALUES(1,7);
+ INSERT INTO t6b VALUES(7,2);
+
+ SELECT p, q FROM t6a UNION ALL SELECT x, y FROM t6b ORDER BY 1, 2
+ }
+} {1 7 1 8 7 2 9 2}
+do_test tkt2822-6.2 {
+ execsql {
+ SELECT p PX, q QX FROM t6a UNION ALL SELECT x XX, y YX FROM t6b
+ ORDER BY PX, YX
+ }
+} {1 7 1 8 7 2 9 2}
+do_test tkt2822-6.3 {
+ execsql {
+ SELECT p PX, q QX FROM t6a UNION ALL SELECT x XX, y YX FROM t6b
+ ORDER BY XX, QX
+ }
+} {1 7 1 8 7 2 9 2}
+do_test tkt2822-6.4 {
+ execsql {
+ SELECT p PX, q QX FROM t6a UNION ALL SELECT x XX, y YX FROM t6b
+ ORDER BY QX, XX
+ }
+} {7 2 9 2 1 7 1 8}
+do_test tkt2822-6.5 {
+ execsql {
+ SELECT p PX, q QX FROM t6a UNION ALL SELECT x XX, y YX FROM t6b
+ ORDER BY t6b.x, QX
+ }
+} {1 7 1 8 7 2 9 2}
+do_test tkt2822-6.6 {
+ execsql {
+ SELECT p PX, q QX FROM t6a UNION ALL SELECT x XX, y YX FROM t6b
+ ORDER BY t6a.q, XX
+ }
+} {7 2 9 2 1 7 1 8}
+
+# More error message tests. This is really more of a test of the
+# %r ordinal value formatting capablity added to sqlite3_snprintf()
+# by ticket #2822.
+#
+do_test tkt2822-7.1 {
+ execsql {
+ CREATE TABLE t7(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,
+ a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25);
+ }
+ catchsql {
+ SELECT * FROM t7 ORDER BY 0;
+ }
+} {1 {1st ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.2 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 0;
+ }
+} {1 {2nd ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.3 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 0;
+ }
+} {1 {3rd ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.4 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 0;
+ }
+} {1 {4th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.9 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 0;
+ }
+} {1 {9th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.10 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 0;
+ }
+} {1 {10th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.11 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0;
+ }
+} {1 {11th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.12 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0;
+ }
+} {1 {12th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.13 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 0;
+ }
+} {1 {13th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.20 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11,12,13,14,15,16,17,18,19, 0
+ }
+} {1 {20th ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.21 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11,12,13,14,15,16,17,18,19, 20, 0
+ }
+} {1 {21st ORDER BY term out of range - should be between 1 and 25}}
+do_test tkt2822-7.22 {
+ catchsql {
+ SELECT * FROM t7 ORDER BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11,12,13,14,15,16,17,18,19, 20, 21, 0
+ }
+} {1 {22nd ORDER BY term out of range - should be between 1 and 25}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2832.test b/third_party/sqlite/test/tkt2832.test
new file mode 100755
index 0000000..6a98a7b
--- /dev/null
+++ b/third_party/sqlite/test/tkt2832.test
@@ -0,0 +1,65 @@
+# 2007 Dec 12
+#
+# 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 is to test that ticket #2832 has been fixed.
+#
+# $Id: tkt2832.test,v 1.4 2008/07/12 14:52:21 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt2832-1.1 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(3);
+ }
+} {}
+do_test tkt2832-1.2 {
+ execsql {
+ UPDATE OR REPLACE t1 SET a = 1;
+ SELECT * FROM t1;
+ }
+} {1}
+
+do_test tkt2832-2.1 {
+ execsql {
+ CREATE TABLE t2(a, b);
+ CREATE TRIGGER t2_t AFTER UPDATE ON t2 BEGIN
+ DELETE FROM t2 WHERE a = new.a + 1;
+ END;
+ INSERT INTO t2 VALUES(1, 2);
+ INSERT INTO t2 VALUES(2, 3);
+ }
+} {}
+do_test tkt2832-2.2 {
+ execsql {
+ UPDATE t2 SET b = 5
+ }
+} {}
+
+do_test tkt2832-3.1 {
+ execsql {
+ CREATE TABLE t3(a, b);
+ CREATE TRIGGER t3_t AFTER DELETE ON t3 BEGIN
+ DELETE FROM t3 WHERE a = old.a + 1;
+ END;
+ INSERT INTO t3 VALUES(1, 2);
+ INSERT INTO t3 VALUES(2, 3);
+ }
+} {}
+do_test tkt2832-3.2 {
+ execsql { DELETE FROM t3 WHERE 1 }
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2854.test b/third_party/sqlite/test/tkt2854.test
new file mode 100755
index 0000000..7bff008
--- /dev/null
+++ b/third_party/sqlite/test/tkt2854.test
@@ -0,0 +1,149 @@
+# 2007 December 20
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: tkt2854.test,v 1.3 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+ifcapable !shared_cache {
+ finish_test
+ return
+}
+
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+# Open 3 database connections. Connection "db" and "db2" share a cache.
+# Connection "db3" has its own cache.
+#
+do_test tkt2854-1.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+
+ # This is taken from shared.test. The Windows VFS expands
+ # ./test.db (and test.db) to be the same thing so the path
+ # matches and they share a cache. By changing the case
+ # for Windows platform, we get around this and get a separate
+ # connection.
+ if {$::tcl_platform(platform)=="unix"} {
+ sqlite3 db3 ./test.db
+ } else {
+ sqlite3 db3 TEST.DB
+ }
+
+ db eval {
+ CREATE TABLE abc(a, b, c);
+ }
+} {}
+
+# Check that an exclusive lock cannot be obtained if some other
+# shared-cache connection has a read-lock on a table.
+#
+do_test tkt2854-1.2 {
+ execsql {
+ BEGIN;
+ SELECT * FROM abc;
+ } db2
+} {}
+do_test tkt2854-1.3 {
+ catchsql { BEGIN EXCLUSIVE } db
+} {1 {database is locked}}
+do_test tkt2854-1.4 {
+ execsql { SELECT * FROM abc } db3
+} {}
+do_test tkt2854-1.5 {
+ catchsql { INSERT INTO abc VALUES(1, 2, 3) } db3
+} {1 {database is locked}}
+do_test tkt2854-1.6 {
+ execsql { COMMIT } db2
+} {}
+
+# Check that an exclusive lock prevents other shared-cache users from
+# starting a transaction.
+#
+do_test tkt2854-1.7 {
+ set ::DB2 [sqlite3_connection_pointer db2]
+ set ::STMT1 [sqlite3_prepare $DB2 "SELECT * FROM abc" -1 TAIL]
+ set ::STMT2 [sqlite3_prepare $DB2 "BEGIN EXCLUSIVE" -1 TAIL]
+ set ::STMT3 [sqlite3_prepare $DB2 "BEGIN IMMEDIATE" -1 TAIL]
+ set ::STMT4 [sqlite3_prepare $DB2 "BEGIN" -1 TAIL]
+ set ::STMT5 [sqlite3_prepare $DB2 "COMMIT" -1 TAIL]
+ execsql { BEGIN EXCLUSIVE } db
+} {}
+do_test tkt2854-1.8 {
+ catchsql { BEGIN EXCLUSIVE } db2
+} {1 {database schema is locked: main}}
+do_test tkt2854-1.9 {
+ catchsql { BEGIN IMMEDIATE } db2
+} {1 {database schema is locked: main}}
+do_test tkt2854-1.10 {
+ # This fails because the schema of main cannot be verified.
+ catchsql { BEGIN } db2
+} {1 {database schema is locked: main}}
+
+# Check that an exclusive lock prevents other shared-cache users from
+# reading the database. Use stored statements so that the error occurs
+# at the b-tree level, not the schema level.
+#
+do_test tkt2854-1.11 {
+ list [sqlite3_step $::STMT1] [sqlite3_finalize $::STMT1]
+} {SQLITE_ERROR SQLITE_LOCKED}
+do_test tkt2854-1.12 {
+ list [sqlite3_step $::STMT2] [sqlite3_finalize $::STMT2]
+} {SQLITE_BUSY SQLITE_BUSY}
+do_test tkt2854-1.13 {
+ list [sqlite3_step $::STMT3] [sqlite3_finalize $::STMT3]
+} {SQLITE_BUSY SQLITE_BUSY}
+do_test tkt2854-1.14 {
+ # A regular "BEGIN" doesn't touch any databases. So it succeeds.
+ list [sqlite3_step $::STMT4] [sqlite3_finalize $::STMT4]
+} {SQLITE_DONE SQLITE_OK}
+do_test tkt2854-1.15 {
+ # As does a COMMIT.
+ list [sqlite3_step $::STMT5] [sqlite3_finalize $::STMT5]
+} {SQLITE_DONE SQLITE_OK}
+
+# Try to read the database using connection "db3" (which does not share
+# a cache with "db"). The database should be locked.
+do_test tkt2854-1.16 {
+ catchsql { SELECT * FROM abc } db3
+} {1 {database is locked}}
+do_test tkt2854-1.17 {
+ execsql { COMMIT } db
+} {}
+do_test tkt2854-1.18 {
+ execsql { SELECT * FROM abc } db2
+} {}
+
+# Check that if an attempt to obtain an exclusive lock fails because an
+# attached db cannot be locked, the internal exclusive flag used by
+# shared-cache users is correctly cleared.
+do_test tkt2854-1.19 {
+ file delete -force test2.db test2.db-journal
+ sqlite3 db4 test2.db
+ execsql { CREATE TABLE def(d, e, f) } db4
+ execsql { ATTACH 'test2.db' AS aux } db
+} {}
+do_test tkt2854-1.20 {
+ execsql {BEGIN IMMEDIATE} db4
+ catchsql {BEGIN EXCLUSIVE} db
+} {1 {database is locked}}
+do_test tkt2854-1.21 {
+ execsql {SELECT * FROM abc} db2
+} {}
+
+db close
+db2 close
+db3 close
+db4 close
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/third_party/sqlite/test/tkt2920.test b/third_party/sqlite/test/tkt2920.test
new file mode 100755
index 0000000..d76c05ba
--- /dev/null
+++ b/third_party/sqlite/test/tkt2920.test
@@ -0,0 +1,78 @@
+# 2008 Feb 1
+#
+# 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 is to test that ticket #2920 is fixed.
+#
+# $Id: tkt2920.test,v 1.1 2008/02/02 02:48:52 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create a database file that is full.
+#
+do_test tkt2920-1.1 {
+ db eval {
+ PRAGMA page_size=1024;
+ PRAGMA max_page_count=40;
+ PRAGMA auto_vacuum=0;
+ CREATE TABLE filler (fill);
+ }
+ file size test.db
+} {2048}
+do_test tkt2920-1.2 {
+ db eval BEGIN
+ for {set i 0} {$i<34} {incr i} {
+ db eval {INSERT INTO filler VALUES(randomblob(1024))}
+ }
+ db eval COMMIT
+} {}
+
+# Try to add a single new page to the full database. We get
+# a disk full error. But this does not corrupt the database.
+#
+do_test tkt2920-1.3 {
+ db eval BEGIN
+ catchsql {
+ INSERT INTO filler VALUES(randomblob(1024))
+ }
+} {1 {database or disk is full}}
+integrity_check tkt2920-1.4
+
+# Increase the maximum size of the database file by 1 page,
+# but then try to add a two-page record. This also fails.
+#
+do_test tkt2920-1.5 {
+ db eval {PRAGMA max_page_count=41}
+ catchsql {
+ INSERT INTO filler VALUES(randomblob(2048))
+ }
+} {1 {database or disk is full}}
+integrity_check tkt2920-1.6
+
+# Increase the maximum size of the database by one more page.
+# This time the insert works.
+#
+do_test tkt2920-1.7 {
+ db eval {PRAGMA max_page_count=42}
+ catchsql {
+ INSERT INTO filler VALUES(randomblob(2048))
+ }
+} {0 {}}
+integrity_check tkt2920-1.8
+
+# The previous errors cancelled the transaction.
+#
+do_test tkt2920-1.9 {
+ catchsql {COMMIT}
+} {1 {cannot commit - no transaction is active}}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2927.test b/third_party/sqlite/test/tkt2927.test
new file mode 100755
index 0000000..fc89a41
--- /dev/null
+++ b/third_party/sqlite/test/tkt2927.test
@@ -0,0 +1,674 @@
+# 2008 Feb 6
+#
+# 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 is to test that ticket #2927 is fixed.
+#
+# $Id: tkt2927.test,v 1.4 2008/08/04 03:51:24 danielk1977 Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !compound {
+ finish_test
+ return
+}
+
+# Create a database.
+#
+do_test tkt2927-1.1 {
+ db eval {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1,11);
+ INSERT INTO t1 VALUES(2,22);
+ INSERT INTO t1 VALUES(3,33);
+ INSERT INTO t1 VALUES(4,44);
+ INSERT INTO t1 VALUES(5,55);
+ SELECT * FROM t1;
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+
+
+do_test tkt2927-2.1 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION ALL
+ SELECT a, b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.2 {
+#set sqlite_addop_trace 1
+ db eval {
+ SELECT a, b FROM t1
+ UNION ALL
+ SELECT a, abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.3 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION ALL
+ SELECT abs(a), b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.4 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION ALL
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.5 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION ALL
+ SELECT a, b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.6 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION ALL
+ SELECT a, abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.7 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION ALL
+ SELECT abs(a), b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.8 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION ALL
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.9 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION ALL
+ SELECT a, b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.10 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION ALL
+ SELECT a, abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.11 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION ALL
+ SELECT abs(a), b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.12 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION ALL
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.13 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a, b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.14 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a, abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.15 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT abs(a), b FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-2.16 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {1 11 2 22 3 33 4 44 5 55 1 11 2 22 3 33 4 44 5 55}
+
+
+do_test tkt2927-3.1 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.2 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.3 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.4 {
+ db eval {
+ SELECT a, b FROM t1
+ UNION
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.5 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.6 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.7 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.8 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ UNION
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.9 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.10 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.11 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.12 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ UNION
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.13 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.14 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.15 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-3.16 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ UNION
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+
+
+do_test tkt2927-4.1 {
+ db eval {
+ SELECT a+b, a-b, a, b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.2 {
+ db eval {
+ SELECT a+b, a-b, a, b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.3 {
+ db eval {
+ SELECT a+b, a-b, a, b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.4 {
+ db eval {
+ SELECT a+b, a-b, a, b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.5 {
+ db eval {
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.6 {
+ db eval {
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.7 {
+ db eval {
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.8 {
+ db eval {
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.9 {
+ db eval {
+ SELECT a+b, a-b, abs(a), b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.10 {
+ db eval {
+ SELECT a+b, a-b, abs(a), b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.11 {
+ db eval {
+ SELECT a+b, a-b, abs(a), b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.12 {
+ db eval {
+ SELECT a+b, a-b, abs(a), b FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.13 {
+ db eval {
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.14 {
+ db eval {
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, a, abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.15 {
+ db eval {
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), b FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+do_test tkt2927-4.16 {
+ db eval {
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ UNION ALL
+ SELECT a+b, a-b, abs(a), abs(b) FROM t1
+ }
+} {12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55 12 -10 1 11 24 -20 2 22 36 -30 3 33 48 -40 4 44 60 -50 5 55}
+
+
+do_test tkt2927-5.1 {
+ db eval {
+ SELECT a, b FROM t1
+ EXCEPT
+ SELECT a, b FROM t1
+ }
+} {}
+do_test tkt2927-5.2 {
+ db eval {
+ SELECT a, b FROM t1
+ EXCEPT
+ SELECT a, abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.3 {
+ db eval {
+ SELECT a, b FROM t1
+ EXCEPT
+ SELECT abs(a), b FROM t1
+ }
+} {}
+do_test tkt2927-5.4 {
+ db eval {
+ SELECT a, b FROM t1
+ EXCEPT
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.5 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ EXCEPT
+ SELECT a, b FROM t1
+ }
+} {}
+do_test tkt2927-5.6 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ EXCEPT
+ SELECT a, abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.7 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ EXCEPT
+ SELECT abs(a), b FROM t1
+ }
+} {}
+do_test tkt2927-5.8 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ EXCEPT
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.9 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ EXCEPT
+ SELECT a, b FROM t1
+ }
+} {}
+do_test tkt2927-5.10 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ EXCEPT
+ SELECT a, abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.11 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ EXCEPT
+ SELECT abs(a), b FROM t1
+ }
+} {}
+do_test tkt2927-5.12 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ EXCEPT
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.13 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ EXCEPT
+ SELECT a, b FROM t1
+ }
+} {}
+do_test tkt2927-5.14 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ EXCEPT
+ SELECT a, abs(b) FROM t1
+ }
+} {}
+do_test tkt2927-5.15 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ EXCEPT
+ SELECT abs(a), b FROM t1
+ }
+} {}
+do_test tkt2927-5.16 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ EXCEPT
+ SELECT abs(a), abs(b) FROM t1
+ }
+} {}
+
+
+do_test tkt2927-6.1 {
+ db eval {
+ SELECT a, b FROM t1
+ INTERSECT
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.2 {
+ db eval {
+ SELECT a, b FROM t1
+ INTERSECT
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.3 {
+ db eval {
+ SELECT a, b FROM t1
+ INTERSECT
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.4 {
+ db eval {
+ SELECT a, b FROM t1
+ INTERSECT
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.5 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ INTERSECT
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.6 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ INTERSECT
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.7 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ INTERSECT
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.8 {
+ db eval {
+ SELECT a, abs(b) FROM t1
+ INTERSECT
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.9 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ INTERSECT
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.10 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ INTERSECT
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.11 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ INTERSECT
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.12 {
+ db eval {
+ SELECT abs(a), b FROM t1
+ INTERSECT
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.13 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ INTERSECT
+ SELECT a, b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.14 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ INTERSECT
+ SELECT a, abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.15 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ INTERSECT
+ SELECT abs(a), b FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+do_test tkt2927-6.16 {
+ db eval {
+ SELECT abs(a), abs(b) FROM t1
+ INTERSECT
+ SELECT abs(a), abs(b) FROM t1
+ ORDER BY 1
+ }
+} {1 11 2 22 3 33 4 44 5 55}
+
+# Ticket #3092 is the same bug. But another test case never hurts.
+#
+do_test tkt2927-7.1 {
+ db eval {
+ CREATE TABLE host (
+ hostname text not null primary key,
+ consoleHost text,
+ consolePort text
+ );
+ INSERT INTO "host" VALUES('aald04','aalp03','4');
+ INSERT INTO "host" VALUES('aald17','aalp01','1');
+ CREATE VIEW consolemap1a as
+ select hostname, consolehost, '/dev/cuaD0.' || (consoleport-1) consoleport
+ from host where consolehost='aalp01';
+ CREATE VIEW consolemap1b as
+ select hostname hostname, consolehost consolehost, '/dev/cuaD' ||
+ substr('01',1+((consoleport-1)/16),1) ||
+ substr('0123456789abcdef',1+((consoleport-1)%16),1) consoleport
+ from host where consolehost='aalp03';
+ CREATE VIEW consolemap1 as
+ select * from consolemap1a
+ union
+ select * from consolemap1b;
+ SELECT * from consolemap1b;
+ }
+} {aald04 aalp03 /dev/cuaD03}
+do_test tkt2927-7.2 {
+ db eval {
+ SELECT * FROM consolemap1
+ }
+} {aald04 aalp03 /dev/cuaD03 aald17 aalp01 /dev/cuaD0.0}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt2942.test b/third_party/sqlite/test/tkt2942.test
new file mode 100755
index 0000000..a7b861c
--- /dev/null
+++ b/third_party/sqlite/test/tkt2942.test
@@ -0,0 +1,62 @@
+# 2008 February 15
+#
+# 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.
+#
+#***********************************************************************
+#
+# Ticket #2942.
+#
+# Queries of the form:
+#
+# SELECT group_concat(x) FROM (SELECT * FROM table ORDER BY 1);
+#
+# The ORDER BY would be dropped by the query flattener. This used
+# to not matter because aggregate functions sum(), min(), max(), avg(),
+# and so forth give the same result regardless of the order of inputs.
+# But with the addition of the group_concat() function, suddenly the
+# order does matter.
+#
+# $Id: tkt2942.test,v 1.1 2008/02/15 14:33:04 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !subquery {
+ finish_test
+ return
+}
+
+do_test tkt2942.1 {
+ execsql {
+ create table t1(num int);
+ insert into t1 values (2);
+ insert into t1 values (1);
+ insert into t1 values (3);
+ insert into t1 values (4);
+ SELECT group_concat(num) FROM (SELECT num FROM t1 ORDER BY num DESC);
+ }
+} {4,3,2,1}
+do_test tkt2942.2 {
+ execsql {
+ SELECT group_concat(num) FROM (SELECT num FROM t1 ORDER BY num);
+ }
+} {1,2,3,4}
+do_test tkt2942.3 {
+ execsql {
+ SELECT group_concat(num) FROM (SELECT num FROM t1);
+ }
+} {2,1,3,4}
+do_test tkt2942.4 {
+ execsql {
+ SELECT group_concat(num) FROM (SELECT num FROM t1 ORDER BY rowid DESC);
+ }
+} {4,3,1,2}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt3080.test b/third_party/sqlite/test/tkt3080.test
new file mode 100755
index 0000000..d03a383
--- /dev/null
+++ b/third_party/sqlite/test/tkt3080.test
@@ -0,0 +1,77 @@
+# 2008 April 27
+#
+# 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.
+#
+#***********************************************************************
+#
+# Ticket #3080
+#
+# Make sure that application-defined functions are able to recursively
+# invoke SQL statements that create and drop virtual tables.
+#
+# $Id: tkt3080.test,v 1.1 2008/04/28 17:12:11 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt3080.1 {
+ db function execsql execsql
+ db eval {
+ SELECT execsql('CREATE TABLE t1(x)');
+ }
+ execsql {SELECT name FROM sqlite_master}
+} {t1}
+do_test tkt3080.2 {
+ db eval {
+ INSERT INTO t1 VALUES('CREATE TABLE t2(y);');
+ SELECT execsql(x) FROM t1;
+ }
+ db eval {
+ SELECT name FROM sqlite_master;
+ }
+} {t1 t2}
+do_test tkt3080.3 {
+ execsql {
+ INSERT INTO t1 VALUES('CREATE TABLE t3(z); DROP TABLE t3;');
+ }
+ catchsql {
+ SELECT execsql(x) FROM t1 WHERE rowid=2;
+ }
+} {1 {database table is locked}}
+do_test tkt3080.4 {
+ db eval {
+ SELECT name FROM sqlite_master;
+ }
+} {t1 t2}
+
+ifcapable vtab {
+ register_echo_module [sqlite3_connection_pointer db]
+ do_test tkt3080.10 {
+ set sql {
+ CREATE VIRTUAL TABLE t4 USING echo(t2);
+ INSERT INTO t4 VALUES(123);
+ DROP TABLE t4;
+ }
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES($sql);
+ }
+ db eval {
+ SELECT execsql(x) FROM t1
+ }
+ execsql {SELECT name FROM sqlite_master}
+ } {t1 t2}
+ do_test tkt3080.11 {
+ execsql {SELECT * FROM t2}
+ } {123}
+}
+
+
+
+finish_test
diff --git a/third_party/sqlite/test/tkt3093.test b/third_party/sqlite/test/tkt3093.test
new file mode 100755
index 0000000..3be46d1
--- /dev/null
+++ b/third_party/sqlite/test/tkt3093.test
@@ -0,0 +1,86 @@
+# 2008 May 2
+#
+# 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.
+#
+#***********************************************************************
+#
+# Ticket #3093
+#
+# Verify that a busy callback waiting on a reserved lock resolves
+# once the lock clears.
+#
+# $Id: tkt3093.test,v 1.2 2008/05/02 14:23:55 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Set up a test database
+#
+do_test tkt3093.1 {
+ db eval {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ SELECT * FROM t1
+ }
+} {1}
+
+# Establish a separate, independent connection to that database.
+#
+do_test tkt3093.2 {
+ catch {sqlite3_enable_shared_cache 0}
+ sqlite3 db2 test.db
+ db2 eval {
+ SELECT * FROM t1
+ }
+} {1}
+
+# Make sure that clearing a lock allows a pending request for
+# a reserved lock to continue.
+#
+do_test tkt3093.3 {
+ # This will be the busy callback for connection db2. On the first
+ # busy callback, commit the transaction in db. This should clear
+ # the lock so that there should not be a second callback. If the
+ # busy handler is called a second time, then fail so that we get
+ # timeout.
+ proc busy_callback {cnt} {
+ if {$cnt==0} {
+ db eval COMMIT
+ return 0
+ } else {
+ return 1
+ }
+ }
+ db2 busy ::busy_callback
+
+ # Start a write transaction on db.
+ db eval {
+ BEGIN;
+ INSERT INTO t1 VALUES(2);
+ }
+
+ # Attempt to modify the database on db2
+ catchsql {
+ UPDATE t1 SET x=x+1;
+ } db2
+} {0 {}}
+
+# Verify that everything worked as expected. The db transaction should
+# have gone first and added entry 2. Then the db2 transaction would have
+# run and added one to each entry.
+#
+do_test tkt3093.4 {
+ db eval {SELECT * FROM t1}
+} {2 3}
+do_test tkt3093.5 {
+ db2 eval {SELECT * FROM t1}
+} {2 3}
+db2 close
+
+finish_test
diff --git a/third_party/sqlite/test/tkt3121.test b/third_party/sqlite/test/tkt3121.test
new file mode 100755
index 0000000..2e8fdbb
--- /dev/null
+++ b/third_party/sqlite/test/tkt3121.test
@@ -0,0 +1,50 @@
+# 2008 May 16
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: tkt3121.test,v 1.2 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# Register the module
+register_echo_module [sqlite3_connection_pointer db]
+
+do_test vtabD-1.1 {
+ execsql {
+ PRAGMA encoding = 'utf16';
+
+ CREATE TABLE r1(field);
+ CREATE TABLE r2(col PRIMARY KEY, descr);
+
+ INSERT INTO r1 VALUES('abcd');
+ INSERT INTO r2 VALUES('abcd', 'A nice description');
+ INSERT INTO r2 VALUES('efgh', 'Another description');
+
+ CREATE VIRTUAL TABLE t1 USING echo(r1);
+ CREATE VIRTUAL TABLE t2 USING echo(r2);
+ }
+} {}
+
+do_test vtabD-1.2 {
+ execsql {
+ select
+ t1.field as Field,
+ t2.descr as Descr
+ from t1 inner join t2 on t1.field = t2.col order by t1.field
+ }
+} {abcd {A nice description}}
+
+finish_test
diff --git a/third_party/sqlite/test/tkt3201.test b/third_party/sqlite/test/tkt3201.test
new file mode 100755
index 0000000..446fd32
--- /dev/null
+++ b/third_party/sqlite/test/tkt3201.test
@@ -0,0 +1,74 @@
+# 2008 July 4
+#
+# 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.
+# Specifically, it tests that bug #3201 has been fixed.
+#
+# $Id: tkt3201.test,v 1.3 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt3201-1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ }
+} {}
+
+do_test tkt3201-2 {
+ execsql {
+ SELECT l.a, r.a FROM t1 AS l, t1 AS r WHERE l.a < r.a;
+ }
+} {1 2}
+
+do_test tkt3201-3 {
+ execsql {
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t2 VALUES(2, 'two');
+ }
+ execsql {
+ SELECT l.a, r.a FROM t1 AS l, t2 AS r WHERE l.a < r.a;
+ }
+} {1 2}
+
+do_test tkt3201-4 {
+ execsql {
+ DELETE FROM t1 WHERE a = 2;
+ }
+ execsql {
+ SELECT l.a, r.a FROM t1 AS l, t2 AS r WHERE l.a < r.a;
+ }
+} {1 2}
+
+do_test tkt3201-5 {
+ execsql {
+ DELETE FROM t1 WHERE a = 2;
+ }
+ execsql {
+ SELECT t1.a, t1.b, t2.a, t2.b FROM t1, t2;
+ }
+} {1 one 2 two}
+
+do_test tkt3201-6 {
+ execsql {
+ CREATE TABLE t3(c INTEGER PRIMARY KEY, d TEXT);
+ INSERT INTO t3 VALUES(2, 'two');
+ }
+ execsql { SELECT a, b, c, d FROM t1, t3 }
+} {1 one 2 two}
+
+do_test tkt3201-7 {
+ execsql { SELECT a, b, c, d FROM t1, t3 WHERE a < c }
+} {1 one 2 two}
+
+
+finish_test
diff --git a/third_party/sqlite/test/tokenize.test b/third_party/sqlite/test/tokenize.test
new file mode 100755
index 0000000..46fd4ee
--- /dev/null
+++ b/third_party/sqlite/test/tokenize.test
@@ -0,0 +1,65 @@
+# 2008 July 7
+#
+# 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 testing the tokenizer
+#
+# $Id: tokenize.test,v 1.1 2008/07/08 00:06:51 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tokenize-1.1 {
+ catchsql {SELECT 1.0e+}
+} {1 {unrecognized token: "1.0e"}}
+do_test tokenize-1.2 {
+ catchsql {SELECT 1.0E+}
+} {1 {unrecognized token: "1.0E"}}
+do_test tokenize-1.3 {
+ catchsql {SELECT 1.0e-}
+} {1 {unrecognized token: "1.0e"}}
+do_test tokenize-1.4 {
+ catchsql {SELECT 1.0E-}
+} {1 {unrecognized token: "1.0E"}}
+do_test tokenize-1.5 {
+ catchsql {SELECT 1.0e+/}
+} {1 {unrecognized token: "1.0e"}}
+do_test tokenize-1.6 {
+ catchsql {SELECT 1.0E+:}
+} {1 {unrecognized token: "1.0E"}}
+do_test tokenize-1.7 {
+ catchsql {SELECT 1.0e-:}
+} {1 {unrecognized token: "1.0e"}}
+do_test tokenize-1.8 {
+ catchsql {SELECT 1.0E-/}
+} {1 {unrecognized token: "1.0E"}}
+do_test tokenize-1.9 {
+ catchsql {SELECT 1.0F+5}
+} {1 {unrecognized token: "1.0F"}}
+do_test tokenize-1.10 {
+ catchsql {SELECT 1.0d-10}
+} {1 {unrecognized token: "1.0d"}}
+do_test tokenize-1.11 {
+ catchsql {SELECT 1.0e,5}
+} {1 {unrecognized token: "1.0e"}}
+do_test tokenize-1.12 {
+ catchsql {SELECT 1.0E.10}
+} {1 {unrecognized token: "1.0E"}}
+
+do_test tokenize-2.1 {
+ catchsql {SELECT 1, 2 /*}
+} {1 {near "*": syntax error}}
+do_test tokenize-2.2 {
+ catchsql {SELECT 1, 2 /* }
+} {0 {1 2}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/trace.test b/third_party/sqlite/test/trace.test
new file mode 100755
index 0000000..67ad0e3
--- /dev/null
+++ b/third_party/sqlite/test/trace.test
@@ -0,0 +1,169 @@
+# 2004 Jun 29
+#
+# 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.
+#
+# This file implements tests for the "sqlite3_trace()" API.
+#
+# $Id: trace.test,v 1.7 2008/01/12 21:35:57 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !trace {
+ finish_test
+ return
+}
+
+set ::stmtlist {}
+do_test trace-1.1 {
+ set rc [catch {db trace 1 2 3} msg]
+ lappend rc $msg
+} {1 {wrong # args: should be "db trace ?CALLBACK?"}}
+proc trace_proc cmd {
+ lappend ::stmtlist [string trim $cmd]
+}
+do_test trace-1.2 {
+ db trace trace_proc
+ db trace
+} {trace_proc}
+do_test trace-1.3 {
+ execsql {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t1;
+ }
+} {1 2}
+do_test trace-1.4 {
+ set ::stmtlist
+} {{CREATE TABLE t1(a,b);} {INSERT INTO t1 VALUES(1,2);} {SELECT * FROM t1;}}
+do_test trace-1.5 {
+ db trace {}
+ db trace
+} {}
+
+# If we prepare a statement and execute it multiple times, the trace
+# happens on each execution.
+#
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test trace-2.1 {
+ set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(2,3)} -1 TAIL]
+ db trace trace_proc
+ proc trace_proc sql {
+ global TRACE_OUT
+ set TRACE_OUT $sql
+ }
+ set TRACE_OUT {}
+ sqlite3_step $STMT
+ set TRACE_OUT
+} {INSERT INTO t1 VALUES(2,3)}
+do_test trace-2.2 {
+ set TRACE_OUT {}
+ sqlite3_reset $STMT
+ set TRACE_OUT
+} {}
+do_test trace-2.3 {
+ sqlite3_step $STMT
+ set TRACE_OUT
+} {INSERT INTO t1 VALUES(2,3)}
+do_test trace-2.4 {
+ execsql {SELECT * FROM t1}
+} {1 2 2 3 2 3}
+do_test trace-2.5 {
+ set TRACE_OUT
+} {SELECT * FROM t1}
+catch {sqlite3_finalize $STMT}
+
+# Similar tests, but this time for profiling.
+#
+do_test trace-3.1 {
+ set rc [catch {db profile 1 2 3} msg]
+ lappend rc $msg
+} {1 {wrong # args: should be "db profile ?CALLBACK?"}}
+set ::stmtlist {}
+proc profile_proc {cmd tm} {
+ lappend ::stmtlist [string trim $cmd]
+}
+do_test trace-3.2 {
+ db trace {}
+ db profile profile_proc
+ db profile
+} {profile_proc}
+do_test trace-3.3 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+} {1 2}
+do_test trace-3.4 {
+ set ::stmtlist
+} {{CREATE TABLE t2(a,b);} {INSERT INTO t2 VALUES(1,2);} {SELECT * FROM t2;}}
+do_test trace-3.5 {
+ db profile {}
+ db profile
+} {}
+
+# If we prepare a statement and execute it multiple times, the profile
+# happens on each execution.
+#
+db close
+sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+do_test trace-4.1 {
+ set STMT [sqlite3_prepare $DB {INSERT INTO t2 VALUES(2,3)} -1 TAIL]
+ db trace trace_proc
+ proc profile_proc {sql tm} {
+ global TRACE_OUT
+ set TRACE_OUT $sql
+ }
+ set TRACE_OUT {}
+ sqlite3_step $STMT
+ set TRACE_OUT
+} {INSERT INTO t2 VALUES(2,3)}
+do_test trace-4.2 {
+ set TRACE_OUT {}
+ sqlite3_reset $STMT
+ set TRACE_OUT
+} {}
+do_test trace-4.3 {
+ sqlite3_step $STMT
+ set TRACE_OUT
+} {INSERT INTO t2 VALUES(2,3)}
+do_test trace-4.4 {
+ execsql {SELECT * FROM t1}
+} {1 2 2 3 2 3}
+do_test trace-4.5 {
+ set TRACE_OUT
+} {SELECT * FROM t1}
+catch {sqlite3_finalize $STMT}
+
+# Trigger tracing.
+#
+do_test trace-5.1 {
+ db eval {
+ CREATE TRIGGER r1t1 AFTER UPDATE ON t1 BEGIN
+ UPDATE t2 SET a=new.a WHERE rowid=new.rowid;
+ END;
+ CREATE TRIGGER r1t2 AFTER UPDATE ON t2 BEGIN
+ SELECT 'hello';
+ END;
+ }
+ set TRACE_OUT {}
+ proc trace_proc cmd {
+ lappend ::TRACE_OUT [string trim $cmd]
+ }
+ db eval {
+ UPDATE t1 SET a=a+1;
+ }
+ set TRACE_OUT
+} {{UPDATE t1 SET a=a+1;} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2}}
+
+finish_test
diff --git a/third_party/sqlite/test/trans.test b/third_party/sqlite/test/trans.test
new file mode 100755
index 0000000..e5ffcd2
--- /dev/null
+++ b/third_party/sqlite/test/trans.test
@@ -0,0 +1,937 @@
+# 2001 September 15
+#
+# 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 database locks.
+#
+# $Id: trans.test,v 1.38 2008/04/19 20:34:19 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# Create several tables to work with.
+#
+do_test trans-1.0 {
+ execsql {
+ CREATE TABLE one(a int PRIMARY KEY, b text);
+ INSERT INTO one VALUES(1,'one');
+ INSERT INTO one VALUES(2,'two');
+ INSERT INTO one VALUES(3,'three');
+ SELECT b FROM one ORDER BY a;
+ }
+} {one two three}
+integrity_check trans-1.0.1
+do_test trans-1.1 {
+ execsql {
+ CREATE TABLE two(a int PRIMARY KEY, b text);
+ INSERT INTO two VALUES(1,'I');
+ INSERT INTO two VALUES(5,'V');
+ INSERT INTO two VALUES(10,'X');
+ SELECT b FROM two ORDER BY a;
+ }
+} {I V X}
+do_test trans-1.9 {
+ sqlite3 altdb test.db
+ execsql {SELECT b FROM one ORDER BY a} altdb
+} {one two three}
+do_test trans-1.10 {
+ execsql {SELECT b FROM two ORDER BY a} altdb
+} {I V X}
+integrity_check trans-1.11
+
+# Basic transactions
+#
+do_test trans-2.1 {
+ set v [catch {execsql {BEGIN}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.2 {
+ set v [catch {execsql {END}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.3 {
+ set v [catch {execsql {BEGIN TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.4 {
+ set v [catch {execsql {COMMIT TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.5 {
+ set v [catch {execsql {BEGIN TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.6 {
+ set v [catch {execsql {ROLLBACK TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.10 {
+ execsql {
+ BEGIN;
+ SELECT a FROM one ORDER BY a;
+ SELECT a FROM two ORDER BY a;
+ END;
+ }
+} {1 2 3 1 5 10}
+integrity_check trans-2.11
+
+# Check the locking behavior
+#
+do_test trans-3.1 {
+ execsql {
+ BEGIN;
+ UPDATE one SET a = 0 WHERE 0;
+ SELECT a FROM one ORDER BY a;
+ }
+} {1 2 3}
+do_test trans-3.2 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+
+do_test trans-3.3 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test trans-3.4 {
+ catchsql {
+ INSERT INTO one VALUES(4,'four');
+ }
+} {0 {}}
+do_test trans-3.5 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+do_test trans-3.6 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test trans-3.7 {
+ catchsql {
+ INSERT INTO two VALUES(4,'IV');
+ }
+} {0 {}}
+do_test trans-3.8 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 5 10}}
+do_test trans-3.9 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3}}
+do_test trans-3.10 {
+ execsql {END TRANSACTION}
+} {}
+
+do_test trans-3.11 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-3.12 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+do_test trans-3.13 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-3.14 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+integrity_check trans-3.15
+
+do_test trans-4.1 {
+ set v [catch {execsql {
+ COMMIT;
+ } db} msg]
+ lappend v $msg
+} {1 {cannot commit - no transaction is active}}
+do_test trans-4.2 {
+ set v [catch {execsql {
+ ROLLBACK;
+ } db} msg]
+ lappend v $msg
+} {1 {cannot rollback - no transaction is active}}
+do_test trans-4.3 {
+ catchsql {
+ BEGIN TRANSACTION;
+ UPDATE two SET a = 0 WHERE 0;
+ SELECT a FROM two ORDER BY a;
+ } db
+} {0 {1 4 5 10}}
+do_test trans-4.4 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 4 5 10}}
+do_test trans-4.5 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3 4}}
+do_test trans-4.6 {
+ catchsql {
+ BEGIN TRANSACTION;
+ SELECT a FROM one ORDER BY a;
+ } db
+} {1 {cannot start a transaction within a transaction}}
+do_test trans-4.7 {
+ catchsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb
+} {0 {1 4 5 10}}
+do_test trans-4.8 {
+ catchsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb
+} {0 {1 2 3 4}}
+do_test trans-4.9 {
+ set v [catch {execsql {
+ END TRANSACTION;
+ SELECT a FROM two ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-4.10 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-4.11 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+integrity_check trans-4.12
+do_test trans-4.98 {
+ altdb close
+ execsql {
+ DROP TABLE one;
+ DROP TABLE two;
+ }
+} {}
+integrity_check trans-4.99
+
+# Check out the commit/rollback behavior of the database
+#
+do_test trans-5.1 {
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test trans-5.2 {
+ execsql {BEGIN TRANSACTION}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test trans-5.3 {
+ execsql {CREATE TABLE one(a text, b int)}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {one}
+do_test trans-5.4 {
+ execsql {SELECT a,b FROM one ORDER BY b}
+} {}
+do_test trans-5.5 {
+ execsql {INSERT INTO one(a,b) VALUES('hello', 1)}
+ execsql {SELECT a,b FROM one ORDER BY b}
+} {hello 1}
+do_test trans-5.6 {
+ execsql {ROLLBACK}
+ execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name}
+} {}
+do_test trans-5.7 {
+ set v [catch {
+ execsql {SELECT a,b FROM one ORDER BY b}
+ } msg]
+ lappend v $msg
+} {1 {no such table: one}}
+
+# Test commits and rollbacks of table CREATE TABLEs, CREATE INDEXs
+# DROP TABLEs and DROP INDEXs
+#
+do_test trans-5.8 {
+ execsql {
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name
+ }
+} {}
+do_test trans-5.9 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a int, b int, c int);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {t1}
+do_test trans-5.10 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test trans-5.11 {
+ execsql {
+ COMMIT;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test trans-5.12 {
+ execsql {
+ BEGIN TRANSACTION;
+ CREATE TABLE t2(a int, b int, c int);
+ CREATE INDEX i2a ON t2(a);
+ CREATE INDEX i2b ON t2(b);
+ DROP TABLE t1;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2a i2b t2}
+do_test trans-5.13 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test trans-5.14 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {t1}
+do_test trans-5.15 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i1 t1}
+do_test trans-5.16 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE TABLE t2(x int, y int, z int);
+ CREATE INDEX i2x ON t2(x);
+ CREATE INDEX i2y ON t2(y);
+ INSERT INTO t2 VALUES(1,2,3);
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test trans-5.17 {
+ execsql {
+ COMMIT;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test trans-5.18 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test trans-5.19 {
+ execsql {
+ SELECT x FROM t2 WHERE y=2;
+ }
+} {1}
+do_test trans-5.20 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ DROP TABLE t2;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {}
+do_test trans-5.21 {
+ set r [catch {execsql {
+ SELECT * FROM t2
+ }} msg]
+ lappend r $msg
+} {1 {no such table: t2}}
+do_test trans-5.22 {
+ execsql {
+ ROLLBACK;
+ SELECT name fROM sqlite_master
+ WHERE type='table' OR type='index'
+ ORDER BY name;
+ }
+} {i2x i2y t1 t2}
+do_test trans-5.23 {
+ execsql {
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+integrity_check trans-5.23
+
+
+# Try to DROP and CREATE tables and indices with the same name
+# within a transaction. Make sure ROLLBACK works.
+#
+do_test trans-6.1 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p,q,r);
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {a 1 b 2 c 3}
+do_test trans-6.2 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p,q,r);
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {}
+do_test trans-6.3 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test trans-6.4 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test trans-6.5 {
+ execsql2 {
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test trans-6.6 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test trans-6.7 {
+ catchsql {
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {1 {no such table: t1}}
+
+# Repeat on a table with an automatically generated index.
+#
+do_test trans-6.10 {
+ execsql2 {
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p unique,q,r);
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {a 1 b 2 c 3}
+do_test trans-6.11 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(p unique,q,r);
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {}
+do_test trans-6.12 {
+ execsql2 {
+ INSERT INTO t1 VALUES(1,2,3);
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test trans-6.13 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test trans-6.14 {
+ execsql2 {
+ ROLLBACK;
+ SELECT * FROM t1;
+ }
+} {p 1 q 2 r 3}
+do_test trans-6.15 {
+ execsql2 {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a unique,b,c);
+ INSERT INTO t1 VALUES(4,5,6);
+ SELECT * FROM t1;
+ DROP TABLE t1;
+ }
+} {a 4 b 5 c 6}
+do_test trans-6.16 {
+ catchsql {
+ COMMIT;
+ SELECT * FROM t1;
+ }
+} {1 {no such table: t1}}
+
+do_test trans-6.20 {
+ execsql {
+ CREATE TABLE t1(a integer primary key,b,c);
+ INSERT INTO t1 VALUES(1,-2,-3);
+ INSERT INTO t1 VALUES(4,-5,-6);
+ SELECT * FROM t1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.21 {
+ execsql {
+ CREATE INDEX i1 ON t1(b);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.22 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE b<1;
+ ROLLBACK;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.23 {
+ execsql {
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.24 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+
+do_test trans-6.25 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE INDEX i1 ON t1(c);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.26 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.27 {
+ execsql {
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.28 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+
+# The following repeats steps 6.20 through 6.28, but puts a "unique"
+# constraint the first field of the table in order to generate an
+# automatic index.
+#
+do_test trans-6.30 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ CREATE TABLE t1(a int unique,b,c);
+ COMMIT;
+ INSERT INTO t1 VALUES(1,-2,-3);
+ INSERT INTO t1 VALUES(4,-5,-6);
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.31 {
+ execsql {
+ CREATE INDEX i1 ON t1(b);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.32 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE b<1;
+ ROLLBACK;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.33 {
+ execsql {
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.34 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP TABLE t1;
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+
+do_test trans-6.35 {
+ execsql {
+ BEGIN TRANSACTION;
+ DROP INDEX i1;
+ CREATE INDEX i1 ON t1(c);
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.36 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.37 {
+ execsql {
+ DROP INDEX i1;
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+do_test trans-6.38 {
+ execsql {
+ ROLLBACK;
+ SELECT * FROM t1 WHERE b<1;
+ }
+} {4 -5 -6 1 -2 -3}
+do_test trans-6.39 {
+ execsql {
+ SELECT * FROM t1 WHERE c<1;
+ }
+} {1 -2 -3 4 -5 -6}
+integrity_check trans-6.40
+
+# Test to make sure rollback restores the database back to its original
+# state.
+#
+do_test trans-7.1 {
+ execsql {BEGIN}
+ for {set i 0} {$i<1000} {incr i} {
+ set r1 [expr {rand()}]
+ set r2 [expr {rand()}]
+ set r3 [expr {rand()}]
+ execsql "INSERT INTO t2 VALUES($r1,$r2,$r3)"
+ }
+ execsql {COMMIT}
+ set ::checksum [execsql {SELECT md5sum(x,y,z) FROM t2}]
+ set ::checksum2 [
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+ ]
+ execsql {SELECT count(*) FROM t2}
+} {1001}
+do_test trans-7.2 {
+ execsql {SELECT md5sum(x,y,z) FROM t2}
+} $checksum
+do_test trans-7.2.1 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+do_test trans-7.3 {
+ execsql {
+ BEGIN;
+ DELETE FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test trans-7.4 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 SELECT * FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test trans-7.5 {
+ execsql {
+ BEGIN;
+ DELETE FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test trans-7.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 SELECT * FROM t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test trans-7.7 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+} $checksum
+do_test trans-7.8 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test trans-7.9 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test trans-7.10 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test trans-7.11 {
+ execsql {
+ BEGIN;
+ CREATE TEMP TABLE t3 AS SELECT * FROM t2;
+ INSERT INTO t2 SELECT * FROM t3;
+ DROP INDEX i2x;
+ DROP INDEX i2y;
+ CREATE INDEX i3a ON t3(x);
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test trans-7.12 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+ifcapable tempdb {
+ do_test trans-7.13 {
+ execsql {
+ BEGIN;
+ DROP TABLE t2;
+ ROLLBACK;
+ SELECT md5sum(x,y,z) FROM t2;
+ }
+ } $checksum
+}
+do_test trans-7.14 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+integrity_check trans-7.15
+
+# Arrange for another process to begin modifying the database but abort
+# and die in the middle of the modification. Then have this process read
+# the database. This process should detect the journal file and roll it
+# back. Verify that this happens correctly.
+#
+set fd [open test.tcl w]
+puts $fd {
+ sqlite3 db test.db
+ db eval {
+ PRAGMA default_cache_size=20;
+ BEGIN;
+ CREATE TABLE t3 AS SELECT * FROM t2;
+ DELETE FROM t2;
+ }
+ sqlite_abort
+}
+close $fd
+do_test trans-8.1 {
+ catch {exec [info nameofexec] test.tcl}
+ execsql {SELECT md5sum(x,y,z) FROM t2}
+} $checksum
+do_test trans-8.2 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+integrity_check trans-8.3
+set fd [open test.tcl w]
+puts $fd {
+ sqlite3 db test.db
+ db eval {
+ PRAGMA journal_mode=persist;
+ PRAGMA default_cache_size=20;
+ BEGIN;
+ CREATE TABLE t3 AS SELECT * FROM t2;
+ DELETE FROM t2;
+ }
+ sqlite_abort
+}
+close $fd
+do_test trans-8.4 {
+ catch {exec [info nameofexec] test.tcl}
+ execsql {SELECT md5sum(x,y,z) FROM t2}
+} $checksum
+do_test trans-8.5 {
+ execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
+} $checksum2
+integrity_check trans-8.6
+
+# In the following sequence of tests, compute the MD5 sum of the content
+# of a table, make lots of modifications to that table, then do a rollback.
+# Verify that after the rollback, the MD5 checksum is unchanged.
+#
+do_test trans-9.1 {
+ execsql {
+ PRAGMA default_cache_size=10;
+ }
+ db close
+ sqlite3 db test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t3(x TEXT);
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 VALUES(randstr(10,400));
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+ COMMIT;
+ SELECT count(*) FROM t3;
+ }
+} {1024}
+
+# The following procedure computes a "signature" for table "t3". If
+# T3 changes in any way, the signature should change.
+#
+# This is used to test ROLLBACK. We gather a signature for t3, then
+# make lots of changes to t3, then rollback and take another signature.
+# The two signatures should be the same.
+#
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(x) FROM t3}]
+}
+
+# Repeat the following group of tests 20 times for quick testing and
+# 40 times for full testing. Each iteration of the test makes table
+# t3 a little larger, and thus takes a little longer, so doing 40 tests
+# is more than 2.0 times slower than doing 20 tests. Considerably more.
+#
+if {[info exists ISQUICK]} {
+ set limit 20
+} elseif {[info exists SOAKTEST]} {
+ set limit 100
+} else {
+ set limit 40
+}
+
+# Do rollbacks. Make sure the signature does not change.
+#
+for {set i 2} {$i<=$limit} {incr i} {
+ set ::sig [signature]
+ set cnt [lindex $::sig 0]
+ if {$i%2==0} {
+ execsql {PRAGMA fullfsync=ON}
+ } else {
+ execsql {PRAGMA fullfsync=OFF}
+ }
+ set sqlite_sync_count 0
+ set sqlite_fullsync_count 0
+ do_test trans-9.$i.1-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+ } $sig
+ do_test trans-9.$i.2-$cnt {
+ execsql {
+ BEGIN;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ DELETE FROM t3 WHERE random()%10!=0;
+ INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+ ROLLBACK;
+ }
+ signature
+ } $sig
+ if {$i<$limit} {
+ do_test trans-9.$i.3-$cnt {
+ execsql {
+ INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0;
+ }
+ } {}
+ if {$tcl_platform(platform)=="unix"} {
+ do_test trans-9.$i.4-$cnt {
+ expr {$sqlite_sync_count>0}
+ } 1
+ ifcapable pager_pragmas {
+ do_test trans-9.$i.5-$cnt {
+ expr {$sqlite_fullsync_count>0}
+ } [expr {$i%2==0}]
+ } else {
+ do_test trans-9.$i.5-$cnt {
+ expr {$sqlite_fullsync_count==0}
+ } {1}
+ }
+ }
+ }
+ set ::pager_old_format 0
+}
+
+finish_test
diff --git a/third_party/sqlite/test/trigger1.test b/third_party/sqlite/test/trigger1.test
new file mode 100755
index 0000000..c4e330a
--- /dev/null
+++ b/third_party/sqlite/test/trigger1.test
@@ -0,0 +1,631 @@
+# 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 tests creating and dropping triggers, and interaction thereof
+# with the database COMMIT/ROLLBACK logic.
+#
+# 1. CREATE and DROP TRIGGER tests
+# trig-1.1: Error if table does not exist
+# trig-1.2: Error if trigger already exists
+# trig-1.3: Created triggers are deleted if the transaction is rolled back
+# trig-1.4: DROP TRIGGER removes trigger
+# trig-1.5: Dropped triggers are restored if the transaction is rolled back
+# trig-1.6: Error if dropped trigger doesn't exist
+# trig-1.7: Dropping the table automatically drops all triggers
+# trig-1.8: A trigger created on a TEMP table is not inserted into sqlite_master
+# trig-1.9: Ensure that we cannot create a trigger on sqlite_master
+# trig-1.10:
+# trig-1.11:
+# trig-1.12: Ensure that INSTEAD OF triggers cannot be created on tables
+# trig-1.13: Ensure that AFTER triggers cannot be created on views
+# trig-1.14: Ensure that BEFORE triggers cannot be created on views
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+do_test trigger1-1.1.1 {
+ catchsql {
+ CREATE TRIGGER trig UPDATE ON no_such_table BEGIN
+ SELECT * from sqlite_master;
+ END;
+ }
+} {1 {no such table: main.no_such_table}}
+
+ifcapable tempdb {
+ do_test trigger1-1.1.2 {
+ catchsql {
+ CREATE TEMP TRIGGER trig UPDATE ON no_such_table BEGIN
+ SELECT * from sqlite_master;
+ END;
+ }
+ } {1 {no such table: no_such_table}}
+}
+
+execsql {
+ CREATE TABLE t1(a);
+}
+do_test trigger1-1.1.3 {
+ catchsql {
+ CREATE TRIGGER trig UPDATE ON t1 FOR EACH STATEMENT BEGIN
+ SELECT * FROM sqlite_master;
+ END;
+ }
+} {1 {near "STATEMENT": syntax error}}
+execsql {
+ CREATE TRIGGER tr1 INSERT ON t1 BEGIN
+ INSERT INTO t1 values(1);
+ END;
+}
+do_test trigger1-1.2.0 {
+ catchsql {
+ CREATE TRIGGER IF NOT EXISTS tr1 DELETE ON t1 BEGIN
+ SELECT * FROM sqlite_master;
+ END
+ }
+} {0 {}}
+do_test trigger1-1.2.1 {
+ catchsql {
+ CREATE TRIGGER tr1 DELETE ON t1 BEGIN
+ SELECT * FROM sqlite_master;
+ END
+ }
+} {1 {trigger tr1 already exists}}
+do_test trigger1-1.2.2 {
+ catchsql {
+ CREATE TRIGGER "tr1" DELETE ON t1 BEGIN
+ SELECT * FROM sqlite_master;
+ END
+ }
+} {1 {trigger "tr1" already exists}}
+do_test trigger1-1.2.3 {
+ catchsql {
+ CREATE TRIGGER [tr1] DELETE ON t1 BEGIN
+ SELECT * FROM sqlite_master;
+ END
+ }
+} {1 {trigger [tr1] already exists}}
+
+do_test trigger1-1.3 {
+ catchsql {
+ BEGIN;
+ CREATE TRIGGER tr2 INSERT ON t1 BEGIN
+ SELECT * from sqlite_master; END;
+ ROLLBACK;
+ CREATE TRIGGER tr2 INSERT ON t1 BEGIN
+ SELECT * from sqlite_master; END;
+ }
+} {0 {}}
+
+do_test trigger1-1.4 {
+ catchsql {
+ DROP TRIGGER IF EXISTS tr1;
+ CREATE TRIGGER tr1 DELETE ON t1 BEGIN
+ SELECT * FROM sqlite_master;
+ END
+ }
+} {0 {}}
+
+do_test trigger1-1.5 {
+ execsql {
+ BEGIN;
+ DROP TRIGGER tr2;
+ ROLLBACK;
+ DROP TRIGGER tr2;
+ }
+} {}
+
+do_test trigger1-1.6.1 {
+ catchsql {
+ DROP TRIGGER IF EXISTS biggles;
+ }
+} {0 {}}
+
+do_test trigger1-1.6.2 {
+ catchsql {
+ DROP TRIGGER biggles;
+ }
+} {1 {no such trigger: biggles}}
+
+do_test trigger1-1.7 {
+ catchsql {
+ DROP TABLE t1;
+ DROP TRIGGER tr1;
+ }
+} {1 {no such trigger: tr1}}
+
+ifcapable tempdb {
+ execsql {
+ CREATE TEMP TABLE temp_table(a);
+ }
+ do_test trigger1-1.8 {
+ execsql {
+ CREATE TRIGGER temp_trig UPDATE ON temp_table BEGIN
+ SELECT * from sqlite_master;
+ END;
+ SELECT count(*) FROM sqlite_master WHERE name = 'temp_trig';
+ }
+ } {0}
+}
+
+do_test trigger1-1.9 {
+ catchsql {
+ CREATE TRIGGER tr1 AFTER UPDATE ON sqlite_master BEGIN
+ SELECT * FROM sqlite_master;
+ END;
+ }
+} {1 {cannot create trigger on system table}}
+
+# Check to make sure that a DELETE statement within the body of
+# a trigger does not mess up the DELETE that caused the trigger to
+# run in the first place.
+#
+do_test trigger1-1.10 {
+ execsql {
+ create table t1(a,b);
+ insert into t1 values(1,'a');
+ insert into t1 values(2,'b');
+ insert into t1 values(3,'c');
+ insert into t1 values(4,'d');
+ create trigger r1 after delete on t1 for each row begin
+ delete from t1 WHERE a=old.a+2;
+ end;
+ delete from t1 where a=1 OR a=3;
+ select * from t1;
+ drop table t1;
+ }
+} {2 b 4 d}
+
+do_test trigger1-1.11 {
+ execsql {
+ create table t1(a,b);
+ insert into t1 values(1,'a');
+ insert into t1 values(2,'b');
+ insert into t1 values(3,'c');
+ insert into t1 values(4,'d');
+ create trigger r1 after update on t1 for each row begin
+ delete from t1 WHERE a=old.a+2;
+ end;
+ update t1 set b='x-' || b where a=1 OR a=3;
+ select * from t1;
+ drop table t1;
+ }
+} {1 x-a 2 b 4 d}
+
+# Ensure that we cannot create INSTEAD OF triggers on tables
+do_test trigger1-1.12 {
+ catchsql {
+ create table t1(a,b);
+ create trigger t1t instead of update on t1 for each row begin
+ delete from t1 WHERE a=old.a+2;
+ end;
+ }
+} {1 {cannot create INSTEAD OF trigger on table: main.t1}}
+
+ifcapable view {
+# Ensure that we cannot create BEFORE triggers on views
+do_test trigger1-1.13 {
+ catchsql {
+ create view v1 as select * from t1;
+ create trigger v1t before update on v1 for each row begin
+ delete from t1 WHERE a=old.a+2;
+ end;
+ }
+} {1 {cannot create BEFORE trigger on view: main.v1}}
+# Ensure that we cannot create AFTER triggers on views
+do_test trigger1-1.14 {
+ catchsql {
+ drop view v1;
+ create view v1 as select * from t1;
+ create trigger v1t AFTER update on v1 for each row begin
+ delete from t1 WHERE a=old.a+2;
+ end;
+ }
+} {1 {cannot create AFTER trigger on view: main.v1}}
+} ;# ifcapable view
+
+# Check for memory leaks in the trigger parser
+#
+do_test trigger1-2.1 {
+ catchsql {
+ CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
+ SELECT * FROM; -- Syntax error
+ END;
+ }
+} {1 {near ";": syntax error}}
+do_test trigger1-2.2 {
+ catchsql {
+ CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
+ SELECT * FROM t1;
+ SELECT * FROM; -- Syntax error
+ END;
+ }
+} {1 {near ";": syntax error}}
+
+# Create a trigger that refers to a table that might not exist.
+#
+ifcapable tempdb {
+ do_test trigger1-3.1 {
+ execsql {
+ CREATE TEMP TABLE t2(x,y);
+ }
+ catchsql {
+ CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
+ INSERT INTO t2 VALUES(NEW.a,NEW.b);
+ END;
+ }
+ } {0 {}}
+ do_test trigger-3.2 {
+ catchsql {
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+ } {1 {no such table: main.t2}}
+ do_test trigger-3.3 {
+ db close
+ set rc [catch {sqlite3 db test.db} err]
+ if {$rc} {lappend rc $err}
+ set rc
+ } {0}
+ do_test trigger-3.4 {
+ catchsql {
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+ } {1 {no such table: main.t2}}
+ do_test trigger-3.5 {
+ catchsql {
+ CREATE TEMP TABLE t2(x,y);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+ } {1 {no such table: main.t2}}
+ do_test trigger-3.6 {
+ catchsql {
+ DROP TRIGGER r1;
+ CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN
+ INSERT INTO t2 VALUES(NEW.a,NEW.b);
+ END;
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t2;
+ }
+ } {0 {1 2}}
+ do_test trigger-3.7 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(x,y);
+ SELECT * FROM t2;
+ }
+ } {}
+
+ # There are two versions of trigger-3.8 and trigger-3.9. One that uses
+ # compound SELECT statements, and another that does not.
+ ifcapable compound {
+ do_test trigger1-3.8 {
+ execsql {
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1 UNION ALL SELECT * FROM t2;
+ }
+ } {1 2 3 4 3 4}
+ do_test trigger1-3.9 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ INSERT INTO t1 VALUES(5,6);
+ SELECT * FROM t1 UNION ALL SELECT * FROM t2;
+ }
+ } {1 2 3 4 5 6 3 4}
+ } ;# ifcapable compound
+ ifcapable !compound {
+ do_test trigger1-3.8 {
+ execsql {
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+ } {1 2 3 4 3 4}
+ do_test trigger1-3.9 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ INSERT INTO t1 VALUES(5,6);
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+ } {1 2 3 4 5 6 3 4}
+ } ;# ifcapable !compound
+
+ do_test trigger1-4.1 {
+ execsql {
+ CREATE TEMP TRIGGER r1 BEFORE INSERT ON t1 BEGIN
+ INSERT INTO t2 VALUES(NEW.a,NEW.b);
+ END;
+ INSERT INTO t1 VALUES(7,8);
+ SELECT * FROM t2;
+ }
+ } {3 4 7 8}
+ do_test trigger1-4.2 {
+ sqlite3 db2 test.db
+ execsql {
+ INSERT INTO t1 VALUES(9,10);
+ } db2;
+ db2 close
+ execsql {
+ SELECT * FROM t2;
+ }
+ } {3 4 7 8}
+ do_test trigger1-4.3 {
+ execsql {
+ DROP TABLE t1;
+ SELECT * FROM t2;
+ };
+ } {3 4 7 8}
+ do_test trigger1-4.4 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM t2;
+ };
+ } {3 4 7 8}
+} else {
+ execsql {
+ CREATE TABLE t2(x,y);
+ DROP TABLE t1;
+ INSERT INTO t2 VALUES(3, 4);
+ INSERT INTO t2 VALUES(7, 8);
+ }
+}
+
+
+integrity_check trigger1-5.1
+
+# Create a trigger with the same name as a table. Make sure the
+# trigger works. Then drop the trigger. Make sure the table is
+# still there.
+#
+set view_v1 {}
+ifcapable view {
+ set view_v1 {view v1}
+}
+do_test trigger1-6.1 {
+ execsql {SELECT type, name FROM sqlite_master}
+} [concat $view_v1 {table t2}]
+do_test trigger1-6.2 {
+ execsql {
+ CREATE TRIGGER t2 BEFORE DELETE ON t2 BEGIN
+ SELECT RAISE(ABORT,'deletes are not allows');
+ END;
+ SELECT type, name FROM sqlite_master;
+ }
+} [concat $view_v1 {table t2 trigger t2}]
+do_test trigger1-6.3 {
+ catchsql {DELETE FROM t2}
+} {1 {deletes are not allows}}
+do_test trigger1-6.4 {
+ execsql {SELECT * FROM t2}
+} {3 4 7 8}
+do_test trigger1-6.5 {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT type, name FROM sqlite_master}
+} [concat $view_v1 {table t2 trigger t2}]
+do_test trigger1-6.6 {
+ execsql {
+ DROP TRIGGER t2;
+ SELECT type, name FROM sqlite_master;
+ }
+} [concat $view_v1 {table t2}]
+do_test trigger1-6.7 {
+ execsql {SELECT * FROM t2}
+} {3 4 7 8}
+do_test trigger1-6.8 {
+ db close
+ sqlite3 db test.db
+ execsql {SELECT * FROM t2}
+} {3 4 7 8}
+
+integrity_check trigger-7.1
+
+# Check to make sure the name of a trigger can be quoted so that keywords
+# can be used as trigger names. Ticket #468
+#
+do_test trigger1-8.1 {
+ execsql {
+ CREATE TRIGGER 'trigger' AFTER INSERT ON t2 BEGIN SELECT 1; END;
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {trigger}
+do_test trigger1-8.2 {
+ execsql {
+ DROP TRIGGER 'trigger';
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {}
+do_test trigger1-8.3 {
+ execsql {
+ CREATE TRIGGER "trigger" AFTER INSERT ON t2 BEGIN SELECT 1; END;
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {trigger}
+do_test trigger1-8.4 {
+ execsql {
+ DROP TRIGGER "trigger";
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {}
+do_test trigger1-8.5 {
+ execsql {
+ CREATE TRIGGER [trigger] AFTER INSERT ON t2 BEGIN SELECT 1; END;
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {trigger}
+do_test trigger1-8.6 {
+ execsql {
+ DROP TRIGGER [trigger];
+ SELECT name FROM sqlite_master WHERE type='trigger';
+ }
+} {}
+
+ifcapable conflict {
+ # Make sure REPLACE works inside of triggers.
+ #
+ # There are two versions of trigger-9.1 and trigger-9.2. One that uses
+ # compound SELECT statements, and another that does not.
+ ifcapable compound {
+ do_test trigger1-9.1 {
+ execsql {
+ CREATE TABLE t3(a,b);
+ CREATE TABLE t4(x UNIQUE, b);
+ CREATE TRIGGER r34 AFTER INSERT ON t3 BEGIN
+ REPLACE INTO t4 VALUES(new.a,new.b);
+ END;
+ INSERT INTO t3 VALUES(1,2);
+ SELECT * FROM t3 UNION ALL SELECT 99, 99 UNION ALL SELECT * FROM t4;
+ }
+ } {1 2 99 99 1 2}
+ do_test trigger1-9.2 {
+ execsql {
+ INSERT INTO t3 VALUES(1,3);
+ SELECT * FROM t3 UNION ALL SELECT 99, 99 UNION ALL SELECT * FROM t4;
+ }
+ } {1 2 1 3 99 99 1 3}
+ } else {
+ do_test trigger1-9.1 {
+ execsql {
+ CREATE TABLE t3(a,b);
+ CREATE TABLE t4(x UNIQUE, b);
+ CREATE TRIGGER r34 AFTER INSERT ON t3 BEGIN
+ REPLACE INTO t4 VALUES(new.a,new.b);
+ END;
+ INSERT INTO t3 VALUES(1,2);
+ SELECT * FROM t3; SELECT 99, 99; SELECT * FROM t4;
+ }
+ } {1 2 99 99 1 2}
+ do_test trigger1-9.2 {
+ execsql {
+ INSERT INTO t3 VALUES(1,3);
+ SELECT * FROM t3; SELECT 99, 99; SELECT * FROM t4;
+ }
+ } {1 2 1 3 99 99 1 3}
+ }
+ execsql {
+ DROP TABLE t3;
+ DROP TABLE t4;
+ }
+}
+
+
+# Ticket #764. At one stage TEMP triggers would fail to re-install when the
+# schema was reloaded. The following tests ensure that TEMP triggers are
+# correctly re-installed.
+#
+# Also verify that references within trigger programs are resolved at
+# statement compile time, not trigger installation time. This means, for
+# example, that you can drop and re-create tables referenced by triggers.
+ifcapable tempdb&&attach {
+ do_test trigger1-10.0 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ } {}
+ do_test trigger1-10.1 {
+ execsql {
+ CREATE TABLE main.t4(a, b, c);
+ CREATE TABLE temp.t4(a, b, c);
+ CREATE TABLE aux.t4(a, b, c);
+ CREATE TABLE insert_log(db, a, b, c);
+ }
+ } {}
+ do_test trigger1-10.2 {
+ execsql {
+ CREATE TEMP TRIGGER trig1 AFTER INSERT ON main.t4 BEGIN
+ INSERT INTO insert_log VALUES('main', new.a, new.b, new.c);
+ END;
+ CREATE TEMP TRIGGER trig2 AFTER INSERT ON temp.t4 BEGIN
+ INSERT INTO insert_log VALUES('temp', new.a, new.b, new.c);
+ END;
+ CREATE TEMP TRIGGER trig3 AFTER INSERT ON aux.t4 BEGIN
+ INSERT INTO insert_log VALUES('aux', new.a, new.b, new.c);
+ END;
+ }
+ } {}
+ do_test trigger1-10.3 {
+ execsql {
+ INSERT INTO main.t4 VALUES(1, 2, 3);
+ INSERT INTO temp.t4 VALUES(4, 5, 6);
+ INSERT INTO aux.t4 VALUES(7, 8, 9);
+ }
+ } {}
+ do_test trigger1-10.4 {
+ execsql {
+ SELECT * FROM insert_log;
+ }
+ } {main 1 2 3 temp 4 5 6 aux 7 8 9}
+ do_test trigger1-10.5 {
+ execsql {
+ BEGIN;
+ INSERT INTO main.t4 VALUES(1, 2, 3);
+ INSERT INTO temp.t4 VALUES(4, 5, 6);
+ INSERT INTO aux.t4 VALUES(7, 8, 9);
+ ROLLBACK;
+ }
+ } {}
+ do_test trigger1-10.6 {
+ execsql {
+ SELECT * FROM insert_log;
+ }
+ } {main 1 2 3 temp 4 5 6 aux 7 8 9}
+ do_test trigger1-10.7 {
+ execsql {
+ DELETE FROM insert_log;
+ INSERT INTO main.t4 VALUES(11, 12, 13);
+ INSERT INTO temp.t4 VALUES(14, 15, 16);
+ INSERT INTO aux.t4 VALUES(17, 18, 19);
+ }
+ } {}
+ do_test trigger1-10.8 {
+ execsql {
+ SELECT * FROM insert_log;
+ }
+ } {main 11 12 13 temp 14 15 16 aux 17 18 19}
+ do_test trigger1-10.8 {
+ # Drop and re-create the insert_log table in a different database. Note
+ # that we can change the column names because the trigger programs don't
+ # use them explicitly.
+ execsql {
+ DROP TABLE insert_log;
+ CREATE TABLE aux.insert_log(db, d, e, f);
+ }
+ } {}
+ do_test trigger1-10.10 {
+ execsql {
+ INSERT INTO main.t4 VALUES(21, 22, 23);
+ INSERT INTO temp.t4 VALUES(24, 25, 26);
+ INSERT INTO aux.t4 VALUES(27, 28, 29);
+ }
+ } {}
+ do_test trigger1-10.11 {
+ execsql {
+ SELECT * FROM insert_log;
+ }
+ } {main 21 22 23 temp 24 25 26 aux 27 28 29}
+}
+
+do_test trigger1-11.1 {
+ catchsql {SELECT raise(abort,'message');}
+} {1 {RAISE() may only be used within a trigger-program}}
+
+
+finish_test
diff --git a/third_party/sqlite/test/trigger2.test b/third_party/sqlite/test/trigger2.test
new file mode 100755
index 0000000..b150c41
--- /dev/null
+++ b/third_party/sqlite/test/trigger2.test
@@ -0,0 +1,742 @@
+# 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.
+#
+#***********************************************************************
+#
+# Regression testing of FOR EACH ROW table triggers
+#
+# 1. Trigger execution order tests.
+# These tests ensure that BEFORE and AFTER triggers are fired at the correct
+# times relative to each other and the triggering statement.
+#
+# trigger2-1.1.*: ON UPDATE trigger execution model.
+# trigger2-1.2.*: DELETE trigger execution model.
+# trigger2-1.3.*: INSERT trigger execution model.
+#
+# 2. Trigger program execution tests.
+# These tests ensure that trigger programs execute correctly (ie. that a
+# trigger program can correctly execute INSERT, UPDATE, DELETE * SELECT
+# statements, and combinations thereof).
+#
+# 3. Selective trigger execution
+# This tests that conditional triggers (ie. UPDATE OF triggers and triggers
+# with WHEN clauses) are fired only fired when they are supposed to be.
+#
+# trigger2-3.1: UPDATE OF triggers
+# trigger2-3.2: WHEN clause
+#
+# 4. Cascaded trigger execution
+# Tests that trigger-programs may cause other triggers to fire. Also that a
+# trigger-program is never executed recursively.
+#
+# trigger2-4.1: Trivial cascading trigger
+# trigger2-4.2: Trivial recursive trigger handling
+#
+# 5. Count changes behaviour.
+# Verify that rows altered by triggers are not included in the return value
+# of the "count changes" interface.
+#
+# 6. ON CONFLICT clause handling
+# trigger2-6.1[a-f]: INSERT statements
+# trigger2-6.2[a-f]: UPDATE statements
+#
+# 7. & 8. Triggers on views fire correctly.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# 1.
+ifcapable subquery {
+ set ii 0
+ set tbl_definitions [list \
+ {CREATE TABLE tbl (a, b);} \
+ {CREATE TABLE tbl (a INTEGER PRIMARY KEY, b);} \
+ {CREATE TABLE tbl (a, b PRIMARY KEY);} \
+ {CREATE TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);} \
+ ]
+ ifcapable tempdb {
+ lappend tbl_definitions \
+ {CREATE TEMP TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);}
+ lappend tbl_definitions {CREATE TEMP TABLE tbl (a, b);}
+ lappend tbl_definitions \
+ {CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b);}
+ }
+ foreach tbl_defn $tbl_definitions {
+ incr ii
+ catchsql { DROP INDEX tbl_idx; }
+ catchsql {
+ DROP TABLE rlog;
+ DROP TABLE clog;
+ DROP TABLE tbl;
+ DROP TABLE other_tbl;
+ }
+
+ execsql $tbl_defn
+
+ execsql {
+ INSERT INTO tbl VALUES(1, 2);
+ INSERT INTO tbl VALUES(3, 4);
+
+ CREATE TABLE rlog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
+ CREATE TABLE clog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
+
+ CREATE TRIGGER before_update_row BEFORE UPDATE ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ old.a, old.b,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ new.a, new.b);
+ END;
+
+ CREATE TRIGGER after_update_row AFTER UPDATE ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ old.a, old.b,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ new.a, new.b);
+ END;
+
+ CREATE TRIGGER conditional_update_row AFTER UPDATE ON tbl FOR EACH ROW
+ WHEN old.a = 1
+ BEGIN
+ INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog),
+ old.a, old.b,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ new.a, new.b);
+ END;
+ }
+
+ do_test trigger2-1.$ii.1 {
+ set r {}
+ foreach v [execsql {
+ UPDATE tbl SET a = a * 10, b = b * 10;
+ SELECT * FROM rlog ORDER BY idx;
+ SELECT * FROM clog ORDER BY idx;
+ }] {
+ lappend r [expr {int($v)}]
+ }
+ set r
+ } [list 1 1 2 4 6 10 20 \
+ 2 1 2 13 24 10 20 \
+ 3 3 4 13 24 30 40 \
+ 4 3 4 40 60 30 40 \
+ 1 1 2 13 24 10 20 ]
+
+ execsql {
+ DELETE FROM rlog;
+ DELETE FROM tbl;
+ INSERT INTO tbl VALUES (100, 100);
+ INSERT INTO tbl VALUES (300, 200);
+ CREATE TRIGGER delete_before_row BEFORE DELETE ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ old.a, old.b,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ 0, 0);
+ END;
+
+ CREATE TRIGGER delete_after_row AFTER DELETE ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ old.a, old.b,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ 0, 0);
+ END;
+ }
+ do_test trigger2-1.$ii.2 {
+ set r {}
+ foreach v [execsql {
+ DELETE FROM tbl;
+ SELECT * FROM rlog;
+ }] {
+ lappend r [expr {int($v)}]
+ }
+ set r
+ } [list 1 100 100 400 300 0 0 \
+ 2 100 100 300 200 0 0 \
+ 3 300 200 300 200 0 0 \
+ 4 300 200 0 0 0 0 ]
+
+ execsql {
+ DELETE FROM rlog;
+ CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ 0, 0,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ new.a, new.b);
+ END;
+
+ CREATE TRIGGER insert_after_row AFTER INSERT ON tbl FOR EACH ROW
+ BEGIN
+ INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
+ 0, 0,
+ (SELECT coalesce(sum(a),0) FROM tbl),
+ (SELECT coalesce(sum(b),0) FROM tbl),
+ new.a, new.b);
+ END;
+ }
+ do_test trigger2-1.$ii.3 {
+ execsql {
+
+ CREATE TABLE other_tbl(a, b);
+ INSERT INTO other_tbl VALUES(1, 2);
+ INSERT INTO other_tbl VALUES(3, 4);
+ -- INSERT INTO tbl SELECT * FROM other_tbl;
+ INSERT INTO tbl VALUES(5, 6);
+ DROP TABLE other_tbl;
+
+ SELECT * FROM rlog;
+ }
+ } [list 1 0 0 0 0 5 6 \
+ 2 0 0 5 6 5 6 ]
+
+ integrity_check trigger2-1.$ii.4
+ }
+ catchsql {
+ DROP TABLE rlog;
+ DROP TABLE clog;
+ DROP TABLE tbl;
+ DROP TABLE other_tbl;
+ }
+}
+
+# 2.
+set ii 0
+foreach tr_program {
+ {UPDATE tbl SET b = old.b;}
+ {INSERT INTO log VALUES(new.c, 2, 3);}
+ {DELETE FROM log WHERE a = 1;}
+ {INSERT INTO tbl VALUES(500, new.b * 10, 700);
+ UPDATE tbl SET c = old.c;
+ DELETE FROM log;}
+ {INSERT INTO log select * from tbl;}
+} {
+ foreach test_varset [ list \
+ {
+ set statement {UPDATE tbl SET c = 10 WHERE a = 1;}
+ set prep {INSERT INTO tbl VALUES(1, 2, 3);}
+ set newC 10
+ set newB 2
+ set newA 1
+ set oldA 1
+ set oldB 2
+ set oldC 3
+ } \
+ {
+ set statement {DELETE FROM tbl WHERE a = 1;}
+ set prep {INSERT INTO tbl VALUES(1, 2, 3);}
+ set oldA 1
+ set oldB 2
+ set oldC 3
+ } \
+ {
+ set statement {INSERT INTO tbl VALUES(1, 2, 3);}
+ set newA 1
+ set newB 2
+ set newC 3
+ }
+ ] \
+ {
+ set statement {}
+ set prep {}
+ set newA {''}
+ set newB {''}
+ set newC {''}
+ set oldA {''}
+ set oldB {''}
+ set oldC {''}
+
+ incr ii
+
+ eval $test_varset
+
+ set statement_type [string range $statement 0 5]
+ set tr_program_fixed $tr_program
+ if {$statement_type == "DELETE"} {
+ regsub -all new\.a $tr_program_fixed {''} tr_program_fixed
+ regsub -all new\.b $tr_program_fixed {''} tr_program_fixed
+ regsub -all new\.c $tr_program_fixed {''} tr_program_fixed
+ }
+ if {$statement_type == "INSERT"} {
+ regsub -all old\.a $tr_program_fixed {''} tr_program_fixed
+ regsub -all old\.b $tr_program_fixed {''} tr_program_fixed
+ regsub -all old\.c $tr_program_fixed {''} tr_program_fixed
+ }
+
+
+ set tr_program_cooked $tr_program
+ regsub -all new\.a $tr_program_cooked $newA tr_program_cooked
+ regsub -all new\.b $tr_program_cooked $newB tr_program_cooked
+ regsub -all new\.c $tr_program_cooked $newC tr_program_cooked
+ regsub -all old\.a $tr_program_cooked $oldA tr_program_cooked
+ regsub -all old\.b $tr_program_cooked $oldB tr_program_cooked
+ regsub -all old\.c $tr_program_cooked $oldC tr_program_cooked
+
+ catchsql {
+ DROP TABLE tbl;
+ DROP TABLE log;
+ }
+
+ execsql {
+ CREATE TABLE tbl(a PRIMARY KEY, b, c);
+ CREATE TABLE log(a, b, c);
+ }
+
+ set query {SELECT * FROM tbl; SELECT * FROM log;}
+ set prep "$prep; INSERT INTO log VALUES(1, 2, 3);\
+ INSERT INTO log VALUES(10, 20, 30);"
+
+# Check execution of BEFORE programs:
+
+ set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]
+
+ execsql "DELETE FROM tbl; DELETE FROM log; $prep";
+ execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6]\
+ ON tbl BEGIN $tr_program_fixed END;"
+
+ do_test trigger2-2.$ii-before "execsql {$statement $query}" $before_data
+
+ execsql "DROP TRIGGER the_trigger;"
+ execsql "DELETE FROM tbl; DELETE FROM log;"
+
+# Check execution of AFTER programs
+ set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]
+
+ execsql "DELETE FROM tbl; DELETE FROM log; $prep";
+ execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\
+ ON tbl BEGIN $tr_program_fixed END;"
+
+ do_test trigger2-2.$ii-after "execsql {$statement $query}" $after_data
+ execsql "DROP TRIGGER the_trigger;"
+
+ integrity_check trigger2-2.$ii-integrity
+ }
+}
+catchsql {
+ DROP TABLE tbl;
+ DROP TABLE log;
+}
+
+# 3.
+
+# trigger2-3.1: UPDATE OF triggers
+execsql {
+ CREATE TABLE tbl (a, b, c, d);
+ CREATE TABLE log (a);
+ INSERT INTO log VALUES (0);
+ INSERT INTO tbl VALUES (0, 0, 0, 0);
+ INSERT INTO tbl VALUES (1, 0, 0, 0);
+ CREATE TRIGGER tbl_after_update_cd BEFORE UPDATE OF c, d ON tbl
+ BEGIN
+ UPDATE log SET a = a + 1;
+ END;
+}
+do_test trigger2-3.1 {
+ execsql {
+ UPDATE tbl SET b = 1, c = 10; -- 2
+ UPDATE tbl SET b = 10; -- 0
+ UPDATE tbl SET d = 4 WHERE a = 0; --1
+ UPDATE tbl SET a = 4, b = 10; --0
+ SELECT * FROM log;
+ }
+} {3}
+execsql {
+ DROP TABLE tbl;
+ DROP TABLE log;
+}
+
+# trigger2-3.2: WHEN clause
+set when_triggers [list {t1 BEFORE INSERT ON tbl WHEN new.a > 20}]
+ifcapable subquery {
+ lappend when_triggers \
+ {t2 BEFORE INSERT ON tbl WHEN (SELECT count(*) FROM tbl) = 0}
+}
+
+execsql {
+ CREATE TABLE tbl (a, b, c, d);
+ CREATE TABLE log (a);
+ INSERT INTO log VALUES (0);
+}
+
+foreach trig $when_triggers {
+ execsql "CREATE TRIGGER $trig BEGIN UPDATE log set a = a + 1; END;"
+}
+
+ifcapable subquery {
+ set t232 {1 0 1}
+} else {
+ set t232 {0 0 1}
+}
+do_test trigger2-3.2 {
+ execsql {
+
+ INSERT INTO tbl VALUES(0, 0, 0, 0); -- 1 (ifcapable subquery)
+ SELECT * FROM log;
+ UPDATE log SET a = 0;
+
+ INSERT INTO tbl VALUES(0, 0, 0, 0); -- 0
+ SELECT * FROM log;
+ UPDATE log SET a = 0;
+
+ INSERT INTO tbl VALUES(200, 0, 0, 0); -- 1
+ SELECT * FROM log;
+ UPDATE log SET a = 0;
+ }
+} $t232
+execsql {
+ DROP TABLE tbl;
+ DROP TABLE log;
+}
+integrity_check trigger2-3.3
+
+# Simple cascaded trigger
+execsql {
+ CREATE TABLE tblA(a, b);
+ CREATE TABLE tblB(a, b);
+ CREATE TABLE tblC(a, b);
+
+ CREATE TRIGGER tr1 BEFORE INSERT ON tblA BEGIN
+ INSERT INTO tblB values(new.a, new.b);
+ END;
+
+ CREATE TRIGGER tr2 BEFORE INSERT ON tblB BEGIN
+ INSERT INTO tblC values(new.a, new.b);
+ END;
+}
+do_test trigger2-4.1 {
+ execsql {
+ INSERT INTO tblA values(1, 2);
+ SELECT * FROM tblA;
+ SELECT * FROM tblB;
+ SELECT * FROM tblC;
+ }
+} {1 2 1 2 1 2}
+execsql {
+ DROP TABLE tblA;
+ DROP TABLE tblB;
+ DROP TABLE tblC;
+}
+
+# Simple recursive trigger
+execsql {
+ CREATE TABLE tbl(a, b, c);
+ CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl
+ BEGIN
+ INSERT INTO tbl VALUES (new.a, new.b, new.c);
+ END;
+}
+do_test trigger2-4.2 {
+ execsql {
+ INSERT INTO tbl VALUES (1, 2, 3);
+ select * from tbl;
+ }
+} {1 2 3 1 2 3}
+execsql {
+ DROP TABLE tbl;
+}
+
+# 5.
+execsql {
+ CREATE TABLE tbl(a, b, c);
+ CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl
+ BEGIN
+ INSERT INTO tbl VALUES (1, 2, 3);
+ INSERT INTO tbl VALUES (2, 2, 3);
+ UPDATE tbl set b = 10 WHERE a = 1;
+ DELETE FROM tbl WHERE a = 1;
+ DELETE FROM tbl;
+ END;
+}
+do_test trigger2-5 {
+ execsql {
+ INSERT INTO tbl VALUES(100, 200, 300);
+ }
+ db changes
+} {1}
+execsql {
+ DROP TABLE tbl;
+}
+
+ifcapable conflict {
+ # Handling of ON CONFLICT by INSERT statements inside triggers
+ execsql {
+ CREATE TABLE tbl (a primary key, b, c);
+ CREATE TRIGGER ai_tbl AFTER INSERT ON tbl BEGIN
+ INSERT OR IGNORE INTO tbl values (new.a, 0, 0);
+ END;
+ }
+ do_test trigger2-6.1a {
+ execsql {
+ BEGIN;
+ INSERT INTO tbl values (1, 2, 3);
+ SELECT * from tbl;
+ }
+ } {1 2 3}
+ do_test trigger2-6.1b {
+ catchsql {
+ INSERT OR ABORT INTO tbl values (2, 2, 3);
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.1c {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 3}
+ do_test trigger2-6.1d {
+ catchsql {
+ INSERT OR FAIL INTO tbl values (2, 2, 3);
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.1e {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 3 2 2 3}
+ do_test trigger2-6.1f {
+ execsql {
+ INSERT OR REPLACE INTO tbl values (2, 2, 3);
+ SELECT * from tbl;
+ }
+ } {1 2 3 2 0 0}
+ do_test trigger2-6.1g {
+ catchsql {
+ INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.1h {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {}
+ execsql {DELETE FROM tbl}
+
+
+ # Handling of ON CONFLICT by UPDATE statements inside triggers
+ execsql {
+ INSERT INTO tbl values (4, 2, 3);
+ INSERT INTO tbl values (6, 3, 4);
+ CREATE TRIGGER au_tbl AFTER UPDATE ON tbl BEGIN
+ UPDATE OR IGNORE tbl SET a = new.a, c = 10;
+ END;
+ }
+ do_test trigger2-6.2a {
+ execsql {
+ BEGIN;
+ UPDATE tbl SET a = 1 WHERE a = 4;
+ SELECT * from tbl;
+ }
+ } {1 2 10 6 3 4}
+ do_test trigger2-6.2b {
+ catchsql {
+ UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.2c {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 10 6 3 4}
+ do_test trigger2-6.2d {
+ catchsql {
+ UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.2e {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {4 2 10 6 3 4}
+ do_test trigger2-6.2f.1 {
+ execsql {
+ UPDATE OR REPLACE tbl SET a = 1 WHERE a = 4;
+ SELECT * from tbl;
+ }
+ } {1 3 10}
+ do_test trigger2-6.2f.2 {
+ execsql {
+ INSERT INTO tbl VALUES (2, 3, 4);
+ SELECT * FROM tbl;
+ }
+ } {1 3 10 2 3 4}
+ do_test trigger2-6.2g {
+ catchsql {
+ UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {column a is not unique}}
+ do_test trigger2-6.2h {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {4 2 3 6 3 4}
+ execsql {
+ DROP TABLE tbl;
+ }
+} ; # ifcapable conflict
+
+# 7. Triggers on views
+ifcapable view {
+
+do_test trigger2-7.1 {
+ execsql {
+ CREATE TABLE ab(a, b);
+ CREATE TABLE cd(c, d);
+ INSERT INTO ab VALUES (1, 2);
+ INSERT INTO ab VALUES (0, 0);
+ INSERT INTO cd VALUES (3, 4);
+
+ CREATE TABLE tlog(ii INTEGER PRIMARY KEY,
+ olda, oldb, oldc, oldd, newa, newb, newc, newd);
+
+ CREATE VIEW abcd AS SELECT a, b, c, d FROM ab, cd;
+
+ CREATE TRIGGER before_update INSTEAD OF UPDATE ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
+ END;
+ CREATE TRIGGER after_update INSTEAD OF UPDATE ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
+ END;
+
+ CREATE TRIGGER before_delete INSTEAD OF DELETE ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ old.a, old.b, old.c, old.d, 0, 0, 0, 0);
+ END;
+ CREATE TRIGGER after_delete INSTEAD OF DELETE ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ old.a, old.b, old.c, old.d, 0, 0, 0, 0);
+ END;
+
+ CREATE TRIGGER before_insert INSTEAD OF INSERT ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ 0, 0, 0, 0, new.a, new.b, new.c, new.d);
+ END;
+ CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
+ INSERT INTO tlog VALUES(NULL,
+ 0, 0, 0, 0, new.a, new.b, new.c, new.d);
+ END;
+ }
+} {};
+
+do_test trigger2-7.2 {
+ execsql {
+ UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+ DELETE FROM abcd WHERE a = 1;
+ INSERT INTO abcd VALUES(10, 20, 30, 40);
+ SELECT * FROM tlog;
+ }
+} [ list 1 1 2 3 4 100 25 3 4 \
+ 2 1 2 3 4 100 25 3 4 \
+ 3 1 2 3 4 0 0 0 0 \
+ 4 1 2 3 4 0 0 0 0 \
+ 5 0 0 0 0 10 20 30 40 \
+ 6 0 0 0 0 10 20 30 40 ]
+
+do_test trigger2-7.3 {
+ execsql {
+ DELETE FROM tlog;
+ INSERT INTO abcd VALUES(10, 20, 30, 40);
+ UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+ DELETE FROM abcd WHERE a = 1;
+ SELECT * FROM tlog;
+ }
+} [ list \
+ 1 0 0 0 0 10 20 30 40 \
+ 2 0 0 0 0 10 20 30 40 \
+ 3 1 2 3 4 100 25 3 4 \
+ 4 1 2 3 4 100 25 3 4 \
+ 5 1 2 3 4 0 0 0 0 \
+ 6 1 2 3 4 0 0 0 0 \
+]
+do_test trigger2-7.4 {
+ execsql {
+ DELETE FROM tlog;
+ DELETE FROM abcd WHERE a = 1;
+ INSERT INTO abcd VALUES(10, 20, 30, 40);
+ UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+ SELECT * FROM tlog;
+ }
+} [ list \
+ 1 1 2 3 4 0 0 0 0 \
+ 2 1 2 3 4 0 0 0 0 \
+ 3 0 0 0 0 10 20 30 40 \
+ 4 0 0 0 0 10 20 30 40 \
+ 5 1 2 3 4 100 25 3 4 \
+ 6 1 2 3 4 100 25 3 4 \
+]
+
+do_test trigger2-8.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ CREATE VIEW v1 AS
+ SELECT a+b AS x, b+c AS y, a+c AS z FROM t1;
+ SELECT * FROM v1;
+ }
+} {3 5 4}
+do_test trigger2-8.2 {
+ execsql {
+ CREATE TABLE v1log(a,b,c,d,e,f);
+ CREATE TRIGGER r1 INSTEAD OF DELETE ON v1 BEGIN
+ INSERT INTO v1log VALUES(OLD.x,NULL,OLD.y,NULL,OLD.z,NULL);
+ END;
+ DELETE FROM v1 WHERE x=1;
+ SELECT * FROM v1log;
+ }
+} {}
+do_test trigger2-8.3 {
+ execsql {
+ DELETE FROM v1 WHERE x=3;
+ SELECT * FROM v1log;
+ }
+} {3 {} 5 {} 4 {}}
+do_test trigger2-8.4 {
+ execsql {
+ INSERT INTO t1 VALUES(4,5,6);
+ DELETE FROM v1log;
+ DELETE FROM v1 WHERE y=11;
+ SELECT * FROM v1log;
+ }
+} {9 {} 11 {} 10 {}}
+do_test trigger2-8.5 {
+ execsql {
+ CREATE TRIGGER r2 INSTEAD OF INSERT ON v1 BEGIN
+ INSERT INTO v1log VALUES(NULL,NEW.x,NULL,NEW.y,NULL,NEW.z);
+ END;
+ DELETE FROM v1log;
+ INSERT INTO v1 VALUES(1,2,3);
+ SELECT * FROM v1log;
+ }
+} {{} 1 {} 2 {} 3}
+do_test trigger2-8.6 {
+ execsql {
+ CREATE TRIGGER r3 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO v1log VALUES(OLD.x,NEW.x,OLD.y,NEW.y,OLD.z,NEW.z);
+ END;
+ DELETE FROM v1log;
+ UPDATE v1 SET x=x+100, y=y+200, z=z+300;
+ SELECT * FROM v1log;
+ }
+} {3 103 5 205 4 304 9 109 11 211 10 310}
+
+} ;# ifcapable view
+
+integrity_check trigger2-9.9
+
+finish_test
diff --git a/third_party/sqlite/test/trigger3.test b/third_party/sqlite/test/trigger3.test
new file mode 100755
index 0000000..c67dd28
--- /dev/null
+++ b/third_party/sqlite/test/trigger3.test
@@ -0,0 +1,190 @@
+# 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 tests the RAISE() function.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# Test that we can cause ROLLBACK, FAIL and ABORT correctly
+# catchsql { DROP TABLE tbl; }
+catchsql { CREATE TABLE tbl (a, b, c) }
+
+execsql {
+ CREATE TRIGGER before_tbl_insert BEFORE INSERT ON tbl BEGIN SELECT CASE
+ WHEN (new.a = 4) THEN RAISE(IGNORE) END;
+ END;
+
+ CREATE TRIGGER after_tbl_insert AFTER INSERT ON tbl BEGIN SELECT CASE
+ WHEN (new.a = 1) THEN RAISE(ABORT, 'Trigger abort')
+ WHEN (new.a = 2) THEN RAISE(FAIL, 'Trigger fail')
+ WHEN (new.a = 3) THEN RAISE(ROLLBACK, 'Trigger rollback') END;
+ END;
+}
+# ABORT
+do_test trigger3-1.1 {
+ catchsql {
+ BEGIN;
+ INSERT INTO tbl VALUES (5, 5, 6);
+ INSERT INTO tbl VALUES (1, 5, 6);
+ }
+} {1 {Trigger abort}}
+do_test trigger3-1.2 {
+ execsql {
+ SELECT * FROM tbl;
+ ROLLBACK;
+ }
+} {5 5 6}
+do_test trigger3-1.3 {
+ execsql {SELECT * FROM tbl}
+} {}
+
+# FAIL
+do_test trigger3-2.1 {
+ catchsql {
+ BEGIN;
+ INSERT INTO tbl VALUES (5, 5, 6);
+ INSERT INTO tbl VALUES (2, 5, 6);
+ }
+} {1 {Trigger fail}}
+do_test trigger3-2.2 {
+ execsql {
+ SELECT * FROM tbl;
+ ROLLBACK;
+ }
+} {5 5 6 2 5 6}
+# ROLLBACK
+do_test trigger3-3.1 {
+ catchsql {
+ BEGIN;
+ INSERT INTO tbl VALUES (5, 5, 6);
+ INSERT INTO tbl VALUES (3, 5, 6);
+ }
+} {1 {Trigger rollback}}
+do_test trigger3-3.2 {
+ execsql {
+ SELECT * FROM tbl;
+ }
+} {}
+
+# Verify that a ROLLBACK trigger works like a FAIL trigger if
+# we are not within a transaction. Ticket #3035.
+#
+do_test trigger3-3.3 {
+ catchsql {COMMIT}
+ catchsql {
+ INSERT INTO tbl VALUES (3, 9, 10);
+ }
+} {1 {Trigger rollback}}
+do_test trigger3-3.4 {
+ execsql {SELECT * FROM tbl}
+} {}
+
+# IGNORE
+do_test trigger3-4.1 {
+ catchsql {
+ BEGIN;
+ INSERT INTO tbl VALUES (5, 5, 6);
+ INSERT INTO tbl VALUES (4, 5, 6);
+ }
+} {0 {}}
+do_test trigger3-4.2 {
+ execsql {
+ SELECT * FROM tbl;
+ ROLLBACK;
+ }
+} {5 5 6}
+
+# Check that we can also do RAISE(IGNORE) for UPDATE and DELETE
+execsql {DROP TABLE tbl;}
+execsql {CREATE TABLE tbl (a, b, c);}
+execsql {INSERT INTO tbl VALUES(1, 2, 3);}
+execsql {INSERT INTO tbl VALUES(4, 5, 6);}
+execsql {
+ CREATE TRIGGER before_tbl_update BEFORE UPDATE ON tbl BEGIN
+ SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
+ END;
+
+ CREATE TRIGGER before_tbl_delete BEFORE DELETE ON tbl BEGIN
+ SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
+ END;
+}
+do_test trigger3-5.1 {
+ execsql {
+ UPDATE tbl SET c = 10;
+ SELECT * FROM tbl;
+ }
+} {1 2 3 4 5 10}
+do_test trigger3-5.2 {
+ execsql {
+ DELETE FROM tbl;
+ SELECT * FROM tbl;
+ }
+} {1 2 3}
+
+# Check that RAISE(IGNORE) works correctly for nested triggers:
+execsql {CREATE TABLE tbl2(a, b, c)}
+execsql {
+ CREATE TRIGGER after_tbl2_insert AFTER INSERT ON tbl2 BEGIN
+ UPDATE tbl SET c = 10;
+ INSERT INTO tbl2 VALUES (new.a, new.b, new.c);
+ END;
+}
+do_test trigger3-6 {
+ execsql {
+ INSERT INTO tbl2 VALUES (1, 2, 3);
+ SELECT * FROM tbl2;
+ SELECT * FROM tbl;
+ }
+} {1 2 3 1 2 3 1 2 3}
+
+# Check that things also work for view-triggers
+
+ifcapable view {
+
+execsql {CREATE VIEW tbl_view AS SELECT * FROM tbl}
+execsql {
+ CREATE TRIGGER tbl_view_insert INSTEAD OF INSERT ON tbl_view BEGIN
+ SELECT CASE WHEN (new.a = 1) THEN RAISE(ROLLBACK, 'View rollback')
+ WHEN (new.a = 2) THEN RAISE(IGNORE)
+ WHEN (new.a = 3) THEN RAISE(ABORT, 'View abort') END;
+ END;
+}
+
+do_test trigger3-7.1 {
+ catchsql {
+ INSERT INTO tbl_view VALUES(1, 2, 3);
+ }
+} {1 {View rollback}}
+do_test trigger3-7.2 {
+ catchsql {
+ INSERT INTO tbl_view VALUES(2, 2, 3);
+ }
+} {0 {}}
+do_test trigger3-7.3 {
+ catchsql {
+ INSERT INTO tbl_view VALUES(3, 2, 3);
+ }
+} {1 {View abort}}
+
+} ;# ifcapable view
+
+integrity_check trigger3-8.1
+
+catchsql { DROP TABLE tbl; }
+catchsql { DROP TABLE tbl2; }
+catchsql { DROP VIEW tbl_view; }
+
+finish_test
diff --git a/third_party/sqlite/test/trigger4.test b/third_party/sqlite/test/trigger4.test
new file mode 100755
index 0000000..0e44ce6
--- /dev/null
+++ b/third_party/sqlite/test/trigger4.test
@@ -0,0 +1,200 @@
+# 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 tests the triggers of views.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If either views or triggers are disabled in this build, omit this file.
+ifcapable {!trigger || !view} {
+ finish_test
+ return
+}
+
+do_test trigger4-1.1 {
+ execsql {
+ create table test1(id integer primary key,a);
+ create table test2(id integer,b);
+ create view test as
+ select test1.id as id,a as a,b as b
+ from test1 join test2 on test2.id = test1.id;
+ create trigger I_test instead of insert on test
+ begin
+ insert into test1 (id,a) values (NEW.id,NEW.a);
+ insert into test2 (id,b) values (NEW.id,NEW.b);
+ end;
+ insert into test values(1,2,3);
+ select * from test1;
+ }
+} {1 2}
+do_test trigger4-1.2 {
+ execsql {
+ select * from test2;
+ }
+} {1 3}
+do_test trigger4-1.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ insert into test values(4,5,6);
+ select * from test1;
+ }
+} {1 2 4 5}
+do_test trigger4-1.4 {
+ execsql {
+ select * from test2;
+ }
+} {1 3 4 6}
+
+do_test trigger4-2.1 {
+ execsql {
+ create trigger U_test instead of update on test
+ begin
+ update test1 set a=NEW.a where id=NEW.id;
+ update test2 set b=NEW.b where id=NEW.id;
+ end;
+ update test set a=22 where id=1;
+ select * from test1;
+ }
+} {1 22 4 5}
+do_test trigger4-2.2 {
+ execsql {
+ select * from test2;
+ }
+} {1 3 4 6}
+do_test trigger4-2.3 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ update test set b=66 where id=4;
+ select * from test1;
+ }
+} {1 22 4 5}
+do_test trigger4-2.4 {
+ execsql {
+ select * from test2;
+ }
+} {1 3 4 66}
+
+do_test trigger4-3.1 {
+ catchsql {
+ drop table test2;
+ insert into test values(7,8,9);
+ }
+} {1 {no such table: main.test2}}
+do_test trigger4-3.2 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ insert into test values(7,8,9);
+ }
+} {1 {no such table: main.test2}}
+do_test trigger4-3.3 {
+ catchsql {
+ update test set a=222 where id=1;
+ }
+} {1 {no such table: main.test2}}
+do_test trigger4-3.4 {
+ execsql {
+ select * from test1;
+ }
+} {1 22 4 5}
+do_test trigger4-3.5 {
+ execsql {
+ create table test2(id,b);
+ insert into test values(7,8,9);
+ select * from test1;
+ }
+} {1 22 4 5 7 8}
+do_test trigger4-3.6 {
+ execsql {
+ select * from test2;
+ }
+} {7 9}
+do_test trigger4-3.7 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ update test set b=99 where id=7;
+ select * from test2;
+ }
+} {7 99}
+
+do_test trigger4-4.1 {
+ db close
+ file delete -force trigtest.db
+ file delete -force trigtest.db-journal
+ sqlite3 db trigtest.db
+ catchsql {drop table tbl; drop view vw}
+ execsql {
+ create table tbl(a integer primary key, b integer);
+ create view vw as select * from tbl;
+ create trigger t_del_tbl instead of delete on vw for each row begin
+ delete from tbl where a = old.a;
+ end;
+ create trigger t_upd_tbl instead of update on vw for each row begin
+ update tbl set a=new.a, b=new.b where a = old.a;
+ end;
+ create trigger t_ins_tbl instead of insert on vw for each row begin
+ insert into tbl values (new.a,new.b);
+ end;
+ insert into tbl values(101,1001);
+ insert into tbl values(102,1002);
+ insert into tbl select a+2, b+2 from tbl;
+ insert into tbl select a+4, b+4 from tbl;
+ insert into tbl select a+8, b+8 from tbl;
+ insert into tbl select a+16, b+16 from tbl;
+ insert into tbl select a+32, b+32 from tbl;
+ insert into tbl select a+64, b+64 from tbl;
+ select count(*) from vw;
+ }
+} {128}
+do_test trigger4-4.2 {
+ execsql {select a, b from vw where a<103 or a>226 order by a}
+} {101 1001 102 1002 227 1127 228 1128}
+
+#test delete from view
+do_test trigger4-5.1 {
+ catchsql {delete from vw where a>101 and a<2000}
+} {0 {}}
+do_test trigger4-5.2 {
+ execsql {select * from vw}
+} {101 1001}
+
+#test insert into view
+do_test trigger4-6.1 {
+ catchsql {
+ insert into vw values(102,1002);
+ insert into vw select a+2, b+2 from vw;
+ insert into vw select a+4, b+4 from vw;
+ insert into vw select a+8, b+8 from vw;
+ insert into vw select a+16, b+16 from vw;
+ insert into vw select a+32, b+32 from vw;
+ insert into vw select a+64, b+64 from vw;
+ }
+} {0 {}}
+do_test trigger4-6.2 {
+ execsql {select count(*) from vw}
+} {128}
+
+#test update of view
+do_test trigger4-7.1 {
+ catchsql {update vw set b=b+1000 where a>101 and a<2000}
+} {0 {}}
+do_test trigger4-7.2 {
+ execsql {select a, b from vw where a<=102 or a>=227 order by a}
+} {101 1001 102 2002 227 2127 228 2128}
+
+integrity_check trigger4-99.9
+db close
+file delete -force trigtest.db trigtest.db-journal
+
+finish_test
diff --git a/third_party/sqlite/test/trigger5.test b/third_party/sqlite/test/trigger5.test
new file mode 100755
index 0000000..75c56b1
--- /dev/null
+++ b/third_party/sqlite/test/trigger5.test
@@ -0,0 +1,43 @@
+# 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 tests the triggers of views.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# Ticket #844
+#
+do_test trigger5-1.1 {
+ execsql {
+ CREATE TABLE Item(
+ a integer PRIMARY KEY NOT NULL ,
+ b double NULL ,
+ c int NOT NULL DEFAULT 0
+ );
+ CREATE TABLE Undo(UndoAction TEXT);
+ INSERT INTO Item VALUES (1,38205.60865,340);
+ CREATE TRIGGER trigItem_UNDO_AD AFTER DELETE ON Item FOR EACH ROW
+ BEGIN
+ INSERT INTO Undo SELECT 'INSERT INTO Item (a,b,c) VALUES ('
+ || coalesce(old.a,'NULL') || ',' || quote(old.b) || ',' || old.c || ');';
+ END;
+ DELETE FROM Item WHERE a = 1;
+ SELECT * FROM Undo;
+ }
+} {{INSERT INTO Item (a,b,c) VALUES (1,38205.60865,340);}}
+
+integrity_check trigger5-99.9
+
+finish_test
diff --git a/third_party/sqlite/test/trigger6.test b/third_party/sqlite/test/trigger6.test
new file mode 100755
index 0000000..bb343fa
--- /dev/null
+++ b/third_party/sqlite/test/trigger6.test
@@ -0,0 +1,82 @@
+# 2004 December 07
+#
+# 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.
+#
+# This file implements tests to make sure expression of an INSERT
+# and UPDATE statement are only evaluated once. See ticket #980.
+# If an expression uses a function that has side-effects or which
+# is not deterministic (ex: random()) then we want to make sure
+# that the same evaluation occurs for the actual INSERT/UPDATE and
+# for the NEW.* fields of any triggers that fire.
+#
+# $Id: trigger6.test,v 1.2 2005/05/05 11:04:50 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+do_test trigger6-1.1 {
+ execsql {
+ CREATE TABLE t1(x, y);
+ CREATE TABLE log(a, b, c);
+ CREATE TRIGGER r1 BEFORE INSERT ON t1 BEGIN
+ INSERT INTO log VALUES(1, new.x, new.y);
+ END;
+ CREATE TRIGGER r2 BEFORE UPDATE ON t1 BEGIN
+ INSERT INTO log VALUES(2, new.x, new.y);
+ END;
+ }
+ set ::trigger6_cnt 0
+ proc trigger6_counter {args} {
+ incr ::trigger6_cnt
+ return $::trigger6_cnt
+ }
+ db function counter trigger6_counter
+ execsql {
+ INSERT INTO t1 VALUES(1,counter());
+ SELECT * FROM t1;
+ }
+} {1 1}
+do_test trigger6-1.2 {
+ execsql {
+ SELECT * FROM log;
+ }
+} {1 1 1}
+do_test trigger6-1.3 {
+ execsql {
+ DELETE FROM t1;
+ DELETE FROM log;
+ INSERT INTO t1 VALUES(2,counter(2,3)+4);
+ SELECT * FROM t1;
+ }
+} {2 6}
+do_test trigger6-1.4 {
+ execsql {
+ SELECT * FROM log;
+ }
+} {1 2 6}
+do_test trigger6-1.5 {
+ execsql {
+ DELETE FROM log;
+ UPDATE t1 SET y=counter(5);
+ SELECT * FROM t1;
+ }
+} {2 3}
+do_test trigger6-1.6 {
+ execsql {
+ SELECT * FROM log;
+ }
+} {2 2 3}
+
+finish_test
diff --git a/third_party/sqlite/test/trigger7.test b/third_party/sqlite/test/trigger7.test
new file mode 100755
index 0000000..2eacd04
--- /dev/null
+++ b/third_party/sqlite/test/trigger7.test
@@ -0,0 +1,121 @@
+# 2005 August 18
+#
+# 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.
+#
+# This file implements tests to increase coverage of trigger.c.
+#
+# $Id: trigger7.test,v 1.2 2008/03/19 13:03:34 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+
+# Error messages resulting from qualified trigger names.
+#
+do_test trigger7-1.1 {
+ execsql {
+ CREATE TABLE t1(x, y);
+ }
+ catchsql {
+ CREATE TEMP TRIGGER main.r1 AFTER INSERT ON t1 BEGIN
+ SELECT 'no nothing';
+ END
+ }
+} {1 {temporary trigger may not have qualified name}}
+do_test trigger7-1.2 {
+ catchsql {
+ CREATE TRIGGER not_a_db.r1 AFTER INSERT ON t1 BEGIN
+ SELECT 'no nothing';
+ END
+ }
+} {1 {unknown database not_a_db}}
+
+
+# When the UPDATE OF syntax is used, no code is generated for triggers
+# that do not match the update columns.
+#
+ifcapable explain {
+ do_test trigger7-2.1 {
+ execsql {
+ CREATE TRIGGER r1 AFTER UPDATE OF x ON t1 BEGIN
+ SELECT '___update_t1.x___';
+ END;
+ CREATE TRIGGER r2 AFTER UPDATE OF y ON t1 BEGIN
+ SELECT '___update_t1.y___';
+ END;
+ }
+ set txt [db eval {EXPLAIN UPDATE t1 SET x=5}]
+ string match *___update_t1.x___* $txt
+ } 1
+ do_test trigger7-2.2 {
+ set txt [db eval {EXPLAIN UPDATE t1 SET x=5}]
+ string match *___update_t1.y___* $txt
+ } 0
+ do_test trigger7-2.3 {
+ set txt [db eval {EXPLAIN UPDATE t1 SET y=5}]
+ string match *___update_t1.x___* $txt
+ } 0
+ do_test trigger7-2.4 {
+ set txt [db eval {EXPLAIN UPDATE t1 SET y=5}]
+ string match *___update_t1.y___* $txt
+ } 1
+ do_test trigger7-2.5 {
+ set txt [db eval {EXPLAIN UPDATE t1 SET rowid=5}]
+ string match *___update_t1.x___* $txt
+ } 0
+ do_test trigger7-2.6 {
+ set txt [db eval {EXPLAIN UPDATE t1 SET rowid=5}]
+ string match *___update_t1.x___* $txt
+ } 0
+}
+
+# Test the ability to create many triggers on the same table, then
+# selectively drop those triggers.
+#
+do_test trigger7-3.1 {
+ execsql {
+ CREATE TABLE t2(x,y,z);
+ CREATE TRIGGER t2r1 AFTER INSERT ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r2 BEFORE INSERT ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r3 AFTER UPDATE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r4 BEFORE UPDATE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r5 AFTER DELETE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r6 BEFORE DELETE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r7 AFTER INSERT ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r8 BEFORE INSERT ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r9 AFTER UPDATE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r10 BEFORE UPDATE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r11 AFTER DELETE ON t2 BEGIN SELECT 1; END;
+ CREATE TRIGGER t2r12 BEFORE DELETE ON t2 BEGIN SELECT 1; END;
+ DROP TRIGGER t2r6;
+ }
+} {}
+
+# This test corrupts the database file so it must be the last test
+# in the series.
+#
+do_test trigger7-99.1 {
+ execsql {
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET sql='nonsense';
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ DROP TRIGGER t2r5
+ }
+} {1 {malformed database schema (t1) - near "nonsense": syntax error}}
+
+finish_test
diff --git a/third_party/sqlite/test/trigger8.test b/third_party/sqlite/test/trigger8.test
new file mode 100755
index 0000000..b4215fb
--- /dev/null
+++ b/third_party/sqlite/test/trigger8.test
@@ -0,0 +1,42 @@
+# 2006 February 27
+#
+# 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.
+#
+# This file implements tests to make sure abusively large triggers
+# (triggers with 100s or 1000s of statements) work.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+
+do_test trigger8-1.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(y);
+ }
+ set sql "CREATE TRIGGER r10000 AFTER INSERT ON t1 BEGIN\n"
+ for {set i 0} {$i<10000} {incr i} {
+ append sql " INSERT INTO t2 VALUES($i);\n"
+ }
+ append sql "END;"
+ execsql $sql
+ execsql {
+ INSERT INTO t1 VALUES(5);
+ SELECT count(*) FROM t2;
+ }
+} {10000}
+
+finish_test
diff --git a/third_party/sqlite/test/trigger9.test b/third_party/sqlite/test/trigger9.test
new file mode 100755
index 0000000..9d776fc
--- /dev/null
+++ b/third_party/sqlite/test/trigger9.test
@@ -0,0 +1,223 @@
+# 2008 January 1
+#
+# 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. Specifically,
+# it tests some compiler optimizations for SQL statements featuring
+# triggers:
+#
+#
+#
+
+# trigger9-1.* - Test that if there are no references to OLD.* cols, or a
+# reference to only OLD.rowid, the data is not loaded.
+#
+# trigger9-2.* - Test that for NEW.* records populated by UPDATE
+# statements, unused fields are populated with NULL values.
+#
+# trigger9-3.* - Test that the temporary tables used for OLD.* references
+# in "INSTEAD OF" triggers have NULL values in unused
+# fields.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+proc has_rowdata {sql} {
+ expr {[lsearch [execsql "explain $sql"] RowData]>=0}
+}
+
+do_test trigger9-1.1 {
+ execsql {
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x, y, z);
+ INSERT INTO t1 VALUES('1', randstr(10000,10000), '2');
+ INSERT INTO t1 VALUES('2', randstr(10000,10000), '4');
+ INSERT INTO t1 VALUES('3', randstr(10000,10000), '6');
+ CREATE TABLE t2(x);
+ }
+} {}
+
+do_test trigger9-1.2.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE DELETE ON t1 BEGIN
+ INSERT INTO t2 VALUES(old.rowid);
+ END;
+ DELETE FROM t1;
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test trigger9-1.2.3 {
+ has_rowdata {DELETE FROM t1}
+} 0
+do_test trigger9-1.2.4 { execsql { ROLLBACK } } {}
+
+do_test trigger9-1.3.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE DELETE ON t1 BEGIN
+ INSERT INTO t2 VALUES(old.x);
+ END;
+ DELETE FROM t1;
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test trigger9-1.3.2 {
+ has_rowdata {DELETE FROM t1}
+} 1
+do_test trigger9-1.3.3 { execsql { ROLLBACK } } {}
+
+do_test trigger9-1.4.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE DELETE ON t1 WHEN old.x='1' BEGIN
+ INSERT INTO t2 VALUES(old.rowid);
+ END;
+ DELETE FROM t1;
+ SELECT * FROM t2;
+ }
+} {1}
+do_test trigger9-1.4.2 {
+ has_rowdata {DELETE FROM t1}
+} 1
+do_test trigger9-1.4.3 { execsql { ROLLBACK } } {}
+
+do_test trigger9-1.5.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE UPDATE ON t1 BEGIN
+ INSERT INTO t2 VALUES(old.rowid);
+ END;
+ UPDATE t1 SET y = '';
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test trigger9-1.5.2 {
+ has_rowdata {UPDATE t1 SET y = ''}
+} 0
+do_test trigger9-1.5.3 { execsql { ROLLBACK } } {}
+
+do_test trigger9-1.6.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE UPDATE ON t1 BEGIN
+ INSERT INTO t2 VALUES(old.x);
+ END;
+ UPDATE t1 SET y = '';
+ SELECT * FROM t2;
+ }
+} {1 2 3}
+do_test trigger9-1.6.2 {
+ has_rowdata {UPDATE t1 SET y = ''}
+} 1
+do_test trigger9-1.6.3 { execsql { ROLLBACK } } {}
+
+do_test trigger9-1.7.1 {
+ execsql {
+ BEGIN;
+ CREATE TRIGGER trig1 BEFORE UPDATE ON t1 WHEN old.x>='2' BEGIN
+ INSERT INTO t2 VALUES(old.x);
+ END;
+ UPDATE t1 SET y = '';
+ SELECT * FROM t2;
+ }
+} {2 3}
+do_test trigger9-1.7.2 {
+ has_rowdata {UPDATE t1 SET y = ''}
+} 1
+do_test trigger9-1.7.3 { execsql { ROLLBACK } } {}
+
+do_test trigger9-3.1 {
+ execsql {
+ CREATE TABLE t3(a, b);
+ INSERT INTO t3 VALUES(1, 'one');
+ INSERT INTO t3 VALUES(2, 'two');
+ INSERT INTO t3 VALUES(3, 'three');
+ }
+} {}
+do_test trigger9-3.2 {
+ execsql {
+ BEGIN;
+ CREATE VIEW v1 AS SELECT * FROM t3;
+ CREATE TRIGGER trig1 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO t2 VALUES(old.a);
+ END;
+ UPDATE v1 SET b = 'hello';
+ SELECT * FROM t2;
+ ROLLBACK;
+ }
+} {1 2 3}
+do_test trigger9-3.3 {
+ # In this test the 'c' column of the view is not required by
+ # the INSTEAD OF trigger, but the expression is reused internally as
+ # part of the view's WHERE clause. Check that this does not cause
+ # a problem.
+ #
+ execsql {
+ BEGIN;
+ CREATE VIEW v1 AS SELECT a, b AS c FROM t3 WHERE c > 'one';
+ CREATE TRIGGER trig1 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO t2 VALUES(old.a);
+ END;
+ UPDATE v1 SET c = 'hello';
+ SELECT * FROM t2;
+ ROLLBACK;
+ }
+} {2 3}
+do_test trigger9-3.4 {
+ execsql {
+ BEGIN;
+ INSERT INTO t3 VALUES(3, 'three');
+ INSERT INTO t3 VALUES(3, 'four');
+ CREATE VIEW v1 AS SELECT DISTINCT a, b FROM t3;
+ CREATE TRIGGER trig1 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO t2 VALUES(old.a);
+ END;
+ UPDATE v1 SET b = 'hello';
+ SELECT * FROM t2;
+ ROLLBACK;
+ }
+} {1 2 3 3}
+
+ifcapable compound {
+ do_test trigger9-3.5 {
+ execsql {
+ BEGIN;
+ INSERT INTO t3 VALUES(1, 'uno');
+ CREATE VIEW v1 AS SELECT a, b FROM t3 EXCEPT SELECT 1, 'one';
+ CREATE TRIGGER trig1 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO t2 VALUES(old.a);
+ END;
+ UPDATE v1 SET b = 'hello';
+ SELECT * FROM t2;
+ ROLLBACK;
+ }
+ } {1 2 3}
+ do_test trigger9-3.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO t3 VALUES(1, 'zero');
+ CREATE VIEW v1 AS
+ SELECT sum(a) AS a, max(b) AS b FROM t3 GROUP BY t3.a HAVING b>'two';
+ CREATE TRIGGER trig1 INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO t2 VALUES(old.a);
+ END;
+ UPDATE v1 SET b = 'hello';
+ SELECT * FROM t2;
+ ROLLBACK;
+ }
+ } {2}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/triggerA.test b/third_party/sqlite/test/triggerA.test
new file mode 100755
index 0000000..e358352
--- /dev/null
+++ b/third_party/sqlite/test/triggerA.test
@@ -0,0 +1,230 @@
+# 2008 February 12
+#
+# 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. Specifically,
+# it tests issues relating to firing an INSTEAD OF trigger on a VIEW
+# when one tries to UPDATE or DELETE from the view. Does the WHERE
+# clause of the UPDATE or DELETE statement get passed down correctly
+# into the query that manifests the view?
+#
+# Ticket #2938
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !trigger||!compound {
+ finish_test
+ return
+}
+
+# Create two table containing some sample data
+#
+do_test triggerA-1.1 {
+ db eval {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT UNIQUE);
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c TEXT);
+ }
+ set i 1
+ foreach word {one two three four five six seven eight nine ten} {
+ set j [expr {$i*100 + [string length $word]}]
+ db eval {
+ INSERT INTO t1 VALUES($i,$word);
+ INSERT INTO t2 VALUES(20-$i,$j,$word);
+ }
+ incr i
+ }
+ db eval {
+ SELECT count(*) FROM t1 UNION ALL SELECT count(*) FROM t2;
+ }
+} {10 10}
+
+# Create views of various forms against one or both of the two tables.
+#
+do_test triggerA-1.2 {
+ db eval {
+ CREATE VIEW v1 AS SELECT y, x FROM t1;
+ SELECT * FROM v1 ORDER BY 1;
+ }
+} {eight 8 five 5 four 4 nine 9 one 1 seven 7 six 6 ten 10 three 3 two 2}
+do_test triggerA-1.3 {
+ db eval {
+ CREATE VIEW v2 AS SELECT x, y FROM t1 WHERE y GLOB '*e*';
+ SELECT * FROM v2 ORDER BY 1;
+ }
+} {1 one 3 three 5 five 7 seven 8 eight 9 nine 10 ten}
+do_test triggerA-1.4 {
+ db eval {
+ CREATE VIEW v3 AS
+ SELECT CAST(x AS TEXT) AS c1 FROM t1 UNION SELECT y FROM t1;
+ SELECT * FROM v3 ORDER BY c1;
+ }
+} {1 10 2 3 4 5 6 7 8 9 eight five four nine one seven six ten three two}
+do_test triggerA-1.5 {
+ db eval {
+ CREATE VIEW v4 AS
+ SELECT CAST(x AS TEXT) AS c1 FROM t1
+ UNION SELECT y FROM t1 WHERE x BETWEEN 3 and 5;
+ SELECT * FROM v4 ORDER BY 1;
+ }
+} {1 10 2 3 4 5 6 7 8 9 five four three}
+do_test triggerA-1.6 {
+ db eval {
+ CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c;
+ SELECT * FROM v5;
+ }
+} {1 103 2 203 3 305 4 404 5 504 6 603 7 705 8 805 9 904 10 1003}
+
+# Create INSTEAD OF triggers on the views. Run UPDATE and DELETE statements
+# using those triggers. Verify correct operation.
+#
+do_test triggerA-2.1 {
+ db eval {
+ CREATE TABLE result2(a,b);
+ CREATE TRIGGER r1d INSTEAD OF DELETE ON v1 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.y, old.x);
+ END;
+ DELETE FROM v1 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {five 5}
+do_test triggerA-2.2 {
+ db eval {
+ CREATE TABLE result4(a,b,c,d);
+ CREATE TRIGGER r1u INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
+ END;
+ UPDATE v1 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {five 5 five-extra 5 four 4 four-extra 4 three 3 three-extra 3}
+
+
+do_test triggerA-2.3 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r2d INSTEAD OF DELETE ON v2 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.y, old.x);
+ END;
+ DELETE FROM v2 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {five 5}
+do_test triggerA-2.4 {
+ db eval {
+ DELETE FROM result4;
+ CREATE TRIGGER r2u INSTEAD OF UPDATE ON v2 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
+ END;
+ UPDATE v2 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {five 5 five-extra 5 three 3 three-extra 3}
+
+
+do_test triggerA-2.5 {
+ db eval {
+ CREATE TABLE result1(a);
+ CREATE TRIGGER r3d INSTEAD OF DELETE ON v3 BEGIN
+ INSERT INTO result1(a) VALUES(old.c1);
+ END;
+ DELETE FROM v3 WHERE c1 BETWEEN '8' AND 'eight';
+ SELECT * FROM result1 ORDER BY a;
+ }
+} {8 9 eight}
+do_test triggerA-2.6 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r3u INSTEAD OF UPDATE ON v3 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
+ END;
+ UPDATE v3 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
+ SELECT * FROM result2 ORDER BY a;
+ }
+} {8 8-extra 9 9-extra eight eight-extra}
+
+
+do_test triggerA-2.7 {
+ db eval {
+ DELETE FROM result1;
+ CREATE TRIGGER r4d INSTEAD OF DELETE ON v4 BEGIN
+ INSERT INTO result1(a) VALUES(old.c1);
+ END;
+ DELETE FROM v4 WHERE c1 BETWEEN '8' AND 'eight';
+ SELECT * FROM result1 ORDER BY a;
+ }
+} {8 9}
+do_test triggerA-2.8 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r4u INSTEAD OF UPDATE ON v4 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
+ END;
+ UPDATE v4 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
+ SELECT * FROM result2 ORDER BY a;
+ }
+} {8 8-extra 9 9-extra}
+
+
+do_test triggerA-2.9 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r5d INSTEAD OF DELETE ON v5 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.x, old.b);
+ END;
+ DELETE FROM v5 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {5 504}
+do_test triggerA-2.10 {
+ db eval {
+ DELETE FROM result4;
+ CREATE TRIGGER r5u INSTEAD OF UPDATE ON v5 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.x, old.b, new.x, new.b);
+ END;
+ UPDATE v5 SET b = b+9900000 WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504}
+
+# Only run the reamining tests if memory debugging is turned on.
+#
+ifcapable !memdebug {
+ puts "Skipping triggerA malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+source $testdir/malloc_common.tcl
+
+# Save a copy of the current database configuration.
+#
+db close
+file delete -force test.db-triggerA
+file copy test.db test.db-triggerA
+sqlite3 db test.db
+
+# Run malloc tests on the INSTEAD OF trigger firing.
+#
+do_malloc_test triggerA-3 -tclprep {
+ db close
+ file delete -force test.db test.db-journal
+ file copy -force test.db-triggerA test.db
+ sqlite3 db test.db
+ sqlite3_extended_result_codes db 1
+ db eval {SELECT * FROM v5; -- warm up the cache}
+} -sqlbody {
+ DELETE FROM v5 WHERE x=5;
+ UPDATE v5 SET b=b+9900000 WHERE x BETWEEN 3 AND 5;
+}
+
+# Clean up the saved database copy.
+#
+file delete -force test.db-triggerA
+
+finish_test
diff --git a/third_party/sqlite/test/triggerB.test b/third_party/sqlite/test/triggerB.test
new file mode 100755
index 0000000..4ba7b53
--- /dev/null
+++ b/third_party/sqlite/test/triggerB.test
@@ -0,0 +1,44 @@
+# 2008 April 15
+#
+# 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. Specifically,
+# it tests updating tables with constraints within a trigger. Ticket #3055.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# Create test tables with constraints.
+#
+do_test triggerB-1.1 {
+ execsql {
+ CREATE TABLE x(x INTEGER PRIMARY KEY, y INT NOT NULL);
+ INSERT INTO x(y) VALUES(1);
+ INSERT INTO x(y) VALUES(1);
+ CREATE TEMP VIEW vx AS SELECT x, y, 0 AS yy FROM x;
+ CREATE TEMP TRIGGER tx INSTEAD OF UPDATE OF y ON vx
+ BEGIN
+ UPDATE x SET y = new.y WHERE x = new.x;
+ END;
+ SELECT * FROM vx;
+ }
+} {1 1 0 2 1 0}
+do_test triggerB-1.2 {
+ execsql {
+ UPDATE vx SET y = yy;
+ SELECT * FROM vx;
+ }
+} {1 0 0 2 0 0}
+
+finish_test
diff --git a/third_party/sqlite/test/types.test b/third_party/sqlite/test/types.test
new file mode 100755
index 0000000..6ebaeb8
--- /dev/null
+++ b/third_party/sqlite/test/types.test
@@ -0,0 +1,324 @@
+# 2001 September 15
+#
+# 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. Specfically
+# it tests that the different storage classes (integer, real, text etc.)
+# all work correctly.
+#
+# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Tests in this file are organized roughly as follows:
+#
+# types-1.*.*: Test that values are stored using the expected storage
+# classes when various forms of literals are inserted into
+# columns with different affinities.
+# types-1.1.*: INSERT INTO <table> VALUES(...)
+# types-1.2.*: INSERT INTO <table> SELECT...
+# types-1.3.*: UPDATE <table> SET...
+#
+# types-2.*.*: Check that values can be stored and retrieving using the
+# various storage classes.
+# types-2.1.*: INTEGER
+# types-2.2.*: REAL
+# types-2.3.*: NULL
+# types-2.4.*: TEXT
+# types-2.5.*: Records with a few different storage classes.
+#
+# types-3.*: Test that the '=' operator respects manifest types.
+#
+
+# Disable encryption on the database for this test.
+db close
+set DB [sqlite3 db test.db; sqlite3_connection_pointer db]
+sqlite3_rekey $DB {}
+
+# Create a table with one column for each type of affinity
+do_test types-1.1.0 {
+ execsql {
+ CREATE TABLE t1(i integer, n numeric, t text, o blob);
+ }
+} {}
+
+# Each element of the following list represents one test case.
+#
+# The first value of each sub-list is an SQL literal. The following
+# four value are the storage classes that would be used if the
+# literal were inserted into a column with affinity INTEGER, NUMERIC, TEXT
+# or NONE, respectively.
+set values {
+ { 5.0 integer integer text real }
+ { 5.1 real real text real }
+ { 5 integer integer text integer }
+ { '5.0' integer integer text text }
+ { '5.1' real real text text }
+ { '-5.0' integer integer text text }
+ { '-5.0' integer integer text text }
+ { '5' integer integer text text }
+ { 'abc' text text text text }
+ { NULL null null null null }
+}
+ifcapable {bloblit} {
+ lappend values { X'00' blob blob blob blob }
+}
+
+# This code tests that the storage classes specified above (in the $values
+# table) are correctly assigned when values are inserted using a statement
+# of the form:
+#
+# INSERT INTO <table> VALUE(<values>);
+#
+set tnum 1
+foreach val $values {
+ set lit [lindex $val 0]
+ execsql "DELETE FROM t1;"
+ execsql "INSERT INTO t1 VALUES($lit, $lit, $lit, $lit);"
+ do_test types-1.1.$tnum {
+ execsql {
+ SELECT typeof(i), typeof(n), typeof(t), typeof(o) FROM t1;
+ }
+ } [lrange $val 1 end]
+ incr tnum
+}
+
+# This code tests that the storage classes specified above (in the $values
+# table) are correctly assigned when values are inserted using a statement
+# of the form:
+#
+# INSERT INTO t1 SELECT ....
+#
+set tnum 1
+foreach val $values {
+ set lit [lindex $val 0]
+ execsql "DELETE FROM t1;"
+ execsql "INSERT INTO t1 SELECT $lit, $lit, $lit, $lit;"
+ do_test types-1.2.$tnum {
+ execsql {
+ SELECT typeof(i), typeof(n), typeof(t), typeof(o) FROM t1;
+ }
+ } [lrange $val 1 end]
+ incr tnum
+}
+
+# This code tests that the storage classes specified above (in the $values
+# table) are correctly assigned when values are inserted using a statement
+# of the form:
+#
+# UPDATE <table> SET <column> = <value>;
+#
+set tnum 1
+foreach val $values {
+ set lit [lindex $val 0]
+ execsql "UPDATE t1 SET i = $lit, n = $lit, t = $lit, o = $lit;"
+ do_test types-1.3.$tnum {
+ execsql {
+ SELECT typeof(i), typeof(n), typeof(t), typeof(o) FROM t1;
+ }
+ } [lrange $val 1 end]
+ incr tnum
+}
+
+execsql {
+ DROP TABLE t1;
+}
+
+# Open the table with root-page $rootpage at the btree
+# level. Return a list that is the length of each record
+# in the table, in the tables default scanning order.
+proc record_sizes {rootpage} {
+ set bt [btree_open test.db 10 0]
+ set c [btree_cursor $bt $rootpage 0]
+ btree_first $c
+ while 1 {
+ lappend res [btree_payload_size $c]
+ if {[btree_next $c]} break
+ }
+ btree_close_cursor $c
+ btree_close $bt
+ set res
+}
+
+
+# Create a table and insert some 1-byte integers. Make sure they
+# can be read back OK. These should be 3 byte records.
+do_test types-2.1.1 {
+ execsql {
+ CREATE TABLE t1(a integer);
+ INSERT INTO t1 VALUES(0);
+ INSERT INTO t1 VALUES(120);
+ INSERT INTO t1 VALUES(-120);
+ }
+} {}
+do_test types-2.1.2 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} {0 120 -120}
+
+# Try some 2-byte integers (4 byte records)
+do_test types-2.1.3 {
+ execsql {
+ INSERT INTO t1 VALUES(30000);
+ INSERT INTO t1 VALUES(-30000);
+ }
+} {}
+do_test types-2.1.4 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} {0 120 -120 30000 -30000}
+
+# 4-byte integers (6 byte records)
+do_test types-2.1.5 {
+ execsql {
+ INSERT INTO t1 VALUES(2100000000);
+ INSERT INTO t1 VALUES(-2100000000);
+ }
+} {}
+do_test types-2.1.6 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} {0 120 -120 30000 -30000 2100000000 -2100000000}
+
+# 8-byte integers (10 byte records)
+do_test types-2.1.7 {
+ execsql {
+ INSERT INTO t1 VALUES(9000000*1000000*1000000);
+ INSERT INTO t1 VALUES(-9000000*1000000*1000000);
+ }
+} {}
+do_test types-2.1.8 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} [list 0 120 -120 30000 -30000 2100000000 -2100000000 \
+ 9000000000000000000 -9000000000000000000]
+
+# Check that all the record sizes are as we expected.
+ifcapable legacyformat {
+ do_test types-2.1.9 {
+ set root [db eval {select rootpage from sqlite_master where name = 't1'}]
+ record_sizes $root
+ } {3 3 3 4 4 6 6 10 10}
+} else {
+ do_test types-2.1.9 {
+ set root [db eval {select rootpage from sqlite_master where name = 't1'}]
+ record_sizes $root
+ } {2 3 3 4 4 6 6 10 10}
+}
+
+# Insert some reals. These should be 10 byte records.
+do_test types-2.2.1 {
+ execsql {
+ CREATE TABLE t2(a float);
+ INSERT INTO t2 VALUES(0.0);
+ INSERT INTO t2 VALUES(12345.678);
+ INSERT INTO t2 VALUES(-12345.678);
+ }
+} {}
+do_test types-2.2.2 {
+ execsql {
+ SELECT a FROM t2;
+ }
+} {0.0 12345.678 -12345.678}
+
+# Check that all the record sizes are as we expected.
+ifcapable legacyformat {
+ do_test types-2.2.3 {
+ set root [db eval {select rootpage from sqlite_master where name = 't2'}]
+ record_sizes $root
+ } {3 10 10}
+} else {
+ do_test types-2.2.3 {
+ set root [db eval {select rootpage from sqlite_master where name = 't2'}]
+ record_sizes $root
+ } {2 10 10}
+}
+
+# Insert a NULL. This should be a two byte record.
+do_test types-2.3.1 {
+ execsql {
+ CREATE TABLE t3(a nullvalue);
+ INSERT INTO t3 VALUES(NULL);
+ }
+} {}
+do_test types-2.3.2 {
+ execsql {
+ SELECT a ISNULL FROM t3;
+ }
+} {1}
+
+# Check that all the record sizes are as we expected.
+do_test types-2.3.3 {
+ set root [db eval {select rootpage from sqlite_master where name = 't3'}]
+ record_sizes $root
+} {2}
+
+# Insert a couple of strings.
+do_test types-2.4.1 {
+ set string10 abcdefghij
+ set string500 [string repeat $string10 50]
+ set string500000 [string repeat $string10 50000]
+
+ execsql "
+ CREATE TABLE t4(a string);
+ INSERT INTO t4 VALUES('$string10');
+ INSERT INTO t4 VALUES('$string500');
+ INSERT INTO t4 VALUES('$string500000');
+ "
+} {}
+do_test types-2.4.2 {
+ execsql {
+ SELECT a FROM t4;
+ }
+} [list $string10 $string500 $string500000]
+
+# Check that all the record sizes are as we expected. This is dependant on
+# the database encoding.
+if { $sqlite_options(utf16)==0 || [execsql {pragma encoding}] == "UTF-8" } {
+ do_test types-2.4.3 {
+ set root [db eval {select rootpage from sqlite_master where name = 't4'}]
+ record_sizes $root
+ } {12 503 500004}
+} else {
+ do_test types-2.4.3 {
+ set root [db eval {select rootpage from sqlite_master where name = 't4'}]
+ record_sizes $root
+ } {22 1003 1000004}
+}
+
+do_test types-2.5.1 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP TABLE t3;
+ DROP TABLE t4;
+ CREATE TABLE t1(a, b, c);
+ }
+} {}
+do_test types-2.5.2 {
+ set string10 abcdefghij
+ set string500 [string repeat $string10 50]
+ set string500000 [string repeat $string10 50000]
+
+ execsql "INSERT INTO t1 VALUES(NULL, '$string10', 4000);"
+ execsql "INSERT INTO t1 VALUES('$string500', 4000, NULL);"
+ execsql "INSERT INTO t1 VALUES(4000, NULL, '$string500000');"
+} {}
+do_test types-2.5.3 {
+ execsql {
+ SELECT * FROM t1;
+ }
+} [list {} $string10 4000 $string500 4000 {} 4000 {} $string500000]
+
+finish_test
diff --git a/third_party/sqlite/test/types2.test b/third_party/sqlite/test/types2.test
new file mode 100755
index 0000000..4a70aa5
--- /dev/null
+++ b/third_party/sqlite/test/types2.test
@@ -0,0 +1,340 @@
+# 2001 September 15
+#
+# 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 file is testing the interaction of manifest types, type affinity
+# and comparison expressions.
+#
+# $Id: types2.test,v 1.7 2007/02/23 03:00:45 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Tests in this file are organized roughly as follows:
+#
+# types2-1.*: The '=' operator in the absence of an index.
+# types2-2.*: The '=' operator implemented using an index.
+# types2-3.*: The '<' operator implemented using an index.
+# types2-4.*: The '>' operator in the absence of an index.
+# types2-5.*: The 'IN(x, y...)' operator in the absence of an index.
+# types2-6.*: The 'IN(x, y...)' operator with an index.
+# types2-7.*: The 'IN(SELECT...)' operator in the absence of an index.
+# types2-8.*: The 'IN(SELECT...)' operator with an index.
+#
+# All tests test the operators using literals and columns, but no
+# other types of expressions. All expressions except columns are
+# handled similarly in the implementation.
+
+execsql {
+ CREATE TABLE t1(
+ i1 INTEGER,
+ i2 INTEGER,
+ n1 NUMERIC,
+ n2 NUMERIC,
+ t1 TEXT,
+ t2 TEXT,
+ o1 BLOB,
+ o2 BLOB
+ );
+ INSERT INTO t1 VALUES(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+}
+
+proc test_bool {testname vars expr res} {
+ if { $vars != "" } {
+ execsql "UPDATE t1 SET $vars"
+ }
+
+ foreach {t e r} [list $testname $expr $res] {}
+
+ do_test $t.1 "execsql {SELECT $e FROM t1}" $r
+ do_test $t.2 "execsql {SELECT 1 FROM t1 WHERE $expr}" [expr $r?"1":""]
+ do_test $t.3 "execsql {SELECT 1 FROM t1 WHERE NOT ($e)}" [expr $r?"":"1"]
+}
+
+# Compare literals against literals. This should always use a numeric
+# comparison.
+#
+# Changed by ticket #805: Use no affinity for literal comparisons.
+#
+test_bool types2-1.1 "" {500 = 500.0} 1
+test_bool types2-1.2 "" {'500' = 500.0} 0
+test_bool types2-1.3 "" {500 = '500.0'} 0
+test_bool types2-1.4 "" {'500' = '500.0'} 0
+
+# Compare literals against a column with TEXT affinity
+test_bool types2-1.5 {t1=500} {500 = t1} 1
+test_bool types2-1.6 {t1=500} {'500' = t1} 1
+test_bool types2-1.7 {t1=500} {500.0 = t1} 0
+test_bool types2-1.8 {t1=500} {'500.0' = t1} 0
+test_bool types2-1.9 {t1='500'} {500 = t1} 1
+test_bool types2-1.10 {t1='500'} {'500' = t1} 1
+test_bool types2-1.11 {t1='500'} {500.0 = t1} 0
+test_bool types2-1.12 {t1='500'} {'500.0' = t1} 0
+
+# Compare literals against a column with NUMERIC affinity
+test_bool types2-1.13 {n1=500} {500 = n1} 1
+test_bool types2-1.14 {n1=500} {'500' = n1} 1
+test_bool types2-1.15 {n1=500} {500.0 = n1} 1
+test_bool types2-1.16 {n1=500} {'500.0' = n1} 1
+test_bool types2-1.17 {n1='500'} {500 = n1} 1
+test_bool types2-1.18 {n1='500'} {'500' = n1} 1
+test_bool types2-1.19 {n1='500'} {500.0 = n1} 1
+test_bool types2-1.20 {n1='500'} {'500.0' = n1} 1
+
+# Compare literals against a column with affinity NONE
+test_bool types2-1.21 {o1=500} {500 = o1} 1
+test_bool types2-1.22 {o1=500} {'500' = o1} 0
+test_bool types2-1.23 {o1=500} {500.0 = o1} 1
+test_bool types2-1.24 {o1=500} {'500.0' = o1} 0
+test_bool types2-1.25 {o1='500'} {500 = o1} 0
+test_bool types2-1.26 {o1='500'} {'500' = o1} 1
+test_bool types2-1.27 {o1='500'} {500.0 = o1} 0
+test_bool types2-1.28 {o1='500'} {'500.0' = o1} 0
+
+set vals [list 10 10.0 '10' '10.0' 20 20.0 '20' '20.0' 30 30.0 '30' '30.0']
+# 1 2 3 4 5 6 7 8 9 10 11 12
+
+execsql {
+ CREATE TABLE t2(i INTEGER, n NUMERIC, t TEXT, o XBLOBY);
+ CREATE INDEX t2i1 ON t2(i);
+ CREATE INDEX t2i2 ON t2(n);
+ CREATE INDEX t2i3 ON t2(t);
+ CREATE INDEX t2i4 ON t2(o);
+}
+foreach v $vals {
+ execsql "INSERT INTO t2 VALUES($v, $v, $v, $v);"
+}
+
+proc test_boolset {testname where set} {
+ set ::tb_sql "SELECT rowid FROM t2 WHERE $where"
+ do_test $testname {
+ lsort -integer [execsql $::tb_sql]
+ } $set
+}
+
+test_boolset types2-2.1 {i = 10} {1 2 3 4}
+test_boolset types2-2.2 {i = 10.0} {1 2 3 4}
+test_boolset types2-2.3 {i = '10'} {1 2 3 4}
+test_boolset types2-2.4 {i = '10.0'} {1 2 3 4}
+
+test_boolset types2-2.5 {n = 20} {5 6 7 8}
+test_boolset types2-2.6 {n = 20.0} {5 6 7 8}
+test_boolset types2-2.7 {n = '20'} {5 6 7 8}
+test_boolset types2-2.8 {n = '20.0'} {5 6 7 8}
+
+test_boolset types2-2.9 {t = 20} {5 7}
+test_boolset types2-2.10 {t = 20.0} {6 8}
+test_boolset types2-2.11 {t = '20'} {5 7}
+test_boolset types2-2.12 {t = '20.0'} {6 8}
+
+test_boolset types2-2.10 {o = 30} {9 10}
+test_boolset types2-2.11 {o = 30.0} {9 10}
+test_boolset types2-2.12 {o = '30'} 11
+test_boolset types2-2.13 {o = '30.0'} 12
+
+test_boolset types2-3.1 {i < 20} {1 2 3 4}
+test_boolset types2-3.2 {i < 20.0} {1 2 3 4}
+test_boolset types2-3.3 {i < '20'} {1 2 3 4}
+test_boolset types2-3.4 {i < '20.0'} {1 2 3 4}
+
+test_boolset types2-3.1 {n < 20} {1 2 3 4}
+test_boolset types2-3.2 {n < 20.0} {1 2 3 4}
+test_boolset types2-3.3 {n < '20'} {1 2 3 4}
+test_boolset types2-3.4 {n < '20.0'} {1 2 3 4}
+
+test_boolset types2-3.1 {t < 20} {1 2 3 4}
+test_boolset types2-3.2 {t < 20.0} {1 2 3 4 5 7}
+test_boolset types2-3.3 {t < '20'} {1 2 3 4}
+test_boolset types2-3.4 {t < '20.0'} {1 2 3 4 5 7}
+
+test_boolset types2-3.1 {o < 20} {1 2}
+test_boolset types2-3.2 {o < 20.0} {1 2}
+test_boolset types2-3.3 {o < '20'} {1 2 3 4 5 6 9 10}
+test_boolset types2-3.3 {o < '20.0'} {1 2 3 4 5 6 7 9 10}
+
+# Compare literals against literals (always a numeric comparison).
+# Change (by ticket #805): No affinity in comparisons
+test_bool types2-4.1 "" {500 > 60.0} 1
+test_bool types2-4.2 "" {'500' > 60.0} 1
+test_bool types2-4.3 "" {500 > '60.0'} 0
+test_bool types2-4.4 "" {'500' > '60.0'} 0
+
+# Compare literals against a column with TEXT affinity
+test_bool types2-4.5 {t1=500.0} {t1 > 500} 1
+test_bool types2-4.6 {t1=500.0} {t1 > '500' } 1
+test_bool types2-4.7 {t1=500.0} {t1 > 500.0 } 0
+test_bool types2-4.8 {t1=500.0} {t1 > '500.0' } 0
+test_bool types2-4.9 {t1='500.0'} {t1 > 500 } 1
+test_bool types2-4.10 {t1='500.0'} {t1 > '500' } 1
+test_bool types2-4.11 {t1='500.0'} {t1 > 500.0 } 0
+test_bool types2-4.12 {t1='500.0'} {t1 > '500.0' } 0
+
+# Compare literals against a column with NUMERIC affinity
+test_bool types2-4.13 {n1=400} {500 > n1} 1
+test_bool types2-4.14 {n1=400} {'500' > n1} 1
+test_bool types2-4.15 {n1=400} {500.0 > n1} 1
+test_bool types2-4.16 {n1=400} {'500.0' > n1} 1
+test_bool types2-4.17 {n1='400'} {500 > n1} 1
+test_bool types2-4.18 {n1='400'} {'500' > n1} 1
+test_bool types2-4.19 {n1='400'} {500.0 > n1} 1
+test_bool types2-4.20 {n1='400'} {'500.0' > n1} 1
+
+# Compare literals against a column with affinity NONE
+test_bool types2-4.21 {o1=500} {500 > o1} 0
+test_bool types2-4.22 {o1=500} {'500' > o1} 1
+test_bool types2-4.23 {o1=500} {500.0 > o1} 0
+test_bool types2-4.24 {o1=500} {'500.0' > o1} 1
+test_bool types2-4.25 {o1='500'} {500 > o1} 0
+test_bool types2-4.26 {o1='500'} {'500' > o1} 0
+test_bool types2-4.27 {o1='500'} {500.0 > o1} 0
+test_bool types2-4.28 {o1='500'} {'500.0' > o1} 1
+
+ifcapable subquery {
+ # types2-5.* - The 'IN (x, y....)' operator with no index.
+ #
+ # Compare literals against literals (no affinity applied)
+ test_bool types2-5.1 {} {(NULL IN ('10.0', 20)) ISNULL} 1
+ test_bool types2-5.2 {} {10 IN ('10.0', 20)} 0
+ test_bool types2-5.3 {} {'10' IN ('10.0', 20)} 0
+ test_bool types2-5.4 {} {10 IN (10.0, 20)} 1
+ test_bool types2-5.5 {} {'10.0' IN (10, 20)} 0
+
+ # Compare literals against a column with TEXT affinity
+ test_bool types2-5.6 {t1='10.0'} {t1 IN (10.0, 20)} 1
+ test_bool types2-5.7 {t1='10.0'} {t1 IN (10, 20)} 0
+ test_bool types2-5.8 {t1='10'} {t1 IN (10.0, 20)} 0
+ test_bool types2-5.9 {t1='10'} {t1 IN (20, '10.0')} 0
+ test_bool types2-5.10 {t1=10} {t1 IN (20, '10')} 1
+
+ # Compare literals against a column with NUMERIC affinity
+ test_bool types2-5.11 {n1='10.0'} {n1 IN (10.0, 20)} 1
+ test_bool types2-5.12 {n1='10.0'} {n1 IN (10, 20)} 1
+ test_bool types2-5.13 {n1='10'} {n1 IN (10.0, 20)} 1
+ test_bool types2-5.14 {n1='10'} {n1 IN (20, '10.0')} 1
+ test_bool types2-5.15 {n1=10} {n1 IN (20, '10')} 1
+
+ # Compare literals against a column with affinity NONE
+ test_bool types2-5.16 {o1='10.0'} {o1 IN (10.0, 20)} 0
+ test_bool types2-5.17 {o1='10.0'} {o1 IN (10, 20)} 0
+ test_bool types2-5.18 {o1='10'} {o1 IN (10.0, 20)} 0
+ test_bool types2-5.19 {o1='10'} {o1 IN (20, '10.0')} 0
+ test_bool types2-5.20 {o1=10} {o1 IN (20, '10')} 0
+ test_bool types2-5.21 {o1='10.0'} {o1 IN (10, 20, '10.0')} 1
+ test_bool types2-5.22 {o1='10'} {o1 IN (10.0, 20, '10')} 1
+ test_bool types2-5.23 {o1=10} {n1 IN (20, '10', 10)} 1
+
+ # Ticket #2248: Comparisons of strings literals that look like
+ # numbers.
+ test_bool types2-5.24 {} {'1' IN ('1')} 1
+ test_bool types2-5.25 {} {'2' IN (2)} 0
+ test_bool types2-5.26 {} {3 IN ('3')} 0
+ test_bool types2-5.27 {} {4 IN (4)} 1
+
+ # The affinity of columns on the right side of IN(...) is ignored.
+ # All values in the expression list are treated as ordinary expressions,
+ # even if they are columns with affinity.
+ test_bool types2-5.30 {t1='10'} {10 IN (5,t1,'abc')} 0
+ test_bool types2-5.31 {t1='10'} {10 IN ('abc',t1,5)} 0
+ test_bool types2-5.32 {t1='010'} {10 IN (5,t1,'abc')} 0
+ test_bool types2-5.33 {t1='010'} {10 IN ('abc',t1,5)} 0
+ test_bool types2-5.34 {t1='10'} {'10' IN (5,t1,'abc')} 1
+ test_bool types2-5.35 {t1='10'} {'10' IN ('abc',t1,5)} 1
+ test_bool types2-5.36 {t1='010'} {'10' IN (5,t1,'abc')} 0
+ test_bool types2-5.37 {t1='010'} {'10' IN ('abc',t1,5)} 0
+
+ # Columns on both the left and right of IN(...). Only the column
+ # on the left matters. The all values on the right are treated like
+ # expressions.
+ test_bool types2-5.40 {t1='10',n1=10} {t1 IN (5,n1,11)} 1
+ test_bool types2-5.41 {t1='010',n1=10} {t1 IN (5,n1,11)} 0
+ test_bool types2-5.42 {t1='10',n1=10} {n1 IN (5,t1,11)} 1
+ test_bool types2-5.43 {t1='010',n1=10} {n1 IN (5,t1,11)} 1
+}
+
+# Tests named types2-6.* use the same infrastructure as the types2-2.*
+# tests. The contents of the vals array is repeated here for easy
+# reference.
+#
+# set vals [list 10 10.0 '10' '10.0' 20 20.0 '20' '20.0' 30 30.0 '30' '30.0']
+# 1 2 3 4 5 6 7 8 9 10 11 12
+
+ifcapable subquery {
+ test_boolset types2-6.1 {o IN ('10', 30)} {3 9 10}
+ test_boolset types2-6.2 {o IN (20.0, 30.0)} {5 6 9 10}
+ test_boolset types2-6.3 {t IN ('10', 30)} {1 3 9 11}
+ test_boolset types2-6.4 {t IN (20.0, 30.0)} {6 8 10 12}
+ test_boolset types2-6.5 {n IN ('10', 30)} {1 2 3 4 9 10 11 12}
+ test_boolset types2-6.6 {n IN (20.0, 30.0)} {5 6 7 8 9 10 11 12}
+ test_boolset types2-6.7 {i IN ('10', 30)} {1 2 3 4 9 10 11 12}
+ test_boolset types2-6.8 {i IN (20.0, 30.0)} {5 6 7 8 9 10 11 12}
+
+ # Also test than IN(x, y, z) works on a rowid:
+ test_boolset types2-6.9 {rowid IN (1, 6, 10)} {1 6 10}
+}
+
+# Tests types2-7.* concentrate on expressions of the form
+# "x IN (SELECT...)" with no index.
+execsql {
+ CREATE TABLE t3(i INTEGER, n NUMERIC, t TEXT, o BLOB);
+ INSERT INTO t3 VALUES(1, 1, 1, 1);
+ INSERT INTO t3 VALUES(2, 2, 2, 2);
+ INSERT INTO t3 VALUES(3, 3, 3, 3);
+ INSERT INTO t3 VALUES('1', '1', '1', '1');
+ INSERT INTO t3 VALUES('1.0', '1.0', '1.0', '1.0');
+}
+
+ifcapable subquery {
+ test_bool types2-7.1 {i1=1} {i1 IN (SELECT i FROM t3)} 1
+ test_bool types2-7.2 {i1='2.0'} {i1 IN (SELECT i FROM t3)} 1
+ test_bool types2-7.3 {i1='2.0'} {i1 IN (SELECT n FROM t3)} 1
+ test_bool types2-7.4 {i1='2.0'} {i1 IN (SELECT t FROM t3)} 1
+ test_bool types2-7.5 {i1='2.0'} {i1 IN (SELECT o FROM t3)} 1
+
+ test_bool types2-7.6 {n1=1} {n1 IN (SELECT n FROM t3)} 1
+ test_bool types2-7.7 {n1='2.0'} {n1 IN (SELECT i FROM t3)} 1
+ test_bool types2-7.8 {n1='2.0'} {n1 IN (SELECT n FROM t3)} 1
+ test_bool types2-7.9 {n1='2.0'} {n1 IN (SELECT t FROM t3)} 1
+ test_bool types2-7.10 {n1='2.0'} {n1 IN (SELECT o FROM t3)} 1
+
+ test_bool types2-7.6 {t1=1} {t1 IN (SELECT t FROM t3)} 1
+ test_bool types2-7.7 {t1='2.0'} {t1 IN (SELECT t FROM t3)} 0
+ test_bool types2-7.8 {t1='2.0'} {t1 IN (SELECT n FROM t3)} 1
+ test_bool types2-7.9 {t1='2.0'} {t1 IN (SELECT i FROM t3)} 1
+ test_bool types2-7.10 {t1='2.0'} {t1 IN (SELECT o FROM t3)} 0
+ test_bool types2-7.11 {t1='1.0'} {t1 IN (SELECT t FROM t3)} 1
+ test_bool types2-7.12 {t1='1.0'} {t1 IN (SELECT o FROM t3)} 1
+
+ test_bool types2-7.13 {o1=2} {o1 IN (SELECT o FROM t3)} 1
+ test_bool types2-7.14 {o1='2'} {o1 IN (SELECT o FROM t3)} 0
+ test_bool types2-7.15 {o1='2'} {o1 IN (SELECT o||'' FROM t3)} 1
+}
+
+# set vals [list 10 10.0 '10' '10.0' 20 20.0 '20' '20.0' 30 30.0 '30' '30.0']
+# 1 2 3 4 5 6 7 8 9 10 11 12
+execsql {
+ CREATE TABLE t4(i INTEGER, n NUMERIC, t VARCHAR(20), o LARGE BLOB);
+ INSERT INTO t4 VALUES(10, 20, 20, 30);
+}
+ifcapable subquery {
+ test_boolset types2-8.1 {i IN (SELECT i FROM t4)} {1 2 3 4}
+ test_boolset types2-8.2 {n IN (SELECT i FROM t4)} {1 2 3 4}
+ test_boolset types2-8.3 {t IN (SELECT i FROM t4)} {1 2 3 4}
+ test_boolset types2-8.4 {o IN (SELECT i FROM t4)} {1 2 3 4}
+ test_boolset types2-8.5 {i IN (SELECT t FROM t4)} {5 6 7 8}
+ test_boolset types2-8.6 {n IN (SELECT t FROM t4)} {5 6 7 8}
+ test_boolset types2-8.7 {t IN (SELECT t FROM t4)} {5 7}
+ test_boolset types2-8.8 {o IN (SELECT t FROM t4)} {7}
+ test_boolset types2-8.9 {i IN (SELECT o FROM t4)} {9 10 11 12}
+ test_boolset types2-8.6 {n IN (SELECT o FROM t4)} {9 10 11 12}
+ test_boolset types2-8.7 {t IN (SELECT o FROM t4)} {9 11}
+ test_boolset types2-8.8 {o IN (SELECT o FROM t4)} {9 10}
+}
+
+finish_test
diff --git a/third_party/sqlite/test/types3.test b/third_party/sqlite/test/types3.test
new file mode 100755
index 0000000..33f2595
--- /dev/null
+++ b/third_party/sqlite/test/types3.test
@@ -0,0 +1,99 @@
+# 2005 June 25
+#
+# 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 file is testing the interaction of SQLite manifest types
+# with Tcl dual-representations.
+#
+# $Id: types3.test,v 1.8 2008/04/28 13:02:58 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# A variable with only a string representation comes in as TEXT
+do_test types3-1.1 {
+ set V {}
+ append V {}
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+} {string text}
+
+# A variable with an integer representation comes in as INTEGER
+do_test types3-1.2 {
+ set V [expr {int(1+2)}]
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+} {int integer}
+set V [expr {1+12345678012345}]
+if {[tcl_variable_type V]=="wideInt"} {
+ do_test types3-1.3 {
+ set V [expr {1+123456789012345}]
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+ } {wideInt integer}
+} else {
+ do_test types3-1.3 {
+ set V [expr {1+123456789012345}]
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+ } {int integer}
+}
+
+# A double variable comes in as REAL
+do_test types3-1.4 {
+ set V [expr {1.0+1}]
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+} {double real}
+
+# A byte-array variable comes in a BLOB if it has no string representation
+# or as TEXT if there is a string representation.
+#
+do_test types3-1.5 {
+ set V [binary format a3 abc]
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+} {bytearray blob}
+do_test types3-1.6 {
+ set V "abc"
+ binary scan $V a3 x
+ concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
+} {bytearray text}
+
+# Check to make sure return values are of the right types.
+#
+ifcapable bloblit {
+ do_test types3-2.1 {
+ set V [db one {SELECT x'616263'}]
+ tcl_variable_type V
+ } bytearray
+}
+do_test types3-2.2 {
+ set V [db one {SELECT 123}]
+ tcl_variable_type V
+} int
+set Vx [expr {1+wide(123456789123456)}]
+do_test types3-2.3 {
+ set V [db one {SELECT 1234567890123456}]
+ tcl_variable_type V
+} [tcl_variable_type Vx]
+do_test types3-2.4.1 {
+ set V [db one {SELECT 1234567890123456.1}]
+ tcl_variable_type V
+} double
+do_test types3-2.4.2 {
+ set V [db one {SELECT 1234567890123.456}]
+ tcl_variable_type V
+} double
+do_test types3-2.5 {
+ set V [db one {SELECT '1234567890123456.0'}]
+ tcl_variable_type V
+} {}
+do_test types3-2.6 {
+ set V [db one {SELECT NULL}]
+ tcl_variable_type V
+} {}
+
+finish_test
diff --git a/third_party/sqlite/test/unique.test b/third_party/sqlite/test/unique.test
new file mode 100755
index 0000000..7bdc363
--- /dev/null
+++ b/third_party/sqlite/test/unique.test
@@ -0,0 +1,253 @@
+# 2001 September 27
+#
+# 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 file is testing the CREATE UNIQUE INDEX statement,
+# and primary keys, and the UNIQUE constraint on table columns
+#
+# $Id: unique.test,v 1.8 2005/06/24 03:53:06 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Try to create a table with two primary keys.
+# (This is allowed in SQLite even that it is not valid SQL)
+#
+do_test unique-1.1 {
+ catchsql {
+ CREATE TABLE t1(
+ a int PRIMARY KEY,
+ b int PRIMARY KEY,
+ c text
+ );
+ }
+} {1 {table "t1" has more than one primary key}}
+do_test unique-1.1b {
+ catchsql {
+ CREATE TABLE t1(
+ a int PRIMARY KEY,
+ b int UNIQUE,
+ c text
+ );
+ }
+} {0 {}}
+do_test unique-1.2 {
+ catchsql {
+ INSERT INTO t1(a,b,c) VALUES(1,2,3)
+ }
+} {0 {}}
+do_test unique-1.3 {
+ catchsql {
+ INSERT INTO t1(a,b,c) VALUES(1,3,4)
+ }
+} {1 {column a is not unique}}
+do_test unique-1.4 {
+ execsql {
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 2 3}
+do_test unique-1.5 {
+ catchsql {
+ INSERT INTO t1(a,b,c) VALUES(3,2,4)
+ }
+} {1 {column b is not unique}}
+do_test unique-1.6 {
+ execsql {
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 2 3}
+do_test unique-1.7 {
+ catchsql {
+ INSERT INTO t1(a,b,c) VALUES(3,4,5)
+ }
+} {0 {}}
+do_test unique-1.8 {
+ execsql {
+ SELECT * FROM t1 ORDER BY a;
+ }
+} {1 2 3 3 4 5}
+integrity_check unique-1.9
+
+do_test unique-2.0 {
+ execsql {
+ DROP TABLE t1;
+ CREATE TABLE t2(a int, b int);
+ INSERT INTO t2(a,b) VALUES(1,2);
+ INSERT INTO t2(a,b) VALUES(3,4);
+ SELECT * FROM t2 ORDER BY a;
+ }
+} {1 2 3 4}
+do_test unique-2.1 {
+ catchsql {
+ CREATE UNIQUE INDEX i2 ON t2(a)
+ }
+} {0 {}}
+do_test unique-2.2 {
+ catchsql {
+ SELECT * FROM t2 ORDER BY a
+ }
+} {0 {1 2 3 4}}
+do_test unique-2.3 {
+ catchsql {
+ INSERT INTO t2 VALUES(1,5);
+ }
+} {1 {column a is not unique}}
+do_test unique-2.4 {
+ catchsql {
+ SELECT * FROM t2 ORDER BY a
+ }
+} {0 {1 2 3 4}}
+do_test unique-2.5 {
+ catchsql {
+ DROP INDEX i2;
+ SELECT * FROM t2 ORDER BY a;
+ }
+} {0 {1 2 3 4}}
+do_test unique-2.6 {
+ catchsql {
+ INSERT INTO t2 VALUES(1,5)
+ }
+} {0 {}}
+do_test unique-2.7 {
+ catchsql {
+ SELECT * FROM t2 ORDER BY a, b;
+ }
+} {0 {1 2 1 5 3 4}}
+do_test unique-2.8 {
+ catchsql {
+ CREATE UNIQUE INDEX i2 ON t2(a);
+ }
+} {1 {indexed columns are not unique}}
+do_test unique-2.9 {
+ catchsql {
+ CREATE INDEX i2 ON t2(a);
+ }
+} {0 {}}
+integrity_check unique-2.10
+
+# Test the UNIQUE keyword as used on two or more fields.
+#
+do_test unique-3.1 {
+ catchsql {
+ CREATE TABLE t3(
+ a int,
+ b int,
+ c int,
+ d int,
+ unique(a,c,d)
+ );
+ }
+} {0 {}}
+do_test unique-3.2 {
+ catchsql {
+ INSERT INTO t3(a,b,c,d) VALUES(1,2,3,4);
+ SELECT * FROM t3 ORDER BY a,b,c,d;
+ }
+} {0 {1 2 3 4}}
+do_test unique-3.3 {
+ catchsql {
+ INSERT INTO t3(a,b,c,d) VALUES(1,2,3,5);
+ SELECT * FROM t3 ORDER BY a,b,c,d;
+ }
+} {0 {1 2 3 4 1 2 3 5}}
+do_test unique-3.4 {
+ catchsql {
+ INSERT INTO t3(a,b,c,d) VALUES(1,4,3,5);
+ SELECT * FROM t3 ORDER BY a,b,c,d;
+ }
+} {1 {columns a, c, d are not unique}}
+integrity_check unique-3.5
+
+# Make sure NULLs are distinct as far as the UNIQUE tests are
+# concerned.
+#
+do_test unique-4.1 {
+ execsql {
+ CREATE TABLE t4(a UNIQUE, b, c, UNIQUE(b,c));
+ INSERT INTO t4 VALUES(1,2,3);
+ INSERT INTO t4 VALUES(NULL, 2, NULL);
+ SELECT * FROM t4;
+ }
+} {1 2 3 {} 2 {}}
+do_test unique-4.2 {
+ catchsql {
+ INSERT INTO t4 VALUES(NULL, 3, 4);
+ }
+} {0 {}}
+do_test unique-4.3 {
+ execsql {
+ SELECT * FROM t4
+ }
+} {1 2 3 {} 2 {} {} 3 4}
+do_test unique-4.4 {
+ catchsql {
+ INSERT INTO t4 VALUES(2, 2, NULL);
+ }
+} {0 {}}
+do_test unique-4.5 {
+ execsql {
+ SELECT * FROM t4
+ }
+} {1 2 3 {} 2 {} {} 3 4 2 2 {}}
+
+# Ticket #1301. Any NULL value in a set of unique columns should
+# cause the rows to be distinct.
+#
+do_test unique-4.6 {
+ catchsql {
+ INSERT INTO t4 VALUES(NULL, 2, NULL);
+ }
+} {0 {}}
+do_test unique-4.7 {
+ execsql {SELECT * FROM t4}
+} {1 2 3 {} 2 {} {} 3 4 2 2 {} {} 2 {}}
+do_test unique-4.8 {
+ catchsql {CREATE UNIQUE INDEX i4a ON t4(a,b)}
+} {0 {}}
+do_test unique-4.9 {
+ catchsql {CREATE UNIQUE INDEX i4b ON t4(a,b,c)}
+} {0 {}}
+do_test unique-4.10 {
+ catchsql {CREATE UNIQUE INDEX i4c ON t4(b)}
+} {1 {indexed columns are not unique}}
+integrity_check unique-4.99
+
+# Test the error message generation logic. In particular, make sure we
+# do not overflow the static buffer used to generate the error message.
+#
+do_test unique-5.1 {
+ execsql {
+ CREATE TABLE t5(
+ first_column_with_long_name,
+ second_column_with_long_name,
+ third_column_with_long_name,
+ fourth_column_with_long_name,
+ fifth_column_with_long_name,
+ sixth_column_with_long_name,
+ UNIQUE(
+ first_column_with_long_name,
+ second_column_with_long_name,
+ third_column_with_long_name,
+ fourth_column_with_long_name,
+ fifth_column_with_long_name,
+ sixth_column_with_long_name
+ )
+ );
+ INSERT INTO t5 VALUES(1,2,3,4,5,6);
+ SELECT * FROM t5;
+ }
+} {1 2 3 4 5 6}
+do_test unique-5.2 {
+ catchsql {
+ INSERT INTO t5 VALUES(1,2,3,4,5,6);
+ }
+} {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, ... are not unique}}
+
+finish_test
diff --git a/third_party/sqlite/test/update.test b/third_party/sqlite/test/update.test
new file mode 100755
index 0000000..a6fef7d
--- /dev/null
+++ b/third_party/sqlite/test/update.test
@@ -0,0 +1,608 @@
+# 2001 September 15
+#
+# 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 file is testing the UPDATE statement.
+#
+# $Id: update.test,v 1.19 2008/04/10 18:44:36 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Try to update an non-existent table
+#
+do_test update-1.1 {
+ set v [catch {execsql {UPDATE test1 SET f2=5 WHERE f1<1}} msg]
+ lappend v $msg
+} {1 {no such table: test1}}
+
+# Try to update a read-only table
+#
+do_test update-2.1 {
+ set v [catch \
+ {execsql {UPDATE sqlite_master SET name='xyz' WHERE name='123'}} msg]
+ lappend v $msg
+} {1 {table sqlite_master may not be modified}}
+
+# Create a table to work with
+#
+do_test update-3.1 {
+ execsql {CREATE TABLE test1(f1 int,f2 int)}
+ for {set i 1} {$i<=10} {incr i} {
+ set sql "INSERT INTO test1 VALUES($i,[expr {1<<$i}])"
+ execsql $sql
+ }
+ execsql {SELECT * FROM test1 ORDER BY f1}
+} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
+
+# Unknown column name in an expression
+#
+do_test update-3.2 {
+ set v [catch {execsql {UPDATE test1 SET f1=f3*2 WHERE f2==32}} msg]
+ lappend v $msg
+} {1 {no such column: f3}}
+do_test update-3.3 {
+ set v [catch {execsql {UPDATE test1 SET f1=test2.f1*2 WHERE f2==32}} msg]
+ lappend v $msg
+} {1 {no such column: test2.f1}}
+do_test update-3.4 {
+ set v [catch {execsql {UPDATE test1 SET f3=f1*2 WHERE f2==32}} msg]
+ lappend v $msg
+} {1 {no such column: f3}}
+
+# Actually do some updates
+#
+do_test update-3.5 {
+ execsql {UPDATE test1 SET f2=f2*3}
+} {}
+do_test update-3.5.1 {
+ db changes
+} {10}
+
+# verify that SELECT does not reset the change counter
+do_test update-3.5.2 {
+ db eval {SELECT count(*) FROM test1}
+} {10}
+do_test update-3.5.3 {
+ db changes
+} {10}
+
+do_test update-3.6 {
+ execsql {SELECT * FROM test1 ORDER BY f1}
+} {1 6 2 12 3 24 4 48 5 96 6 192 7 384 8 768 9 1536 10 3072}
+do_test update-3.7 {
+ execsql {PRAGMA count_changes=on}
+ execsql {UPDATE test1 SET f2=f2/3 WHERE f1<=5}
+} {5}
+do_test update-3.8 {
+ execsql {SELECT * FROM test1 ORDER BY f1}
+} {1 2 2 4 3 8 4 16 5 32 6 192 7 384 8 768 9 1536 10 3072}
+do_test update-3.9 {
+ execsql {UPDATE test1 SET f2=f2/3 WHERE f1>5}
+} {5}
+do_test update-3.10 {
+ execsql {SELECT * FROM test1 ORDER BY f1}
+} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
+
+# Swap the values of f1 and f2 for all elements
+#
+do_test update-3.11 {
+ execsql {UPDATE test1 SET F2=f1, F1=f2}
+} {10}
+do_test update-3.12 {
+ execsql {SELECT * FROM test1 ORDER BY F1}
+} {2 1 4 2 8 3 16 4 32 5 64 6 128 7 256 8 512 9 1024 10}
+do_test update-3.13 {
+ execsql {PRAGMA count_changes=off}
+ execsql {UPDATE test1 SET F2=f1, F1=f2}
+} {}
+do_test update-3.14 {
+ execsql {SELECT * FROM test1 ORDER BY F1}
+} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
+
+# Create duplicate entries and make sure updating still
+# works.
+#
+do_test update-4.0 {
+ execsql {
+ DELETE FROM test1 WHERE f1<=5;
+ INSERT INTO test1(f1,f2) VALUES(8,88);
+ INSERT INTO test1(f1,f2) VALUES(8,888);
+ INSERT INTO test1(f1,f2) VALUES(77,128);
+ INSERT INTO test1(f1,f2) VALUES(777,128);
+ }
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-4.1 {
+ execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
+do_test update-4.2 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
+do_test update-4.3 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-4.4 {
+ execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
+do_test update-4.5 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
+do_test update-4.6 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128;
+ }
+} {2}
+do_test update-4.7 {
+ execsql {
+ PRAGMA count_changes=off;
+ SELECT * FROM test1 ORDER BY f1,f2
+ }
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+
+# Repeat the previous sequence of tests with an index.
+#
+do_test update-5.0 {
+ execsql {CREATE INDEX idx1 ON test1(f1)}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-5.1 {
+ execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
+do_test update-5.2 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
+do_test update-5.3 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-5.4 {
+ execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
+do_test update-5.4.1 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-5.4.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {778 128}
+do_test update-5.4.3 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-5.5 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
+} {}
+do_test update-5.5.1 {
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
+do_test update-5.5.2 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-5.5.3 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-5.5.4 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-5.5.5 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-5.6 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128;
+ }
+} {2}
+do_test update-5.6.1 {
+ execsql {
+ PRAGMA count_changes=off;
+ SELECT * FROM test1 ORDER BY f1,f2
+ }
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-5.6.2 {
+ execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
+} {77 128}
+do_test update-5.6.3 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-5.6.4 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-5.6.5 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 256 8 888}
+
+# Repeat the previous sequence of tests with a different index.
+#
+execsql {PRAGMA synchronous=FULL}
+do_test update-6.0 {
+ execsql {DROP INDEX idx1}
+ execsql {CREATE INDEX idx1 ON test1(f2)}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-6.1 {
+ execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
+do_test update-6.1.1 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 89 8 257 8 889}
+do_test update-6.1.2 {
+ execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
+} {8 89}
+do_test update-6.1.3 {
+ execsql {SELECT * FROM test1 WHERE f1==88 ORDER BY f1,f2}
+} {}
+do_test update-6.2 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
+do_test update-6.3 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-6.3.1 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 256 8 888}
+do_test update-6.3.2 {
+ execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
+} {}
+do_test update-6.3.3 {
+ execsql {SELECT * FROM test1 WHERE f2==88 ORDER BY f1,f2}
+} {8 88}
+do_test update-6.4 {
+ execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
+do_test update-6.4.1 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-6.4.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {778 128}
+do_test update-6.4.3 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-6.5 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
+do_test update-6.5.1 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-6.5.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-6.5.3 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-6.5.4 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-6.6 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-6.6.1 {
+ execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
+} {77 128}
+do_test update-6.6.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-6.6.3 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-6.6.4 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 256 8 888}
+
+# Repeat the previous sequence of tests with multiple
+# indices
+#
+do_test update-7.0 {
+ execsql {CREATE INDEX idx2 ON test1(f2)}
+ execsql {CREATE INDEX idx3 ON test1(f1,f2)}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-7.1 {
+ execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
+do_test update-7.1.1 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 89 8 257 8 889}
+do_test update-7.1.2 {
+ execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
+} {8 89}
+do_test update-7.1.3 {
+ execsql {SELECT * FROM test1 WHERE f1==88 ORDER BY f1,f2}
+} {}
+do_test update-7.2 {
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
+do_test update-7.3 {
+ # explain {UPDATE test1 SET f2=f2-1 WHERE f1==8 and F2<300}
+ execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-7.3.1 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 256 8 888}
+do_test update-7.3.2 {
+ execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
+} {}
+do_test update-7.3.3 {
+ execsql {SELECT * FROM test1 WHERE f2==88 ORDER BY f1,f2}
+} {8 88}
+do_test update-7.4 {
+ execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
+do_test update-7.4.1 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-7.4.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {778 128}
+do_test update-7.4.3 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-7.5 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
+do_test update-7.5.1 {
+ execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
+} {78 128}
+do_test update-7.5.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-7.5.3 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-7.5.4 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 128 8 256 8 888}
+do_test update-7.6 {
+ execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
+ execsql {SELECT * FROM test1 ORDER BY f1,f2}
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-7.6.1 {
+ execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
+} {77 128}
+do_test update-7.6.2 {
+ execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
+} {}
+do_test update-7.6.3 {
+ execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
+} {777 128}
+do_test update-7.6.4 {
+ execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
+} {8 88 8 256 8 888}
+
+# Error messages
+#
+do_test update-9.1 {
+ set v [catch {execsql {
+ UPDATE test1 SET x=11 WHERE f1=1025
+ }} msg]
+ lappend v $msg
+} {1 {no such column: x}}
+do_test update-9.2 {
+ set v [catch {execsql {
+ UPDATE test1 SET f1=x(11) WHERE f1=1025
+ }} msg]
+ lappend v $msg
+} {1 {no such function: x}}
+do_test update-9.3 {
+ set v [catch {execsql {
+ UPDATE test1 SET f1=11 WHERE x=1025
+ }} msg]
+ lappend v $msg
+} {1 {no such column: x}}
+do_test update-9.4 {
+ set v [catch {execsql {
+ UPDATE test1 SET f1=11 WHERE x(f1)=1025
+ }} msg]
+ lappend v $msg
+} {1 {no such function: x}}
+
+# Try doing updates on a unique column where the value does not
+# really change.
+#
+do_test update-10.1 {
+ execsql {
+ DROP TABLE test1;
+ CREATE TABLE t1(
+ a integer primary key,
+ b UNIQUE,
+ c, d,
+ e, f,
+ UNIQUE(c,d)
+ );
+ INSERT INTO t1 VALUES(1,2,3,4,5,6);
+ INSERT INTO t1 VALUES(2,3,4,4,6,7);
+ SELECT * FROM t1
+ }
+} {1 2 3 4 5 6 2 3 4 4 6 7}
+do_test update-10.2 {
+ catchsql {
+ UPDATE t1 SET a=1, e=9 WHERE f=6;
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 9 6 2 3 4 4 6 7}}
+do_test update-10.3 {
+ catchsql {
+ UPDATE t1 SET a=1, e=10 WHERE f=7;
+ SELECT * FROM t1;
+ }
+} {1 {PRIMARY KEY must be unique}}
+do_test update-10.4 {
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 9 6 2 3 4 4 6 7}}
+do_test update-10.5 {
+ catchsql {
+ UPDATE t1 SET b=2, e=11 WHERE f=6;
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 11 6 2 3 4 4 6 7}}
+do_test update-10.6 {
+ catchsql {
+ UPDATE t1 SET b=2, e=12 WHERE f=7;
+ SELECT * FROM t1;
+ }
+} {1 {column b is not unique}}
+do_test update-10.7 {
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 11 6 2 3 4 4 6 7}}
+do_test update-10.8 {
+ catchsql {
+ UPDATE t1 SET c=3, d=4, e=13 WHERE f=6;
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 13 6 2 3 4 4 6 7}}
+do_test update-10.9 {
+ catchsql {
+ UPDATE t1 SET c=3, d=4, e=14 WHERE f=7;
+ SELECT * FROM t1;
+ }
+} {1 {columns c, d are not unique}}
+do_test update-10.10 {
+ catchsql {
+ SELECT * FROM t1;
+ }
+} {0 {1 2 3 4 13 6 2 3 4 4 6 7}}
+
+# Make sure we can handle a subquery in the where clause.
+#
+ifcapable subquery {
+ do_test update-11.1 {
+ execsql {
+ UPDATE t1 SET e=e+1 WHERE b IN (SELECT b FROM t1);
+ SELECT b,e FROM t1;
+ }
+ } {2 14 3 7}
+ do_test update-11.2 {
+ execsql {
+ UPDATE t1 SET e=e+1 WHERE a IN (SELECT a FROM t1);
+ SELECT a,e FROM t1;
+ }
+ } {1 15 2 8}
+}
+
+integrity_check update-12.1
+
+# Ticket 602. Updates should occur in the same order as the records
+# were discovered in the WHERE clause.
+#
+do_test update-13.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ INSERT INTO t2 SELECT a+2 FROM t2;
+ INSERT INTO t2 SELECT a+4 FROM t2;
+ INSERT INTO t2 SELECT a+8 FROM t2;
+ INSERT INTO t2 SELECT a+16 FROM t2;
+ INSERT INTO t2 SELECT a+32 FROM t2;
+ INSERT INTO t2 SELECT a+64 FROM t2;
+ INSERT INTO t2 SELECT a+128 FROM t2;
+ INSERT INTO t2 SELECT a+256 FROM t2;
+ INSERT INTO t2 SELECT a+512 FROM t2;
+ INSERT INTO t2 SELECT a+1024 FROM t2;
+ COMMIT;
+ SELECT count(*) FROM t2;
+ }
+} {2048}
+do_test update-13.2 {
+ execsql {
+ SELECT count(*) FROM t2 WHERE a=rowid;
+ }
+} {2048}
+do_test update-13.3 {
+ execsql {
+ UPDATE t2 SET rowid=rowid-1;
+ SELECT count(*) FROM t2 WHERE a=rowid+1;
+ }
+} {2048}
+do_test update-13.3 {
+ execsql {
+ UPDATE t2 SET rowid=rowid+10000;
+ UPDATE t2 SET rowid=rowid-9999;
+ SELECT count(*) FROM t2 WHERE a=rowid;
+ }
+} {2048}
+do_test update-13.4 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 SELECT a+2048 FROM t2;
+ INSERT INTO t2 SELECT a+4096 FROM t2;
+ INSERT INTO t2 SELECT a+8192 FROM t2;
+ SELECT count(*) FROM t2 WHERE a=rowid;
+ COMMIT;
+ }
+} 16384
+do_test update-13.5 {
+ execsql {
+ UPDATE t2 SET rowid=rowid-1;
+ SELECT count(*) FROM t2 WHERE a=rowid+1;
+ }
+} 16384
+
+integrity_check update-13.6
+
+ifcapable {trigger} {
+# Test for proper detection of malformed WHEN clauses on UPDATE triggers.
+#
+do_test update-14.1 {
+ execsql {
+ CREATE TABLE t3(a,b,c);
+ CREATE TRIGGER t3r1 BEFORE UPDATE on t3 WHEN nosuchcol BEGIN
+ SELECT 'illegal WHEN clause';
+ END;
+ }
+} {}
+do_test update-14.2 {
+ catchsql {
+ UPDATE t3 SET a=1;
+ }
+} {1 {no such column: nosuchcol}}
+do_test update-14.3 {
+ execsql {
+ CREATE TABLE t4(a,b,c);
+ CREATE TRIGGER t4r1 AFTER UPDATE on t4 WHEN nosuchcol BEGIN
+ SELECT 'illegal WHEN clause';
+ END;
+ }
+} {}
+do_test update-14.4 {
+ catchsql {
+ UPDATE t4 SET a=1;
+ }
+} {1 {no such column: nosuchcol}}
+
+} ;# ifcapable {trigger}
+
+
+finish_test
diff --git a/third_party/sqlite/test/utf16align.test b/third_party/sqlite/test/utf16align.test
new file mode 100755
index 0000000..fb41b77
--- /dev/null
+++ b/third_party/sqlite/test/utf16align.test
@@ -0,0 +1,84 @@
+# 2006 February 16
+#
+# 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 contains code to verify that the SQLITE_UTF16_ALIGNED
+# flag passed into the sqlite3_create_collation() function insures
+# that all strings passed to that function are aligned on an even
+# byte boundary.
+#
+# $Id: utf16align.test,v 1.1 2006/02/16 18:16:38 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this entire test if we do not support UTF16
+#
+ifcapable !utf16 {
+ finish_test
+ return
+}
+
+# Create a database with a UTF16 encoding. Put in lots of string
+# data of varying lengths.
+#
+do_test utf16align-1.0 {
+ set unaligned_string_counter 0
+ add_alignment_test_collations [sqlite3_connection_pointer db]
+ execsql {
+ PRAGMA encoding=UTF16;
+ CREATE TABLE t1(
+ id INTEGER PRIMARY KEY,
+ spacer TEXT,
+ a TEXT COLLATE utf16_aligned,
+ b TEXT COLLATE utf16_unaligned
+ );
+ INSERT INTO t1(a) VALUES("abc");
+ INSERT INTO t1(a) VALUES("defghi");
+ INSERT INTO t1(a) VALUES("jklmnopqrstuv");
+ INSERT INTO t1(a) VALUES("wxyz0123456789-");
+ UPDATE t1 SET b=a||'-'||a;
+ INSERT INTO t1(a,b) SELECT a||b, b||a FROM t1;
+ INSERT INTO t1(a,b) SELECT a||b, b||a FROM t1;
+ INSERT INTO t1(a,b) SELECT a||b, b||a FROM t1;
+ INSERT INTO t1(a,b) VALUES('one','two');
+ INSERT INTO t1(a,b) SELECT a, b FROM t1;
+ UPDATE t1 SET spacer = CASE WHEN rowid&1 THEN 'x' ELSE 'xx' END;
+ SELECT count(*) FROM t1;
+ }
+} 66
+do_test utf16align-1.1 {
+ set unaligned_string_counter
+} 0
+
+# Creating an index that uses the unaligned collation. We should see
+# some unaligned strings passed to the collating function.
+#
+do_test utf16align-1.2 {
+ execsql {
+ CREATE INDEX t1i1 ON t1(spacer, b);
+ }
+ # puts $unaligned_string_counter
+ expr {$unaligned_string_counter>0}
+} 1
+
+# Create another index that uses the aligned collation. This time
+# there should be no unaligned accesses
+#
+do_test utf16align-1.3 {
+ set unaligned_string_counter 0
+ execsql {
+ CREATE INDEX t1i2 ON t1(spacer, a);
+ }
+ expr {$unaligned_string_counter>0}
+} 0
+integrity_check utf16align-1.4
+
+finish_test
diff --git a/third_party/sqlite/test/vacuum.test b/third_party/sqlite/test/vacuum.test
new file mode 100755
index 0000000..a81ce28
--- /dev/null
+++ b/third_party/sqlite/test/vacuum.test
@@ -0,0 +1,340 @@
+# 2001 September 15
+#
+# 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 file is testing the VACUUM statement.
+#
+# $Id: vacuum.test,v 1.41 2008/04/15 02:36:34 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If the VACUUM statement is disabled in the current build, skip all
+# the tests in this file.
+#
+ifcapable {!vacuum} {
+ omit_test vacuum.test {Compiled with SQLITE_OMIT_VACUUM}
+ finish_test
+ return
+}
+if $AUTOVACUUM {
+ omit_test vacuum.test {Auto-vacuum is enabled}
+ finish_test
+ return
+}
+
+set fcnt 1
+do_test vacuum-1.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(NULL,randstr(10,100),randstr(5,50));
+ INSERT INTO t1 VALUES(123456,randstr(10,100),randstr(5,50));
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
+ CREATE INDEX i1 ON t1(b,c);
+ CREATE UNIQUE INDEX i2 ON t1(c,a);
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t2;
+ }
+ set ::size1 [file size test.db]
+ set ::cksum [cksum]
+ expr {$::cksum!=""}
+} {1}
+do_test vacuum-1.2 {
+ execsql {
+ VACUUM;
+ }
+ cksum
+} $cksum
+ifcapable vacuum {
+ do_test vacuum-1.3 {
+ expr {[file size test.db]<$::size1}
+ } {1}
+}
+do_test vacuum-1.4 {
+ set sql_script {
+ BEGIN;
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ CREATE VIEW v1 AS SELECT b, c FROM t3;
+ CREATE TRIGGER r1 AFTER DELETE ON t2 BEGIN SELECT 1; END;
+ COMMIT;
+ DROP TABLE t2;
+ }
+ # If the library was compiled to omit view support, comment out the
+ # create view in the script $sql_script before executing it. Similarly,
+ # if triggers are not supported, comment out the trigger definition.
+ ifcapable !view {
+ regsub {CREATE VIEW} $sql_script {-- CREATE VIEW} sql_script
+ }
+ ifcapable !trigger {
+ regsub {CREATE TRIGGER} $sql_script {-- CREATE TRIGGER} sql_script
+ }
+ execsql $sql_script
+ set ::size1 [file size test.db]
+ set ::cksum [cksum]
+ expr {$::cksum!=""}
+} {1}
+do_test vacuum-1.5 {
+ execsql {
+ VACUUM;
+ }
+ cksum
+} $cksum
+
+ifcapable vacuum {
+ do_test vacuum-1.6 {
+ expr {[file size test.db]<$::size1}
+ } {1}
+}
+ifcapable vacuum {
+ do_test vacuum-2.1 {
+ catchsql {
+ BEGIN;
+ VACUUM;
+ COMMIT;
+ }
+ } {1 {cannot VACUUM from within a transaction}}
+ catch {db eval COMMIT}
+}
+do_test vacuum-2.2 {
+ sqlite3 db2 test.db
+ execsql {
+ BEGIN;
+ CREATE TABLE t4 AS SELECT * FROM t1;
+ CREATE TABLE t5 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t4;
+ DROP TABLE t5;
+ } db2
+ set ::cksum [cksum db2]
+ catchsql {
+ VACUUM
+ }
+} {0 {}}
+do_test vacuum-2.3 {
+ cksum
+} $cksum
+do_test vacuum-2.4 {
+ catch {db2 eval {SELECT count(*) FROM sqlite_master}}
+ cksum db2
+} $cksum
+
+# Make sure the schema cookie is incremented by vacuum.
+#
+do_test vacuum-2.5 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t6 AS SELECT * FROM t1;
+ CREATE TABLE t7 AS SELECT * FROM t1;
+ COMMIT;
+ }
+ sqlite3 db3 test.db
+ execsql {
+ -- The "SELECT * FROM sqlite_master" statement ensures that this test
+ -- works when shared-cache is enabled. If shared-cache is enabled, then
+ -- db3 shares a cache with db2 (but not db - it was opened as
+ -- "./test.db").
+ SELECT * FROM sqlite_master;
+ SELECT * FROM t7 LIMIT 1
+ } db3
+ execsql {
+ VACUUM;
+ }
+ execsql {
+ INSERT INTO t7 VALUES(1234567890,'hello','world');
+ } db3
+ execsql {
+ SELECT * FROM t7 WHERE a=1234567890
+ }
+} {1234567890 hello world}
+integrity_check vacuum-2.6
+do_test vacuum-2.7 {
+ execsql {
+ SELECT * FROM t7 WHERE a=1234567890
+ } db3
+} {1234567890 hello world}
+do_test vacuum-2.8 {
+ execsql {
+ INSERT INTO t7 SELECT * FROM t6;
+ SELECT count(*) FROM t7;
+ }
+} 513
+integrity_check vacuum-2.9
+do_test vacuum-2.10 {
+ execsql {
+ DELETE FROM t7;
+ SELECT count(*) FROM t7;
+ } db3
+} 0
+integrity_check vacuum-2.11
+db3 close
+
+
+# Ticket #427. Make sure VACUUM works when the EMPTY_RESULT_CALLBACKS
+# pragma is turned on.
+#
+do_test vacuum-3.1 {
+ db close
+ db2 close
+ file delete test.db
+ sqlite3 db test.db
+ execsql {
+ PRAGMA empty_result_callbacks=on;
+ VACUUM;
+ }
+} {}
+
+# Ticket #464. Make sure VACUUM works with the sqlite3_prepare() API.
+#
+do_test vacuum-4.1 {
+ db close
+ sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
+ set VM [sqlite3_prepare $DB {VACUUM} -1 TAIL]
+ sqlite3_step $VM
+} {SQLITE_DONE}
+do_test vacuum-4.2 {
+ sqlite3_finalize $VM
+} SQLITE_OK
+
+# Ticket #515. VACUUM after deleting and recreating the table that
+# a view refers to. Omit this test if the library is not view-enabled.
+#
+ifcapable view {
+do_test vacuum-5.1 {
+ db close
+ file delete -force test.db
+ sqlite3 db test.db
+ catchsql {
+ CREATE TABLE Test (TestID int primary key);
+ INSERT INTO Test VALUES (NULL);
+ CREATE VIEW viewTest AS SELECT * FROM Test;
+
+ BEGIN;
+ CREATE TABLE tempTest (TestID int primary key, Test2 int NULL);
+ INSERT INTO tempTest SELECT TestID, 1 FROM Test;
+ DROP TABLE Test;
+ CREATE TABLE Test(TestID int primary key, Test2 int NULL);
+ INSERT INTO Test SELECT * FROM tempTest;
+ DROP TABLE tempTest;
+ COMMIT;
+ VACUUM;
+ }
+} {0 {}}
+do_test vacuum-5.2 {
+ catchsql {
+ VACUUM;
+ }
+} {0 {}}
+} ;# ifcapable view
+
+# Ensure vacuum works with complicated tables names.
+do_test vacuum-6.1 {
+ execsql {
+ CREATE TABLE "abc abc"(a, b, c);
+ INSERT INTO "abc abc" VALUES(1, 2, 3);
+ VACUUM;
+ }
+} {}
+do_test vacuum-6.2 {
+ execsql {
+ select * from "abc abc";
+ }
+} {1 2 3}
+
+# Also ensure that blobs survive a vacuum.
+ifcapable {bloblit} {
+ do_test vacuum-6.3 {
+ execsql {
+ DELETE FROM "abc abc";
+ INSERT INTO "abc abc" VALUES(X'00112233', NULL, NULL);
+ VACUUM;
+ }
+ } {}
+ do_test vacuum-6.4 {
+ execsql {
+ select count(*) from "abc abc" WHERE a = X'00112233';
+ }
+ } {1}
+}
+
+# Check what happens when an in-memory database is vacuumed. The
+# [file delete] command covers us in case the library was compiled
+# without in-memory database support.
+#
+file delete -force :memory:
+do_test vacuum-7.0 {
+ sqlite3 db2 :memory:
+ execsql {
+ CREATE TABLE t1(t);
+ VACUUM;
+ } db2
+} {}
+db2 close
+
+# Ticket #873. VACUUM a database that has ' in its name.
+#
+do_test vacuum-8.1 {
+ file delete -force a'z.db
+ file delete -force a'z.db-journal
+ sqlite3 db2 a'z.db
+ execsql {
+ CREATE TABLE t1(t);
+ VACUUM;
+ } db2
+} {}
+db2 close
+
+# Ticket #1095: Vacuum a table that uses AUTOINCREMENT
+#
+ifcapable {autoinc} {
+ do_test vacuum-9.1 {
+ execsql {
+ DROP TABLE 'abc abc';
+ CREATE TABLE autoinc(a INTEGER PRIMARY KEY AUTOINCREMENT, b);
+ INSERT INTO autoinc(b) VALUES('hi');
+ INSERT INTO autoinc(b) VALUES('there');
+ DELETE FROM autoinc;
+ }
+ set ::cksum [cksum]
+ expr {$::cksum!=""}
+ } {1}
+ do_test vacuum-9.2 {
+ execsql {
+ VACUUM;
+ }
+ cksum
+ } $::cksum
+ do_test vacuum-9.3 {
+ execsql {
+ INSERT INTO autoinc(b) VALUES('one');
+ INSERT INTO autoinc(b) VALUES('two');
+ }
+ set ::cksum [cksum]
+ expr {$::cksum!=""}
+ } {1}
+ do_test vacuum-9.4 {
+ execsql {
+ VACUUM;
+ }
+ cksum
+ } $::cksum
+}
+
+file delete -force {a'z.db}
+
+finish_test
diff --git a/third_party/sqlite/test/vacuum2.test b/third_party/sqlite/test/vacuum2.test
new file mode 100755
index 0000000..5c46d0c
--- /dev/null
+++ b/third_party/sqlite/test/vacuum2.test
@@ -0,0 +1,128 @@
+# 2005 February 15
+#
+# 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 file is testing the VACUUM statement.
+#
+# $Id: vacuum2.test,v 1.7 2008/01/17 02:36:28 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If the VACUUM statement is disabled in the current build, skip all
+# the tests in this file.
+#
+ifcapable {!vacuum||!autoinc} {
+ finish_test
+ return
+}
+if $AUTOVACUUM {
+ finish_test
+ return
+}
+
+# Ticket #1121 - make sure vacuum works if all autoincrement tables
+# have been deleted.
+#
+do_test vacuum2-1.1 {
+ execsql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y);
+ DROP TABLE t1;
+ VACUUM;
+ }
+} {}
+
+# Ticket #2518. Make sure vacuum increments the change counter
+# in the database header.
+#
+do_test vacuum2-2.1 {
+ execsql {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(y);
+ INSERT INTO t1 VALUES(1);
+ }
+ hexio_get_int [hexio_read test.db 24 4]
+} [expr {[hexio_get_int [hexio_read test.db 24 4]]+3}]
+do_test vacuum2-2.1 {
+ execsql {
+ VACUUM
+ }
+ hexio_get_int [hexio_read test.db 24 4]
+} [expr {[hexio_get_int [hexio_read test.db 24 4]]+1}]
+
+############################################################################
+# Verify that we can use the auto_vacuum pragma to request a new
+# autovacuum setting, do a VACUUM, and the new setting takes effect.
+# Make sure this happens correctly even if there are multiple open
+# connections to the same database file.
+#
+sqlite3 db2 test.db
+set pageSize [db eval {pragma page_size}]
+
+# We are currently not autovacuuming so the database should be 3 pages
+# in size. 1 page for each of sqlite_master, t1, and t2.
+#
+do_test vacuum2-3.1 {
+ execsql {
+ INSERT INTO t1 VALUES('hello');
+ INSERT INTO t2 VALUES('out there');
+ }
+ expr {[file size test.db]/$pageSize}
+} {3}
+set cksum [cksum]
+do_test vacuum2-3.2 {
+ cksum db2
+} $cksum
+
+# Convert the database to an autovacuumed database.
+do_test vacuum2-3.3 {
+ execsql {
+ PRAGMA auto_vacuum=FULL;
+ VACUUM;
+ }
+ expr {[file size test.db]/$pageSize}
+} {4}
+do_test vacuum2-3.4 {
+ cksum db2
+} $cksum
+do_test vacuum2-3.5 {
+ cksum
+} $cksum
+do_test vacuum2-3.6 {
+ execsql {PRAGMA integrity_check} db2
+} {ok}
+do_test vacuum2-3.7 {
+ execsql {PRAGMA integrity_check} db
+} {ok}
+
+# Convert the database back to a non-autovacuumed database.
+do_test vacuum2-3.13 {
+ execsql {
+ PRAGMA auto_vacuum=NONE;
+ VACUUM;
+ }
+ expr {[file size test.db]/$pageSize}
+} {3}
+do_test vacuum2-3.14 {
+ cksum db2
+} $cksum
+do_test vacuum2-3.15 {
+ cksum
+} $cksum
+do_test vacuum2-3.16 {
+ execsql {PRAGMA integrity_check} db2
+} {ok}
+do_test vacuum2-3.17 {
+ execsql {PRAGMA integrity_check} db
+} {ok}
+
+db2 close
+
+finish_test
diff --git a/third_party/sqlite/test/vacuum3.test b/third_party/sqlite/test/vacuum3.test
new file mode 100755
index 0000000..fb97a60
--- /dev/null
+++ b/third_party/sqlite/test/vacuum3.test
@@ -0,0 +1,317 @@
+# 2007 March 19
+#
+# 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 file is changing the database page size using a
+# VACUUM statement.
+#
+# $Id: vacuum3.test,v 1.7 2008/08/02 03:50:40 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If the VACUUM statement is disabled in the current build, skip all
+# the tests in this file.
+#
+ifcapable !vacuum {
+ finish_test
+ return
+}
+
+
+#-------------------------------------------------------------------
+# Test cases vacuum3-1.* convert a simple 2-page database between a
+# few different page sizes.
+#
+do_test vacuum3-1.1 {
+ execsql {
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, 2, 3);
+ }
+} {}
+do_test vacuum3-1.2 {
+ execsql { PRAGMA page_size }
+} {1024}
+do_test vacuum3-1.3 {
+ file size test.db
+} {2048}
+
+set I 4
+foreach {request actual database} [list \
+ 2048 2048 4096 \
+ 1024 1024 2048 \
+ 1170 1024 2048 \
+ 256 1024 2048 \
+ 512 512 1024 \
+ 4096 4096 8192 \
+ 1024 1024 2048 \
+] {
+ do_test vacuum3-1.$I.1 {
+ execsql "
+ PRAGMA page_size = $request;
+ VACUUM;
+ "
+ execsql { PRAGMA page_size }
+ } $actual
+ do_test vacuum3-1.$I.2 {
+ file size test.db
+ } $database
+ do_test vacuum3-1.$I.3 {
+ execsql { SELECT * FROM t1 }
+ } {1 2 3}
+ integrity_check vacuum3-1.$I.4
+
+ incr I
+}
+
+#-------------------------------------------------------------------
+# Test cases vacuum3-2.* convert a simple 3-page database between a
+# few different page sizes.
+#
+do_test vacuum3-2.1 {
+ execsql {
+ PRAGMA page_size = 1024;
+ VACUUM;
+ ALTER TABLE t1 ADD COLUMN d;
+ UPDATE t1 SET d = randomblob(1000);
+ }
+ file size test.db
+} {3072}
+do_test vacuum3-2.2 {
+ execsql { PRAGMA page_size }
+} {1024}
+do_test vacuum3-2.3 {
+ set blob [db one {select d from t1}]
+ string length $blob
+} {1000}
+
+set I 4
+foreach {request actual database} [list \
+ 2048 2048 4096 \
+ 1024 1024 3072 \
+ 1170 1024 3072 \
+ 256 1024 3072 \
+ 512 512 2048 \
+ 4096 4096 8192 \
+ 1024 1024 3072 \
+] {
+ do_test vacuum3-2.$I.1 {
+ execsql "
+ PRAGMA page_size = $request;
+ VACUUM;
+ "
+ execsql { PRAGMA page_size }
+ } $actual
+ do_test vacuum3-2.$I.2 {
+ file size test.db
+ } $database
+ do_test vacuum3-2.$I.3 {
+ execsql { SELECT * FROM t1 }
+ } [list 1 2 3 $blob]
+ integrity_check vacuum3-1.$I.4
+
+ incr I
+}
+
+#-------------------------------------------------------------------
+# Test cases vacuum3-3.* converts a database large enough to include
+# the locking page (in a test environment) between few different
+# page sizes.
+#
+proc signature {} {
+ return [db eval {SELECT count(*), md5sum(a), md5sum(b), md5sum(c) FROM abc}]
+}
+do_test vacuum3-3.1 {
+ execsql "
+ PRAGMA page_size = 1024;
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c);
+ INSERT INTO abc VALUES(randomblob(100), randomblob(200), randomblob(1000));
+ INSERT INTO abc
+ SELECT randomblob(1000), randomblob(200), randomblob(100)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(25), randomblob(45), randomblob(9456)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(100), randomblob(200), randomblob(1000)
+ FROM abc;
+ INSERT INTO abc
+ SELECT randomblob(25), randomblob(45), randomblob(9456)
+ FROM abc;
+ COMMIT;
+ "
+} {}
+do_test vacuum3-3.2 {
+ execsql { PRAGMA page_size }
+} {1024}
+
+set ::sig [signature]
+
+set I 3
+foreach {request actual} [list \
+ 2048 2048 \
+ 1024 1024 \
+ 1170 1024 \
+ 256 1024 \
+ 512 512 \
+ 4096 4096 \
+ 1024 1024 \
+] {
+ do_test vacuum3-3.$I.1 {
+ execsql "
+ PRAGMA page_size = $request;
+ VACUUM;
+ "
+ execsql { PRAGMA page_size }
+ } $actual
+ do_test vacuum3-3.$I.2 {
+ signature
+ } $::sig
+ integrity_check vacuum3-3.$I.3
+
+ incr I
+}
+
+do_test vacuum3-4.1 {
+ db close
+ file delete test.db
+ sqlite3 db test.db
+ execsql {
+ PRAGMA page_size=1024;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ INSERT INTO abc VALUES(4, 5, 6);
+ }
+ execsql { SELECT * FROM abc }
+} {1 2 3 4 5 6}
+do_test vacuum3-4.2 {
+ sqlite3 db2 test.db
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4 5 6}
+do_test vacuum3-4.3 {
+ execsql {
+ PRAGMA page_size = 2048;
+ VACUUM;
+ }
+ execsql { SELECT * FROM abc }
+} {1 2 3 4 5 6}
+do_test vacuum3-4.4 {
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4 5 6}
+do_test vacuum3-4.5 {
+ execsql {
+ PRAGMA page_size=16384;
+ VACUUM;
+ } db2
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4 5 6}
+do_test vacuum3-4.6 {
+ execsql {
+ PRAGMA page_size=1024;
+ VACUUM;
+ }
+ execsql { SELECT * FROM abc } db2
+} {1 2 3 4 5 6}
+
+db2 close
+
+do_ioerr_test vacuum3-ioerr-1 -cksum true -sqlprep {
+ PRAGMA page_size = 1024;
+ BEGIN;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
+ INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t2;
+} -sqlbody {
+ PRAGMA page_size = 4096;
+ VACUUM;
+}
+do_ioerr_test vacuum3-ioerr-2 -cksum true -sqlprep {
+ PRAGMA page_size = 2048;
+ BEGIN;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
+ INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t2;
+} -sqlbody {
+ PRAGMA page_size = 512;
+ VACUUM;
+}
+
+source $testdir/malloc_common.tcl
+if {$MEMDEBUG} {
+ do_malloc_test vacuum3-malloc-1 -sqlprep {
+ PRAGMA page_size = 2048;
+ BEGIN;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
+ INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
+ INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE TABLE t3 AS SELECT * FROM t1;
+ COMMIT;
+ DROP TABLE t2;
+ } -sqlbody {
+ PRAGMA page_size = 512;
+ VACUUM;
+ }
+ do_malloc_test vacuum3-malloc-2 -sqlprep {
+ PRAGMA encoding=UTF16;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, 2, 3);
+ CREATE TABLE t2(x,y,z);
+ INSERT INTO t2 SELECT * FROM t1;
+ } -sqlbody {
+ VACUUM;
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/varint.test b/third_party/sqlite/test/varint.test
new file mode 100755
index 0000000..974e88f
--- /dev/null
+++ b/third_party/sqlite/test/varint.test
@@ -0,0 +1,32 @@
+# 2001 September 15
+#
+# 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 variable-length integer encoding scheme.
+#
+# $Id: varint.test,v 1.1 2004/05/18 15:57:42 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Test reading and writing of varints.
+#
+set cnt 0
+foreach start {0 100 10000 1000000 0x10000000} {
+ foreach mult {1 0x10 0x100 0x1000 0x10000 0x100000 0x1000000 0x10000000} {
+ foreach incr {1 500 10000 50000000} {
+ incr cnt
+ do_test varint-1.$cnt {
+ btree_varint_test $start $mult 5000 $incr
+ } {}
+ }
+ }
+}
diff --git a/third_party/sqlite/test/veryquick.test b/third_party/sqlite/test/veryquick.test
new file mode 100755
index 0000000..f212341
--- /dev/null
+++ b/third_party/sqlite/test/veryquick.test
@@ -0,0 +1,15 @@
+#
+# 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 runs all the tests run by quick.test except for those related
+# to malloc or IO error simulation. With these tests omitted, the overall
+# run time is reduced by about 75%.
+#
+# $Id: veryquick.test,v 1.9 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+set ISVERYQUICK 1
+source $testdir/quick.test
diff --git a/third_party/sqlite/test/view.test b/third_party/sqlite/test/view.test
new file mode 100755
index 0000000..8dabf1f
--- /dev/null
+++ b/third_party/sqlite/test/view.test
@@ -0,0 +1,542 @@
+# 2002 February 26
+#
+# 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 file is testing VIEW statements.
+#
+# $Id: view.test,v 1.37 2008/07/10 00:32:42 drh Exp $
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Omit this entire file if the library is not configured with views enabled.
+ifcapable !view {
+ finish_test
+ return
+}
+
+do_test view-1.0 {
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(4,5,6);
+ INSERT INTO t1 VALUES(7,8,9);
+ SELECT * FROM t1;
+ }
+} {1 2 3 4 5 6 7 8 9}
+
+do_test view-1.1 {
+ execsql {
+ BEGIN;
+ CREATE VIEW IF NOT EXISTS v1 AS SELECT a,b FROM t1;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 2 4 5 7 8}
+do_test view-1.2 {
+ catchsql {
+ ROLLBACK;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 {no such table: v1}}
+do_test view-1.3 {
+ execsql {
+ CREATE VIEW v1 AS SELECT a,b FROM t1;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 2 4 5 7 8}
+do_test view-1.3.1 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 2 4 5 7 8}
+do_test view-1.4 {
+ catchsql {
+ DROP VIEW IF EXISTS v1;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 {no such table: v1}}
+do_test view-1.5 {
+ execsql {
+ CREATE VIEW v1 AS SELECT a,b FROM t1;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 2 4 5 7 8}
+do_test view-1.6 {
+ catchsql {
+ DROP TABLE t1;
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {1 {no such table: main.t1}}
+do_test view-1.7 {
+ execsql {
+ CREATE TABLE t1(x,a,b,c);
+ INSERT INTO t1 VALUES(1,2,3,4);
+ INSERT INTO t1 VALUES(4,5,6,7);
+ INSERT INTO t1 VALUES(7,8,9,10);
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {2 3 5 6 8 9}
+do_test view-1.8 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM v1 ORDER BY a;
+ }
+} {2 3 5 6 8 9}
+
+do_test view-2.1 {
+ execsql {
+ CREATE VIEW v2 AS SELECT * FROM t1 WHERE a>5
+ }; # No semicolon
+ execsql2 {
+ SELECT * FROM v2;
+ }
+} {x 7 a 8 b 9 c 10}
+do_test view-2.2 {
+ catchsql {
+ INSERT INTO v2 VALUES(1,2,3,4);
+ }
+} {1 {cannot modify v2 because it is a view}}
+do_test view-2.3 {
+ catchsql {
+ UPDATE v2 SET a=10 WHERE a=5;
+ }
+} {1 {cannot modify v2 because it is a view}}
+do_test view-2.4 {
+ catchsql {
+ DELETE FROM v2;
+ }
+} {1 {cannot modify v2 because it is a view}}
+do_test view-2.5 {
+ execsql {
+ INSERT INTO t1 VALUES(11,12,13,14);
+ SELECT * FROM v2 ORDER BY x;
+ }
+} {7 8 9 10 11 12 13 14}
+do_test view-2.6 {
+ execsql {
+ SELECT x FROM v2 WHERE a>10
+ }
+} {11}
+
+# Test that column name of views are generated correctly.
+#
+do_test view-3.1 {
+ execsql2 {
+ SELECT * FROM v1 LIMIT 1
+ }
+} {a 2 b 3}
+do_test view-3.2 {
+ execsql2 {
+ SELECT * FROM v2 LIMIT 1
+ }
+} {x 7 a 8 b 9 c 10}
+do_test view-3.3.1 {
+ execsql2 {
+ DROP VIEW v1;
+ CREATE VIEW v1 AS SELECT a AS 'xyz', b+c AS 'pqr', c-b FROM t1;
+ SELECT * FROM v1 LIMIT 1
+ }
+} {xyz 2 pqr 7 c-b 1}
+do_test view-3.3.2 {
+ execsql2 {
+ CREATE VIEW v1b AS SELECT t1.a, b+c, t1.c FROM t1;
+ SELECT * FROM v1b LIMIT 1
+ }
+} {a 2 b+c 7 c 4}
+
+ifcapable compound {
+do_test view-3.4 {
+ execsql2 {
+ CREATE VIEW v3 AS SELECT a FROM t1 UNION SELECT b FROM t1 ORDER BY b;
+ SELECT * FROM v3 LIMIT 4;
+ }
+} {a 2 a 3 a 5 a 6}
+do_test view-3.5 {
+ execsql2 {
+ CREATE VIEW v4 AS
+ SELECT a, b FROM t1
+ UNION
+ SELECT b AS 'x', a AS 'y' FROM t1
+ ORDER BY x, y;
+ SELECT b FROM v4 ORDER BY b LIMIT 4;
+ }
+} {b 2 b 3 b 5 b 6}
+} ;# ifcapable compound
+
+
+do_test view-4.1 {
+ catchsql {
+ DROP VIEW t1;
+ }
+} {1 {use DROP TABLE to delete table t1}}
+do_test view-4.2 {
+ execsql {
+ SELECT 1 FROM t1 LIMIT 1;
+ }
+} 1
+do_test view-4.3 {
+ catchsql {
+ DROP TABLE v1;
+ }
+} {1 {use DROP VIEW to delete view v1}}
+do_test view-4.4 {
+ execsql {
+ SELECT 1 FROM v1 LIMIT 1;
+ }
+} {1}
+do_test view-4.5 {
+ catchsql {
+ CREATE INDEX i1v1 ON v1(xyz);
+ }
+} {1 {views may not be indexed}}
+
+do_test view-5.1 {
+ execsql {
+ CREATE TABLE t2(y,a);
+ INSERT INTO t2 VALUES(22,2);
+ INSERT INTO t2 VALUES(33,3);
+ INSERT INTO t2 VALUES(44,4);
+ INSERT INTO t2 VALUES(55,5);
+ SELECT * FROM t2;
+ }
+} {22 2 33 3 44 4 55 5}
+do_test view-5.2 {
+ execsql {
+ CREATE VIEW v5 AS
+ SELECT t1.x AS v, t2.y AS w FROM t1 JOIN t2 USING(a);
+ SELECT * FROM v5;
+ }
+} {1 22 4 55}
+
+# Verify that the view v5 gets flattened. see sqliteFlattenSubquery().
+# This will only work if EXPLAIN is enabled.
+# Ticket #272
+#
+ifcapable {explain} {
+do_test view-5.3 {
+ lsearch [execsql {
+ EXPLAIN SELECT * FROM v5;
+ }] OpenEphemeral
+} {-1}
+do_test view-5.4 {
+ execsql {
+ SELECT * FROM v5 AS a, t2 AS b WHERE a.w=b.y;
+ }
+} {1 22 22 2 4 55 55 5}
+do_test view-5.5 {
+ lsearch [execsql {
+ EXPLAIN SELECT * FROM v5 AS a, t2 AS b WHERE a.w=b.y;
+ }] OpenEphemeral
+} {-1}
+do_test view-5.6 {
+ execsql {
+ SELECT * FROM t2 AS b, v5 AS a WHERE a.w=b.y;
+ }
+} {22 2 1 22 55 5 4 55}
+do_test view-5.7 {
+ lsearch [execsql {
+ EXPLAIN SELECT * FROM t2 AS b, v5 AS a WHERE a.w=b.y;
+ }] OpenEphemeral
+} {-1}
+do_test view-5.8 {
+ execsql {
+ SELECT * FROM t1 AS a, v5 AS b, t2 AS c WHERE a.x=b.v AND b.w=c.y;
+ }
+} {1 2 3 4 1 22 22 2 4 5 6 7 4 55 55 5}
+do_test view-5.9 {
+ lsearch [execsql {
+ EXPLAIN SELECT * FROM t1 AS a, v5 AS b, t2 AS c WHERE a.x=b.v AND b.w=c.y;
+ }] OpenEphemeral
+} {-1}
+} ;# endif explain
+
+do_test view-6.1 {
+ execsql {
+ SELECT min(x), min(a), min(b), min(c), min(a+b+c) FROM v2;
+ }
+} {7 8 9 10 27}
+do_test view-6.2 {
+ execsql {
+ SELECT max(x), max(a), max(b), max(c), max(a+b+c) FROM v2;
+ }
+} {11 12 13 14 39}
+
+do_test view-7.1 {
+ execsql {
+ CREATE TABLE test1(id integer primary key, a);
+ CREATE TABLE test2(id integer, b);
+ INSERT INTO test1 VALUES(1,2);
+ INSERT INTO test2 VALUES(1,3);
+ CREATE VIEW test AS
+ SELECT test1.id, a, b
+ FROM test1 JOIN test2 ON test2.id=test1.id;
+ SELECT * FROM test;
+ }
+} {1 2 3}
+do_test view-7.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM test;
+ }
+} {1 2 3}
+do_test view-7.3 {
+ execsql {
+ DROP VIEW test;
+ CREATE VIEW test AS
+ SELECT test1.id, a, b
+ FROM test1 JOIN test2 USING(id);
+ SELECT * FROM test;
+ }
+} {1 2 3}
+do_test view-7.4 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM test;
+ }
+} {1 2 3}
+do_test view-7.5 {
+ execsql {
+ DROP VIEW test;
+ CREATE VIEW test AS
+ SELECT test1.id, a, b
+ FROM test1 NATURAL JOIN test2;
+ SELECT * FROM test;
+ }
+} {1 2 3}
+do_test view-7.6 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM test;
+ }
+} {1 2 3}
+
+do_test view-8.1 {
+ execsql {
+ CREATE VIEW v6 AS SELECT pqr, xyz FROM v1;
+ SELECT * FROM v6 ORDER BY xyz;
+ }
+} {7 2 13 5 19 8 27 12}
+do_test view-8.2 {
+ db close
+ sqlite3 db test.db
+ execsql {
+ SELECT * FROM v6 ORDER BY xyz;
+ }
+} {7 2 13 5 19 8 27 12}
+do_test view-8.3 {
+ execsql {
+ CREATE VIEW v7 AS SELECT pqr+xyz AS a FROM v6;
+ SELECT * FROM v7 ORDER BY a;
+ }
+} {9 18 27 39}
+
+ifcapable subquery {
+ do_test view-8.4 {
+ execsql {
+ CREATE VIEW v8 AS SELECT max(cnt) AS mx FROM
+ (SELECT a%2 AS eo, count(*) AS cnt FROM t1 GROUP BY eo);
+ SELECT * FROM v8;
+ }
+ } 3
+ do_test view-8.5 {
+ execsql {
+ SELECT mx+10, mx*2 FROM v8;
+ }
+ } {13 6}
+ do_test view-8.6 {
+ execsql {
+ SELECT mx+10, pqr FROM v6, v8 WHERE xyz=2;
+ }
+ } {13 7}
+ do_test view-8.7 {
+ execsql {
+ SELECT mx+10, pqr FROM v6, v8 WHERE xyz>2;
+ }
+ } {13 13 13 19 13 27}
+} ;# ifcapable subquery
+
+# Tests for a bug found by Michiel de Wit involving ORDER BY in a VIEW.
+#
+do_test view-9.1 {
+ execsql {
+ INSERT INTO t2 SELECT * FROM t2 WHERE a<5;
+ INSERT INTO t2 SELECT * FROM t2 WHERE a<4;
+ INSERT INTO t2 SELECT * FROM t2 WHERE a<3;
+ SELECT DISTINCT count(*) FROM t2 GROUP BY a ORDER BY 1;
+ }
+} {1 2 4 8}
+do_test view-9.2 {
+ execsql {
+ SELECT DISTINCT count(*) FROM t2 GROUP BY a ORDER BY 1 LIMIT 3;
+ }
+} {1 2 4}
+do_test view-9.3 {
+ execsql {
+ CREATE VIEW v9 AS
+ SELECT DISTINCT count(*) FROM t2 GROUP BY a ORDER BY 1 LIMIT 3;
+ SELECT * FROM v9;
+ }
+} {1 2 4}
+do_test view-9.4 {
+ execsql {
+ SELECT * FROM v9 ORDER BY 1 DESC;
+ }
+} {4 2 1}
+do_test view-9.5 {
+ execsql {
+ CREATE VIEW v10 AS
+ SELECT DISTINCT a, count(*) FROM t2 GROUP BY a ORDER BY 2 LIMIT 3;
+ SELECT * FROM v10;
+ }
+} {5 1 4 2 3 4}
+do_test view-9.6 {
+ execsql {
+ SELECT * FROM v10 ORDER BY 1;
+ }
+} {3 4 4 2 5 1}
+
+# Tables with columns having peculiar quoted names used in views
+# Ticket #756.
+#
+do_test view-10.1 {
+ execsql {
+ CREATE TABLE t3("9" integer, [4] text);
+ INSERT INTO t3 VALUES(1,2);
+ CREATE VIEW v_t3_a AS SELECT a.[9] FROM t3 AS a;
+ CREATE VIEW v_t3_b AS SELECT "4" FROM t3;
+ SELECT * FROM v_t3_a;
+ }
+} {1}
+do_test view-10.2 {
+ execsql {
+ SELECT * FROM v_t3_b;
+ }
+} {2}
+
+do_test view-11.1 {
+ execsql {
+ CREATE TABLE t4(a COLLATE NOCASE);
+ INSERT INTO t4 VALUES('This');
+ INSERT INTO t4 VALUES('this');
+ INSERT INTO t4 VALUES('THIS');
+ SELECT * FROM t4 WHERE a = 'THIS';
+ }
+} {This this THIS}
+ifcapable subquery {
+ do_test view-11.2 {
+ execsql {
+ SELECT * FROM (SELECT * FROM t4) WHERE a = 'THIS';
+ }
+ } {This this THIS}
+}
+do_test view-11.3 {
+ execsql {
+ CREATE VIEW v11 AS SELECT * FROM t4;
+ SELECT * FROM v11 WHERE a = 'THIS';
+ }
+} {This this THIS}
+
+# Ticket #1270: Do not allow parameters in view definitions.
+#
+do_test view-12.1 {
+ catchsql {
+ CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=?
+ }
+} {1 {parameters are not allowed in views}}
+
+ifcapable attach {
+ do_test view-13.1 {
+ file delete -force test2.db
+ catchsql {
+ ATTACH 'test2.db' AS two;
+ CREATE TABLE two.t2(x,y);
+ CREATE VIEW v13 AS SELECT y FROM two.t2;
+ }
+ } {1 {view v13 cannot reference objects in database two}}
+}
+
+# Ticket #1658
+#
+do_test view-14.1 {
+ catchsql {
+ CREATE TEMP VIEW t1 AS SELECT a,b FROM t1;
+ SELECT * FROM temp.t1;
+ }
+} {1 {view t1 is circularly defined}}
+
+# Tickets #1688, #1709
+#
+do_test view-15.1 {
+ execsql2 {
+ CREATE VIEW v15 AS SELECT a AS x, b AS y FROM t1;
+ SELECT * FROM v15 LIMIT 1;
+ }
+} {x 2 y 3}
+do_test view-15.2 {
+ execsql2 {
+ SELECT x, y FROM v15 LIMIT 1
+ }
+} {x 2 y 3}
+
+do_test view-16.1 {
+ catchsql {
+ CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1;
+ }
+} {0 {}}
+do_test view-16.2 {
+ execsql {
+ SELECT sql FROM sqlite_master WHERE name='v1'
+ }
+} {{CREATE VIEW v1 AS SELECT a AS 'xyz', b+c AS 'pqr', c-b FROM t1}}
+do_test view-16.3 {
+ catchsql {
+ DROP VIEW IF EXISTS nosuchview
+ }
+} {0 {}}
+
+# correct error message when attempting to drop a view that does not
+# exist.
+#
+do_test view-17.1 {
+ catchsql {
+ DROP VIEW nosuchview
+ }
+} {1 {no such view: nosuchview}}
+do_test view-17.2 {
+ catchsql {
+ DROP VIEW main.nosuchview
+ }
+} {1 {no such view: main.nosuchview}}
+
+do_test view-18.1 {
+ execsql {
+ DROP VIEW t1;
+ DROP TABLE t1;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES(4, 5, 6);
+
+ CREATE VIEW vv1 AS SELECT * FROM t1;
+ CREATE VIEW vv2 AS SELECT * FROM vv1;
+ CREATE VIEW vv3 AS SELECT * FROM vv2;
+ CREATE VIEW vv4 AS SELECT * FROM vv3;
+ CREATE VIEW vv5 AS SELECT * FROM vv4;
+
+ SELECT * FROM vv5;
+ }
+} {1 2 3 4 5 6}
+
+
+finish_test
diff --git a/third_party/sqlite/test/vtab1.test b/third_party/sqlite/test/vtab1.test
new file mode 100755
index 0000000..3bf7286
--- /dev/null
+++ b/third_party/sqlite/test/vtab1.test
@@ -0,0 +1,1165 @@
+# 2006 June 10
+#
+# 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 file is creating and dropping virtual tables.
+#
+# $Id: vtab1.test,v 1.57 2008/08/01 17:51:47 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab||!schema_pragmas {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------
+# Organization of tests in this file:
+#
+# vtab1-1.*: Error conditions and other issues surrounding creation/connection
+# of a virtual module.
+# vtab1-2.*: Test sqlite3_declare_vtab() and the xConnect/xDisconnect methods.
+# vtab1-3.*: Table scans and WHERE clauses.
+# vtab1-4.*: Table scans and ORDER BY clauses.
+# vtab1-5.*: Test queries that include joins. This brings the
+# sqlite3_index_info.estimatedCost variable into play.
+# vtab1-6.*: Test UPDATE/INSERT/DELETE on vtables.
+# vtab1-7.*: Test sqlite3_last_insert_rowid().
+#
+# This file uses the "echo" module (see src/test8.c). Refer to comments
+# in that file for the special behaviour of the Tcl $echo_module variable.
+#
+# TODO:
+# * How to test the sqlite3_index_constraint_usage.omit field?
+# * vtab1-5.*
+#
+# vtab1-14.*: Test 'IN' constraints - i.e. "SELECT * FROM t1 WHERE id IN(...)"
+#
+
+
+#----------------------------------------------------------------------
+# Test cases vtab1.1.*
+#
+
+# We cannot create a virtual table if the module has not been registered.
+#
+do_test vtab1-1.1 {
+ explain {
+ CREATE VIRTUAL TABLE t1 USING echo;
+ }
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING echo;
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.2 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1
+ }
+} {}
+
+# Register the module
+register_echo_module [sqlite3_connection_pointer db]
+
+# Once a module has been registered, virtual tables using that module
+# may be created. However if a module xCreate() fails to call
+# sqlite3_declare_vtab() an error will be raised and the table not created.
+#
+# The "echo" module does not invoke sqlite3_declare_vtab() if it is
+# passed zero arguments.
+#
+do_test vtab1-1.3 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING echo;
+ }
+} {1 {vtable constructor did not declare schema: t1}}
+do_test vtab1-1.4 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1
+ }
+} {}
+
+# The "echo" module xCreate method returns an error and does not create
+# the virtual table if it is passed an argument that does not correspond
+# to an existing real table in the same database.
+#
+do_test vtab1-1.5 {
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING echo(no_such_table);
+ }
+} {1 {vtable constructor failed: t1}}
+do_test vtab1-1.6 {
+ execsql {
+ SELECT name FROM sqlite_master ORDER BY 1
+ }
+} {}
+
+# Ticket #2156. Using the sqlite3_prepare_v2() API, make sure that
+# a CREATE VIRTUAL TABLE statement can be used multiple times.
+#
+do_test vtab1-1.2152.1 {
+ set DB [sqlite3_connection_pointer db]
+ set sql {CREATE VIRTUAL TABLE t2152a USING echo(t2152b)}
+ set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+ sqlite3_step $STMT
+} SQLITE_ERROR
+do_test vtab-1.2152.2 {
+ sqlite3_reset $STMT
+ sqlite3_step $STMT
+} SQLITE_ERROR
+do_test vtab-1.2152.3 {
+ sqlite3_reset $STMT
+ db eval {CREATE TABLE t2152b(x,y)}
+ sqlite3_step $STMT
+} SQLITE_DONE
+do_test vtab-1.2152.4 {
+ sqlite3_finalize $STMT
+ db eval {DROP TABLE t2152a; DROP TABLE t2152b}
+} {}
+
+# Test to make sure nothing goes wrong and no memory is leaked if we
+# select an illegal table-name (i.e a reserved name or the name of a
+# table that already exists).
+#
+do_test vtab1-1.7 {
+ catchsql {
+ CREATE VIRTUAL TABLE sqlite_master USING echo;
+ }
+} {1 {object name reserved for internal use: sqlite_master}}
+do_test vtab1-1.8 {
+ catchsql {
+ CREATE TABLE treal(a, b, c);
+ CREATE VIRTUAL TABLE treal USING echo(treal);
+ }
+} {1 {table treal already exists}}
+do_test vtab1-1.9 {
+ execsql {
+ DROP TABLE treal;
+ SELECT name FROM sqlite_master ORDER BY 1
+ }
+} {}
+
+do_test vtab1-1.10 {
+ execsql {
+ CREATE TABLE treal(a, b, c);
+ CREATE VIRTUAL TABLE techo USING echo(treal);
+ }
+ db close
+ sqlite3 db test.db
+ catchsql {
+ SELECT * FROM techo;
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.11 {
+ catchsql {
+ INSERT INTO techo VALUES(1, 2, 3);
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.12 {
+ catchsql {
+ UPDATE techo SET a = 10;
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.13 {
+ catchsql {
+ DELETE FROM techo;
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.14 {
+ catchsql {
+ PRAGMA table_info(techo)
+ }
+} {1 {no such module: echo}}
+do_test vtab1-1.15 {
+ catchsql {
+ DROP TABLE techo;
+ }
+} {1 {no such module: echo}}
+
+register_echo_module [sqlite3_connection_pointer db]
+register_echo_module [sqlite3_connection_pointer db]
+
+# Test an error message returned from a v-table constructor.
+#
+do_test vtab1-1.16 {
+ execsql {
+ DROP TABLE techo;
+ CREATE TABLE logmsg(log);
+ }
+ catchsql {
+ CREATE VIRTUAL TABLE techo USING echo(treal, logmsg);
+ }
+} {1 {table 'logmsg' already exists}}
+
+do_test vtab1-1.17 {
+ execsql {
+ DROP TABLE treal;
+ DROP TABLE logmsg;
+ SELECT sql FROM sqlite_master;
+ }
+} {}
+
+#----------------------------------------------------------------------
+# Test cases vtab1.2.*
+#
+# At this point, the database is completely empty. The echo module
+# has already been registered.
+
+# If a single argument is passed to the echo module during table
+# creation, it is assumed to be the name of a table in the same
+# database. The echo module attempts to set the schema of the
+# new virtual table to be the same as the existing database table.
+#
+do_test vtab1-2.1 {
+ execsql {
+ CREATE TABLE template(a, b, c);
+ }
+ execsql { PRAGMA table_info(template); }
+} [list \
+ 0 a {} 0 {} 0 \
+ 1 b {} 0 {} 0 \
+ 2 c {} 0 {} 0 \
+]
+do_test vtab1-2.2 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING echo(template);
+ }
+ execsql { PRAGMA table_info(t1); }
+} [list \
+ 0 a {} 0 {} 0 \
+ 1 b {} 0 {} 0 \
+ 2 c {} 0 {} 0 \
+]
+
+# Test that the database can be unloaded. This should invoke the xDisconnect()
+# callback for the successfully create virtual table (t1).
+#
+do_test vtab1-2.3 {
+ set echo_module [list]
+ db close
+ set echo_module
+} [list xDisconnect]
+
+# Re-open the database. This should not cause any virtual methods to
+# be called. The invocation of xConnect() is delayed until the virtual
+# table schema is first required by the compiler.
+#
+do_test vtab1-2.4 {
+ set echo_module [list]
+ sqlite3 db test.db
+ db cache size 0
+ set echo_module
+} {}
+
+# Try to query the virtual table schema. This should fail, as the
+# echo module has not been registered with this database connection.
+#
+do_test vtab1.2.6 {
+ catchsql { PRAGMA table_info(t1); }
+} {1 {no such module: echo}}
+
+# Register the module
+register_echo_module [sqlite3_connection_pointer db]
+
+# Try to query the virtual table schema again. This time it should
+# invoke the xConnect method and succeed.
+#
+do_test vtab1.2.7 {
+ execsql { PRAGMA table_info(t1); }
+} [list \
+ 0 a {} 0 {} 0 \
+ 1 b {} 0 {} 0 \
+ 2 c {} 0 {} 0 \
+]
+do_test vtab1.2.8 {
+ set echo_module
+} {xConnect echo main t1 template}
+
+# Drop table t1. This should cause the xDestroy (but not xDisconnect) method
+# to be invoked.
+do_test vtab1-2.5 {
+ set echo_module ""
+ execsql {
+ DROP TABLE t1;
+ }
+ set echo_module
+} {xDestroy}
+
+do_test vtab1-2.6 {
+ execsql {
+ PRAGMA table_info(t1);
+ }
+} {}
+do_test vtab1-2.7 {
+ execsql {
+ SELECT sql FROM sqlite_master;
+ }
+} [list {CREATE TABLE template(a, b, c)}]
+# Clean up other test artifacts:
+do_test vtab1-2.8 {
+ execsql {
+ DROP TABLE template;
+ SELECT sql FROM sqlite_master;
+ }
+} [list]
+
+#----------------------------------------------------------------------
+# Test case vtab1-3 test table scans and the echo module's
+# xBestIndex/xFilter handling of WHERE conditions.
+
+do_test vtab1-3.1 {
+ set echo_module ""
+ execsql {
+ CREATE TABLE treal(a INTEGER, b INTEGER, c);
+ CREATE INDEX treal_idx ON treal(b);
+ CREATE VIRTUAL TABLE t1 USING echo(treal);
+ }
+ set echo_module
+} [list xCreate echo main t1 treal \
+ xSync echo(treal) \
+ xCommit echo(treal) \
+]
+
+# Test that a SELECT on t1 doesn't crash. No rows are returned
+# because the underlying real table is currently empty.
+#
+do_test vtab1-3.2 {
+ execsql {
+ SELECT a, b, c FROM t1;
+ }
+} {}
+
+# Put some data into the table treal. Then try a few simple SELECT
+# statements on t1.
+#
+do_test vtab1-3.3 {
+ execsql {
+ INSERT INTO treal VALUES(1, 2, 3);
+ INSERT INTO treal VALUES(4, 5, 6);
+ SELECT * FROM t1;
+ }
+} {1 2 3 4 5 6}
+do_test vtab1-3.4 {
+ execsql {
+ SELECT a FROM t1;
+ }
+} {1 4}
+do_test vtab1-3.5 {
+ execsql {
+ SELECT rowid FROM t1;
+ }
+} {1 2}
+do_test vtab1-3.6 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1;
+ }
+} {1 2 3 4 5 6}
+do_test vtab1-3.7 {
+ execsql {
+ SELECT rowid, * FROM t1;
+ }
+} {1 1 2 3 2 4 5 6}
+do_test vtab1-3.8 {
+ execsql {
+ SELECT a AS d, b AS e, c AS f FROM t1;
+ }
+} {1 2 3 4 5 6}
+
+# Execute some SELECT statements with WHERE clauses on the t1 table.
+# Then check the echo_module variable (written to by the module methods
+# in test8.c) to make sure the xBestIndex() and xFilter() methods were
+# called correctly.
+#
+do_test vtab1-3.8 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xFilter {SELECT rowid, * FROM 'treal'} ]
+do_test vtab1-3.9 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1 WHERE b = 5;
+ }
+} {4 5 6}
+do_test vtab1-3.10 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b = ?} \
+ xFilter {SELECT rowid, * FROM 'treal' WHERE b = ?} 5 ]
+do_test vtab1-3.10 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1 WHERE b >= 5 AND b <= 10;
+ }
+} {4 5 6}
+do_test vtab1-3.11 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \
+ xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 5 10 ]
+do_test vtab1-3.12 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1 WHERE b BETWEEN 2 AND 10;
+ }
+} {1 2 3 4 5 6}
+do_test vtab1-3.13 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \
+ xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 2 10 ]
+
+# Add a function for the MATCH operator. Everything always matches!
+#proc test_match {lhs rhs} {
+# lappend ::echo_module MATCH $lhs $rhs
+# return 1
+#}
+#db function match test_match
+
+set echo_module ""
+do_test vtab1-3.12 {
+ set echo_module ""
+ catchsql {
+ SELECT * FROM t1 WHERE a MATCH 'string';
+ }
+} {1 {unable to use function MATCH in the requested context}}
+do_test vtab1-3.13 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xFilter {SELECT rowid, * FROM 'treal'}]
+ifcapable subquery {
+# The echo module uses a subquery internally to implement the MATCH operator.
+do_test vtab1-3.14 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM t1 WHERE b MATCH 'string';
+ }
+} {}
+do_test vtab1-3.15 {
+ set echo_module
+} [list xBestIndex \
+ {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \
+ xFilter \
+ {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \
+ string ]
+}; #ifcapable subquery
+
+#----------------------------------------------------------------------
+# Test case vtab1-3 test table scans and the echo module's
+# xBestIndex/xFilter handling of ORDER BY clauses.
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+do_test vtab1-4.1 {
+ set echo_module ""
+ cksort {
+ SELECT b FROM t1 ORDER BY b;
+ }
+} {2 5 nosort}
+do_test vtab1-4.2 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b ASC} \
+ xFilter {SELECT rowid, * FROM 'treal' ORDER BY b ASC} ]
+do_test vtab1-4.3 {
+ set echo_module ""
+ cksort {
+ SELECT b FROM t1 ORDER BY b DESC;
+ }
+} {5 2 nosort}
+do_test vtab1-4.4 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b DESC} \
+ xFilter {SELECT rowid, * FROM 'treal' ORDER BY b DESC} ]
+do_test vtab1-4.3 {
+ set echo_module ""
+ cksort {
+ SELECT b FROM t1 ORDER BY b||'';
+ }
+} {2 5 sort}
+do_test vtab1-4.4 {
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xFilter {SELECT rowid, * FROM 'treal'} ]
+
+execsql {
+ DROP TABLE t1;
+ DROP TABLE treal;
+}
+
+#----------------------------------------------------------------------
+# Test cases vtab1-5 test SELECT queries that include joins on virtual
+# tables.
+
+proc filter {log} {
+ set out [list]
+ for {set ii 0} {$ii < [llength $log]} {incr ii} {
+ if {[lindex $log $ii] eq "xFilter"} {
+ lappend out xFilter
+ lappend out [lindex $log [expr $ii+1]]
+ }
+ }
+ return $out
+}
+
+do_test vtab1-5-1 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE TABLE t2(d, e, f);
+ INSERT INTO t1 VALUES(1, 'red', 'green');
+ INSERT INTO t1 VALUES(2, 'blue', 'black');
+ INSERT INTO t2 VALUES(1, 'spades', 'clubs');
+ INSERT INTO t2 VALUES(2, 'hearts', 'diamonds');
+ CREATE VIRTUAL TABLE et1 USING echo(t1);
+ CREATE VIRTUAL TABLE et2 USING echo(t2);
+ }
+} {}
+
+do_test vtab1-5-2 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM et1, et2;
+ }
+} [list \
+ 1 red green 1 spades clubs \
+ 1 red green 2 hearts diamonds \
+ 2 blue black 1 spades clubs \
+ 2 blue black 2 hearts diamonds \
+]
+do_test vtab1-5-3 {
+ filter $echo_module
+} [list \
+ xFilter {SELECT rowid, * FROM 't1'} \
+ xFilter {SELECT rowid, * FROM 't2'} \
+ xFilter {SELECT rowid, * FROM 't2'} \
+]
+do_test vtab1-5-4 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM et1, et2 WHERE et2.d = 2;
+ }
+} [list \
+ 1 red green 2 hearts diamonds \
+ 2 blue black 2 hearts diamonds \
+]
+do_test vtab1-5-5 {
+ filter $echo_module
+} [list \
+ xFilter {SELECT rowid, * FROM 't1'} \
+ xFilter {SELECT rowid, * FROM 't2'} \
+ xFilter {SELECT rowid, * FROM 't2'} \
+]
+do_test vtab1-5-6 {
+ execsql {
+ CREATE INDEX i1 ON t2(d);
+ }
+
+ db close
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+
+ set ::echo_module ""
+ execsql {
+ SELECT * FROM et1, et2 WHERE et2.d = 2;
+ }
+} [list \
+ 1 red green 2 hearts diamonds \
+ 2 blue black 2 hearts diamonds \
+]
+do_test vtab1-5-7 {
+ filter $::echo_module
+} [list \
+ xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \
+ xFilter {SELECT rowid, * FROM 't1'} \
+]
+
+execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP TABLE et1;
+ DROP TABLE et2;
+}
+
+#----------------------------------------------------------------------
+# Test cases vtab1-6 test INSERT, UPDATE and DELETE operations
+# on virtual tables.
+do_test vtab1-6-1 {
+ execsql { SELECT sql FROM sqlite_master }
+} {}
+do_test vtab1-6-2 {
+ execsql {
+ CREATE TABLE treal(a PRIMARY KEY, b, c);
+ CREATE VIRTUAL TABLE techo USING echo(treal);
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+} {treal techo}
+do_test vtab1-6-3.1.1 {
+ execsql {
+ PRAGMA count_changes=ON;
+ INSERT INTO techo VALUES(1, 2, 3);
+ }
+} {1}
+do_test vtab1-6-3.1.2 {
+ db changes
+} {1}
+do_test vtab1-6-3.2 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {1 2 3}
+do_test vtab1-6-4.1 {
+ execsql {
+ UPDATE techo SET a = 5;
+ }
+ db changes
+} {1}
+do_test vtab1-6-4.2 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {5 2 3}
+do_test vtab1-6-4.3 {
+ execsql {
+ UPDATE techo SET a=6 WHERE a<0;
+ }
+ db changes
+} {0}
+do_test vtab1-6-4.4 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {5 2 3}
+
+do_test vtab1-6-5.1 {
+ execsql {
+ UPDATE techo set a = a||b||c;
+ }
+ db changes
+} {1}
+do_test vtab1-6-5.2 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {523 2 3}
+
+do_test vtab1-6-6.1 {
+ execsql {
+ UPDATE techo set rowid = 10;
+ }
+ db changes
+} {1}
+do_test vtab1-6-6.2 {
+ execsql {
+ SELECT rowid FROM techo;
+ }
+} {10}
+
+do_test vtab1-6-7.1.1 {
+ execsql {
+ INSERT INTO techo VALUES(11,12,13);
+ }
+} {1}
+do_test vtab1-6-7.1.2 {
+ db changes
+} {1}
+do_test vtab1-6-7.2 {
+ execsql {
+ SELECT * FROM techo ORDER BY a;
+ }
+} {11 12 13 523 2 3}
+do_test vtab1-6-7.3 {
+ execsql {
+ UPDATE techo SET b=b+1000
+ }
+ db changes
+} {2}
+do_test vtab1-6-7.4 {
+ execsql {
+ SELECT * FROM techo ORDER BY a;
+ }
+} {11 1012 13 523 1002 3}
+
+
+do_test vtab1-6-8.1 {
+ execsql {
+ DELETE FROM techo WHERE a=5;
+ }
+ db changes
+} {0}
+do_test vtab1-6-8.2 {
+ execsql {
+ SELECT * FROM techo ORDER BY a;
+ }
+} {11 1012 13 523 1002 3}
+do_test vtab1-6-8.3 {
+ execsql {
+ DELETE FROM techo;
+ }
+ db changes
+} {2}
+do_test vtab1-6-8.4 {
+ execsql {
+ SELECT * FROM techo ORDER BY a;
+ }
+} {}
+execsql {PRAGMA count_changes=OFF}
+
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+execsql {
+ CREATE TABLE techo(a PRIMARY KEY, b, c);
+} db2
+proc check_echo_table {tn} {
+ set ::data1 [execsql {SELECT rowid, * FROM techo}]
+ set ::data2 [execsql {SELECT rowid, * FROM techo} db2]
+ do_test $tn {
+ string equal $::data1 $::data2
+ } 1
+}
+set tn 0
+foreach stmt [list \
+ {INSERT INTO techo VALUES('abc', 'def', 'ghi')} \
+ {INSERT INTO techo SELECT a||'.'||rowid, b, c FROM techo} \
+ {INSERT INTO techo SELECT a||'x'||rowid, b, c FROM techo} \
+ {INSERT INTO techo SELECT a||'y'||rowid, b, c FROM techo} \
+ {DELETE FROM techo WHERE (oid % 3) = 0} \
+ {UPDATE techo set rowid = 100 WHERE rowid = 1} \
+ {INSERT INTO techo(a, b) VALUES('hello', 'world')} \
+ {DELETE FROM techo} \
+] {
+ execsql $stmt
+ execsql $stmt db2
+ check_echo_table vtab1-6.8.[incr tn]
+}
+
+db2 close
+
+
+
+#----------------------------------------------------------------------
+# Test cases vtab1-7 tests that the value returned by
+# sqlite3_last_insert_rowid() is set correctly when rows are inserted
+# into virtual tables.
+do_test vtab1.7-1 {
+ execsql {
+ CREATE TABLE real_abc(a PRIMARY KEY, b, c);
+ CREATE VIRTUAL TABLE echo_abc USING echo(real_abc);
+ }
+} {}
+do_test vtab1.7-2 {
+ execsql {
+ INSERT INTO echo_abc VALUES(1, 2, 3);
+ SELECT last_insert_rowid();
+ }
+} {1}
+do_test vtab1.7-3 {
+ execsql {
+ INSERT INTO echo_abc(rowid) VALUES(31427);
+ SELECT last_insert_rowid();
+ }
+} {31427}
+do_test vtab1.7-4 {
+ execsql {
+ INSERT INTO echo_abc SELECT a||'.v2', b, c FROM echo_abc;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-5 {
+ execsql {
+ SELECT rowid, a, b, c FROM echo_abc
+ }
+} [list 1 1 2 3 \
+ 31427 {} {} {} \
+ 31428 1.v2 2 3 \
+ 31429 {} {} {} \
+]
+
+# Now test that DELETE and UPDATE operations do not modify the value.
+do_test vtab1.7-6 {
+ execsql {
+ UPDATE echo_abc SET c = 5 WHERE b = 2;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-7 {
+ execsql {
+ UPDATE echo_abc SET rowid = 5 WHERE rowid = 1;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-8 {
+ execsql {
+ DELETE FROM echo_abc WHERE b = 2;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-9 {
+ execsql {
+ SELECT rowid, a, b, c FROM echo_abc
+ }
+} [list 31427 {} {} {} \
+ 31429 {} {} {} \
+]
+do_test vtab1.7-10 {
+ execsql {
+ DELETE FROM echo_abc WHERE b = 2;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-11 {
+ execsql {
+ SELECT rowid, a, b, c FROM real_abc
+ }
+} [list 31427 {} {} {} \
+ 31429 {} {} {} \
+]
+do_test vtab1.7-12 {
+ execsql {
+ DELETE FROM echo_abc;
+ SELECT last_insert_rowid();
+ }
+} {31429}
+do_test vtab1.7-13 {
+ execsql {
+ SELECT rowid, a, b, c FROM real_abc
+ }
+} {}
+
+ifcapable attach {
+ do_test vtab1.8-1 {
+ set echo_module ""
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ CREATE VIRTUAL TABLE aux.e2 USING echo(real_abc);
+ }
+ set echo_module
+ } [list xCreate echo aux e2 real_abc \
+ xSync echo(real_abc) \
+ xCommit echo(real_abc) \
+ ]
+}
+do_test vtab1.8-2 {
+ catchsql {
+ DROP TABLE aux.e2;
+ }
+ execsql {
+ DROP TABLE treal;
+ DROP TABLE techo;
+ DROP TABLE echo_abc;
+ DROP TABLE real_abc;
+ }
+} {}
+
+do_test vtab1.9-1 {
+ set echo_module ""
+ execsql {
+ CREATE TABLE r(a, b, c);
+ CREATE VIRTUAL TABLE e USING echo(r, e_log);
+ SELECT name FROM sqlite_master;
+ }
+} {r e e_log}
+do_test vtab1.9-2 {
+ execsql {
+ DROP TABLE e;
+ SELECT name FROM sqlite_master;
+ }
+} {r}
+
+do_test vtab1.9-3 {
+ set echo_module ""
+ execsql {
+ CREATE VIRTUAL TABLE e USING echo(r, e_log, virtual 1 2 3 varchar(32));
+ }
+ set echo_module
+} [list \
+ xCreate echo main e r e_log {virtual 1 2 3 varchar(32)} \
+ xSync echo(r) \
+ xCommit echo(r) \
+]
+
+do_test vtab1.10-1 {
+ execsql {
+ CREATE TABLE del(d);
+ CREATE VIRTUAL TABLE e2 USING echo(del);
+ }
+ db close
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ DROP TABLE del;
+ }
+ catchsql {
+ SELECT * FROM e2;
+ }
+} {1 {vtable constructor failed: e2}}
+do_test vtab1.10-2 {
+ set rc [catch {
+ set ptr [sqlite3_connection_pointer db]
+ sqlite3_declare_vtab $ptr {CREATE TABLE abc(a, b, c)}
+ } msg]
+ list $rc $msg
+} {1 {library routine called out of sequence}}
+do_test vtab1.10-3 {
+ set ::echo_module_begin_fail r
+ catchsql {
+ INSERT INTO e VALUES(1, 2, 3);
+ }
+} {1 {SQL logic error or missing database}}
+do_test vtab1.10-4 {
+ catch {execsql {
+ EXPLAIN SELECT * FROM e WHERE rowid = 2;
+ EXPLAIN QUERY PLAN SELECT * FROM e WHERE rowid = 2 ORDER BY rowid;
+ }}
+} {0}
+
+do_test vtab1.10-5 {
+ set echo_module ""
+ execsql {
+ SELECT * FROM e WHERE rowid||'' MATCH 'pattern';
+ }
+ set echo_module
+} [list \
+ xBestIndex {SELECT rowid, * FROM 'r'} \
+ xFilter {SELECT rowid, * FROM 'r'} \
+]
+proc match_func {args} {return ""}
+do_test vtab1.10-6 {
+ set echo_module ""
+ db function match match_func
+ execsql {
+ SELECT * FROM e WHERE match('pattern', rowid, 'pattern2');
+ }
+ set echo_module
+} [list \
+ xBestIndex {SELECT rowid, * FROM 'r'} \
+ xFilter {SELECT rowid, * FROM 'r'} \
+]
+
+
+# Testing the xFindFunction interface
+#
+catch {rename ::echo_glob_overload {}}
+do_test vtab1.11-1 {
+ execsql {
+ INSERT INTO r(a,b,c) VALUES(1,'?',99);
+ INSERT INTO r(a,b,c) VALUES(2,3,99);
+ SELECT a GLOB b FROM e
+ }
+} {1 0}
+proc ::echo_glob_overload {a b} {
+ return [list $b $a]
+}
+do_test vtab1.11-2 {
+ execsql {
+ SELECT a like 'b' FROM e
+ }
+} {0 0}
+do_test vtab1.11-3 {
+ execsql {
+ SELECT a glob '2' FROM e
+ }
+} {{1 2} {2 2}}
+do_test vtab1.11-4 {
+ execsql {
+ SELECT glob('2',a) FROM e
+ }
+} {0 1}
+do_test vtab1.11-5 {
+ execsql {
+ SELECT glob(a,'2') FROM e
+ }
+} {{2 1} {2 2}}
+
+#----------------------------------------------------------------------
+# Test the outcome if a constraint is encountered half-way through
+# a multi-row INSERT that is inside a transaction
+#
+do_test vtab1.12-1 {
+ execsql {
+ CREATE TABLE b(a, b, c);
+ CREATE TABLE c(a UNIQUE, b, c);
+ INSERT INTO b VALUES(1, 'A', 'B');
+ INSERT INTO b VALUES(2, 'C', 'D');
+ INSERT INTO b VALUES(3, 'E', 'F');
+ INSERT INTO c VALUES(3, 'G', 'H');
+ CREATE VIRTUAL TABLE echo_c USING echo(c);
+ }
+} {}
+
+# First test outside of a transaction.
+do_test vtab1.12-2 {
+ catchsql { INSERT INTO echo_c SELECT * FROM b; }
+} {1 {echo-vtab-error: column a is not unique}}
+do_test vtab1.12-2.1 {
+ sqlite3_errmsg db
+} {echo-vtab-error: column a is not unique}
+do_test vtab1.12-3 {
+ execsql { SELECT * FROM c }
+} {3 G H}
+
+# Now the real test - wrapped in a transaction.
+do_test vtab1.12-4 {
+ execsql {BEGIN}
+ catchsql { INSERT INTO echo_c SELECT * FROM b; }
+} {1 {echo-vtab-error: column a is not unique}}
+do_test vtab1.12-5 {
+ execsql { SELECT * FROM c }
+} {3 G H}
+do_test vtab1.12-6 {
+ execsql { COMMIT }
+ execsql { SELECT * FROM c }
+} {3 G H}
+
+# At one point (ticket #2759), a WHERE clause of the form "<column> IS NULL"
+# on a virtual table was causing an assert() to fail in the compiler.
+#
+# "IS NULL" clauses should not be passed through to the virtual table
+# implementation. They are handled by SQLite after the vtab returns its
+# data.
+#
+do_test vtab1.13-1 {
+ execsql {
+ SELECT * FROM echo_c WHERE a IS NULL
+ }
+} {}
+do_test vtab1.13-2 {
+ execsql {
+ INSERT INTO c VALUES(NULL, 15, 16);
+ SELECT * FROM echo_c WHERE a IS NULL
+ }
+} {{} 15 16}
+do_test vtab1.13-3 {
+ execsql {
+ INSERT INTO c VALUES(15, NULL, 16);
+ SELECT * FROM echo_c WHERE b IS NULL
+ }
+} {15 {} 16}
+do_test vtab1.13-3 {
+ execsql {
+ SELECT * FROM echo_c WHERE b IS NULL AND a = 15;
+ }
+} {15 {} 16}
+
+
+do_test vtab1-14.1 {
+ execsql { DELETE FROM c }
+ set echo_module ""
+ execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}]
+
+do_test vtab1-14.2 {
+ set echo_module ""
+ execsql { SELECT * FROM echo_c WHERE rowid = 1 }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'c' WHERE rowid = ?} xFilter {SELECT rowid, * FROM 'c' WHERE rowid = ?} 1]
+
+do_test vtab1-14.3 {
+ set echo_module ""
+ execsql { SELECT * FROM echo_c WHERE a = 1 }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'c' WHERE a = ?} xFilter {SELECT rowid, * FROM 'c' WHERE a = ?} 1]
+
+do_test vtab1-14.4 {
+ set echo_module ""
+ execsql { SELECT * FROM echo_c WHERE a IN (1, 2) }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}]
+
+do_test vtab1-15.1 {
+ execsql {
+ CREATE TABLE t1(a, b, c);
+ CREATE VIRTUAL TABLE echo_t1 USING echo(t1);
+ }
+} {}
+do_test vtab1-15.2 {
+ execsql {
+ INSERT INTO echo_t1(rowid) VALUES(45);
+ SELECT rowid, * FROM echo_t1;
+ }
+} {45 {} {} {}}
+do_test vtab1-15.3 {
+ execsql {
+ INSERT INTO echo_t1(rowid) VALUES(NULL);
+ SELECT rowid, * FROM echo_t1;
+ }
+} {45 {} {} {} 46 {} {} {}}
+do_test vtab1-15.4 {
+ catchsql {
+ INSERT INTO echo_t1(rowid) VALUES('new rowid');
+ }
+} {1 {datatype mismatch}}
+
+# The following tests - vtab1-16.* - are designed to test that setting
+# sqlite3_vtab.zErrMsg variable can be used by the vtab interface to
+# return an error message to the user.
+#
+do_test vtab1-16.1 {
+ execsql {
+ CREATE TABLE t2(a PRIMARY KEY, b, c);
+ INSERT INTO t2 VALUES(1, 2, 3);
+ INSERT INTO t2 VALUES(4, 5, 6);
+ CREATE VIRTUAL TABLE echo_t2 USING echo(t2);
+ }
+} {}
+
+set tn 2
+foreach method [list \
+ xBestIndex \
+ xOpen \
+ xFilter \
+ xNext \
+ xColumn \
+ xRowid \
+] {
+ do_test vtab1-16.$tn {
+ set echo_module_fail($method,t2) "the $method method has failed"
+ catchsql { SELECT rowid, * FROM echo_t2 WHERE a >= 1 }
+ } "1 {echo-vtab-error: the $method method has failed}"
+ unset echo_module_fail($method,t2)
+ incr tn
+}
+
+foreach method [list \
+ xUpdate \
+ xBegin \
+ xSync \
+] {
+ do_test vtab1-16.$tn {
+ set echo_module_fail($method,t2) "the $method method has failed"
+ catchsql { INSERT INTO echo_t2 VALUES(7, 8, 9) }
+ } "1 {echo-vtab-error: the $method method has failed}"
+ unset echo_module_fail($method,t2)
+ incr tn
+}
+
+do_test vtab1-16.$tn {
+ set echo_module_fail(xRename,t2) "the xRename method has failed"
+ catchsql { ALTER TABLE echo_t2 RENAME TO another_name }
+} "1 {echo-vtab-error: the xRename method has failed}"
+unset echo_module_fail(xRename,t2)
+incr tn
+
+unset -nocomplain echo_module_begin_fail
+finish_test
diff --git a/third_party/sqlite/test/vtab2.test b/third_party/sqlite/test/vtab2.test
new file mode 100755
index 0000000..5e7ac42
--- /dev/null
+++ b/third_party/sqlite/test/vtab2.test
@@ -0,0 +1,108 @@
+# 2006 June 10
+#
+# 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.
+#
+# $Id: vtab2.test,v 1.8 2008/01/31 15:53:46 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab||!schema_pragmas {
+ finish_test
+ return
+}
+
+register_schema_module [sqlite3_connection_pointer db]
+do_test vtab2-1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE schema USING schema;
+ SELECT * FROM schema;
+ }
+} [list \
+ main schema 0 database {} 0 {} 0 \
+ main schema 1 tablename {} 0 {} 0 \
+ main schema 2 cid {} 0 {} 0 \
+ main schema 3 name {} 0 {} 0 \
+ main schema 4 type {} 0 {} 0 \
+ main schema 5 not_null {} 0 {} 0 \
+ main schema 6 dflt_value {} 0 {} 0 \
+ main schema 7 pk {} 0 {} 0 \
+]
+
+# See ticket #2230.
+#
+do_test vtab2-1.2 {
+ execsql {
+ SELECT length(tablename) FROM schema GROUP by tablename;
+ }
+} {6}
+do_test vtab2-1.3 {
+ execsql {
+ SELECT tablename FROM schema GROUP by length(tablename);
+ }
+} {schema}
+do_test vtab2-1.4 {
+ execsql {
+ SELECT length(tablename) FROM schema GROUP by length(tablename);
+ }
+} {6}
+
+register_tclvar_module [sqlite3_connection_pointer db]
+do_test vtab2-2.1 {
+ set ::abc 123
+ execsql {
+ CREATE VIRTUAL TABLE vars USING tclvar;
+ SELECT * FROM vars WHERE name='abc';
+ }
+} [list abc "" 123]
+do_test vtab2-2.2 {
+ set A(1) 1
+ set A(2) 4
+ set A(3) 9
+ execsql {
+ SELECT * FROM vars WHERE name='A';
+ }
+} [list A 1 1 A 2 4 A 3 9]
+unset -nocomplain result
+unset -nocomplain var
+set result {}
+foreach var [lsort [info vars tcl_*]] {
+ catch {lappend result $var [set $var]}
+}
+do_test vtab2-2.3 {
+ execsql {
+ SELECT name, value FROM vars
+ WHERE name MATCH 'tcl_*' AND arrayname = ''
+ ORDER BY name;
+ }
+} $result
+unset result
+unset var
+
+# Ticket #2894.
+#
+# Make sure we do call Column(), and Rowid() methods of
+# a virtual table when that table is in a LEFT JOIN.
+#
+do_test vtab2-3.1 {
+ execsql {
+ SELECT * FROM schema WHERE dflt_value IS NULL LIMIT 1
+ }
+} {main schema 0 database {} 0 {} 0}
+do_test vtab2-3.2 {
+ execsql {
+ SELECT *, b.rowid
+ FROM schema a LEFT JOIN schema b ON a.dflt_value=b.dflt_value
+ WHERE a.rowid=1
+ }
+} {main schema 0 database {} 0 {} 0 {} {} {} {} {} {} {} {} {}}
+
+finish_test
diff --git a/third_party/sqlite/test/vtab3.test b/third_party/sqlite/test/vtab3.test
new file mode 100755
index 0000000..ebf8369
--- /dev/null
+++ b/third_party/sqlite/test/vtab3.test
@@ -0,0 +1,140 @@
+# 2006 June 10
+#
+# 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 file is the authorisation callback and virtual tables.
+#
+# $Id: vtab3.test,v 1.3 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab||!auth {
+ finish_test
+ return
+}
+
+set ::auth_fail 0
+set ::auth_log [list]
+set ::auth_filter [list SQLITE_READ SQLITE_UPDATE SQLITE_SELECT SQLITE_PRAGMA]
+
+proc auth {code arg1 arg2 arg3 arg4} {
+ if {[lsearch $::auth_filter $code]>-1} {
+ return SQLITE_OK
+ }
+ lappend ::auth_log $code $arg1 $arg2 $arg3 $arg4
+ incr ::auth_fail -1
+ if {$::auth_fail == 0} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+}
+
+do_test vtab3-1.1 {
+ execsql {
+ CREATE TABLE elephant(
+ name VARCHAR(32),
+ color VARCHAR(16),
+ age INTEGER,
+ UNIQUE(name, color)
+ );
+ }
+} {}
+
+
+do_test vtab3-1.2 {
+ register_echo_module [sqlite3_connection_pointer db]
+ db authorizer ::auth
+ execsql {
+ CREATE VIRTUAL TABLE pachyderm USING echo(elephant);
+ }
+ set ::auth_log
+} [list \
+ SQLITE_INSERT sqlite_master {} main {} \
+ SQLITE_CREATE_VTABLE pachyderm echo main {} \
+]
+
+do_test vtab3-1.3 {
+ set ::auth_log [list]
+ execsql {
+ DROP TABLE pachyderm;
+ }
+ set ::auth_log
+} [list \
+ SQLITE_DELETE sqlite_master {} main {} \
+ SQLITE_DROP_VTABLE pachyderm echo main {} \
+ SQLITE_DELETE pachyderm {} main {} \
+ SQLITE_DELETE sqlite_master {} main {} \
+]
+
+do_test vtab3-1.4 {
+ set ::auth_fail 1
+ catchsql {
+ CREATE VIRTUAL TABLE pachyderm USING echo(elephant);
+ }
+} {1 {not authorized}}
+do_test vtab3-1.5 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+} {elephant}
+
+do_test vtab3-1.5 {
+ set ::auth_fail 2
+ catchsql {
+ CREATE VIRTUAL TABLE pachyderm USING echo(elephant);
+ }
+} {1 {not authorized}}
+do_test vtab3-1.6 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+} {elephant}
+
+do_test vtab3-1.5 {
+ set ::auth_fail 3
+ catchsql {
+ CREATE VIRTUAL TABLE pachyderm USING echo(elephant);
+ }
+} {0 {}}
+do_test vtab3-1.6 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+} {elephant pachyderm}
+
+foreach i [list 1 2 3 4] {
+ set ::auth_fail $i
+ do_test vtab3-1.7.$i.1 {
+ set rc [catch {
+ execsql {DROP TABLE pachyderm;}
+ } msg]
+ if {$msg eq "authorization denied"} {set msg "not authorized"}
+ list $rc $msg
+ } {1 {not authorized}}
+ do_test vtab3-1.7.$i.2 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+ } {elephant pachyderm}
+}
+do_test vtab3-1.8.1 {
+ set ::auth_fail 0
+ catchsql {
+ DROP TABLE pachyderm;
+ }
+} {0 {}}
+do_test vtab3-1.8.2 {
+ execsql {
+ SELECT name FROM sqlite_master WHERE type = 'table';
+ }
+} {elephant}
+
+finish_test
diff --git a/third_party/sqlite/test/vtab4.test b/third_party/sqlite/test/vtab4.test
new file mode 100755
index 0000000..07b6e83
--- /dev/null
+++ b/third_party/sqlite/test/vtab4.test
@@ -0,0 +1,193 @@
+# 2006 June 10
+#
+# 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 is on testing the following virtual table methods:
+#
+# xBegin
+# xSync
+# xCommit
+# xRollback
+#
+# $Id: vtab4.test,v 1.3 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+unset -nocomplain echo_module
+unset -nocomplain echo_module_sync_fail
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# Register the echo module
+db cache size 0
+register_echo_module [sqlite3_connection_pointer db]
+
+do_test vtab4-1.1 {
+ execsql {
+ CREATE TABLE treal(a PRIMARY KEY, b, c);
+ CREATE VIRTUAL TABLE techo USING echo(treal);
+ }
+} {}
+
+# Test an INSERT, UPDATE and DELETE statement on the virtual table
+# in an implicit transaction. Each should result in a single call
+# to xBegin, xSync and xCommit.
+#
+do_test vtab4-1.2 {
+ set echo_module [list]
+ execsql {
+ INSERT INTO techo VALUES(1, 2, 3);
+ }
+ set echo_module
+} {xBegin echo(treal) xSync echo(treal) xCommit echo(treal)}
+do_test vtab4-1.3 {
+ set echo_module [list]
+ execsql {
+ UPDATE techo SET a = 2;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xBegin echo(treal) \
+ xFilter {SELECT rowid, * FROM 'treal'} \
+ xSync echo(treal) \
+ xCommit echo(treal) \
+]
+do_test vtab4-1.4 {
+ set echo_module [list]
+ execsql {
+ DELETE FROM techo;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xBegin echo(treal) \
+ xFilter {SELECT rowid, * FROM 'treal'} \
+ xSync echo(treal) \
+ xCommit echo(treal) \
+]
+
+# Ensure xBegin is not called more than once in a single transaction.
+#
+do_test vtab4-2.1 {
+ set echo_module [list]
+ execsql {
+ BEGIN;
+ INSERT INTO techo VALUES(1, 2, 3);
+ INSERT INTO techo VALUES(4, 5, 6);
+ INSERT INTO techo VALUES(7, 8, 9);
+ COMMIT;
+ }
+ set echo_module
+} {xBegin echo(treal) xSync echo(treal) xCommit echo(treal)}
+
+# Try a transaction with two virtual tables.
+#
+do_test vtab4-2.2 {
+ execsql {
+ CREATE TABLE sreal(a, b, c UNIQUE);
+ CREATE VIRTUAL TABLE secho USING echo(sreal);
+ }
+ set echo_module [list]
+ execsql {
+ BEGIN;
+ INSERT INTO secho SELECT * FROM techo;
+ DELETE FROM techo;
+ COMMIT;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xBegin echo(sreal) \
+ xFilter {SELECT rowid, * FROM 'treal'} \
+ xBestIndex {SELECT rowid, * FROM 'treal'} \
+ xBegin echo(treal) \
+ xFilter {SELECT rowid, * FROM 'treal'} \
+ xSync echo(sreal) \
+ xSync echo(treal) \
+ xCommit echo(sreal) \
+ xCommit echo(treal) \
+]
+do_test vtab4-2.3 {
+ execsql {
+ SELECT * FROM secho;
+ }
+} {1 2 3 4 5 6 7 8 9}
+do_test vtab4-2.4 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {}
+
+# Try an explicit ROLLBACK on a transaction with two open virtual tables.
+do_test vtab4-2.5 {
+ set echo_module [list]
+ execsql {
+ BEGIN;
+ INSERT INTO techo SELECT * FROM secho;
+ DELETE FROM secho;
+ ROLLBACK;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \
+ xBegin echo(treal) \
+ xFilter {SELECT rowid, * FROM 'sreal'} \
+ xBestIndex {SELECT rowid, * FROM 'sreal'} \
+ xBegin echo(sreal) \
+ xFilter {SELECT rowid, * FROM 'sreal'} \
+ xRollback echo(treal) \
+ xRollback echo(sreal) \
+]
+do_test vtab4-2.6 {
+ execsql {
+ SELECT * FROM secho;
+ }
+} {1 2 3 4 5 6 7 8 9}
+do_test vtab4-2.7 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {}
+
+do_test vtab4-3.1 {
+ set echo_module [list]
+ set echo_module_sync_fail treal
+ catchsql {
+ INSERT INTO techo VALUES(1, 2, 3);
+ }
+} {1 {unknown error}}
+do_test vtab4-3.2 {
+ set echo_module
+} {xBegin echo(treal) xSync echo(treal) xRollback echo(treal)}
+
+do_test vtab4-3.3 {
+ set echo_module [list]
+ set echo_module_sync_fail sreal
+ catchsql {
+ BEGIN;
+ INSERT INTO techo SELECT * FROM secho;
+ DELETE FROM secho;
+ COMMIT;
+ }
+ set echo_module
+} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \
+ xBegin echo(treal) \
+ xFilter {SELECT rowid, * FROM 'sreal'} \
+ xBestIndex {SELECT rowid, * FROM 'sreal'} \
+ xBegin echo(sreal) \
+ xFilter {SELECT rowid, * FROM 'sreal'} \
+ xSync echo(treal) \
+ xSync echo(sreal) \
+ xRollback echo(treal) \
+ xRollback echo(sreal) \
+]
+
+finish_test
diff --git a/third_party/sqlite/test/vtab5.test b/third_party/sqlite/test/vtab5.test
new file mode 100755
index 0000000..7fd7e0e1
--- /dev/null
+++ b/third_party/sqlite/test/vtab5.test
@@ -0,0 +1,152 @@
+# 2006 June 10
+#
+# 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.
+#
+# $Id: vtab5.test,v 1.8 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# The following tests - vtab5-1.* - ensure that an INSERT, DELETE or UPDATE
+# statement can be executed immediately after a CREATE or schema reload. The
+# point here is testing that the parser always calls xConnect() before the
+# schema of a virtual table is used.
+#
+register_echo_module [sqlite3_connection_pointer db]
+do_test vtab5-1.1 {
+ execsql {
+ CREATE TABLE treal(a VARCHAR(16), b INTEGER, c FLOAT);
+ INSERT INTO treal VALUES('a', 'b', 'c');
+ CREATE VIRTUAL TABLE techo USING echo(treal);
+ }
+} {}
+do_test vtab5.1.2 {
+ execsql {
+ SELECT * FROM techo;
+ }
+} {a b c}
+do_test vtab5.1.3 {
+ db close
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ INSERT INTO techo VALUES('c', 'd', 'e');
+ SELECT * FROM techo;
+ }
+} {a b c c d e}
+do_test vtab5.1.4 {
+ db close
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ UPDATE techo SET a = 10;
+ SELECT * FROM techo;
+ }
+} {10 b c 10 d e}
+do_test vtab5.1.5 {
+ db close
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ DELETE FROM techo WHERE b > 'c';
+ SELECT * FROM techo;
+ }
+} {10 b c}
+do_test vtab5.1.X {
+ execsql {
+ DROP TABLE techo;
+ DROP TABLE treal;
+ }
+} {}
+
+# The following tests - vtab5-2.* - ensure that collation sequences
+# assigned to virtual table columns via the "CREATE TABLE" statement
+# passed to sqlite3_declare_vtab() are used correctly.
+#
+do_test vtab5.2.1 {
+ execsql {
+ CREATE TABLE strings(str COLLATE NOCASE);
+ INSERT INTO strings VALUES('abc1');
+ INSERT INTO strings VALUES('Abc3');
+ INSERT INTO strings VALUES('ABc2');
+ INSERT INTO strings VALUES('aBc4');
+ SELECT str FROM strings ORDER BY 1;
+ }
+} {abc1 ABc2 Abc3 aBc4}
+do_test vtab5.2.2 {
+ execsql {
+ CREATE VIRTUAL TABLE echo_strings USING echo(strings);
+ SELECT str FROM echo_strings ORDER BY 1;
+ }
+} {abc1 ABc2 Abc3 aBc4}
+do_test vtab5.2.3 {
+ execsql {
+ SELECT str||'' FROM echo_strings ORDER BY 1;
+ }
+} {ABc2 Abc3 aBc4 abc1}
+
+# Test that it is impossible to create a triggger on a virtual table.
+#
+ifcapable trigger {
+ do_test vtab5.3.1 {
+ catchsql {
+ CREATE TRIGGER trig INSTEAD OF INSERT ON echo_strings BEGIN
+ SELECT 1, 2, 3;
+ END;
+ }
+ } {1 {cannot create triggers on virtual tables}}
+ do_test vtab5.3.2 {
+ catchsql {
+ CREATE TRIGGER trig AFTER INSERT ON echo_strings BEGIN
+ SELECT 1, 2, 3;
+ END;
+ }
+ } {1 {cannot create triggers on virtual tables}}
+ do_test vtab5.3.2 {
+ catchsql {
+ CREATE TRIGGER trig BEFORE INSERT ON echo_strings BEGIN
+ SELECT 1, 2, 3;
+ END;
+ }
+ } {1 {cannot create triggers on virtual tables}}
+}
+
+# Test that it is impossible to create an index on a virtual table.
+#
+do_test vtab5.4.1 {
+ catchsql {
+ CREATE INDEX echo_strings_i ON echo_strings(str);
+ }
+} {1 {virtual tables may not be indexed}}
+
+# Test that it is impossible to add a column to a virtual table.
+#
+do_test vtab5.4.2 {
+ catchsql {
+ ALTER TABLE echo_strings ADD COLUMN col2;
+ }
+} {1 {virtual tables may not be altered}}
+
+# Test that it is impossible to rename a virtual table.
+# UPDATE: It is now possible.
+#
+# do_test vtab5.4.3 {
+# catchsql {
+# ALTER TABLE echo_strings RENAME TO echo_strings2;
+# }
+# } {1 {virtual tables may not be altered}}
+
+finish_test
diff --git a/third_party/sqlite/test/vtab6.test b/third_party/sqlite/test/vtab6.test
new file mode 100755
index 0000000..ed644a1
--- /dev/null
+++ b/third_party/sqlite/test/vtab6.test
@@ -0,0 +1,574 @@
+# 2002 May 24
+#
+# 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.
+#
+# This file implements tests for joins, including outer joins involving
+# virtual tables. The test cases in this file are copied from the file
+# join.test, and some of the comments still reflect that.
+#
+# $Id: vtab6.test,v 1.4 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+register_echo_module [sqlite3_connection_pointer db]
+
+execsql {
+ CREATE TABLE real_t1(a,b,c);
+ CREATE TABLE real_t2(b,c,d);
+ CREATE TABLE real_t3(c,d,e);
+ CREATE TABLE real_t4(d,e,f);
+ CREATE TABLE real_t5(a INTEGER PRIMARY KEY);
+ CREATE TABLE real_t6(a INTEGER);
+ CREATE TABLE real_t7 (x, y);
+ CREATE TABLE real_t8 (a integer primary key, b);
+ CREATE TABLE real_t9(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE real_t10(x INTEGER PRIMARY KEY, y);
+ CREATE TABLE real_t11(p INTEGER PRIMARY KEY, q);
+ CREATE TABLE real_t12(a,b);
+ CREATE TABLE real_t13(b,c);
+ CREATE TABLE real_t21(a,b,c);
+ CREATE TABLE real_t22(p,q);
+}
+foreach t [list t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t21 t22] {
+ execsql "CREATE VIRTUAL TABLE $t USING echo(real_$t)"
+}
+
+do_test vtab6-1.1 {
+ execsql {
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(2,3,4);
+ INSERT INTO t1 VALUES(3,4,5);
+ SELECT * FROM t1;
+ }
+} {1 2 3 2 3 4 3 4 5}
+do_test vtab6-1.2 {
+ execsql {
+ INSERT INTO t2 VALUES(1,2,3);
+ INSERT INTO t2 VALUES(2,3,4);
+ INSERT INTO t2 VALUES(3,4,5);
+ SELECT * FROM t2;
+ }
+} {1 2 3 2 3 4 3 4 5}
+
+do_test vtab6-1.3 {
+ execsql2 {
+ SELECT * FROM t1 NATURAL JOIN t2;
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test vtab6-1.3.1 {
+ execsql2 {
+ SELECT * FROM t2 NATURAL JOIN t1;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test vtab6-1.3.2 {
+ execsql2 {
+ SELECT * FROM t2 AS x NATURAL JOIN t1;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test vtab6-1.3.3 {
+ execsql2 {
+ SELECT * FROM t2 NATURAL JOIN t1 AS y;
+ }
+} {b 2 c 3 d 4 a 1 b 3 c 4 d 5 a 2}
+do_test vtab6-1.3.4 {
+ execsql {
+ SELECT b FROM t1 NATURAL JOIN t2;
+ }
+} {2 3}
+do_test vtab6-1.4.1 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test vtab6-1.4.2 {
+ execsql2 {
+ SELECT * FROM t1 AS x INNER JOIN t2 USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test vtab6-1.4.3 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 AS y USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test vtab6-1.4.4 {
+ execsql2 {
+ SELECT * FROM t1 AS x INNER JOIN t2 AS y USING(b,c);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+do_test vtab6-1.4.5 {
+ execsql {
+ SELECT b FROM t1 JOIN t2 USING(b);
+ }
+} {2 3}
+do_test vtab6-1.5 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(b);
+ }
+} {a 1 b 2 c 3 c 3 d 4 a 2 b 3 c 4 c 4 d 5}
+do_test vtab6-1.6 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(c);
+ }
+} {a 1 b 2 c 3 b 2 d 4 a 2 b 3 c 4 b 3 d 5}
+do_test vtab6-1.7 {
+ execsql2 {
+ SELECT * FROM t1 INNER JOIN t2 USING(c,b);
+ }
+} {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5}
+
+do_test vtab6-1.8 {
+ execsql {
+ SELECT * FROM t1 NATURAL CROSS JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5}
+do_test vtab6-1.9 {
+ execsql {
+ SELECT * FROM t1 CROSS JOIN t2 USING(b,c);
+ }
+} {1 2 3 4 2 3 4 5}
+do_test vtab6-1.10 {
+ execsql {
+ SELECT * FROM t1 NATURAL INNER JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5}
+do_test vtab6-1.11 {
+ execsql {
+ SELECT * FROM t1 INNER JOIN t2 USING(b,c);
+ }
+} {1 2 3 4 2 3 4 5}
+do_test vtab6-1.12 {
+ execsql {
+ SELECT * FROM t1 natural inner join t2;
+ }
+} {1 2 3 4 2 3 4 5}
+
+ifcapable subquery {
+ do_test vtab6-1.13 {
+ execsql2 {
+ SELECT * FROM t1 NATURAL JOIN
+ (SELECT b as 'c', c as 'd', d as 'e' FROM t2) as t3
+ }
+ } {a 1 b 2 c 3 d 4 e 5}
+ do_test vtab6-1.14 {
+ execsql2 {
+ SELECT * FROM (SELECT b as 'c', c as 'd', d as 'e' FROM t2) as 'tx'
+ NATURAL JOIN t1
+ }
+ } {c 3 d 4 e 5 a 1 b 2}
+}
+
+do_test vtab6-1.15 {
+ execsql {
+ INSERT INTO t3 VALUES(2,3,4);
+ INSERT INTO t3 VALUES(3,4,5);
+ INSERT INTO t3 VALUES(4,5,6);
+ SELECT * FROM t3;
+ }
+} {2 3 4 3 4 5 4 5 6}
+do_test vtab6-1.16 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t3;
+ }
+} {1 2 3 4 5 2 3 4 5 6}
+do_test vtab6-1.17 {
+ execsql2 {
+ SELECT * FROM t1 natural join t2 natural join t3;
+ }
+} {a 1 b 2 c 3 d 4 e 5 a 2 b 3 c 4 d 5 e 6}
+do_test vtab6-1.18 {
+ execsql {
+ INSERT INTO t4 VALUES(2,3,4);
+ INSERT INTO t4 VALUES(3,4,5);
+ INSERT INTO t4 VALUES(4,5,6);
+ SELECT * FROM t4;
+ }
+} {2 3 4 3 4 5 4 5 6}
+do_test vtab6-1.19.1 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t4;
+ }
+} {1 2 3 4 5 6}
+do_test vtab6-1.19.2 {
+ execsql2 {
+ SELECT * FROM t1 natural join t2 natural join t4;
+ }
+} {a 1 b 2 c 3 d 4 e 5 f 6}
+do_test vtab6-1.20 {
+ execsql {
+ SELECT * FROM t1 natural join t2 natural join t3 WHERE t1.a=1
+ }
+} {1 2 3 4 5}
+
+do_test vtab6-2.1 {
+ execsql {
+ SELECT * FROM t1 NATURAL LEFT JOIN t2;
+ }
+} {1 2 3 4 2 3 4 5 3 4 5 {}}
+do_test vtab6-2.2 {
+ execsql {
+ SELECT * FROM t2 NATURAL LEFT OUTER JOIN t1;
+ }
+} {1 2 3 {} 2 3 4 1 3 4 5 2}
+do_test vtab6-2.3 {
+ catchsql {
+ SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2;
+ }
+} {1 {RIGHT and FULL OUTER JOINs are not currently supported}}
+do_test vtab6-2.4 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d
+ }
+} {1 2 3 {} {} {} 2 3 4 {} {} {} 3 4 5 1 2 3}
+do_test vtab6-2.5 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d WHERE t1.a>1
+ }
+} {2 3 4 {} {} {} 3 4 5 1 2 3}
+do_test vtab6-2.6 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.d WHERE t2.b IS NULL OR t2.b>1
+ }
+} {1 2 3 {} {} {} 2 3 4 {} {} {}}
+
+do_test vtab6-3.1 {
+ catchsql {
+ SELECT * FROM t1 NATURAL JOIN t2 ON t1.a=t2.b;
+ }
+} {1 {a NATURAL join may not have an ON or USING clause}}
+do_test vtab6-3.2 {
+ catchsql {
+ SELECT * FROM t1 NATURAL JOIN t2 USING(b);
+ }
+} {1 {a NATURAL join may not have an ON or USING clause}}
+do_test vtab6-3.3 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 ON t1.a=t2.b USING(b);
+ }
+} {1 {cannot have both ON and USING clauses in the same join}}
+do_test vtab6-3.4 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 USING(a);
+ }
+} {1 {cannot join using column a - column not present in both tables}}
+do_test vtab6-3.5 {
+ catchsql {
+ SELECT * FROM t1 USING(a);
+ }
+} {0 {1 2 3 2 3 4 3 4 5}}
+do_test vtab6-3.6 {
+ catchsql {
+ SELECT * FROM t1 JOIN t2 ON t3.a=t2.b;
+ }
+} {1 {no such column: t3.a}}
+do_test vtab6-3.7 {
+ catchsql {
+ SELECT * FROM t1 INNER OUTER JOIN t2;
+ }
+} {1 {unknown or unsupported join type: INNER OUTER}}
+do_test vtab6-3.7 {
+ catchsql {
+ SELECT * FROM t1 LEFT BOGUS JOIN t2;
+ }
+} {1 {unknown or unsupported join type: LEFT BOGUS}}
+
+do_test vtab6-4.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t6 VALUES(NULL);
+ INSERT INTO t6 VALUES(NULL);
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ INSERT INTO t6 SELECT * FROM t6;
+ COMMIT;
+ }
+ execsql {
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test vtab6-4.2 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test vtab6-4.3 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+do_test vtab6-4.4 {
+ execsql {
+ UPDATE t6 SET a='xyz';
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test vtab6-4.6 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test vtab6-4.7 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+do_test vtab6-4.8 {
+ execsql {
+ UPDATE t6 SET a=1;
+ SELECT * FROM t6 NATURAL JOIN t5;
+ }
+} {}
+do_test vtab6-4.9 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a<t5.a;
+ }
+} {}
+do_test vtab6-4.10 {
+ execsql {
+ SELECT * FROM t6, t5 WHERE t6.a>t5.a;
+ }
+} {}
+
+# A test for ticket #247.
+#
+do_test vtab6-7.1 {
+ execsql {
+ INSERT INTO t7 VALUES ("pa1", 1);
+ INSERT INTO t7 VALUES ("pa2", NULL);
+ INSERT INTO t7 VALUES ("pa3", NULL);
+ INSERT INTO t7 VALUES ("pa4", 2);
+ INSERT INTO t7 VALUES ("pa30", 131);
+ INSERT INTO t7 VALUES ("pa31", 130);
+ INSERT INTO t7 VALUES ("pa28", NULL);
+
+ INSERT INTO t8 VALUES (1, "pa1");
+ INSERT INTO t8 VALUES (2, "pa4");
+ INSERT INTO t8 VALUES (3, NULL);
+ INSERT INTO t8 VALUES (4, NULL);
+ INSERT INTO t8 VALUES (130, "pa31");
+ INSERT INTO t8 VALUES (131, "pa30");
+
+ SELECT coalesce(t8.a,999) from t7 LEFT JOIN t8 on y=a;
+ }
+} {1 999 999 2 131 130 999}
+
+# Make sure a left join where the right table is really a view that
+# is itself a join works right. Ticket #306.
+#
+ifcapable view {
+do_test vtab6-8.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t9 VALUES(1,11);
+ INSERT INTO t9 VALUES(2,22);
+ INSERT INTO t10 VALUES(1,2);
+ INSERT INTO t10 VALUES(3,3);
+ INSERT INTO t11 VALUES(2,111);
+ INSERT INTO t11 VALUES(3,333);
+ CREATE VIEW v10_11 AS SELECT x, q FROM t10, t11 WHERE t10.y=t11.p;
+ COMMIT;
+ SELECT * FROM t9 LEFT JOIN v10_11 ON( a=x );
+ }
+} {1 11 1 111 2 22 {} {}}
+ifcapable subquery {
+ do_test vtab6-8.2 {
+ execsql {
+ SELECT * FROM t9 LEFT JOIN (SELECT x, q FROM t10, t11 WHERE t10.y=t11.p)
+ ON( a=x);
+ }
+ } {1 11 1 111 2 22 {} {}}
+}
+do_test vtab6-8.3 {
+ execsql {
+ SELECT * FROM v10_11 LEFT JOIN t9 ON( a=x );
+ }
+} {1 111 1 11 3 333 {} {}}
+} ;# ifcapable view
+
+# Ticket #350 describes a scenario where LEFT OUTER JOIN does not
+# function correctly if the right table in the join is really
+# subquery.
+#
+# To test the problem, we generate the same LEFT OUTER JOIN in two
+# separate selects but with on using a subquery and the other calling
+# the table directly. Then connect the two SELECTs using an EXCEPT.
+# Both queries should generate the same results so the answer should
+# be an empty set.
+#
+ifcapable compound {
+do_test vtab6-9.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t12 VALUES(1,11);
+ INSERT INTO t12 VALUES(2,22);
+ INSERT INTO t13 VALUES(22,222);
+ COMMIT;
+ }
+} {}
+
+ifcapable subquery {
+ do_test vtab6-9.1.1 {
+ execsql {
+ SELECT * FROM t12 NATURAL LEFT JOIN t13
+ EXCEPT
+ SELECT * FROM t12 NATURAL LEFT JOIN (SELECT * FROM t13 WHERE b>0);
+ }
+ } {}
+}
+ifcapable view {
+ do_test vtab6-9.2 {
+ execsql {
+ CREATE VIEW v13 AS SELECT * FROM t13 WHERE b>0;
+ SELECT * FROM t12 NATURAL LEFT JOIN t13
+ EXCEPT
+ SELECT * FROM t12 NATURAL LEFT JOIN v13;
+ }
+ } {}
+} ;# ifcapable view
+} ;# ifcapable compound
+
+ifcapable subquery {
+do_test vtab6-10.1 {
+ execsql {
+ CREATE INDEX i22 ON real_t22(q);
+ SELECT a FROM t21 LEFT JOIN t22 ON b=p WHERE q=
+ (SELECT max(m.q) FROM t22 m JOIN t21 n ON n.b=m.p WHERE n.c=1);
+ }
+} {}
+} ;# ifcapable subquery
+
+do_test vtab6-11.1.0 {
+ execsql {
+ CREATE TABLE ab_r(a, b);
+ CREATE TABLE bc_r(b, c);
+
+ CREATE VIRTUAL TABLE ab USING echo(ab_r);
+ CREATE VIRTUAL TABLE bc USING echo(bc_r);
+
+ INSERT INTO ab VALUES(1, 2);
+ INSERT INTO bc VALUES(2, 3);
+ }
+} {}
+
+do_test vtab6-11.1.1 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+do_test vtab6-11.1.2 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+set ::echo_module_cost 1.0
+
+do_test vtab6-11.1.3 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+do_test vtab6-11.1.4 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+
+do_test vtab6-11.2.0 {
+ execsql {
+ CREATE INDEX ab_i ON ab_r(b);
+ }
+} {}
+
+unset ::echo_module_cost
+
+do_test vtab6-11.2.1 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+do_test vtab6-11.2.2 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+set ::echo_module_cost 1.0
+
+do_test vtab6-11.2.3 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+do_test vtab6-11.2.4 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+unset ::echo_module_cost
+db close
+sqlite3 db test.db
+register_echo_module [sqlite3_connection_pointer db]
+
+do_test vtab6-11.3.1 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+
+do_test vtab6-11.3.2 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+set ::echo_module_cost 1.0
+
+do_test vtab6-11.3.3 {
+ execsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 2 3}
+do_test vtab6-11.3.4 {
+ execsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 2 3}
+
+unset ::echo_module_cost
+
+set ::echo_module_ignore_usable 1
+db cache flush
+
+do_test vtab6-11.4.1 {
+ catchsql {
+ SELECT a, b, c FROM ab NATURAL JOIN bc;
+ }
+} {1 {table ab: xBestIndex returned an invalid plan}}
+do_test vtab6-11.4.2 {
+ catchsql {
+ SELECT a, b, c FROM bc NATURAL JOIN ab;
+ }
+} {1 {table ab: xBestIndex returned an invalid plan}}
+
+unset ::echo_module_ignore_usable
+
+finish_test
diff --git a/third_party/sqlite/test/vtab7.test b/third_party/sqlite/test/vtab7.test
new file mode 100755
index 0000000..d1b1f59
--- /dev/null
+++ b/third_party/sqlite/test/vtab7.test
@@ -0,0 +1,207 @@
+# 2006 July 25
+#
+# 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 test is reading and writing to the database from within a
+# virtual table xSync() callback.
+#
+# $Id: vtab7.test,v 1.4 2007/12/04 16:54:53 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# Register the echo module. Code inside the echo module appends elements
+# to the global tcl list variable ::echo_module whenever SQLite invokes
+# certain module callbacks. This includes the xSync(), xCommit() and
+# xRollback() callbacks. For each of these callback, two elements are
+# appended to ::echo_module, as follows:
+#
+# Module method Elements appended to ::echo_module
+# -------------------------------------------------------
+# xSync() xSync echo($tablename)
+# xCommit() xCommit echo($tablename)
+# xRollback() xRollback echo($tablename)
+# -------------------------------------------------------
+#
+# In each case, $tablename is replaced by the name of the real table (not
+# the echo table). By setting up a tcl trace on the ::echo_module variable,
+# code in this file arranges for a Tcl script to be executed from within
+# the echo module xSync() callback.
+#
+register_echo_module [sqlite3_connection_pointer db]
+trace add variable ::echo_module write echo_module_trace
+
+# This Tcl proc is invoked whenever the ::echo_module variable is written.
+#
+proc echo_module_trace {args} {
+ # Filter out writes to ::echo_module that are not xSync, xCommit or
+ # xRollback callbacks.
+ if {[llength $::echo_module] < 2} return
+ set x [lindex $::echo_module end-1]
+ if {$x ne "xSync" && $x ne "xCommit" && $x ne "xRollback"} return
+
+ regexp {^echo.(.*).$} [lindex $::echo_module end] dummy tablename
+ # puts "Ladies and gentlemen, an $x on $tablename!"
+
+ if {[info exists ::callbacks($x,$tablename)]} {
+ eval $::callbacks($x,$tablename)
+ }
+}
+
+# The following tests, vtab7-1.*, test that the trace callback on
+# ::echo_module is providing the expected tcl callbacks.
+do_test vtab7-1.1 {
+ execsql {
+ CREATE TABLE abc(a, b, c);
+ CREATE VIRTUAL TABLE abc2 USING echo(abc);
+ }
+} {}
+
+do_test vtab7-1.2 {
+ set ::callbacks(xSync,abc) {incr ::counter}
+ set ::counter 0
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ }
+ set ::counter
+} {1}
+
+# Write to an existing database table from within an xSync callback.
+do_test vtab7-2.1 {
+ set ::callbacks(xSync,abc) {
+ execsql {INSERT INTO log VALUES('xSync');}
+ }
+ execsql {
+ CREATE TABLE log(msg);
+ INSERT INTO abc2 VALUES(4, 5, 6);
+ SELECT * FROM log;
+ }
+} {xSync}
+do_test vtab7-2.3 {
+ execsql {
+ INSERT INTO abc2 VALUES(4, 5, 6);
+ SELECT * FROM log;
+ }
+} {xSync xSync}
+do_test vtab7-2.4 {
+ execsql {
+ INSERT INTO abc2 VALUES(4, 5, 6);
+ SELECT * FROM log;
+ }
+} {xSync xSync xSync}
+
+# Create a database table from within xSync callback.
+do_test vtab7-2.5 {
+ set ::callbacks(xSync,abc) {
+ execsql { CREATE TABLE newtab(d, e, f); }
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ SELECT name FROM sqlite_master ORDER BY name;
+ }
+} {abc abc2 log newtab}
+
+# Drop a database table from within xSync callback.
+# This is not allowed. Tables cannot be dropped while
+# any other statement is active.
+#
+do_test vtab7-2.6 {
+ set ::callbacks(xSync,abc) {
+ set ::rc [catchsql { DROP TABLE newtab }]
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ SELECT name FROM sqlite_master ORDER BY name;
+ }
+} {abc abc2 log newtab}
+do_test vtab7-2.6.1 {
+ set ::rc
+} {1 {database table is locked}}
+execsql {DROP TABLE newtab}
+
+# Write to an attached database from xSync().
+ifcapable attach {
+ do_test vtab7-3.1 {
+ file delete -force test2.db
+ file delete -force test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS db2;
+ CREATE TABLE db2.stuff(description, shape, color);
+ }
+ set ::callbacks(xSync,abc) {
+ execsql { INSERT INTO db2.stuff VALUES('abc', 'square', 'green'); }
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ SELECT * from stuff;
+ }
+ } {abc square green}
+}
+
+# UPDATE: The next test passes, but leaks memory. So leave it out.
+#
+# The following tests test that writing to the database from within
+# the xCommit callback causes a misuse error.
+# do_test vtab7-4.1 {
+# unset -nocomplain ::callbacks(xSync,abc)
+# set ::callbacks(xCommit,abc) {
+# execsql { INSERT INTO log VALUES('hello') }
+# }
+# catchsql {
+# INSERT INTO abc2 VALUES(1, 2, 3);
+# }
+# } {1 {library routine called out of sequence}}
+
+# These tests, vtab7-4.*, test that an SQLITE_LOCKED error is returned
+# if an attempt to write to a virtual module table or create a new
+# virtual table from within an xSync() callback.
+do_test vtab7-4.1 {
+ execsql {
+ CREATE TABLE def(d, e, f);
+ CREATE VIRTUAL TABLE def2 USING echo(def);
+ }
+ set ::callbacks(xSync,abc) {
+ set ::error [catchsql { INSERT INTO def2 VALUES(1, 2, 3) }]
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ }
+ set ::error
+} {1 {database table is locked}}
+do_test vtab7-4.2 {
+ set ::callbacks(xSync,abc) {
+ set ::error [catchsql { CREATE VIRTUAL TABLE def3 USING echo(def) }]
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ }
+ set ::error
+} {1 {database table is locked}}
+
+do_test vtab7-4.3 {
+ set ::callbacks(xSync,abc) {
+ set ::error [catchsql { DROP TABLE def2 }]
+ }
+ execsql {
+ INSERT INTO abc2 VALUES(1, 2, 3);
+ SELECT name FROM sqlite_master ORDER BY name;
+ }
+ set ::error
+} {1 {database table is locked}}
+
+trace remove variable ::echo_module write echo_module_trace
+unset -nocomplain ::callbacks
+
+finish_test
diff --git a/third_party/sqlite/test/vtab8.test b/third_party/sqlite/test/vtab8.test
new file mode 100755
index 0000000..8e04a41
--- /dev/null
+++ b/third_party/sqlite/test/vtab8.test
@@ -0,0 +1,78 @@
+# 2006 August 29
+#
+# 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 file inserting into virtual tables from a SELECT
+# statement.
+#
+# $Id: vtab8.test,v 1.2 2007/03/02 08:12:23 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+register_echo_module [sqlite3_connection_pointer db]
+
+# See ticket #2244
+#
+do_test vtab1.2244-1 {
+ execsql {
+ CREATE TABLE t2244(a, b);
+ CREATE VIRTUAL TABLE t2244e USING echo(t2244);
+ INSERT INTO t2244 VALUES('AA', 'BB');
+ INSERT INTO t2244 VALUES('CC', 'DD');
+ SELECT rowid, * FROM t2244e;
+ }
+} {1 AA BB 2 CC DD}
+do_test vtab1.2244-2 {
+ execsql {
+ SELECT * FROM t2244e WHERE rowid = 10;
+ }
+} {}
+do_test vtab1.2244-3 {
+ execsql {
+ UPDATE t2244e SET a = 'hello world' WHERE 0;
+ SELECT rowid, * FROM t2244e;
+ }
+} {1 AA BB 2 CC DD}
+
+do_test vtab1-2250-2 {
+ execsql {
+ CREATE TABLE t2250(a, b);
+ INSERT INTO t2250 VALUES(10, 20);
+ CREATE VIRTUAL TABLE t2250e USING echo(t2250);
+ select max(rowid) from t2250;
+ select max(rowid) from t2250e;
+ }
+} {1 1}
+
+# See ticket #2260.
+#
+do_test vtab1.2260-1 {
+ execsql {
+ CREATE TABLE t2260a_real(a, b);
+ CREATE TABLE t2260b_real(a, b);
+
+ CREATE INDEX i2260 ON t2260a_real(a);
+ CREATE INDEX i2260x ON t2260b_real(a);
+
+ CREATE VIRTUAL TABLE t2260a USING echo(t2260a_real);
+ CREATE VIRTUAL TABLE t2260b USING echo(t2260b_real);
+
+ SELECT * FROM t2260a, t2260b WHERE t2260a.a = t2260b.a AND t2260a.a > 101;
+ }
+} {}
+
+unset -nocomplain echo_module_begin_fail
+finish_test
diff --git a/third_party/sqlite/test/vtab9.test b/third_party/sqlite/test/vtab9.test
new file mode 100755
index 0000000..b1290eb
--- /dev/null
+++ b/third_party/sqlite/test/vtab9.test
@@ -0,0 +1,70 @@
+# 2006 August 29
+#
+# 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 file inserting into virtual tables from a SELECT
+# statement.
+#
+# $Id: vtab9.test,v 1.2 2007/04/16 15:06:26 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+do_test vtab9-1.1 {
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TABLE t0(a);
+ CREATE VIRTUAL TABLE t1 USING echo(t0);
+ INSERT INTO t1 SELECT 'hello';
+ SELECT rowid, * FROM t1;
+ }
+} {1 hello}
+
+do_test vtab9-1.2 {
+ execsql {
+ CREATE TABLE t2(a,b,c);
+ CREATE VIRTUAL TABLE t3 USING echo(t2);
+ CREATE TABLE d1(a,b,c);
+ INSERT INTO d1 VALUES(1,2,3);
+ INSERT INTO d1 VALUES('a','b','c');
+ INSERT INTO d1 VALUES(NULL,'x',123.456);
+ INSERT INTO d1 VALUES(x'6869',123456789,-12345);
+ INSERT INTO t3(a,b,c) SELECT * FROM d1;
+ SELECT rowid, * FROM t3;
+ }
+} {1 1 2 3 2 a b c 3 {} x 123.456 4 hi 123456789 -12345}
+
+# do_test vtab9-2.1 {
+# execsql {
+# CREATE TABLE t4(a);
+# CREATE VIRTUAL TABLE t5 USING echo(t4);
+# INSERT INTO t4 VALUES('hello');
+# SELECT rowid, a FROM t5;
+# }
+# } {1 hello}
+# do_test vtab9-2.2 {
+# execsql {
+# INSERT INTO t5(rowid, a) VALUES(1, 'goodbye');
+# }
+# } {1 hello}
+# do_test vtab9-2.3 {
+# execsql {
+# REPLACE INTO t5(rowid, a) VALUES(1, 'goodbye');
+# SELECT * FROM t5;
+# }
+# } {1 goodbye}
+
+unset -nocomplain echo_module_begin_fail
+finish_test
diff --git a/third_party/sqlite/test/vtabA.test b/third_party/sqlite/test/vtabA.test
new file mode 100755
index 0000000..5eba5b5
--- /dev/null
+++ b/third_party/sqlite/test/vtabA.test
@@ -0,0 +1,134 @@
+# 2007 June 26
+#
+# 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 file is 'hidden' virtual table columns.
+#
+# $Id: vtabA.test,v 1.2 2008/07/12 14:52:21 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+proc get_decltype {table col} {
+ set STMT [sqlite3_prepare $::DB "SELECT $col FROM $table" -1 TAIL]
+ set decltype [sqlite3_column_decltype $STMT 0]
+ sqlite3_finalize $STMT
+ set decltype
+}
+
+proc get_collist {table} {
+ set ret [list]
+ db eval "PRAGMA table_info($table)" { lappend ret $name }
+ set ret
+}
+
+# Register the echo module
+register_echo_module [sqlite3_connection_pointer db]
+
+# Create a virtual table with a 'hidden' column (column b).
+#
+do_test vtabA-1.1 {
+ execsql { CREATE TABLE t1(a, b HIDDEN VARCHAR, c INTEGER) }
+} {}
+do_test vtabA-1.2 {
+ execsql { CREATE VIRTUAL TABLE t1e USING echo(t1) }
+} {}
+
+# Test that the hidden column is not listed by [PRAGMA table_info].
+#
+do_test vtabA-1.3 {
+ execsql { PRAGMA table_info(t1e) }
+} [list \
+ 0 a {} 0 {} 0 \
+ 1 c INTEGER 0 {} 0 \
+]
+
+# Test that the hidden column is not require in the default column
+# list for an INSERT statement.
+#
+do_test vtabA-1.4 {
+ catchsql {
+ INSERT INTO t1e VALUES('value a', 'value c');
+ }
+} {0 {}}
+do_test vtabA-1.5 {
+ execsql {
+ SELECT a, b, c FROM t1e;
+ }
+} {{value a} {} {value c}}
+
+do_test vtabA-1.6 {
+ execsql {
+ SELECT * FROM t1e;
+ }
+} {{value a} {value c}}
+
+# Test that the expansion of a '*' expression in the result set of
+# a SELECT does not include the hidden column.
+#
+do_test vtabA-1.7 {
+ execsql {
+ INSERT INTO t1e SELECT * FROM t1e;
+ }
+} {}
+do_test vtabA-1.8 {
+ execsql {
+ SELECT * FROM t1e;
+ }
+} {{value a} {value c} {value a} {value c}}
+
+# Test that the declaration type of the hidden column does not include
+# the token "HIDDEN".
+#
+do_test vtabA-1.9 {
+ get_decltype t1e b
+} {VARCHAR}
+do_test vtabA-1.10 {
+ get_collist t1e
+} {a c}
+
+#----------------------------------------------------------------------
+# These tests vtabA-2.* concentrate on testing that the HIDDEN token
+# is detected and handled correctly in various declarations.
+#
+proc analyse_parse {columns decltype_list} {
+ db eval { DROP TABLE IF EXISTS t1e; }
+ db eval { DROP TABLE IF EXISTS t1; }
+ db eval " CREATE TABLE t1 $columns "
+ db eval { CREATE VIRTUAL TABLE t1e USING echo(t1) }
+ set ret [list [get_collist t1e]]
+ foreach c $decltype_list {
+ lappend ret [get_decltype t1e $c]
+ }
+ set ret
+}
+
+do_test vtabA-2.1 {
+ analyse_parse {(a text, b integer hidden, c hidden)} {a b c}
+} {a text integer {}}
+
+do_test vtabA-2.2 {
+ analyse_parse {(a hidden , b integerhidden, c hidden1)} {a b c}
+} {{b c} {} integerhidden hidden1}
+
+do_test vtabA-2.3 {
+ analyse_parse {(a HiDden, b HIDDEN, c hidden)} {a b c}
+} {{} {} {} {}}
+
+do_test vtabA-2.4 {
+ analyse_parse {(a whatelse can i hidden test, b HIDDEN hidden)} {a b}
+} {{} {whatelse can i test} hidden}
+
+finish_test
diff --git a/third_party/sqlite/test/vtabB.test b/third_party/sqlite/test/vtabB.test
new file mode 100755
index 0000000..201e978
--- /dev/null
+++ b/third_party/sqlite/test/vtabB.test
@@ -0,0 +1,79 @@
+# 2008 April 10
+#
+# 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 file is is verifying that a virtual table in the
+# TEMP database that is created and dropped within a transaction
+# is handled correctly. Ticket #2994.
+#
+# Also make sure a virtual table on the right-hand side of an IN operator
+# is materialized rather than being used directly. Ticket #3082.
+#
+
+#
+# $Id: vtabB.test,v 1.2 2008/04/25 12:10:15 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+do_test vtabB-1.1 {
+ register_echo_module [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TABLE t1(x);
+ BEGIN;
+ CREATE VIRTUAL TABLE temp.echo_test1 USING echo(t1);
+ DROP TABLE echo_test1;
+ ROLLBACK;
+ }
+} {}
+
+do_test vtabB-2.1 {
+ execsql {
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ CREATE TABLE t2(y);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ CREATE VIRTUAL TABLE echo_t2 USING echo(t2);
+ SELECT * FROM t1 WHERE x IN (SELECT rowid FROM t2);
+ }
+} {2}
+do_test vtab8-2.2 {
+ execsql {
+ SELECT rowid FROM echo_t2
+ }
+} {1 2}
+do_test vtabB-2.3 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (SELECT rowid FROM t2);
+ }
+} {2}
+do_test vtabB-2.4 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (SELECT rowid FROM echo_t2);
+ }
+} {2}
+do_test vtabB-2.5 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (SELECT y FROM t2);
+ }
+} {2}
+do_test vtabB-2.6 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (SELECT y FROM echo_t2);
+ }
+} {2}
+
+finish_test
diff --git a/third_party/sqlite/test/vtabC.test b/third_party/sqlite/test/vtabC.test
new file mode 100755
index 0000000..4839748
--- /dev/null
+++ b/third_party/sqlite/test/vtabC.test
@@ -0,0 +1,114 @@
+# 2008 April 10
+#
+# 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 file is is verifying that the xUpdate, xSync, xCommit
+# and xRollback methods are only invoked after an xBegin or xCreate.
+# Ticket #3083.
+#
+# $Id: vtabC.test,v 1.1 2008/04/28 20:27:54 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+
+# N will be the number of virtual tables we have defined.
+#
+unset -nocomplain N
+for {set N 1} {$N<=20} {incr N} {
+ db close
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ register_echo_module [sqlite3_connection_pointer db]
+
+ # Create $N tables and $N virtual tables to echo them.
+ #
+ unset -nocomplain tablist
+ set tablist {}
+ do_test vtabC-1.$N.1 {
+ for {set i 1} {$i<=$::N} {incr i} {
+ execsql "CREATE TABLE t${i}(x)"
+ execsql "CREATE VIRTUAL TABLE vt$i USING echo(t$i)"
+ lappend ::tablist t$i vt$i
+ }
+ execsql {SELECT count(*) FROM sqlite_master}
+ } [expr {$N*2}]
+ do_test vtabC-1.$N.2 {
+ execsql {SELECT name FROM sqlite_master}
+ } $tablist
+
+ # Create a table m and add triggers to make changes on all
+ # of the virtual tables when m is changed.
+ #
+ do_test vtabC-1.$N.3 {
+ execsql {CREATE TABLE m(a)}
+ set sql "CREATE TRIGGER rins AFTER INSERT ON m BEGIN\n"
+ for {set i 1} {$i<=$::N} {incr i} {
+ append sql " INSERT INTO vt$i VALUES(NEW.a+$i);\n"
+ }
+ append sql "END;"
+ execsql $sql
+ execsql {SELECT count(*) FROM sqlite_master}
+ } [expr {$N*2+2}]
+ do_test vtabC-1.$N.4 {
+ execsql {
+ INSERT INTO m VALUES(1000);
+ SELECT * FROM m;
+ }
+ } {1000}
+ for {set j 1} {$j<=$::N} {incr j} {
+ do_test vtabC-1.$N.5.$j {
+ execsql "SELECT * FROM t$::j"
+ } [expr {$j+1000}]
+ do_test vtabC-1.$N.6.$j {
+ execsql "SELECT * FROM vt$::j"
+ } [expr {$j+1000}]
+ }
+ do_test vtabC-1.$N.7 {
+ set sql "CREATE TRIGGER rins2 BEFORE INSERT ON m BEGIN\n"
+ for {set i 1} {$i<=$::N} {incr i} {
+ append sql " INSERT INTO vt$i VALUES(NEW.a+$i*100);\n"
+ }
+ for {set i 1} {$i<=$::N} {incr i} {
+ append sql " INSERT INTO vt$i VALUES(NEW.a+$i*10000);\n"
+ }
+ append sql "END;"
+ execsql $sql
+ execsql {SELECT count(*) FROM sqlite_master}
+ } [expr {$N*2+3}]
+ do_test vtabC-1.$N.8 {
+ execsql {
+ INSERT INTO m VALUES(9000000);
+ SELECT * FROM m;
+ }
+ } {1000 9000000}
+ unset -nocomplain res
+ for {set j 1} {$j<=$::N} {incr j} {
+ set res [expr {$j+1000}]
+ lappend res [expr {$j*100+9000000}]
+ lappend res [expr {$j*10000+9000000}]
+ lappend res [expr {$j+9000000}]
+ do_test vtabC-1.$N.9.$j {
+ execsql "SELECT * FROM t$::j"
+ } $res
+ do_test vtabC-1.$N.10.$j {
+ execsql "SELECT * FROM vt$::j"
+ } $res
+ }
+}
+unset -nocomplain res N i j
+
+
+finish_test
diff --git a/third_party/sqlite/test/vtab_alter.test b/third_party/sqlite/test/vtab_alter.test
new file mode 100755
index 0000000..cf28822
--- /dev/null
+++ b/third_party/sqlite/test/vtab_alter.test
@@ -0,0 +1,103 @@
+# 2007 June 26
+#
+# 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 file is testing the ALTER TABLE ... RENAME TO
+# command on virtual tables.
+#
+# $Id: vtab_alter.test,v 1.3 2007/12/13 21:54:11 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+# Register the echo module.
+#
+# This test uses a special feature of the echo module. If the name
+# of the virtual table is a prefix of the name of the underlying
+# real table (for example if the v-table is "tbl" and the real table
+# is "tbl_base"), then the name of the real table is modified
+# when an "ALTER TABLE ... RENAME TO" is executed on the v-table.
+# For example:
+#
+# sqlite> CREATE TABLE t1_base(a, b, c);
+# sqlite> CREATE VIRTUAL TABLE t1 USING(t1_base);
+# sqlite> ALTER TABLE t1 RENAME TO t2;
+# sqlite> SELECT tbl_name FROM sqlite_master;
+# t2_base
+# t2
+#
+register_echo_module [sqlite3_connection_pointer db]
+
+
+# Try to rename an echo table. Make sure nothing terrible happens.
+#
+do_test vtab_alter-1.1 {
+ execsql { CREATE TABLE t1(a, b VARCHAR, c INTEGER) }
+} {}
+do_test vtab_alter-1.2 {
+ execsql { CREATE VIRTUAL TABLE t1echo USING echo(t1) }
+} {}
+do_test vtab_alter-1.3 {
+ catchsql { SELECT * FROM t1echo }
+} {0 {}}
+do_test vtab_alter-1.4 {
+ execsql { ALTER TABLE t1echo RENAME TO new }
+} {}
+do_test vtab_alter-1.5 {
+ catchsql { SELECT * FROM t1echo }
+} {1 {no such table: t1echo}}
+do_test vtab_alter-1.6 {
+ catchsql { SELECT * FROM new }
+} {0 {}}
+
+# Try to rename an echo table that renames its base table. Make
+# sure nothing terrible happens.
+#
+do_test vtab_alter-2.1 {
+ execsql {
+ DROP TABLE new;
+ DROP TABLE t1;
+ CREATE TABLE t1_base(a, b, c);
+ CREATE VIRTUAL TABLE t1 USING echo('*_base');
+ }
+} {}
+do_test vtab_alter-2.2 {
+ execsql {
+ INSERT INTO t1_base VALUES(1, 2, 3);
+ SELECT * FROM t1;
+ }
+} {1 2 3}
+do_test vtab_alter-2.3 {
+ execsql { ALTER TABLE t1 RENAME TO x }
+} {}
+do_test vtab_alter-2.4 {
+ execsql { SELECT * FROM x; }
+} {1 2 3}
+do_test vtab_alter-2.5 {
+ execsql { SELECT * FROM x_base; }
+} {1 2 3}
+
+# Cause an error to occur when the echo module renames its
+# backing store table.
+#
+do_test vtab_alter-3.1 {
+ execsql { CREATE TABLE y_base(a, b, c) }
+ catchsql { ALTER TABLE x RENAME TO y }
+} {1 {SQL logic error or missing database}}
+do_test vtab_alter-3.2 {
+ execsql { SELECT * FROM x }
+} {1 2 3}
+
+finish_test
diff --git a/third_party/sqlite/test/vtab_err.test b/third_party/sqlite/test/vtab_err.test
new file mode 100755
index 0000000..068386e
--- /dev/null
+++ b/third_party/sqlite/test/vtab_err.test
@@ -0,0 +1,71 @@
+# 2006 June 10
+#
+# 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.
+#
+#***********************************************************************
+#
+# $Id: vtab_err.test,v 1.8 2007/09/03 16:12:10 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+
+
+unset -nocomplain echo_module_begin_fail
+do_ioerr_test vtab_err-1 -tclprep {
+ register_echo_module [sqlite3_connection_pointer db]
+} -sqlbody {
+ BEGIN;
+ CREATE TABLE r(a PRIMARY KEY, b, c);
+ CREATE VIRTUAL TABLE e USING echo(r);
+ INSERT INTO e VALUES(1, 2, 3);
+ INSERT INTO e VALUES('a', 'b', 'c');
+ UPDATE e SET c = 10;
+ DELETE FROM e WHERE a = 'a';
+ COMMIT;
+ BEGIN;
+ CREATE TABLE r2(a, b, c);
+ INSERT INTO r2 SELECT * FROM e;
+ INSERT INTO e SELECT a||'x', b, c FROM r2;
+ COMMIT;
+}
+
+ifcapable !memdebug {
+ puts "Skipping vtab_err-2 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+source $testdir/malloc_common.tcl
+
+
+do_malloc_test vtab_err-2 -tclprep {
+ register_echo_module [sqlite3_connection_pointer db]
+} -sqlbody {
+ BEGIN;
+ CREATE TABLE r(a PRIMARY KEY, b, c);
+ CREATE VIRTUAL TABLE e USING echo(r);
+ INSERT INTO e VALUES(1, 2, 3);
+ INSERT INTO e VALUES('a', 'b', 'c');
+ UPDATE e SET c = 10;
+ DELETE FROM e WHERE a = 'a';
+ COMMIT;
+ BEGIN;
+ CREATE TABLE r2(a, b, c);
+ INSERT INTO r2 SELECT * FROM e;
+ INSERT INTO e SELECT a||'x', b, c FROM r2;
+ COMMIT;
+}
+
+sqlite3_memdebug_fail -1
+
+finish_test
diff --git a/third_party/sqlite/test/vtab_shared.test b/third_party/sqlite/test/vtab_shared.test
new file mode 100755
index 0000000..cfc48d9
--- /dev/null
+++ b/third_party/sqlite/test/vtab_shared.test
@@ -0,0 +1,61 @@
+# 2007 April 16
+#
+# 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 tests interactions between the virtual table and
+# shared-schema functionality.
+#
+# $Id: vtab_shared.test,v 1.2 2008/03/19 13:03:34 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab||!shared_cache {
+ finish_test
+ return
+}
+
+db close
+sqlite3_enable_shared_cache 1
+sqlite3 db test.db
+
+do_test vtab_shared-1.0 {
+ register_echo_module [sqlite3_connection_pointer db]
+ catchsql {
+ CREATE TABLE t0(a, b, c);
+ CREATE VIRTUAL TABLE t1 USING echo(t0);
+ }
+} {1 {Cannot use virtual tables in shared-cache mode}}
+
+db close
+sqlite3_enable_shared_cache 0
+sqlite3 db test.db
+
+do_test vtab_shared-1.1 {
+ register_echo_module [sqlite3_connection_pointer db]
+ catchsql {
+ CREATE VIRTUAL TABLE t1 USING echo(t0);
+ }
+} {0 {}}
+
+db close
+sqlite3_enable_shared_cache 1
+sqlite3 db test.db
+
+do_test vtab_shared-1.2 {
+ register_echo_module [sqlite3_connection_pointer db]
+ catchsql {
+ SELECT * FROM t1;
+ }
+} [list 1 \
+ {malformed database schema (t1) - Cannot use virtual tables in shared-cache mode}]
+
+db close
+sqlite3_enable_shared_cache 0
+finish_test
diff --git a/third_party/sqlite/test/where.test b/third_party/sqlite/test/where.test
new file mode 100755
index 0000000..1213617
--- /dev/null
+++ b/third_party/sqlite/test/where.test
@@ -0,0 +1,1160 @@
+# 4 2001 September 15
+#
+# 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 file is testing the use of indices in WHERE clases.
+#
+# $Id: where.test,v 1.46 2008/07/15 00:27:35 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test where-1.0 {
+ execsql {
+ CREATE TABLE t1(w int, x int, y int);
+ CREATE TABLE t2(p int, q int, r int, s int);
+ }
+ for {set i 1} {$i<=100} {incr i} {
+ set w $i
+ set x [expr {int(log($i)/log(2))}]
+ set y [expr {$i*$i + 2*$i + 1}]
+ execsql "INSERT INTO t1 VALUES($w,$x,$y)"
+ }
+
+ ifcapable subquery {
+ execsql {
+ INSERT INTO t2 SELECT 101-w, x, (SELECT max(y) FROM t1)+1-y, y FROM t1;
+ }
+ } else {
+ set maxy [execsql {select max(y) from t1}]
+ execsql "
+ INSERT INTO t2 SELECT 101-w, x, $maxy+1-y, y FROM t1;
+ "
+ }
+
+ execsql {
+ CREATE INDEX i1w ON t1(w);
+ CREATE INDEX i1xy ON t1(x,y);
+ CREATE INDEX i2p ON t2(p);
+ CREATE INDEX i2r ON t2(r);
+ CREATE INDEX i2qs ON t2(q, s);
+ }
+} {}
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# Verify that queries use an index. We are using the special variable
+# "sqlite_search_count" which tallys the number of executions of MoveTo
+# and Next operators in the VDBE. By verifing that the search count is
+# small we can be assured that indices are being used properly.
+#
+do_test where-1.1.1 {
+ count {SELECT x, y, w FROM t1 WHERE w=10}
+} {3 121 10 3}
+do_test where-1.1.2 {
+ set sqlite_query_plan
+} {t1 i1w}
+do_test where-1.1.3 {
+ count {SELECT x, y, w AS abc FROM t1 WHERE abc=10}
+} {3 121 10 3}
+do_test where-1.1.4 {
+ set sqlite_query_plan
+} {t1 i1w}
+do_test where-1.2.1 {
+ count {SELECT x, y, w FROM t1 WHERE w=11}
+} {3 144 11 3}
+do_test where-1.2.2 {
+ count {SELECT x, y, w AS abc FROM t1 WHERE abc=11}
+} {3 144 11 3}
+do_test where-1.3.1 {
+ count {SELECT x, y, w AS abc FROM t1 WHERE 11=w}
+} {3 144 11 3}
+do_test where-1.3.2 {
+ count {SELECT x, y, w AS abc FROM t1 WHERE 11=abc}
+} {3 144 11 3}
+do_test where-1.4.1 {
+ count {SELECT w, x, y FROM t1 WHERE 11=w AND x>2}
+} {11 3 144 3}
+do_test where-1.4.2 {
+ set sqlite_query_plan
+} {t1 i1w}
+do_test where-1.4.3 {
+ count {SELECT w AS a, x AS b, y FROM t1 WHERE 11=a AND b>2}
+} {11 3 144 3}
+do_test where-1.4.4 {
+ set sqlite_query_plan
+} {t1 i1w}
+do_test where-1.5 {
+ count {SELECT x, y FROM t1 WHERE y<200 AND w=11 AND x>2}
+} {3 144 3}
+do_test where-1.5.2 {
+ set sqlite_query_plan
+} {t1 i1w}
+do_test where-1.6 {
+ count {SELECT x, y FROM t1 WHERE y<200 AND x>2 AND w=11}
+} {3 144 3}
+do_test where-1.7 {
+ count {SELECT x, y FROM t1 WHERE w=11 AND y<200 AND x>2}
+} {3 144 3}
+do_test where-1.8 {
+ count {SELECT x, y FROM t1 WHERE w>10 AND y=144 AND x=3}
+} {3 144 3}
+do_test where-1.8.2 {
+ set sqlite_query_plan
+} {t1 i1xy}
+do_test where-1.8.3 {
+ count {SELECT x, y FROM t1 WHERE y=144 AND x=3}
+ set sqlite_query_plan
+} {{} i1xy}
+do_test where-1.9 {
+ count {SELECT x, y FROM t1 WHERE y=144 AND w>10 AND x=3}
+} {3 144 3}
+do_test where-1.10 {
+ count {SELECT x, y FROM t1 WHERE x=3 AND w>=10 AND y=121}
+} {3 121 3}
+do_test where-1.11 {
+ count {SELECT x, y FROM t1 WHERE x=3 AND y=100 AND w<10}
+} {3 100 3}
+
+# New for SQLite version 2.1: Verify that that inequality constraints
+# are used correctly.
+#
+do_test where-1.12 {
+ count {SELECT w FROM t1 WHERE x=3 AND y<100}
+} {8 3}
+do_test where-1.13 {
+ count {SELECT w FROM t1 WHERE x=3 AND 100>y}
+} {8 3}
+do_test where-1.14 {
+ count {SELECT w FROM t1 WHERE 3=x AND y<100}
+} {8 3}
+do_test where-1.15 {
+ count {SELECT w FROM t1 WHERE 3=x AND 100>y}
+} {8 3}
+do_test where-1.16 {
+ count {SELECT w FROM t1 WHERE x=3 AND y<=100}
+} {8 9 5}
+do_test where-1.17 {
+ count {SELECT w FROM t1 WHERE x=3 AND 100>=y}
+} {8 9 5}
+do_test where-1.18 {
+ count {SELECT w FROM t1 WHERE x=3 AND y>225}
+} {15 3}
+do_test where-1.19 {
+ count {SELECT w FROM t1 WHERE x=3 AND 225<y}
+} {15 3}
+do_test where-1.20 {
+ count {SELECT w FROM t1 WHERE x=3 AND y>=225}
+} {14 15 5}
+do_test where-1.21 {
+ count {SELECT w FROM t1 WHERE x=3 AND 225<=y}
+} {14 15 5}
+do_test where-1.22 {
+ count {SELECT w FROM t1 WHERE x=3 AND y>121 AND y<196}
+} {11 12 5}
+do_test where-1.23 {
+ count {SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<=196}
+} {10 11 12 13 9}
+do_test where-1.24 {
+ count {SELECT w FROM t1 WHERE x=3 AND 121<y AND 196>y}
+} {11 12 5}
+do_test where-1.25 {
+ count {SELECT w FROM t1 WHERE x=3 AND 121<=y AND 196>=y}
+} {10 11 12 13 9}
+
+# Need to work on optimizing the BETWEEN operator.
+#
+# do_test where-1.26 {
+# count {SELECT w FROM t1 WHERE x=3 AND y BETWEEN 121 AND 196}
+# } {10 11 12 13 9}
+
+do_test where-1.27 {
+ count {SELECT w FROM t1 WHERE x=3 AND y+1==122}
+} {10 10}
+
+do_test where-1.28 {
+ count {SELECT w FROM t1 WHERE x+1=4 AND y+1==122}
+} {10 99}
+do_test where-1.29 {
+ count {SELECT w FROM t1 WHERE y==121}
+} {10 99}
+
+
+do_test where-1.30 {
+ count {SELECT w FROM t1 WHERE w>97}
+} {98 99 100 3}
+do_test where-1.31 {
+ count {SELECT w FROM t1 WHERE w>=97}
+} {97 98 99 100 4}
+do_test where-1.33 {
+ count {SELECT w FROM t1 WHERE w==97}
+} {97 2}
+do_test where-1.33.1 {
+ count {SELECT w FROM t1 WHERE w<=97 AND w==97}
+} {97 2}
+do_test where-1.33.2 {
+ count {SELECT w FROM t1 WHERE w<98 AND w==97}
+} {97 2}
+do_test where-1.33.3 {
+ count {SELECT w FROM t1 WHERE w>=97 AND w==97}
+} {97 2}
+do_test where-1.33.4 {
+ count {SELECT w FROM t1 WHERE w>96 AND w==97}
+} {97 2}
+do_test where-1.33.5 {
+ count {SELECT w FROM t1 WHERE w==97 AND w==97}
+} {97 2}
+do_test where-1.34 {
+ count {SELECT w FROM t1 WHERE w+1==98}
+} {97 99}
+do_test where-1.35 {
+ count {SELECT w FROM t1 WHERE w<3}
+} {1 2 2}
+do_test where-1.36 {
+ count {SELECT w FROM t1 WHERE w<=3}
+} {1 2 3 3}
+do_test where-1.37 {
+ count {SELECT w FROM t1 WHERE w+1<=4 ORDER BY w}
+} {1 2 3 99}
+
+do_test where-1.38 {
+ count {SELECT (w) FROM t1 WHERE (w)>(97)}
+} {98 99 100 3}
+do_test where-1.39 {
+ count {SELECT (w) FROM t1 WHERE (w)>=(97)}
+} {97 98 99 100 4}
+do_test where-1.40 {
+ count {SELECT (w) FROM t1 WHERE (w)==(97)}
+} {97 2}
+do_test where-1.41 {
+ count {SELECT (w) FROM t1 WHERE ((w)+(1))==(98)}
+} {97 99}
+
+
+# Do the same kind of thing except use a join as the data source.
+#
+do_test where-2.1 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE x=q AND y=s AND r=8977
+ }
+} {34 67 6}
+do_test where-2.2 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE x=q AND s=y AND r=8977
+ }
+} {34 67 6}
+do_test where-2.3 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE x=q AND s=y AND r=8977 AND w>10
+ }
+} {34 67 6}
+do_test where-2.4 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE p<80 AND x=q AND s=y AND r=8977 AND w>10
+ }
+} {34 67 6}
+do_test where-2.5 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE p<80 AND x=q AND 8977=r AND s=y AND w>10
+ }
+} {34 67 6}
+do_test where-2.6 {
+ count {
+ SELECT w, p FROM t2, t1
+ WHERE x=q AND p=77 AND s=y AND w>5
+ }
+} {24 77 6}
+do_test where-2.7 {
+ count {
+ SELECT w, p FROM t1, t2
+ WHERE x=q AND p>77 AND s=y AND w=5
+ }
+} {5 96 6}
+
+# Lets do a 3-way join.
+#
+do_test where-3.1 {
+ count {
+ SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
+ WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=11
+ }
+} {11 90 11 8}
+do_test where-3.2 {
+ count {
+ SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
+ WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=12
+ }
+} {12 89 12 8}
+do_test where-3.3 {
+ count {
+ SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
+ WHERE A.w=15 AND B.p=C.w AND B.r=10202-A.y
+ }
+} {15 86 86 8}
+
+# Test to see that the special case of a constant WHERE clause is
+# handled.
+#
+do_test where-4.1 {
+ count {
+ SELECT * FROM t1 WHERE 0
+ }
+} {0}
+do_test where-4.2 {
+ count {
+ SELECT * FROM t1 WHERE 1 LIMIT 1
+ }
+} {1 0 4 0}
+do_test where-4.3 {
+ execsql {
+ SELECT 99 WHERE 0
+ }
+} {}
+do_test where-4.4 {
+ execsql {
+ SELECT 99 WHERE 1
+ }
+} {99}
+do_test where-4.5 {
+ execsql {
+ SELECT 99 WHERE 0.1
+ }
+} {99}
+do_test where-4.6 {
+ execsql {
+ SELECT 99 WHERE 0.0
+ }
+} {}
+do_test where-4.7 {
+ execsql {
+ SELECT count(*) FROM t1 WHERE t1.w
+ }
+} {100}
+
+# Verify that IN operators in a WHERE clause are handled correctly.
+# Omit these tests if the build is not capable of sub-queries.
+#
+ifcapable subquery {
+ do_test where-5.1 {
+ count {
+ SELECT * FROM t1 WHERE rowid IN (1,2,3,1234) order by 1;
+ }
+ } {1 0 4 2 1 9 3 1 16 4}
+ do_test where-5.2 {
+ count {
+ SELECT * FROM t1 WHERE rowid+0 IN (1,2,3,1234) order by 1;
+ }
+ } {1 0 4 2 1 9 3 1 16 102}
+ do_test where-5.3 {
+ count {
+ SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1;
+ }
+ } {1 0 4 2 1 9 3 1 16 14}
+ do_test where-5.4 {
+ count {
+ SELECT * FROM t1 WHERE w+0 IN (-1,1,2,3) order by 1;
+ }
+ } {1 0 4 2 1 9 3 1 16 102}
+ do_test where-5.5 {
+ count {
+ SELECT * FROM t1 WHERE rowid IN
+ (select rowid from t1 where rowid IN (-1,2,4))
+ ORDER BY 1;
+ }
+ } {2 1 9 4 2 25 3}
+ do_test where-5.6 {
+ count {
+ SELECT * FROM t1 WHERE rowid+0 IN
+ (select rowid from t1 where rowid IN (-1,2,4))
+ ORDER BY 1;
+ }
+ } {2 1 9 4 2 25 103}
+ do_test where-5.7 {
+ count {
+ SELECT * FROM t1 WHERE w IN
+ (select rowid from t1 where rowid IN (-1,2,4))
+ ORDER BY 1;
+ }
+ } {2 1 9 4 2 25 9}
+ do_test where-5.8 {
+ count {
+ SELECT * FROM t1 WHERE w+0 IN
+ (select rowid from t1 where rowid IN (-1,2,4))
+ ORDER BY 1;
+ }
+ } {2 1 9 4 2 25 103}
+ do_test where-5.9 {
+ count {
+ SELECT * FROM t1 WHERE x IN (1,7) ORDER BY 1;
+ }
+ } {2 1 9 3 1 16 7}
+ do_test where-5.10 {
+ count {
+ SELECT * FROM t1 WHERE x+0 IN (1,7) ORDER BY 1;
+ }
+ } {2 1 9 3 1 16 199}
+ do_test where-5.11 {
+ count {
+ SELECT * FROM t1 WHERE y IN (6400,8100) ORDER BY 1;
+ }
+ } {79 6 6400 89 6 8100 199}
+ do_test where-5.12 {
+ count {
+ SELECT * FROM t1 WHERE x=6 AND y IN (6400,8100) ORDER BY 1;
+ }
+ } {79 6 6400 89 6 8100 7}
+ do_test where-5.13 {
+ count {
+ SELECT * FROM t1 WHERE x IN (1,7) AND y NOT IN (6400,8100) ORDER BY 1;
+ }
+ } {2 1 9 3 1 16 7}
+ do_test where-5.14 {
+ count {
+ SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,10) ORDER BY 1;
+ }
+ } {2 1 9 8}
+ do_test where-5.15 {
+ count {
+ SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,16) ORDER BY 1;
+ }
+ } {2 1 9 3 1 16 11}
+}
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+# Check out the logic that attempts to implement the ORDER BY clause
+# using an index rather than by sorting.
+#
+do_test where-6.1 {
+ execsql {
+ CREATE TABLE t3(a,b,c);
+ CREATE INDEX t3a ON t3(a);
+ CREATE INDEX t3bc ON t3(b,c);
+ CREATE INDEX t3acb ON t3(a,c,b);
+ INSERT INTO t3 SELECT w, 101-w, y FROM t1;
+ SELECT count(*), sum(a), sum(b), sum(c) FROM t3;
+ }
+} {100 5050 5050 348550}
+do_test where-6.2 {
+ cksort {
+ SELECT * FROM t3 ORDER BY a LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 nosort}
+do_test where-6.3 {
+ cksort {
+ SELECT * FROM t3 ORDER BY a+1 LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 sort}
+do_test where-6.4 {
+ cksort {
+ SELECT * FROM t3 WHERE a<10 ORDER BY a LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 nosort}
+do_test where-6.5 {
+ cksort {
+ SELECT * FROM t3 WHERE a>0 AND a<10 ORDER BY a LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 nosort}
+do_test where-6.6 {
+ cksort {
+ SELECT * FROM t3 WHERE a>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 nosort}
+do_test where-6.7 {
+ cksort {
+ SELECT * FROM t3 WHERE b>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 2 99 9 3 98 16 nosort}
+ifcapable subquery {
+ do_test where-6.8 {
+ cksort {
+ SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a LIMIT 3
+ }
+ } {1 100 4 2 99 9 3 98 16 sort}
+}
+do_test where-6.9.1 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.1.1 {
+ cksort {
+ SELECT * FROM t3 WHERE a>=1 AND a=1 AND c>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.1.2 {
+ cksort {
+ SELECT * FROM t3 WHERE a<2 AND a=1 AND c>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.2 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a,c LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.3 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.4 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.5 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c DESC LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.6 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c DESC LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.7 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c,a LIMIT 3
+ }
+} {1 100 4 sort}
+do_test where-6.9.8 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c ASC LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.9.9 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a ASC, c DESC LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.10 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.11 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a,c LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.12 {
+ cksort {
+ SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a,c,b LIMIT 3
+ }
+} {1 100 4 nosort}
+do_test where-6.13 {
+ cksort {
+ SELECT * FROM t3 WHERE a>0 ORDER BY a DESC LIMIT 3
+ }
+} {100 1 10201 99 2 10000 98 3 9801 nosort}
+do_test where-6.13.1 {
+ cksort {
+ SELECT * FROM t3 WHERE a>0 ORDER BY -a LIMIT 3
+ }
+} {100 1 10201 99 2 10000 98 3 9801 sort}
+do_test where-6.14 {
+ cksort {
+ SELECT * FROM t3 ORDER BY b LIMIT 3
+ }
+} {100 1 10201 99 2 10000 98 3 9801 nosort}
+do_test where-6.15 {
+ cksort {
+ SELECT t3.a, t1.x FROM t3, t1 WHERE t3.a=t1.w ORDER BY t3.a LIMIT 3
+ }
+} {1 0 2 1 3 1 nosort}
+do_test where-6.16 {
+ cksort {
+ SELECT t3.a, t1.x FROM t3, t1 WHERE t3.a=t1.w ORDER BY t1.x, t3.a LIMIT 3
+ }
+} {1 0 2 1 3 1 sort}
+do_test where-6.19 {
+ cksort {
+ SELECT y FROM t1 ORDER BY w LIMIT 3;
+ }
+} {4 9 16 nosort}
+do_test where-6.20 {
+ cksort {
+ SELECT y FROM t1 ORDER BY rowid LIMIT 3;
+ }
+} {4 9 16 nosort}
+do_test where-6.21 {
+ cksort {
+ SELECT y FROM t1 ORDER BY rowid, y LIMIT 3;
+ }
+} {4 9 16 nosort}
+do_test where-6.22 {
+ cksort {
+ SELECT y FROM t1 ORDER BY rowid, y DESC LIMIT 3;
+ }
+} {4 9 16 nosort}
+do_test where-6.23 {
+ cksort {
+ SELECT y FROM t1 WHERE y>4 ORDER BY rowid, w, x LIMIT 3;
+ }
+} {9 16 25 nosort}
+do_test where-6.24 {
+ cksort {
+ SELECT y FROM t1 WHERE y>=9 ORDER BY rowid, x DESC, w LIMIT 3;
+ }
+} {9 16 25 nosort}
+do_test where-6.25 {
+ cksort {
+ SELECT y FROM t1 WHERE y>4 AND y<25 ORDER BY rowid;
+ }
+} {9 16 nosort}
+do_test where-6.26 {
+ cksort {
+ SELECT y FROM t1 WHERE y>=4 AND y<=25 ORDER BY oid;
+ }
+} {4 9 16 25 nosort}
+do_test where-6.27 {
+ cksort {
+ SELECT y FROM t1 WHERE y<=25 ORDER BY _rowid_, w+y;
+ }
+} {4 9 16 25 nosort}
+
+
+# Tests for reverse-order sorting.
+#
+do_test where-7.1 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 ORDER BY y;
+ }
+} {8 9 10 11 12 13 14 15 nosort}
+do_test where-7.2 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 ORDER BY y DESC;
+ }
+} {15 14 13 12 11 10 9 8 nosort}
+do_test where-7.3 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>100 ORDER BY y LIMIT 3;
+ }
+} {10 11 12 nosort}
+do_test where-7.4 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>100 ORDER BY y DESC LIMIT 3;
+ }
+} {15 14 13 nosort}
+do_test where-7.5 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>121 ORDER BY y DESC;
+ }
+} {15 14 13 12 11 nosort}
+do_test where-7.6 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=121 ORDER BY y DESC;
+ }
+} {15 14 13 12 11 10 nosort}
+do_test where-7.7 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<196 ORDER BY y DESC;
+ }
+} {12 11 10 nosort}
+do_test where-7.8 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<=196 ORDER BY y DESC;
+ }
+} {13 12 11 10 nosort}
+do_test where-7.9 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>121 AND y<=196 ORDER BY y DESC;
+ }
+} {13 12 11 nosort}
+do_test where-7.10 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>100 AND y<196 ORDER BY y DESC;
+ }
+} {12 11 10 nosort}
+do_test where-7.11 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<196 ORDER BY y;
+ }
+} {10 11 12 nosort}
+do_test where-7.12 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=121 AND y<=196 ORDER BY y;
+ }
+} {10 11 12 13 nosort}
+do_test where-7.13 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>121 AND y<=196 ORDER BY y;
+ }
+} {11 12 13 nosort}
+do_test where-7.14 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>100 AND y<196 ORDER BY y;
+ }
+} {10 11 12 nosort}
+do_test where-7.15 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y<81 ORDER BY y;
+ }
+} {nosort}
+do_test where-7.16 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y<=81 ORDER BY y;
+ }
+} {8 nosort}
+do_test where-7.17 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>256 ORDER BY y;
+ }
+} {nosort}
+do_test where-7.18 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=256 ORDER BY y;
+ }
+} {15 nosort}
+do_test where-7.19 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y<81 ORDER BY y DESC;
+ }
+} {nosort}
+do_test where-7.20 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y<=81 ORDER BY y DESC;
+ }
+} {8 nosort}
+do_test where-7.21 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>256 ORDER BY y DESC;
+ }
+} {nosort}
+do_test where-7.22 {
+ cksort {
+ SELECT w FROM t1 WHERE x=3 AND y>=256 ORDER BY y DESC;
+ }
+} {15 nosort}
+do_test where-7.23 {
+ cksort {
+ SELECT w FROM t1 WHERE x=0 AND y<4 ORDER BY y;
+ }
+} {nosort}
+do_test where-7.24 {
+ cksort {
+ SELECT w FROM t1 WHERE x=0 AND y<=4 ORDER BY y;
+ }
+} {1 nosort}
+do_test where-7.25 {
+ cksort {
+ SELECT w FROM t1 WHERE x=6 AND y>10201 ORDER BY y;
+ }
+} {nosort}
+do_test where-7.26 {
+ cksort {
+ SELECT w FROM t1 WHERE x=6 AND y>=10201 ORDER BY y;
+ }
+} {100 nosort}
+do_test where-7.27 {
+ cksort {
+ SELECT w FROM t1 WHERE x=0 AND y<4 ORDER BY y DESC;
+ }
+} {nosort}
+do_test where-7.28 {
+ cksort {
+ SELECT w FROM t1 WHERE x=0 AND y<=4 ORDER BY y DESC;
+ }
+} {1 nosort}
+do_test where-7.29 {
+ cksort {
+ SELECT w FROM t1 WHERE x=6 AND y>10201 ORDER BY y DESC;
+ }
+} {nosort}
+do_test where-7.30 {
+ cksort {
+ SELECT w FROM t1 WHERE x=6 AND y>=10201 ORDER BY y DESC;
+ }
+} {100 nosort}
+do_test where-7.31 {
+ cksort {
+ SELECT y FROM t1 ORDER BY rowid DESC LIMIT 3
+ }
+} {10201 10000 9801 nosort}
+do_test where-7.32 {
+ cksort {
+ SELECT y FROM t1 WHERE y<25 ORDER BY rowid DESC
+ }
+} {16 9 4 nosort}
+do_test where-7.33 {
+ cksort {
+ SELECT y FROM t1 WHERE y<=25 ORDER BY rowid DESC
+ }
+} {25 16 9 4 nosort}
+do_test where-7.34 {
+ cksort {
+ SELECT y FROM t1 WHERE y<25 AND y>4 ORDER BY rowid DESC, y DESC
+ }
+} {16 9 nosort}
+do_test where-7.35 {
+ cksort {
+ SELECT y FROM t1 WHERE y<25 AND y>=4 ORDER BY rowid DESC
+ }
+} {16 9 4 nosort}
+
+do_test where-8.1 {
+ execsql {
+ CREATE TABLE t4 AS SELECT * FROM t1;
+ CREATE INDEX i4xy ON t4(x,y);
+ }
+ cksort {
+ SELECT w FROM t4 WHERE x=4 and y<1000 ORDER BY y DESC limit 3;
+ }
+} {30 29 28 nosort}
+do_test where-8.2 {
+ execsql {
+ DELETE FROM t4;
+ }
+ cksort {
+ SELECT w FROM t4 WHERE x=4 and y<1000 ORDER BY y DESC limit 3;
+ }
+} {nosort}
+
+# Make sure searches with an index work with an empty table.
+#
+do_test where-9.1 {
+ execsql {
+ CREATE TABLE t5(x PRIMARY KEY);
+ SELECT * FROM t5 WHERE x<10;
+ }
+} {}
+do_test where-9.2 {
+ execsql {
+ SELECT * FROM t5 WHERE x<10 ORDER BY x DESC;
+ }
+} {}
+do_test where-9.3 {
+ execsql {
+ SELECT * FROM t5 WHERE x=10;
+ }
+} {}
+
+do_test where-10.1 {
+ execsql {
+ SELECT 1 WHERE abs(random())<0
+ }
+} {}
+do_test where-10.2 {
+ proc tclvar_func {vname} {return [set ::$vname]}
+ db function tclvar tclvar_func
+ set ::v1 0
+ execsql {
+ SELECT count(*) FROM t1 WHERE tclvar('v1');
+ }
+} {0}
+do_test where-10.3 {
+ set ::v1 1
+ execsql {
+ SELECT count(*) FROM t1 WHERE tclvar('v1');
+ }
+} {100}
+do_test where-10.4 {
+ set ::v1 1
+ proc tclvar_func {vname} {
+ upvar #0 $vname v
+ set v [expr {!$v}]
+ return $v
+ }
+ execsql {
+ SELECT count(*) FROM t1 WHERE tclvar('v1');
+ }
+} {50}
+
+# Ticket #1376. The query below was causing a segfault.
+# The problem was the age-old error of calling realloc() on an
+# array while there are still pointers to individual elements of
+# that array.
+#
+do_test where-11.1 {
+ execsql {
+ CREATE TABLE t99(Dte INT, X INT);
+ DELETE FROM t99 WHERE (Dte = 2451337) OR (Dte = 2451339) OR
+ (Dte BETWEEN 2451345 AND 2451347) OR (Dte = 2451351) OR
+ (Dte BETWEEN 2451355 AND 2451356) OR (Dte = 2451358) OR
+ (Dte = 2451362) OR (Dte = 2451365) OR (Dte = 2451367) OR
+ (Dte BETWEEN 2451372 AND 2451376) OR (Dte BETWEEN 2451382 AND 2451384) OR
+ (Dte = 2451387) OR (Dte BETWEEN 2451389 AND 2451391) OR
+ (Dte BETWEEN 2451393 AND 2451395) OR (Dte = 2451400) OR
+ (Dte = 2451402) OR (Dte = 2451404) OR (Dte BETWEEN 2451416 AND 2451418) OR
+ (Dte = 2451422) OR (Dte = 2451426) OR (Dte BETWEEN 2451445 AND 2451446) OR
+ (Dte = 2451456) OR (Dte = 2451458) OR (Dte BETWEEN 2451465 AND 2451467) OR
+ (Dte BETWEEN 2451469 AND 2451471) OR (Dte = 2451474) OR
+ (Dte BETWEEN 2451477 AND 2451501) OR (Dte BETWEEN 2451503 AND 2451509) OR
+ (Dte BETWEEN 2451511 AND 2451514) OR (Dte BETWEEN 2451518 AND 2451521) OR
+ (Dte BETWEEN 2451523 AND 2451531) OR (Dte BETWEEN 2451533 AND 2451537) OR
+ (Dte BETWEEN 2451539 AND 2451544) OR (Dte BETWEEN 2451546 AND 2451551) OR
+ (Dte BETWEEN 2451553 AND 2451555) OR (Dte = 2451557) OR
+ (Dte BETWEEN 2451559 AND 2451561) OR (Dte = 2451563) OR
+ (Dte BETWEEN 2451565 AND 2451566) OR (Dte BETWEEN 2451569 AND 2451571) OR
+ (Dte = 2451573) OR (Dte = 2451575) OR (Dte = 2451577) OR (Dte = 2451581) OR
+ (Dte BETWEEN 2451583 AND 2451586) OR (Dte BETWEEN 2451588 AND 2451592) OR
+ (Dte BETWEEN 2451596 AND 2451598) OR (Dte = 2451600) OR
+ (Dte BETWEEN 2451602 AND 2451603) OR (Dte = 2451606) OR (Dte = 2451611);
+ }
+} {}
+
+# Ticket #2116: Make sure sorting by index works well with nn INTEGER PRIMARY
+# KEY.
+#
+do_test where-12.1 {
+ execsql {
+ CREATE TABLE t6(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t6 VALUES(1,'one');
+ INSERT INTO t6 VALUES(4,'four');
+ CREATE INDEX t6i1 ON t6(b);
+ }
+ cksort {
+ SELECT * FROM t6 ORDER BY b;
+ }
+} {4 four 1 one nosort}
+do_test where-12.2 {
+ cksort {
+ SELECT * FROM t6 ORDER BY b, a;
+ }
+} {4 four 1 one nosort}
+do_test where-12.3 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a;
+ }
+} {1 one 4 four nosort}
+do_test where-12.4 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a, b;
+ }
+} {1 one 4 four nosort}
+do_test where-12.5 {
+ cksort {
+ SELECT * FROM t6 ORDER BY b DESC;
+ }
+} {1 one 4 four nosort}
+do_test where-12.6 {
+ cksort {
+ SELECT * FROM t6 ORDER BY b DESC, a DESC;
+ }
+} {1 one 4 four nosort}
+do_test where-12.7 {
+ cksort {
+ SELECT * FROM t6 ORDER BY b DESC, a ASC;
+ }
+} {1 one 4 four sort}
+do_test where-12.8 {
+ cksort {
+ SELECT * FROM t6 ORDER BY b ASC, a DESC;
+ }
+} {4 four 1 one sort}
+do_test where-12.9 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a DESC;
+ }
+} {4 four 1 one nosort}
+do_test where-12.10 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a DESC, b DESC;
+ }
+} {4 four 1 one nosort}
+do_test where-12.11 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a DESC, b ASC;
+ }
+} {4 four 1 one nosort}
+do_test where-12.12 {
+ cksort {
+ SELECT * FROM t6 ORDER BY a ASC, b DESC;
+ }
+} {1 one 4 four nosort}
+do_test where-13.1 {
+ execsql {
+ CREATE TABLE t7(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t7 VALUES(1,'one');
+ INSERT INTO t7 VALUES(4,'four');
+ CREATE INDEX t7i1 ON t7(b);
+ }
+ cksort {
+ SELECT * FROM t7 ORDER BY b;
+ }
+} {4 four 1 one nosort}
+do_test where-13.2 {
+ cksort {
+ SELECT * FROM t7 ORDER BY b, a;
+ }
+} {4 four 1 one nosort}
+do_test where-13.3 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a;
+ }
+} {1 one 4 four nosort}
+do_test where-13.4 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a, b;
+ }
+} {1 one 4 four nosort}
+do_test where-13.5 {
+ cksort {
+ SELECT * FROM t7 ORDER BY b DESC;
+ }
+} {1 one 4 four nosort}
+do_test where-13.6 {
+ cksort {
+ SELECT * FROM t7 ORDER BY b DESC, a DESC;
+ }
+} {1 one 4 four nosort}
+do_test where-13.7 {
+ cksort {
+ SELECT * FROM t7 ORDER BY b DESC, a ASC;
+ }
+} {1 one 4 four sort}
+do_test where-13.8 {
+ cksort {
+ SELECT * FROM t7 ORDER BY b ASC, a DESC;
+ }
+} {4 four 1 one sort}
+do_test where-13.9 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a DESC;
+ }
+} {4 four 1 one nosort}
+do_test where-13.10 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a DESC, b DESC;
+ }
+} {4 four 1 one nosort}
+do_test where-13.11 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a DESC, b ASC;
+ }
+} {4 four 1 one nosort}
+do_test where-13.12 {
+ cksort {
+ SELECT * FROM t7 ORDER BY a ASC, b DESC;
+ }
+} {1 one 4 four nosort}
+
+# Ticket #2211.
+#
+# When optimizing out ORDER BY clauses, make sure that trailing terms
+# of the ORDER BY clause do not reference other tables in a join.
+#
+do_test where-14.1 {
+ execsql {
+ CREATE TABLE t8(a INTEGER PRIMARY KEY, b TEXT UNIQUE);
+ INSERT INTO t8 VALUES(1,'one');
+ INSERT INTO t8 VALUES(4,'four');
+ }
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b
+ }
+} {1/4 1/1 4/4 4/1 sort}
+do_test where-14.2 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b DESC
+ }
+} {1/1 1/4 4/1 4/4 sort}
+do_test where-14.3 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b
+ }
+} {1/1 1/4 4/1 4/4 nosort}
+do_test where-14.4 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b DESC
+ }
+} {1/1 1/4 4/1 4/4 nosort}
+do_test where-14.5 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b
+ }
+} {4/1 4/4 1/1 1/4 nosort}
+do_test where-14.6 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b DESC
+ }
+} {4/1 4/4 1/1 1/4 nosort}
+do_test where-14.7 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b
+ }
+} {4/1 4/4 1/1 1/4 sort}
+do_test where-14.7.1 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, y.a||y.b
+ }
+} {4/1 4/4 1/1 1/4 sort}
+do_test where-14.7.2 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, x.a||x.b
+ }
+} {4/1 4/4 1/1 1/4 nosort}
+do_test where-14.8 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b DESC
+ }
+} {4/4 4/1 1/4 1/1 sort}
+do_test where-14.9 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||y.b
+ }
+} {4/4 4/1 1/4 1/1 sort}
+do_test where-14.10 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||y.b DESC
+ }
+} {4/1 4/4 1/1 1/4 sort}
+do_test where-14.11 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||x.b
+ }
+} {4/1 4/4 1/1 1/4 sort}
+do_test where-14.12 {
+ cksort {
+ SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||x.b DESC
+ }
+} {4/4 4/1 1/4 1/1 sort}
+
+# Ticket #2445.
+#
+# There was a crash that could occur when a where clause contains an
+# alias for an expression in the result set, and that expression retrieves
+# a column of the second or subsequent table in a join.
+#
+do_test where-15.1 {
+ execsql {
+ CREATE TEMP TABLE t1 (a, b, c, d, e);
+ CREATE TEMP TABLE t2 (f);
+ SELECT t1.e AS alias FROM t2, t1 WHERE alias = 1 ;
+ }
+} {}
+
+integrity_check {where-99.0}
+
+finish_test
diff --git a/third_party/sqlite/test/where2.test b/third_party/sqlite/test/where2.test
new file mode 100755
index 0000000..ae7c7a3
--- /dev/null
+++ b/third_party/sqlite/test/where2.test
@@ -0,0 +1,624 @@
+# 2005 July 28
+#
+# 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 file is testing the use of indices in WHERE clauses
+# based on recent changes to the optimizer.
+#
+# $Id: where2.test,v 1.13 2007/12/10 05:03:48 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test where2-1.0 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(w int, x int, y int, z int);
+ }
+ for {set i 1} {$i<=100} {incr i} {
+ set w $i
+ set x [expr {int(log($i)/log(2))}]
+ set y [expr {$i*$i + 2*$i + 1}]
+ set z [expr {$x+$y}]
+ ifcapable tclvar {
+ execsql {INSERT INTO t1 VALUES($::w,$::x,$::y,$::z)}
+ } else {
+ execsql {INSERT INTO t1 VALUES(:w,:x,:y,:z)}
+ }
+ }
+ execsql {
+ CREATE UNIQUE INDEX i1w ON t1(w);
+ CREATE INDEX i1xy ON t1(x,y);
+ CREATE INDEX i1zyx ON t1(z,y,x);
+ COMMIT;
+ }
+} {}
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# This procedure executes the SQL. Then it checks to see if the OP_Sort
+# opcode was executed. If an OP_Sort did occur, then "sort" is appended
+# to the result. If no OP_Sort happened, then "nosort" is appended.
+#
+# This procedure is used to check to make sure sorting is or is not
+# occurring as expected.
+#
+proc cksort {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return $data
+}
+
+# This procedure executes the SQL. Then it appends to the result the
+# "sort" or "nosort" keyword (as in the cksort procedure above) then
+# it appends the ::sqlite_query_plan variable.
+#
+proc queryplan {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ if {$::sqlite_sort_count} {set x sort} {set x nosort}
+ lappend data $x
+ return [concat $data $::sqlite_query_plan]
+}
+
+
+# Prefer a UNIQUE index over another index.
+#
+do_test where2-1.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396
+ }
+} {85 6 7396 7402 nosort t1 i1w}
+
+# Always prefer a rowid== constraint over any other index.
+#
+do_test where2-1.3 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=85 AND x=6 AND y=7396 AND rowid=85
+ }
+} {85 6 7396 7402 nosort t1 *}
+
+# When constrained by a UNIQUE index, the ORDER BY clause is always ignored.
+#
+do_test where2-2.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=85 ORDER BY random(5);
+ }
+} {85 6 7396 7402 nosort t1 i1w}
+do_test where2-2.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE x=6 AND y=7396 ORDER BY random(5);
+ }
+} {85 6 7396 7402 sort t1 i1xy}
+do_test where2-2.3 {
+ queryplan {
+ SELECT * FROM t1 WHERE rowid=85 AND x=6 AND y=7396 ORDER BY random(5);
+ }
+} {85 6 7396 7402 nosort t1 *}
+
+
+# Efficient handling of forward and reverse table scans.
+#
+do_test where2-3.1 {
+ queryplan {
+ SELECT * FROM t1 ORDER BY rowid LIMIT 2
+ }
+} {1 0 4 4 2 1 9 10 nosort t1 *}
+do_test where2-3.2 {
+ queryplan {
+ SELECT * FROM t1 ORDER BY rowid DESC LIMIT 2
+ }
+} {100 6 10201 10207 99 6 10000 10006 nosort t1 *}
+
+# The IN operator can be used by indices at multiple layers
+#
+ifcapable subquery {
+ do_test where2-4.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (10207,10006) AND y IN (10000,10201)
+ AND x>0 AND x<10
+ ORDER BY w
+ }
+ } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+ do_test where2-4.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (10207,10006) AND y=10000
+ AND x>0 AND x<10
+ ORDER BY w
+ }
+ } {99 6 10000 10006 sort t1 i1zyx}
+ do_test where2-4.3 {
+ queryplan {
+ SELECT * FROM t1 WHERE z=10006 AND y IN (10000,10201)
+ AND x>0 AND x<10
+ ORDER BY w
+ }
+ } {99 6 10000 10006 sort t1 i1zyx}
+ ifcapable compound {
+ do_test where2-4.4 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006)
+ AND y IN (10000,10201)
+ AND x>0 AND x<10
+ ORDER BY w
+ }
+ } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+ do_test where2-4.5 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (SELECT 10207 UNION SELECT 10006)
+ AND y IN (SELECT 10000 UNION SELECT 10201)
+ AND x>0 AND x<10
+ ORDER BY w
+ }
+ } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+ }
+ do_test where2-4.6 {
+ queryplan {
+ SELECT * FROM t1
+ WHERE x IN (1,2,3,4,5,6,7,8)
+ AND y IN (10000,10001,10002,10003,10004,10005)
+ ORDER BY 2
+ }
+ } {99 6 10000 10006 sort t1 i1xy}
+
+ # Duplicate entires on the RHS of an IN operator do not cause duplicate
+ # output rows.
+ #
+ do_test where2-4.6 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207)
+ ORDER BY w
+ }
+ } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+ ifcapable compound {
+ do_test where2-4.7 {
+ queryplan {
+ SELECT * FROM t1 WHERE z IN (
+ SELECT 10207 UNION ALL SELECT 10006
+ UNION ALL SELECT 10006 UNION ALL SELECT 10207)
+ ORDER BY w
+ }
+ } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx}
+ }
+
+} ;# ifcapable subquery
+
+# The use of an IN operator disables the index as a sorter.
+#
+do_test where2-5.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=99 ORDER BY w
+ }
+} {99 6 10000 10006 nosort t1 i1w}
+
+ifcapable subquery {
+ do_test where2-5.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE w IN (99) ORDER BY w
+ }
+ } {99 6 10000 10006 sort t1 i1w}
+}
+
+# Verify that OR clauses get translated into IN operators.
+#
+set ::idx {}
+ifcapable subquery {set ::idx i1w}
+do_test where2-6.1.1 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=99 OR w=100 ORDER BY +w
+ }
+} [list 99 6 10000 10006 100 6 10201 10207 sort t1 $::idx]
+do_test where2-6.1.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE 99=w OR 100=w ORDER BY +w
+ }
+} [list 99 6 10000 10006 100 6 10201 10207 sort t1 $::idx]
+do_test where2-6.2 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=99 OR w=100 OR 6=w ORDER BY +w
+ }
+} [list 6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 $::idx]
+
+do_test where2-6.3 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=99 OR w=100 OR 6=+w ORDER BY +w
+ }
+} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 {}}
+do_test where2-6.4 {
+ queryplan {
+ SELECT * FROM t1 WHERE w=99 OR +w=100 OR 6=w ORDER BY +w
+ }
+} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 {}}
+
+set ::idx {}
+ifcapable subquery {set ::idx i1zyx}
+do_test where2-6.5 {
+ queryplan {
+ SELECT b.* FROM t1 a, t1 b
+ WHERE a.w=1 AND (a.y=b.z OR b.z=10)
+ ORDER BY +b.w
+ }
+} [list 1 0 4 4 2 1 9 10 sort a i1w b $::idx]
+do_test where2-6.6 {
+ queryplan {
+ SELECT b.* FROM t1 a, t1 b
+ WHERE a.w=1 AND (b.z=10 OR a.y=b.z OR b.z=10)
+ ORDER BY +b.w
+ }
+} [list 1 0 4 4 2 1 9 10 sort a i1w b $::idx]
+
+# Ticket #2249. Make sure the OR optimization is not attempted if
+# comparisons between columns of different affinities are needed.
+#
+do_test where2-6.7 {
+ execsql {
+ CREATE TABLE t2249a(a TEXT UNIQUE);
+ CREATE TABLE t2249b(b INTEGER);
+ INSERT INTO t2249a VALUES('0123');
+ INSERT INTO t2249b VALUES(123);
+ }
+ queryplan {
+ -- Because a is type TEXT and b is type INTEGER, both a and b
+ -- will attempt to convert to NUMERIC before the comparison.
+ -- They will thus compare equal.
+ --
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=b;
+ }
+} {123 0123 nosort t2249b {} t2249a {}}
+do_test where2-6.9 {
+ queryplan {
+ -- The + operator removes affinity from the rhs. No conversions
+ -- occur and the comparison is false. The result is an empty set.
+ --
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b;
+ }
+} {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+do_test where2-6.9.2 {
+ # The same thing but with the expression flipped around.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE +b=a
+ }
+} {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+do_test where2-6.10 {
+ queryplan {
+ -- Use + on both sides of the comparison to disable indices
+ -- completely. Make sure we get the same result.
+ --
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE +a=+b;
+ }
+} {nosort t2249b {} t2249a {}}
+do_test where2-6.11 {
+ # This will not attempt the OR optimization because of the a=b
+ # comparison.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=b OR a='hello';
+ }
+} {123 0123 nosort t2249b {} t2249a {}}
+do_test where2-6.11.2 {
+ # Permutations of the expression terms.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE b=a OR a='hello';
+ }
+} {123 0123 nosort t2249b {} t2249a {}}
+do_test where2-6.11.3 {
+ # Permutations of the expression terms.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE 'hello'=a OR b=a;
+ }
+} {123 0123 nosort t2249b {} t2249a {}}
+do_test where2-6.11.4 {
+ # Permutations of the expression terms.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR b=a;
+ }
+} {123 0123 nosort t2249b {} t2249a {}}
+ifcapable explain&&subquery {
+ # These tests are not run if subquery support is not included in the
+ # build. This is because these tests test the "a = 1 OR a = 2" to
+ # "a IN (1, 2)" optimisation transformation, which is not enabled if
+ # subqueries and the IN operator is not available.
+ #
+ do_test where2-6.12 {
+ # In this case, the +b disables the affinity conflict and allows
+ # the OR optimization to be used again. The result is now an empty
+ # set, the same as in where2-6.9.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b OR a='hello';
+ }
+ } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ do_test where2-6.12.2 {
+ # In this case, the +b disables the affinity conflict and allows
+ # the OR optimization to be used again. The result is now an empty
+ # set, the same as in where2-6.9.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR +b=a;
+ }
+ } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ do_test where2-6.12.3 {
+ # In this case, the +b disables the affinity conflict and allows
+ # the OR optimization to be used again. The result is now an empty
+ # set, the same as in where2-6.9.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE +b=a OR a='hello';
+ }
+ } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ do_test where2-6.13 {
+ # The addition of +a on the second term disabled the OR optimization.
+ # But we should still get the same empty-set result as in where2-6.9.
+ queryplan {
+ SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b OR +a='hello';
+ }
+ } {nosort t2249b {} t2249a {}}
+}
+
+# Variations on the order of terms in a WHERE clause in order
+# to make sure the OR optimizer can recognize them all.
+do_test where2-6.20 {
+ queryplan {
+ SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a
+ }
+} {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ifcapable explain&&subquery {
+ # These tests are not run if subquery support is not included in the
+ # build. This is because these tests test the "a = 1 OR a = 2" to
+ # "a IN (1, 2)" optimisation transformation, which is not enabled if
+ # subqueries and the IN operator is not available.
+ #
+ do_test where2-6.21 {
+ queryplan {
+ SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a OR y.a='hello'
+ }
+ } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ do_test where2-6.22 {
+ queryplan {
+ SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE y.a=x.a OR y.a='hello'
+ }
+ } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ do_test where2-6.23 {
+ queryplan {
+ SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE y.a='hello' OR x.a=y.a
+ }
+ } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+}
+
+# Unique queries (queries that are guaranteed to return only a single
+# row of result) do not call the sorter. But all tables must give
+# a unique result. If any one table in the join does not give a unique
+# result then sorting is necessary.
+#
+do_test where2-7.1 {
+ cksort {
+ create table t8(a unique, b, c);
+ insert into t8 values(1,2,3);
+ insert into t8 values(2,3,4);
+ create table t9(x,y);
+ insert into t9 values(2,4);
+ insert into t9 values(2,3);
+ select y from t8, t9 where a=1 order by a, y;
+ }
+} {3 4 sort}
+do_test where2-7.2 {
+ cksort {
+ select * from t8 where a=1 order by b, c
+ }
+} {1 2 3 nosort}
+do_test where2-7.3 {
+ cksort {
+ select * from t8, t9 where a=1 and y=3 order by b, x
+ }
+} {1 2 3 2 3 sort}
+do_test where2-7.4 {
+ cksort {
+ create unique index i9y on t9(y);
+ select * from t8, t9 where a=1 and y=3 order by b, x
+ }
+} {1 2 3 2 3 nosort}
+
+# Ticket #1807. Using IN constrains on multiple columns of
+# a multi-column index.
+#
+ifcapable subquery {
+ do_test where2-8.1 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (20,21) AND y IN (1,2)
+ }
+ } {}
+ do_test where2-8.2 {
+ execsql {
+ SELECT * FROM t1 WHERE x IN (1,2) AND y IN (-5,-6)
+ }
+ } {}
+ execsql {CREATE TABLE tx AS SELECT * FROM t1}
+ do_test where2-8.3 {
+ execsql {
+ SELECT w FROM t1
+ WHERE x IN (SELECT x FROM tx WHERE rowid<0)
+ AND +y IN (SELECT y FROM tx WHERE rowid=1)
+ }
+ } {}
+ do_test where2-8.4 {
+ execsql {
+ SELECT w FROM t1
+ WHERE x IN (SELECT x FROM tx WHERE rowid=1)
+ AND y IN (SELECT y FROM tx WHERE rowid<0)
+ }
+ } {}
+ #set sqlite_where_trace 1
+ do_test where2-8.5 {
+ execsql {
+ CREATE INDEX tx_xyz ON tx(x, y, z, w);
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 12 AND 14)
+ }
+ } {12 13 14}
+ do_test where2-8.6 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 12 AND 14)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {12 13 14}
+ do_test where2-8.7 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 12 AND 14)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {10 11 12 13 14 15}
+ do_test where2-8.8 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {10 11 12 13 14 15 16 17 18 19 20}
+ do_test where2-8.9 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 2 AND 4)
+ }
+ } {}
+ do_test where2-8.10 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 2 AND 4)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.11 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 2 AND 4)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.12 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN -4 AND -2)
+ }
+ } {}
+ do_test where2-8.13 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN -4 AND -2)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.14 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN -4 AND -2)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.15 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 200 AND 300)
+ }
+ } {}
+ do_test where2-8.16 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 200 AND 300)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.17 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE w BETWEEN 200 AND 300)
+ AND y IN (SELECT y FROM t1 WHERE w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.18 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE +w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE +w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE +w BETWEEN 200 AND 300)
+ }
+ } {}
+ do_test where2-8.19 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE +w BETWEEN 10 AND 20)
+ AND y IN (SELECT y FROM t1 WHERE +w BETWEEN 200 AND 300)
+ AND z IN (SELECT z FROM t1 WHERE +w BETWEEN 10 AND 20)
+ }
+ } {}
+ do_test where2-8.20 {
+ execsql {
+ SELECT w FROM tx
+ WHERE x IN (SELECT x FROM t1 WHERE +w BETWEEN 200 AND 300)
+ AND y IN (SELECT y FROM t1 WHERE +w BETWEEN 10 AND 20)
+ AND z IN (SELECT z FROM t1 WHERE +w BETWEEN 10 AND 20)
+ }
+ } {}
+}
+
+# Make sure WHERE clauses of the form A=1 AND (B=2 OR B=3) are optimized
+# when we have an index on A and B.
+#
+ifcapable or_opt&&tclvar {
+ do_test where2-9.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t10(a,b,c);
+ INSERT INTO t10 VALUES(1,1,1);
+ INSERT INTO t10 VALUES(1,2,2);
+ INSERT INTO t10 VALUES(1,3,3);
+ }
+ for {set i 4} {$i<=1000} {incr i} {
+ execsql {INSERT INTO t10 VALUES(1,$i,$i)}
+ }
+ execsql {
+ CREATE INDEX i10 ON t10(a,b);
+ COMMIT;
+ SELECT count(*) FROM t10;
+ }
+ } 1000
+ ifcapable subquery {
+ do_test where2-9.2 {
+ count {
+ SELECT * FROM t10 WHERE a=1 AND (b=2 OR b=3)
+ }
+ } {1 2 2 1 3 3 7}
+ }
+}
+
+finish_test
diff --git a/third_party/sqlite/test/where3.test b/third_party/sqlite/test/where3.test
new file mode 100755
index 0000000..c395d0a
--- /dev/null
+++ b/third_party/sqlite/test/where3.test
@@ -0,0 +1,216 @@
+# 2006 January 31
+#
+# 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 file is testing the join reordering optimization
+# in cases that include a LEFT JOIN.
+#
+# $Id: where3.test,v 1.4 2008/04/17 19:14:02 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# The following is from ticket #1652.
+#
+# A comma join then a left outer join: A,B left join C.
+# Arrange indices so that the B table is chosen to go first.
+# Also put an index on C, but make sure that A is chosen before C.
+#
+do_test where3-1.1 {
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(p, q);
+ CREATE TABLE t3(x, y);
+
+ INSERT INTO t1 VALUES(111,'one');
+ INSERT INTO t1 VALUES(222,'two');
+ INSERT INTO t1 VALUES(333,'three');
+
+ INSERT INTO t2 VALUES(1,111);
+ INSERT INTO t2 VALUES(2,222);
+ INSERT INTO t2 VALUES(4,444);
+ CREATE INDEX t2i1 ON t2(p);
+
+ INSERT INTO t3 VALUES(999,'nine');
+ CREATE INDEX t3i1 ON t3(x);
+
+ SELECT * FROM t1, t2 LEFT JOIN t3 ON q=x WHERE p=2 AND a=q;
+ }
+} {222 two 2 222 {} {}}
+
+ifcapable explain {
+ do_test where3-1.1.1 {
+ explain_no_trace {SELECT * FROM t1, t2 LEFT JOIN t3 ON q=x
+ WHERE p=2 AND a=q}
+ } [explain_no_trace {SELECT * FROM t1, t2 LEFT JOIN t3 ON x=q
+ WHERE p=2 AND a=q}]
+}
+
+# Ticket #1830
+#
+# This is similar to the above but with the LEFT JOIN on the
+# other side.
+#
+do_test where3-1.2 {
+ execsql {
+ CREATE TABLE parent1(parent1key, child1key, Child2key, child3key);
+ CREATE TABLE child1 ( child1key NVARCHAR, value NVARCHAR );
+ CREATE UNIQUE INDEX PKIDXChild1 ON child1 ( child1key );
+ CREATE TABLE child2 ( child2key NVARCHAR, value NVARCHAR );
+
+ INSERT INTO parent1(parent1key,child1key,child2key)
+ VALUES ( 1, 'C1.1', 'C2.1' );
+ INSERT INTO child1 ( child1key, value ) VALUES ( 'C1.1', 'Value for C1.1' );
+ INSERT INTO child2 ( child2key, value ) VALUES ( 'C2.1', 'Value for C2.1' );
+
+ INSERT INTO parent1 ( parent1key, child1key, child2key )
+ VALUES ( 2, 'C1.2', 'C2.2' );
+ INSERT INTO child2 ( child2key, value ) VALUES ( 'C2.2', 'Value for C2.2' );
+
+ INSERT INTO parent1 ( parent1key, child1key, child2key )
+ VALUES ( 3, 'C1.3', 'C2.3' );
+ INSERT INTO child1 ( child1key, value ) VALUES ( 'C1.3', 'Value for C1.3' );
+ INSERT INTO child2 ( child2key, value ) VALUES ( 'C2.3', 'Value for C2.3' );
+
+ SELECT parent1.parent1key, child1.value, child2.value
+ FROM parent1
+ LEFT OUTER JOIN child1 ON child1.child1key = parent1.child1key
+ INNER JOIN child2 ON child2.child2key = parent1.child2key;
+ }
+} {1 {Value for C1.1} {Value for C2.1} 2 {} {Value for C2.2} 3 {Value for C1.3} {Value for C2.3}}
+
+ifcapable explain {
+ do_test where3-1.2.1 {
+ explain_no_trace {
+ SELECT parent1.parent1key, child1.value, child2.value
+ FROM parent1
+ LEFT OUTER JOIN child1 ON child1.child1key = parent1.child1key
+ INNER JOIN child2 ON child2.child2key = parent1.child2key;
+ }
+ } [explain_no_trace {
+ SELECT parent1.parent1key, child1.value, child2.value
+ FROM parent1
+ LEFT OUTER JOIN child1 ON parent1.child1key = child1.child1key
+ INNER JOIN child2 ON child2.child2key = parent1.child2key;
+ }]
+}
+
+# This procedure executes the SQL. Then it appends
+# the ::sqlite_query_plan variable.
+#
+proc queryplan {sql} {
+ set ::sqlite_sort_count 0
+ set data [execsql $sql]
+ return [concat $data $::sqlite_query_plan]
+}
+
+
+# If you have a from clause of the form: A B C left join D
+# then make sure the query optimizer is able to reorder the
+# A B C part anyway it wants.
+#
+# Following the fix to ticket #1652, there was a time when
+# the C table would not reorder. So the following reorderings
+# were possible:
+#
+# A B C left join D
+# B A C left join D
+#
+# But these reorders were not allowed
+#
+# C A B left join D
+# A C B left join D
+# C B A left join D
+# B C A left join D
+#
+# The following tests are here to verify that the latter four
+# reorderings are allowed again.
+#
+do_test where3-2.1 {
+ execsql {
+ CREATE TABLE tA(apk integer primary key, ax);
+ CREATE TABLE tB(bpk integer primary key, bx);
+ CREATE TABLE tC(cpk integer primary key, cx);
+ CREATE TABLE tD(dpk integer primary key, dx);
+ }
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=bx AND bpk=ax
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.1.1 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk
+ WHERE cpk=bx AND bpk=ax
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.1.2 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk
+ WHERE bx=cpk AND bpk=ax
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.1.3 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk
+ WHERE bx=cpk AND ax=bpk
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.1.4 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE bx=cpk AND ax=bpk
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.1.5 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=bx AND ax=bpk
+ }
+} {tA {} tB * tC * tD *}
+do_test where3-2.2 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=bx AND apk=bx
+ }
+} {tB {} tA * tC * tD *}
+do_test where3-2.3 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=bx AND apk=bx
+ }
+} {tB {} tA * tC * tD *}
+do_test where3-2.4 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE apk=cx AND bpk=ax
+ }
+} {tC {} tA * tB * tD *}
+do_test where3-2.5 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=ax AND bpk=cx
+ }
+} {tA {} tC * tB * tD *}
+do_test where3-2.5 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE bpk=cx AND apk=bx
+ }
+} {tC {} tB * tA * tD *}
+do_test where3-2.6 {
+ queryplan {
+ SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
+ WHERE cpk=bx AND apk=cx
+ }
+} {tB {} tC * tA * tD *}
+
+
+finish_test
diff --git a/third_party/sqlite/test/where4.test b/third_party/sqlite/test/where4.test
new file mode 100755
index 0000000..280eb5f
--- /dev/null
+++ b/third_party/sqlite/test/where4.test
@@ -0,0 +1,273 @@
+# 2006 October 27
+#
+# 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 file is testing the use of indices in WHERE clauses.
+# This file was created when support for optimizing IS NULL phrases
+# was added. And so the principle purpose of this file is to test
+# that IS NULL phrases are correctly optimized. But you can never
+# have too many tests, so some other tests are thrown in as well.
+#
+# $Id: where4.test,v 1.6 2007/12/10 05:03:48 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !tclvar||!bloblit {
+ finish_test
+ return
+}
+
+# Build some test data
+#
+do_test where4-1.0 {
+ execsql {
+ CREATE TABLE t1(w, x, y);
+ CREATE INDEX i1wxy ON t1(w,x,y);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(1,NULL,3);
+ INSERT INTO t1 VALUES('a','b','c');
+ INSERT INTO t1 VALUES('a',NULL,'c');
+ INSERT INTO t1 VALUES(X'78',x'79',x'7a');
+ INSERT INTO t1 VALUES(X'78',NULL,X'7A');
+ INSERT INTO t1 VALUES(NULL,NULL,NULL);
+ SELECT count(*) FROM t1;
+ }
+} {7}
+
+# Do an SQL statement. Append the search count to the end of the result.
+#
+proc count sql {
+ set ::sqlite_search_count 0
+ return [concat [execsql $sql] $::sqlite_search_count]
+}
+
+# Verify that queries use an index. We are using the special variable
+# "sqlite_search_count" which tallys the number of executions of MoveTo
+# and Next operators in the VDBE. By verifing that the search count is
+# small we can be assured that indices are being used properly.
+#
+do_test where4-1.1 {
+ count {SELECT rowid FROM t1 WHERE w IS NULL}
+} {7 2}
+do_test where4-1.2 {
+ count {SELECT rowid FROM t1 WHERE +w IS NULL}
+} {7 6}
+do_test where4-1.3 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND x IS NULL}
+} {2 2}
+do_test where4-1.4 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND +x IS NULL}
+} {2 3}
+do_test where4-1.5 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND x>0}
+} {1 2}
+do_test where4-1.6 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND x<9}
+} {1 3}
+do_test where4-1.7 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND x IS NULL AND y=3}
+} {2 2}
+do_test where4-1.8 {
+ count {SELECT rowid FROM t1 WHERE w=1 AND x IS NULL AND y>2}
+} {2 2}
+do_test where4-1.9 {
+ count {SELECT rowid FROM t1 WHERE w='a' AND x IS NULL AND y='c'}
+} {4 2}
+do_test where4-1.10 {
+ count {SELECT rowid FROM t1 WHERE w=x'78' AND x IS NULL}
+} {6 2}
+do_test where4-1.11 {
+ count {SELECT rowid FROM t1 WHERE w=x'78' AND x IS NULL AND y=123}
+} {1}
+do_test where4-1.12 {
+ count {SELECT rowid FROM t1 WHERE w=x'78' AND x IS NULL AND y=x'7A'}
+} {6 2}
+do_test where4-1.13 {
+ count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL}
+} {7 2}
+do_test where4-1.14 {
+ count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL AND y IS NULL}
+} {7 2}
+do_test where4-1.15 {
+ count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL AND y<0}
+} {2}
+do_test where4-1.16 {
+ count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL AND y>=0}
+} {1}
+
+do_test where4-2.1 {
+ execsql {SELECT rowid FROM t1 ORDER BY w, x, y}
+} {7 2 1 4 3 6 5}
+do_test where4-2.2 {
+ execsql {SELECT rowid FROM t1 ORDER BY w DESC, x, y}
+} {6 5 4 3 2 1 7}
+do_test where4-2.3 {
+ execsql {SELECT rowid FROM t1 ORDER BY w, x DESC, y}
+} {7 1 2 3 4 5 6}
+
+
+# Ticket #2177
+#
+# Suppose you have a left join where the right table of the left
+# join (the one that can be NULL) has an index on two columns.
+# The first indexed column is used in the ON clause of the join.
+# The second indexed column is used in the WHERE clause with an IS NULL
+# constraint. It is not allowed to use the IS NULL optimization to
+# optimize the query because the second column might be NULL because
+# the right table did not match - something the index does not know
+# about.
+#
+do_test where4-3.1 {
+ execsql {
+ CREATE TABLE t2(a);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ INSERT INTO t2 VALUES(3);
+ CREATE TABLE t3(x,y,UNIQUE(x,y));
+ INSERT INTO t3 VALUES(1,11);
+ INSERT INTO t3 VALUES(2,NULL);
+
+ SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE +y IS NULL;
+ }
+} {2 2 {} 3 {} {}}
+do_test where4-3.2 {
+ execsql {
+ SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE y IS NULL;
+ }
+} {2 2 {} 3 {} {}}
+
+# Ticket #2189. Probably the same bug as #2177.
+#
+do_test where4-4.1 {
+ execsql {
+ CREATE TABLE test(col1 TEXT PRIMARY KEY);
+ INSERT INTO test(col1) values('a');
+ INSERT INTO test(col1) values('b');
+ INSERT INTO test(col1) values('c');
+ CREATE TABLE test2(col1 TEXT PRIMARY KEY);
+ INSERT INTO test2(col1) values('a');
+ INSERT INTO test2(col1) values('b');
+ INSERT INTO test2(col1) values('c');
+ SELECT * FROM test t1 LEFT OUTER JOIN test2 t2 ON t1.col1 = t2.col1
+ WHERE +t2.col1 IS NULL;
+ }
+} {}
+do_test where4-4.2 {
+ execsql {
+ SELECT * FROM test t1 LEFT OUTER JOIN test2 t2 ON t1.col1 = t2.col1
+ WHERE t2.col1 IS NULL;
+ }
+} {}
+do_test where4-4.3 {
+ execsql {
+ SELECT * FROM test t1 LEFT OUTER JOIN test2 t2 ON t1.col1 = t2.col1
+ WHERE +t1.col1 IS NULL;
+ }
+} {}
+do_test where4-4.4 {
+ execsql {
+ SELECT * FROM test t1 LEFT OUTER JOIN test2 t2 ON t1.col1 = t2.col1
+ WHERE t1.col1 IS NULL;
+ }
+} {}
+
+# Ticket #2273. Problems with IN operators and NULLs.
+#
+ifcapable subquery {
+do_test where4-5.1 {
+ execsql {
+ CREATE TABLE t4(x,y,z,PRIMARY KEY(x,y));
+ }
+ execsql {
+ SELECT *
+ FROM t2 LEFT JOIN t4 b1
+ LEFT JOIN t4 b2 ON b2.x=b1.x AND b2.y IN (b1.y);
+ }
+} {1 {} {} {} {} {} {} 2 {} {} {} {} {} {} 3 {} {} {} {} {} {}}
+do_test where4-5.2 {
+ execsql {
+ INSERT INTO t4 VALUES(1,1,11);
+ INSERT INTO t4 VALUES(1,2,12);
+ INSERT INTO t4 VALUES(1,3,13);
+ INSERT INTO t4 VALUES(2,2,22);
+ SELECT rowid FROM t4 WHERE x IN (1,9,2,5) AND y IN (1,3,NULL,2) AND z!=13;
+ }
+} {1 2 4}
+do_test where4-5.3 {
+ execsql {
+ SELECT rowid FROM t4 WHERE x IN (1,9,NULL,2) AND y IN (1,3,2) AND z!=13;
+ }
+} {1 2 4}
+do_test where4-6.1 {
+ execsql {
+ CREATE TABLE t5(a,b,c,d,e,f,UNIQUE(a,b,c,d,e,f));
+ INSERT INTO t5 VALUES(1,1,1,1,1,11111);
+ INSERT INTO t5 VALUES(2,2,2,2,2,22222);
+ INSERT INTO t5 VALUES(1,2,3,4,5,12345);
+ INSERT INTO t5 VALUES(2,3,4,5,6,23456);
+ }
+ execsql {
+ SELECT rowid FROM t5
+ WHERE a IN (1,9,2) AND b=2 AND c IN (1,2,3,4) AND d>0
+ }
+} {3 2}
+do_test where4-6.2 {
+ execsql {
+ SELECT rowid FROM t5
+ WHERE a IN (1,NULL,2) AND b=2 AND c IN (1,2,3,4) AND d>0
+ }
+} {3 2}
+do_test where4-7.1 {
+ execsql {
+ CREATE TABLE t6(y,z,PRIMARY KEY(y,z));
+ }
+ execsql {
+ SELECT * FROM t6 WHERE y=NULL AND z IN ('hello');
+ }
+} {}
+
+integrity_check {where4-99.0}
+
+do_test where4-7.1 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t8(a, b, c, d);
+ CREATE INDEX t8_i ON t8(a, b, c);
+ CREATE TABLE t7(i);
+
+ INSERT INTO t7 VALUES(1);
+ INSERT INTO t7 SELECT i*2 FROM t7;
+ INSERT INTO t7 SELECT i*2 FROM t7;
+ INSERT INTO t7 SELECT i*2 FROM t7;
+ INSERT INTO t7 SELECT i*2 FROM t7;
+ INSERT INTO t7 SELECT i*2 FROM t7;
+ INSERT INTO t7 SELECT i*2 FROM t7;
+
+ COMMIT;
+ }
+} {}
+
+# At one point the sub-select inside the aggregate sum() function in the
+# following query was leaking a couple of stack entries. This query
+# runs the SELECT in a loop enough times that an assert() fails. Or rather,
+# did fail before the bug was fixed.
+#
+do_test where4-7.2 {
+ execsql {
+ SELECT sum((
+ SELECT d FROM t8 WHERE a = i AND b = i AND c < NULL
+ )) FROM t7;
+ }
+} {{}}
+
+}; #ifcapable subquery
+
+finish_test
diff --git a/third_party/sqlite/test/where5.test b/third_party/sqlite/test/where5.test
new file mode 100755
index 0000000..760224c
--- /dev/null
+++ b/third_party/sqlite/test/where5.test
@@ -0,0 +1,288 @@
+# 2007 June 8
+#
+# 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 file is testing NULL comparisons in the WHERE clause.
+# See ticket #2404.
+#
+# $Id: where5.test,v 1.2 2007/06/08 08:43:10 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test where5-1.0 {
+ execsql {
+ CREATE TABLE t1(x TEXT);
+ CREATE TABLE t2(x INTEGER);
+ CREATE TABLE t3(x INTEGER PRIMARY KEY);
+ INSERT INTO t1 VALUES(-1);
+ INSERT INTO t1 VALUES(0);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t2 SELECT * FROM t1;
+ INSERT INTO t3 SELECT * FROM t2;
+ }
+ execsql {
+ SELECT * FROM t1 WHERE x<0
+ }
+} {-1}
+do_test where5-1.1 {
+ execsql {
+ SELECT * FROM t1 WHERE x<=0
+ }
+} {-1 0}
+do_test where5-1.2 {
+ execsql {
+ SELECT * FROM t1 WHERE x=0
+ }
+} {0}
+do_test where5-1.3 {
+ execsql {
+ SELECT * FROM t1 WHERE x>=0
+ }
+} {0 1}
+do_test where5-1.4 {
+ execsql {
+ SELECT * FROM t1 WHERE x>0
+ }
+} {1}
+do_test where5-1.5 {
+ execsql {
+ SELECT * FROM t1 WHERE x<>0
+ }
+} {-1 1}
+do_test where5-1.6 {
+ execsql {
+ SELECT * FROM t1 WHERE x<NULL
+ }
+} {}
+do_test where5-1.7 {
+ execsql {
+ SELECT * FROM t1 WHERE x<=NULL
+ }
+} {}
+do_test where5-1.8 {
+ execsql {
+ SELECT * FROM t1 WHERE x=NULL
+ }
+} {}
+do_test where5-1.9 {
+ execsql {
+ SELECT * FROM t1 WHERE x>=NULL
+ }
+} {}
+do_test where5-1.10 {
+ execsql {
+ SELECT * FROM t1 WHERE x>NULL
+ }
+} {}
+do_test where5-1.11 {
+ execsql {
+ SELECT * FROM t1 WHERE x!=NULL
+ }
+} {}
+do_test where5-1.12 {
+ execsql {
+ SELECT * FROM t1 WHERE x IS NULL
+ }
+} {}
+do_test where5-1.13 {
+ execsql {
+ SELECT * FROM t1 WHERE x IS NOT NULL
+ }
+} {-1 0 1}
+
+
+do_test where5-2.0 {
+ execsql {
+ SELECT * FROM t2 WHERE x<0
+ }
+} {-1}
+do_test where5-2.1 {
+ execsql {
+ SELECT * FROM t2 WHERE x<=0
+ }
+} {-1 0}
+do_test where5-2.2 {
+ execsql {
+ SELECT * FROM t2 WHERE x=0
+ }
+} {0}
+do_test where5-2.3 {
+ execsql {
+ SELECT * FROM t2 WHERE x>=0
+ }
+} {0 1}
+do_test where5-2.4 {
+ execsql {
+ SELECT * FROM t2 WHERE x>0
+ }
+} {1}
+do_test where5-2.5 {
+ execsql {
+ SELECT * FROM t2 WHERE x<>0
+ }
+} {-1 1}
+do_test where5-2.6 {
+ execsql {
+ SELECT * FROM t2 WHERE x<NULL
+ }
+} {}
+do_test where5-2.7 {
+ execsql {
+ SELECT * FROM t2 WHERE x<=NULL
+ }
+} {}
+do_test where5-2.8 {
+ execsql {
+ SELECT * FROM t2 WHERE x=NULL
+ }
+} {}
+do_test where5-2.9 {
+ execsql {
+ SELECT * FROM t2 WHERE x>=NULL
+ }
+} {}
+do_test where5-2.10 {
+ execsql {
+ SELECT * FROM t2 WHERE x>NULL
+ }
+} {}
+do_test where5-2.11 {
+ execsql {
+ SELECT * FROM t2 WHERE x!=NULL
+ }
+} {}
+do_test where5-2.12 {
+ execsql {
+ SELECT * FROM t2 WHERE x IS NULL
+ }
+} {}
+do_test where5-2.13 {
+ execsql {
+ SELECT * FROM t2 WHERE x IS NOT NULL
+ }
+} {-1 0 1}
+
+
+do_test where5-3.0 {
+ execsql {
+ SELECT * FROM t3 WHERE x<0
+ }
+} {-1}
+do_test where5-3.1 {
+ execsql {
+ SELECT * FROM t3 WHERE x<=0
+ }
+} {-1 0}
+do_test where5-3.2 {
+ execsql {
+ SELECT * FROM t3 WHERE x=0
+ }
+} {0}
+do_test where5-3.3 {
+ execsql {
+ SELECT * FROM t3 WHERE x>=0
+ }
+} {0 1}
+do_test where5-3.4 {
+ execsql {
+ SELECT * FROM t3 WHERE x>0
+ }
+} {1}
+do_test where5-3.5 {
+ execsql {
+ SELECT * FROM t3 WHERE x<>0
+ }
+} {-1 1}
+do_test where5-3.6 {
+ execsql {
+ SELECT * FROM t3 WHERE x<NULL
+ }
+} {}
+do_test where5-3.7 {
+ execsql {
+ SELECT * FROM t3 WHERE x<=NULL
+ }
+} {}
+do_test where5-3.8 {
+ execsql {
+ SELECT * FROM t3 WHERE x=NULL
+ }
+} {}
+do_test where5-3.9 {
+ execsql {
+ SELECT * FROM t3 WHERE x>=NULL
+ }
+} {}
+do_test where5-3.10 {
+ execsql {
+ SELECT * FROM t3 WHERE x>NULL
+ }
+} {}
+do_test where5-3.11 {
+ execsql {
+ SELECT * FROM t3 WHERE x!=NULL
+ }
+} {}
+do_test where5-3.12 {
+ execsql {
+ SELECT * FROM t3 WHERE x IS NULL
+ }
+} {}
+do_test where5-3.13 {
+ execsql {
+ SELECT * FROM t3 WHERE x IS NOT NULL
+ }
+} {-1 0 1}
+
+do_test where5-4.0 {
+ execsql {
+ SELECT x<NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.1 {
+ execsql {
+ SELECT x<=NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.2 {
+ execsql {
+ SELECT x==NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.3 {
+ execsql {
+ SELECT x>NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.4 {
+ execsql {
+ SELECT x>=NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.5 {
+ execsql {
+ SELECT x!=NULL FROM t3
+ }
+} {{} {} {}}
+do_test where5-4.6 {
+ execsql {
+ SELECT x IS NULL FROM t3
+ }
+} {0 0 0}
+do_test where5-4.7 {
+ execsql {
+ SELECT x IS NOT NULL FROM t3
+ }
+} {1 1 1}
+
+finish_test
diff --git a/third_party/sqlite/test/where6.test b/third_party/sqlite/test/where6.test
new file mode 100755
index 0000000..bcea654
--- /dev/null
+++ b/third_party/sqlite/test/where6.test
@@ -0,0 +1,130 @@
+# 2007 June 8
+#
+# 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 file is testing that terms in the ON clause of
+# a LEFT OUTER JOIN are not used with indices. See ticket #3015.
+#
+# $Id: where6.test,v 1.2 2008/04/17 19:14:02 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test where6-1.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c);
+ INSERT INTO t1 VALUES(1,3,1);
+ INSERT INTO t1 VALUES(2,4,2);
+ CREATE TABLE t2(x INTEGER PRIMARY KEY);
+ INSERT INTO t2 VALUES(3);
+
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x AND c=1;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-1.2 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b AND c=1;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-1.3 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b AND 1=c;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-1.4 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x AND 1=c;
+ }
+} {1 3 1 3 2 4 2 {}}
+
+ifcapable explain {
+ do_test where6-1.5 {
+ explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON x=b AND 1=c}
+ } [explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON b=x AND c=1}]
+ do_test where6-1.6 {
+ explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON x=b WHERE 1=c}
+ } [explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE c=1}]
+}
+
+do_test where6-1.11 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE c=1;
+ }
+} {1 3 1 3}
+do_test where6-1.12 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b WHERE c=1;
+ }
+} {1 3 1 3}
+do_test where6-1.13 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE 1=c;
+ }
+} {1 3 1 3}
+
+
+
+do_test where6-2.1 {
+ execsql {
+ CREATE INDEX i1 ON t1(c);
+
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x AND c=1;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-2.2 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b AND c=1;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-2.3 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b AND 1=c;
+ }
+} {1 3 1 3 2 4 2 {}}
+do_test where6-2.4 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x AND 1=c;
+ }
+} {1 3 1 3 2 4 2 {}}
+
+ifcapable explain {
+ do_test where6-2.5 {
+ explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON x=b AND 1=c}
+ } [explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON b=x AND c=1}]
+ do_test where6-2.6 {
+ explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON x=b WHERE 1=c}
+ } [explain_no_trace {SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE c=1}]
+}
+
+
+do_test where6-2.11 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE c=1;
+ }
+} {1 3 1 3}
+do_test where6-2.12 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b WHERE c=1;
+ }
+} {1 3 1 3}
+do_test where6-2.13 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON x=b WHERE 1=c;
+ }
+} {1 3 1 3}
+do_test where6-2.14 {
+ execsql {
+ SELECT * FROM t1 LEFT JOIN t2 ON b=x WHERE 1=c;
+ }
+} {1 3 1 3}
+
+finish_test
diff --git a/third_party/sqlite/test/zeroblob.test b/third_party/sqlite/test/zeroblob.test
new file mode 100755
index 0000000..79723d4
--- /dev/null
+++ b/third_party/sqlite/test/zeroblob.test
@@ -0,0 +1,230 @@
+# 2007 May 02
+#
+# 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 file is testing of the zero-filled blob functionality
+# including the sqlite3_bind_zeroblob(), sqlite3_result_zeroblob(),
+# and the built-in zeroblob() SQL function.
+#
+# $Id: zeroblob.test,v 1.13 2008/06/13 18:24:28 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !incrblob {
+ finish_test
+ return
+}
+
+# When zeroblob() is used for the last field of a column, then the
+# content of the zeroblob is never instantiated on the VDBE stack.
+# But it does get inserted into the database correctly.
+#
+db eval {PRAGMA cache_size=10}
+sqlite3_memory_highwater 1
+unset -nocomplain memused
+set memused [sqlite3_memory_used]
+do_test zeroblob-1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c,d);
+ }
+ set ::sqlite3_max_blobsize 0
+ execsql {
+ INSERT INTO t1 VALUES(2,3,4,zeroblob(1000000));
+ }
+ set ::sqlite3_max_blobsize
+} {10}
+do_test zeroblob-1.1.1 {
+ expr {[sqlite3_memory_highwater]<$::memused+25000}
+} {1}
+do_test zeroblob-1.2 {
+ execsql {
+ SELECT length(d) FROM t1
+ }
+} {1000000}
+
+# If a non-NULL column follows the zeroblob, then the content of
+# the zeroblob must be instantiated.
+#
+do_test zeroblob-1.3 {
+ set ::sqlite3_max_blobsize 0
+ execsql {
+ INSERT INTO t1 VALUES(3,4,zeroblob(10000),5);
+ }
+ set ::sqlite3_max_blobsize
+} {10010}
+do_test zeroblob-1.4 {
+ execsql {
+ SELECT length(c), length(d) FROM t1
+ }
+} {1 1000000 10000 1}
+
+# Multiple zeroblobs can appear at the end of record. No instantiation
+# of the blob content occurs on the stack.
+#
+do_test zeroblob-1.5 {
+ set ::sqlite3_max_blobsize 0
+ execsql {
+ INSERT INTO t1 VALUES(4,5,zeroblob(10000),zeroblob(10000));
+ }
+ set ::sqlite3_max_blobsize
+} {11}
+do_test zeroblob-1.6 {
+ execsql {
+ SELECT length(c), length(d) FROM t1
+ }
+} {1 1000000 10000 1 10000 10000}
+
+# NULLs can follow the zeroblob() or be intermixed with zeroblobs and
+# no instantiation of the zeroblobs occurs on the stack.
+#
+do_test zeroblob-1.7 {
+ set ::sqlite3_max_blobsize 0
+ execsql {
+ INSERT INTO t1 VALUES(5,zeroblob(10000),NULL,zeroblob(10000));
+ }
+ set ::sqlite3_max_blobsize
+} {10}
+do_test zeroblob-1.8 {
+ execsql {
+ SELECT length(b), length(d) FROM t1 WHERE a=5
+ }
+} {10000 10000}
+
+# Comparisons against zeroblobs work.
+#
+do_test zeroblob-2.1 {
+ execsql {
+ SELECT a FROM t1 WHERE b=zeroblob(10000)
+ }
+} {5}
+
+# Comparisons against zeroblobs work even when indexed.
+#
+do_test zeroblob-2.2 {
+ execsql {
+ CREATE INDEX i1_1 ON t1(b);
+ SELECT a FROM t1 WHERE b=zeroblob(10000);
+ }
+} {5}
+
+# DISTINCT works for zeroblobs
+#
+ifcapable bloblit&&subquery&&compound {
+ do_test zeroblob-3.1 {
+ execsql {
+ SELECT count(DISTINCT a) FROM (
+ SELECT x'00000000000000000000' AS a
+ UNION ALL
+ SELECT zeroblob(10) AS a
+ )
+ }
+ } {1}
+}
+
+# Concatentation works with zeroblob
+#
+ifcapable bloblit {
+ do_test zeroblob-4.1 {
+ execsql {
+ SELECT hex(zeroblob(2) || x'61')
+ }
+ } {000061}
+}
+
+# Check various CAST(...) operations on zeroblob.
+#
+do_test zeroblob-5.1 {
+ execsql {
+ SELECT CAST (zeroblob(100) AS REAL);
+ }
+} {0.0}
+do_test zeroblob-5.2 {
+ execsql {
+ SELECT CAST (zeroblob(100) AS INTEGER);
+ }
+} {0}
+do_test zeroblob-5.3 {
+ execsql {
+ SELECT CAST (zeroblob(100) AS TEXT);
+ }
+} {{}}
+do_test zeroblob-5.4 {
+ execsql {
+ SELECT CAST(zeroblob(100) AS BLOB);
+ }
+} [execsql {SELECT zeroblob(100)}]
+
+
+# Check for malicious use of zeroblob. Make sure nothing crashes.
+#
+do_test zeroblob-6.1.1 {
+ execsql {select zeroblob(-1)}
+} {{}}
+do_test zeroblob-6.1.2 {
+ execsql {select zeroblob(-10)}
+} {{}}
+do_test zeroblob-6.1.3 {
+ execsql {select zeroblob(-100)}
+} {{}}
+do_test zeroblob-6.2 {
+ execsql {select length(zeroblob(-1))}
+} {0}
+do_test zeroblob-6.3 {
+ execsql {select zeroblob(-1)|1}
+} {1}
+do_test zeroblob-6.4 {
+ catchsql {select length(zeroblob(2147483648))}
+} {1 {string or blob too big}}
+do_test zeroblob-6.5 {
+ catchsql {select zeroblob(2147483648)}
+} {1 {string or blob too big}}
+do_test zeroblob-6.6 {
+ execsql {select hex(zeroblob(-1))}
+} {{}}
+do_test zeroblob-6.7 {
+ execsql {select typeof(zeroblob(-1))}
+} {blob}
+
+# Test bind_zeroblob()
+#
+sqlite3_memory_highwater 1
+unset -nocomplain memused
+set memused [sqlite3_memory_used]
+do_test zeroblob-7.1 {
+ set ::STMT [sqlite3_prepare $::DB "SELECT length(?)" -1 DUMMY]
+ set ::sqlite3_max_blobsize 0
+ sqlite3_bind_zeroblob $::STMT 1 450000
+ sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test zeroblob-7.2 {
+ sqlite3_column_int $::STMT 0
+} {450000}
+do_test zeroblob-7.3 {
+ sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test zeroblob-7.4 {
+ set ::sqlite3_max_blobsize
+} {0}
+do_test zeroblob-7.5 {
+ expr {[sqlite3_memory_highwater]<$::memused+10000}
+} {1}
+
+# Test that MakeRecord can handle a value with some real content
+# and a zero-blob tail.
+#
+do_test zeroblob-8.1 {
+ llength [execsql {
+ SELECT 'hello' AS a, zeroblob(10) as b from t1 ORDER BY a, b;
+ }]
+} {8}
+
+
+finish_test
diff --git a/third_party/sqlite/tool/diffdb.c b/third_party/sqlite/tool/diffdb.c
new file mode 100755
index 0000000..0537d38
--- /dev/null
+++ b/third_party/sqlite/tool/diffdb.c
@@ -0,0 +1,44 @@
+/*
+** A utility for printing the differences between two SQLite database files.
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+#define PAGESIZE 1024
+static int db1 = -1;
+static int db2 = -1;
+
+int main(int argc, char **argv){
+ int iPg;
+ unsigned char a1[PAGESIZE], a2[PAGESIZE];
+ if( argc!=3 ){
+ fprintf(stderr,"Usage: %s FILENAME FILENAME\n", argv[0]);
+ exit(1);
+ }
+ db1 = open(argv[1], O_RDONLY);
+ if( db1<0 ){
+ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
+ exit(1);
+ }
+ db2 = open(argv[2], O_RDONLY);
+ if( db2<0 ){
+ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[2]);
+ exit(1);
+ }
+ iPg = 1;
+ while( read(db1, a1, PAGESIZE)==PAGESIZE && read(db2,a2,PAGESIZE)==PAGESIZE ){
+ if( memcmp(a1,a2,PAGESIZE) ){
+ printf("Page %d\n", iPg);
+ }
+ iPg++;
+ }
+ printf("%d pages checked\n", iPg-1);
+ close(db1);
+ close(db2);
+}
diff --git a/third_party/sqlite/tool/fragck.tcl b/third_party/sqlite/tool/fragck.tcl
new file mode 100755
index 0000000..35e76f4
--- /dev/null
+++ b/third_party/sqlite/tool/fragck.tcl
@@ -0,0 +1,149 @@
+# Run this TCL script using "testfixture" to get a report that shows
+# the sequence of database pages used by a particular table or index.
+# This information is used for fragmentation analysis.
+#
+
+# Get the name of the database to analyze
+#
+
+if {[llength $argv]!=2} {
+ puts stderr "Usage: $argv0 database-name table-or-index-name"
+ exit 1
+}
+set file_to_analyze [lindex $argv 0]
+if {![file exists $file_to_analyze]} {
+ puts stderr "No such file: $file_to_analyze"
+ exit 1
+}
+if {![file readable $file_to_analyze]} {
+ puts stderr "File is not readable: $file_to_analyze"
+ exit 1
+}
+if {[file size $file_to_analyze]<512} {
+ puts stderr "Empty or malformed database: $file_to_analyze"
+ exit 1
+}
+set objname [lindex $argv 1]
+
+# Open the database
+#
+sqlite3 db [lindex $argv 0]
+set DB [btree_open [lindex $argv 0] 1000 0]
+
+# This proc is a wrapper around the btree_cursor_info command. The
+# second argument is an open btree cursor returned by [btree_cursor].
+# The first argument is the name of an array variable that exists in
+# the scope of the caller. If the third argument is non-zero, then
+# info is returned for the page that lies $up entries upwards in the
+# tree-structure. (i.e. $up==1 returns the parent page, $up==2 the
+# grandparent etc.)
+#
+# The following entries in that array are filled in with information retrieved
+# using [btree_cursor_info]:
+#
+# $arrayvar(page_no) = The page number
+# $arrayvar(entry_no) = The entry number
+# $arrayvar(page_entries) = Total number of entries on this page
+# $arrayvar(cell_size) = Cell size (local payload + header)
+# $arrayvar(page_freebytes) = Number of free bytes on this page
+# $arrayvar(page_freeblocks) = Number of free blocks on the page
+# $arrayvar(payload_bytes) = Total payload size (local + overflow)
+# $arrayvar(header_bytes) = Header size in bytes
+# $arrayvar(local_payload_bytes) = Local payload size
+# $arrayvar(parent) = Parent page number
+#
+proc cursor_info {arrayvar csr {up 0}} {
+ upvar $arrayvar a
+ foreach [list a(page_no) \
+ a(entry_no) \
+ a(page_entries) \
+ a(cell_size) \
+ a(page_freebytes) \
+ a(page_freeblocks) \
+ a(payload_bytes) \
+ a(header_bytes) \
+ a(local_payload_bytes) \
+ a(parent) \
+ a(first_ovfl) ] [btree_cursor_info $csr $up] break
+}
+
+# Determine the page-size of the database. This global variable is used
+# throughout the script.
+#
+set pageSize [db eval {PRAGMA page_size}]
+
+# Find the root page of table or index to be analyzed. Also find out
+# if the object is a table or an index.
+#
+if {$objname=="sqlite_master"} {
+ set rootpage 1
+ set type table
+} else {
+ db eval {
+ SELECT rootpage, type FROM sqlite_master
+ WHERE name=$objname
+ } break
+ if {![info exists rootpage]} {
+ puts stderr "no such table or index: $objname"
+ exit 1
+ }
+ if {$type!="table" && $type!="index"} {
+ puts stderr "$objname is something other than a table or index"
+ exit 1
+ }
+ if {![string is integer -strict $rootpage]} {
+ puts stderr "invalid root page for $objname: $rootpage"
+ exit 1
+ }
+}
+
+# The cursor $csr is pointing to an entry. Print out information
+# about the page that $up levels above that page that contains
+# the entry. If $up==0 use the page that contains the entry.
+#
+# If information about the page has been printed already, then
+# this is a no-op.
+#
+proc page_info {csr up} {
+ global seen
+ cursor_info ci $csr $up
+ set pg $ci(page_no)
+ if {[info exists seen($pg)]} return
+ set seen($pg) 1
+
+ # Do parent pages first
+ #
+ if {$ci(parent)} {
+ page_info $csr [expr {$up+1}]
+ }
+
+ # Find the depth of this page
+ #
+ set depth 1
+ set i $up
+ while {$ci(parent)} {
+ incr i
+ incr depth
+ cursor_info ci $csr $i
+ }
+
+ # print the results
+ #
+ puts [format {LEVEL %d: %6d} $depth $pg]
+}
+
+
+
+
+# Loop through the object and print out page numbers
+#
+set csr [btree_cursor $DB $rootpage 0]
+for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} {
+ page_info $csr 0
+ set i 1
+ foreach pg [btree_ovfl_info $DB $csr] {
+ puts [format {OVFL %3d: %6d} $i $pg]
+ incr i
+ }
+}
+exit 0
diff --git a/third_party/sqlite/tool/lemon.c b/third_party/sqlite/tool/lemon.c
new file mode 100755
index 0000000..155fdbc
--- /dev/null
+++ b/third_party/sqlite/tool/lemon.c
@@ -0,0 +1,4866 @@
+/*
+** This file contains all sources (including headers) to the LEMON
+** LALR(1) parser generator. The sources have been combined into a
+** single file to make it easy to include LEMON in the source tree
+** and Makefile of another program.
+**
+** The author of this program disclaims copyright.
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef __WIN32__
+# if defined(_WIN32) || defined(WIN32)
+# define __WIN32__
+# endif
+#endif
+
+#ifdef __WIN32__
+extern int access();
+#else
+#include <unistd.h>
+#endif
+
+/* #define PRIVATE static */
+#define PRIVATE
+
+#ifdef TEST
+#define MAXRHS 5 /* Set low to exercise exception code */
+#else
+#define MAXRHS 1000
+#endif
+
+static char *msort(char*,char**,int(*)(const char*,const char*));
+
+static struct action *Action_new(void);
+static struct action *Action_sort(struct action *);
+
+/********** From the file "build.h" ************************************/
+void FindRulePrecedences();
+void FindFirstSets();
+void FindStates();
+void FindLinks();
+void FindFollowSets();
+void FindActions();
+
+/********* From the file "configlist.h" *********************************/
+void Configlist_init(/* void */);
+struct config *Configlist_add(/* struct rule *, int */);
+struct config *Configlist_addbasis(/* struct rule *, int */);
+void Configlist_closure(/* void */);
+void Configlist_sort(/* void */);
+void Configlist_sortbasis(/* void */);
+struct config *Configlist_return(/* void */);
+struct config *Configlist_basis(/* void */);
+void Configlist_eat(/* struct config * */);
+void Configlist_reset(/* void */);
+
+/********* From the file "error.h" ***************************************/
+void ErrorMsg(const char *, int,const char *, ...);
+
+/****** From the file "option.h" ******************************************/
+struct s_options {
+ enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR,
+ OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type;
+ char *label;
+ char *arg;
+ char *message;
+};
+int OptInit(/* char**,struct s_options*,FILE* */);
+int OptNArgs(/* void */);
+char *OptArg(/* int */);
+void OptErr(/* int */);
+void OptPrint(/* void */);
+
+/******** From the file "parse.h" *****************************************/
+void Parse(/* struct lemon *lemp */);
+
+/********* From the file "plink.h" ***************************************/
+struct plink *Plink_new(/* void */);
+void Plink_add(/* struct plink **, struct config * */);
+void Plink_copy(/* struct plink **, struct plink * */);
+void Plink_delete(/* struct plink * */);
+
+/********** From the file "report.h" *************************************/
+void Reprint(/* struct lemon * */);
+void ReportOutput(/* struct lemon * */);
+void ReportTable(/* struct lemon * */);
+void ReportHeader(/* struct lemon * */);
+void CompressTables(/* struct lemon * */);
+void ResortStates(/* struct lemon * */);
+
+/********** From the file "set.h" ****************************************/
+void SetSize(/* int N */); /* All sets will be of size N */
+char *SetNew(/* void */); /* A new set for element 0..N */
+void SetFree(/* char* */); /* Deallocate a set */
+
+int SetAdd(/* char*,int */); /* Add element to a set */
+int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */
+
+#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */
+
+/********** From the file "struct.h" *************************************/
+/*
+** Principal data structures for the LEMON parser generator.
+*/
+
+typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean;
+
+/* Symbols (terminals and nonterminals) of the grammar are stored
+** in the following: */
+struct symbol {
+ char *name; /* Name of the symbol */
+ int index; /* Index number for this symbol */
+ enum {
+ TERMINAL,
+ NONTERMINAL,
+ MULTITERMINAL
+ } type; /* Symbols are all either TERMINALS or NTs */
+ struct rule *rule; /* Linked list of rules of this (if an NT) */
+ struct symbol *fallback; /* fallback token in case this token doesn't parse */
+ int prec; /* Precedence if defined (-1 otherwise) */
+ enum e_assoc {
+ LEFT,
+ RIGHT,
+ NONE,
+ UNK
+ } assoc; /* Associativity if precedence is defined */
+ char *firstset; /* First-set for all rules of this symbol */
+ Boolean lambda; /* True if NT and can generate an empty string */
+ int useCnt; /* Number of times used */
+ char *destructor; /* Code which executes whenever this symbol is
+ ** popped from the stack during error processing */
+ int destLineno; /* Line number for start of destructor */
+ char *datatype; /* The data type of information held by this
+ ** object. Only used if type==NONTERMINAL */
+ int dtnum; /* The data type number. In the parser, the value
+ ** stack is a union. The .yy%d element of this
+ ** union is the correct data type for this object */
+ /* The following fields are used by MULTITERMINALs only */
+ int nsubsym; /* Number of constituent symbols in the MULTI */
+ struct symbol **subsym; /* Array of constituent symbols */
+};
+
+/* Each production rule in the grammar is stored in the following
+** structure. */
+struct rule {
+ struct symbol *lhs; /* Left-hand side of the rule */
+ char *lhsalias; /* Alias for the LHS (NULL if none) */
+ int lhsStart; /* True if left-hand side is the start symbol */
+ int ruleline; /* Line number for the rule */
+ int nrhs; /* Number of RHS symbols */
+ struct symbol **rhs; /* The RHS symbols */
+ char **rhsalias; /* An alias for each RHS symbol (NULL if none) */
+ int line; /* Line number at which code begins */
+ char *code; /* The code executed when this rule is reduced */
+ struct symbol *precsym; /* Precedence symbol for this rule */
+ int index; /* An index number for this rule */
+ Boolean canReduce; /* True if this rule is ever reduced */
+ struct rule *nextlhs; /* Next rule with the same LHS */
+ struct rule *next; /* Next rule in the global list */
+};
+
+/* A configuration is a production rule of the grammar together with
+** a mark (dot) showing how much of that rule has been processed so far.
+** Configurations also contain a follow-set which is a list of terminal
+** symbols which are allowed to immediately follow the end of the rule.
+** Every configuration is recorded as an instance of the following: */
+struct config {
+ struct rule *rp; /* The rule upon which the configuration is based */
+ int dot; /* The parse point */
+ char *fws; /* Follow-set for this configuration only */
+ struct plink *fplp; /* Follow-set forward propagation links */
+ struct plink *bplp; /* Follow-set backwards propagation links */
+ struct state *stp; /* Pointer to state which contains this */
+ enum {
+ COMPLETE, /* The status is used during followset and */
+ INCOMPLETE /* shift computations */
+ } status;
+ struct config *next; /* Next configuration in the state */
+ struct config *bp; /* The next basis configuration */
+};
+
+/* Every shift or reduce operation is stored as one of the following */
+struct action {
+ struct symbol *sp; /* The look-ahead symbol */
+ enum e_action {
+ SHIFT,
+ ACCEPT,
+ REDUCE,
+ ERROR,
+ SSCONFLICT, /* A shift/shift conflict */
+ SRCONFLICT, /* Was a reduce, but part of a conflict */
+ RRCONFLICT, /* Was a reduce, but part of a conflict */
+ SH_RESOLVED, /* Was a shift. Precedence resolved conflict */
+ RD_RESOLVED, /* Was reduce. Precedence resolved conflict */
+ NOT_USED /* Deleted by compression */
+ } type;
+ union {
+ struct state *stp; /* The new state, if a shift */
+ struct rule *rp; /* The rule, if a reduce */
+ } x;
+ struct action *next; /* Next action for this state */
+ struct action *collide; /* Next action with the same hash */
+};
+
+/* Each state of the generated parser's finite state machine
+** is encoded as an instance of the following structure. */
+struct state {
+ struct config *bp; /* The basis configurations for this state */
+ struct config *cfp; /* All configurations in this set */
+ int statenum; /* Sequential number for this state */
+ struct action *ap; /* Array of actions for this state */
+ int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */
+ int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */
+ int iDflt; /* Default action */
+};
+#define NO_OFFSET (-2147483647)
+
+/* A followset propagation link indicates that the contents of one
+** configuration followset should be propagated to another whenever
+** the first changes. */
+struct plink {
+ struct config *cfp; /* The configuration to which linked */
+ struct plink *next; /* The next propagate link */
+};
+
+/* The state vector for the entire parser generator is recorded as
+** follows. (LEMON uses no global variables and makes little use of
+** static variables. Fields in the following structure can be thought
+** of as begin global variables in the program.) */
+struct lemon {
+ struct state **sorted; /* Table of states sorted by state number */
+ struct rule *rule; /* List of all rules */
+ int nstate; /* Number of states */
+ int nrule; /* Number of rules */
+ int nsymbol; /* Number of terminal and nonterminal symbols */
+ int nterminal; /* Number of terminal symbols */
+ struct symbol **symbols; /* Sorted array of pointers to symbols */
+ int errorcnt; /* Number of errors */
+ struct symbol *errsym; /* The error symbol */
+ struct symbol *wildcard; /* Token that matches anything */
+ char *name; /* Name of the generated parser */
+ char *arg; /* Declaration of the 3th argument to parser */
+ char *tokentype; /* Type of terminal symbols in the parser stack */
+ char *vartype; /* The default type of non-terminal symbols */
+ char *start; /* Name of the start symbol for the grammar */
+ char *stacksize; /* Size of the parser stack */
+ char *include; /* Code to put at the start of the C file */
+ char *error; /* Code to execute when an error is seen */
+ char *overflow; /* Code to execute on a stack overflow */
+ char *failure; /* Code to execute on parser failure */
+ char *accept; /* Code to execute when the parser excepts */
+ char *extracode; /* Code appended to the generated file */
+ char *tokendest; /* Code to execute to destroy token data */
+ char *vardest; /* Code for the default non-terminal destructor */
+ char *filename; /* Name of the input file */
+ char *outname; /* Name of the current output file */
+ char *tokenprefix; /* A prefix added to token names in the .h file */
+ int nconflict; /* Number of parsing conflicts */
+ int tablesize; /* Size of the parse tables */
+ int basisflag; /* Print only basis configurations */
+ int has_fallback; /* True if any %fallback is seen in the grammar */
+ char *argv0; /* Name of the program */
+};
+
+#define MemoryCheck(X) if((X)==0){ \
+ extern void memory_error(); \
+ memory_error(); \
+}
+
+/**************** From the file "table.h" *********************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+/* Routines for handling a strings */
+
+char *Strsafe();
+
+void Strsafe_init(/* void */);
+int Strsafe_insert(/* char * */);
+char *Strsafe_find(/* char * */);
+
+/* Routines for handling symbols of the grammar */
+
+struct symbol *Symbol_new();
+int Symbolcmpp(/* struct symbol **, struct symbol ** */);
+void Symbol_init(/* void */);
+int Symbol_insert(/* struct symbol *, char * */);
+struct symbol *Symbol_find(/* char * */);
+struct symbol *Symbol_Nth(/* int */);
+int Symbol_count(/* */);
+struct symbol **Symbol_arrayof(/* */);
+
+/* Routines to manage the state table */
+
+int Configcmp(/* struct config *, struct config * */);
+struct state *State_new();
+void State_init(/* void */);
+int State_insert(/* struct state *, struct config * */);
+struct state *State_find(/* struct config * */);
+struct state **State_arrayof(/* */);
+
+/* Routines used for efficiency in Configlist_add */
+
+void Configtable_init(/* void */);
+int Configtable_insert(/* struct config * */);
+struct config *Configtable_find(/* struct config * */);
+void Configtable_clear(/* int(*)(struct config *) */);
+/****************** From the file "action.c" *******************************/
+/*
+** Routines processing parser actions in the LEMON parser generator.
+*/
+
+/* Allocate a new parser action */
+static struct action *Action_new(void){
+ static struct action *freelist = 0;
+ struct action *new;
+
+ if( freelist==0 ){
+ int i;
+ int amt = 100;
+ freelist = (struct action *)calloc(amt, sizeof(struct action));
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new parser action.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* Compare two actions for sorting purposes. Return negative, zero, or
+** positive if the first action is less than, equal to, or greater than
+** the first
+*/
+static int actioncmp(
+ struct action *ap1,
+ struct action *ap2
+){
+ int rc;
+ rc = ap1->sp->index - ap2->sp->index;
+ if( rc==0 ){
+ rc = (int)ap1->type - (int)ap2->type;
+ }
+ if( rc==0 && ap1->type==REDUCE ){
+ rc = ap1->x.rp->index - ap2->x.rp->index;
+ }
+ return rc;
+}
+
+/* Sort parser actions */
+static struct action *Action_sort(
+ struct action *ap
+){
+ ap = (struct action *)msort((char *)ap,(char **)&ap->next,
+ (int(*)(const char*,const char*))actioncmp);
+ return ap;
+}
+
+void Action_add(app,type,sp,arg)
+struct action **app;
+enum e_action type;
+struct symbol *sp;
+char *arg;
+{
+ struct action *new;
+ new = Action_new();
+ new->next = *app;
+ *app = new;
+ new->type = type;
+ new->sp = sp;
+ if( type==SHIFT ){
+ new->x.stp = (struct state *)arg;
+ }else{
+ new->x.rp = (struct rule *)arg;
+ }
+}
+/********************** New code to implement the "acttab" module ***********/
+/*
+** This module implements routines use to construct the yy_action[] table.
+*/
+
+/*
+** The state of the yy_action table under construction is an instance of
+** the following structure
+*/
+typedef struct acttab acttab;
+struct acttab {
+ int nAction; /* Number of used slots in aAction[] */
+ int nActionAlloc; /* Slots allocated for aAction[] */
+ struct {
+ int lookahead; /* Value of the lookahead token */
+ int action; /* Action to take on the given lookahead */
+ } *aAction, /* The yy_action[] table under construction */
+ *aLookahead; /* A single new transaction set */
+ int mnLookahead; /* Minimum aLookahead[].lookahead */
+ int mnAction; /* Action associated with mnLookahead */
+ int mxLookahead; /* Maximum aLookahead[].lookahead */
+ int nLookahead; /* Used slots in aLookahead[] */
+ int nLookaheadAlloc; /* Slots allocated in aLookahead[] */
+};
+
+/* Return the number of entries in the yy_action table */
+#define acttab_size(X) ((X)->nAction)
+
+/* The value for the N-th entry in yy_action */
+#define acttab_yyaction(X,N) ((X)->aAction[N].action)
+
+/* The value for the N-th entry in yy_lookahead */
+#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead)
+
+/* Free all memory associated with the given acttab */
+void acttab_free(acttab *p){
+ free( p->aAction );
+ free( p->aLookahead );
+ free( p );
+}
+
+/* Allocate a new acttab structure */
+acttab *acttab_alloc(void){
+ acttab *p = calloc( 1, sizeof(*p) );
+ if( p==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new acttab.");
+ exit(1);
+ }
+ memset(p, 0, sizeof(*p));
+ return p;
+}
+
+/* Add a new action to the current transaction set
+*/
+void acttab_action(acttab *p, int lookahead, int action){
+ if( p->nLookahead>=p->nLookaheadAlloc ){
+ p->nLookaheadAlloc += 25;
+ p->aLookahead = realloc( p->aLookahead,
+ sizeof(p->aLookahead[0])*p->nLookaheadAlloc );
+ if( p->aLookahead==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ }
+ if( p->nLookahead==0 ){
+ p->mxLookahead = lookahead;
+ p->mnLookahead = lookahead;
+ p->mnAction = action;
+ }else{
+ if( p->mxLookahead<lookahead ) p->mxLookahead = lookahead;
+ if( p->mnLookahead>lookahead ){
+ p->mnLookahead = lookahead;
+ p->mnAction = action;
+ }
+ }
+ p->aLookahead[p->nLookahead].lookahead = lookahead;
+ p->aLookahead[p->nLookahead].action = action;
+ p->nLookahead++;
+}
+
+/*
+** Add the transaction set built up with prior calls to acttab_action()
+** into the current action table. Then reset the transaction set back
+** to an empty set in preparation for a new round of acttab_action() calls.
+**
+** Return the offset into the action table of the new transaction.
+*/
+int acttab_insert(acttab *p){
+ int i, j, k, n;
+ assert( p->nLookahead>0 );
+
+ /* Make sure we have enough space to hold the expanded action table
+ ** in the worst case. The worst case occurs if the transaction set
+ ** must be appended to the current action table
+ */
+ n = p->mxLookahead + 1;
+ if( p->nAction + n >= p->nActionAlloc ){
+ int oldAlloc = p->nActionAlloc;
+ p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20;
+ p->aAction = realloc( p->aAction,
+ sizeof(p->aAction[0])*p->nActionAlloc);
+ if( p->aAction==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ for(i=oldAlloc; i<p->nActionAlloc; i++){
+ p->aAction[i].lookahead = -1;
+ p->aAction[i].action = -1;
+ }
+ }
+
+ /* Scan the existing action table looking for an offset where we can
+ ** insert the current transaction set. Fall out of the loop when that
+ ** offset is found. In the worst case, we fall out of the loop when
+ ** i reaches p->nAction, which means we append the new transaction set.
+ **
+ ** i is the index in p->aAction[] where p->mnLookahead is inserted.
+ */
+ for(i=0; i<p->nAction+p->mnLookahead; i++){
+ if( p->aAction[i].lookahead<0 ){
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ if( k<0 ) break;
+ if( p->aAction[k].lookahead>=0 ) break;
+ }
+ if( j<p->nLookahead ) continue;
+ for(j=0; j<p->nAction; j++){
+ if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break;
+ }
+ if( j==p->nAction ){
+ break; /* Fits in empty slots */
+ }
+ }else if( p->aAction[i].lookahead==p->mnLookahead ){
+ if( p->aAction[i].action!=p->mnAction ) continue;
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ if( k<0 || k>=p->nAction ) break;
+ if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break;
+ if( p->aLookahead[j].action!=p->aAction[k].action ) break;
+ }
+ if( j<p->nLookahead ) continue;
+ n = 0;
+ for(j=0; j<p->nAction; j++){
+ if( p->aAction[j].lookahead<0 ) continue;
+ if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++;
+ }
+ if( n==p->nLookahead ){
+ break; /* Same as a prior transaction set */
+ }
+ }
+ }
+ /* Insert transaction set at index i. */
+ for(j=0; j<p->nLookahead; j++){
+ k = p->aLookahead[j].lookahead - p->mnLookahead + i;
+ p->aAction[k] = p->aLookahead[j];
+ if( k>=p->nAction ) p->nAction = k+1;
+ }
+ p->nLookahead = 0;
+
+ /* Return the offset that is added to the lookahead in order to get the
+ ** index into yy_action of the action */
+ return i - p->mnLookahead;
+}
+
+/********************** From the file "build.c" *****************************/
+/*
+** Routines to construction the finite state machine for the LEMON
+** parser generator.
+*/
+
+/* Find a precedence symbol of every rule in the grammar.
+**
+** Those rules which have a precedence symbol coded in the input
+** grammar using the "[symbol]" construct will already have the
+** rp->precsym field filled. Other rules take as their precedence
+** symbol the first RHS symbol with a defined precedence. If there
+** are not RHS symbols with a defined precedence, the precedence
+** symbol field is left blank.
+*/
+void FindRulePrecedences(xp)
+struct lemon *xp;
+{
+ struct rule *rp;
+ for(rp=xp->rule; rp; rp=rp->next){
+ if( rp->precsym==0 ){
+ int i, j;
+ for(i=0; i<rp->nrhs && rp->precsym==0; i++){
+ struct symbol *sp = rp->rhs[i];
+ if( sp->type==MULTITERMINAL ){
+ for(j=0; j<sp->nsubsym; j++){
+ if( sp->subsym[j]->prec>=0 ){
+ rp->precsym = sp->subsym[j];
+ break;
+ }
+ }
+ }else if( sp->prec>=0 ){
+ rp->precsym = rp->rhs[i];
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* Find all nonterminals which will generate the empty string.
+** Then go back and compute the first sets of every nonterminal.
+** The first set is the set of all terminal symbols which can begin
+** a string generated by that nonterminal.
+*/
+void FindFirstSets(lemp)
+struct lemon *lemp;
+{
+ int i, j;
+ struct rule *rp;
+ int progress;
+
+ for(i=0; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->lambda = LEMON_FALSE;
+ }
+ for(i=lemp->nterminal; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->firstset = SetNew();
+ }
+
+ /* First compute all lambdas */
+ do{
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->lhs->lambda ) continue;
+ for(i=0; i<rp->nrhs; i++){
+ struct symbol *sp = rp->rhs[i];
+ if( sp->type!=TERMINAL || sp->lambda==LEMON_FALSE ) break;
+ }
+ if( i==rp->nrhs ){
+ rp->lhs->lambda = LEMON_TRUE;
+ progress = 1;
+ }
+ }
+ }while( progress );
+
+ /* Now compute all first sets */
+ do{
+ struct symbol *s1, *s2;
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ s1 = rp->lhs;
+ for(i=0; i<rp->nrhs; i++){
+ s2 = rp->rhs[i];
+ if( s2->type==TERMINAL ){
+ progress += SetAdd(s1->firstset,s2->index);
+ break;
+ }else if( s2->type==MULTITERMINAL ){
+ for(j=0; j<s2->nsubsym; j++){
+ progress += SetAdd(s1->firstset,s2->subsym[j]->index);
+ }
+ break;
+ }else if( s1==s2 ){
+ if( s1->lambda==LEMON_FALSE ) break;
+ }else{
+ progress += SetUnion(s1->firstset,s2->firstset);
+ if( s2->lambda==LEMON_FALSE ) break;
+ }
+ }
+ }
+ }while( progress );
+ return;
+}
+
+/* Compute all LR(0) states for the grammar. Links
+** are added to between some states so that the LR(1) follow sets
+** can be computed later.
+*/
+PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */
+void FindStates(lemp)
+struct lemon *lemp;
+{
+ struct symbol *sp;
+ struct rule *rp;
+
+ Configlist_init();
+
+ /* Find the start symbol */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ){
+ ErrorMsg(lemp->filename,0,
+"The specified start symbol \"%s\" is not \
+in a nonterminal of the grammar. \"%s\" will be used as the start \
+symbol instead.",lemp->start,lemp->rule->lhs->name);
+ lemp->errorcnt++;
+ sp = lemp->rule->lhs;
+ }
+ }else{
+ sp = lemp->rule->lhs;
+ }
+
+ /* Make sure the start symbol doesn't occur on the right-hand side of
+ ** any rule. Report an error if it does. (YACC would generate a new
+ ** start symbol in this case.) */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ int i;
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */
+ ErrorMsg(lemp->filename,0,
+"The start symbol \"%s\" occurs on the \
+right-hand side of a rule. This will result in a parser which \
+does not work properly.",sp->name);
+ lemp->errorcnt++;
+ }
+ }
+ }
+
+ /* The basis configuration set for the first state
+ ** is all rules which have the start symbol as their
+ ** left-hand side */
+ for(rp=sp->rule; rp; rp=rp->nextlhs){
+ struct config *newcfp;
+ rp->lhsStart = 1;
+ newcfp = Configlist_addbasis(rp,0);
+ SetAdd(newcfp->fws,0);
+ }
+
+ /* Compute the first state. All other states will be
+ ** computed automatically during the computation of the first one.
+ ** The returned pointer to the first state is not used. */
+ (void)getstate(lemp);
+ return;
+}
+
+/* Return a pointer to a state which is described by the configuration
+** list which has been built from calls to Configlist_add.
+*/
+PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */
+PRIVATE struct state *getstate(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *bp;
+ struct state *stp;
+
+ /* Extract the sorted basis of the new state. The basis was constructed
+ ** by prior calls to "Configlist_addbasis()". */
+ Configlist_sortbasis();
+ bp = Configlist_basis();
+
+ /* Get a state with the same basis */
+ stp = State_find(bp);
+ if( stp ){
+ /* A state with the same basis already exists! Copy all the follow-set
+ ** propagation links from the state under construction into the
+ ** preexisting state, then return a pointer to the preexisting state */
+ struct config *x, *y;
+ for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){
+ Plink_copy(&y->bplp,x->bplp);
+ Plink_delete(x->fplp);
+ x->fplp = x->bplp = 0;
+ }
+ cfp = Configlist_return();
+ Configlist_eat(cfp);
+ }else{
+ /* This really is a new state. Construct all the details */
+ Configlist_closure(lemp); /* Compute the configuration closure */
+ Configlist_sort(); /* Sort the configuration closure */
+ cfp = Configlist_return(); /* Get a pointer to the config list */
+ stp = State_new(); /* A new state structure */
+ MemoryCheck(stp);
+ stp->bp = bp; /* Remember the configuration basis */
+ stp->cfp = cfp; /* Remember the configuration closure */
+ stp->statenum = lemp->nstate++; /* Every state gets a sequence number */
+ stp->ap = 0; /* No actions, yet. */
+ State_insert(stp,stp->bp); /* Add to the state table */
+ buildshifts(lemp,stp); /* Recursively compute successor states */
+ }
+ return stp;
+}
+
+/*
+** Return true if two symbols are the same.
+*/
+int same_symbol(a,b)
+struct symbol *a;
+struct symbol *b;
+{
+ int i;
+ if( a==b ) return 1;
+ if( a->type!=MULTITERMINAL ) return 0;
+ if( b->type!=MULTITERMINAL ) return 0;
+ if( a->nsubsym!=b->nsubsym ) return 0;
+ for(i=0; i<a->nsubsym; i++){
+ if( a->subsym[i]!=b->subsym[i] ) return 0;
+ }
+ return 1;
+}
+
+/* Construct all successor states to the given state. A "successor"
+** state is any state which can be reached by a shift action.
+*/
+PRIVATE void buildshifts(lemp,stp)
+struct lemon *lemp;
+struct state *stp; /* The state from which successors are computed */
+{
+ struct config *cfp; /* For looping thru the config closure of "stp" */
+ struct config *bcfp; /* For the inner loop on config closure of "stp" */
+ struct config *new; /* */
+ struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
+ struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
+ struct state *newstp; /* A pointer to a successor state */
+
+ /* Each configuration becomes complete after it contibutes to a successor
+ ** state. Initially, all configurations are incomplete */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
+
+ /* Loop through all configurations of the state "stp" */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */
+ if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */
+ Configlist_reset(); /* Reset the new config set */
+ sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */
+
+ /* For every configuration in the state "stp" which has the symbol "sp"
+ ** following its dot, add the same configuration to the basis set under
+ ** construction but with the dot shifted one symbol to the right. */
+ for(bcfp=cfp; bcfp; bcfp=bcfp->next){
+ if( bcfp->status==COMPLETE ) continue; /* Already used */
+ if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */
+ bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */
+ if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */
+ bcfp->status = COMPLETE; /* Mark this config as used */
+ new = Configlist_addbasis(bcfp->rp,bcfp->dot+1);
+ Plink_add(&new->bplp,bcfp);
+ }
+
+ /* Get a pointer to the state described by the basis configuration set
+ ** constructed in the preceding loop */
+ newstp = getstate(lemp);
+
+ /* The state "newstp" is reached from the state "stp" by a shift action
+ ** on the symbol "sp" */
+ if( sp->type==MULTITERMINAL ){
+ int i;
+ for(i=0; i<sp->nsubsym; i++){
+ Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp);
+ }
+ }else{
+ Action_add(&stp->ap,SHIFT,sp,(char *)newstp);
+ }
+ }
+}
+
+/*
+** Construct the propagation links
+*/
+void FindLinks(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp, *other;
+ struct state *stp;
+ struct plink *plp;
+
+ /* Housekeeping detail:
+ ** Add to every propagate link a pointer back to the state to
+ ** which the link is attached. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ cfp->stp = stp;
+ }
+ }
+
+ /* Convert all backlinks into forward links. Only the forward
+ ** links are used in the follow-set computation. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ for(plp=cfp->bplp; plp; plp=plp->next){
+ other = plp->cfp;
+ Plink_add(&other->fplp,cfp);
+ }
+ }
+ }
+}
+
+/* Compute all followsets.
+**
+** A followset is the set of all symbols which can come immediately
+** after a configuration.
+*/
+void FindFollowSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp;
+ struct plink *plp;
+ int progress;
+ int change;
+
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ cfp->status = INCOMPLETE;
+ }
+ }
+
+ do{
+ progress = 0;
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue;
+ for(plp=cfp->fplp; plp; plp=plp->next){
+ change = SetUnion(plp->cfp->fws,cfp->fws);
+ if( change ){
+ plp->cfp->status = INCOMPLETE;
+ progress = 1;
+ }
+ }
+ cfp->status = COMPLETE;
+ }
+ }
+ }while( progress );
+}
+
+static int resolve_conflict();
+
+/* Compute the reduce actions, and resolve conflicts.
+*/
+void FindActions(lemp)
+struct lemon *lemp;
+{
+ int i,j;
+ struct config *cfp;
+ struct state *stp;
+ struct symbol *sp;
+ struct rule *rp;
+
+ /* Add all of the reduce actions
+ ** A reduce action is added for each element of the followset of
+ ** a configuration which has its dot at the extreme right.
+ */
+ for(i=0; i<lemp->nstate; i++){ /* Loop over all states */
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */
+ if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */
+ for(j=0; j<lemp->nterminal; j++){
+ if( SetFind(cfp->fws,j) ){
+ /* Add a reduce action to the state "stp" which will reduce by the
+ ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */
+ Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp);
+ }
+ }
+ }
+ }
+ }
+
+ /* Add the accepting token */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ) sp = lemp->rule->lhs;
+ }else{
+ sp = lemp->rule->lhs;
+ }
+ /* Add to the first state (which is always the starting state of the
+ ** finite state machine) an action to ACCEPT if the lookahead is the
+ ** start nonterminal. */
+ Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0);
+
+ /* Resolve conflicts */
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap, *nap;
+ struct state *stp;
+ stp = lemp->sorted[i];
+ /* assert( stp->ap ); */
+ stp->ap = Action_sort(stp->ap);
+ for(ap=stp->ap; ap && ap->next; ap=ap->next){
+ for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){
+ /* The two actions "ap" and "nap" have the same lookahead.
+ ** Figure out which one should be used */
+ lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym);
+ }
+ }
+ }
+
+ /* Report an error for each rule that can never be reduced. */
+ for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE;
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap;
+ for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE;
+ }
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->canReduce ) continue;
+ ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n");
+ lemp->errorcnt++;
+ }
+}
+
+/* Resolve a conflict between the two given actions. If the
+** conflict can't be resolved, return non-zero.
+**
+** NO LONGER TRUE:
+** To resolve a conflict, first look to see if either action
+** is on an error rule. In that case, take the action which
+** is not associated with the error rule. If neither or both
+** actions are associated with an error rule, then try to
+** use precedence to resolve the conflict.
+**
+** If either action is a SHIFT, then it must be apx. This
+** function won't work if apx->type==REDUCE and apy->type==SHIFT.
+*/
+static int resolve_conflict(apx,apy,errsym)
+struct action *apx;
+struct action *apy;
+struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */
+{
+ struct symbol *spx, *spy;
+ int errcnt = 0;
+ assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */
+ if( apx->type==SHIFT && apy->type==SHIFT ){
+ apy->type = SSCONFLICT;
+ errcnt++;
+ }
+ if( apx->type==SHIFT && apy->type==REDUCE ){
+ spx = apx->sp;
+ spy = apy->x.rp->precsym;
+ if( spy==0 || spx->prec<0 || spy->prec<0 ){
+ /* Not enough precedence information. */
+ apy->type = SRCONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){ /* Lower precedence wins */
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = SH_RESOLVED;
+ }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */
+ apy->type = RD_RESOLVED; /* associativity */
+ }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */
+ apx->type = SH_RESOLVED;
+ }else{
+ assert( spx->prec==spy->prec && spx->assoc==NONE );
+ apy->type = SRCONFLICT;
+ errcnt++;
+ }
+ }else if( apx->type==REDUCE && apy->type==REDUCE ){
+ spx = apx->x.rp->precsym;
+ spy = apy->x.rp->precsym;
+ if( spx==0 || spy==0 || spx->prec<0 ||
+ spy->prec<0 || spx->prec==spy->prec ){
+ apy->type = RRCONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = RD_RESOLVED;
+ }
+ }else{
+ assert(
+ apx->type==SH_RESOLVED ||
+ apx->type==RD_RESOLVED ||
+ apx->type==SSCONFLICT ||
+ apx->type==SRCONFLICT ||
+ apx->type==RRCONFLICT ||
+ apy->type==SH_RESOLVED ||
+ apy->type==RD_RESOLVED ||
+ apy->type==SSCONFLICT ||
+ apy->type==SRCONFLICT ||
+ apy->type==RRCONFLICT
+ );
+ /* The REDUCE/SHIFT case cannot happen because SHIFTs come before
+ ** REDUCEs on the list. If we reach this point it must be because
+ ** the parser conflict had already been resolved. */
+ }
+ return errcnt;
+}
+/********************* From the file "configlist.c" *************************/
+/*
+** Routines to processing a configuration list and building a state
+** in the LEMON parser generator.
+*/
+
+static struct config *freelist = 0; /* List of free configurations */
+static struct config *current = 0; /* Top of list of configurations */
+static struct config **currentend = 0; /* Last on list of configs */
+static struct config *basis = 0; /* Top of list of basis configs */
+static struct config **basisend = 0; /* End of list of basis configs */
+
+/* Return a pointer to a new configuration */
+PRIVATE struct config *newconfig(){
+ struct config *new;
+ if( freelist==0 ){
+ int i;
+ int amt = 3;
+ freelist = (struct config *)calloc( amt, sizeof(struct config) );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new configuration.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* The configuration "old" is no longer used */
+PRIVATE void deleteconfig(old)
+struct config *old;
+{
+ old->next = freelist;
+ freelist = old;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_init(){
+ current = 0;
+ currentend = &current;
+ basis = 0;
+ basisend = &basis;
+ Configtable_init();
+ return;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_reset(){
+ current = 0;
+ currentend = &current;
+ basis = 0;
+ basisend = &basis;
+ Configtable_clear(0);
+ return;
+}
+
+/* Add another configuration to the configuration list */
+struct config *Configlist_add(rp,dot)
+struct rule *rp; /* The rule */
+int dot; /* Index into the RHS of the rule where the dot goes */
+{
+ struct config *cfp, model;
+
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Add a basis configuration to the configuration list */
+struct config *Configlist_addbasis(rp,dot)
+struct rule *rp;
+int dot;
+{
+ struct config *cfp, model;
+
+ assert( basisend!=0 );
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ *basisend = cfp;
+ basisend = &cfp->bp;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Compute the closure of the configuration list */
+void Configlist_closure(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *newcfp;
+ struct rule *rp, *newrp;
+ struct symbol *sp, *xsp;
+ int i, dot;
+
+ assert( currentend!=0 );
+ for(cfp=current; cfp; cfp=cfp->next){
+ rp = cfp->rp;
+ dot = cfp->dot;
+ if( dot>=rp->nrhs ) continue;
+ sp = rp->rhs[dot];
+ if( sp->type==NONTERMINAL ){
+ if( sp->rule==0 && sp!=lemp->errsym ){
+ ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.",
+ sp->name);
+ lemp->errorcnt++;
+ }
+ for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){
+ newcfp = Configlist_add(newrp,0);
+ for(i=dot+1; i<rp->nrhs; i++){
+ xsp = rp->rhs[i];
+ if( xsp->type==TERMINAL ){
+ SetAdd(newcfp->fws,xsp->index);
+ break;
+ }else if( xsp->type==MULTITERMINAL ){
+ int k;
+ for(k=0; k<xsp->nsubsym; k++){
+ SetAdd(newcfp->fws, xsp->subsym[k]->index);
+ }
+ break;
+ }else{
+ SetUnion(newcfp->fws,xsp->firstset);
+ if( xsp->lambda==LEMON_FALSE ) break;
+ }
+ }
+ if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp);
+ }
+ }
+ }
+ return;
+}
+
+/* Sort the configuration list */
+void Configlist_sort(){
+ current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp);
+ currentend = 0;
+ return;
+}
+
+/* Sort the basis configuration list */
+void Configlist_sortbasis(){
+ basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp);
+ basisend = 0;
+ return;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_return(){
+ struct config *old;
+ old = current;
+ current = 0;
+ currentend = 0;
+ return old;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_basis(){
+ struct config *old;
+ old = basis;
+ basis = 0;
+ basisend = 0;
+ return old;
+}
+
+/* Free all elements of the given configuration list */
+void Configlist_eat(cfp)
+struct config *cfp;
+{
+ struct config *nextcfp;
+ for(; cfp; cfp=nextcfp){
+ nextcfp = cfp->next;
+ assert( cfp->fplp==0 );
+ assert( cfp->bplp==0 );
+ if( cfp->fws ) SetFree(cfp->fws);
+ deleteconfig(cfp);
+ }
+ return;
+}
+/***************** From the file "error.c" *********************************/
+/*
+** Code for printing error message.
+*/
+
+/* Find a good place to break "msg" so that its length is at least "min"
+** but no more than "max". Make the point as close to max as possible.
+*/
+static int findbreak(msg,min,max)
+char *msg;
+int min;
+int max;
+{
+ int i,spot;
+ char c;
+ for(i=spot=min; i<=max; i++){
+ c = msg[i];
+ if( c=='\t' ) msg[i] = ' ';
+ if( c=='\n' ){ msg[i] = ' '; spot = i; break; }
+ if( c==0 ){ spot = i; break; }
+ if( c=='-' && i<max-1 ) spot = i+1;
+ if( c==' ' ) spot = i;
+ }
+ return spot;
+}
+
+/*
+** The error message is split across multiple lines if necessary. The
+** splits occur at a space, if there is a space available near the end
+** of the line.
+*/
+#define ERRMSGSIZE 10000 /* Hope this is big enough. No way to error check */
+#define LINEWIDTH 79 /* Max width of any output line */
+#define PREFIXLIMIT 30 /* Max width of the prefix on each line */
+void ErrorMsg(const char *filename, int lineno, const char *format, ...){
+ char errmsg[ERRMSGSIZE];
+ char prefix[PREFIXLIMIT+10];
+ int errmsgsize;
+ int prefixsize;
+ int availablewidth;
+ va_list ap;
+ int end, restart, base;
+
+ va_start(ap, format);
+ /* Prepare a prefix to be prepended to every output line */
+ if( lineno>0 ){
+ sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno);
+ }else{
+ sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename);
+ }
+ prefixsize = strlen(prefix);
+ availablewidth = LINEWIDTH - prefixsize;
+
+ /* Generate the error message */
+ vsprintf(errmsg,format,ap);
+ va_end(ap);
+ errmsgsize = strlen(errmsg);
+ /* Remove trailing '\n's from the error message. */
+ while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){
+ errmsg[--errmsgsize] = 0;
+ }
+
+ /* Print the error message */
+ base = 0;
+ while( errmsg[base]!=0 ){
+ end = restart = findbreak(&errmsg[base],0,availablewidth);
+ restart += base;
+ while( errmsg[restart]==' ' ) restart++;
+ fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]);
+ base = restart;
+ }
+}
+/**************** From the file "main.c" ************************************/
+/*
+** Main program file for the LEMON parser generator.
+*/
+
+/* Report an out-of-memory condition and abort. This function
+** is used mostly by the "MemoryCheck" macro in struct.h
+*/
+void memory_error(){
+ fprintf(stderr,"Out of memory. Aborting...\n");
+ exit(1);
+}
+
+static int nDefine = 0; /* Number of -D options on the command line */
+static char **azDefine = 0; /* Name of the -D macros */
+
+/* This routine is called with the argument to each -D command-line option.
+** Add the macro defined to the azDefine array.
+*/
+static void handle_D_option(char *z){
+ char **paz;
+ nDefine++;
+ azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine);
+ if( azDefine==0 ){
+ fprintf(stderr,"out of memory\n");
+ exit(1);
+ }
+ paz = &azDefine[nDefine-1];
+ *paz = malloc( strlen(z)+1 );
+ if( *paz==0 ){
+ fprintf(stderr,"out of memory\n");
+ exit(1);
+ }
+ strcpy(*paz, z);
+ for(z=*paz; *z && *z!='='; z++){}
+ *z = 0;
+}
+
+
+/* The main program. Parse the command line and do it... */
+int main(argc,argv)
+int argc;
+char **argv;
+{
+ static int version = 0;
+ static int rpflag = 0;
+ static int basisflag = 0;
+ static int compress = 0;
+ static int quiet = 0;
+ static int statistics = 0;
+ static int mhflag = 0;
+ static struct s_options options[] = {
+ {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
+ {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
+ {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."},
+ {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
+ {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"},
+ {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
+ {OPT_FLAG, "s", (char*)&statistics,
+ "Print parser stats to standard output."},
+ {OPT_FLAG, "x", (char*)&version, "Print the version number."},
+ {OPT_FLAG,0,0,0}
+ };
+ int i;
+ struct lemon lem;
+
+ OptInit(argv,options,stderr);
+ if( version ){
+ printf("Lemon version 1.0\n");
+ exit(0);
+ }
+ if( OptNArgs()!=1 ){
+ fprintf(stderr,"Exactly one filename argument is required.\n");
+ exit(1);
+ }
+ memset(&lem, 0, sizeof(lem));
+ lem.errorcnt = 0;
+
+ /* Initialize the machine */
+ Strsafe_init();
+ Symbol_init();
+ State_init();
+ lem.argv0 = argv[0];
+ lem.filename = OptArg(0);
+ lem.basisflag = basisflag;
+ Symbol_new("$");
+ lem.errsym = Symbol_new("error");
+ lem.errsym->useCnt = 0;
+
+ /* Parse the input file */
+ Parse(&lem);
+ if( lem.errorcnt ) exit(lem.errorcnt);
+ if( lem.nrule==0 ){
+ fprintf(stderr,"Empty grammar.\n");
+ exit(1);
+ }
+
+ /* Count and index the symbols of the grammar */
+ lem.nsymbol = Symbol_count();
+ Symbol_new("{default}");
+ lem.symbols = Symbol_arrayof();
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*),
+ (int(*)())Symbolcmpp);
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=1; isupper(lem.symbols[i]->name[0]); i++);
+ lem.nterminal = i;
+
+ /* Generate a reprint of the grammar, if requested on the command line */
+ if( rpflag ){
+ Reprint(&lem);
+ }else{
+ /* Initialize the size for all follow and first sets */
+ SetSize(lem.nterminal+1);
+
+ /* Find the precedence for every production rule (that has one) */
+ FindRulePrecedences(&lem);
+
+ /* Compute the lambda-nonterminals and the first-sets for every
+ ** nonterminal */
+ FindFirstSets(&lem);
+
+ /* Compute all LR(0) states. Also record follow-set propagation
+ ** links so that the follow-set can be computed later */
+ lem.nstate = 0;
+ FindStates(&lem);
+ lem.sorted = State_arrayof();
+
+ /* Tie up loose ends on the propagation links */
+ FindLinks(&lem);
+
+ /* Compute the follow set of every reducible configuration */
+ FindFollowSets(&lem);
+
+ /* Compute the action tables */
+ FindActions(&lem);
+
+ /* Compress the action tables */
+ if( compress==0 ) CompressTables(&lem);
+
+ /* Reorder and renumber the states so that states with fewer choices
+ ** occur at the end. */
+ ResortStates(&lem);
+
+ /* Generate a report of the parser generated. (the "y.output" file) */
+ if( !quiet ) ReportOutput(&lem);
+
+ /* Generate the source code for the parser */
+ ReportTable(&lem, mhflag);
+
+ /* Produce a header file for use by the scanner. (This step is
+ ** omitted if the "-m" option is used because makeheaders will
+ ** generate the file for us.) */
+ if( !mhflag ) ReportHeader(&lem);
+ }
+ if( statistics ){
+ printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n",
+ lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule);
+ printf(" %d states, %d parser table entries, %d conflicts\n",
+ lem.nstate, lem.tablesize, lem.nconflict);
+ }
+ if( lem.nconflict ){
+ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
+ }
+ exit(lem.errorcnt + lem.nconflict);
+ return (lem.errorcnt + lem.nconflict);
+}
+/******************** From the file "msort.c" *******************************/
+/*
+** A generic merge-sort program.
+**
+** USAGE:
+** Let "ptr" be a pointer to some structure which is at the head of
+** a null-terminated list. Then to sort the list call:
+**
+** ptr = msort(ptr,&(ptr->next),cmpfnc);
+**
+** In the above, "cmpfnc" is a pointer to a function which compares
+** two instances of the structure and returns an integer, as in
+** strcmp. The second argument is a pointer to the pointer to the
+** second element of the linked list. This address is used to compute
+** the offset to the "next" field within the structure. The offset to
+** the "next" field must be constant for all structures in the list.
+**
+** The function returns a new pointer which is the head of the list
+** after sorting.
+**
+** ALGORITHM:
+** Merge-sort.
+*/
+
+/*
+** Return a pointer to the next structure in the linked list.
+*/
+#define NEXT(A) (*(char**)(((unsigned long)A)+offset))
+
+/*
+** Inputs:
+** a: A sorted, null-terminated linked list. (May be null).
+** b: A sorted, null-terminated linked list. (May be null).
+** cmp: A pointer to the comparison function.
+** offset: Offset in the structure to the "next" field.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** of both a and b.
+**
+** Side effects:
+** The "next" pointers for elements in the lists a and b are
+** changed.
+*/
+static char *merge(
+ char *a,
+ char *b,
+ int (*cmp)(const char*,const char*),
+ int offset
+){
+ char *ptr, *head;
+
+ if( a==0 ){
+ head = b;
+ }else if( b==0 ){
+ head = a;
+ }else{
+ if( (*cmp)(a,b)<0 ){
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ ptr = b;
+ b = NEXT(b);
+ }
+ head = ptr;
+ while( a && b ){
+ if( (*cmp)(a,b)<0 ){
+ NEXT(ptr) = a;
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ NEXT(ptr) = b;
+ ptr = b;
+ b = NEXT(b);
+ }
+ }
+ if( a ) NEXT(ptr) = a;
+ else NEXT(ptr) = b;
+ }
+ return head;
+}
+
+/*
+** Inputs:
+** list: Pointer to a singly-linked list of structures.
+** next: Pointer to pointer to the second element of the list.
+** cmp: A comparison function.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** orginally in list.
+**
+** Side effects:
+** The "next" pointers for elements in list are changed.
+*/
+#define LISTSIZE 30
+static char *msort(
+ char *list,
+ char **next,
+ int (*cmp)(const char*,const char*)
+){
+ unsigned long offset;
+ char *ep;
+ char *set[LISTSIZE];
+ int i;
+ offset = (unsigned long)next - (unsigned long)list;
+ for(i=0; i<LISTSIZE; i++) set[i] = 0;
+ while( list ){
+ ep = list;
+ list = NEXT(list);
+ NEXT(ep) = 0;
+ for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){
+ ep = merge(ep,set[i],cmp,offset);
+ set[i] = 0;
+ }
+ set[i] = ep;
+ }
+ ep = 0;
+ for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(ep,set[i],cmp,offset);
+ return ep;
+}
+/************************ From the file "option.c" **************************/
+static char **argv;
+static struct s_options *op;
+static FILE *errstream;
+
+#define ISOPT(X) ((X)[0]=='-'||(X)[0]=='+'||strchr((X),'=')!=0)
+
+/*
+** Print the command line with a carrot pointing to the k-th character
+** of the n-th field.
+*/
+static void errline(n,k,err)
+int n;
+int k;
+FILE *err;
+{
+ int spcnt, i;
+ if( argv[0] ) fprintf(err,"%s",argv[0]);
+ spcnt = strlen(argv[0]) + 1;
+ for(i=1; i<n && argv[i]; i++){
+ fprintf(err," %s",argv[i]);
+ spcnt += strlen(argv[i])+1;
+ }
+ spcnt += k;
+ for(; argv[i]; i++) fprintf(err," %s",argv[i]);
+ if( spcnt<20 ){
+ fprintf(err,"\n%*s^-- here\n",spcnt,"");
+ }else{
+ fprintf(err,"\n%*shere --^\n",spcnt-7,"");
+ }
+}
+
+/*
+** Return the index of the N-th non-switch argument. Return -1
+** if N is out of range.
+*/
+static int argindex(n)
+int n;
+{
+ int i;
+ int dashdash = 0;
+ if( argv!=0 && *argv!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ){
+ if( n==0 ) return i;
+ n--;
+ }
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return -1;
+}
+
+static char emsg[] = "Command line syntax error: ";
+
+/*
+** Process a flag command line argument.
+*/
+static int handleflags(i,err)
+int i;
+FILE *err;
+{
+ int v;
+ int errcnt = 0;
+ int j;
+ for(j=0; op[j].label; j++){
+ if( strncmp(&argv[i][1],op[j].label,strlen(op[j].label))==0 ) break;
+ }
+ v = argv[i][0]=='-' ? 1 : 0;
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }else if( op[j].type==OPT_FLAG ){
+ *((int*)op[j].arg) = v;
+ }else if( op[j].type==OPT_FFLAG ){
+ (*(void(*)())(op[j].arg))(v);
+ }else if( op[j].type==OPT_FSTR ){
+ (*(void(*)())(op[j].arg))(&argv[i][2]);
+ }else{
+ if( err ){
+ fprintf(err,"%smissing argument on switch.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }
+ return errcnt;
+}
+
+/*
+** Process a command line switch which has an argument.
+*/
+static int handleswitch(i,err)
+int i;
+FILE *err;
+{
+ int lv = 0;
+ double dv = 0.0;
+ char *sv = 0, *end;
+ char *cp;
+ int j;
+ int errcnt = 0;
+ cp = strchr(argv[i],'=');
+ assert( cp!=0 );
+ *cp = 0;
+ for(j=0; op[j].label; j++){
+ if( strcmp(argv[i],op[j].label)==0 ) break;
+ }
+ *cp = '=';
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ }else{
+ cp++;
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ if( err ){
+ fprintf(err,"%soption requires an argument.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ dv = strtod(cp,&end);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in floating-point argument.\n",emsg);
+ errline(i,((unsigned long)end)-(unsigned long)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ lv = strtol(cp,&end,0);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in integer argument.\n",emsg);
+ errline(i,((unsigned long)end)-(unsigned long)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ sv = cp;
+ break;
+ }
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_DBL:
+ *(double*)(op[j].arg) = dv;
+ break;
+ case OPT_FDBL:
+ (*(void(*)())(op[j].arg))(dv);
+ break;
+ case OPT_INT:
+ *(int*)(op[j].arg) = lv;
+ break;
+ case OPT_FINT:
+ (*(void(*)())(op[j].arg))((int)lv);
+ break;
+ case OPT_STR:
+ *(char**)(op[j].arg) = sv;
+ break;
+ case OPT_FSTR:
+ (*(void(*)())(op[j].arg))(sv);
+ break;
+ }
+ }
+ return errcnt;
+}
+
+int OptInit(a,o,err)
+char **a;
+struct s_options *o;
+FILE *err;
+{
+ int errcnt = 0;
+ argv = a;
+ op = o;
+ errstream = err;
+ if( argv && *argv && op ){
+ int i;
+ for(i=1; argv[i]; i++){
+ if( argv[i][0]=='+' || argv[i][0]=='-' ){
+ errcnt += handleflags(i,err);
+ }else if( strchr(argv[i],'=') ){
+ errcnt += handleswitch(i,err);
+ }
+ }
+ }
+ if( errcnt>0 ){
+ fprintf(err,"Valid command line options for \"%s\" are:\n",*a);
+ OptPrint();
+ exit(1);
+ }
+ return 0;
+}
+
+int OptNArgs(){
+ int cnt = 0;
+ int dashdash = 0;
+ int i;
+ if( argv!=0 && argv[0]!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ) cnt++;
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return cnt;
+}
+
+char *OptArg(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ return i>=0 ? argv[i] : 0;
+}
+
+void OptErr(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ if( i>=0 ) errline(i,0,errstream);
+}
+
+void OptPrint(){
+ int i;
+ int max, len;
+ max = 0;
+ for(i=0; op[i].label; i++){
+ len = strlen(op[i].label) + 1;
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ len += 9; /* length of "<integer>" */
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ len += 6; /* length of "<real>" */
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ len += 8; /* length of "<string>" */
+ break;
+ }
+ if( len>max ) max = len;
+ }
+ for(i=0; op[i].label; i++){
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message);
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ fprintf(errstream," %s=<integer>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-9),"",op[i].message);
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ fprintf(errstream," %s=<real>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-6),"",op[i].message);
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ fprintf(errstream," %s=<string>%*s %s\n",op[i].label,
+ (int)(max-strlen(op[i].label)-8),"",op[i].message);
+ break;
+ }
+ }
+}
+/*********************** From the file "parse.c" ****************************/
+/*
+** Input file parser for the LEMON parser generator.
+*/
+
+/* The state of the parser */
+struct pstate {
+ char *filename; /* Name of the input file */
+ int tokenlineno; /* Linenumber at which current token starts */
+ int errorcnt; /* Number of errors so far */
+ char *tokenstart; /* Text of current token */
+ struct lemon *gp; /* Global state vector */
+ enum e_state {
+ INITIALIZE,
+ WAITING_FOR_DECL_OR_RULE,
+ WAITING_FOR_DECL_KEYWORD,
+ WAITING_FOR_DECL_ARG,
+ WAITING_FOR_PRECEDENCE_SYMBOL,
+ WAITING_FOR_ARROW,
+ IN_RHS,
+ LHS_ALIAS_1,
+ LHS_ALIAS_2,
+ LHS_ALIAS_3,
+ RHS_ALIAS_1,
+ RHS_ALIAS_2,
+ PRECEDENCE_MARK_1,
+ PRECEDENCE_MARK_2,
+ RESYNC_AFTER_RULE_ERROR,
+ RESYNC_AFTER_DECL_ERROR,
+ WAITING_FOR_DESTRUCTOR_SYMBOL,
+ WAITING_FOR_DATATYPE_SYMBOL,
+ WAITING_FOR_FALLBACK_ID,
+ WAITING_FOR_WILDCARD_ID
+ } state; /* The state of the parser */
+ struct symbol *fallback; /* The fallback token */
+ struct symbol *lhs; /* Left-hand side of current rule */
+ char *lhsalias; /* Alias for the LHS */
+ int nrhs; /* Number of right-hand side symbols seen */
+ struct symbol *rhs[MAXRHS]; /* RHS symbols */
+ char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */
+ struct rule *prevrule; /* Previous rule parsed */
+ char *declkeyword; /* Keyword of a declaration */
+ char **declargslot; /* Where the declaration argument should be put */
+ int insertLineMacro; /* Add #line before declaration insert */
+ int *decllinenoslot; /* Where to write declaration line number */
+ enum e_assoc declassoc; /* Assign this association to decl arguments */
+ int preccounter; /* Assign this precedence to decl arguments */
+ struct rule *firstrule; /* Pointer to first rule in the grammar */
+ struct rule *lastrule; /* Pointer to the most recently parsed rule */
+};
+
+/* Parse a single token */
+static void parseonetoken(psp)
+struct pstate *psp;
+{
+ char *x;
+ x = Strsafe(psp->tokenstart); /* Save the token permanently */
+#if 0
+ printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno,
+ x,psp->state);
+#endif
+ switch( psp->state ){
+ case INITIALIZE:
+ psp->prevrule = 0;
+ psp->preccounter = 0;
+ psp->firstrule = psp->lastrule = 0;
+ psp->gp->nrule = 0;
+ /* Fall thru to next case */
+ case WAITING_FOR_DECL_OR_RULE:
+ if( x[0]=='%' ){
+ psp->state = WAITING_FOR_DECL_KEYWORD;
+ }else if( islower(x[0]) ){
+ psp->lhs = Symbol_new(x);
+ psp->nrhs = 0;
+ psp->lhsalias = 0;
+ psp->state = WAITING_FOR_ARROW;
+ }else if( x[0]=='{' ){
+ if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"There is no prior rule opon which to attach the code \
+fragment which begins on this line.");
+ psp->errorcnt++;
+ }else if( psp->prevrule->code!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Code fragment beginning on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->line = psp->tokenlineno;
+ psp->prevrule->code = &x[1];
+ }
+ }else if( x[0]=='[' ){
+ psp->state = PRECEDENCE_MARK_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Token \"%s\" should be either \"%%\" or a nonterminal name.",
+ x);
+ psp->errorcnt++;
+ }
+ break;
+ case PRECEDENCE_MARK_1:
+ if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The precedence symbol must be a terminal.");
+ psp->errorcnt++;
+ }else if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "There is no prior rule to assign precedence \"[%s]\".",x);
+ psp->errorcnt++;
+ }else if( psp->prevrule->precsym!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Precedence mark on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->precsym = Symbol_new(x);
+ }
+ psp->state = PRECEDENCE_MARK_2;
+ break;
+ case PRECEDENCE_MARK_2:
+ if( x[0]!=']' ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"]\" on precedence mark.");
+ psp->errorcnt++;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ break;
+ case WAITING_FOR_ARROW:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else if( x[0]=='(' ){
+ psp->state = LHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Expected to see a \":\" following the LHS symbol \"%s\".",
+ psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->lhsalias = x;
+ psp->state = LHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the LHS \"%s\"\n",
+ x,psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = LHS_ALIAS_3;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_3:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"->\" following: \"%s(%s)\".",
+ psp->lhs->name,psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case IN_RHS:
+ if( x[0]=='.' ){
+ struct rule *rp;
+ rp = (struct rule *)calloc( sizeof(struct rule) +
+ sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1);
+ if( rp==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't allocate enough memory for this rule.");
+ psp->errorcnt++;
+ psp->prevrule = 0;
+ }else{
+ int i;
+ rp->ruleline = psp->tokenlineno;
+ rp->rhs = (struct symbol**)&rp[1];
+ rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]);
+ for(i=0; i<psp->nrhs; i++){
+ rp->rhs[i] = psp->rhs[i];
+ rp->rhsalias[i] = psp->alias[i];
+ }
+ rp->lhs = psp->lhs;
+ rp->lhsalias = psp->lhsalias;
+ rp->nrhs = psp->nrhs;
+ rp->code = 0;
+ rp->precsym = 0;
+ rp->index = psp->gp->nrule++;
+ rp->nextlhs = rp->lhs->rule;
+ rp->lhs->rule = rp;
+ rp->next = 0;
+ if( psp->firstrule==0 ){
+ psp->firstrule = psp->lastrule = rp;
+ }else{
+ psp->lastrule->next = rp;
+ psp->lastrule = rp;
+ }
+ psp->prevrule = rp;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isalpha(x[0]) ){
+ if( psp->nrhs>=MAXRHS ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Too many symbols on RHS of rule beginning at \"%s\".",
+ x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }else{
+ psp->rhs[psp->nrhs] = Symbol_new(x);
+ psp->alias[psp->nrhs] = 0;
+ psp->nrhs++;
+ }
+ }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){
+ struct symbol *msp = psp->rhs[psp->nrhs-1];
+ if( msp->type!=MULTITERMINAL ){
+ struct symbol *origsp = msp;
+ msp = calloc(1,sizeof(*msp));
+ memset(msp, 0, sizeof(*msp));
+ msp->type = MULTITERMINAL;
+ msp->nsubsym = 1;
+ msp->subsym = calloc(1,sizeof(struct symbol*));
+ msp->subsym[0] = origsp;
+ msp->name = origsp->name;
+ psp->rhs[psp->nrhs-1] = msp;
+ }
+ msp->nsubsym++;
+ msp->subsym = realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym);
+ msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]);
+ if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Cannot form a compound containing a non-terminal");
+ psp->errorcnt++;
+ }
+ }else if( x[0]=='(' && psp->nrhs>0 ){
+ psp->state = RHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal character on RHS of rule: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->alias[psp->nrhs-1] = x;
+ psp->state = RHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
+ x,psp->rhs[psp->nrhs-1]->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case WAITING_FOR_DECL_KEYWORD:
+ if( isalpha(x[0]) ){
+ psp->declkeyword = x;
+ psp->declargslot = 0;
+ psp->decllinenoslot = 0;
+ psp->insertLineMacro = 1;
+ psp->state = WAITING_FOR_DECL_ARG;
+ if( strcmp(x,"name")==0 ){
+ psp->declargslot = &(psp->gp->name);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"include")==0 ){
+ psp->declargslot = &(psp->gp->include);
+ }else if( strcmp(x,"code")==0 ){
+ psp->declargslot = &(psp->gp->extracode);
+ }else if( strcmp(x,"token_destructor")==0 ){
+ psp->declargslot = &psp->gp->tokendest;
+ }else if( strcmp(x,"default_destructor")==0 ){
+ psp->declargslot = &psp->gp->vardest;
+ }else if( strcmp(x,"token_prefix")==0 ){
+ psp->declargslot = &psp->gp->tokenprefix;
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"syntax_error")==0 ){
+ psp->declargslot = &(psp->gp->error);
+ }else if( strcmp(x,"parse_accept")==0 ){
+ psp->declargslot = &(psp->gp->accept);
+ }else if( strcmp(x,"parse_failure")==0 ){
+ psp->declargslot = &(psp->gp->failure);
+ }else if( strcmp(x,"stack_overflow")==0 ){
+ psp->declargslot = &(psp->gp->overflow);
+ }else if( strcmp(x,"extra_argument")==0 ){
+ psp->declargslot = &(psp->gp->arg);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"token_type")==0 ){
+ psp->declargslot = &(psp->gp->tokentype);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"default_type")==0 ){
+ psp->declargslot = &(psp->gp->vartype);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"stack_size")==0 ){
+ psp->declargslot = &(psp->gp->stacksize);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"start_symbol")==0 ){
+ psp->declargslot = &(psp->gp->start);
+ psp->insertLineMacro = 0;
+ }else if( strcmp(x,"left")==0 ){
+ psp->preccounter++;
+ psp->declassoc = LEFT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"right")==0 ){
+ psp->preccounter++;
+ psp->declassoc = RIGHT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"nonassoc")==0 ){
+ psp->preccounter++;
+ psp->declassoc = NONE;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"destructor")==0 ){
+ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL;
+ }else if( strcmp(x,"type")==0 ){
+ psp->state = WAITING_FOR_DATATYPE_SYMBOL;
+ }else if( strcmp(x,"fallback")==0 ){
+ psp->fallback = 0;
+ psp->state = WAITING_FOR_FALLBACK_ID;
+ }else if( strcmp(x,"wildcard")==0 ){
+ psp->state = WAITING_FOR_WILDCARD_ID;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Unknown declaration keyword: \"%%%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal declaration keyword: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_DESTRUCTOR_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->destructor;
+ psp->decllinenoslot = &sp->destLineno;
+ psp->insertLineMacro = 1;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_DATATYPE_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->datatype;
+ psp->insertLineMacro = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_PRECEDENCE_SYMBOL:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) ){
+ struct symbol *sp;
+ sp = Symbol_new(x);
+ if( sp->prec>=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol \"%s\" has already be given a precedence.",x);
+ psp->errorcnt++;
+ }else{
+ sp->prec = psp->preccounter;
+ sp->assoc = psp->declassoc;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't assign a precedence to \"%s\".",x);
+ psp->errorcnt++;
+ }
+ break;
+ case WAITING_FOR_DECL_ARG:
+ if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){
+ char *zOld, *zNew, *zBuf, *z;
+ int nOld, n, nLine, nNew, nBack;
+ int addLineMacro;
+ char zLine[50];
+ zNew = x;
+ if( zNew[0]=='"' || zNew[0]=='{' ) zNew++;
+ nNew = strlen(zNew);
+ if( *psp->declargslot ){
+ zOld = *psp->declargslot;
+ }else{
+ zOld = "";
+ }
+ nOld = strlen(zOld);
+ n = nOld + nNew + 20;
+ addLineMacro = psp->insertLineMacro &&
+ (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0);
+ if( addLineMacro ){
+ for(z=psp->filename, nBack=0; *z; z++){
+ if( *z=='\\' ) nBack++;
+ }
+ sprintf(zLine, "#line %d ", psp->tokenlineno);
+ nLine = strlen(zLine);
+ n += nLine + strlen(psp->filename) + nBack;
+ }
+ *psp->declargslot = zBuf = realloc(*psp->declargslot, n);
+ zBuf += nOld;
+ if( addLineMacro ){
+ if( nOld && zBuf[-1]!='\n' ){
+ *(zBuf++) = '\n';
+ }
+ memcpy(zBuf, zLine, nLine);
+ zBuf += nLine;
+ *(zBuf++) = '"';
+ for(z=psp->filename; *z; z++){
+ if( *z=='\\' ){
+ *(zBuf++) = '\\';
+ }
+ *(zBuf++) = *z;
+ }
+ *(zBuf++) = '"';
+ *(zBuf++) = '\n';
+ }
+ if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){
+ psp->decllinenoslot[0] = psp->tokenlineno;
+ }
+ memcpy(zBuf, zNew, nNew);
+ zBuf += nNew;
+ *zBuf = 0;
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal argument to %%%s: %s",psp->declkeyword,x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_FALLBACK_ID:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%fallback argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ if( psp->fallback==0 ){
+ psp->fallback = sp;
+ }else if( sp->fallback ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "More than one fallback assigned to token %s", x);
+ psp->errorcnt++;
+ }else{
+ sp->fallback = psp->fallback;
+ psp->gp->has_fallback = 1;
+ }
+ }
+ break;
+ case WAITING_FOR_WILDCARD_ID:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%wildcard argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ if( psp->gp->wildcard==0 ){
+ psp->gp->wildcard = sp;
+ }else{
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "Extra wildcard to token: %s", x);
+ psp->errorcnt++;
+ }
+ }
+ break;
+ case RESYNC_AFTER_RULE_ERROR:
+/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+** break; */
+ case RESYNC_AFTER_DECL_ERROR:
+ if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+ if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD;
+ break;
+ }
+}
+
+/* Run the preprocessor over the input file text. The global variables
+** azDefine[0] through azDefine[nDefine-1] contains the names of all defined
+** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and
+** comments them out. Text in between is also commented out as appropriate.
+*/
+static void preprocess_input(char *z){
+ int i, j, k, n;
+ int exclude = 0;
+ int start = 0;
+ int lineno = 1;
+ int start_lineno = 1;
+ for(i=0; z[i]; i++){
+ if( z[i]=='\n' ) lineno++;
+ if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue;
+ if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){
+ if( exclude ){
+ exclude--;
+ if( exclude==0 ){
+ for(j=start; j<i; j++) if( z[j]!='\n' ) z[j] = ' ';
+ }
+ }
+ for(j=i; z[j] && z[j]!='\n'; j++) z[j] = ' ';
+ }else if( (strncmp(&z[i],"%ifdef",6)==0 && isspace(z[i+6]))
+ || (strncmp(&z[i],"%ifndef",7)==0 && isspace(z[i+7])) ){
+ if( exclude ){
+ exclude++;
+ }else{
+ for(j=i+7; isspace(z[j]); j++){}
+ for(n=0; z[j+n] && !isspace(z[j+n]); n++){}
+ exclude = 1;
+ for(k=0; k<nDefine; k++){
+ if( strncmp(azDefine[k],&z[j],n)==0 && strlen(azDefine[k])==n ){
+ exclude = 0;
+ break;
+ }
+ }
+ if( z[i+3]=='n' ) exclude = !exclude;
+ if( exclude ){
+ start = i;
+ start_lineno = lineno;
+ }
+ }
+ for(j=i; z[j] && z[j]!='\n'; j++) z[j] = ' ';
+ }
+ }
+ if( exclude ){
+ fprintf(stderr,"unterminated %%ifdef starting on line %d\n", start_lineno);
+ exit(1);
+ }
+}
+
+/* In spite of its name, this function is really a scanner. It read
+** in the entire input file (all at once) then tokenizes it. Each
+** token is passed to the function "parseonetoken" which builds all
+** the appropriate data structures in the global state vector "gp".
+*/
+void Parse(gp)
+struct lemon *gp;
+{
+ struct pstate ps;
+ FILE *fp;
+ char *filebuf;
+ int filesize;
+ int lineno;
+ int c;
+ char *cp, *nextcp;
+ int startline = 0;
+
+ memset(&ps, '\0', sizeof(ps));
+ ps.gp = gp;
+ ps.filename = gp->filename;
+ ps.errorcnt = 0;
+ ps.state = INITIALIZE;
+
+ /* Begin by reading the input file */
+ fp = fopen(ps.filename,"rb");
+ if( fp==0 ){
+ ErrorMsg(ps.filename,0,"Can't open this file for reading.");
+ gp->errorcnt++;
+ return;
+ }
+ fseek(fp,0,2);
+ filesize = ftell(fp);
+ rewind(fp);
+ filebuf = (char *)malloc( filesize+1 );
+ if( filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.",
+ filesize+1);
+ gp->errorcnt++;
+ return;
+ }
+ if( fread(filebuf,1,filesize,fp)!=filesize ){
+ ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.",
+ filesize);
+ free(filebuf);
+ gp->errorcnt++;
+ return;
+ }
+ fclose(fp);
+ filebuf[filesize] = 0;
+
+ /* Make an initial pass through the file to handle %ifdef and %ifndef */
+ preprocess_input(filebuf);
+
+ /* Now scan the text of the input file */
+ lineno = 1;
+ for(cp=filebuf; (c= *cp)!=0; ){
+ if( c=='\n' ) lineno++; /* Keep track of the line number */
+ if( isspace(c) ){ cp++; continue; } /* Skip all white space */
+ if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ continue;
+ }
+ if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c ) cp++;
+ continue;
+ }
+ ps.tokenstart = cp; /* Mark the beginning of the token */
+ ps.tokenlineno = lineno; /* Linenumber on which token begins */
+ if( c=='\"' ){ /* String literals */
+ cp++;
+ while( (c= *cp)!=0 && c!='\"' ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"String starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( c=='{' ){ /* A block of C code */
+ int level;
+ cp++;
+ for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){
+ if( c=='\n' ) lineno++;
+ else if( c=='{' ) level++;
+ else if( c=='}' ) level--;
+ else if( c=='/' && cp[1]=='*' ){ /* Skip comments */
+ int prevc;
+ cp = &cp[2];
+ prevc = 0;
+ while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){
+ if( c=='\n' ) lineno++;
+ prevc = c;
+ cp++;
+ }
+ }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */
+ cp = &cp[2];
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ if( c ) lineno++;
+ }else if( c=='\'' || c=='\"' ){ /* String a character literals */
+ int startchar, prevc;
+ startchar = c;
+ prevc = 0;
+ for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){
+ if( c=='\n' ) lineno++;
+ if( prevc=='\\' ) prevc = 0;
+ else prevc = c;
+ }
+ }
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,ps.tokenlineno,
+"C code starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( isalnum(c) ){ /* Identifiers */
+ while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */
+ cp += 3;
+ nextcp = cp;
+ }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){
+ cp += 2;
+ while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else{ /* All other (one character) operators */
+ cp++;
+ nextcp = cp;
+ }
+ c = *cp;
+ *cp = 0; /* Null terminate the token */
+ parseonetoken(&ps); /* Parse the token */
+ *cp = c; /* Restore the buffer */
+ cp = nextcp;
+ }
+ free(filebuf); /* Release the buffer after parsing */
+ gp->rule = ps.firstrule;
+ gp->errorcnt = ps.errorcnt;
+}
+/*************************** From the file "plink.c" *********************/
+/*
+** Routines processing configuration follow-set propagation links
+** in the LEMON parser generator.
+*/
+static struct plink *plink_freelist = 0;
+
+/* Allocate a new plink */
+struct plink *Plink_new(){
+ struct plink *new;
+
+ if( plink_freelist==0 ){
+ int i;
+ int amt = 100;
+ plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) );
+ if( plink_freelist==0 ){
+ fprintf(stderr,
+ "Unable to allocate memory for a new follow-set propagation link.\n");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) plink_freelist[i].next = &plink_freelist[i+1];
+ plink_freelist[amt-1].next = 0;
+ }
+ new = plink_freelist;
+ plink_freelist = plink_freelist->next;
+ return new;
+}
+
+/* Add a plink to a plink list */
+void Plink_add(plpp,cfp)
+struct plink **plpp;
+struct config *cfp;
+{
+ struct plink *new;
+ new = Plink_new();
+ new->next = *plpp;
+ *plpp = new;
+ new->cfp = cfp;
+}
+
+/* Transfer every plink on the list "from" to the list "to" */
+void Plink_copy(to,from)
+struct plink **to;
+struct plink *from;
+{
+ struct plink *nextpl;
+ while( from ){
+ nextpl = from->next;
+ from->next = *to;
+ *to = from;
+ from = nextpl;
+ }
+}
+
+/* Delete every plink on the list */
+void Plink_delete(plp)
+struct plink *plp;
+{
+ struct plink *nextpl;
+
+ while( plp ){
+ nextpl = plp->next;
+ plp->next = plink_freelist;
+ plink_freelist = plp;
+ plp = nextpl;
+ }
+}
+/*********************** From the file "report.c" **************************/
+/*
+** Procedures for generating reports and tables in the LEMON parser generator.
+*/
+
+/* Generate a filename with the given suffix. Space to hold the
+** name comes from malloc() and must be freed by the calling
+** function.
+*/
+PRIVATE char *file_makename(lemp,suffix)
+struct lemon *lemp;
+char *suffix;
+{
+ char *name;
+ char *cp;
+
+ name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 );
+ if( name==0 ){
+ fprintf(stderr,"Can't allocate space for a filename.\n");
+ exit(1);
+ }
+ strcpy(name,lemp->filename);
+ cp = strrchr(name,'.');
+ if( cp ) *cp = 0;
+ strcat(name,suffix);
+ return name;
+}
+
+/* Open a file with a name based on the name of the input file,
+** but with a different (specified) suffix, and return a pointer
+** to the stream */
+PRIVATE FILE *file_open(lemp,suffix,mode)
+struct lemon *lemp;
+char *suffix;
+char *mode;
+{
+ FILE *fp;
+
+ if( lemp->outname ) free(lemp->outname);
+ lemp->outname = file_makename(lemp, suffix);
+ fp = fopen(lemp->outname,mode);
+ if( fp==0 && *mode=='w' ){
+ fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return fp;
+}
+
+/* Duplicate the input file without comments and without actions
+** on rules */
+void Reprint(lemp)
+struct lemon *lemp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j, maxlen, len, ncolumns, skip;
+ printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename);
+ maxlen = 10;
+ for(i=0; i<lemp->nsymbol; i++){
+ sp = lemp->symbols[i];
+ len = strlen(sp->name);
+ if( len>maxlen ) maxlen = len;
+ }
+ ncolumns = 76/(maxlen+5);
+ if( ncolumns<1 ) ncolumns = 1;
+ skip = (lemp->nsymbol + ncolumns - 1)/ncolumns;
+ for(i=0; i<skip; i++){
+ printf("//");
+ for(j=i; j<lemp->nsymbol; j+=skip){
+ sp = lemp->symbols[j];
+ assert( sp->index==j );
+ printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name);
+ }
+ printf("\n");
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ printf("%s",rp->lhs->name);
+ /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */
+ printf(" ::=");
+ for(i=0; i<rp->nrhs; i++){
+ sp = rp->rhs[i];
+ printf(" %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ for(j=1; j<sp->nsubsym; j++){
+ printf("|%s", sp->subsym[j]->name);
+ }
+ }
+ /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
+ }
+ printf(".");
+ if( rp->precsym ) printf(" [%s]",rp->precsym->name);
+ /* if( rp->code ) printf("\n %s",rp->code); */
+ printf("\n");
+ }
+}
+
+void ConfigPrint(fp,cfp)
+FILE *fp;
+struct config *cfp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j;
+ rp = cfp->rp;
+ fprintf(fp,"%s ::=",rp->lhs->name);
+ for(i=0; i<=rp->nrhs; i++){
+ if( i==cfp->dot ) fprintf(fp," *");
+ if( i==rp->nrhs ) break;
+ sp = rp->rhs[i];
+ fprintf(fp," %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ for(j=1; j<sp->nsubsym; j++){
+ fprintf(fp,"|%s",sp->subsym[j]->name);
+ }
+ }
+ }
+}
+
+/* #define TEST */
+#if 0
+/* Print a set */
+PRIVATE void SetPrint(out,set,lemp)
+FILE *out;
+char *set;
+struct lemon *lemp;
+{
+ int i;
+ char *spacer;
+ spacer = "";
+ fprintf(out,"%12s[","");
+ for(i=0; i<lemp->nterminal; i++){
+ if( SetFind(set,i) ){
+ fprintf(out,"%s%s",spacer,lemp->symbols[i]->name);
+ spacer = " ";
+ }
+ }
+ fprintf(out,"]\n");
+}
+
+/* Print a plink chain */
+PRIVATE void PlinkPrint(out,plp,tag)
+FILE *out;
+struct plink *plp;
+char *tag;
+{
+ while( plp ){
+ fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum);
+ ConfigPrint(out,plp->cfp);
+ fprintf(out,"\n");
+ plp = plp->next;
+ }
+}
+#endif
+
+/* Print an action to the given file descriptor. Return FALSE if
+** nothing was actually printed.
+*/
+int PrintAction(struct action *ap, FILE *fp, int indent){
+ int result = 1;
+ switch( ap->type ){
+ case SHIFT:
+ fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum);
+ break;
+ case REDUCE:
+ fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case ACCEPT:
+ fprintf(fp,"%*s accept",indent,ap->sp->name);
+ break;
+ case ERROR:
+ fprintf(fp,"%*s error",indent,ap->sp->name);
+ break;
+ case SRCONFLICT:
+ case RRCONFLICT:
+ fprintf(fp,"%*s reduce %-3d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case SSCONFLICT:
+ fprintf(fp,"%*s shift %d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.stp->statenum);
+ break;
+ case SH_RESOLVED:
+ case RD_RESOLVED:
+ case NOT_USED:
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+/* Generate the "y.output" log file */
+void ReportOutput(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct config *cfp;
+ struct action *ap;
+ FILE *fp;
+
+ fp = file_open(lemp,".out","wb");
+ if( fp==0 ) return;
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ fprintf(fp,"State %d:\n",stp->statenum);
+ if( lemp->basisflag ) cfp=stp->bp;
+ else cfp=stp->cfp;
+ while( cfp ){
+ char buf[20];
+ if( cfp->dot==cfp->rp->nrhs ){
+ sprintf(buf,"(%d)",cfp->rp->index);
+ fprintf(fp," %5s ",buf);
+ }else{
+ fprintf(fp," ");
+ }
+ ConfigPrint(fp,cfp);
+ fprintf(fp,"\n");
+#if 0
+ SetPrint(fp,cfp->fws,lemp);
+ PlinkPrint(fp,cfp->fplp,"To ");
+ PlinkPrint(fp,cfp->bplp,"From");
+#endif
+ if( lemp->basisflag ) cfp=cfp->bp;
+ else cfp=cfp->next;
+ }
+ fprintf(fp,"\n");
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( PrintAction(ap,fp,30) ) fprintf(fp,"\n");
+ }
+ fprintf(fp,"\n");
+ }
+ fprintf(fp, "----------------------------------------------------\n");
+ fprintf(fp, "Symbols:\n");
+ for(i=0; i<lemp->nsymbol; i++){
+ int j;
+ struct symbol *sp;
+
+ sp = lemp->symbols[i];
+ fprintf(fp, " %3d: %s", i, sp->name);
+ if( sp->type==NONTERMINAL ){
+ fprintf(fp, ":");
+ if( sp->lambda ){
+ fprintf(fp, " <lambda>");
+ }
+ for(j=0; j<lemp->nterminal; j++){
+ if( sp->firstset && SetFind(sp->firstset, j) ){
+ fprintf(fp, " %s", lemp->symbols[j]->name);
+ }
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ fclose(fp);
+ return;
+}
+
+/* Search for the file "name" which is in the same directory as
+** the exacutable */
+PRIVATE char *pathsearch(argv0,name,modemask)
+char *argv0;
+char *name;
+int modemask;
+{
+ char *pathlist;
+ char *path,*cp;
+ char c;
+
+#ifdef __WIN32__
+ cp = strrchr(argv0,'\\');
+#else
+ cp = strrchr(argv0,'/');
+#endif
+ if( cp ){
+ c = *cp;
+ *cp = 0;
+ path = (char *)malloc( strlen(argv0) + strlen(name) + 2 );
+ if( path ) sprintf(path,"%s/%s",argv0,name);
+ *cp = c;
+ }else{
+ extern char *getenv();
+ pathlist = getenv("PATH");
+ if( pathlist==0 ) pathlist = ".:/bin:/usr/bin";
+ path = (char *)malloc( strlen(pathlist)+strlen(name)+2 );
+ if( path!=0 ){
+ while( *pathlist ){
+ cp = strchr(pathlist,':');
+ if( cp==0 ) cp = &pathlist[strlen(pathlist)];
+ c = *cp;
+ *cp = 0;
+ sprintf(path,"%s/%s",pathlist,name);
+ *cp = c;
+ if( c==0 ) pathlist = "";
+ else pathlist = &cp[1];
+ if( access(path,modemask)==0 ) break;
+ }
+ }
+ }
+ return path;
+}
+
+/* Given an action, compute the integer value for that action
+** which is to be put in the action table of the generated machine.
+** Return negative if no action should be generated.
+*/
+PRIVATE int compute_action(lemp,ap)
+struct lemon *lemp;
+struct action *ap;
+{
+ int act;
+ switch( ap->type ){
+ case SHIFT: act = ap->x.stp->statenum; break;
+ case REDUCE: act = ap->x.rp->index + lemp->nstate; break;
+ case ERROR: act = lemp->nstate + lemp->nrule; break;
+ case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break;
+ default: act = -1; break;
+ }
+ return act;
+}
+
+#define LINESIZE 1000
+/* The next cluster of routines are for reading the template file
+** and writing the results to the generated parser */
+/* The first function transfers data from "in" to "out" until
+** a line is seen which begins with "%%". The line number is
+** tracked.
+**
+** if name!=0, then any word that begin with "Parse" is changed to
+** begin with *name instead.
+*/
+PRIVATE void tplt_xfer(name,in,out,lineno)
+char *name;
+FILE *in;
+FILE *out;
+int *lineno;
+{
+ int i, iStart;
+ char line[LINESIZE];
+ while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
+ (*lineno)++;
+ iStart = 0;
+ if( name ){
+ for(i=0; line[i]; i++){
+ if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0
+ && (i==0 || !isalpha(line[i-1]))
+ ){
+ if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]);
+ fprintf(out,"%s",name);
+ i += 4;
+ iStart = i+1;
+ }
+ }
+ }
+ fprintf(out,"%s",&line[iStart]);
+ }
+}
+
+/* The next function finds the template file and opens it, returning
+** a pointer to the opened file. */
+PRIVATE FILE *tplt_open(lemp)
+struct lemon *lemp;
+{
+ static char templatename[] = "lempar.c";
+ char buf[1000];
+ FILE *in;
+ char *tpltname;
+ char *cp;
+
+ cp = strrchr(lemp->filename,'.');
+ if( cp ){
+ sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
+ }else{
+ sprintf(buf,"%s.lt",lemp->filename);
+ }
+ if( access(buf,004)==0 ){
+ tpltname = buf;
+ }else if( access(templatename,004)==0 ){
+ tpltname = templatename;
+ }else{
+ tpltname = pathsearch(lemp->argv0,templatename,0);
+ }
+ if( tpltname==0 ){
+ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n",
+ templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ in = fopen(tpltname,"rb");
+ if( in==0 ){
+ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return in;
+}
+
+/* Print a #line directive line to the output file. */
+PRIVATE void tplt_linedir(out,lineno,filename)
+FILE *out;
+int lineno;
+char *filename;
+{
+ fprintf(out,"#line %d \"",lineno);
+ while( *filename ){
+ if( *filename == '\\' ) putc('\\',out);
+ putc(*filename,out);
+ filename++;
+ }
+ fprintf(out,"\"\n");
+}
+
+/* Print a string to the file and keep the linenumber up to date */
+PRIVATE void tplt_print(out,lemp,str,lineno)
+FILE *out;
+struct lemon *lemp;
+char *str;
+int *lineno;
+{
+ if( str==0 ) return;
+ (*lineno)++;
+ while( *str ){
+ if( *str=='\n' ) (*lineno)++;
+ putc(*str,out);
+ str++;
+ }
+ if( str[-1]!='\n' ){
+ putc('\n',out);
+ (*lineno)++;
+ }
+ tplt_linedir(out,*lineno+2,lemp->outname);
+ (*lineno)+=2;
+ return;
+}
+
+/*
+** The following routine emits code for the destructor for the
+** symbol sp
+*/
+void emit_destructor_code(out,sp,lemp,lineno)
+FILE *out;
+struct symbol *sp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp = 0;
+
+ int linecnt = 0;
+ if( sp->type==TERMINAL ){
+ cp = lemp->tokendest;
+ if( cp==0 ) return;
+ fprintf(out,"{\n"); (*lineno)++;
+ }else if( sp->destructor ){
+ cp = sp->destructor;
+ fprintf(out,"{\n"); (*lineno)++;
+ tplt_linedir(out,sp->destLineno,lemp->filename); (*lineno)++;
+ }else if( lemp->vardest ){
+ cp = lemp->vardest;
+ if( cp==0 ) return;
+ fprintf(out,"{\n"); (*lineno)++;
+ }else{
+ assert( 0 ); /* Cannot happen */
+ }
+ for(; *cp; cp++){
+ if( *cp=='$' && cp[1]=='$' ){
+ fprintf(out,"(yypminor->yy%d)",sp->dtnum);
+ cp++;
+ continue;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ }
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"\n");
+ tplt_linedir(out,*lineno,lemp->outname);
+ fprintf(out,"}\n");
+ return;
+}
+
+/*
+** Return TRUE (non-zero) if the given symbol has a destructor.
+*/
+int has_destructor(sp, lemp)
+struct symbol *sp;
+struct lemon *lemp;
+{
+ int ret;
+ if( sp->type==TERMINAL ){
+ ret = lemp->tokendest!=0;
+ }else{
+ ret = lemp->vardest!=0 || sp->destructor!=0;
+ }
+ return ret;
+}
+
+/*
+** Append text to a dynamically allocated string. If zText is 0 then
+** reset the string to be empty again. Always return the complete text
+** of the string (which is overwritten with each call).
+**
+** n bytes of zText are stored. If n==0 then all of zText up to the first
+** \000 terminator is stored. zText can contain up to two instances of
+** %d. The values of p1 and p2 are written into the first and second
+** %d.
+**
+** If n==-1, then the previous character is overwritten.
+*/
+PRIVATE char *append_str(char *zText, int n, int p1, int p2){
+ static char *z = 0;
+ static int alloced = 0;
+ static int used = 0;
+ int c;
+ char zInt[40];
+
+ if( zText==0 ){
+ used = 0;
+ return z;
+ }
+ if( n<=0 ){
+ if( n<0 ){
+ used += n;
+ assert( used>=0 );
+ }
+ n = strlen(zText);
+ }
+ if( n+sizeof(zInt)*2+used >= alloced ){
+ alloced = n + sizeof(zInt)*2 + used + 200;
+ z = realloc(z, alloced);
+ }
+ if( z==0 ) return "";
+ while( n-- > 0 ){
+ c = *(zText++);
+ if( c=='%' && n>0 && zText[0]=='d' ){
+ sprintf(zInt, "%d", p1);
+ p1 = p2;
+ strcpy(&z[used], zInt);
+ used += strlen(&z[used]);
+ zText++;
+ n--;
+ }else{
+ z[used++] = c;
+ }
+ }
+ z[used] = 0;
+ return z;
+}
+
+/*
+** zCode is a string that is the action associated with a rule. Expand
+** the symbols in this string so that the refer to elements of the parser
+** stack.
+*/
+PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){
+ char *cp, *xp;
+ int i;
+ char lhsused = 0; /* True if the LHS element has been used */
+ char used[MAXRHS]; /* True for each RHS element which is used */
+
+ for(i=0; i<rp->nrhs; i++) used[i] = 0;
+ lhsused = 0;
+
+ if( rp->code==0 ){
+ rp->code = "\n";
+ rp->line = rp->ruleline;
+ }
+
+ append_str(0,0,0,0);
+ for(cp=rp->code; *cp; cp++){
+ if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){
+ char saved;
+ for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++);
+ saved = *xp;
+ *xp = 0;
+ if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){
+ append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0);
+ cp = xp;
+ lhsused = 1;
+ }else{
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){
+ if( cp!=rp->code && cp[-1]=='@' ){
+ /* If the argument is of the form @X then substituted
+ ** the token number of X, not the value of X */
+ append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0);
+ }else{
+ struct symbol *sp = rp->rhs[i];
+ int dtnum;
+ if( sp->type==MULTITERMINAL ){
+ dtnum = sp->subsym[0]->dtnum;
+ }else{
+ dtnum = sp->dtnum;
+ }
+ append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum);
+ }
+ cp = xp;
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ *xp = saved;
+ }
+ append_str(cp, 1, 0, 0);
+ } /* End loop */
+
+ /* Check to make sure the LHS has been used */
+ if( rp->lhsalias && !lhsused ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label \"%s\" for \"%s(%s)\" is never used.",
+ rp->lhsalias,rp->lhs->name,rp->lhsalias);
+ lemp->errorcnt++;
+ }
+
+ /* Generate destructor code for RHS symbols which are not used in the
+ ** reduce code */
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && !used[i] ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label %s for \"%s(%s)\" is never used.",
+ rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]);
+ lemp->errorcnt++;
+ }else if( rp->rhsalias[i]==0 ){
+ if( has_destructor(rp->rhs[i],lemp) ){
+ append_str(" yy_destructor(%d,&yymsp[%d].minor);\n", 0,
+ rp->rhs[i]->index,i-rp->nrhs+1);
+ }else{
+ /* No destructor defined for this term */
+ }
+ }
+ }
+ if( rp->code ){
+ cp = append_str(0,0,0,0);
+ rp->code = Strsafe(cp?cp:"");
+ }
+}
+
+/*
+** Generate code which executes when the rule "rp" is reduced. Write
+** the code to "out". Make sure lineno stays up-to-date.
+*/
+PRIVATE void emit_code(out,rp,lemp,lineno)
+FILE *out;
+struct rule *rp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp;
+ int linecnt = 0;
+
+ /* Generate code to do the reduce action */
+ if( rp->code ){
+ tplt_linedir(out,rp->line,lemp->filename);
+ fprintf(out,"{%s",rp->code);
+ for(cp=rp->code; *cp; cp++){
+ if( *cp=='\n' ) linecnt++;
+ } /* End loop */
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n");
+ tplt_linedir(out,*lineno,lemp->outname);
+ } /* End if( rp->code ) */
+
+ return;
+}
+
+/*
+** Print the definition of the union used for the parser's data stack.
+** This union contains fields for every possible data type for tokens
+** and nonterminals. In the process of computing and printing this
+** union, also set the ".dtnum" field of every terminal and nonterminal
+** symbol.
+*/
+void print_stack_union(out,lemp,plineno,mhflag)
+FILE *out; /* The output stream */
+struct lemon *lemp; /* The main info structure for this parser */
+int *plineno; /* Pointer to the line number */
+int mhflag; /* True if generating makeheaders output */
+{
+ int lineno = *plineno; /* The line number of the output */
+ char **types; /* A hash table of datatypes */
+ int arraysize; /* Size of the "types" array */
+ int maxdtlength; /* Maximum length of any ".datatype" field. */
+ char *stddt; /* Standardized name for a datatype */
+ int i,j; /* Loop counters */
+ int hash; /* For hashing the name of a type */
+ char *name; /* Name of the parser */
+
+ /* Allocate and initialize types[] and allocate stddt[] */
+ arraysize = lemp->nsymbol * 2;
+ types = (char**)calloc( arraysize, sizeof(char*) );
+ for(i=0; i<arraysize; i++) types[i] = 0;
+ maxdtlength = 0;
+ if( lemp->vartype ){
+ maxdtlength = strlen(lemp->vartype);
+ }
+ for(i=0; i<lemp->nsymbol; i++){
+ int len;
+ struct symbol *sp = lemp->symbols[i];
+ if( sp->datatype==0 ) continue;
+ len = strlen(sp->datatype);
+ if( len>maxdtlength ) maxdtlength = len;
+ }
+ stddt = (char*)malloc( maxdtlength*2 + 1 );
+ if( types==0 || stddt==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+
+ /* Build a hash table of datatypes. The ".dtnum" field of each symbol
+ ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is
+ ** used for terminal symbols. If there is no %default_type defined then
+ ** 0 is also used as the .dtnum value for nonterminals which do not specify
+ ** a datatype using the %type directive.
+ */
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ char *cp;
+ if( sp==lemp->errsym ){
+ sp->dtnum = arraysize+1;
+ continue;
+ }
+ if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){
+ sp->dtnum = 0;
+ continue;
+ }
+ cp = sp->datatype;
+ if( cp==0 ) cp = lemp->vartype;
+ j = 0;
+ while( isspace(*cp) ) cp++;
+ while( *cp ) stddt[j++] = *cp++;
+ while( j>0 && isspace(stddt[j-1]) ) j--;
+ stddt[j] = 0;
+ if( strcmp(stddt, lemp->tokentype)==0 ){
+ sp->dtnum = 0;
+ continue;
+ }
+ hash = 0;
+ for(j=0; stddt[j]; j++){
+ hash = hash*53 + stddt[j];
+ }
+ hash = (hash & 0x7fffffff)%arraysize;
+ while( types[hash] ){
+ if( strcmp(types[hash],stddt)==0 ){
+ sp->dtnum = hash + 1;
+ break;
+ }
+ hash++;
+ if( hash>=arraysize ) hash = 0;
+ }
+ if( types[hash]==0 ){
+ sp->dtnum = hash + 1;
+ types[hash] = (char*)malloc( strlen(stddt)+1 );
+ if( types[hash]==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+ strcpy(types[hash],stddt);
+ }
+ }
+
+ /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */
+ name = lemp->name ? lemp->name : "Parse";
+ lineno = *plineno;
+ if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; }
+ fprintf(out,"#define %sTOKENTYPE %s\n",name,
+ lemp->tokentype?lemp->tokentype:"void*"); lineno++;
+ if( mhflag ){ fprintf(out,"#endif\n"); lineno++; }
+ fprintf(out,"typedef union {\n"); lineno++;
+ fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++;
+ for(i=0; i<arraysize; i++){
+ if( types[i]==0 ) continue;
+ fprintf(out," %s yy%d;\n",types[i],i+1); lineno++;
+ free(types[i]);
+ }
+ if( lemp->errsym->useCnt ){
+ fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++;
+ }
+ free(stddt);
+ free(types);
+ fprintf(out,"} YYMINORTYPE;\n"); lineno++;
+ *plineno = lineno;
+}
+
+/*
+** Return the name of a C datatype able to represent values between
+** lwr and upr, inclusive.
+*/
+static const char *minimum_size_type(int lwr, int upr){
+ if( lwr>=0 ){
+ if( upr<=255 ){
+ return "unsigned char";
+ }else if( upr<65535 ){
+ return "unsigned short int";
+ }else{
+ return "unsigned int";
+ }
+ }else if( lwr>=-127 && upr<=127 ){
+ return "signed char";
+ }else if( lwr>=-32767 && upr<32767 ){
+ return "short";
+ }else{
+ return "int";
+ }
+}
+
+/*
+** Each state contains a set of token transaction and a set of
+** nonterminal transactions. Each of these sets makes an instance
+** of the following structure. An array of these structures is used
+** to order the creation of entries in the yy_action[] table.
+*/
+struct axset {
+ struct state *stp; /* A pointer to a state */
+ int isTkn; /* True to use tokens. False for non-terminals */
+ int nAction; /* Number of actions */
+};
+
+/*
+** Compare to axset structures for sorting purposes
+*/
+static int axset_compare(const void *a, const void *b){
+ struct axset *p1 = (struct axset*)a;
+ struct axset *p2 = (struct axset*)b;
+ return p2->nAction - p1->nAction;
+}
+
+/*
+** Write text on "out" that describes the rule "rp".
+*/
+static void writeRuleText(FILE *out, struct rule *rp){
+ int j;
+ fprintf(out,"%s ::=", rp->lhs->name);
+ for(j=0; j<rp->nrhs; j++){
+ struct symbol *sp = rp->rhs[j];
+ fprintf(out," %s", sp->name);
+ if( sp->type==MULTITERMINAL ){
+ int k;
+ for(k=1; k<sp->nsubsym; k++){
+ fprintf(out,"|%s",sp->subsym[k]->name);
+ }
+ }
+ }
+}
+
+
+/* Generate C source code for the parser */
+void ReportTable(lemp, mhflag)
+struct lemon *lemp;
+int mhflag; /* Output in makeheaders format if true */
+{
+ FILE *out, *in;
+ char line[LINESIZE];
+ int lineno;
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ struct acttab *pActtab;
+ int i, j, n;
+ char *name;
+ int mnTknOfst, mxTknOfst;
+ int mnNtOfst, mxNtOfst;
+ struct axset *ax;
+
+ in = tplt_open(lemp);
+ if( in==0 ) return;
+ out = file_open(lemp,".c","wb");
+ if( out==0 ){
+ fclose(in);
+ return;
+ }
+ lineno = 1;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the include code, if any */
+ tplt_print(out,lemp,lemp->include,&lineno);
+ if( mhflag ){
+ char *name = file_makename(lemp, ".h");
+ fprintf(out,"#include \"%s\"\n", name); lineno++;
+ free(name);
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate #defines for all tokens */
+ if( mhflag ){
+ char *prefix;
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lineno++;
+ }
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the defines */
+ fprintf(out,"#define YYCODETYPE %s\n",
+ minimum_size_type(0, lemp->nsymbol+5)); lineno++;
+ fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
+ fprintf(out,"#define YYACTIONTYPE %s\n",
+ minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++;
+ if( lemp->wildcard ){
+ fprintf(out,"#define YYWILDCARD %d\n",
+ lemp->wildcard->index); lineno++;
+ }
+ print_stack_union(out,lemp,&lineno,mhflag);
+ fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++;
+ if( lemp->stacksize ){
+ fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++;
+ }else{
+ fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++;
+ }
+ fprintf(out, "#endif\n"); lineno++;
+ if( mhflag ){
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ }
+ name = lemp->name ? lemp->name : "Parse";
+ if( lemp->arg && lemp->arg[0] ){
+ int i;
+ i = strlen(lemp->arg);
+ while( i>=1 && isspace(lemp->arg[i-1]) ) i--;
+ while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--;
+ fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n",
+ name,lemp->arg,&lemp->arg[i]); lineno++;
+ fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n",
+ name,&lemp->arg[i],&lemp->arg[i]); lineno++;
+ }else{
+ fprintf(out,"#define %sARG_SDECL\n",name); lineno++;
+ fprintf(out,"#define %sARG_PDECL\n",name); lineno++;
+ fprintf(out,"#define %sARG_FETCH\n",name); lineno++;
+ fprintf(out,"#define %sARG_STORE\n",name); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++;
+ fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
+ if( lemp->errsym->useCnt ){
+ fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
+ fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
+ }
+ if( lemp->has_fallback ){
+ fprintf(out,"#define YYFALLBACK 1\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the action table and its associates:
+ **
+ ** yy_action[] A single table containing all actions.
+ ** yy_lookahead[] A table containing the lookahead for each entry in
+ ** yy_action. Used to detect hash collisions.
+ ** yy_shift_ofst[] For each state, the offset into yy_action for
+ ** shifting terminals.
+ ** yy_reduce_ofst[] For each state, the offset into yy_action for
+ ** shifting non-terminals after a reduce.
+ ** yy_default[] Default action for each state.
+ */
+
+ /* Compute the actions on all states and count them up */
+ ax = calloc(lemp->nstate*2, sizeof(ax[0]));
+ if( ax==0 ){
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ ax[i*2].stp = stp;
+ ax[i*2].isTkn = 1;
+ ax[i*2].nAction = stp->nTknAct;
+ ax[i*2+1].stp = stp;
+ ax[i*2+1].isTkn = 0;
+ ax[i*2+1].nAction = stp->nNtAct;
+ }
+ mxTknOfst = mnTknOfst = 0;
+ mxNtOfst = mnNtOfst = 0;
+
+ /* Compute the action table. In order to try to keep the size of the
+ ** action table to a minimum, the heuristic of placing the largest action
+ ** sets first is used.
+ */
+ qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare);
+ pActtab = acttab_alloc();
+ for(i=0; i<lemp->nstate*2 && ax[i].nAction>0; i++){
+ stp = ax[i].stp;
+ if( ax[i].isTkn ){
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action;
+ if( ap->sp->index>=lemp->nterminal ) continue;
+ action = compute_action(lemp, ap);
+ if( action<0 ) continue;
+ acttab_action(pActtab, ap->sp->index, action);
+ }
+ stp->iTknOfst = acttab_insert(pActtab);
+ if( stp->iTknOfst<mnTknOfst ) mnTknOfst = stp->iTknOfst;
+ if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst;
+ }else{
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action;
+ if( ap->sp->index<lemp->nterminal ) continue;
+ if( ap->sp->index==lemp->nsymbol ) continue;
+ action = compute_action(lemp, ap);
+ if( action<0 ) continue;
+ acttab_action(pActtab, ap->sp->index, action);
+ }
+ stp->iNtOfst = acttab_insert(pActtab);
+ if( stp->iNtOfst<mnNtOfst ) mnNtOfst = stp->iNtOfst;
+ if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst;
+ }
+ }
+ free(ax);
+
+ /* Output the yy_action table */
+ fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++;
+ n = acttab_size(pActtab);
+ for(i=j=0; i<n; i++){
+ int action = acttab_yyaction(pActtab, i);
+ if( action<0 ) action = lemp->nstate + lemp->nrule + 2;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", action);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_lookahead table */
+ fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++;
+ for(i=j=0; i<n; i++){
+ int la = acttab_yylookahead(pActtab, i);
+ if( la<0 ) la = lemp->nsymbol;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", la);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_shift_ofst[] table */
+ fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++;
+ n = lemp->nstate;
+ while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
+ fprintf(out, "#define YY_SHIFT_MAX %d\n", n-1); lineno++;
+ fprintf(out, "static const %s yy_shift_ofst[] = {\n",
+ minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++;
+ for(i=j=0; i<n; i++){
+ int ofst;
+ stp = lemp->sorted[i];
+ ofst = stp->iTknOfst;
+ if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", ofst);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the yy_reduce_ofst[] table */
+ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++;
+ n = lemp->nstate;
+ while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--;
+ fprintf(out, "#define YY_REDUCE_MAX %d\n", n-1); lineno++;
+ fprintf(out, "static const %s yy_reduce_ofst[] = {\n",
+ minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++;
+ for(i=j=0; i<n; i++){
+ int ofst;
+ stp = lemp->sorted[i];
+ ofst = stp->iNtOfst;
+ if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1;
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", ofst);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+
+ /* Output the default action table */
+ fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++;
+ n = lemp->nstate;
+ for(i=j=0; i<n; i++){
+ stp = lemp->sorted[i];
+ if( j==0 ) fprintf(out," /* %5d */ ", i);
+ fprintf(out, " %4d,", stp->iDflt);
+ if( j==9 || i==n-1 ){
+ fprintf(out, "\n"); lineno++;
+ j = 0;
+ }else{
+ j++;
+ }
+ }
+ fprintf(out, "};\n"); lineno++;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of fallback tokens.
+ */
+ if( lemp->has_fallback ){
+ for(i=0; i<lemp->nterminal; i++){
+ struct symbol *p = lemp->symbols[i];
+ if( p->fallback==0 ){
+ fprintf(out, " 0, /* %10s => nothing */\n", p->name);
+ }else{
+ fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index,
+ p->name, p->fallback->name);
+ }
+ lineno++;
+ }
+ }
+ tplt_xfer(lemp->name, in, out, &lineno);
+
+ /* Generate a table containing the symbolic name of every symbol
+ */
+ for(i=0; i<lemp->nsymbol; i++){
+ sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ fprintf(out," %-15s",line);
+ if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
+ }
+ if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate a table containing a text string that describes every
+ ** rule in the rule set of the grammar. This information is used
+ ** when tracing REDUCE actions.
+ */
+ for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){
+ assert( rp->index==i );
+ fprintf(out," /* %3d */ \"", i);
+ writeRuleText(out, rp);
+ fprintf(out,"\",\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes every time a symbol is popped from
+ ** the stack while processing errors or while destroying the parser.
+ ** (In other words, generate the %destructor actions)
+ */
+ if( lemp->tokendest ){
+ int once = 1;
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type!=TERMINAL ) continue;
+ if( once ){
+ fprintf(out, " /* TERMINAL Destructor */\n"); lineno++;
+ once = 0;
+ }
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+ }
+ for(i=0; i<lemp->nsymbol && lemp->symbols[i]->type!=TERMINAL; i++);
+ if( i<lemp->nsymbol ){
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ }
+ if( lemp->vardest ){
+ struct symbol *dflt_sp = 0;
+ int once = 1;
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL ||
+ sp->index<=0 || sp->destructor!=0 ) continue;
+ if( once ){
+ fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++;
+ once = 0;
+ }
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+ dflt_sp = sp;
+ }
+ if( dflt_sp!=0 ){
+ emit_destructor_code(out,dflt_sp,lemp,&lineno);
+ }
+ fprintf(out," break;\n"); lineno++;
+ }
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue;
+ fprintf(out," case %d: /* %s */\n",
+ sp->index, sp->name); lineno++;
+
+ /* Combine duplicate destructors into a single case */
+ for(j=i+1; j<lemp->nsymbol; j++){
+ struct symbol *sp2 = lemp->symbols[j];
+ if( sp2 && sp2->type!=TERMINAL && sp2->destructor
+ && sp2->dtnum==sp->dtnum
+ && strcmp(sp->destructor,sp2->destructor)==0 ){
+ fprintf(out," case %d: /* %s */\n",
+ sp2->index, sp2->name); lineno++;
+ sp2->destructor = 0;
+ }
+ }
+
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes whenever the parser stack overflows */
+ tplt_print(out,lemp,lemp->overflow,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of rule information
+ **
+ ** Note: This code depends on the fact that rules are number
+ ** sequentually beginning with 0.
+ */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which execution during each REDUCE action */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ translate_code(lemp, rp);
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ struct rule *rp2;
+ if( rp->code==0 ) continue;
+ fprintf(out," case %d: /* ", rp->index);
+ writeRuleText(out, rp);
+ fprintf(out, " */\n"); lineno++;
+ for(rp2=rp->next; rp2; rp2=rp2->next){
+ if( rp2->code==rp->code ){
+ fprintf(out," case %d: /* ", rp2->index);
+ writeRuleText(out, rp2);
+ fprintf(out," */\n"); lineno++;
+ rp2->code = 0;
+ }
+ }
+ emit_code(out,rp,lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes if a parse fails */
+ tplt_print(out,lemp,lemp->failure,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when a syntax error occurs */
+ tplt_print(out,lemp,lemp->error,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when the parser accepts its input */
+ tplt_print(out,lemp,lemp->accept,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Append any addition code the user desires */
+ tplt_print(out,lemp,lemp->extracode,&lineno);
+
+ fclose(in);
+ fclose(out);
+ return;
+}
+
+/* Generate a header file for the parser */
+void ReportHeader(lemp)
+struct lemon *lemp;
+{
+ FILE *out, *in;
+ char *prefix;
+ char line[LINESIZE];
+ char pattern[LINESIZE];
+ int i;
+
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ in = file_open(lemp,".h","rb");
+ if( in ){
+ for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){
+ sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ if( strcmp(line,pattern) ) break;
+ }
+ fclose(in);
+ if( i==lemp->nterminal ){
+ /* No change in the file. Don't rewrite it. */
+ return;
+ }
+ }
+ out = file_open(lemp,".h","wb");
+ if( out ){
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ }
+ fclose(out);
+ }
+ return;
+}
+
+/* Reduce the size of the action tables, if possible, by making use
+** of defaults.
+**
+** In this version, we take the most frequent REDUCE action and make
+** it the default. Except, there is no default if the wildcard token
+** is a possible look-ahead.
+*/
+void CompressTables(lemp)
+struct lemon *lemp;
+{
+ struct state *stp;
+ struct action *ap, *ap2;
+ struct rule *rp, *rp2, *rbest;
+ int nbest, n;
+ int i;
+ int usesWildcard;
+
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ nbest = 0;
+ rbest = 0;
+ usesWildcard = 0;
+
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->type==SHIFT && ap->sp==lemp->wildcard ){
+ usesWildcard = 1;
+ }
+ if( ap->type!=REDUCE ) continue;
+ rp = ap->x.rp;
+ if( rp->lhsStart ) continue;
+ if( rp==rbest ) continue;
+ n = 1;
+ for(ap2=ap->next; ap2; ap2=ap2->next){
+ if( ap2->type!=REDUCE ) continue;
+ rp2 = ap2->x.rp;
+ if( rp2==rbest ) continue;
+ if( rp2==rp ) n++;
+ }
+ if( n>nbest ){
+ nbest = n;
+ rbest = rp;
+ }
+ }
+
+ /* Do not make a default if the number of rules to default
+ ** is not at least 1 or if the wildcard token is a possible
+ ** lookahead.
+ */
+ if( nbest<1 || usesWildcard ) continue;
+
+
+ /* Combine matching REDUCE actions into a single default */
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE && ap->x.rp==rbest ) break;
+ }
+ assert( ap );
+ ap->sp = Symbol_new("{default}");
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED;
+ }
+ stp->ap = Action_sort(stp->ap);
+ }
+}
+
+
+/*
+** Compare two states for sorting purposes. The smaller state is the
+** one with the most non-terminal actions. If they have the same number
+** of non-terminal actions, then the smaller is the one with the most
+** token actions.
+*/
+static int stateResortCompare(const void *a, const void *b){
+ const struct state *pA = *(const struct state**)a;
+ const struct state *pB = *(const struct state**)b;
+ int n;
+
+ n = pB->nNtAct - pA->nNtAct;
+ if( n==0 ){
+ n = pB->nTknAct - pA->nTknAct;
+ }
+ return n;
+}
+
+
+/*
+** Renumber and resort states so that states with fewer choices
+** occur at the end. Except, keep state 0 as the first state.
+*/
+void ResortStates(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct action *ap;
+
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ stp->nTknAct = stp->nNtAct = 0;
+ stp->iDflt = lemp->nstate + lemp->nrule;
+ stp->iTknOfst = NO_OFFSET;
+ stp->iNtOfst = NO_OFFSET;
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( compute_action(lemp,ap)>=0 ){
+ if( ap->sp->index<lemp->nterminal ){
+ stp->nTknAct++;
+ }else if( ap->sp->index<lemp->nsymbol ){
+ stp->nNtAct++;
+ }else{
+ stp->iDflt = compute_action(lemp, ap);
+ }
+ }
+ }
+ }
+ qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]),
+ stateResortCompare);
+ for(i=0; i<lemp->nstate; i++){
+ lemp->sorted[i]->statenum = i;
+ }
+}
+
+
+/***************** From the file "set.c" ************************************/
+/*
+** Set manipulation routines for the LEMON parser generator.
+*/
+
+static int size = 0;
+
+/* Set the set size */
+void SetSize(n)
+int n;
+{
+ size = n+1;
+}
+
+/* Allocate a new set */
+char *SetNew(){
+ char *s;
+ s = (char*)calloc( size, 1);
+ if( s==0 ){
+ extern void memory_error();
+ memory_error();
+ }
+ return s;
+}
+
+/* Deallocate a set */
+void SetFree(s)
+char *s;
+{
+ free(s);
+}
+
+/* Add a new element to the set. Return TRUE if the element was added
+** and FALSE if it was already there. */
+int SetAdd(s,e)
+char *s;
+int e;
+{
+ int rv;
+ assert( e>=0 && e<size );
+ rv = s[e];
+ s[e] = 1;
+ return !rv;
+}
+
+/* Add every element of s2 to s1. Return TRUE if s1 changes. */
+int SetUnion(s1,s2)
+char *s1;
+char *s2;
+{
+ int i, progress;
+ progress = 0;
+ for(i=0; i<size; i++){
+ if( s2[i]==0 ) continue;
+ if( s1[i]==0 ){
+ progress = 1;
+ s1[i] = 1;
+ }
+ }
+ return progress;
+}
+/********************** From the file "table.c" ****************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+PRIVATE int strhash(x)
+char *x;
+{
+ int h = 0;
+ while( *x) h = h*13 + *(x++);
+ return h;
+}
+
+/* Works like strdup, sort of. Save a string in malloced memory, but
+** keep strings in a table so that the same string is not in more
+** than one place.
+*/
+char *Strsafe(y)
+char *y;
+{
+ char *z;
+
+ if( y==0 ) return 0;
+ z = Strsafe_find(y);
+ if( z==0 && (z=malloc( strlen(y)+1 ))!=0 ){
+ strcpy(z,y);
+ Strsafe_insert(z);
+ }
+ MemoryCheck(z);
+ return z;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x1".
+*/
+struct s_x1 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x1node *tbl; /* The data stored here */
+ struct s_x1node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x1".
+*/
+typedef struct s_x1node {
+ char *data; /* The data */
+ struct s_x1node *next; /* Next entry with the same hash */
+ struct s_x1node **from; /* Previous link */
+} x1node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x1 *x1a;
+
+/* Allocate a new associative array */
+void Strsafe_init(){
+ if( x1a ) return;
+ x1a = (struct s_x1*)malloc( sizeof(struct s_x1) );
+ if( x1a ){
+ x1a->size = 1024;
+ x1a->count = 0;
+ x1a->tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*1024 );
+ if( x1a->tbl==0 ){
+ free(x1a);
+ x1a = 0;
+ }else{
+ int i;
+ x1a->ht = (x1node**)&(x1a->tbl[1024]);
+ for(i=0; i<1024; i++) x1a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Strsafe_insert(data)
+char *data;
+{
+ x1node *np;
+ int h;
+ int ph;
+
+ if( x1a==0 ) return 0;
+ ph = strhash(data);
+ h = ph & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x1a->count>=x1a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x1 array;
+ array.size = size = x1a->size*2;
+ array.count = x1a->count;
+ array.tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x1node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x1a->count; i++){
+ x1node *oldnp, *newnp;
+ oldnp = &(x1a->tbl[i]);
+ h = strhash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x1a->tbl);
+ *x1a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x1a->size-1);
+ np = &(x1a->tbl[x1a->count++]);
+ np->data = data;
+ if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next);
+ np->next = x1a->ht[h];
+ x1a->ht[h] = np;
+ np->from = &(x1a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+char *Strsafe_find(key)
+char *key;
+{
+ int h;
+ x1node *np;
+
+ if( x1a==0 ) return 0;
+ h = strhash(key) & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return a pointer to the (terminal or nonterminal) symbol "x".
+** Create a new symbol if this is the first time "x" has been seen.
+*/
+struct symbol *Symbol_new(x)
+char *x;
+{
+ struct symbol *sp;
+
+ sp = Symbol_find(x);
+ if( sp==0 ){
+ sp = (struct symbol *)calloc(1, sizeof(struct symbol) );
+ MemoryCheck(sp);
+ sp->name = Strsafe(x);
+ sp->type = isupper(*x) ? TERMINAL : NONTERMINAL;
+ sp->rule = 0;
+ sp->fallback = 0;
+ sp->prec = -1;
+ sp->assoc = UNK;
+ sp->firstset = 0;
+ sp->lambda = LEMON_FALSE;
+ sp->destructor = 0;
+ sp->destLineno = 0;
+ sp->datatype = 0;
+ sp->useCnt = 0;
+ Symbol_insert(sp,sp->name);
+ }
+ sp->useCnt++;
+ return sp;
+}
+
+/* Compare two symbols for working purposes
+**
+** Symbols that begin with upper case letters (terminals or tokens)
+** must sort before symbols that begin with lower case letters
+** (non-terminals). Other than that, the order does not matter.
+**
+** We find experimentally that leaving the symbols in their original
+** order (the order they appeared in the grammar file) gives the
+** smallest parser tables in SQLite.
+*/
+int Symbolcmpp(struct symbol **a, struct symbol **b){
+ int i1 = (**a).index + 10000000*((**a).name[0]>'Z');
+ int i2 = (**b).index + 10000000*((**b).name[0]>'Z');
+ return i1-i2;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x2".
+*/
+struct s_x2 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x2node *tbl; /* The data stored here */
+ struct s_x2node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x2".
+*/
+typedef struct s_x2node {
+ struct symbol *data; /* The data */
+ char *key; /* The key */
+ struct s_x2node *next; /* Next entry with the same hash */
+ struct s_x2node **from; /* Previous link */
+} x2node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x2 *x2a;
+
+/* Allocate a new associative array */
+void Symbol_init(){
+ if( x2a ) return;
+ x2a = (struct s_x2*)malloc( sizeof(struct s_x2) );
+ if( x2a ){
+ x2a->size = 128;
+ x2a->count = 0;
+ x2a->tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*128 );
+ if( x2a->tbl==0 ){
+ free(x2a);
+ x2a = 0;
+ }else{
+ int i;
+ x2a->ht = (x2node**)&(x2a->tbl[128]);
+ for(i=0; i<128; i++) x2a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Symbol_insert(data,key)
+struct symbol *data;
+char *key;
+{
+ x2node *np;
+ int h;
+ int ph;
+
+ if( x2a==0 ) return 0;
+ ph = strhash(key);
+ h = ph & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x2a->count>=x2a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x2 array;
+ array.size = size = x2a->size*2;
+ array.count = x2a->count;
+ array.tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x2node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x2a->count; i++){
+ x2node *oldnp, *newnp;
+ oldnp = &(x2a->tbl[i]);
+ h = strhash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x2a->tbl);
+ *x2a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x2a->size-1);
+ np = &(x2a->tbl[x2a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next);
+ np->next = x2a->ht[h];
+ x2a->ht[h] = np;
+ np->from = &(x2a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct symbol *Symbol_find(key)
+char *key;
+{
+ int h;
+ x2node *np;
+
+ if( x2a==0 ) return 0;
+ h = strhash(key) & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return the n-th data. Return NULL if n is out of range. */
+struct symbol *Symbol_Nth(n)
+int n;
+{
+ struct symbol *data;
+ if( x2a && n>0 && n<=x2a->count ){
+ data = x2a->tbl[n-1].data;
+ }else{
+ data = 0;
+ }
+ return data;
+}
+
+/* Return the size of the array */
+int Symbol_count()
+{
+ return x2a ? x2a->count : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct symbol **Symbol_arrayof()
+{
+ struct symbol **array;
+ int i,size;
+ if( x2a==0 ) return 0;
+ size = x2a->count;
+ array = (struct symbol **)calloc(size, sizeof(struct symbol *));
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x2a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Compare two configurations */
+int Configcmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int x;
+ x = a->rp->index - b->rp->index;
+ if( x==0 ) x = a->dot - b->dot;
+ return x;
+}
+
+/* Compare two states */
+PRIVATE int statecmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int rc;
+ for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){
+ rc = a->rp->index - b->rp->index;
+ if( rc==0 ) rc = a->dot - b->dot;
+ }
+ if( rc==0 ){
+ if( a ) rc = 1;
+ if( b ) rc = -1;
+ }
+ return rc;
+}
+
+/* Hash a state */
+PRIVATE int statehash(a)
+struct config *a;
+{
+ int h=0;
+ while( a ){
+ h = h*571 + a->rp->index*37 + a->dot;
+ a = a->bp;
+ }
+ return h;
+}
+
+/* Allocate a new state structure */
+struct state *State_new()
+{
+ struct state *new;
+ new = (struct state *)calloc(1, sizeof(struct state) );
+ MemoryCheck(new);
+ return new;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x3".
+*/
+struct s_x3 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x3node *tbl; /* The data stored here */
+ struct s_x3node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x3".
+*/
+typedef struct s_x3node {
+ struct state *data; /* The data */
+ struct config *key; /* The key */
+ struct s_x3node *next; /* Next entry with the same hash */
+ struct s_x3node **from; /* Previous link */
+} x3node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x3 *x3a;
+
+/* Allocate a new associative array */
+void State_init(){
+ if( x3a ) return;
+ x3a = (struct s_x3*)malloc( sizeof(struct s_x3) );
+ if( x3a ){
+ x3a->size = 128;
+ x3a->count = 0;
+ x3a->tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*128 );
+ if( x3a->tbl==0 ){
+ free(x3a);
+ x3a = 0;
+ }else{
+ int i;
+ x3a->ht = (x3node**)&(x3a->tbl[128]);
+ for(i=0; i<128; i++) x3a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int State_insert(data,key)
+struct state *data;
+struct config *key;
+{
+ x3node *np;
+ int h;
+ int ph;
+
+ if( x3a==0 ) return 0;
+ ph = statehash(key);
+ h = ph & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x3a->count>=x3a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x3 array;
+ array.size = size = x3a->size*2;
+ array.count = x3a->count;
+ array.tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x3node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x3a->count; i++){
+ x3node *oldnp, *newnp;
+ oldnp = &(x3a->tbl[i]);
+ h = statehash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x3a->tbl);
+ *x3a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x3a->size-1);
+ np = &(x3a->tbl[x3a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next);
+ np->next = x3a->ht[h];
+ x3a->ht[h] = np;
+ np->from = &(x3a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct state *State_find(key)
+struct config *key;
+{
+ int h;
+ x3node *np;
+
+ if( x3a==0 ) return 0;
+ h = statehash(key) & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct state **State_arrayof()
+{
+ struct state **array;
+ int i,size;
+ if( x3a==0 ) return 0;
+ size = x3a->count;
+ array = (struct state **)malloc( sizeof(struct state *)*size );
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x3a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Hash a configuration */
+PRIVATE int confighash(a)
+struct config *a;
+{
+ int h=0;
+ h = h*571 + a->rp->index*37 + a->dot;
+ return h;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x4".
+*/
+struct s_x4 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x4node *tbl; /* The data stored here */
+ struct s_x4node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x4".
+*/
+typedef struct s_x4node {
+ struct config *data; /* The data */
+ struct s_x4node *next; /* Next entry with the same hash */
+ struct s_x4node **from; /* Previous link */
+} x4node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x4 *x4a;
+
+/* Allocate a new associative array */
+void Configtable_init(){
+ if( x4a ) return;
+ x4a = (struct s_x4*)malloc( sizeof(struct s_x4) );
+ if( x4a ){
+ x4a->size = 64;
+ x4a->count = 0;
+ x4a->tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*64 );
+ if( x4a->tbl==0 ){
+ free(x4a);
+ x4a = 0;
+ }else{
+ int i;
+ x4a->ht = (x4node**)&(x4a->tbl[64]);
+ for(i=0; i<64; i++) x4a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Configtable_insert(data)
+struct config *data;
+{
+ x4node *np;
+ int h;
+ int ph;
+
+ if( x4a==0 ) return 0;
+ ph = confighash(data);
+ h = ph & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x4a->count>=x4a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x4 array;
+ array.size = size = x4a->size*2;
+ array.count = x4a->count;
+ array.tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x4node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x4a->count; i++){
+ x4node *oldnp, *newnp;
+ oldnp = &(x4a->tbl[i]);
+ h = confighash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x4a->tbl);
+ *x4a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x4a->size-1);
+ np = &(x4a->tbl[x4a->count++]);
+ np->data = data;
+ if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next);
+ np->next = x4a->ht[h];
+ x4a->ht[h] = np;
+ np->from = &(x4a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct config *Configtable_find(key)
+struct config *key;
+{
+ int h;
+ x4node *np;
+
+ if( x4a==0 ) return 0;
+ h = confighash(key) & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Remove all data from the table. Pass each data to the function "f"
+** as it is removed. ("f" may be null to avoid this step.) */
+void Configtable_clear(f)
+int(*f)(/* struct config * */);
+{
+ int i;
+ if( x4a==0 || x4a->count==0 ) return;
+ if( f ) for(i=0; i<x4a->count; i++) (*f)(x4a->tbl[i].data);
+ for(i=0; i<x4a->size; i++) x4a->ht[i] = 0;
+ x4a->count = 0;
+ return;
+}
diff --git a/third_party/sqlite/tool/lempar.c b/third_party/sqlite/tool/lempar.c
new file mode 100755
index 0000000..f5fafd4
--- /dev/null
+++ b/third_party/sqlite/tool/lempar.c
@@ -0,0 +1,813 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
+#include <stdio.h>
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYFALLBACK If defined, this indicates that one or more tokens
+** have fall-back values which should be used if the
+** original value of the token will not parse.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** ParseTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is ParseTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack. If
+** zero the stack is dynamically sized using realloc()
+** ParseARG_SDECL A static variable declaration for the %extra_argument
+** ParseARG_PDECL A parameter declaration for the %extra_argument
+** ParseARG_STORE Code to store %extra_argument into yypParser
+** ParseARG_FETCH Code to extract %extra_argument from yypParser
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+
+/* The yyzerominor constant is used to initialize instances of
+** YYMINORTYPE objects to zero. */
+static const YYMINORTYPE yyzerominor;
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+**
+** N == YYNSTATE+YYNRULE A syntax error has occurred.
+**
+** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+**
+** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+** yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** yy_action[] A single table containing all actions.
+** yy_lookahead[] A table containing the lookahead for each entry in
+** yy_action. Used to detect hash collisions.
+** yy_shift_ofst[] For each state, the offset into yy_action for
+** shifting terminals.
+** yy_reduce_ofst[] For each state, the offset into yy_action for
+** shifting non-terminals after a reduce.
+** yy_default[] Default action for each state.
+*/
+%%
+#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
+
+/* The next table maps tokens into fallback tokens. If a construct
+** like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+%%
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ YYACTIONTYPE stateno; /* The state-number */
+ YYCODETYPE major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int yyidx; /* Index of top element in stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+ int yyidxMax; /* Maximum value of yyidx */
+#endif
+ int yyerrcnt; /* Shifts left before out of the error */
+ ParseARG_SDECL /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+ int yystksz; /* Current side of the stack */
+ yyStackEntry *yystack; /* The parser's stack */
+#else
+ yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const yyTokenName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void yyGrowStack(yyParser *p){
+ int newSize;
+ yyStackEntry *pNew;
+
+ newSize = p->yystksz*2 + 100;
+ pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew ){
+ p->yystack = pNew;
+ p->yystksz = newSize;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
+ yyTracePrompt, p->yystksz);
+ }
+#endif
+ }
+}
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+void *ParseAlloc(void *(*mallocProc)(size_t)){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ if( pParser ){
+ pParser->yyidx = -1;
+#ifdef YYTRACKMAXSTACKDEPTH
+ pParser->yyidxMax = 0;
+#endif
+#if YYSTACKDEPTH<=0
+ yyGrowStack(pParser);
+#endif
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(
+ yyParser *yypParser, /* The parser */
+ YYCODETYPE yymajor, /* Type code for object to destroy */
+ YYMINORTYPE *yypminor /* The object to be destroyed */
+){
+ ParseARG_FETCH;
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+%%
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+ if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->yyidx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ yymajor = yytos->major;
+ yy_destructor(pParser, yymajor, &yytos->minor);
+ pParser->yyidx--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from ParseAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void ParseFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+ free(pParser->yystack);
+#endif
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int ParseStackPeak(void *p){
+ yyParser *pParser = (yyParser*)p;
+ return pParser->yyidxMax;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+ yyParser *pParser, /* The parser */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+ if( iLookAhead>0 ){
+#ifdef YYFALLBACK
+ int iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
+#endif
+ return yy_find_shift_action(pParser, iFallback);
+ }
+#endif
+#ifdef YYWILDCARD
+ {
+ int j = i - iLookAhead + YYWILDCARD;
+ if( j>=0 && j<YY_SZ_ACTTAB && yy_lookahead[j]==YYWILDCARD ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+ }
+#endif /* NDEBUG */
+ return yy_action[j];
+ }
+ }
+#endif /* YYWILDCARD */
+ }
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+ int stateno, /* Current state number */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+#ifdef YYERRORSYMBOL
+ if( stateno>YY_REDUCE_MAX ){
+ return yy_default[stateno];
+ }
+#else
+ assert( stateno<=YY_REDUCE_MAX );
+#endif
+ i = yy_reduce_ofst[stateno];
+ assert( i!=YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+#ifdef YYERRORSYMBOL
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+ return yy_default[stateno];
+ }
+#else
+ assert( i>=0 && i<YY_SZ_ACTTAB );
+ assert( yy_lookahead[i]==iLookAhead );
+#endif
+ return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+ ParseARG_FETCH;
+ yypParser->yyidx--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */
+){
+ yyStackEntry *yytos;
+ yypParser->yyidx++;
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( yypParser->yyidx>yypParser->yyidxMax ){
+ yypParser->yyidxMax = yypParser->yyidx;
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yyidx>=YYSTACKDEPTH ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+#else
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyGrowStack(yypParser);
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+ }
+#endif
+ yytos = &yypParser->yystack[yypParser->yyidx];
+ yytos->stateno = yyNewState;
+ yytos->major = yyMajor;
+ yytos->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->yyidx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ ParseARG_FETCH;
+ yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+ if( yyTraceFILE && yyruleno>=0
+ && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+ yyRuleName[yyruleno]);
+ }
+#endif /* NDEBUG */
+
+ /* Silence complaints from purify about yygotominor being uninitialized
+ ** in some cases when it is copied into the stack after the following
+ ** switch. yygotominor is uninitialized when a rule reduces that does
+ ** not set the value of its left-hand side nonterminal. Leaving the
+ ** value of the nonterminal uninitialized is utterly harmless as long
+ ** as the value is never used. So really the only thing this code
+ ** accomplishes is to quieten purify.
+ **
+ ** 2007-01-16: The wireshark project (www.wireshark.org) reports that
+ ** without this code, their parser segfaults. I'm not sure what there
+ ** parser is doing to make this happen. This is the second bug report
+ ** from wireshark this week. Clearly they are stressing Lemon in ways
+ ** that it has not been previously stressed... (SQLite ticket #2172)
+ */
+ /*memset(&yygotominor, 0, sizeof(yygotominor));*/
+ yygotominor = yyzerominor;
+
+
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+%%
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->yyidx -= yysize;
+ yyact = yy_find_reduce_action(yymsp[-yysize].stateno,yygoto);
+ if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+ /* If we are not debugging and the reduce action popped at least
+ ** one element off the stack, then we can push the new element back
+ ** onto the stack here, and skip the stack overflow test in yy_shift().
+ ** That gives a significant speed improvement. */
+ if( yysize ){
+ yypParser->yyidx++;
+ yymsp -= yysize-1;
+ yymsp->stateno = yyact;
+ yymsp->major = yygoto;
+ yymsp->minor = yygotominor;
+ }else
+#endif
+ {
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }
+ }else{
+ assert( yyact == YYNSTATE + YYNRULE + 1 );
+ yy_accept(yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+){
+ ParseARG_FETCH;
+#define TOKEN (yyminor.yy0)
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "ParseAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void Parse(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ ParseTOKENTYPE yyminor /* The value for the token */
+ ParseARG_PDECL /* Optional %extra_argument parameter */
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+#endif
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->yyidx<0 ){
+#if YYSTACKDEPTH<=0
+ if( yypParser->yystksz <=0 ){
+ /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
+ yyminorunion = yyzerominor;
+ yyStackOverflow(yypParser, &yyminorunion);
+ return;
+ }
+#endif
+ yypParser->yyidx = 0;
+ yypParser->yyerrcnt = -1;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+ ParseARG_STORE;
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_shift_action(yypParser,yymajor);
+ if( yyact<YYNSTATE ){
+ assert( !yyendofinput ); /* Impossible to shift the $ token */
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->yyerrcnt--;
+ yymajor = YYNOCODE;
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE);
+ }else{
+ assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
+ int yymx;
+#endif
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->yyerrcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yymx = yypParser->yystack[yypParser->yyidx].major;
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yypParser, yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->yyidx >= 0 &&
+ yymx != YYERRORSYMBOL &&
+ (yyact = yy_find_reduce_action(
+ yypParser->yystack[yypParser->yyidx].stateno,
+ YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->yyidx < 0 || yymajor==0 ){
+ yy_destructor(yypParser,yymajor,&yyminorunion);
+ yy_parse_failed(yypParser);
+ yymajor = YYNOCODE;
+ }else if( yymx!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->yyerrcnt = 3;
+ yyerrorhit = 1;
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->yyerrcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yypParser->yyerrcnt = 3;
+ yy_destructor(yypParser,yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }
+ }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ return;
+}
diff --git a/third_party/sqlite/tool/memleak.awk b/third_party/sqlite/tool/memleak.awk
new file mode 100755
index 0000000..928d3b6
--- /dev/null
+++ b/third_party/sqlite/tool/memleak.awk
@@ -0,0 +1,29 @@
+#
+# This script looks for memory leaks by analyzing the output of "sqlite"
+# when compiled with the SQLITE_DEBUG=2 option.
+#
+/[0-9]+ malloc / {
+ mem[$6] = $0
+}
+/[0-9]+ realloc / {
+ mem[$8] = "";
+ mem[$10] = $0
+}
+/[0-9]+ free / {
+ if (mem[$6]=="") {
+ print "*** free without a malloc at",$6
+ }
+ mem[$6] = "";
+ str[$6] = ""
+}
+/^string at / {
+ addr = $4
+ sub("string at " addr " is ","")
+ str[addr] = $0
+}
+END {
+ for(addr in mem){
+ if( mem[addr]=="" ) continue
+ print mem[addr], str[addr]
+ }
+}
diff --git a/third_party/sqlite/tool/memleak2.awk b/third_party/sqlite/tool/memleak2.awk
new file mode 100755
index 0000000..5d81b70
--- /dev/null
+++ b/third_party/sqlite/tool/memleak2.awk
@@ -0,0 +1,29 @@
+# This AWK script reads the output of testfixture when compiled for memory
+# debugging. It generates SQL commands that can be fed into an sqlite
+# instance to determine what memory is never freed. A typical usage would
+# be as follows:
+#
+# make -f memleak.mk fulltest 2>mem.out
+# awk -f ../sqlite/tool/memleak2.awk mem.out | ./sqlite :memory:
+#
+# The job performed by this script is the same as that done by memleak.awk.
+# The difference is that this script uses much less memory when the size
+# of the mem.out file is huge.
+#
+BEGIN {
+ print "CREATE TABLE mem(loc INTEGER PRIMARY KEY, src);"
+}
+/[0-9]+ malloc / {
+ print "INSERT INTO mem VALUES(" strtonum($6) ",'" $0 "');"
+}
+/[0-9]+ realloc / {
+ print "INSERT INTO mem VALUES(" strtonum($10) \
+ ",(SELECT src FROM mem WHERE loc=" strtonum($8) "));"
+ print "DELETE FROM mem WHERE loc=" strtonum($8) ";"
+}
+/[0-9]+ free / {
+ print "DELETE FROM mem WHERE loc=" strtonum($6) ";"
+}
+END {
+ print "SELECT src FROM mem;"
+}
diff --git a/third_party/sqlite/tool/memleak3.tcl b/third_party/sqlite/tool/memleak3.tcl
new file mode 100755
index 0000000..3c6e9b9
--- /dev/null
+++ b/third_party/sqlite/tool/memleak3.tcl
@@ -0,0 +1,233 @@
+#/bin/sh
+# \
+exec `which tclsh` $0 "$@"
+#
+# 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.
+######################################################################
+
+set doco "
+This script is a tool to help track down memory leaks in the sqlite
+library. The library must be compiled with the preprocessor symbol
+SQLITE_MEMDEBUG set to at least 2. It must be set to 3 to enable stack
+traces.
+
+To use, run the leaky application and save the standard error output.
+Then, execute this program with the first argument the name of the
+application binary (or interpreter) and the second argument the name of the
+text file that contains the collected stderr output.
+
+If all goes well a summary of unfreed allocations is printed out. If the
+GNU C library is in use and SQLITE_DEBUG is 3 or greater a stack trace is
+printed out for each unmatched allocation.
+
+If the \"-r <n>\" option is passed, then the program stops and prints out
+the state of the heap immediately after the <n>th call to malloc() or
+realloc().
+
+Example:
+
+$ ./testfixture ../sqlite/test/select1.test 2> memtrace.out
+$ tclsh $argv0 ?-r <malloc-number>? ./testfixture memtrace.out
+"
+
+
+proc usage {} {
+ set prg [file tail $::argv0]
+ puts "Usage: $prg ?-r <malloc-number>? <binary file> <mem trace file>"
+ puts ""
+ puts [string trim $::doco]
+ exit -1
+}
+
+proc shift {listvar} {
+ upvar $listvar l
+ set ret [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $ret
+}
+
+# Argument handling. The following vars are set:
+#
+# $exe - the name of the executable (i.e. "testfixture" or "./sqlite3")
+# $memfile - the name of the file containing the trace output.
+# $report_at - The malloc number to stop and report at. Or -1 to read
+# all of $memfile.
+#
+set report_at -1
+while {[llength $argv]>2} {
+ set arg [shift argv]
+ switch -- $arg {
+ "-r" {
+ set report_at [shift argv]
+ }
+ default {
+ usage
+ }
+ }
+}
+if {[llength $argv]!=2} usage
+set exe [lindex $argv 0]
+set memfile [lindex $argv 1]
+
+# If stack traces are enabled, the 'addr2line' program is called to
+# translate a binary stack address into a human-readable form.
+set addr2line addr2line
+
+# When the SQLITE_MEMDEBUG is set as described above, SQLite prints
+# out a line for each malloc(), realloc() or free() call that the
+# library makes. If SQLITE_MEMDEBUG is 3, then a stack trace is printed
+# out before each malloc() and realloc() line.
+#
+# This program parses each line the SQLite library outputs and updates
+# the following global Tcl variables to reflect the "current" state of
+# the heap used by SQLite.
+#
+set nBytes 0 ;# Total number of bytes currently allocated.
+set nMalloc 0 ;# Total number of malloc()/realloc() calls.
+set nPeak 0 ;# Peak of nBytes.
+set iPeak 0 ;# nMalloc when nPeak was set.
+#
+# More detailed state information is stored in the $memmap array.
+# Each key in the memmap array is the address of a chunk of memory
+# currently allocated from the heap. The value is a list of the
+# following form
+#
+# {<number-of-bytes> <malloc id> <stack trace>}
+#
+array unset memmap
+
+proc process_input {input_file array_name} {
+ upvar $array_name mem
+ set input [open $input_file]
+
+ set MALLOC {([[:digit:]]+) malloc ([[:digit:]]+) bytes at 0x([[:xdigit:]]+)}
+ # set STACK {^[[:digit:]]+: STACK: (.*)$}
+ set STACK {^STACK: (.*)$}
+ set FREE {[[:digit:]]+ free ([[:digit:]]+) bytes at 0x([[:xdigit:]]+)}
+ set REALLOC {([[:digit:]]+) realloc ([[:digit:]]+) to ([[:digit:]]+)}
+ append REALLOC { bytes at 0x([[:xdigit:]]+) to 0x([[:xdigit:]]+)}
+
+ set stack ""
+ while { ![eof $input] } {
+ set line [gets $input]
+ if {[regexp $STACK $line dummy stack]} {
+ # Do nothing. The variable $stack now stores the hexadecimal stack dump
+ # for the next malloc() or realloc().
+
+ } elseif { [regexp $MALLOC $line dummy mallocid bytes addr] } {
+ # If this is a 'malloc' line, set an entry in the mem array. Each entry
+ # is a list of length three, the number of bytes allocated , the malloc
+ # number and the stack dump when it was allocated.
+ set mem($addr) [list $bytes "malloc $mallocid" $stack]
+ set stack ""
+
+ # Increase the current heap usage
+ incr ::nBytes $bytes
+
+ # Increase the number of malloc() calls
+ incr ::nMalloc
+
+ if {$::nBytes > $::nPeak} {
+ set ::nPeak $::nBytes
+ set ::iPeak $::nMalloc
+ }
+
+ } elseif { [regexp $FREE $line dummy bytes addr] } {
+ # If this is a 'free' line, remove the entry from the mem array. If the
+ # entry does not exist, or is the wrong number of bytes, announce a
+ # problem. This is more likely a bug in the regular expressions for
+ # this script than an SQLite defect.
+ if { [lindex $mem($addr) 0] != $bytes } {
+ error "byte count mismatch"
+ }
+ unset mem($addr)
+
+ # Decrease the current heap usage
+ incr ::nBytes [expr -1 * $bytes]
+
+ } elseif { [regexp $REALLOC $line dummy mallocid ob b oa a] } {
+ # "free" the old allocation in the internal model:
+ incr ::nBytes [expr -1 * $ob]
+ unset mem($oa);
+
+ # "malloc" the new allocation
+ set mem($a) [list $b "realloc $mallocid" $stack]
+ incr ::nBytes $b
+ set stack ""
+
+ # Increase the number of malloc() calls
+ incr ::nMalloc
+
+ if {$::nBytes > $::nPeak} {
+ set ::nPeak $::nBytes
+ set ::iPeak $::nMalloc
+ }
+
+ } else {
+ # puts "REJECT: $line"
+ }
+
+ if {$::nMalloc==$::report_at} report
+ }
+
+ close $input
+}
+
+proc printstack {stack} {
+ set fcount 10
+ if {[llength $stack]<10} {
+ set fcount [llength $stack]
+ }
+ foreach frame [lrange $stack 1 $fcount] {
+ foreach {f l} [split [exec $::addr2line -f --exe=$::exe $frame] \n] {}
+ puts [format "%-30s %s" $f $l]
+ }
+ if {[llength $stack]>0 } {puts ""}
+}
+
+proc report {} {
+
+ foreach key [array names ::memmap] {
+ set stack [lindex $::memmap($key) 2]
+ set bytes [lindex $::memmap($key) 0]
+ lappend summarymap($stack) $bytes
+ }
+
+ set sorted [list]
+ foreach stack [array names summarymap] {
+ set allocs $summarymap($stack)
+ set sum 0
+ foreach a $allocs {
+ incr sum $a
+ }
+ lappend sorted [list $sum $stack]
+ }
+
+ set sorted [lsort -integer -index 0 $sorted]
+ foreach s $sorted {
+ set sum [lindex $s 0]
+ set stack [lindex $s 1]
+ set allocs $summarymap($stack)
+ puts "$sum bytes in [llength $allocs] chunks ($allocs)"
+ printstack $stack
+ }
+
+ # Print out summary statistics
+ puts "Total allocations : $::nMalloc"
+ puts "Total outstanding allocations: [array size ::memmap]"
+ puts "Current heap usage : $::nBytes bytes"
+ puts "Peak heap usage : $::nPeak bytes (malloc #$::iPeak)"
+
+ exit
+}
+
+process_input $memfile memmap
+report
+
+
+
diff --git a/third_party/sqlite/tool/mkkeywordhash.c b/third_party/sqlite/tool/mkkeywordhash.c
new file mode 100755
index 0000000..3a34224
--- /dev/null
+++ b/third_party/sqlite/tool/mkkeywordhash.c
@@ -0,0 +1,559 @@
+/*
+** Compile and run this standalone program in order to generate code that
+** implements a function that will translate alphabetic identifiers into
+** parser token codes.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+** A header comment placed at the beginning of generated code.
+*/
+static const char zHdr[] =
+ "/***** This file contains automatically generated code ******\n"
+ "**\n"
+ "** The code in this file has been automatically generated by\n"
+ "**\n"
+ "** $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.31 2007/07/30 18:26:20 rse Exp $\n"
+ "**\n"
+ "** The code in this file implements a function that determines whether\n"
+ "** or not a given identifier is really an SQL keyword. The same thing\n"
+ "** might be implemented more directly using a hand-written hash table.\n"
+ "** But by using this automatically generated code, the size of the code\n"
+ "** is substantially reduced. This is important for embedded applications\n"
+ "** on platforms with limited memory.\n"
+ "*/\n"
+;
+
+/*
+** All the keywords of the SQL language are stored as in a hash
+** table composed of instances of the following structure.
+*/
+typedef struct Keyword Keyword;
+struct Keyword {
+ char *zName; /* The keyword name */
+ char *zTokenType; /* Token value for this keyword */
+ int mask; /* Code this keyword if non-zero */
+ int id; /* Unique ID for this record */
+ int hash; /* Hash on the keyword */
+ int offset; /* Offset to start of name string */
+ int len; /* Length of this keyword, not counting final \000 */
+ int prefix; /* Number of characters in prefix */
+ int longestSuffix; /* Longest suffix that is a prefix on another word */
+ int iNext; /* Index in aKeywordTable[] of next with same hash */
+ int substrId; /* Id to another keyword this keyword is embedded in */
+ int substrOffset; /* Offset into substrId for start of this keyword */
+};
+
+/*
+** Define masks used to determine which keywords are allowed
+*/
+#ifdef SQLITE_OMIT_ALTERTABLE
+# define ALTER 0
+#else
+# define ALTER 0x00000001
+#endif
+#define ALWAYS 0x00000002
+#ifdef SQLITE_OMIT_ANALYZE
+# define ANALYZE 0
+#else
+# define ANALYZE 0x00000004
+#endif
+#ifdef SQLITE_OMIT_ATTACH
+# define ATTACH 0
+#else
+# define ATTACH 0x00000008
+#endif
+#ifdef SQLITE_OMIT_AUTOINCREMENT
+# define AUTOINCR 0
+#else
+# define AUTOINCR 0x00000010
+#endif
+#ifdef SQLITE_OMIT_CAST
+# define CAST 0
+#else
+# define CAST 0x00000020
+#endif
+#ifdef SQLITE_OMIT_COMPOUND_SELECT
+# define COMPOUND 0
+#else
+# define COMPOUND 0x00000040
+#endif
+#ifdef SQLITE_OMIT_CONFLICT_CLAUSE
+# define CONFLICT 0
+#else
+# define CONFLICT 0x00000080
+#endif
+#ifdef SQLITE_OMIT_EXPLAIN
+# define EXPLAIN 0
+#else
+# define EXPLAIN 0x00000100
+#endif
+#ifdef SQLITE_OMIT_FOREIGN_KEY
+# define FKEY 0
+#else
+# define FKEY 0x00000200
+#endif
+#ifdef SQLITE_OMIT_PRAGMA
+# define PRAGMA 0
+#else
+# define PRAGMA 0x00000400
+#endif
+#ifdef SQLITE_OMIT_REINDEX
+# define REINDEX 0
+#else
+# define REINDEX 0x00000800
+#endif
+#ifdef SQLITE_OMIT_SUBQUERY
+# define SUBQUERY 0
+#else
+# define SUBQUERY 0x00001000
+#endif
+#ifdef SQLITE_OMIT_TRIGGER
+# define TRIGGER 0
+#else
+# define TRIGGER 0x00002000
+#endif
+#if defined(SQLITE_OMIT_AUTOVACUUM) && \
+ (defined(SQLITE_OMIT_VACUUM) || defined(SQLITE_OMIT_ATTACH))
+# define VACUUM 0
+#else
+# define VACUUM 0x00004000
+#endif
+#ifdef SQLITE_OMIT_VIEW
+# define VIEW 0
+#else
+# define VIEW 0x00008000
+#endif
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+# define VTAB 0
+#else
+# define VTAB 0x00010000
+#endif
+#ifdef SQLITE_OMIT_AUTOVACUUM
+# define AUTOVACUUM 0
+#else
+# define AUTOVACUUM 0x00020000
+#endif
+
+/*
+** These are the keywords
+*/
+static Keyword aKeywordTable[] = {
+ { "ABORT", "TK_ABORT", CONFLICT|TRIGGER },
+ { "ADD", "TK_ADD", ALTER },
+ { "AFTER", "TK_AFTER", TRIGGER },
+ { "ALL", "TK_ALL", ALWAYS },
+ { "ALTER", "TK_ALTER", ALTER },
+ { "ANALYZE", "TK_ANALYZE", ANALYZE },
+ { "AND", "TK_AND", ALWAYS },
+ { "AS", "TK_AS", ALWAYS },
+ { "ASC", "TK_ASC", ALWAYS },
+ { "ATTACH", "TK_ATTACH", ATTACH },
+ { "AUTOINCREMENT", "TK_AUTOINCR", AUTOINCR },
+ { "BEFORE", "TK_BEFORE", TRIGGER },
+ { "BEGIN", "TK_BEGIN", ALWAYS },
+ { "BETWEEN", "TK_BETWEEN", ALWAYS },
+ { "BY", "TK_BY", ALWAYS },
+ { "CASCADE", "TK_CASCADE", FKEY },
+ { "CASE", "TK_CASE", ALWAYS },
+ { "CAST", "TK_CAST", CAST },
+ { "CHECK", "TK_CHECK", ALWAYS },
+ { "COLLATE", "TK_COLLATE", ALWAYS },
+ { "COLUMN", "TK_COLUMNKW", ALTER },
+ { "COMMIT", "TK_COMMIT", ALWAYS },
+ { "CONFLICT", "TK_CONFLICT", CONFLICT },
+ { "CONSTRAINT", "TK_CONSTRAINT", ALWAYS },
+ { "CREATE", "TK_CREATE", ALWAYS },
+ { "CROSS", "TK_JOIN_KW", ALWAYS },
+ { "CURRENT_DATE", "TK_CTIME_KW", ALWAYS },
+ { "CURRENT_TIME", "TK_CTIME_KW", ALWAYS },
+ { "CURRENT_TIMESTAMP","TK_CTIME_KW", ALWAYS },
+ { "DATABASE", "TK_DATABASE", ATTACH },
+ { "DEFAULT", "TK_DEFAULT", ALWAYS },
+ { "DEFERRED", "TK_DEFERRED", ALWAYS },
+ { "DEFERRABLE", "TK_DEFERRABLE", FKEY },
+ { "DELETE", "TK_DELETE", ALWAYS },
+ { "DESC", "TK_DESC", ALWAYS },
+ { "DETACH", "TK_DETACH", ATTACH },
+ { "DISTINCT", "TK_DISTINCT", ALWAYS },
+ { "DROP", "TK_DROP", ALWAYS },
+ { "END", "TK_END", ALWAYS },
+ { "EACH", "TK_EACH", TRIGGER },
+ { "ELSE", "TK_ELSE", ALWAYS },
+ { "ESCAPE", "TK_ESCAPE", ALWAYS },
+ { "EXCEPT", "TK_EXCEPT", COMPOUND },
+ { "EXCLUSIVE", "TK_EXCLUSIVE", ALWAYS },
+ { "EXISTS", "TK_EXISTS", ALWAYS },
+ { "EXPLAIN", "TK_EXPLAIN", EXPLAIN },
+ { "FAIL", "TK_FAIL", CONFLICT|TRIGGER },
+ { "FOR", "TK_FOR", TRIGGER },
+ { "FOREIGN", "TK_FOREIGN", FKEY },
+ { "FROM", "TK_FROM", ALWAYS },
+ { "FULL", "TK_JOIN_KW", ALWAYS },
+ { "GLOB", "TK_LIKE_KW", ALWAYS },
+ { "GROUP", "TK_GROUP", ALWAYS },
+ { "HAVING", "TK_HAVING", ALWAYS },
+ { "IF", "TK_IF", ALWAYS },
+ { "IGNORE", "TK_IGNORE", CONFLICT|TRIGGER },
+ { "IMMEDIATE", "TK_IMMEDIATE", ALWAYS },
+ { "IN", "TK_IN", ALWAYS },
+ { "INDEX", "TK_INDEX", ALWAYS },
+ { "INITIALLY", "TK_INITIALLY", FKEY },
+ { "INNER", "TK_JOIN_KW", ALWAYS },
+ { "INSERT", "TK_INSERT", ALWAYS },
+ { "INSTEAD", "TK_INSTEAD", TRIGGER },
+ { "INTERSECT", "TK_INTERSECT", COMPOUND },
+ { "INTO", "TK_INTO", ALWAYS },
+ { "IS", "TK_IS", ALWAYS },
+ { "ISNULL", "TK_ISNULL", ALWAYS },
+ { "JOIN", "TK_JOIN", ALWAYS },
+ { "KEY", "TK_KEY", ALWAYS },
+ { "LEFT", "TK_JOIN_KW", ALWAYS },
+ { "LIKE", "TK_LIKE_KW", ALWAYS },
+ { "LIMIT", "TK_LIMIT", ALWAYS },
+ { "MATCH", "TK_MATCH", ALWAYS },
+ { "NATURAL", "TK_JOIN_KW", ALWAYS },
+ { "NOT", "TK_NOT", ALWAYS },
+ { "NOTNULL", "TK_NOTNULL", ALWAYS },
+ { "NULL", "TK_NULL", ALWAYS },
+ { "OF", "TK_OF", ALWAYS },
+ { "OFFSET", "TK_OFFSET", ALWAYS },
+ { "ON", "TK_ON", ALWAYS },
+ { "OR", "TK_OR", ALWAYS },
+ { "ORDER", "TK_ORDER", ALWAYS },
+ { "OUTER", "TK_JOIN_KW", ALWAYS },
+ { "PLAN", "TK_PLAN", EXPLAIN },
+ { "PRAGMA", "TK_PRAGMA", PRAGMA },
+ { "PRIMARY", "TK_PRIMARY", ALWAYS },
+ { "QUERY", "TK_QUERY", EXPLAIN },
+ { "RAISE", "TK_RAISE", TRIGGER },
+ { "REFERENCES", "TK_REFERENCES", FKEY },
+ { "REGEXP", "TK_LIKE_KW", ALWAYS },
+ { "REINDEX", "TK_REINDEX", REINDEX },
+ { "RENAME", "TK_RENAME", ALTER },
+ { "REPLACE", "TK_REPLACE", CONFLICT },
+ { "RESTRICT", "TK_RESTRICT", FKEY },
+ { "RIGHT", "TK_JOIN_KW", ALWAYS },
+ { "ROLLBACK", "TK_ROLLBACK", ALWAYS },
+ { "ROW", "TK_ROW", TRIGGER },
+ { "SELECT", "TK_SELECT", ALWAYS },
+ { "SET", "TK_SET", ALWAYS },
+ { "TABLE", "TK_TABLE", ALWAYS },
+ { "TEMP", "TK_TEMP", ALWAYS },
+ { "TEMPORARY", "TK_TEMP", ALWAYS },
+ { "THEN", "TK_THEN", ALWAYS },
+ { "TO", "TK_TO", ALTER },
+ { "TRANSACTION", "TK_TRANSACTION", ALWAYS },
+ { "TRIGGER", "TK_TRIGGER", TRIGGER },
+ { "UNION", "TK_UNION", COMPOUND },
+ { "UNIQUE", "TK_UNIQUE", ALWAYS },
+ { "UPDATE", "TK_UPDATE", ALWAYS },
+ { "USING", "TK_USING", ALWAYS },
+ { "VACUUM", "TK_VACUUM", VACUUM },
+ { "VALUES", "TK_VALUES", ALWAYS },
+ { "VIEW", "TK_VIEW", VIEW },
+ { "VIRTUAL", "TK_VIRTUAL", VTAB },
+ { "WHEN", "TK_WHEN", ALWAYS },
+ { "WHERE", "TK_WHERE", ALWAYS },
+};
+
+/* Number of keywords */
+static int nKeyword = (sizeof(aKeywordTable)/sizeof(aKeywordTable[0]));
+
+/* An array to map all upper-case characters into their corresponding
+** lower-case character.
+*/
+const unsigned char sqlite3UpperToLower[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
+ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
+ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
+ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
+ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
+ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
+ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
+ 252,253,254,255
+};
+#define UpperToLower sqlite3UpperToLower
+
+/*
+** Comparision function for two Keyword records
+*/
+static int keywordCompare1(const void *a, const void *b){
+ const Keyword *pA = (Keyword*)a;
+ const Keyword *pB = (Keyword*)b;
+ int n = pA->len - pB->len;
+ if( n==0 ){
+ n = strcmp(pA->zName, pB->zName);
+ }
+ return n;
+}
+static int keywordCompare2(const void *a, const void *b){
+ const Keyword *pA = (Keyword*)a;
+ const Keyword *pB = (Keyword*)b;
+ int n = pB->longestSuffix - pA->longestSuffix;
+ if( n==0 ){
+ n = strcmp(pA->zName, pB->zName);
+ }
+ return n;
+}
+static int keywordCompare3(const void *a, const void *b){
+ const Keyword *pA = (Keyword*)a;
+ const Keyword *pB = (Keyword*)b;
+ int n = pA->offset - pB->offset;
+ return n;
+}
+
+/*
+** Return a KeywordTable entry with the given id
+*/
+static Keyword *findById(int id){
+ int i;
+ for(i=0; i<nKeyword; i++){
+ if( aKeywordTable[i].id==id ) break;
+ }
+ return &aKeywordTable[i];
+}
+
+/*
+** This routine does the work. The generated code is printed on standard
+** output.
+*/
+int main(int argc, char **argv){
+ int i, j, k, h;
+ int bestSize, bestCount;
+ int count;
+ int nChar;
+ int totalLen = 0;
+ int aHash[1000]; /* 1000 is much bigger than nKeyword */
+
+ /* Remove entries from the list of keywords that have mask==0 */
+ for(i=j=0; i<nKeyword; i++){
+ if( aKeywordTable[i].mask==0 ) continue;
+ if( j<i ){
+ aKeywordTable[j] = aKeywordTable[i];
+ }
+ j++;
+ }
+ nKeyword = j;
+
+ /* Fill in the lengths of strings and hashes for all entries. */
+ for(i=0; i<nKeyword; i++){
+ Keyword *p = &aKeywordTable[i];
+ p->len = strlen(p->zName);
+ totalLen += p->len;
+ p->hash = (UpperToLower[(int)p->zName[0]]*4) ^
+ (UpperToLower[(int)p->zName[p->len-1]]*3) ^ p->len;
+ p->id = i+1;
+ }
+
+ /* Sort the table from shortest to longest keyword */
+ qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare1);
+
+ /* Look for short keywords embedded in longer keywords */
+ for(i=nKeyword-2; i>=0; i--){
+ Keyword *p = &aKeywordTable[i];
+ for(j=nKeyword-1; j>i && p->substrId==0; j--){
+ Keyword *pOther = &aKeywordTable[j];
+ if( pOther->substrId ) continue;
+ if( pOther->len<=p->len ) continue;
+ for(k=0; k<=pOther->len-p->len; k++){
+ if( memcmp(p->zName, &pOther->zName[k], p->len)==0 ){
+ p->substrId = pOther->id;
+ p->substrOffset = k;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Compute the longestSuffix value for every word */
+ for(i=0; i<nKeyword; i++){
+ Keyword *p = &aKeywordTable[i];
+ if( p->substrId ) continue;
+ for(j=0; j<nKeyword; j++){
+ Keyword *pOther;
+ if( j==i ) continue;
+ pOther = &aKeywordTable[j];
+ if( pOther->substrId ) continue;
+ for(k=p->longestSuffix+1; k<p->len && k<pOther->len; k++){
+ if( memcmp(&p->zName[p->len-k], pOther->zName, k)==0 ){
+ p->longestSuffix = k;
+ }
+ }
+ }
+ }
+
+ /* Sort the table into reverse order by length */
+ qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare2);
+
+ /* Fill in the offset for all entries */
+ nChar = 0;
+ for(i=0; i<nKeyword; i++){
+ Keyword *p = &aKeywordTable[i];
+ if( p->offset>0 || p->substrId ) continue;
+ p->offset = nChar;
+ nChar += p->len;
+ for(k=p->len-1; k>=1; k--){
+ for(j=i+1; j<nKeyword; j++){
+ Keyword *pOther = &aKeywordTable[j];
+ if( pOther->offset>0 || pOther->substrId ) continue;
+ if( pOther->len<=k ) continue;
+ if( memcmp(&p->zName[p->len-k], pOther->zName, k)==0 ){
+ p = pOther;
+ p->offset = nChar - k;
+ nChar = p->offset + p->len;
+ p->zName += k;
+ p->len -= k;
+ p->prefix = k;
+ j = i;
+ k = p->len;
+ }
+ }
+ }
+ }
+ for(i=0; i<nKeyword; i++){
+ Keyword *p = &aKeywordTable[i];
+ if( p->substrId ){
+ p->offset = findById(p->substrId)->offset + p->substrOffset;
+ }
+ }
+
+ /* Sort the table by offset */
+ qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare3);
+
+ /* Figure out how big to make the hash table in order to minimize the
+ ** number of collisions */
+ bestSize = nKeyword;
+ bestCount = nKeyword*nKeyword;
+ for(i=nKeyword/2; i<=2*nKeyword; i++){
+ for(j=0; j<i; j++) aHash[j] = 0;
+ for(j=0; j<nKeyword; j++){
+ h = aKeywordTable[j].hash % i;
+ aHash[h] *= 2;
+ aHash[h]++;
+ }
+ for(j=count=0; j<i; j++) count += aHash[j];
+ if( count<bestCount ){
+ bestCount = count;
+ bestSize = i;
+ }
+ }
+
+ /* Compute the hash */
+ for(i=0; i<bestSize; i++) aHash[i] = 0;
+ for(i=0; i<nKeyword; i++){
+ h = aKeywordTable[i].hash % bestSize;
+ aKeywordTable[i].iNext = aHash[h];
+ aHash[h] = i+1;
+ }
+
+ /* Begin generating code */
+ printf("%s", zHdr);
+ printf("/* Hash score: %d */\n", bestCount);
+ printf("static int keywordCode(const char *z, int n){\n");
+ printf(" /* zText[] encodes %d bytes of keywords in %d bytes */\n",
+ totalLen + nKeyword, nChar+1 );
+
+ printf(" static const char zText[%d] =\n", nChar+1);
+ for(i=j=0; i<nKeyword; i++){
+ Keyword *p = &aKeywordTable[i];
+ if( p->substrId ) continue;
+ if( j==0 ) printf(" \"");
+ printf("%s", p->zName);
+ j += p->len;
+ if( j>60 ){
+ printf("\"\n");
+ j = 0;
+ }
+ }
+ printf("%s;\n", j>0 ? "\"" : " ");
+
+ printf(" static const unsigned char aHash[%d] = {\n", bestSize);
+ for(i=j=0; i<bestSize; i++){
+ if( j==0 ) printf(" ");
+ printf(" %3d,", aHash[i]);
+ j++;
+ if( j>12 ){
+ printf("\n");
+ j = 0;
+ }
+ }
+ printf("%s };\n", j==0 ? "" : "\n");
+
+ printf(" static const unsigned char aNext[%d] = {\n", nKeyword);
+ for(i=j=0; i<nKeyword; i++){
+ if( j==0 ) printf(" ");
+ printf(" %3d,", aKeywordTable[i].iNext);
+ j++;
+ if( j>12 ){
+ printf("\n");
+ j = 0;
+ }
+ }
+ printf("%s };\n", j==0 ? "" : "\n");
+
+ printf(" static const unsigned char aLen[%d] = {\n", nKeyword);
+ for(i=j=0; i<nKeyword; i++){
+ if( j==0 ) printf(" ");
+ printf(" %3d,", aKeywordTable[i].len+aKeywordTable[i].prefix);
+ j++;
+ if( j>12 ){
+ printf("\n");
+ j = 0;
+ }
+ }
+ printf("%s };\n", j==0 ? "" : "\n");
+
+ printf(" static const unsigned short int aOffset[%d] = {\n", nKeyword);
+ for(i=j=0; i<nKeyword; i++){
+ if( j==0 ) printf(" ");
+ printf(" %3d,", aKeywordTable[i].offset);
+ j++;
+ if( j>12 ){
+ printf("\n");
+ j = 0;
+ }
+ }
+ printf("%s };\n", j==0 ? "" : "\n");
+
+ printf(" static const unsigned char aCode[%d] = {\n", nKeyword);
+ for(i=j=0; i<nKeyword; i++){
+ char *zToken = aKeywordTable[i].zTokenType;
+ if( j==0 ) printf(" ");
+ printf("%s,%*s", zToken, (int)(14-strlen(zToken)), "");
+ j++;
+ if( j>=5 ){
+ printf("\n");
+ j = 0;
+ }
+ }
+ printf("%s };\n", j==0 ? "" : "\n");
+
+ printf(" int h, i;\n");
+ printf(" if( n<2 ) return TK_ID;\n");
+ printf(" h = ((charMap(z[0])*4) ^\n"
+ " (charMap(z[n-1])*3) ^\n"
+ " n) %% %d;\n", bestSize);
+ printf(" for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){\n");
+ printf(" if( aLen[i]==n &&"
+ " sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){\n");
+ printf(" return aCode[i];\n");
+ printf(" }\n");
+ printf(" }\n");
+ printf(" return TK_ID;\n");
+ printf("}\n");
+ printf("int sqlite3KeywordCode(const unsigned char *z, int n){\n");
+ printf(" return keywordCode((char*)z, n);\n");
+ printf("}\n");
+
+ return 0;
+}
diff --git a/third_party/sqlite/tool/mkopts.tcl b/third_party/sqlite/tool/mkopts.tcl
new file mode 100755
index 0000000..e3ddcb9
--- /dev/null
+++ b/third_party/sqlite/tool/mkopts.tcl
@@ -0,0 +1,51 @@
+#!/usr/bin/tclsh
+#
+# This script is used to generate the array of strings and the enum
+# that appear at the beginning of the C code implementation of a
+# a TCL command and that define the available subcommands for that
+# TCL command.
+
+set prefix {}
+while {![eof stdin]} {
+ set line [gets stdin]
+ if {$line==""} continue
+ regsub -all "\[ \t\n,\]+" [string trim $line] { } line
+ foreach token [split $line { }] {
+ if {![regexp {(([a-zA-Z]+)_)?([_a-zA-Z]+)} $token all px p2 name]} continue
+ lappend namelist [string tolower $name]
+ if {$px!=""} {set prefix $p2}
+ }
+}
+
+puts " static const char *${prefix}_strs\[\] = \173"
+set col 0
+proc put_item x {
+ global col
+ if {$col==0} {puts -nonewline " "}
+ if {$col<2} {
+ puts -nonewline [format " %-21s" $x]
+ incr col
+ } else {
+ puts $x
+ set col 0
+ }
+}
+proc finalize {} {
+ global col
+ if {$col>0} {puts {}}
+ set col 0
+}
+
+foreach name [lsort $namelist] {
+ put_item \"$name\",
+}
+put_item 0
+finalize
+puts " \175;"
+puts " enum ${prefix}_enum \173"
+foreach name [lsort $namelist] {
+ regsub -all {@} $name {} name
+ put_item ${prefix}_[string toupper $name],
+}
+finalize
+puts " \175;"
diff --git a/third_party/sqlite/tool/mksqlite3c.tcl b/third_party/sqlite/tool/mksqlite3c.tcl
new file mode 100755
index 0000000..98aede2
--- /dev/null
+++ b/third_party/sqlite/tool/mksqlite3c.tcl
@@ -0,0 +1,288 @@
+#!/usr/bin/tclsh
+#
+# To build a single huge source file holding all of SQLite (or at
+# least the core components - the test harness, shell, and TCL
+# interface are omitted.) first do
+#
+# make target_source
+#
+# The make target above moves all of the source code files into
+# a subdirectory named "tsrc". (This script expects to find the files
+# there and will not work if they are not found.) There are a few
+# generated C code files that are also added to the tsrc directory.
+# For example, the "parse.c" and "parse.h" files to implement the
+# the parser are derived from "parse.y" using lemon. And the
+# "keywordhash.h" files is generated by a program named "mkkeywordhash".
+#
+# After the "tsrc" directory has been created and populated, run
+# this script:
+#
+# tclsh mksqlite3c.tcl
+#
+# The amalgamated SQLite code will be written into sqlite3.c
+#
+
+# Begin by reading the "sqlite3.h" header file. Count the number of lines
+# in this file and extract the version number. That information will be
+# needed in order to generate the header of the amalgamation.
+#
+if {[lsearch $argv --nostatic]>=0} {
+ set addstatic 0
+} else {
+ set addstatic 1
+}
+set in [open tsrc/sqlite3.h]
+set cnt 0
+set VERSION ?????
+while {![eof $in]} {
+ set line [gets $in]
+ if {$line=="" && [eof $in]} break
+ incr cnt
+ regexp {#define\s+SQLITE_VERSION\s+"(.*)"} $line all VERSION
+}
+close $in
+
+# Open the output file and write a header comment at the beginning
+# of the file.
+#
+set out [open sqlite3.c w]
+set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1]
+puts $out [subst \
+{/******************************************************************************
+** This file is an amalgamation of many separate C source files from SQLite
+** version $VERSION. By combining all the individual C code files into this
+** single large file, the entire code can be compiled as a one translation
+** unit. This allows many compilers to do optimizations that would not be
+** possible if the files were compiled separately. Performance improvements
+** of 5% are more are commonly seen when SQLite is compiled as a single
+** translation unit.
+**
+** This file is all you need to compile SQLite. To use SQLite in other
+** programs, you need this file and the "sqlite3.h" header file that defines
+** the programming interface to the SQLite library. (If you do not have
+** the "sqlite3.h" header file at hand, you will find a copy in the first
+** $cnt lines past this header comment.) Additional code files may be
+** needed if you want a wrapper to interface SQLite with your choice of
+** programming language. The code for the "sqlite3" command-line shell
+** is also in a separate file. This file contains only code for the core
+** SQLite library.
+**
+** This amalgamation was generated on $today.
+*/
+#define SQLITE_CORE 1
+#define SQLITE_AMALGAMATION 1}]
+if {$addstatic} {
+ puts $out \
+{#ifndef SQLITE_PRIVATE
+# define SQLITE_PRIVATE static
+#endif
+#ifndef SQLITE_API
+# define SQLITE_API
+#endif}
+}
+
+# These are the header files used by SQLite. The first time any of these
+# files are seen in a #include statement in the C code, include the complete
+# text of the file in-line. The file only needs to be included once.
+#
+foreach hdr {
+ btree.h
+ btreeInt.h
+ fts3.h
+ fts3_hash.h
+ fts3_tokenizer.h
+ hash.h
+ hwtime.h
+ keywordhash.h
+ mutex.h
+ opcodes.h
+ os_common.h
+ os.h
+ os_os2.h
+ pager.h
+ parse.h
+ rtree.h
+ sqlite3ext.h
+ sqlite3.h
+ sqliteInt.h
+ sqliteLimit.h
+ vdbe.h
+ vdbeInt.h
+} {
+ set available_hdr($hdr) 1
+}
+set available_hdr(sqliteInt.h) 0
+
+# 78 stars used for comment formatting.
+set s78 \
+{*****************************************************************************}
+
+# Insert a comment into the code
+#
+proc section_comment {text} {
+ global out s78
+ set n [string length $text]
+ set nstar [expr {60 - $n}]
+ set stars [string range $s78 0 $nstar]
+ puts $out "/************** $text $stars/"
+}
+
+# Read the source file named $filename and write it into the
+# sqlite3.c output file. If any #include statements are seen,
+# process them approprately.
+#
+proc copy_file {filename} {
+ global seen_hdr available_hdr out addstatic
+ set tail [file tail $filename]
+ section_comment "Begin file $tail"
+ set in [open $filename r]
+ set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+(sqlite3[_a-zA-Z0-9]+)(\[|;| =)}
+ set declpattern {[a-zA-Z][a-zA-Z_0-9 ]+ \*?(sqlite3[_a-zA-Z0-9]+)\(}
+ if {[file extension $filename]==".h"} {
+ set declpattern " *$declpattern"
+ }
+ set declpattern ^$declpattern
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} {
+ if {[info exists available_hdr($hdr)]} {
+ if {$available_hdr($hdr)} {
+ if {$hdr!="os_common.h" && $hdr!="hwtime.h"} {
+ set available_hdr($hdr) 0
+ }
+ section_comment "Include $hdr in the middle of $tail"
+ copy_file tsrc/$hdr
+ section_comment "Continuing where we left off in $tail"
+ }
+ } elseif {![info exists seen_hdr($hdr)]} {
+ set seen_hdr($hdr) 1
+ puts $out $line
+ }
+ } elseif {[regexp {^#ifdef __cplusplus} $line]} {
+ puts $out "#if 0"
+ } elseif {[regexp {^#line} $line]} {
+ # Skip #line directives.
+ } elseif {$addstatic && ![regexp {^(static|typedef)} $line]} {
+ if {[regexp $declpattern $line all funcname]} {
+ # Add the SQLITE_PRIVATE or SQLITE_API keyword before functions.
+ # so that linkage can be modified at compile-time.
+ if {[regexp {^sqlite3_} $funcname]} {
+ puts $out "SQLITE_API $line"
+ } else {
+ puts $out "SQLITE_PRIVATE $line"
+ }
+ } elseif {[regexp $varpattern $line all varname]} {
+ # Add the SQLITE_PRIVATE before variable declarations or
+ # definitions for internal use
+ if {![regexp {^sqlite3_} $varname]} {
+ regsub {^extern } $line {} line
+ puts $out "SQLITE_PRIVATE $line"
+ } else {
+ regsub {^SQLITE_EXTERN } $line {} line
+ puts $out "SQLITE_API $line"
+ }
+ } elseif {[regexp {^(SQLITE_EXTERN )?void \(\*sqlite3IoTrace\)} $line]} {
+ regsub {^SQLITE_EXTERN } $line {} line
+ puts $out "SQLITE_PRIVATE $line"
+ } else {
+ puts $out $line
+ }
+ } else {
+ puts $out $line
+ }
+ }
+ close $in
+ section_comment "End of $tail"
+}
+
+
+# Process the source files. Process files containing commonly
+# used subroutines first in order to help the compiler find
+# inlining opportunities.
+#
+foreach file {
+ sqliteInt.h
+
+ global.c
+ status.c
+ date.c
+ os.c
+
+ fault.c
+ mem1.c
+ mem2.c
+ mem3.c
+ mem5.c
+ mem6.c
+ mutex.c
+ mutex_os2.c
+ mutex_unix.c
+ mutex_w32.c
+ malloc.c
+ printf.c
+ random.c
+ utf.c
+ util.c
+ hash.c
+ opcodes.c
+
+ os_os2.c
+ os_unix.c
+ os_win.c
+
+ bitvec.c
+ pager.c
+
+ btmutex.c
+ btree.c
+
+ vdbefifo.c
+ vdbemem.c
+ vdbeaux.c
+ vdbeapi.c
+ vdbe.c
+ vdbeblob.c
+ journal.c
+
+ expr.c
+ alter.c
+ analyze.c
+ attach.c
+ auth.c
+ build.c
+ callback.c
+ delete.c
+ func.c
+ insert.c
+ legacy.c
+ loadext.c
+ pragma.c
+ prepare.c
+ select.c
+ table.c
+ trigger.c
+ update.c
+ vacuum.c
+ vtab.c
+ where.c
+
+ parse.c
+
+ tokenize.c
+ complete.c
+
+ main.c
+
+ fts3.c
+ fts3_hash.c
+ fts3_porter.c
+ fts3_tokenizer.c
+ fts3_tokenizer1.c
+
+ rtree.c
+ icu.c
+} {
+ copy_file tsrc/$file
+}
+
+close $out
diff --git a/third_party/sqlite/tool/mksqlite3internalh.tcl b/third_party/sqlite/tool/mksqlite3internalh.tcl
new file mode 100755
index 0000000..f02a62d
--- /dev/null
+++ b/third_party/sqlite/tool/mksqlite3internalh.tcl
@@ -0,0 +1,146 @@
+#!/usr/bin/tclsh
+#
+# To build a single huge source file holding all of SQLite (or at
+# least the core components - the test harness, shell, and TCL
+# interface are omitted.) first do
+#
+# make target_source
+#
+# The make target above moves all of the source code files into
+# a subdirectory named "tsrc". (This script expects to find the files
+# there and will not work if they are not found.) There are a few
+# generated C code files that are also added to the tsrc directory.
+# For example, the "parse.c" and "parse.h" files to implement the
+# the parser are derived from "parse.y" using lemon. And the
+# "keywordhash.h" files is generated by a program named "mkkeywordhash".
+#
+# After the "tsrc" directory has been created and populated, run
+# this script:
+#
+# tclsh mksqlite3c.tcl
+#
+# The amalgamated SQLite code will be written into sqlite3.c
+#
+
+# Begin by reading the "sqlite3.h" header file. Count the number of lines
+# in this file and extract the version number. That information will be
+# needed in order to generate the header of the amalgamation.
+#
+set in [open tsrc/sqlite3.h]
+set cnt 0
+set VERSION ?????
+while {![eof $in]} {
+ set line [gets $in]
+ if {$line=="" && [eof $in]} break
+ incr cnt
+ regexp {#define\s+SQLITE_VERSION\s+"(.*)"} $line all VERSION
+}
+close $in
+
+# Open the output file and write a header comment at the beginning
+# of the file.
+#
+set out [open sqlite3internal.h w]
+set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1]
+puts $out [subst \
+{/******************************************************************************
+** This file is an amalgamation of many private header files from SQLite
+** version $VERSION.
+*/}]
+
+# These are the header files used by SQLite. The first time any of these
+# files are seen in a #include statement in the C code, include the complete
+# text of the file in-line. The file only needs to be included once.
+#
+foreach hdr {
+ btree.h
+ btreeInt.h
+ hash.h
+ hwtime.h
+ keywordhash.h
+ opcodes.h
+ os_common.h
+ os.h
+ os_os2.h
+ pager.h
+ parse.h
+ sqlite3ext.h
+ sqlite3.h
+ sqliteInt.h
+ sqliteLimit.h
+ vdbe.h
+ vdbeInt.h
+} {
+ set available_hdr($hdr) 1
+}
+
+# 78 stars used for comment formatting.
+set s78 \
+{*****************************************************************************}
+
+# Insert a comment into the code
+#
+proc section_comment {text} {
+ global out s78
+ set n [string length $text]
+ set nstar [expr {60 - $n}]
+ set stars [string range $s78 0 $nstar]
+ puts $out "/************** $text $stars/"
+}
+
+# Read the source file named $filename and write it into the
+# sqlite3.c output file. If any #include statements are seen,
+# process them approprately.
+#
+proc copy_file {filename} {
+ global seen_hdr available_hdr out
+ set tail [file tail $filename]
+ section_comment "Begin file $tail"
+ set in [open $filename r]
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} {
+ if {[info exists available_hdr($hdr)]} {
+ if {$available_hdr($hdr)} {
+ section_comment "Include $hdr in the middle of $tail"
+ copy_file tsrc/$hdr
+ section_comment "Continuing where we left off in $tail"
+ }
+ } elseif {![info exists seen_hdr($hdr)]} {
+ set seen_hdr($hdr) 1
+ puts $out $line
+ }
+ } elseif {[regexp {^#ifdef __cplusplus} $line]} {
+ puts $out "#if 0"
+ } elseif {[regexp {^#line} $line]} {
+ # Skip #line directives.
+ } else {
+ puts $out $line
+ }
+ }
+ close $in
+ section_comment "End of $tail"
+}
+
+
+# Process the source files. Process files containing commonly
+# used subroutines first in order to help the compiler find
+# inlining opportunities.
+#
+foreach file {
+ sqliteInt.h
+ sqlite3.h
+ btree.h
+ hash.h
+ os.h
+ pager.h
+ parse.h
+ sqlite3ext.h
+ vdbe.h
+} {
+ if {$available_hdr($file)} {
+ copy_file tsrc/$file
+ }
+}
+
+close $out
diff --git a/third_party/sqlite/tool/omittest.tcl b/third_party/sqlite/tool/omittest.tcl
new file mode 100755
index 0000000..13a70cd2
--- /dev/null
+++ b/third_party/sqlite/tool/omittest.tcl
@@ -0,0 +1,215 @@
+
+set rcsid {$Id: omittest.tcl,v 1.6 2008/08/04 03:51:24 danielk1977 Exp $}
+
+# Documentation for this script. This may be output to stderr
+# if the script is invoked incorrectly.
+set ::USAGE_MESSAGE {
+This Tcl script is used to test the various compile time options
+available for omitting code (the SQLITE_OMIT_xxx options). It
+should be invoked as follows:
+
+ <script> ?-makefile PATH-TO-MAKEFILE?
+
+The default value for ::MAKEFILE is "../Makefile.linux.gcc".
+
+This script builds the testfixture program and runs the SQLite test suite
+once with each SQLITE_OMIT_ option defined and then once with all options
+defined together. Each run is performed in a seperate directory created
+as a sub-directory of the current directory by the script. The output
+of the build is saved in <sub-directory>/build.log. The output of the
+test-suite is saved in <sub-directory>/test.log.
+
+Almost any SQLite makefile (except those generated by configure - see below)
+should work. The following properties are required:
+
+ * The makefile should support the "testfixture" target.
+ * The makefile should support the "test" target.
+ * The makefile should support the variable "OPTS" as a way to pass
+ options from the make command line to lemon and the C compiler.
+
+More precisely, the following two invocations must be supported:
+
+ make -f $::MAKEFILE testfixture OPTS="-DSQLITE_OMIT_ALTERTABLE=1"
+ make -f $::MAKEFILE test
+
+Makefiles generated by the sqlite configure program cannot be used as
+they do not respect the OPTS variable.
+}
+
+
+# Build a testfixture executable and run quick.test using it. The first
+# parameter is the name of the directory to create and use to run the
+# test in. The second parameter is a list of OMIT symbols to define
+# when doing so. For example:
+#
+# run_quick_test /tmp/testdir {SQLITE_OMIT_TRIGGER SQLITE_OMIT_VIEW}
+#
+#
+proc run_quick_test {dir omit_symbol_list} {
+ # Compile the value of the OPTS Makefile variable.
+ set opts "-DSQLITE_MEMDEBUG -DSQLITE_DEBUG"
+ if {$::tcl_platform(platform)=="windows"} {
+ append opts " -DSQLITE_OS_WIN=1"
+ } else {
+ append opts " -DSQLITE_OS_UNIX=1"
+ }
+ foreach sym $omit_symbol_list {
+ append opts " -D${sym}=1"
+ }
+
+ # Create the directory and do the build. If an error occurs return
+ # early without attempting to run the test suite.
+ file mkdir $dir
+ puts -nonewline "Building $dir..."
+ flush stdout
+catch {
+ file copy -force ./config.h $dir
+ file copy -force ./libtool $dir
+}
+ set rc [catch {
+ exec make -C $dir -f $::MAKEFILE testfixture OPTS=$opts >& $dir/build.log
+ }]
+ if {$rc} {
+ puts "No good. See $dir/build.log."
+ return
+ } else {
+ puts "Ok"
+ }
+
+ # Create an empty file "$dir/sqlite3". This is to trick the makefile out
+ # of trying to build the sqlite shell. The sqlite shell won't build
+ # with some of the OMIT options (i.e OMIT_COMPLETE).
+ set sqlite3_dummy $dir/sqlite3
+ if {$::tcl_platform(platform)=="windows"} {
+ append sqlite3_dummy ".exe"
+ }
+ if {![file exists $sqlite3_dummy]} {
+ set wr [open $sqlite3_dummy w]
+ puts $wr "dummy"
+ close $wr
+ }
+
+ # Run the test suite.
+ puts -nonewline "Testing $dir..."
+ flush stdout
+ set rc [catch {
+ exec make -C $dir -f $::MAKEFILE test OPTS=$opts >& $dir/test.log
+ }]
+ if {$rc} {
+ puts "No good. See $dir/test.log."
+ } else {
+ puts "Ok"
+ }
+}
+
+
+# This proc processes the command line options passed to this script.
+# Currently the only option supported is "-makefile", default
+# "../Makefile.linux-gcc". Set the ::MAKEFILE variable to the value of this
+# option.
+#
+proc process_options {argv} {
+ if {$::tcl_platform(platform)=="windows"} {
+ set ::MAKEFILE ../Makefile ;# Default value
+ } else {
+ set ::MAKEFILE ../Makefile.linux-gcc ;# Default value
+ }
+ for {set i 0} {$i < [llength $argv]} {incr i} {
+ switch -- [lindex $argv $i] {
+ -makefile {
+ incr i
+ set ::MAKEFILE [lindex $argv $i]
+ }
+
+ default {
+ puts stderr [string trim $::USAGE_MESSAGE]
+ exit -1
+ }
+ }
+ set ::MAKEFILE [file normalize $::MAKEFILE]
+ }
+}
+
+# Main routine.
+#
+
+proc main {argv} {
+ # List of SQLITE_OMIT_XXX symbols supported by SQLite.
+ set ::SYMBOLS [list \
+ SQLITE_OMIT_ALTERTABLE \
+ SQLITE_OMIT_ANALYZE \
+ SQLITE_OMIT_ATTACH \
+ SQLITE_OMIT_AUTHORIZATION \
+ SQLITE_OMIT_AUTOINCREMENT \
+ SQLITE_OMIT_AUTOINIT \
+ SQLITE_OMIT_AUTOVACUUM \
+ SQLITE_OMIT_BETWEEN_OPTIMIZATION \
+ SQLITE_OMIT_BLOB_LITERAL \
+ SQLITE_OMIT_BUILTIN_TEST \
+ SQLITE_OMIT_CAST \
+ SQLITE_OMIT_CHECK \
+ SQLITE_OMIT_COMPLETE \
+ SQLITE_OMIT_COMPOUND_SELECT \
+ SQLITE_OMIT_CONFLICT_CLAUSE \
+ SQLITE_OMIT_DATETIME_FUNCS \
+ SQLITE_OMIT_DECLTYPE \
+ SQLITE_OMIT_DISKIO \
+ SQLITE_OMIT_EXPLAIN \
+ SQLITE_OMIT_FLAG_PRAGMAS \
+ SQLITE_OMIT_FLOATING_POINT \
+ SQLITE_OMIT_FOREIGN_KEY \
+ SQLITE_OMIT_GET_TABLE \
+ SQLITE_OMIT_GLOBALRECOVER \
+ SQLITE_OMIT_INCRBLOB \
+ SQLITE_OMIT_INTEGRITY_CHECK \
+ SQLITE_OMIT_LIKE_OPTIMIZATION \
+ SQLITE_OMIT_LOAD_EXTENSION \
+ SQLITE_OMIT_LOCALTIME \
+ SQLITE_OMIT_MEMORYDB \
+ SQLITE_OMIT_OR_OPTIMIZATION \
+ SQLITE_OMIT_PAGER_PRAGMAS \
+ SQLITE_OMIT_PARSER \
+ SQLITE_OMIT_PRAGMA \
+ SQLITE_OMIT_PROGRESS_CALLBACK \
+ SQLITE_OMIT_QUICKBALANCE \
+ SQLITE_OMIT_REINDEX \
+ SQLITE_OMIT_SCHEMA_PRAGMAS \
+ SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS \
+ SQLITE_OMIT_SHARED_CACHE \
+ SQLITE_OMIT_SUBQUERY \
+ SQLITE_OMIT_TCL_VARIABLE \
+ SQLITE_OMIT_TEMPDB \
+ SQLITE_OMIT_TRACE \
+ SQLITE_OMIT_TRIGGER \
+ SQLITE_OMIT_UTF16 \
+ SQLITE_OMIT_VACUUM \
+ SQLITE_OMIT_VIEW \
+ SQLITE_OMIT_VIRTUALTABLE \
+ SQLITE_OMIT_XFER_OPT \
+ ]
+
+ # Process any command line options.
+ process_options $argv
+
+ # First try a test with all OMIT symbols except SQLITE_OMIT_FLOATING_POINT
+ # and SQLITE_OMIT_PRAGMA defined. The former doesn't work (causes segfaults)
+ # and the latter is currently incompatible with the test suite (this should
+ # be fixed, but it will be a lot of work).
+ set allsyms [list]
+ foreach s $::SYMBOLS {
+ if {$s!="SQLITE_OMIT_FLOATING_POINT" && $s!="SQLITE_OMIT_PRAGMA"} {
+ lappend allsyms $s
+ }
+ }
+ run_quick_test test_OMIT_EVERYTHING $allsyms
+
+ # Now try one quick.test with each of the OMIT symbols defined. Included
+ # are the OMIT_FLOATING_POINT and OMIT_PRAGMA symbols, even though we
+ # know they will fail. It's good to be reminded of this from time to time.
+ foreach sym $::SYMBOLS {
+ set dirname "test_[string range $sym 7 end]"
+ run_quick_test $dirname $sym
+ }
+}
+
+main $argv
diff --git a/third_party/sqlite/tool/opcodeDoc.awk b/third_party/sqlite/tool/opcodeDoc.awk
new file mode 100755
index 0000000..4920106
--- /dev/null
+++ b/third_party/sqlite/tool/opcodeDoc.awk
@@ -0,0 +1,23 @@
+#
+# Extract opcode documentation for sqliteVdbe.c and generate HTML
+#
+BEGIN {
+ print "<html><body bgcolor=white>"
+ print "<h1>SQLite Virtual Database Engine Opcodes</h1>"
+ print "<table>"
+}
+/ Opcode: /,/\*\// {
+ if( $2=="Opcode:" ){
+ printf "<tr><td>%s&nbsp;%s&nbsp;%s&nbsp;%s</td>\n<td>\n", $3, $4, $5, $6
+ }else if( $1=="*/" ){
+ printf "</td></tr>\n"
+ }else if( NF>1 ){
+ sub(/^ *\*\* /,"")
+ gsub(/</,"&lt;")
+ gsub(/&/,"&amp;")
+ print
+ }
+}
+END {
+ print "</table></body></html>"
+}
diff --git a/third_party/sqlite/tool/report1.txt b/third_party/sqlite/tool/report1.txt
new file mode 100755
index 0000000..7820b8c
--- /dev/null
+++ b/third_party/sqlite/tool/report1.txt
@@ -0,0 +1,66 @@
+The SQL database used for ACD contains 113 tables and indices implemented
+in GDBM. The following are statistics on the sizes of keys and data
+within these tables and indices.
+
+Entries: 962080
+Size: 45573853
+Avg Size: 48
+Key Size: 11045299
+Avg Key Size: 12
+Max Key Size: 99
+
+
+ Size of key Cummulative
+ and data Instances Percentage
+------------ ---------- -----------
+ 0..8 266 0%
+ 9..12 5485 0%
+ 13..16 73633 8%
+ 17..24 180918 27%
+ 25..32 209823 48%
+ 33..40 148995 64%
+ 41..48 76304 72%
+ 49..56 14346 73%
+ 57..64 15725 75%
+ 65..80 44916 80%
+ 81..96 127815 93%
+ 97..112 34769 96%
+ 113..128 13314 98%
+ 129..144 8098 99%
+ 145..160 3355 99%
+ 161..176 1159 99%
+ 177..192 629 99%
+ 193..208 221 99%
+ 209..224 210 99%
+ 225..240 129 99%
+ 241..256 57 99%
+ 257..288 496 99%
+ 289..320 60 99%
+ 321..352 37 99%
+ 353..384 46 99%
+ 385..416 22 99%
+ 417..448 24 99%
+ 449..480 26 99%
+ 481..512 27 99%
+ 513..1024 471 99%
+ 1025..2048 389 99%
+ 2049..4096 182 99%
+ 4097..8192 74 99%
+ 8193..16384 34 99%
+16385..32768 17 99%
+32769..65536 5 99%
+65537..131073 3 100%
+
+
+This information is gathered to help design the new built-in
+backend for sqlite 2.0. Note in particular that 99% of all
+database entries have a combined key and data size of less than
+144 bytes. So if a leaf node in the new database is able to
+store 144 bytes of combined key and data, only 1% of the leaves
+will require overflow pages. Furthermore, note that no key
+is larger than 99 bytes, so if the key will never be on an
+overflow page.
+
+The average combined size of key+data is 48. Add in 16 bytes of
+overhead for a total of 64. That means that a 1K page will
+store (on average) about 16 entries.
diff --git a/third_party/sqlite/tool/showdb.c b/third_party/sqlite/tool/showdb.c
new file mode 100755
index 0000000..b2ed562
--- /dev/null
+++ b/third_party/sqlite/tool/showdb.c
@@ -0,0 +1,86 @@
+/*
+** A utility for printing all or part of an SQLite database file.
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+static int pagesize = 1024;
+static int db = -1;
+static int mxPage = 0;
+static int perLine = 32;
+
+static void out_of_memory(void){
+ fprintf(stderr,"Out of memory...\n");
+ exit(1);
+}
+
+static print_page(int iPg){
+ unsigned char *aData;
+ int i, j;
+ aData = malloc(pagesize);
+ if( aData==0 ) out_of_memory();
+ lseek(db, (iPg-1)*pagesize, SEEK_SET);
+ read(db, aData, pagesize);
+ fprintf(stdout, "Page %d:\n", iPg);
+ for(i=0; i<pagesize; i += perLine){
+ fprintf(stdout, " %03x: ",i);
+ for(j=0; j<perLine; j++){
+ fprintf(stdout,"%02x ", aData[i+j]);
+ }
+ for(j=0; j<perLine; j++){
+ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
+ }
+ fprintf(stdout,"\n");
+ }
+ free(aData);
+}
+
+int main(int argc, char **argv){
+ struct stat sbuf;
+ if( argc<2 ){
+ fprintf(stderr,"Usage: %s FILENAME ?PAGE? ...\n", argv[0]);
+ exit(1);
+ }
+ db = open(argv[1], O_RDONLY);
+ if( db<0 ){
+ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
+ exit(1);
+ }
+ fstat(db, &sbuf);
+ mxPage = sbuf.st_size/pagesize + 1;
+ if( argc==2 ){
+ int i;
+ for(i=1; i<=mxPage; i++) print_page(i);
+ }else{
+ int i;
+ for(i=2; i<argc; i++){
+ int iStart, iEnd;
+ char *zLeft;
+ iStart = strtol(argv[i], &zLeft, 0);
+ if( zLeft && strcmp(zLeft,"..end")==0 ){
+ iEnd = mxPage;
+ }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
+ iEnd = strtol(&zLeft[2], 0, 0);
+ }else{
+ iEnd = iStart;
+ }
+ if( iStart<1 || iEnd<iStart || iEnd>mxPage ){
+ fprintf(stderr,
+ "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
+ mxPage);
+ exit(1);
+ }
+ while( iStart<=iEnd ){
+ print_page(iStart);
+ iStart++;
+ }
+ }
+ }
+ close(db);
+}
diff --git a/third_party/sqlite/tool/showjournal.c b/third_party/sqlite/tool/showjournal.c
new file mode 100755
index 0000000..ec93c91
--- /dev/null
+++ b/third_party/sqlite/tool/showjournal.c
@@ -0,0 +1,76 @@
+/*
+** A utility for printing an SQLite database journal.
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+static int pagesize = 1024;
+static int db = -1;
+static int mxPage = 0;
+
+static void out_of_memory(void){
+ fprintf(stderr,"Out of memory...\n");
+ exit(1);
+}
+
+static print_page(int iPg){
+ unsigned char *aData;
+ int i, j;
+ aData = malloc(pagesize);
+ if( aData==0 ) out_of_memory();
+ read(db, aData, pagesize);
+ fprintf(stdout, "Page %d:\n", iPg);
+ for(i=0; i<pagesize; i += 16){
+ fprintf(stdout, " %03x: ",i);
+ for(j=0; j<16; j++){
+ fprintf(stdout,"%02x ", aData[i+j]);
+ }
+ for(j=0; j<16; j++){
+ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
+ }
+ fprintf(stdout,"\n");
+ }
+ free(aData);
+}
+
+int main(int argc, char **argv){
+ struct stat sbuf;
+ unsigned int u;
+ int rc;
+ unsigned char zBuf[10];
+ unsigned char zBuf2[sizeof(u)];
+ if( argc!=2 ){
+ fprintf(stderr,"Usage: %s FILENAME\n", argv[0]);
+ exit(1);
+ }
+ db = open(argv[1], O_RDONLY);
+ if( db<0 ){
+ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
+ exit(1);
+ }
+ read(db, zBuf, 8);
+ if( zBuf[7]==0xd6 ){
+ read(db, &u, sizeof(u));
+ printf("Records in Journal: %u\n", u);
+ read(db, &u, sizeof(u));
+ printf("Magic Number: 0x%08x\n", u);
+ }
+ read(db, zBuf2, sizeof(zBuf2));
+ u = zBuf2[0]<<24 | zBuf2[1]<<16 | zBuf2[2]<<8 | zBuf2[3];
+ printf("Database Size: %u\n", u);
+ while( read(db, zBuf2, sizeof(zBuf2))==sizeof(zBuf2) ){
+ u = zBuf2[0]<<24 | zBuf2[1]<<16 | zBuf2[2]<<8 | zBuf2[3];
+ print_page(u);
+ if( zBuf[7]==0xd6 ){
+ read(db, &u, sizeof(u));
+ printf("Checksum: 0x%08x\n", u);
+ }
+ }
+ close(db);
+}
diff --git a/third_party/sqlite/tool/soak1.tcl b/third_party/sqlite/tool/soak1.tcl
new file mode 100755
index 0000000..846f905
--- /dev/null
+++ b/third_party/sqlite/tool/soak1.tcl
@@ -0,0 +1,103 @@
+#!/usr/bin/tclsh
+#
+# Usage:
+#
+# tclsh soak1.tcl local-makefile.mk ?target? ?scenario?
+#
+# This generates many variations on local-makefile.mk (by modifing
+# the OPT = lines) and runs them will fulltest, one by one. The
+# constructed makefiles are named "soak1.mk".
+#
+# If ?target? is provided, that is the makefile target that is run.
+# The default is "fulltest"
+#
+# If ?scenario? is provided, it is the name of a single scenario to
+# be run. All other scenarios are skipped.
+#
+set localmake [lindex $argv 0]
+set target [lindex $argv 1]
+set scene [lindex $argv 2]
+if {$target==""} {set target fulltest}
+if {$scene==""} {set scene all}
+
+set in [open $localmake]
+set maketxt [read $in]
+close $in
+regsub -all {\\\n} $maketxt {} maketxt
+#set makefilename "soak1-[expr {int(rand()*1000000000)}].mk"
+set makefilename "soak1.mk"
+
+# Generate a makefile
+#
+proc generate_makefile {pattern} {
+ global makefilename maketxt
+ set out [open $makefilename w]
+ set seen_opt 0
+ foreach line [split $maketxt \n] {
+ if {[regexp {^ *#? *OPTS[ =+]} $line]} {
+ if {!$seen_opt} {
+ puts $out "OPTS += -DSQLITE_NO_SYNC=1"
+ foreach x $pattern {
+ puts $out "OPTS += -D$x"
+ }
+ set seen_opt 1
+ }
+ } else {
+ puts $out $line
+ }
+ }
+ close $out
+}
+
+# Run a test
+#
+proc scenario {id title pattern} {
+ global makefilename target scene
+ if {$scene!="all" && $scene!=$id && $scene!=$title} return
+ puts "**************** $title ***************"
+ generate_makefile $pattern
+ exec make -f $makefilename clean >@stdout 2>@stdout
+ exec make -f $makefilename $target >@stdout 2>@stdout
+}
+
+###############################################################################
+# Add new scenarios here
+#
+scenario 0 {Default} {}
+scenario 1 {Debug} {
+ SQLITE_DEBUG=1
+ SQLITE_MEMDEBUG=1
+}
+scenario 2 {Everything} {
+ SQLITE_DEBUG=1
+ SQLITE_MEMDEBUG=1
+ SQLITE_ENABLE_MEMORY_MANAGEMENT=1
+ SQLITE_ENABLE_COLUMN_METADATA=1
+ SQLITE_ENABLE_LOAD_EXTENSION=1 HAVE_DLOPEN=1
+ SQLITE_ENABLE_MEMORY_MANAGEMENT=1
+}
+scenario 3 {Customer-1} {
+ SQLITE_DEBUG=1 SQLITE_MEMDEBUG=1
+ SQLITE_THREADSAFE=1 SQLITE_OS_UNIX=1
+ SQLITE_DISABLE_LFS=1
+ SQLITE_DEFAULT_AUTOVACUUM=1
+ SQLITE_DEFAULT_PAGE_SIZE=1024
+ SQLITE_MAX_PAGE_SIZE=4096
+ SQLITE_DEFAULT_CACHE_SIZE=64
+ SQLITE_DEFAULT_TEMP_CACHE_SIZE=32
+ SQLITE_TEMP_STORE=3
+ SQLITE_OMIT_PROGRESS_CALLBACK=1
+ SQLITE_OMIT_LOAD_EXTENSION=1
+ SQLITE_OMIT_VIRTUALTABLE=1
+ SQLITE_ENABLE_IOTRACE=1
+}
+scenario 4 {Small-Cache} {
+ SQLITE_DEBUG=1 SQLITE_MEMDEBUG=1
+ SQLITE_THREADSAFE=1 SQLITE_OS_UNIX=1
+ SQLITE_DEFAULT_AUTOVACUUM=1
+ SQLITE_DEFAULT_PAGE_SIZE=1024
+ SQLITE_MAX_PAGE_SIZE=2048
+ SQLITE_DEFAULT_CACHE_SIZE=13
+ SQLITE_DEFAULT_TEMP_CACHE_SIZE=11
+ SQLITE_TEMP_STORE=1
+}
diff --git a/third_party/sqlite/tool/space_used.tcl b/third_party/sqlite/tool/space_used.tcl
new file mode 100755
index 0000000..2044aa3
--- /dev/null
+++ b/third_party/sqlite/tool/space_used.tcl
@@ -0,0 +1,111 @@
+# Run this TCL script using "testfixture" in order get a report that shows
+# how much disk space is used by a particular data to actually store data
+# versus how much space is unused.
+#
+
+# Get the name of the database to analyze
+#
+if {[llength $argv]!=1} {
+ puts stderr "Usage: $argv0 database-name"
+ exit 1
+}
+set file_to_analyze [lindex $argv 0]
+
+# Open the database
+#
+sqlite db [lindex $argv 0]
+set DB [btree_open [lindex $argv 0]]
+
+# Output the schema for the generated report
+#
+puts \
+{BEGIN;
+CREATE TABLE space_used(
+ name clob, -- Name of a table or index in the database file
+ is_index boolean, -- TRUE if it is an index, false for a table
+ payload int, -- Total amount of data stored in this table or index
+ pri_pages int, -- Number of primary pages used
+ ovfl_pages int, -- Number of overflow pages used
+ pri_unused int, -- Number of unused bytes on primary pages
+ ovfl_unused int -- Number of unused bytes on overflow pages
+);}
+
+# This query will be used to find the root page number for every index and
+# table in the database.
+#
+set sql {
+ SELECT name, type, rootpage FROM sqlite_master
+ UNION ALL
+ SELECT 'sqlite_master', 'table', 2
+ ORDER BY 1
+}
+
+# Initialize variables used for summary statistics.
+#
+set total_size 0
+set total_primary 0
+set total_overflow 0
+set total_unused_primary 0
+set total_unused_ovfl 0
+
+# Analyze every table in the database, one at a time.
+#
+foreach {name type rootpage} [db eval $sql] {
+ set cursor [btree_cursor $DB $rootpage 0]
+ set go [btree_first $cursor]
+ set size 0
+ catch {unset pg_used}
+ set unused_ovfl 0
+ set n_overflow 0
+ while {$go==0} {
+ set payload [btree_payload_size $cursor]
+ incr size $payload
+ set stat [btree_cursor_dump $cursor]
+ set pgno [lindex $stat 0]
+ set freebytes [lindex $stat 4]
+ set pg_used($pgno) $freebytes
+ if {$payload>238} {
+ set n [expr {($payload-238+1019)/1020}]
+ incr n_overflow $n
+ incr unused_ovfl [expr {$n*1020+238-$payload}]
+ }
+ set go [btree_next $cursor]
+ }
+ btree_close_cursor $cursor
+ set n_primary [llength [array names pg_used]]
+ set unused_primary 0
+ foreach x [array names pg_used] {incr unused_primary $pg_used($x)}
+ regsub -all ' $name '' name
+ puts -nonewline "INSERT INTO space_used VALUES('$name'"
+ puts -nonewline ",[expr {$type=="index"}]"
+ puts ",$size,$n_primary,$n_overflow,$unused_primary,$unused_ovfl);"
+ incr total_size $size
+ incr total_primary $n_primary
+ incr total_overflow $n_overflow
+ incr total_unused_primary $unused_primary
+ incr total_unused_ovfl $unused_ovfl
+}
+
+# Output summary statistics:
+#
+puts "-- Total payload size: $total_size"
+puts "-- Total pages used: $total_primary primary and $total_overflow overflow"
+set file_pgcnt [expr {[file size [lindex $argv 0]]/1024}]
+puts -nonewline "-- Total unused bytes on primary pages: $total_unused_primary"
+if {$total_primary>0} {
+ set upp [expr {$total_unused_primary/$total_primary}]
+ puts " (avg $upp bytes/page)"
+} else {
+ puts ""
+}
+puts -nonewline "-- Total unused bytes on overflow pages: $total_unused_ovfl"
+if {$total_overflow>0} {
+ set upp [expr {$total_unused_ovfl/$total_overflow}]
+ puts " (avg $upp bytes/page)"
+} else {
+ puts ""
+}
+set n_free [expr {$file_pgcnt-$total_primary-$total_overflow}]
+if {$n_free>0} {incr n_free -1}
+puts "-- Total pages on freelist: $n_free"
+puts "COMMIT;"
diff --git a/third_party/sqlite/tool/spaceanal.tcl b/third_party/sqlite/tool/spaceanal.tcl
new file mode 100755
index 0000000..3718357
--- /dev/null
+++ b/third_party/sqlite/tool/spaceanal.tcl
@@ -0,0 +1,863 @@
+# Run this TCL script using "testfixture" in order get a report that shows
+# how much disk space is used by a particular data to actually store data
+# versus how much space is unused.
+#
+
+if {[catch {
+
+# Get the name of the database to analyze
+#
+#set argv $argv0
+if {[llength $argv]!=1} {
+ puts stderr "Usage: $argv0 database-name"
+ exit 1
+}
+set file_to_analyze [lindex $argv 0]
+if {![file exists $file_to_analyze]} {
+ puts stderr "No such file: $file_to_analyze"
+ exit 1
+}
+if {![file readable $file_to_analyze]} {
+ puts stderr "File is not readable: $file_to_analyze"
+ exit 1
+}
+if {[file size $file_to_analyze]<512} {
+ puts stderr "Empty or malformed database: $file_to_analyze"
+ exit 1
+}
+
+# Maximum distance between pages before we consider it a "gap"
+#
+set MAXGAP 3
+
+# Open the database
+#
+sqlite3 db [lindex $argv 0]
+set DB [btree_open [lindex $argv 0] 1000 0]
+
+# In-memory database for collecting statistics. This script loops through
+# the tables and indices in the database being analyzed, adding a row for each
+# to an in-memory database (for which the schema is shown below). It then
+# queries the in-memory db to produce the space-analysis report.
+#
+sqlite3 mem :memory:
+set tabledef\
+{CREATE TABLE space_used(
+ name clob, -- Name of a table or index in the database file
+ tblname clob, -- Name of associated table
+ is_index boolean, -- TRUE if it is an index, false for a table
+ nentry int, -- Number of entries in the BTree
+ leaf_entries int, -- Number of leaf entries
+ payload int, -- Total amount of data stored in this table or index
+ ovfl_payload int, -- Total amount of data stored on overflow pages
+ ovfl_cnt int, -- Number of entries that use overflow
+ mx_payload int, -- Maximum payload size
+ int_pages int, -- Number of interior pages used
+ leaf_pages int, -- Number of leaf pages used
+ ovfl_pages int, -- Number of overflow pages used
+ int_unused int, -- Number of unused bytes on interior pages
+ leaf_unused int, -- Number of unused bytes on primary pages
+ ovfl_unused int, -- Number of unused bytes on overflow pages
+ gap_cnt int -- Number of gaps in the page layout
+);}
+mem eval $tabledef
+
+proc integerify {real} {
+ if {[string is double -strict $real]} {
+ return [expr {int($real)}]
+ } else {
+ return 0
+ }
+}
+mem function int integerify
+
+# Quote a string for use in an SQL query. Examples:
+#
+# [quote {hello world}] == {'hello world'}
+# [quote {hello world's}] == {'hello world''s'}
+#
+proc quote {txt} {
+ regsub -all ' $txt '' q
+ return '$q'
+}
+
+# This proc is a wrapper around the btree_cursor_info command. The
+# second argument is an open btree cursor returned by [btree_cursor].
+# The first argument is the name of an array variable that exists in
+# the scope of the caller. If the third argument is non-zero, then
+# info is returned for the page that lies $up entries upwards in the
+# tree-structure. (i.e. $up==1 returns the parent page, $up==2 the
+# grandparent etc.)
+#
+# The following entries in that array are filled in with information retrieved
+# using [btree_cursor_info]:
+#
+# $arrayvar(page_no) = The page number
+# $arrayvar(entry_no) = The entry number
+# $arrayvar(page_entries) = Total number of entries on this page
+# $arrayvar(cell_size) = Cell size (local payload + header)
+# $arrayvar(page_freebytes) = Number of free bytes on this page
+# $arrayvar(page_freeblocks) = Number of free blocks on the page
+# $arrayvar(payload_bytes) = Total payload size (local + overflow)
+# $arrayvar(header_bytes) = Header size in bytes
+# $arrayvar(local_payload_bytes) = Local payload size
+# $arrayvar(parent) = Parent page number
+#
+proc cursor_info {arrayvar csr {up 0}} {
+ upvar $arrayvar a
+ foreach [list a(page_no) \
+ a(entry_no) \
+ a(page_entries) \
+ a(cell_size) \
+ a(page_freebytes) \
+ a(page_freeblocks) \
+ a(payload_bytes) \
+ a(header_bytes) \
+ a(local_payload_bytes) \
+ a(parent) \
+ a(first_ovfl) ] [btree_cursor_info $csr $up] break
+}
+
+# Determine the page-size of the database. This global variable is used
+# throughout the script.
+#
+set pageSize [db eval {PRAGMA page_size}]
+
+# Analyze every table in the database, one at a time.
+#
+# The following query returns the name and root-page of each table in the
+# database, including the sqlite_master table.
+#
+set sql {
+ SELECT name, rootpage FROM sqlite_master
+ WHERE type='table' AND rootpage>0
+ UNION ALL
+ SELECT 'sqlite_master', 1
+ ORDER BY 1
+}
+set wideZero [expr {10000000000 - 10000000000}]
+foreach {name rootpage} [db eval $sql] {
+ puts stderr "Analyzing table $name..."
+
+ # Code below traverses the table being analyzed (table name $name), using the
+ # btree cursor $cursor. Statistics related to table $name are accumulated in
+ # the following variables:
+ #
+ set total_payload $wideZero ;# Payload space used by all entries
+ set total_ovfl $wideZero ;# Payload space on overflow pages
+ set unused_int $wideZero ;# Unused space on interior nodes
+ set unused_leaf $wideZero ;# Unused space on leaf nodes
+ set unused_ovfl $wideZero ;# Unused space on overflow pages
+ set cnt_ovfl $wideZero ;# Number of entries that use overflows
+ set cnt_leaf_entry $wideZero ;# Number of leaf entries
+ set cnt_int_entry $wideZero ;# Number of interor entries
+ set mx_payload $wideZero ;# Maximum payload size
+ set ovfl_pages $wideZero ;# Number of overflow pages used
+ set leaf_pages $wideZero ;# Number of leaf pages
+ set int_pages $wideZero ;# Number of interior pages
+ set gap_cnt 0 ;# Number of holes in the page sequence
+ set prev_pgno 0 ;# Last page number seen
+
+ # As the btree is traversed, the array variable $seen($pgno) is set to 1
+ # the first time page $pgno is encountered.
+ #
+ catch {unset seen}
+
+ # The following loop runs once for each entry in table $name. The table
+ # is traversed using the btree cursor stored in variable $csr
+ #
+ set csr [btree_cursor $DB $rootpage 0]
+ for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} {
+ incr cnt_leaf_entry
+
+ # Retrieve information about the entry the btree-cursor points to into
+ # the array variable $ci (cursor info).
+ #
+ cursor_info ci $csr
+
+ # Check if the payload of this entry is greater than the current
+ # $mx_payload statistic for the table. Also increase the $total_payload
+ # statistic.
+ #
+ if {$ci(payload_bytes)>$mx_payload} {set mx_payload $ci(payload_bytes)}
+ incr total_payload $ci(payload_bytes)
+
+ # If this entry uses overflow pages, then update the $cnt_ovfl,
+ # $total_ovfl, $ovfl_pages and $unused_ovfl statistics.
+ #
+ set ovfl [expr {$ci(payload_bytes)-$ci(local_payload_bytes)}]
+ if {$ovfl} {
+ incr cnt_ovfl
+ incr total_ovfl $ovfl
+ set n [expr {int(ceil($ovfl/($pageSize-4.0)))}]
+ incr ovfl_pages $n
+ incr unused_ovfl [expr {$n*($pageSize-4) - $ovfl}]
+ set pglist [btree_ovfl_info $DB $csr]
+ } else {
+ set pglist {}
+ }
+
+ # If this is the first table entry analyzed for the page, then update
+ # the page-related statistics $leaf_pages and $unused_leaf. Also, if
+ # this page has a parent page that has not been analyzed, retrieve
+ # info for the parent and update statistics for it too.
+ #
+ if {![info exists seen($ci(page_no))]} {
+ set seen($ci(page_no)) 1
+ incr leaf_pages
+ incr unused_leaf $ci(page_freebytes)
+ set pglist "$ci(page_no) $pglist"
+
+ # Now check if the page has a parent that has not been analyzed. If
+ # so, update the $int_pages, $cnt_int_entry and $unused_int statistics
+ # accordingly. Then check if the parent page has a parent that has
+ # not yet been analyzed etc.
+ #
+ # set parent $ci(parent_page_no)
+ for {set up 1} \
+ {$ci(parent)!=0 && ![info exists seen($ci(parent))]} {incr up} \
+ {
+ # Mark the parent as seen.
+ #
+ set seen($ci(parent)) 1
+
+ # Retrieve info for the parent and update statistics.
+ cursor_info ci $csr $up
+ incr int_pages
+ incr cnt_int_entry $ci(page_entries)
+ incr unused_int $ci(page_freebytes)
+
+ # parent pages come before their first child
+ set pglist "$ci(page_no) $pglist"
+ }
+ }
+
+ # Check the page list for fragmentation
+ #
+ foreach pg $pglist {
+ if {$pg!=$prev_pgno+1 && $prev_pgno>0} {
+ incr gap_cnt
+ }
+ set prev_pgno $pg
+ }
+ }
+ btree_close_cursor $csr
+
+ # Handle the special case where a table contains no data. In this case
+ # all statistics are zero, except for the number of leaf pages (1) and
+ # the unused bytes on leaf pages ($pageSize - 8).
+ #
+ # An exception to the above is the sqlite_master table. If it is empty
+ # then all statistics are zero except for the number of leaf pages (1),
+ # and the number of unused bytes on leaf pages ($pageSize - 112).
+ #
+ if {[llength [array names seen]]==0} {
+ set leaf_pages 1
+ if {$rootpage==1} {
+ set unused_leaf [expr {$pageSize-112}]
+ } else {
+ set unused_leaf [expr {$pageSize-8}]
+ }
+ }
+
+ # Insert the statistics for the table analyzed into the in-memory database.
+ #
+ set sql "INSERT INTO space_used VALUES("
+ append sql [quote $name]
+ append sql ",[quote $name]"
+ append sql ",0"
+ append sql ",[expr {$cnt_leaf_entry+$cnt_int_entry}]"
+ append sql ",$cnt_leaf_entry"
+ append sql ",$total_payload"
+ append sql ",$total_ovfl"
+ append sql ",$cnt_ovfl"
+ append sql ",$mx_payload"
+ append sql ",$int_pages"
+ append sql ",$leaf_pages"
+ append sql ",$ovfl_pages"
+ append sql ",$unused_int"
+ append sql ",$unused_leaf"
+ append sql ",$unused_ovfl"
+ append sql ",$gap_cnt"
+ append sql );
+ mem eval $sql
+}
+
+# Analyze every index in the database, one at a time.
+#
+# The query below returns the name, associated table and root-page number
+# for every index in the database.
+#
+set sql {
+ SELECT name, tbl_name, rootpage FROM sqlite_master WHERE type='index'
+ ORDER BY 2, 1
+}
+foreach {name tbl_name rootpage} [db eval $sql] {
+ puts stderr "Analyzing index $name of table $tbl_name..."
+
+ # Code below traverses the index being analyzed (index name $name), using the
+ # btree cursor $cursor. Statistics related to index $name are accumulated in
+ # the following variables:
+ #
+ set total_payload $wideZero ;# Payload space used by all entries
+ set total_ovfl $wideZero ;# Payload space on overflow pages
+ set unused_leaf $wideZero ;# Unused space on leaf nodes
+ set unused_ovfl $wideZero ;# Unused space on overflow pages
+ set cnt_ovfl $wideZero ;# Number of entries that use overflows
+ set cnt_leaf_entry $wideZero ;# Number of leaf entries
+ set mx_payload $wideZero ;# Maximum payload size
+ set ovfl_pages $wideZero ;# Number of overflow pages used
+ set leaf_pages $wideZero ;# Number of leaf pages
+ set gap_cnt 0 ;# Number of holes in the page sequence
+ set prev_pgno 0 ;# Last page number seen
+
+ # As the btree is traversed, the array variable $seen($pgno) is set to 1
+ # the first time page $pgno is encountered.
+ #
+ catch {unset seen}
+
+ # The following loop runs once for each entry in index $name. The index
+ # is traversed using the btree cursor stored in variable $csr
+ #
+ set csr [btree_cursor $DB $rootpage 0]
+ for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} {
+ incr cnt_leaf_entry
+
+ # Retrieve information about the entry the btree-cursor points to into
+ # the array variable $ci (cursor info).
+ #
+ cursor_info ci $csr
+
+ # Check if the payload of this entry is greater than the current
+ # $mx_payload statistic for the table. Also increase the $total_payload
+ # statistic.
+ #
+ set payload [btree_keysize $csr]
+ if {$payload>$mx_payload} {set mx_payload $payload}
+ incr total_payload $payload
+
+ # If this entry uses overflow pages, then update the $cnt_ovfl,
+ # $total_ovfl, $ovfl_pages and $unused_ovfl statistics.
+ #
+ set ovfl [expr {$payload-$ci(local_payload_bytes)}]
+ if {$ovfl} {
+ incr cnt_ovfl
+ incr total_ovfl $ovfl
+ set n [expr {int(ceil($ovfl/($pageSize-4.0)))}]
+ incr ovfl_pages $n
+ incr unused_ovfl [expr {$n*($pageSize-4) - $ovfl}]
+ }
+
+ # If this is the first table entry analyzed for the page, then update
+ # the page-related statistics $leaf_pages and $unused_leaf.
+ #
+ if {![info exists seen($ci(page_no))]} {
+ set seen($ci(page_no)) 1
+ incr leaf_pages
+ incr unused_leaf $ci(page_freebytes)
+ set pg $ci(page_no)
+ if {$prev_pgno>0 && $pg!=$prev_pgno+1} {
+ incr gap_cnt
+ }
+ set prev_pgno $ci(page_no)
+ }
+ }
+ btree_close_cursor $csr
+
+ # Handle the special case where a index contains no data. In this case
+ # all statistics are zero, except for the number of leaf pages (1) and
+ # the unused bytes on leaf pages ($pageSize - 8).
+ #
+ if {[llength [array names seen]]==0} {
+ set leaf_pages 1
+ set unused_leaf [expr {$pageSize-8}]
+ }
+
+ # Insert the statistics for the index analyzed into the in-memory database.
+ #
+ set sql "INSERT INTO space_used VALUES("
+ append sql [quote $name]
+ append sql ",[quote $tbl_name]"
+ append sql ",1"
+ append sql ",$cnt_leaf_entry"
+ append sql ",$cnt_leaf_entry"
+ append sql ",$total_payload"
+ append sql ",$total_ovfl"
+ append sql ",$cnt_ovfl"
+ append sql ",$mx_payload"
+ append sql ",0"
+ append sql ",$leaf_pages"
+ append sql ",$ovfl_pages"
+ append sql ",0"
+ append sql ",$unused_leaf"
+ append sql ",$unused_ovfl"
+ append sql ",$gap_cnt"
+ append sql );
+ mem eval $sql
+}
+
+# Generate a single line of output in the statistics section of the
+# report.
+#
+proc statline {title value {extra {}}} {
+ set len [string length $title]
+ set dots [string range {......................................} $len end]
+ set len [string length $value]
+ set sp2 [string range { } $len end]
+ if {$extra ne ""} {
+ set extra " $extra"
+ }
+ puts "$title$dots $value$sp2$extra"
+}
+
+# Generate a formatted percentage value for $num/$denom
+#
+proc percent {num denom {of {}}} {
+ if {$denom==0.0} {return ""}
+ set v [expr {$num*100.0/$denom}]
+ set of {}
+ if {$v==100.0 || $v<0.001 || ($v>1.0 && $v<99.0)} {
+ return [format {%5.1f%% %s} $v $of]
+ } elseif {$v<0.1 || $v>99.9} {
+ return [format {%7.3f%% %s} $v $of]
+ } else {
+ return [format {%6.2f%% %s} $v $of]
+ }
+}
+
+proc divide {num denom} {
+ if {$denom==0} {return 0.0}
+ return [format %.2f [expr double($num)/double($denom)]]
+}
+
+# Generate a subreport that covers some subset of the database.
+# the $where clause determines which subset to analyze.
+#
+proc subreport {title where} {
+ global pageSize file_pgcnt
+
+ # Query the in-memory database for the sum of various statistics
+ # for the subset of tables/indices identified by the WHERE clause in
+ # $where. Note that even if the WHERE clause matches no rows, the
+ # following query returns exactly one row (because it is an aggregate).
+ #
+ # The results of the query are stored directly by SQLite into local
+ # variables (i.e. $nentry, $nleaf etc.).
+ #
+ mem eval "
+ SELECT
+ int(sum(nentry)) AS nentry,
+ int(sum(leaf_entries)) AS nleaf,
+ int(sum(payload)) AS payload,
+ int(sum(ovfl_payload)) AS ovfl_payload,
+ max(mx_payload) AS mx_payload,
+ int(sum(ovfl_cnt)) as ovfl_cnt,
+ int(sum(leaf_pages)) AS leaf_pages,
+ int(sum(int_pages)) AS int_pages,
+ int(sum(ovfl_pages)) AS ovfl_pages,
+ int(sum(leaf_unused)) AS leaf_unused,
+ int(sum(int_unused)) AS int_unused,
+ int(sum(ovfl_unused)) AS ovfl_unused,
+ int(sum(gap_cnt)) AS gap_cnt
+ FROM space_used WHERE $where" {} {}
+
+ # Output the sub-report title, nicely decorated with * characters.
+ #
+ puts ""
+ set len [string length $title]
+ set stars [string repeat * [expr 65-$len]]
+ puts "*** $title $stars"
+ puts ""
+
+ # Calculate statistics and store the results in TCL variables, as follows:
+ #
+ # total_pages: Database pages consumed.
+ # total_pages_percent: Pages consumed as a percentage of the file.
+ # storage: Bytes consumed.
+ # payload_percent: Payload bytes used as a percentage of $storage.
+ # total_unused: Unused bytes on pages.
+ # avg_payload: Average payload per btree entry.
+ # avg_fanout: Average fanout for internal pages.
+ # avg_unused: Average unused bytes per btree entry.
+ # ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
+ #
+ set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
+ set total_pages_percent [percent $total_pages $file_pgcnt]
+ set storage [expr {$total_pages*$pageSize}]
+ set payload_percent [percent $payload $storage {of storage consumed}]
+ set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
+ set avg_payload [divide $payload $nleaf]
+ set avg_unused [divide $total_unused $nleaf]
+ if {$int_pages>0} {
+ # TODO: Is this formula correct?
+ set nTab [mem eval "
+ SELECT count(*) FROM (
+ SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0
+ )
+ "]
+ set avg_fanout [mem eval "
+ SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
+ WHERE $where AND is_index = 0
+ "]
+ set avg_fanout [format %.2f $avg_fanout]
+ }
+ set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}]
+
+ # Print out the sub-report statistics.
+ #
+ statline {Percentage of total database} $total_pages_percent
+ statline {Number of entries} $nleaf
+ statline {Bytes of storage consumed} $storage
+ statline {Bytes of payload} $payload $payload_percent
+ statline {Average payload per entry} $avg_payload
+ statline {Average unused bytes per entry} $avg_unused
+ if {[info exists avg_fanout]} {
+ statline {Average fanout} $avg_fanout
+ }
+ if {$total_pages>1} {
+ set fragmentation [percent $gap_cnt [expr {$total_pages-1}] {fragmentation}]
+ statline {Fragmentation} $fragmentation
+ }
+ statline {Maximum payload per entry} $mx_payload
+ statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
+ if {$int_pages>0} {
+ statline {Index pages used} $int_pages
+ }
+ statline {Primary pages used} $leaf_pages
+ statline {Overflow pages used} $ovfl_pages
+ statline {Total pages used} $total_pages
+ if {$int_unused>0} {
+ set int_unused_percent \
+ [percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
+ statline "Unused bytes on index pages" $int_unused $int_unused_percent
+ }
+ statline "Unused bytes on primary pages" $leaf_unused \
+ [percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
+ statline "Unused bytes on overflow pages" $ovfl_unused \
+ [percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
+ statline "Unused bytes on all pages" $total_unused \
+ [percent $total_unused $storage {of all space}]
+ return 1
+}
+
+# Calculate the overhead in pages caused by auto-vacuum.
+#
+# This procedure calculates and returns the number of pages used by the
+# auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
+# then 0 is returned. The two arguments are the size of the database file in
+# pages and the page size used by the database (in bytes).
+proc autovacuum_overhead {filePages pageSize} {
+
+ # Read the value of meta 4. If non-zero, then the database supports
+ # auto-vacuum. It would be possible to use "PRAGMA auto_vacuum" instead,
+ # but that would not work if the SQLITE_OMIT_PRAGMA macro was defined
+ # when the library was built.
+ set meta4 [lindex [btree_get_meta $::DB] 4]
+
+ # If the database is not an auto-vacuum database or the file consists
+ # of one page only then there is no overhead for auto-vacuum. Return zero.
+ if {0==$meta4 || $filePages==1} {
+ return 0
+ }
+
+ # The number of entries on each pointer map page. The layout of the
+ # database file is one pointer-map page, followed by $ptrsPerPage other
+ # pages, followed by a pointer-map page etc. The first pointer-map page
+ # is the second page of the file overall.
+ set ptrsPerPage [expr double($pageSize/5)]
+
+ # Return the number of pointer map pages in the database.
+ return [expr int(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
+}
+
+
+# Calculate the summary statistics for the database and store the results
+# in TCL variables. They are output below. Variables are as follows:
+#
+# pageSize: Size of each page in bytes.
+# file_bytes: File size in bytes.
+# file_pgcnt: Number of pages in the file.
+# file_pgcnt2: Number of pages in the file (calculated).
+# av_pgcnt: Pages consumed by the auto-vacuum pointer-map.
+# av_percent: Percentage of the file consumed by auto-vacuum pointer-map.
+# inuse_pgcnt: Data pages in the file.
+# inuse_percent: Percentage of pages used to store data.
+# free_pgcnt: Free pages calculated as (<total pages> - <in-use pages>)
+# free_pgcnt2: Free pages in the file according to the file header.
+# free_percent: Percentage of file consumed by free pages (calculated).
+# free_percent2: Percentage of file consumed by free pages (header).
+# ntable: Number of tables in the db.
+# nindex: Number of indices in the db.
+# nautoindex: Number of indices created automatically.
+# nmanindex: Number of indices created manually.
+# user_payload: Number of bytes of payload in table btrees
+# (not including sqlite_master)
+# user_percent: $user_payload as a percentage of total file size.
+
+set file_bytes [file size $file_to_analyze]
+set file_pgcnt [expr {$file_bytes/$pageSize}]
+
+set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize]
+set av_percent [percent $av_pgcnt $file_pgcnt]
+
+set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
+set inuse_pgcnt [expr int([mem eval $sql])]
+set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
+
+set free_pgcnt [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt]
+set free_percent [percent $free_pgcnt $file_pgcnt]
+set free_pgcnt2 [lindex [btree_get_meta $DB] 0]
+set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
+
+set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
+
+set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
+set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
+set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
+set nautoindex [db eval $sql]
+set nmanindex [expr {$nindex-$nautoindex}]
+
+# set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
+set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
+ WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
+set user_percent [percent $user_payload $file_bytes]
+
+# Output the summary statistics calculated above.
+#
+puts "/** Disk-Space Utilization Report For $file_to_analyze"
+catch {
+ puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]"
+}
+puts ""
+statline {Page size in bytes} $pageSize
+statline {Pages in the whole file (measured)} $file_pgcnt
+statline {Pages in the whole file (calculated)} $file_pgcnt2
+statline {Pages that store data} $inuse_pgcnt $inuse_percent
+statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
+statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
+statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
+statline {Number of tables in the database} $ntable
+statline {Number of indices} $nindex
+statline {Number of named indices} $nmanindex
+statline {Automatically generated indices} $nautoindex
+statline {Size of the file in bytes} $file_bytes
+statline {Bytes of user payload stored} $user_payload $user_percent
+
+# Output table rankings
+#
+puts ""
+puts "*** Page counts for all tables with their indices ********************"
+puts ""
+mem eval {SELECT tblname, count(*) AS cnt,
+ int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
+ FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
+ statline [string toupper $tblname] $size [percent $size $file_pgcnt]
+}
+
+# Output subreports
+#
+if {$nindex>0} {
+ subreport {All tables and indices} 1
+}
+subreport {All tables} {NOT is_index}
+if {$nindex>0} {
+ subreport {All indices} {is_index}
+}
+foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index
+ ORDER BY name}] {
+ regsub ' $tbl '' qn
+ set name [string toupper $tbl]
+ set n [mem eval "SELECT count(*) FROM space_used WHERE tblname='$qn'"]
+ if {$n>1} {
+ subreport "Table $name and all its indices" "tblname='$qn'"
+ subreport "Table $name w/o any indices" "name='$qn'"
+ subreport "Indices of table $name" "tblname='$qn' AND is_index"
+ } else {
+ subreport "Table $name" "name='$qn'"
+ }
+}
+
+# Output instructions on what the numbers above mean.
+#
+puts {
+*** Definitions ******************************************************
+
+Page size in bytes
+
+ The number of bytes in a single page of the database file.
+ Usually 1024.
+
+Number of pages in the whole file
+}
+puts \
+" The number of $pageSize-byte pages that go into forming the complete
+ database"
+puts \
+{
+Pages that store data
+
+ The number of pages that store data, either as primary B*Tree pages or
+ as overflow pages. The number at the right is the data pages divided by
+ the total number of pages in the file.
+
+Pages on the freelist
+
+ The number of pages that are not currently in use but are reserved for
+ future use. The percentage at the right is the number of freelist pages
+ divided by the total number of pages in the file.
+
+Pages of auto-vacuum overhead
+
+ The number of pages that store data used by the database to facilitate
+ auto-vacuum. This is zero for databases that do not support auto-vacuum.
+
+Number of tables in the database
+
+ The number of tables in the database, including the SQLITE_MASTER table
+ used to store schema information.
+
+Number of indices
+
+ The total number of indices in the database.
+
+Number of named indices
+
+ The number of indices created using an explicit CREATE INDEX statement.
+
+Automatically generated indices
+
+ The number of indices used to implement PRIMARY KEY or UNIQUE constraints
+ on tables.
+
+Size of the file in bytes
+
+ The total amount of disk space used by the entire database files.
+
+Bytes of user payload stored
+
+ The total number of bytes of user payload stored in the database. The
+ schema information in the SQLITE_MASTER table is not counted when
+ computing this number. The percentage at the right shows the payload
+ divided by the total file size.
+
+Percentage of total database
+
+ The amount of the complete database file that is devoted to storing
+ information described by this category.
+
+Number of entries
+
+ The total number of B-Tree key/value pairs stored under this category.
+
+Bytes of storage consumed
+
+ The total amount of disk space required to store all B-Tree entries
+ under this category. The is the total number of pages used times
+ the pages size.
+
+Bytes of payload
+
+ The amount of payload stored under this category. Payload is the data
+ part of table entries and the key part of index entries. The percentage
+ at the right is the bytes of payload divided by the bytes of storage
+ consumed.
+
+Average payload per entry
+
+ The average amount of payload on each entry. This is just the bytes of
+ payload divided by the number of entries.
+
+Average unused bytes per entry
+
+ The average amount of free space remaining on all pages under this
+ category on a per-entry basis. This is the number of unused bytes on
+ all pages divided by the number of entries.
+
+Fragmentation
+
+ The percentage of pages in the table or index that are not
+ consecutive in the disk file. Many filesystems are optimized
+ for sequential file access so smaller fragmentation numbers
+ sometimes result in faster queries, especially for larger
+ database files that do not fit in the disk cache.
+
+Maximum payload per entry
+
+ The largest payload size of any entry.
+
+Entries that use overflow
+
+ The number of entries that user one or more overflow pages.
+
+Total pages used
+
+ This is the number of pages used to hold all information in the current
+ category. This is the sum of index, primary, and overflow pages.
+
+Index pages used
+
+ This is the number of pages in a table B-tree that hold only key (rowid)
+ information and no data.
+
+Primary pages used
+
+ This is the number of B-tree pages that hold both key and data.
+
+Overflow pages used
+
+ The total number of overflow pages used for this category.
+
+Unused bytes on index pages
+
+ The total number of bytes of unused space on all index pages. The
+ percentage at the right is the number of unused bytes divided by the
+ total number of bytes on index pages.
+
+Unused bytes on primary pages
+
+ The total number of bytes of unused space on all primary pages. The
+ percentage at the right is the number of unused bytes divided by the
+ total number of bytes on primary pages.
+
+Unused bytes on overflow pages
+
+ The total number of bytes of unused space on all overflow pages. The
+ percentage at the right is the number of unused bytes divided by the
+ total number of bytes on overflow pages.
+
+Unused bytes on all pages
+
+ The total number of bytes of unused space on all primary and overflow
+ pages. The percentage at the right is the number of unused bytes
+ divided by the total number of bytes.
+}
+
+# Output a dump of the in-memory database. This can be used for more
+# complex offline analysis.
+#
+puts "**********************************************************************"
+puts "The entire text of this report can be sourced into any SQL database"
+puts "engine for further analysis. All of the text above is an SQL comment."
+puts "The data used to generate this report follows:"
+puts "*/"
+puts "BEGIN;"
+puts $tabledef
+unset -nocomplain x
+mem eval {SELECT * FROM space_used} x {
+ puts -nonewline "INSERT INTO space_used VALUES"
+ set sep (
+ foreach col $x(*) {
+ set v $x($col)
+ if {$v=="" || ![string is double $v]} {set v [quote $v]}
+ puts -nonewline $sep$v
+ set sep ,
+ }
+ puts ");"
+}
+puts "COMMIT;"
+
+} err]} {
+ puts "ERROR: $err"
+ puts $errorInfo
+ exit 1
+}
diff --git a/third_party/sqlite/tool/speedtest.tcl b/third_party/sqlite/tool/speedtest.tcl
new file mode 100755
index 0000000..ef39dc5
--- /dev/null
+++ b/third_party/sqlite/tool/speedtest.tcl
@@ -0,0 +1,275 @@
+#!/usr/bin/tclsh
+#
+# Run this script using TCLSH to do a speed comparison between
+# various versions of SQLite and PostgreSQL and MySQL
+#
+
+# Run a test
+#
+set cnt 1
+proc runtest {title} {
+ global cnt
+ set sqlfile test$cnt.sql
+ puts "<h2>Test $cnt: $title</h2>"
+ incr cnt
+ set fd [open $sqlfile r]
+ set sql [string trim [read $fd [file size $sqlfile]]]
+ close $fd
+ set sx [split $sql \n]
+ set n [llength $sx]
+ if {$n>8} {
+ set sql {}
+ for {set i 0} {$i<3} {incr i} {append sql [lindex $sx $i]<br>\n}
+ append sql "<i>... [expr {$n-6}] lines omitted</i><br>\n"
+ for {set i [expr {$n-3}]} {$i<$n} {incr i} {
+ append sql [lindex $sx $i]<br>\n
+ }
+ } else {
+ regsub -all \n [string trim $sql] <br> sql
+ }
+ puts "<blockquote>"
+ puts "$sql"
+ puts "</blockquote><table border=0 cellpadding=0 cellspacing=0>"
+ set format {<tr><td>%s</td><td align="right">&nbsp;&nbsp;&nbsp;%.3f</td></tr>}
+ set delay 1000
+# exec sync; after $delay;
+# set t [time "exec psql drh <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format PostgreSQL: $t]
+ exec sync; after $delay;
+ set t [time "exec mysql -f drh <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format MySQL: $t]
+# set t [time "exec ./sqlite232 s232.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.3.2:} $t]
+# set t [time "exec ./sqlite-100 s100.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.4 (cache=100):} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite248 s2k.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4.8:} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite248 sns.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4.8 (nosync):} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite2412 s2kb.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4.12:} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite2412 snsb.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4.12 (nosync):} $t]
+# set t [time "exec ./sqlite-t1 st1.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.4 (test):} $t]
+ puts "</table>"
+}
+
+# Initialize the environment
+#
+expr srand(1)
+catch {exec /bin/sh -c {rm -f s*.db}}
+set fd [open clear.sql w]
+puts $fd {
+ drop table t1;
+ drop table t2;
+}
+close $fd
+catch {exec psql drh <clear.sql}
+catch {exec mysql drh <clear.sql}
+set fd [open 2kinit.sql w]
+puts $fd {
+ PRAGMA default_cache_size=2000;
+ PRAGMA default_synchronous=on;
+}
+close $fd
+exec ./sqlite248 s2k.db <2kinit.sql
+exec ./sqlite2412 s2kb.db <2kinit.sql
+set fd [open nosync-init.sql w]
+puts $fd {
+ PRAGMA default_cache_size=2000;
+ PRAGMA default_synchronous=off;
+}
+close $fd
+exec ./sqlite248 sns.db <nosync-init.sql
+exec ./sqlite2412 snsb.db <nosync-init.sql
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));"
+for {set i 1} {$i<=1000} {incr i} {
+ set r [expr {int(rand()*100000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+close $fd
+runtest {1000 INSERTs}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+puts $fd "CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100));"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 0} {$i<100} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+10)*100}]
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
+}
+close $fd
+runtest {100 SELECTs without an index}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 1} {$i<=100} {incr i} {
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%[number_name $i]%';"
+}
+close $fd
+runtest {100 SELECTs on a string comparison}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {CREATE INDEX i2a ON t2(a);}
+puts $fd {CREATE INDEX i2b ON t2(b);}
+close $fd
+runtest {Creating an index}
+
+
+
+set fd [open test$cnt.sql w]
+for {set i 0} {$i<5000} {incr i} {
+ set lwr [expr {$i*100}]
+ set upr [expr {($i+1)*100}]
+ puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
+}
+close $fd
+runtest {5000 SELECTs with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 0} {$i<1000} {incr i} {
+ set lwr [expr {$i*10}]
+ set upr [expr {($i+1)*10}]
+ puts $fd "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {1000 UPDATEs without an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "UPDATE t2 SET b=$r WHERE a=$i;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 UPDATEs with an index}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "UPDATE t2 SET c='[number_name $r]' WHERE a=$i;"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 text UPDATEs with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+puts $fd "INSERT INTO t1 SELECT * FROM t2;"
+puts $fd "INSERT INTO t2 SELECT * FROM t1;"
+puts $fd "COMMIT;"
+close $fd
+runtest {INSERTs from a SELECT}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DELETE FROM t2 WHERE c LIKE '%fifty%';}
+close $fd
+runtest {DELETE without an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DELETE FROM t2 WHERE a>10 AND a<20000;}
+close $fd
+runtest {DELETE with an index}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {INSERT INTO t2 SELECT * FROM t1;}
+close $fd
+runtest {A big INSERT after a big DELETE}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {BEGIN;}
+puts $fd {DELETE FROM t1;}
+for {set i 1} {$i<=3000} {incr i} {
+ set r [expr {int(rand()*100000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd {COMMIT;}
+close $fd
+runtest {A big DELETE followed by many small INSERTs}
+
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DROP TABLE t1;}
+puts $fd {DROP TABLE t2;}
+close $fd
+runtest {DROP TABLE}
diff --git a/third_party/sqlite/tool/speedtest16.c b/third_party/sqlite/tool/speedtest16.c
new file mode 100755
index 0000000..e81f1a6
--- /dev/null
+++ b/third_party/sqlite/tool/speedtest16.c
@@ -0,0 +1,169 @@
+/*
+** Performance test for SQLite.
+**
+** This program reads ASCII text from a file named on the command-line.
+** It converts each SQL statement into UTF16 and submits it to SQLite
+** for evaluation. A new UTF16 database is created at the beginning of
+** the program. All statements are timed using the high-resolution timer
+** built into Intel-class processors.
+**
+** To compile this program, first compile the SQLite library separately
+** will full optimizations. For example:
+**
+** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
+**
+** Then link against this program. But to do optimize this program
+** because that defeats the hi-res timer.
+**
+** gcc speedtest16.c sqlite3.o -ldl -I../src
+**
+** Then run this program with a single argument which is the name of
+** a file containing SQL script that you want to test:
+**
+** ./a.out database.db test.sql
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "sqlite3.h"
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+#include "hwtime.h"
+
+/*
+** Convert a zero-terminated ASCII string into a zero-terminated
+** UTF-16le string. Memory to hold the returned string comes
+** from malloc() and should be freed by the caller.
+*/
+static void *asciiToUtf16le(const char *z){
+ int n = strlen(z);
+ char *z16;
+ int i, j;
+
+ z16 = malloc( n*2 + 2 );
+ for(i=j=0; i<=n; i++){
+ z16[j++] = z[i];
+ z16[j++] = 0;
+ }
+ return (void*)z16;
+}
+
+/*
+** Timers
+*/
+static sqlite_uint64 prepTime = 0;
+static sqlite_uint64 runTime = 0;
+static sqlite_uint64 finalizeTime = 0;
+
+/*
+** Prepare and run a single statement of SQL.
+*/
+static void prepareAndRun(sqlite3 *db, const char *zSql){
+ void *utf16;
+ sqlite3_stmt *pStmt;
+ const void *stmtTail;
+ sqlite_uint64 iStart, iElapse;
+ int rc;
+
+ printf("****************************************************************\n");
+ printf("SQL statement: [%s]\n", zSql);
+ utf16 = asciiToUtf16le(zSql);
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_prepare16_v2(db, utf16, -1, &pStmt, &stmtTail);
+ iElapse = sqlite3Hwtime() - iStart;
+ prepTime += iElapse;
+ printf("sqlite3_prepare16_v2() returns %d in %llu cycles\n", rc, iElapse);
+ if( rc==SQLITE_OK ){
+ int nRow = 0;
+ iStart = sqlite3Hwtime();
+ while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
+ iElapse = sqlite3Hwtime() - iStart;
+ runTime += iElapse;
+ printf("sqlite3_step() returns %d after %d rows in %llu cycles\n",
+ rc, nRow, iElapse);
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_finalize(pStmt);
+ iElapse = sqlite3Hwtime() - iStart;
+ finalizeTime += iElapse;
+ printf("sqlite3_finalize() returns %d in %llu cycles\n", rc, iElapse);
+ }
+ free(utf16);
+}
+
+int main(int argc, char **argv){
+ void *utf16;
+ sqlite3 *db;
+ int rc;
+ int nSql;
+ char *zSql;
+ int i, j;
+ FILE *in;
+ sqlite_uint64 iStart, iElapse;
+ sqlite_uint64 iSetup = 0;
+ int nStmt = 0;
+ int nByte = 0;
+
+ if( argc!=3 ){
+ fprintf(stderr, "Usage: %s FILENAME SQL-SCRIPT\n"
+ "Runs SQL-SCRIPT as UTF16 against a UTF16 database\n",
+ argv[0]);
+ exit(1);
+ }
+ in = fopen(argv[2], "r");
+ fseek(in, 0L, SEEK_END);
+ nSql = ftell(in);
+ zSql = malloc( nSql+1 );
+ fseek(in, 0L, SEEK_SET);
+ nSql = fread(zSql, 1, nSql, in);
+ zSql[nSql] = 0;
+
+ printf("SQLite version: %d\n", sqlite3_libversion_number());
+ unlink(argv[1]);
+ utf16 = asciiToUtf16le(argv[1]);
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_open16(utf16, &db);
+ iElapse = sqlite3Hwtime() - iStart;
+ iSetup = iElapse;
+ printf("sqlite3_open16() returns %d in %llu cycles\n", rc, iElapse);
+ free(utf16);
+ for(i=j=0; j<nSql; j++){
+ if( zSql[j]==';' ){
+ int isComplete;
+ char c = zSql[j+1];
+ zSql[j+1] = 0;
+ isComplete = sqlite3_complete(&zSql[i]);
+ zSql[j+1] = c;
+ if( isComplete ){
+ zSql[j] = 0;
+ while( i<j && isspace(zSql[i]) ){ i++; }
+ if( i<j ){
+ nStmt++;
+ nByte += j-i;
+ prepareAndRun(db, &zSql[i]);
+ }
+ zSql[j] = ';';
+ i = j+1;
+ }
+ }
+ }
+ iStart = sqlite3Hwtime();
+ sqlite3_close(db);
+ iElapse = sqlite3Hwtime() - iStart;
+ iSetup += iElapse;
+ printf("sqlite3_close() returns in %llu cycles\n", iElapse);
+ printf("\n");
+ printf("Statements run: %15d\n", nStmt);
+ printf("Bytes of SQL text: %15d\n", nByte);
+ printf("Total prepare time: %15llu cycles\n", prepTime);
+ printf("Total run time: %15llu cycles\n", runTime);
+ printf("Total finalize time: %15llu cycles\n", finalizeTime);
+ printf("Open/Close time: %15llu cycles\n", iSetup);
+ printf("Total Time: %15llu cycles\n",
+ prepTime + runTime + finalizeTime + iSetup);
+ return 0;
+}
diff --git a/third_party/sqlite/tool/speedtest2.tcl b/third_party/sqlite/tool/speedtest2.tcl
new file mode 100755
index 0000000..4fd632d
--- /dev/null
+++ b/third_party/sqlite/tool/speedtest2.tcl
@@ -0,0 +1,207 @@
+#!/usr/bin/tclsh
+#
+# Run this script using TCLSH to do a speed comparison between
+# various versions of SQLite and PostgreSQL and MySQL
+#
+
+# Run a test
+#
+set cnt 1
+proc runtest {title} {
+ global cnt
+ set sqlfile test$cnt.sql
+ puts "<h2>Test $cnt: $title</h2>"
+ incr cnt
+ set fd [open $sqlfile r]
+ set sql [string trim [read $fd [file size $sqlfile]]]
+ close $fd
+ set sx [split $sql \n]
+ set n [llength $sx]
+ if {$n>8} {
+ set sql {}
+ for {set i 0} {$i<3} {incr i} {append sql [lindex $sx $i]<br>\n}
+ append sql "<i>... [expr {$n-6}] lines omitted</i><br>\n"
+ for {set i [expr {$n-3}]} {$i<$n} {incr i} {
+ append sql [lindex $sx $i]<br>\n
+ }
+ } else {
+ regsub -all \n [string trim $sql] <br> sql
+ }
+ puts "<blockquote>"
+ puts "$sql"
+ puts "</blockquote><table border=0 cellpadding=0 cellspacing=0>"
+ set format {<tr><td>%s</td><td align="right">&nbsp;&nbsp;&nbsp;%.3f</td></tr>}
+ set delay 1000
+ exec sync; after $delay;
+ set t [time "exec psql drh <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format PostgreSQL: $t]
+ exec sync; after $delay;
+ set t [time "exec mysql -f drh <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format MySQL: $t]
+# set t [time "exec ./sqlite232 s232.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.3.2:} $t]
+# set t [time "exec ./sqlite-100 s100.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.4 (cache=100):} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite240 s2k.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4:} $t]
+ exec sync; after $delay;
+ set t [time "exec ./sqlite240 sns.db <$sqlfile" 1]
+ set t [expr {[lindex $t 0]/1000000.0}]
+ puts [format $format {SQLite 2.4 (nosync):} $t]
+# set t [time "exec ./sqlite-t1 st1.db <$sqlfile" 1]
+# set t [expr {[lindex $t 0]/1000000.0}]
+# puts [format $format {SQLite 2.4 (test):} $t]
+ puts "</table>"
+}
+
+# Initialize the environment
+#
+expr srand(1)
+catch {exec /bin/sh -c {rm -f s*.db}}
+set fd [open clear.sql w]
+puts $fd {
+ drop table t1;
+ drop table t2;
+}
+close $fd
+catch {exec psql drh <clear.sql}
+catch {exec mysql drh <clear.sql}
+set fd [open 2kinit.sql w]
+puts $fd {
+ PRAGMA default_cache_size=2000;
+ PRAGMA default_synchronous=on;
+}
+close $fd
+exec ./sqlite240 s2k.db <2kinit.sql
+exec ./sqlite-t1 st1.db <2kinit.sql
+set fd [open nosync-init.sql w]
+puts $fd {
+ PRAGMA default_cache_size=2000;
+ PRAGMA default_synchronous=off;
+}
+close $fd
+exec ./sqlite240 sns.db <nosync-init.sql
+set ones {zero one two three four five six seven eight nine
+ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+ eighteen nineteen}
+set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
+proc number_name {n} {
+ if {$n>=1000} {
+ set txt "[number_name [expr {$n/1000}]] thousand"
+ set n [expr {$n%1000}]
+ } else {
+ set txt {}
+ }
+ if {$n>=100} {
+ append txt " [lindex $::ones [expr {$n/100}]] hundred"
+ set n [expr {$n%100}]
+ }
+ if {$n>=20} {
+ append txt " [lindex $::tens [expr {$n/10}]]"
+ set n [expr {$n%10}]
+ }
+ if {$n>0} {
+ append txt " [lindex $::ones $n]"
+ }
+ set txt [string trim $txt]
+ if {$txt==""} {set txt zero}
+ return $txt
+}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+puts $fd "CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "DELETE FROM t1;"
+close $fd
+runtest {DELETE everything}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "DELETE FROM t1;"
+close $fd
+runtest {DELETE everything}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "DELETE FROM t1;"
+close $fd
+runtest {DELETE everything}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "DELETE FROM t1;"
+close $fd
+runtest {DELETE everything}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "BEGIN;"
+for {set i 1} {$i<=25000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
+}
+puts $fd "COMMIT;"
+close $fd
+runtest {25000 INSERTs in a transaction}
+
+
+set fd [open test$cnt.sql w]
+puts $fd "DELETE FROM t1;"
+close $fd
+runtest {DELETE everything}
+
+
+set fd [open test$cnt.sql w]
+puts $fd {DROP TABLE t1;}
+close $fd
+runtest {DROP TABLE}
diff --git a/third_party/sqlite/tool/speedtest8.c b/third_party/sqlite/tool/speedtest8.c
new file mode 100755
index 0000000..5972947
--- /dev/null
+++ b/third_party/sqlite/tool/speedtest8.c
@@ -0,0 +1,351 @@
+/*
+** Performance test for SQLite.
+**
+** This program reads ASCII text from a file named on the command-line
+** and submits that text to SQLite for evaluation. A new database
+** is created at the beginning of the program. All statements are
+** timed using the high-resolution timer built into Intel-class processors.
+**
+** To compile this program, first compile the SQLite library separately
+** will full optimizations. For example:
+**
+** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
+**
+** Then link against this program. But to do optimize this program
+** because that defeats the hi-res timer.
+**
+** gcc speedtest8.c sqlite3.o -ldl -I../src
+**
+** Then run this program with a single argument which is the name of
+** a file containing SQL script that you want to test:
+**
+** ./a.out test.db test.sql
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+
+#if defined(_MSC_VER)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/times.h>
+#include <sched.h>
+#endif
+
+#include "sqlite3.h"
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+#include "hwtime.h"
+
+/*
+** Timers
+*/
+static sqlite_uint64 prepTime = 0;
+static sqlite_uint64 runTime = 0;
+static sqlite_uint64 finalizeTime = 0;
+
+/*
+** Prepare and run a single statement of SQL.
+*/
+static void prepareAndRun(sqlite3 *db, const char *zSql, int bQuiet){
+ sqlite3_stmt *pStmt;
+ const char *stmtTail;
+ sqlite_uint64 iStart, iElapse;
+ int rc;
+
+ if (!bQuiet){
+ printf("***************************************************************\n");
+ }
+ if (!bQuiet) printf("SQL statement: [%s]\n", zSql);
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &stmtTail);
+ iElapse = sqlite3Hwtime() - iStart;
+ prepTime += iElapse;
+ if (!bQuiet){
+ printf("sqlite3_prepare_v2() returns %d in %llu cycles\n", rc, iElapse);
+ }
+ if( rc==SQLITE_OK ){
+ int nRow = 0;
+ iStart = sqlite3Hwtime();
+ while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
+ iElapse = sqlite3Hwtime() - iStart;
+ runTime += iElapse;
+ if (!bQuiet){
+ printf("sqlite3_step() returns %d after %d rows in %llu cycles\n",
+ rc, nRow, iElapse);
+ }
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_finalize(pStmt);
+ iElapse = sqlite3Hwtime() - iStart;
+ finalizeTime += iElapse;
+ if (!bQuiet){
+ printf("sqlite3_finalize() returns %d in %llu cycles\n", rc, iElapse);
+ }
+ }
+}
+
+/***************************************************************************
+** The "overwrite" VFS is an overlay over the default VFS. It modifies
+** the xTruncate operation on journal files so that xTruncate merely
+** writes zeros into the first 50 bytes of the file rather than truely
+** truncating the file.
+**
+** The following variables are initialized to be the virtual function
+** tables for the overwrite VFS.
+*/
+static sqlite3_vfs overwrite_vfs;
+static sqlite3_io_methods overwrite_methods;
+
+/*
+** The truncate method for journal files in the overwrite VFS.
+*/
+static int overwriteTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ int rc;
+ static const char buf[50];
+ if( size ){
+ return SQLITE_IOERR;
+ }
+ rc = pFile->pMethods->xWrite(pFile, buf, sizeof(buf), 0);
+ if( rc==SQLITE_OK ){
+ rc = pFile->pMethods->xSync(pFile, SQLITE_SYNC_NORMAL);
+ }
+ return rc;
+}
+
+/*
+** The delete method for journal files in the overwrite VFS.
+*/
+static int overwriteDelete(sqlite3_file *pFile){
+ return overwriteTruncate(pFile, 0);
+}
+
+/*
+** The open method for overwrite VFS. If the file being opened is
+** a journal file then substitute the alternative xTruncate method.
+*/
+static int overwriteOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ int rc;
+ sqlite3_vfs *pRealVfs;
+ int isJournal;
+
+ isJournal = (flags & (SQLITE_OPEN_MAIN_JOURNAL|SQLITE_OPEN_TEMP_JOURNAL))!=0;
+ pRealVfs = (sqlite3_vfs*)pVfs->pAppData;
+ rc = pRealVfs->xOpen(pRealVfs, zName, pFile, flags, pOutFlags);
+ if( rc==SQLITE_OK && isJournal ){
+ if( overwrite_methods.xTruncate==0 ){
+ sqlite3_io_methods temp;
+ memcpy(&temp, pFile->pMethods, sizeof(temp));
+ temp.xTruncate = overwriteTruncate;
+ memcpy(&overwrite_methods, &temp, sizeof(temp));
+ }
+ pFile->pMethods = &overwrite_methods;
+ }
+ return rc;
+}
+
+/*
+** Overlay the overwrite VFS over top of the current default VFS
+** and make the overlay VFS the new default.
+**
+** This routine can only be evaluated once. On second and subsequent
+** executions it becomes a no-op.
+*/
+static void registerOverwriteVfs(void){
+ sqlite3_vfs *pBase;
+ if( overwrite_vfs.iVersion ) return;
+ pBase = sqlite3_vfs_find(0);
+ memcpy(&overwrite_vfs, pBase, sizeof(overwrite_vfs));
+ overwrite_vfs.pAppData = pBase;
+ overwrite_vfs.xOpen = overwriteOpen;
+ overwrite_vfs.zName = "overwriteVfs";
+ sqlite3_vfs_register(&overwrite_vfs, 1);
+}
+
+int main(int argc, char **argv){
+ sqlite3 *db;
+ int rc;
+ int nSql;
+ char *zSql;
+ int i, j;
+ FILE *in;
+ sqlite_uint64 iStart, iElapse;
+ sqlite_uint64 iSetup = 0;
+ int nStmt = 0;
+ int nByte = 0;
+ const char *zArgv0 = argv[0];
+ int bQuiet = 0;
+#if !defined(_MSC_VER)
+ struct tms tmsStart, tmsEnd;
+ clock_t clkStart, clkEnd;
+#endif
+
+#ifdef HAVE_OSINST
+ extern sqlite3_vfs *sqlite3_instvfs_binarylog(char *, char *, char *);
+ extern void sqlite3_instvfs_destroy(sqlite3_vfs *);
+ sqlite3_vfs *pVfs = 0;
+#endif
+
+ while (argc>3)
+ {
+ if( argc>3 && strcmp(argv[1], "-overwrite")==0 ){
+ registerOverwriteVfs();
+ argv++;
+ argc--;
+ continue;
+ }
+
+#ifdef HAVE_OSINST
+ if( argc>4 && (strcmp(argv[1], "-log")==0) ){
+ pVfs = sqlite3_instvfs_binarylog("oslog", 0, argv[2]);
+ sqlite3_vfs_register(pVfs, 1);
+ argv += 2;
+ argc -= 2;
+ continue;
+ }
+#endif
+
+ /*
+ ** Increasing the priority slightly above normal can help with
+ ** repeatability of testing. Note that with Cygwin, -5 equates
+ ** to "High", +5 equates to "Low", and anything in between
+ ** equates to "Normal".
+ */
+ if( argc>4 && (strcmp(argv[1], "-priority")==0) ){
+#if defined(_MSC_VER)
+ int new_priority = atoi(argv[2]);
+ if(!SetPriorityClass(GetCurrentProcess(),
+ (new_priority<=-5) ? HIGH_PRIORITY_CLASS :
+ (new_priority<=0) ? ABOVE_NORMAL_PRIORITY_CLASS :
+ (new_priority==0) ? NORMAL_PRIORITY_CLASS :
+ (new_priority<5) ? BELOW_NORMAL_PRIORITY_CLASS :
+ IDLE_PRIORITY_CLASS)){
+ printf ("error setting priority\n");
+ exit(2);
+ }
+#else
+ struct sched_param myParam;
+ sched_getparam(0, &myParam);
+ printf ("Current process priority is %d.\n", (int)myParam.sched_priority);
+ myParam.sched_priority = atoi(argv[2]);
+ printf ("Setting process priority to %d.\n", (int)myParam.sched_priority);
+ if (sched_setparam (0, &myParam) != 0){
+ printf ("error setting priority\n");
+ exit(2);
+ }
+#endif
+ argv += 2;
+ argc -= 2;
+ continue;
+ }
+
+ if( argc>3 && strcmp(argv[1], "-quiet")==0 ){
+ bQuiet = -1;
+ argv++;
+ argc--;
+ continue;
+ }
+
+ break;
+ }
+
+ if( argc!=3 ){
+ fprintf(stderr, "Usage: %s [options] FILENAME SQL-SCRIPT\n"
+ "Runs SQL-SCRIPT against a UTF8 database\n"
+ "\toptions:\n"
+ "\t-overwrite\n"
+#ifdef HAVE_OSINST
+ "\t-log <log>\n"
+#endif
+ "\t-priority <value> : set priority of task\n"
+ "\t-quiet : only display summary results\n",
+ zArgv0);
+ exit(1);
+ }
+
+ in = fopen(argv[2], "r");
+ fseek(in, 0L, SEEK_END);
+ nSql = ftell(in);
+ zSql = malloc( nSql+1 );
+ fseek(in, 0L, SEEK_SET);
+ nSql = fread(zSql, 1, nSql, in);
+ zSql[nSql] = 0;
+
+ printf("SQLite version: %d\n", sqlite3_libversion_number());
+ unlink(argv[1]);
+#if !defined(_MSC_VER)
+ clkStart = times(&tmsStart);
+#endif
+ iStart = sqlite3Hwtime();
+ rc = sqlite3_open(argv[1], &db);
+ iElapse = sqlite3Hwtime() - iStart;
+ iSetup = iElapse;
+ if (!bQuiet) printf("sqlite3_open() returns %d in %llu cycles\n", rc, iElapse);
+ for(i=j=0; j<nSql; j++){
+ if( zSql[j]==';' ){
+ int isComplete;
+ char c = zSql[j+1];
+ zSql[j+1] = 0;
+ isComplete = sqlite3_complete(&zSql[i]);
+ zSql[j+1] = c;
+ if( isComplete ){
+ zSql[j] = 0;
+ while( i<j && isspace(zSql[i]) ){ i++; }
+ if( i<j ){
+ int n = j - i;
+ if( n>=6 && memcmp(&zSql[i], ".crash",6)==0 ) exit(1);
+ nStmt++;
+ nByte += n;
+ prepareAndRun(db, &zSql[i], bQuiet);
+ }
+ zSql[j] = ';';
+ i = j+1;
+ }
+ }
+ }
+ iStart = sqlite3Hwtime();
+ sqlite3_close(db);
+ iElapse = sqlite3Hwtime() - iStart;
+#if !defined(_MSC_VER)
+ clkEnd = times(&tmsEnd);
+#endif
+ iSetup += iElapse;
+ if (!bQuiet) printf("sqlite3_close() returns in %llu cycles\n", iElapse);
+
+ printf("\n");
+ printf("Statements run: %15d stmts\n", nStmt);
+ printf("Bytes of SQL text: %15d bytes\n", nByte);
+ printf("Total prepare time: %15llu cycles\n", prepTime);
+ printf("Total run time: %15llu cycles\n", runTime);
+ printf("Total finalize time: %15llu cycles\n", finalizeTime);
+ printf("Open/Close time: %15llu cycles\n", iSetup);
+ printf("Total time: %15llu cycles\n",
+ prepTime + runTime + finalizeTime + iSetup);
+
+#if !defined(_MSC_VER)
+ printf("\n");
+ printf("Total user CPU time: %15.3g secs\n", (tmsEnd.tms_utime - tmsStart.tms_utime)/(double)CLOCKS_PER_SEC );
+ printf("Total system CPU time: %15.3g secs\n", (tmsEnd.tms_stime - tmsStart.tms_stime)/(double)CLOCKS_PER_SEC );
+ printf("Total real time: %15.3g secs\n", (clkEnd -clkStart)/(double)CLOCKS_PER_SEC );
+#endif
+
+#ifdef HAVE_OSINST
+ if( pVfs ){
+ sqlite3_instvfs_destroy(pVfs);
+ printf("vfs log written to %s\n", argv[0]);
+ }
+#endif
+
+ return 0;
+}
diff --git a/third_party/sqlite/tool/speedtest8inst1.c b/third_party/sqlite/tool/speedtest8inst1.c
new file mode 100755
index 0000000..f0cb544
--- /dev/null
+++ b/third_party/sqlite/tool/speedtest8inst1.c
@@ -0,0 +1,216 @@
+/*
+** Performance test for SQLite.
+**
+** This program reads ASCII text from a file named on the command-line
+** and submits that text to SQLite for evaluation. A new database
+** is created at the beginning of the program. All statements are
+** timed using the high-resolution timer built into Intel-class processors.
+**
+** To compile this program, first compile the SQLite library separately
+** will full optimizations. For example:
+**
+** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
+**
+** Then link against this program. But to do optimize this program
+** because that defeats the hi-res timer.
+**
+** gcc speedtest8.c sqlite3.o -ldl -I../src
+**
+** Then run this program with a single argument which is the name of
+** a file containing SQL script that you want to test:
+**
+** ./a.out test.db test.sql
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include "sqlite3.h"
+
+#include "test_osinst.c"
+
+/*
+** Prepare and run a single statement of SQL.
+*/
+static void prepareAndRun(sqlite3_vfs *pInstVfs, sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ const char *stmtTail;
+ int rc;
+ char zMessage[1024];
+ zMessage[1023] = '\0';
+
+ sqlite3_uint64 iTime;
+
+ sqlite3_snprintf(1023, zMessage, "sqlite3_prepare_v2: %s", zSql);
+ sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
+
+ iTime = sqlite3Hwtime();
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &stmtTail);
+ iTime = sqlite3Hwtime() - iTime;
+ sqlite3_instvfs_binarylog_call(pInstVfs,BINARYLOG_PREPARE_V2,iTime,rc,zSql);
+
+ if( rc==SQLITE_OK ){
+ int nRow = 0;
+
+ sqlite3_snprintf(1023, zMessage, "sqlite3_step loop: %s", zSql);
+ sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
+ iTime = sqlite3Hwtime();
+ while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
+ iTime = sqlite3Hwtime() - iTime;
+ sqlite3_instvfs_binarylog_call(pInstVfs, BINARYLOG_STEP, iTime, rc, zSql);
+
+ sqlite3_snprintf(1023, zMessage, "sqlite3_finalize: %s", zSql);
+ sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
+ iTime = sqlite3Hwtime();
+ rc = sqlite3_finalize(pStmt);
+ iTime = sqlite3Hwtime() - iTime;
+ sqlite3_instvfs_binarylog_call(pInstVfs, BINARYLOG_FINALIZE, iTime, rc, zSql);
+ }
+}
+
+static int stringcompare(const char *zLeft, const char *zRight){
+ int ii;
+ for(ii=0; zLeft[ii] && zRight[ii]; ii++){
+ if( zLeft[ii]!=zRight[ii] ) return 0;
+ }
+ return( zLeft[ii]==zRight[ii] );
+}
+
+static char *readScriptFile(const char *zFile, int *pnScript){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ sqlite3_file *p;
+ int rc;
+ sqlite3_int64 nByte;
+ char *zData = 0;
+ int flags = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_DB;
+
+ p = (sqlite3_file *)malloc(pVfs->szOsFile);
+ rc = pVfs->xOpen(pVfs, zFile, p, flags, &flags);
+ if( rc!=SQLITE_OK ){
+ goto error_out;
+ }
+
+ rc = p->pMethods->xFileSize(p, &nByte);
+ if( rc!=SQLITE_OK ){
+ goto close_out;
+ }
+
+ zData = (char *)malloc(nByte+1);
+ rc = p->pMethods->xRead(p, zData, nByte, 0);
+ if( rc!=SQLITE_OK ){
+ goto close_out;
+ }
+ zData[nByte] = '\0';
+
+ p->pMethods->xClose(p);
+ free(p);
+ *pnScript = nByte;
+ return zData;
+
+close_out:
+ p->pMethods->xClose(p);
+
+error_out:
+ free(p);
+ free(zData);
+ return 0;
+}
+
+int main(int argc, char **argv){
+
+ const char zUsageMsg[] =
+ "Usage: %s options...\n"
+ " where available options are:\n"
+ "\n"
+ " -db DATABASE-FILE (database file to operate on)\n"
+ " -script SCRIPT-FILE (script file to read sql from)\n"
+ " -log LOG-FILE (log file to create)\n"
+ " -logdata (log all data to log file)\n"
+ "\n"
+ " Options -db, -script and -log are compulsory\n"
+ "\n"
+ ;
+
+ const char *zDb = 0;
+ const char *zScript = 0;
+ const char *zLog = 0;
+ int logdata = 0;
+
+ int ii;
+ int i, j;
+ int rc;
+
+ sqlite3_vfs *pInstVfs; /* Instrumentation VFS */
+
+ char *zSql = 0;
+ int nSql;
+
+ sqlite3 *db;
+
+ for(ii=1; ii<argc; ii++){
+ if( stringcompare("-db", argv[ii]) && (ii+1)<argc ){
+ zDb = argv[++ii];
+ }
+
+ else if( stringcompare("-script", argv[ii]) && (ii+1)<argc ){
+ zScript = argv[++ii];
+ }
+
+ else if( stringcompare("-log", argv[ii]) && (ii+1)<argc ){
+ zLog = argv[++ii];
+ }
+
+ else if( stringcompare("-logdata", argv[ii]) ){
+ logdata = 1;
+ }
+
+ else {
+ goto usage;
+ }
+ }
+ if( !zDb || !zScript || !zLog ) goto usage;
+
+ zSql = readScriptFile(zScript, &nSql);
+ if( !zSql ){
+ fprintf(stderr, "Failed to read script file\n");
+ return -1;
+ }
+
+ pInstVfs = sqlite3_instvfs_binarylog("logging", 0, zLog, logdata);
+
+ rc = sqlite3_open_v2(
+ zDb, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "logging"
+ );
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "Failed to open db: %s\n", sqlite3_errmsg(db));
+ return -2;
+ }
+
+ for(i=j=0; j<nSql; j++){
+ if( zSql[j]==';' ){
+ int isComplete;
+ char c = zSql[j+1];
+ zSql[j+1] = 0;
+ isComplete = sqlite3_complete(&zSql[i]);
+ zSql[j+1] = c;
+ if( isComplete ){
+ zSql[j] = 0;
+ while( i<j && isspace(zSql[i]) ){ i++; }
+ if( i<j ){
+ prepareAndRun(pInstVfs, db, &zSql[i]);
+ }
+ zSql[j] = ';';
+ i = j+1;
+ }
+ }
+ }
+
+ sqlite3_instvfs_destroy(pInstVfs);
+ return 0;
+
+usage:
+ fprintf(stderr, zUsageMsg, argv[0]);
+ return -3;
+}
diff --git a/third_party/sqlite/www/capi3ref.tcl b/third_party/sqlite/www/capi3ref.tcl
new file mode 100755
index 0000000..631acef
--- /dev/null
+++ b/third_party/sqlite/www/capi3ref.tcl
@@ -0,0 +1,1882 @@
+set rcsid {$Id: capi3ref.tcl,v 1.60 2007/05/19 06:48:43 danielk1977 Exp $}
+source common.tcl
+header {C/C++ Interface For SQLite Version 3}
+puts {
+<h2 class=pdf_section>C/C++ Interface For SQLite Version 3</h2>
+}
+
+proc api {name prototype desc {notused x}} {
+ global apilist specialname
+ if {$name==""} {
+ regsub -all {sqlite3_[a-z0-9_]+\(} $prototype \
+ {[lappend name [string trimright & (]]} x1
+ subst $x1
+ } else {
+ lappend specialname $name
+ }
+ lappend apilist [list $name $prototype $desc]
+}
+
+api {extended-result-codes} {
+#define SQLITE_IOERR_READ
+#define SQLITE_IOERR_SHORT_READ
+#define SQLITE_IOERR_WRITE
+#define SQLITE_IOERR_FSYNC
+#define SQLITE_IOERR_DIR_FSYNC
+#define SQLITE_IOERR_TRUNCATE
+#define SQLITE_IOERR_FSTAT
+#define SQLITE_IOERR_UNLOCK
+#define SQLITE_IOERR_RDLOCK
+...
+} {
+In its default configuration, SQLite API routines return one of 26 integer
+result codes described at result-codes. However, experience has shown that
+many of these result codes are too course-grained. They do not provide as
+much information about problems as users might like. In an effort to
+address this, newer versions of SQLite (version 3.3.8 and later) include
+support for additional result codes that provide more detailed information
+about errors. The extended result codes are enabled (or disabled) for
+each database
+connection using the sqlite3_extended_result_codes() API.
+
+Some of the available extended result codes are listed above.
+We expect the number of extended result codes will be expand
+over time. Software that uses extended result codes should expect
+to see new result codes in future releases of SQLite.
+
+The symbolic name for an extended result code always contains a related
+primary result code as a prefix. Primary result codes contain a single
+"_" character. Extended result codes contain two or more "_" characters.
+The numeric value of an extended result code can be converted to its
+corresponding primary result code by masking off the lower 8 bytes.
+
+A complete list of available extended result codes and
+details about the meaning of the various extended result codes can be
+found by consulting the C code, especially the sqlite3.h header
+file and its antecedent sqlite.h.in. Additional information
+is also available at the SQLite wiki:
+http://www.sqlite.org/cvstrac/wiki?p=ExtendedResultCodes
+}
+
+
+api {result-codes} {
+#define SQLITE_OK 0 /* Successful result */
+#define SQLITE_ERROR 1 /* SQL error or missing database */
+#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
+#define SQLITE_PERM 3 /* Access permission denied */
+#define SQLITE_ABORT 4 /* Callback routine requested an abort */
+#define SQLITE_BUSY 5 /* The database file is locked */
+#define SQLITE_LOCKED 6 /* A table in the database is locked */
+#define SQLITE_NOMEM 7 /* A malloc() failed */
+#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
+#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
+#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
+#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
+#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
+#define SQLITE_FULL 13 /* Insertion failed because database is full */
+#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
+#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
+#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
+#define SQLITE_SCHEMA 17 /* The database schema changed */
+#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
+#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
+#define SQLITE_MISMATCH 20 /* Data type mismatch */
+#define SQLITE_MISUSE 21 /* Library used incorrectly */
+#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
+#define SQLITE_AUTH 23 /* Authorization denied */
+#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
+#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
+} {
+Many SQLite functions return an integer result code from the set shown
+above in order to indicates success or failure.
+
+The result codes above are the only ones returned by SQLite in its
+default configuration. However, the sqlite3_extended_result_codes()
+API can be used to set a database connectoin to return more detailed
+result codes. See the documentation on sqlite3_extended_result_codes()
+or extended-result-codes for additional information.
+}
+
+api {} {
+ int sqlite3_extended_result_codes(sqlite3*, int onoff);
+} {
+This routine enables or disabled extended-result-codes feature.
+By default, SQLite API routines return one of only 26 integer
+result codes described at result-codes. When extended result codes
+are enabled by this routine, the repetoire of result codes can be
+much larger and can (hopefully) provide more detailed information
+about the cause of an error.
+
+The second argument is a boolean value that turns extended result
+codes on and off. Extended result codes are off by default for
+backwards compatibility with older versions of SQLite.
+}
+
+api {} {
+ const char *sqlite3_libversion(void);
+} {
+ Return a pointer to a string which contains the version number of
+ the library. The same string is available in the global
+ variable named "sqlite3_version". This interface is provided since
+ windows is unable to access global variables in DLLs.
+}
+
+api {} {
+ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
+} {
+ Aggregate functions use this routine to allocate
+ a structure for storing their state. The first time this routine
+ is called for a particular aggregate, a new structure of size nBytes
+ is allocated, zeroed, and returned. On subsequent calls (for the
+ same aggregate instance) the same buffer is returned. The implementation
+ of the aggregate can use the returned buffer to accumulate data.
+
+ The buffer is freed automatically by SQLite when the query that
+ invoked the aggregate function terminates.
+}
+
+api {} {
+ int sqlite3_aggregate_count(sqlite3_context*);
+} {
+ This function is deprecated. It continues to exist so as not to
+ break any legacy code that might happen to use it. But it should not
+ be used in any new code.
+
+ In order to encourage people to not use this function, we are not going
+ to tell you what it does.
+}
+
+api {} {
+ int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+ int sqlite3_bind_double(sqlite3_stmt*, int, double);
+ int sqlite3_bind_int(sqlite3_stmt*, int, int);
+ int sqlite3_bind_int64(sqlite3_stmt*, int, long long int);
+ int sqlite3_bind_null(sqlite3_stmt*, int);
+ int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+ int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+ #define SQLITE_STATIC ((void(*)(void *))0)
+ #define SQLITE_TRANSIENT ((void(*)(void *))-1)
+} {
+ In the SQL strings input to sqlite3_prepare_v2() and sqlite3_prepare16_v2(),
+ one or more literals can be replace by a parameter "?" or "?NNN"
+ or ":AAA" or "@AAA" or "\$VVV" where NNN is an integer literal,
+ AAA is an alphanumeric identifier and VVV is a variable name according
+ to the syntax rules of the TCL programming language.
+ The values of these parameters (also called "host parameter names")
+ can be set using the sqlite3_bind_*() routines.
+
+ The first argument to the sqlite3_bind_*() routines always is a pointer
+ to the sqlite3_stmt structure returned from sqlite3_prepare_v2(). The second
+ argument is the index of the parameter to be set. The first parameter has
+ an index of 1. When the same named parameter is used more than once, second
+ and subsequent
+ occurrences have the same index as the first occurrence. The index for
+ named parameters can be looked up using the
+ sqlite3_bind_parameter_name() API if desired. The index for "?NNN"
+ parametes is the value of NNN. The NNN value must be between 1 and 999.
+
+
+ The third argument is the value to bind to the parameter.
+
+ In those
+ routines that have a fourth argument, its value is the number of bytes
+ in the parameter. To be clear: the value is the number of bytes in the
+ string, not the number of characters. The number
+ of bytes does not include the zero-terminator at the end of strings.
+ If the fourth parameter is negative, the length of the string is
+ number of bytes up to the first zero terminator.
+
+ The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
+ sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+ text after SQLite has finished with it. If the fifth argument is the
+ special value SQLITE_STATIC, then the library assumes that the information
+ is in static, unmanaged space and does not need to be freed. If the
+ fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its
+ own private copy of the data immediately, before the sqlite3_bind_*()
+ routine returns.
+
+ The sqlite3_bind_*() routines must be called after
+ sqlite3_prepare_v2() or sqlite3_reset() and before sqlite3_step().
+ Bindings are not cleared by the sqlite3_reset() routine.
+ Unbound parameters are interpreted as NULL.
+
+ These routines return SQLITE_OK on success or an error code if
+ anything goes wrong. SQLITE_RANGE is returned if the parameter
+ index is out of range. SQLITE_NOMEM is returned if malloc fails.
+ SQLITE_MISUSE is returned if these routines are called on a virtual
+ machine that is the wrong state or which has already been finalized.
+}
+
+api {} {
+ int sqlite3_bind_parameter_count(sqlite3_stmt*);
+} {
+ Return the number of parameters in the precompiled statement given as
+ the argument.
+}
+
+api {} {
+ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int n);
+} {
+ Return the name of the n-th parameter in the precompiled statement.
+ Parameters of the form ":AAA" or "@AAA" or "\$VVV" have a name which is the
+ string ":AAA" or "@AAA" or "\$VVV".
+ In other words, the initial ":" or "$" or "@"
+ is included as part of the name.
+ Parameters of the form "?" or "?NNN" have no name.
+
+ The first bound parameter has an index of 1, not 0.
+
+ If the value n is out of range or if the n-th parameter is nameless,
+ then NULL is returned. The returned string is always in the
+ UTF-8 encoding even if the named parameter was originally specified
+ as UTF-16 in sqlite3_prepare16_v2().
+}
+
+api {} {
+ int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
+} {
+ Return the index of the parameter with the given name.
+ The name must match exactly.
+ If there is no parameter with the given name, return 0.
+ The string zName is always in the UTF-8 encoding.
+}
+
+api {} {
+ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+} {
+ This routine identifies a callback function that might be invoked
+ whenever an attempt is made to open a database table
+ that another thread or process has locked.
+ If the busy callback is NULL, then SQLITE_BUSY is returned immediately
+ upon encountering the lock.
+ If the busy callback is not NULL, then the
+ callback will be invoked with two arguments. The
+ first argument to the handler is a copy of the void* pointer which
+ is the third argument to this routine. The second argument to
+ the handler is the number of times that the busy handler has
+ been invoked for this locking event. If the
+ busy callback returns 0, then no additional attempts are made to
+ access the database and SQLITE_BUSY is returned.
+ If the callback returns non-zero, then another attempt is made to open the
+ database for reading and the cycle repeats.
+
+ The presence of a busy handler does not guarantee that
+ it will be invoked when there is lock contention.
+ If SQLite determines that invoking the busy handler could result in
+ a deadlock, it will return SQLITE_BUSY instead.
+ Consider a scenario where one process is holding a read lock that
+ it is trying to promote to a reserved lock and
+ a second process is holding a reserved lock that it is trying
+ to promote to an exclusive lock. The first process cannot proceed
+ because it is blocked by the second and the second process cannot
+ proceed because it is blocked by the first. If both processes
+ invoke the busy handlers, neither will make any progress. Therefore,
+ SQLite returns SQLITE_BUSY for the first process, hoping that this
+ will induce the first process to release its read lock and allow
+ the second process to proceed.
+
+ The default busy callback is NULL.
+
+ Sqlite is re-entrant, so the busy handler may start a new query.
+ (It is not clear why anyone would every want to do this, but it
+ is allowed, in theory.) But the busy handler may not close the
+ database. Closing the database from a busy handler will delete
+ data structures out from under the executing query and will
+ probably result in a coredump.
+
+ There can only be a single busy handler defined for each database
+ connection. Setting a new busy handler clears any previous one.
+ Note that calling sqlite3_busy_timeout() will also set or clear
+ the busy handler.
+}
+
+api {} {
+ int sqlite3_busy_timeout(sqlite3*, int ms);
+} {
+ This routine sets a busy handler that sleeps for a while when a
+ table is locked. The handler will sleep multiple times until
+ at least "ms" milliseconds of sleeping have been done. After
+ "ms" milliseconds of sleeping, the handler returns 0 which
+ causes sqlite3_exec() to return SQLITE_BUSY.
+
+ Calling this routine with an argument less than or equal to zero
+ turns off all busy handlers.
+
+ There can only be a single busy handler for a particular database
+ connection. If another busy handler was defined
+ (using sqlite3_busy_handler()) prior to calling
+ this routine, that other busy handler is cleared.
+}
+
+api {} {
+ int sqlite3_changes(sqlite3*);
+} {
+ This function returns the number of database rows that were changed
+ (or inserted or deleted) by the most recently completed
+ INSERT, UPDATE, or DELETE
+ statement. Only changes that are directly specified by the INSERT,
+ UPDATE, or DELETE statement are counted. Auxiliary changes caused by
+ triggers are not counted. Use the sqlite3_total_changes() function
+ to find the total number of changes including changes caused by triggers.
+
+ Within the body of a trigger, the sqlite3_changes() function does work
+ to report the number of rows that were changed for the most recently
+ completed INSERT, UPDATE, or DELETE statement within the trigger body.
+
+ SQLite implements the command "DELETE FROM table" without a WHERE clause
+ by dropping and recreating the table. (This is much faster than going
+ through and deleting individual elements from the table.) Because of
+ this optimization, the change count for "DELETE FROM table" will be
+ zero regardless of the number of elements that were originally in the
+ table. To get an accurate count of the number of rows deleted, use
+ "DELETE FROM table WHERE 1" instead.
+}
+
+api {} {
+ int sqlite3_total_changes(sqlite3*);
+} {
+ This function returns the total number of database rows that have
+ be modified, inserted, or deleted since the database connection was
+ created using sqlite3_open(). All changes are counted, including
+ changes by triggers and changes to TEMP and auxiliary databases.
+ Except, changes to the SQLITE_MASTER table (caused by statements
+ such as CREATE TABLE) are not counted. Nor are changes counted when
+ an entire table is deleted using DROP TABLE.
+
+ See also the sqlite3_changes() API.
+
+ SQLite implements the command "DELETE FROM table" without a WHERE clause
+ by dropping and recreating the table. (This is much faster than going
+ through and deleting individual elements form the table.) Because of
+ this optimization, the change count for "DELETE FROM table" will be
+ zero regardless of the number of elements that were originally in the
+ table. To get an accurate count of the number of rows deleted, use
+ "DELETE FROM table WHERE 1" instead.
+}
+
+api {} {
+ int sqlite3_close(sqlite3*);
+} {
+ Call this function with a pointer to a structure that was previously
+ returned from sqlite3_open() or sqlite3_open16()
+ and the corresponding database will by closed.
+
+ SQLITE_OK is returned if the close is successful. If there are
+ prepared statements that have not been finalized, then SQLITE_BUSY
+ is returned. SQLITE_ERROR might be returned if the argument is not
+ a valid connection pointer returned by sqlite3_open() or if the connection
+ pointer has been closed previously.
+}
+
+api {} {
+const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
+double sqlite3_column_double(sqlite3_stmt*, int iCol);
+int sqlite3_column_int(sqlite3_stmt*, int iCol);
+long long int sqlite3_column_int64(sqlite3_stmt*, int iCol);
+const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
+int sqlite3_column_type(sqlite3_stmt*, int iCol);
+#define SQLITE_INTEGER 1
+#define SQLITE_FLOAT 2
+#define SQLITE_TEXT 3
+#define SQLITE_BLOB 4
+#define SQLITE_NULL 5
+} {
+ These routines return information about the information
+ in a single column of the current result row of a query. In every
+ case the first argument is a pointer to the SQL statement that is being
+ executed (the sqlite_stmt* that was returned from sqlite3_prepare_v2()) and
+ the second argument is the index of the column for which information
+ should be returned. iCol is zero-indexed. The left-most column has an
+ index of 0.
+
+ If the SQL statement is not currently point to a valid row, or if the
+ the column index is out of range, the result is undefined.
+
+ The sqlite3_column_type() routine returns the initial data type
+ of the result column. The returned value is one of SQLITE_INTEGER,
+ SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, or SQLITE_NULL. The value
+ returned by sqlite3_column_type() is only meaningful if no type
+ conversions have occurred as described below. After a type conversion,
+ the value returned by sqlite3_column_type() is undefined. Future
+ versions of SQLite may change the behavior of sqlite3_column_type()
+ following a type conversion.
+
+ If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()
+ routine returns the number of bytes in that BLOB or string.
+ If the result is a UTF-16 string, then sqlite3_column_bytes() converts
+ the string to UTF-8 and then returns the number of bytes.
+ If the result is a numeric value then sqlite3_column_bytes() uses
+ sqlite3_snprintf() to convert that value to a UTF-8 string and returns
+ the number of bytes in that string.
+ The value returned does
+ not include the \\000 terminator at the end of the string.
+
+ The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes()
+ but leaves the result in UTF-16 instead of UTF-8.
+ The \\u0000 terminator is not included in this count.
+
+ These routines attempt to convert the value where appropriate. For
+ example, if the internal representation is FLOAT and a text result
+ is requested, sqlite3_snprintf() is used internally to do the conversion
+ automatically. The following table details the conversions that
+ are applied:
+
+<blockquote>
+<table border="1">
+<tr><th>Internal Type</th><th>Requested Type</th><th>Conversion</th></tr>
+<tr><td> NULL </td><td> INTEGER</td><td>Result is 0</td></tr>
+<tr><td> NULL </td><td> FLOAT </td><td> Result is 0.0</td></tr>
+<tr><td> NULL </td><td> TEXT </td><td> Result is NULL pointer</td></tr>
+<tr><td> NULL </td><td> BLOB </td><td> Result is NULL pointer</td></tr>
+<tr><td> INTEGER </td><td> FLOAT </td><td> Convert from integer to float</td></tr>
+<tr><td> INTEGER </td><td> TEXT </td><td> ASCII rendering of the integer</td></tr>
+<tr><td> INTEGER </td><td> BLOB </td><td> Same as for INTEGER->TEXT</td></tr>
+<tr><td> FLOAT </td><td> INTEGER</td><td>Convert from float to integer</td></tr>
+<tr><td> FLOAT </td><td> TEXT </td><td> ASCII rendering of the float</td></tr>
+<tr><td> FLOAT </td><td> BLOB </td><td> Same as FLOAT->TEXT</td></tr>
+<tr><td> TEXT </td><td> INTEGER</td><td>Use atoi()</td></tr>
+<tr><td> TEXT </td><td> FLOAT </td><td> Use atof()</td></tr>
+<tr><td> TEXT </td><td> BLOB </td><td> No change</td></tr>
+<tr><td> BLOB </td><td> INTEGER</td><td>Convert to TEXT then use atoi()</td></tr>
+<tr><td> BLOB </td><td> FLOAT </td><td> Convert to TEXT then use atof()</td></tr>
+<tr><td> BLOB </td><td> TEXT </td><td> Add a \\000 terminator if needed</td></tr>
+</table>
+</blockquote>
+
+ Note that when type conversions occur, pointers returned by prior
+ calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
+ sqlite3_column_text16() may be invalidated.
+ Type conversions and pointer invalidations might occur
+ in the following cases:
+
+ <ul>
+ <li><p>
+ The initial content is a BLOB and sqlite3_column_text()
+ or sqlite3_column_text16()
+ is called. A zero-terminator might need to be added to the string.
+ </p></li>
+ <li><p>
+ The initial content is UTF-8 text and sqlite3_column_bytes16() or
+ sqlite3_column_text16() is called. The content must be converted to UTF-16.
+ </p></li>
+ <li><p>
+ The initial content is UTF-16 text and sqlite3_column_bytes() or
+ sqlite3_column_text() is called. The content must be converted to UTF-8.
+ </p></li>
+ </ul>
+
+ Conversions between UTF-16be and UTF-16le
+ are always done in place and do
+ not invalidate a prior pointer, though of course the content of the buffer
+ that the prior pointer points to will have been modified. Other kinds
+ of conversion are done in place when it is possible, but sometime it is
+ not possible and in those cases prior pointers are invalidated.
+
+ The safest and easiest to remember policy is to invoke these routines
+ in one of the following ways:
+
+ <ul>
+ <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>
+ <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>
+ <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>
+ </ul>
+
+ In other words, you should call sqlite3_column_text(), sqlite3_column_blob(),
+ or sqlite3_column_text16() first to force the result into the desired
+ format, then invoke sqlite3_column_bytes() or sqlite3_column_bytes16() to
+ find the size of the result. Do not mix call to sqlite3_column_text() or
+ sqlite3_column_blob() with calls to sqlite3_column_bytes16(). And do not
+ mix calls to sqlite3_column_text16() with calls to sqlite3_column_bytes().
+}
+
+api {} {
+int sqlite3_column_count(sqlite3_stmt *pStmt);
+} {
+ Return the number of columns in the result set returned by the prepared
+ SQL statement. This routine returns 0 if pStmt is an SQL statement
+ that does not return data (for example an UPDATE).
+
+ See also sqlite3_data_count().
+}
+
+api {} {
+const char *sqlite3_column_decltype(sqlite3_stmt *, int i);
+const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
+} {
+ The first argument is a prepared SQL statement. If this statement
+ is a SELECT statement, the Nth column of the returned result set
+ of the SELECT is a table column then the declared type of the table
+ column is returned. If the Nth column of the result set is not a table
+ column, then a NULL pointer is returned. The returned string is
+ UTF-8 encoded for sqlite3_column_decltype() and UTF-16 encoded
+ for sqlite3_column_decltype16(). For example, in the database schema:
+
+ <blockquote><pre>
+ CREATE TABLE t1(c1 INTEGER);
+ </pre></blockquote>
+
+ And the following statement compiled:
+
+ <blockquote><pre>
+ SELECT c1 + 1, c1 FROM t1;
+ </pre></blockquote>
+
+ Then this routine would return the string "INTEGER" for the second
+ result column (i==1), and a NULL pointer for the first result column
+ (i==0).
+
+ If the following statements were compiled then this routine would
+ return "INTEGER" for the first (only) result column.
+
+ <blockquote><pre>
+ SELECT (SELECT c1) FROM t1;
+ SELECT (SELECT c1 FROM t1);
+ SELECT c1 FROM (SELECT c1 FROM t1);
+ SELECT * FROM (SELECT c1 FROM t1);
+ SELECT * FROM (SELECT * FROM t1);
+ </pre></blockquote>
+}
+
+api {} {
+ int sqlite3_table_column_metadata(
+ sqlite3 *db, /* Connection handle */
+ const char *zDbName, /* Database name or NULL */
+ const char *zTableName, /* Table name */
+ const char *zColumnName, /* Column name */
+ char const **pzDataType, /* OUTPUT: Declared data type */
+ char const **pzCollSeq, /* OUTPUT: Collation sequence name */
+ int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */
+ int *pPrimaryKey, /* OUTPUT: True if column part of PK */
+ int *pAutoinc /* OUTPUT: True if colums is auto-increment */
+ );
+} {
+ This routine is used to obtain meta information about a specific column of a
+ specific database table accessible using the connection handle passed as the
+ first function argument.
+
+ The column is identified by the second, third and fourth parameters to
+ this function. The second parameter is either the name of the database
+ (i.e. "main", "temp" or an attached database) containing the specified
+ table or NULL. If it is NULL, then all attached databases are searched
+ for the table using the same algorithm as the database engine uses to
+ resolve unqualified table references.
+
+ The third and fourth parameters to this function are the table and column
+ name of the desired column, respectively. Neither of these parameters
+ may be NULL.
+
+ Meta information is returned by writing to the memory locations passed as
+ the 5th and subsequent parameters to this function. Any of these
+ arguments may be NULL, in which case the corresponding element of meta
+ information is ommitted.
+
+<pre>
+ Parameter Output Type Description
+ -----------------------------------
+ 5th const char* Declared data type
+ 6th const char* Name of the columns default collation sequence
+ 7th int True if the column has a NOT NULL constraint
+ 8th int True if the column is part of the PRIMARY KEY
+ 9th int True if the column is AUTOINCREMENT
+</pre>
+
+ The memory pointed to by the character pointers returned for the
+ declaration type and collation sequence is valid only until the next
+ call to any sqlite API function.
+
+ This function may load one or more schemas from database files. If an
+ error occurs during this process, or if the requested table or column
+ cannot be found, an SQLITE error code is returned and an error message
+ left in the database handle (to be retrieved using sqlite3_errmsg()).
+ Specifying an SQL view instead of a table as the third argument is also
+ considered an error.
+
+ If the specified column is "rowid", "oid" or "_rowid_" and an
+ INTEGER PRIMARY KEY column has been explicitly declared, then the output
+ parameters are set for the explicitly declared column. If there is no
+ explicitly declared IPK column, then the data-type is "INTEGER", the
+ collation sequence "BINARY" and the primary-key flag is set. Both
+ the not-null and auto-increment flags are clear.
+
+ This API is only available if the library was compiled with the
+ SQLITE_ENABLE_COLUMN_METADATA preprocessor symbol defined.
+}
+
+api {} {
+const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N);
+const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N);
+} {
+If the Nth column returned by statement pStmt is a column reference,
+these functions may be used to access the name of the database (either
+"main", "temp" or the name of an attached database) that contains
+the column. If the Nth column is not a column reference, NULL is
+returned.
+
+See the description of function sqlite3_column_decltype() for a
+description of exactly which expressions are considered column references.
+
+Function sqlite3_column_database_name() returns a pointer to a UTF-8
+encoded string. sqlite3_column_database_name16() returns a pointer
+to a UTF-16 encoded string.
+}
+
+api {} {
+const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N);
+const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N);
+} {
+If the Nth column returned by statement pStmt is a column reference,
+these functions may be used to access the schema name of the referenced
+column in the database schema. If the Nth column is not a column
+reference, NULL is returned.
+
+See the description of function sqlite3_column_decltype() for a
+description of exactly which expressions are considered column references.
+
+Function sqlite3_column_origin_name() returns a pointer to a UTF-8
+encoded string. sqlite3_column_origin_name16() returns a pointer
+to a UTF-16 encoded string.
+}
+
+api {} {
+const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N);
+const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N);
+} {
+If the Nth column returned by statement pStmt is a column reference,
+these functions may be used to access the name of the table that
+contains the column. If the Nth column is not a column reference,
+NULL is returned.
+
+See the description of function sqlite3_column_decltype() for a
+description of exactly which expressions are considered column references.
+
+Function sqlite3_column_table_name() returns a pointer to a UTF-8
+encoded string. sqlite3_column_table_name16() returns a pointer
+to a UTF-16 encoded string.
+}
+
+api {} {
+const char *sqlite3_column_name(sqlite3_stmt*,int);
+const void *sqlite3_column_name16(sqlite3_stmt*,int);
+} {
+ The first argument is a prepared SQL statement. This function returns
+ the column heading for the Nth column of that statement, where N is the
+ second function argument. The string returned is UTF-8 for
+ sqlite3_column_name() and UTF-16 for sqlite3_column_name16().
+}
+
+api {} {
+void *sqlite3_commit_hook(sqlite3*, int(*xCallback)(void*), void *pArg);
+} {
+ <i>Experimental</i>
+
+ Register a callback function to be invoked whenever a new transaction
+ is committed. The pArg argument is passed through to the callback.
+ callback. If the callback function returns non-zero, then the commit
+ is converted into a rollback.
+
+ If another function was previously registered, its pArg value is returned.
+ Otherwise NULL is returned.
+
+ Registering a NULL function disables the callback. Only a single commit
+ hook callback can be registered at a time.
+}
+
+api {} {
+int sqlite3_complete(const char *sql);
+int sqlite3_complete16(const void *sql);
+} {
+ These functions return true if the given input string comprises
+ one or more complete SQL statements.
+ The argument must be a nul-terminated UTF-8 string for sqlite3_complete()
+ and a nul-terminated UTF-16 string for sqlite3_complete16().
+
+ These routines do not check to see if the SQL statement is well-formed.
+ They only check to see that the statement is terminated by a semicolon
+ that is not part of a string literal and is not inside
+ the body of a trigger.
+} {}
+
+api {} {
+int sqlite3_create_collation(
+ sqlite3*,
+ const char *zName,
+ int pref16,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+int sqlite3_create_collation16(
+ sqlite3*,
+ const char *zName,
+ int pref16,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+#define SQLITE_UTF8 1
+#define SQLITE_UTF16BE 2
+#define SQLITE_UTF16LE 3
+#define SQLITE_UTF16 4
+} {
+ These two functions are used to add new collation sequences to the
+ sqlite3 handle specified as the first argument.
+
+ The name of the new collation sequence is specified as a UTF-8 string
+ for sqlite3_create_collation() and a UTF-16 string for
+ sqlite3_create_collation16(). In both cases the name is passed as the
+ second function argument.
+
+ The third argument must be one of the constants SQLITE_UTF8,
+ SQLITE_UTF16LE or SQLITE_UTF16BE, indicating that the user-supplied
+ routine expects to be passed pointers to strings encoded using UTF-8,
+ UTF-16 little-endian or UTF-16 big-endian respectively. The
+ SQLITE_UTF16 constant indicates that text strings are expected in
+ UTF-16 in the native byte order of the host machine.
+
+ A pointer to the user supplied routine must be passed as the fifth
+ argument. If it is NULL, this is the same as deleting the collation
+ sequence (so that SQLite cannot call it anymore). Each time the user
+ supplied function is invoked, it is passed a copy of the void* passed as
+ the fourth argument to sqlite3_create_collation() or
+ sqlite3_create_collation16() as its first argument.
+
+ The remaining arguments to the user-supplied routine are two strings,
+ each represented by a [length, data] pair and encoded in the encoding
+ that was passed as the third argument when the collation sequence was
+ registered. The user routine should return negative, zero or positive if
+ the first string is less than, equal to, or greater than the second
+ string. i.e. (STRING1 - STRING2).
+}
+
+api {} {
+int sqlite3_collation_needed(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const char*)
+);
+int sqlite3_collation_needed16(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const void*)
+);
+} {
+ To avoid having to register all collation sequences before a database
+ can be used, a single callback function may be registered with the
+ database handle to be called whenever an undefined collation sequence is
+ required.
+
+ If the function is registered using the sqlite3_collation_needed() API,
+ then it is passed the names of undefined collation sequences as strings
+ encoded in UTF-8. If sqlite3_collation_needed16() is used, the names
+ are passed as UTF-16 in machine native byte order. A call to either
+ function replaces any existing callback.
+
+ When the user-function is invoked, the first argument passed is a copy
+ of the second argument to sqlite3_collation_needed() or
+ sqlite3_collation_needed16(). The second argument is the database
+ handle. The third argument is one of SQLITE_UTF8, SQLITE_UTF16BE or
+ SQLITE_UTF16LE, indicating the most desirable form of the collation
+ sequence function required. The fourth argument is the name of the
+ required collation sequence.
+
+ The collation sequence is returned to SQLite by a collation-needed
+ callback using the sqlite3_create_collation() or
+ sqlite3_create_collation16() APIs, described above.
+}
+
+api {} {
+int sqlite3_create_function(
+ sqlite3 *,
+ const char *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pUserData,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+int sqlite3_create_function16(
+ sqlite3*,
+ const void *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pUserData,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+#define SQLITE_UTF8 1
+#define SQLITE_UTF16 2
+#define SQLITE_UTF16BE 3
+#define SQLITE_UTF16LE 4
+#define SQLITE_ANY 5
+} {
+ These two functions are used to add SQL functions or aggregates
+ implemented in C. The
+ only difference between these two routines is that the second argument, the
+ name of the (scalar) function or aggregate, is encoded in UTF-8 for
+ sqlite3_create_function() and UTF-16 for sqlite3_create_function16().
+ The length of the name is limited to 255 bytes, exclusive of the
+ zero-terminator. Note that the name length limit is in bytes, not
+ characters. Any attempt to create a function with a longer name
+ will result in an SQLITE_ERROR error.
+
+ The first argument is the database handle that the new function or
+ aggregate is to be added to. If a single program uses more than one
+ database handle internally, then user functions or aggregates must
+ be added individually to each database handle with which they will be
+ used.
+
+ The third argument is the number of arguments that the function or
+ aggregate takes. If this argument is -1 then the function or
+ aggregate may take any number of arguments. The maximum number
+ of arguments to a new SQL function is 127. A number larger than
+ 127 for the third argument results in an SQLITE_ERROR error.
+
+ The fourth argument, eTextRep, specifies what type of text arguments
+ this function prefers to receive. Any function should be able to work
+ work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
+ more efficient with one representation than another. Users are allowed
+ to specify separate implementations for the same function which are called
+ depending on the text representation of the arguments. The the implementation
+ which provides the best match is used. If there is only a single
+ implementation which does not care what text representation is used,
+ then the fourth argument should be SQLITE_ANY.
+
+ The fifth argument is an arbitrary pointer. The function implementations
+ can gain access to this pointer using the sqlite_user_data() API.
+
+ The sixth, seventh and eighth argumens, xFunc, xStep and xFinal, are
+ pointers to user implemented C functions that implement the user
+ function or aggregate. A scalar function requires an implementation of
+ the xFunc callback only, NULL pointers should be passed as the xStep
+ and xFinal arguments. An aggregate function requires an implementation
+ of xStep and xFinal, and NULL should be passed for xFunc. To delete an
+ existing user function or aggregate, pass NULL for all three function
+ callbacks. Specifying an inconstant set of callback values, such as an
+ xFunc and an xFinal, or an xStep but no xFinal, results in an SQLITE_ERROR
+ return.
+}
+
+api {} {
+int sqlite3_data_count(sqlite3_stmt *pStmt);
+} {
+ Return the number of values in the current row of the result set.
+
+ After a call to sqlite3_step() that returns SQLITE_ROW, this routine
+ will return the same value as the sqlite3_column_count() function.
+ After sqlite3_step() has returned an SQLITE_DONE, SQLITE_BUSY or
+ error code, or before sqlite3_step() has been called on a
+ prepared SQL statement, this routine returns zero.
+}
+
+api {} {
+int sqlite3_errcode(sqlite3 *db);
+} {
+ Return the error code for the most recent failed sqlite3_* API call associated
+ with sqlite3 handle 'db'. If a prior API call failed but the most recent
+ API call succeeded, the return value from this routine is undefined.
+
+ Calls to many sqlite3_* functions set the error code and string returned
+ by sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16()
+ (overwriting the previous values). Note that calls to sqlite3_errcode(),
+ sqlite3_errmsg() and sqlite3_errmsg16() themselves do not affect the
+ results of future invocations. Calls to API routines that do not return
+ an error code (examples: sqlite3_data_count() or sqlite3_mprintf()) do
+ not change the error code returned by this routine.
+
+ Assuming no other intervening sqlite3_* API calls are made, the error
+ code returned by this function is associated with the same error as
+ the strings returned by sqlite3_errmsg() and sqlite3_errmsg16().
+} {}
+
+api {} {
+const char *sqlite3_errmsg(sqlite3*);
+const void *sqlite3_errmsg16(sqlite3*);
+} {
+ Return a pointer to a UTF-8 encoded string (sqlite3_errmsg)
+ or a UTF-16 encoded string (sqlite3_errmsg16) describing in English the
+ error condition for the most recent sqlite3_* API call. The returned
+ string is always terminated by an 0x00 byte.
+
+ The string "not an error" is returned when the most recent API call was
+ successful.
+}
+
+api {} {
+int sqlite3_exec(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be executed */
+ sqlite_callback, /* Callback function */
+ void *, /* 1st argument to callback function */
+ char **errmsg /* Error msg written here */
+);
+} {
+ A function to executes one or more statements of SQL.
+
+ If one or more of the SQL statements are queries, then
+ the callback function specified by the 3rd argument is
+ invoked once for each row of the query result. This callback
+ should normally return 0. If the callback returns a non-zero
+ value then the query is aborted, all subsequent SQL statements
+ are skipped and the sqlite3_exec() function returns the SQLITE_ABORT.
+
+ The 1st argument is an arbitrary pointer that is passed
+ to the callback function as its first argument.
+
+ The 2nd argument to the callback function is the number of
+ columns in the query result. The 3rd argument to the callback
+ is an array of strings holding the values for each column.
+ The 4th argument to the callback is an array of strings holding
+ the names of each column.
+
+ The callback function may be NULL, even for queries. A NULL
+ callback is not an error. It just means that no callback
+ will be invoked.
+
+ If an error occurs while parsing or evaluating the SQL (but
+ not while executing the callback) then an appropriate error
+ message is written into memory obtained from malloc() and
+ *errmsg is made to point to that message. The calling function
+ is responsible for freeing the memory that holds the error
+ message. Use sqlite3_free() for this. If errmsg==NULL,
+ then no error message is ever written.
+
+ The return value is is SQLITE_OK if there are no errors and
+ some other return code if there is an error. The particular
+ return value depends on the type of error.
+
+ If the query could not be executed because a database file is
+ locked or busy, then this function returns SQLITE_BUSY. (This
+ behavior can be modified somewhat using the sqlite3_busy_handler()
+ and sqlite3_busy_timeout() functions.)
+} {}
+
+api {} {
+int sqlite3_finalize(sqlite3_stmt *pStmt);
+} {
+ The sqlite3_finalize() function is called to delete a prepared
+ SQL statement obtained by a previous call to sqlite3_prepare(),
+ sqlite3_prepare_v2(), sqlite3_prepare16(), or sqlite3_prepare16_v2().
+ If the statement was executed successfully, or
+ not executed at all, then SQLITE_OK is returned. If execution of the
+ statement failed then an error code is returned.
+
+ After sqlite_finalize() has been called, the statement handle is
+ invalidated. Passing it to any other SQLite function may cause a
+ crash.
+
+ All prepared statements must finalized before sqlite3_close() is
+ called or else the close will fail with a return code of SQLITE_BUSY.
+
+ This routine can be called at any point during the execution of the
+ virtual machine. If the virtual machine has not completed execution
+ when this routine is called, that is like encountering an error or
+ an interrupt. (See sqlite3_interrupt().) Incomplete updates may be
+ rolled back and transactions canceled, depending on the circumstances,
+ and the result code returned will be SQLITE_ABORT.
+}
+
+api {} {
+void *sqlite3_malloc(int);
+void *sqlite3_realloc(void*, int);
+void sqlite3_free(void*);
+} {
+ These routines provide access to the memory allocator used by SQLite.
+ Depending on how SQLite has been compiled and the OS-layer backend,
+ the memory allocator used by SQLite might be the standard system
+ malloc()/realloc()/free(), or it might be something different. With
+ certain compile-time flags, SQLite will add wrapper logic around the
+ memory allocator to add memory leak and buffer overrun detection. The
+ OS layer might substitute a completely different memory allocator.
+ Use these APIs to be sure you are always using the correct memory
+ allocator.
+
+ The sqlite3_free() API, not the standard free() from the system library,
+ should always be used to free the memory buffer returned by
+ sqlite3_mprintf() or sqlite3_vmprintf() and to free the error message
+ string returned by sqlite3_exec(). Using free() instead of sqlite3_free()
+ might accidentally work on some systems and build configurations but
+ will fail on others.
+
+ Compatibility Note: Prior to version 3.4.0, the sqlite3_free API
+ was prototyped to take a <tt>char*</tt> parameter rather than
+ <tt>void*</tt>. Like this:
+<blockquote><pre>
+void sqlite3_free(char*);
+</pre></blockquote>
+ The change to using <tt>void*</tt> might cause warnings when
+ compiling older code against
+ newer libraries, but everything should still work correctly.
+}
+
+api {} {
+int sqlite3_get_table(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be executed */
+ char ***resultp, /* Result written to a char *[] that this points to */
+ int *nrow, /* Number of result rows written here */
+ int *ncolumn, /* Number of result columns written here */
+ char **errmsg /* Error msg written here */
+);
+void sqlite3_free_table(char **result);
+} {
+ This next routine is really just a wrapper around sqlite3_exec().
+ Instead of invoking a user-supplied callback for each row of the
+ result, this routine remembers each row of the result in memory
+ obtained from malloc(), then returns all of the result after the
+ query has finished.
+
+ As an example, suppose the query result where this table:
+
+ <pre>
+ Name | Age
+ -----------------------
+ Alice | 43
+ Bob | 28
+ Cindy | 21
+ </pre>
+
+ If the 3rd argument were &azResult then after the function returns
+ azResult will contain the following data:
+
+ <pre>
+ azResult[0] = "Name";
+ azResult[1] = "Age";
+ azResult[2] = "Alice";
+ azResult[3] = "43";
+ azResult[4] = "Bob";
+ azResult[5] = "28";
+ azResult[6] = "Cindy";
+ azResult[7] = "21";
+ </pre>
+
+ Notice that there is an extra row of data containing the column
+ headers. But the *nrow return value is still 3. *ncolumn is
+ set to 2. In general, the number of values inserted into azResult
+ will be ((*nrow) + 1)*(*ncolumn).
+
+ After the calling function has finished using the result, it should
+ pass the result data pointer to sqlite3_free_table() in order to
+ release the memory that was malloc-ed. Because of the way the
+ malloc() happens, the calling function must not try to call
+ malloc() directly. Only sqlite3_free_table() is able to release
+ the memory properly and safely.
+
+ The return value of this routine is the same as from sqlite3_exec().
+}
+
+api {sqlite3_interrupt} {
+ void sqlite3_interrupt(sqlite3*);
+} {
+ This function causes any pending database operation to abort and
+ return at its earliest opportunity. This routine is typically
+ called in response to a user action such as pressing "Cancel"
+ or Ctrl-C where the user wants a long query operation to halt
+ immediately.
+} {}
+
+api {} {
+long long int sqlite3_last_insert_rowid(sqlite3*);
+} {
+ Each entry in an SQLite table has a unique integer key called the "rowid".
+ The rowid is always available as an undeclared column
+ named ROWID, OID, or _ROWID_.
+ If the table has a column of type INTEGER PRIMARY KEY then that column
+ is another an alias for the rowid.
+
+ This routine
+ returns the rowid of the most recent INSERT into the database
+ from the database connection given in the first argument. If
+ no inserts have ever occurred on this database connection, zero
+ is returned.
+
+ If an INSERT occurs within a trigger, then the rowid of the
+ inserted row is returned by this routine as long as the trigger
+ is running. But once the trigger terminates, the value returned
+ by this routine reverts to the last value inserted before the
+ trigger fired.
+} {}
+
+api {} {
+char *sqlite3_mprintf(const char*,...);
+char *sqlite3_vmprintf(const char*, va_list);
+} {
+ These routines are variants of the "sprintf()" from the
+ standard C library. The resulting string is written into memory
+ obtained from malloc() so that there is never a possibility of buffer
+ overflow. These routines also implement some additional formatting
+ options that are useful for constructing SQL statements.
+
+ The strings returned by these routines should be freed by calling
+ sqlite3_free().
+
+ All of the usual printf formatting options apply. In addition, there
+ is a "%q" option. %q works like %s in that it substitutes a null-terminated
+ string from the argument list. But %q also doubles every '\\'' character.
+ %q is designed for use inside a string literal. By doubling each '\\''
+ character it escapes that character and allows it to be inserted into
+ the string.
+
+ For example, so some string variable contains text as follows:
+
+ <blockquote><pre>
+ char *zText = "It's a happy day!";
+ </pre></blockquote>
+
+ One can use this text in an SQL statement as follows:
+
+ <blockquote><pre>
+ sqlite3_exec_printf(db, "INSERT INTO table VALUES('%q')",
+ callback1, 0, 0, zText);
+ </pre></blockquote>
+
+ Because the %q format string is used, the '\\'' character in zText
+ is escaped and the SQL generated is as follows:
+
+ <blockquote><pre>
+ INSERT INTO table1 VALUES('It''s a happy day!')
+ </pre></blockquote>
+
+ This is correct. Had we used %s instead of %q, the generated SQL
+ would have looked like this:
+
+ <blockquote><pre>
+ INSERT INTO table1 VALUES('It's a happy day!');
+ </pre></blockquote>
+
+ This second example is an SQL syntax error. As a general rule you
+ should always use %q instead of %s when inserting text into a string
+ literal.
+} {}
+
+api {} {
+char *sqlite3_snprintf(int bufSize, char *buf, const char *zFormat, ...);
+} {
+ This routine works like "sprintf()", writing a formatted string into
+ the buf[]. However, no more than bufSize characters will be written
+ into buf[]. This routine returns a pointer to buf[]. If bufSize is
+ greater than zero, then buf[] is guaranteed to be zero-terminated.
+
+ This routine uses the same extended formatting options as
+ sqlite3_mprintf() and sqlite3_vmprintf().
+
+ Note these differences with the snprintf() function found in many
+ standard libraries: (1) sqlite3_snprintf() returns a pointer to the
+ buffer rather than the number of characters written. (It would,
+ arguably, be more useful to return the number of characters written,
+ but we discovered that after the interface had been published and
+ are unwilling to break backwards compatibility.) (2) The order
+ of the bufSize and buf parameter is reversed from snprintf().
+ And (3) sqlite3_snprintf() always writes a zero-terminator if bufSize
+ is positive.
+
+ Please do not use the return value of this routine. We may
+ decide to make the minor compatibility break and change this routine
+ to return the number of characters written rather than a pointer to
+ the buffer in a future minor version increment.
+}
+
+api {} {
+int sqlite3_open(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open16(
+ const void *filename, /* Database filename (UTF-16) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+} {
+ Open the sqlite database file "filename". The "filename" is UTF-8
+ encoded for sqlite3_open() and UTF-16 encoded in the native byte order
+ for sqlite3_open16(). An sqlite3* handle is returned in *ppDb, even
+ if an error occurs. If the database is opened (or created) successfully,
+ then SQLITE_OK is returned. Otherwise an error code is returned. The
+ sqlite3_errmsg() or sqlite3_errmsg16() routines can be used to obtain
+ an English language description of the error.
+
+ If the database file does not exist, then a new database will be created
+ as needed.
+ The encoding for the database will be UTF-8 if sqlite3_open() is called and
+ UTF-16 if sqlite3_open16 is used.
+
+ Whether or not an error occurs when it is opened, resources associated
+ with the sqlite3* handle should be released by passing it to
+ sqlite3_close() when it is no longer required.
+
+ The returned sqlite3* can only be used in the same thread in which it
+ was created. It is an error to call sqlite3_open() in one thread then
+ pass the resulting database handle off to another thread to use. This
+ restriction is due to goofy design decisions (bugs?) in the way some
+ threading implementations interact with file locks.
+
+ Note to windows users: The encoding used for the filename argument
+ of sqlite3_open() must be UTF-8, not whatever codepage is currently
+ defined. Filenames containing international characters must be converted
+ to UTF-8 prior to passing them into sqlite3_open().
+}
+
+api {} {
+int sqlite3_prepare_v2(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16_v2(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+
+/* Legacy Interfaces */
+int sqlite3_prepare(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+} {
+ To execute an SQL query, it must first be compiled into a byte-code
+ program using one of these routines.
+
+ The first argument "db" is an SQLite database handle. The second
+ argument "zSql" is the statement to be compiled, encoded as either
+ UTF-8 or UTF-16. The sqlite3_prepare_v2()
+ interfaces uses UTF-8 and sqlite3_prepare16_v2()
+ use UTF-16. If the next argument, "nBytes", is less
+ than zero, then zSql is read up to the first nul terminator. If
+ "nBytes" is not less than zero, then it is the length of the string zSql
+ in bytes (not characters).
+
+ *pzTail is made to point to the first byte past the end of the first
+ SQL statement in zSql. This routine only compiles the first statement
+ in zSql, so *pzTail is left pointing to what remains uncompiled.
+
+ *ppStmt is left pointing to a compiled SQL statement that can be
+ executed using sqlite3_step(). Or if there is an error, *ppStmt may be
+ set to NULL. If the input text contained no SQL (if the input is and
+ empty string or a comment) then *ppStmt is set to NULL. The calling
+ procedure is responsible for deleting this compiled SQL statement
+ using sqlite3_finalize() after it has finished with it.
+
+ On success, SQLITE_OK is returned. Otherwise an error code is returned.
+
+ The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
+ recommended for all new programs. The two older interfaces are retained
+ for backwards compatibility, but their use is discouraged.
+ In the "v2" interfaces, the prepared statement
+ that is returned (the sqlite3_stmt object) contains a copy of the original
+ SQL. This causes the sqlite3_step() interface to behave a differently in
+ two ways:
+
+ <ol>
+ <li>
+ If the database schema changes, instead of returning SQLITE_SCHEMA as it
+ always used to do, sqlite3_step() will automatically recompile the SQL
+ statement and try to run it again. If the schema has changed in a way
+ that makes the statement no longer valid, sqlite3_step() will still
+ return SQLITE_SCHEMA. But unlike the legacy behavior, SQLITE_SCHEMA is
+ now a fatal error. Calling sqlite3_prepare_v2() again will not make the
+ error go away. Note: use sqlite3_errmsg() to find the text of the parsing
+ error that results in an SQLITE_SCHEMA return.
+ </li>
+
+ <li>
+ When an error occurs,
+ sqlite3_step() will return one of the detailed result-codes
+ like SQLITE_IOERR or SQLITE_FULL or SQLITE_SCHEMA directly. The
+ legacy behavior was that sqlite3_step() would only return a generic
+ SQLITE_ERROR code and you would have to make a second call to
+ sqlite3_reset() in order to find the underlying cause of the problem.
+ With the "v2" prepare interfaces, the underlying reason for the error is
+ returned directly.
+ </li>
+ </ol>
+}
+
+api {} {
+void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
+} {
+ <i>Experimental</i>
+
+ This routine configures a callback function - the progress callback - that
+ is invoked periodically during long running calls to sqlite3_exec(),
+ sqlite3_step() and sqlite3_get_table().
+ An example use for this API is to keep
+ a GUI updated during a large query.
+
+ The progress callback is invoked once for every N virtual machine opcodes,
+ where N is the second argument to this function. The progress callback
+ itself is identified by the third argument to this function. The fourth
+ argument to this function is a void pointer passed to the progress callback
+ function each time it is invoked.
+
+ If a call to sqlite3_exec(), sqlite3_step() or sqlite3_get_table() results
+ in less than N opcodes being executed, then the progress callback is not
+ invoked.
+
+ To remove the progress callback altogether, pass NULL as the third
+ argument to this function.
+
+ If the progress callback returns a result other than 0, then the current
+ query is immediately terminated and any database changes rolled back. If the
+ query was part of a larger transaction, then the transaction is not rolled
+ back and remains active. The sqlite3_exec() call returns SQLITE_ABORT.
+
+}
+
+api {} {
+int sqlite3_reset(sqlite3_stmt *pStmt);
+} {
+ The sqlite3_reset() function is called to reset a prepared SQL
+ statement obtained by a previous call to
+ sqlite3_prepare_v2() or
+ sqlite3_prepare16_v2() back to it's initial state, ready to be re-executed.
+ Any SQL statement variables that had values bound to them using
+ the sqlite3_bind_*() API retain their values.
+}
+
+api {} {
+void sqlite3_result_blob(sqlite3_context*, const void*, int n, void(*)(void*));
+void sqlite3_result_double(sqlite3_context*, double);
+void sqlite3_result_error(sqlite3_context*, const char*, int);
+void sqlite3_result_error16(sqlite3_context*, const void*, int);
+void sqlite3_result_int(sqlite3_context*, int);
+void sqlite3_result_int64(sqlite3_context*, long long int);
+void sqlite3_result_null(sqlite3_context*);
+void sqlite3_result_text(sqlite3_context*, const char*, int n, void(*)(void*));
+void sqlite3_result_text16(sqlite3_context*, const void*, int n, void(*)(void*));
+void sqlite3_result_text16be(sqlite3_context*, const void*, int n, void(*)(void*));
+void sqlite3_result_text16le(sqlite3_context*, const void*, int n, void(*)(void*));
+void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+} {
+ User-defined functions invoke these routines in order to
+ set their return value. The sqlite3_result_value() routine is used
+ to return an exact copy of one of the arguments to the function.
+
+ The operation of these routines is very similar to the operation of
+ sqlite3_bind_blob() and its cousins. Refer to the documentation there
+ for additional information.
+}
+
+api {} {
+int sqlite3_set_authorizer(
+ sqlite3*,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pUserData
+);
+#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
+#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
+#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
+#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
+#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
+#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
+#define SQLITE_DELETE 9 /* Table Name NULL */
+#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
+#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
+#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
+#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
+#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
+#define SQLITE_DROP_VIEW 17 /* View Name NULL */
+#define SQLITE_INSERT 18 /* Table Name NULL */
+#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
+#define SQLITE_READ 20 /* Table Name Column Name */
+#define SQLITE_SELECT 21 /* NULL NULL */
+#define SQLITE_TRANSACTION 22 /* NULL NULL */
+#define SQLITE_UPDATE 23 /* Table Name Column Name */
+#define SQLITE_ATTACH 24 /* Filename NULL */
+#define SQLITE_DETACH 25 /* Database Name NULL */
+#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */
+#define SQLITE_REINDEX 27 /* Index Name NULL */
+#define SQLITE_ANALYZE 28 /* Table Name NULL */
+#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */
+#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */
+#define SQLITE_FUNCTION 31 /* Function Name NULL */
+
+#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
+#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
+} {
+ This routine registers a callback with the SQLite library. The
+ callback is invoked by sqlite3_prepare_v2() to authorize various
+ operations against the database. The callback should
+ return SQLITE_OK if access is allowed, SQLITE_DENY if the entire
+ SQL statement should be aborted with an error and SQLITE_IGNORE
+ if the operation should be treated as a no-op.
+
+ Each database connection have at most one authorizer registered
+ at a time one time. Each call
+ to sqlite3_set_authorizer() overrides the previous authorizer.
+ Setting the callback to NULL disables the authorizer.
+
+ The second argument to the access authorization function will be one
+ of the defined constants shown. These values signify what kind of operation
+ is to be authorized. The 3rd and 4th arguments to the authorization
+ function will be arguments or NULL depending on which of the
+ codes is used as the second argument. For example, if the the
+ 2nd argument code is SQLITE_READ then the 3rd argument will be the name
+ of the table that is being read from and the 4th argument will be the
+ name of the column that is being read from. Or if the 2nd argument
+ is SQLITE_FUNCTION then the 3rd argument will be the name of the
+ function that is being invoked and the 4th argument will be NULL.
+
+ The 5th argument is the name
+ of the database ("main", "temp", etc.) where applicable. The 6th argument
+ is the name of the inner-most trigger or view that is responsible for
+ the access attempt or NULL if this access attempt is directly from
+ input SQL code.
+
+ The return value of the authorization callback function should be one of the
+ constants SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. A return of
+ SQLITE_OK means that the operation is permitted and that
+ sqlite3_prepare_v2() can proceed as normal.
+ A return of SQLITE_DENY means that the sqlite3_prepare_v2()
+ should fail with an error. A return of SQLITE_IGNORE causes the
+ sqlite3_prepare_v2() to continue as normal but the requested
+ operation is silently converted into a no-op. A return of SQLITE_IGNORE
+ in response to an SQLITE_READ or SQLITE_FUNCTION causes the column
+ being read or the function being invoked to return a NULL.
+
+ The intent of this routine is to allow applications to safely execute
+ user-entered SQL. An appropriate callback can deny the user-entered
+ SQL access certain operations (ex: anything that changes the database)
+ or to deny access to certain tables or columns within the database.
+
+ SQLite is not reentrant through the authorization callback function.
+ The authorization callback function should not attempt to invoke
+ any other SQLite APIs for the same database connection. If the
+ authorization callback function invokes some other SQLite API, an
+ SQLITE_MISUSE error or a segmentation fault may result.
+}
+
+api {} {
+int sqlite3_step(sqlite3_stmt*);
+} {
+ After an SQL query has been prepared with a call to either
+ sqlite3_prepare_v2() or sqlite3_prepare16_v2() or to one of
+ the legacy interfaces sqlite3_prepare() or sqlite3_prepare16(),
+ then this function must be
+ called one or more times to execute the statement.
+
+ The details of the behavior of this sqlite3_step() interface depend
+ on whether the statement was prepared using the newer "v2" interface
+ sqlite3_prepare_v2() and sqlite3_prepare16_v2() or the older legacy
+ interface sqlite3_prepare() and sqlite3_prepare16(). The use of the
+ new "v2" interface is recommended for new applications but the legacy
+ interface will continue to be supported.
+
+ In the lagacy interface, the return value will be either SQLITE_BUSY,
+ SQLITE_DONE, SQLITE_ROW, SQLITE_ERROR, or SQLITE_MISUSE. With the "v2"
+ interface, any of the other SQLite result-codes might be returned as
+ well.
+
+ SQLITE_BUSY means that the database engine attempted to open
+ a locked database and there is no busy callback registered.
+ Call sqlite3_step() again to retry the open.
+
+ SQLITE_DONE means that the statement has finished executing
+ successfully. sqlite3_step() should not be called again on this virtual
+ machine without first calling sqlite3_reset() to reset the virtual
+ machine back to its initial state.
+
+ If the SQL statement being executed returns any data, then
+ SQLITE_ROW is returned each time a new row of data is ready
+ for processing by the caller. The values may be accessed using
+ the sqlite3_column_int(), sqlite3_column_text(), and similar functions.
+ sqlite3_step() is called again to retrieve the next row of data.
+
+ SQLITE_ERROR means that a run-time error (such as a constraint
+ violation) has occurred. sqlite3_step() should not be called again on
+ the VM. More information may be found by calling sqlite3_errmsg().
+ A more specific error code (example: SQLITE_INTERRUPT, SQLITE_SCHEMA,
+ SQLITE_CORRUPT, and so forth) can be obtained by calling
+ sqlite3_reset() on the prepared statement. In the "v2" interface,
+ the more specific error code is returned directly by sqlite3_step().
+
+ SQLITE_MISUSE means that the this routine was called inappropriately.
+ Perhaps it was called on a virtual machine that had already been
+ finalized or on one that had previously returned SQLITE_ERROR or
+ SQLITE_DONE. Or it could be the case that a database connection
+ is being used by a different thread than the one it was created it.
+
+ <b>Goofy Interface Alert:</b>
+ In the legacy interface,
+ the sqlite3_step() API always returns a generic error code,
+ SQLITE_ERROR, following any error other than SQLITE_BUSY and SQLITE_MISUSE.
+ You must call sqlite3_reset() (or sqlite3_finalize()) in order to find
+ one of the specific result-codes that better describes the error.
+ We admit that this is a goofy design. The problem has been fixed
+ with the "v2" interface. If you prepare all of your SQL statements
+ using either sqlite3_prepare_v2() or sqlite3_prepare16_v2() instead
+ of the legacy sqlite3_prepare() and sqlite3_prepare16(), then the
+ more specific result-codes are returned directly by sqlite3_step().
+ The use of the "v2" interface is recommended.
+}
+
+api {} {
+void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+} {
+ Register a function that is called each time an SQL statement is evaluated.
+ The callback function is invoked on the first call to sqlite3_step() after
+ calls to sqlite3_prepare_v2() or sqlite3_reset().
+ This function can be used (for example) to generate
+ a log file of all SQL executed against a database. This can be
+ useful when debugging an application that uses SQLite.
+}
+
+api {} {
+void *sqlite3_user_data(sqlite3_context*);
+} {
+ The pUserData argument to the sqlite3_create_function() and
+ sqlite3_create_function16() routines used to register user functions
+ is available to the implementation of the function using this
+ call.
+}
+
+api {} {
+const void *sqlite3_value_blob(sqlite3_value*);
+int sqlite3_value_bytes(sqlite3_value*);
+int sqlite3_value_bytes16(sqlite3_value*);
+double sqlite3_value_double(sqlite3_value*);
+int sqlite3_value_int(sqlite3_value*);
+long long int sqlite3_value_int64(sqlite3_value*);
+const unsigned char *sqlite3_value_text(sqlite3_value*);
+const void *sqlite3_value_text16(sqlite3_value*);
+const void *sqlite3_value_text16be(sqlite3_value*);
+const void *sqlite3_value_text16le(sqlite3_value*);
+int sqlite3_value_type(sqlite3_value*);
+} {
+ This group of routines returns information about arguments to
+ a user-defined function. Function implementations use these routines
+ to access their arguments. These routines are the same as the
+ sqlite3_column_... routines except that these routines take a single
+ sqlite3_value* pointer instead of an sqlite3_stmt* and an integer
+ column number.
+
+ See the documentation under sqlite3_column_blob for additional
+ information.
+
+ Please pay particular attention to the fact that the pointer that
+ is returned from sqlite3_value_blob(), sqlite3_value_text(), or
+ sqlite3_value_text16() can be invalidated by a subsequent call to
+ sqlite3_value_bytes(), sqlite3_value_bytes16(), sqlite_value_text(),
+ or sqlite3_value_text16().
+}
+
+api {} {
+ int sqlite3_sleep(int);
+} {
+ Sleep for a little while. The second parameter is the number of
+ miliseconds to sleep for.
+
+ If the operating system does not support sleep requests with
+ milisecond time resolution, then the time will be rounded up to
+ the nearest second. The number of miliseconds of sleep actually
+ requested from the operating system is returned.
+}
+
+api {} {
+ int sqlite3_expired(sqlite3_stmt*);
+} {
+ Return TRUE (non-zero) if the statement supplied as an argument needs
+ to be recompiled. A statement needs to be recompiled whenever the
+ execution environment changes in a way that would alter the program
+ that sqlite3_prepare() generates. For example, if new functions or
+ collating sequences are registered or if an authorizer function is
+ added or changed.
+}
+
+api {} {
+ int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
+} {
+ Move all bindings from the first prepared statement over to the second.
+ This routine is useful, for example, if the first prepared statement
+ fails with an SQLITE_SCHEMA error. The same SQL can be prepared into
+ the second prepared statement then all of the bindings transfered over
+ to the second statement before the first statement is finalized.
+}
+
+api {} {
+ int sqlite3_global_recover();
+} {
+ This function used to be involved in recovering from out-of-memory
+ errors. But as of SQLite version 3.3.0, out-of-memory recovery is
+ automatic and this routine now does nothing. THe interface is retained
+ to avoid link errors with legacy code.
+}
+
+api {} {
+ int sqlite3_get_autocommit(sqlite3*);
+} {
+ Test to see whether or not the database connection is in autocommit
+ mode. Return TRUE if it is and FALSE if not. Autocommit mode is on
+ by default. Autocommit is disabled by a BEGIN statement and reenabled
+ by the next COMMIT or ROLLBACK.
+}
+
+api {} {
+ int sqlite3_clear_bindings(sqlite3_stmt*);
+} {
+ Set all the parameters in the compiled SQL statement back to NULL.
+}
+
+api {} {
+ sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
+} {
+ Return the sqlite3* database handle to which the prepared statement given
+ in the argument belongs. This is the same database handle that was
+ the first argument to the sqlite3_prepare() that was used to create
+ the statement in the first place.
+}
+
+api {} {
+ void *sqlite3_update_hook(
+ sqlite3*,
+ void(*)(void *,int ,char const *,char const *,sqlite_int64),
+ void*
+ );
+} {
+ Register a callback function with the database connection identified by the
+ first argument to be invoked whenever a row is updated, inserted or deleted.
+ Any callback set by a previous call to this function for the same
+ database connection is overridden.
+
+ The second argument is a pointer to the function to invoke when a
+ row is updated, inserted or deleted. The first argument to the callback is
+ a copy of the third argument to sqlite3_update_hook. The second callback
+ argument is one of SQLITE_INSERT, SQLITE_DELETE or SQLITE_UPDATE, depending
+ on the operation that caused the callback to be invoked. The third and
+ fourth arguments to the callback contain pointers to the database and
+ table name containing the affected row. The final callback parameter is
+ the rowid of the row. In the case of an update, this is the rowid after
+ the update takes place.
+
+ The update hook is not invoked when internal system tables are
+ modified (i.e. sqlite_master and sqlite_sequence).
+
+ If another function was previously registered, its pArg value is returned.
+ Otherwise NULL is returned.
+
+ See also: sqlite3_commit_hook(), sqlite3_rollback_hook()
+}
+
+api {} {
+ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
+} {
+ Register a callback to be invoked whenever a transaction is rolled
+ back.
+
+ The new callback function overrides any existing rollback-hook
+ callback. If there was an existing callback, then it's pArg value
+ (the third argument to sqlite3_rollback_hook() when it was registered)
+ is returned. Otherwise, NULL is returned.
+
+ For the purposes of this API, a transaction is said to have been
+ rolled back if an explicit "ROLLBACK" statement is executed, or
+ an error or constraint causes an implicit rollback to occur. The
+ callback is not invoked if a transaction is automatically rolled
+ back because the database connection is closed.
+}
+
+api {} {
+ int sqlite3_enable_shared_cache(int);
+} {
+ This routine enables or disables the sharing of the database cache
+ and schema data structures between connections to the same database.
+ Sharing is enabled if the argument is true and disabled if the argument
+ is false.
+
+ Cache sharing is enabled and disabled on a thread-by-thread basis.
+ Each call to this routine enables or disables cache sharing only for
+ connections created in the same thread in which this routine is called.
+ There is no mechanism for sharing cache between database connections
+ running in different threads.
+
+ Sharing must be disabled prior to shutting down a thread or else
+ the thread will leak memory. Call this routine with an argument of
+ 0 to turn off sharing. Or use the sqlite3_thread_cleanup() API.
+
+ This routine must not be called when any database connections
+ are active in the current thread. Enabling or disabling shared
+ cache while there are active database connections will result
+ in memory corruption.
+
+ When the shared cache is enabled, the
+ following routines must always be called from the same thread:
+ sqlite3_open(), sqlite3_prepare_v2(), sqlite3_step(), sqlite3_reset(),
+ sqlite3_finalize(), and sqlite3_close().
+ This is due to the fact that the shared cache makes use of
+ thread-specific storage so that it will be available for sharing
+ with other connections.
+
+ Virtual tables cannot be used with a shared cache. When shared
+ cache is enabled, the sqlite3_create_module() API used to register
+ virtual tables will always return an error.
+
+ This routine returns SQLITE_OK if shared cache was
+ enabled or disabled successfully. An error code is returned
+ otherwise.
+
+ Shared cache is disabled by default for backward compatibility.
+}
+
+api {} {
+ void sqlite3_thread_cleanup(void);
+} {
+ This routine makes sure that all thread local storage used by SQLite
+ in the current thread has been deallocated. A thread can call this
+ routine prior to terminating in order to make sure there are no memory
+ leaks.
+
+ This routine is not strictly necessary. If cache sharing has been
+ disabled using sqlite3_enable_shared_cache() and if all database
+ connections have been closed and if SQLITE_ENABLE_MEMORY_MANAGMENT is
+ on and all memory has been freed, then the thread local storage will
+ already have been automatically deallocated. This routine is provided
+ as a convenience to the program who just wants to make sure that there
+ are no leaks.
+}
+
+api {} {
+ int sqlite3_release_memory(int N);
+} {
+ This routine attempts to free at least N bytes of memory from the caches
+ of database connecions that were created in the same thread from which this
+ routine is called. The value returned is the number of bytes actually
+ freed.
+
+ This routine is only available if memory management has been enabled
+ by compiling with the SQLITE_ENABLE_MEMORY_MANAGMENT macro.
+}
+
+api {} {
+ void sqlite3_soft_heap_limit(int N);
+} {
+ This routine sets the soft heap limit for the current thread to N.
+ If the total heap usage by SQLite in the current thread exceeds N,
+ then sqlite3_release_memory() is called to try to reduce the memory usage
+ below the soft limit.
+
+ Prior to shutting down a thread sqlite3_soft_heap_limit() must be set to
+ zero (the default) or else the thread will leak memory. Alternatively, use
+ the sqlite3_thread_cleanup() API.
+
+ A negative or zero value for N means that there is no soft heap limit and
+ sqlite3_release_memory() will only be called when memory is exhaused.
+ The default value for the soft heap limit is zero.
+
+ SQLite makes a best effort to honor the soft heap limit. But if it
+ is unable to reduce memory usage below the soft limit, execution will
+ continue without error or notification. This is why the limit is
+ called a "soft" limit. It is advisory only.
+
+ This routine is only available if memory management has been enabled
+ by compiling with the SQLITE_ENABLE_MEMORY_MANAGMENT macro.
+}
+
+api {} {
+ void sqlite3_thread_cleanup(void);
+} {
+ This routine ensures that a thread that has used SQLite in the past
+ has released any thread-local storage it might have allocated.
+ When the rest of the API is used properly, the cleanup of
+ thread-local storage should be completely automatic. You should
+ never really need to invoke this API. But it is provided to you
+ as a precaution and as a potential work-around for future
+ thread-releated memory-leaks.
+}
+
+set n 0
+set i 0
+foreach item $apilist {
+ set namelist [lindex $item 0]
+ foreach name $namelist {
+ set n_to_name($n) $name
+ set n_to_idx($n) $i
+ set name_to_idx($name) $i
+ incr n
+ }
+ incr i
+}
+set i 0
+foreach name [lsort [array names name_to_idx]] {
+ set sname($i) $name
+ incr i
+}
+#parray n_to_name
+#parray n_to_idx
+#parray name_to_idx
+#parray sname
+incr n -1
+puts "<DIV class=pdf_ignore>"
+puts {<table width="100%" cellpadding="5"><tr>}
+set nrow [expr {($n+2)/3}]
+set i 0
+for {set j 0} {$j<3} {incr j} {
+ if {$j>0} {puts {<td width="10"></td>}}
+ puts {<td valign="top">}
+ set limit [expr {$i+$nrow}]
+ puts {<ul>}
+ while {$i<$limit && $i<$n} {
+ set name $sname($i)
+ if {[regexp {^sqlite} $name]} {set display $name} {set display <i>$name</i>}
+ puts "<li><a href=\"#$name\">$display</a></li>"
+ incr i
+ }
+ puts {</ul></td>}
+}
+puts "</table>"
+puts "<!-- $n entries. $nrow rows in 3 columns -->"
+puts "</DIV>"
+
+proc resolve_name {ignore_list name} {
+ global name_to_idx
+ if {![info exists name_to_idx($name)] || [lsearch $ignore_list $name]>=0} {
+ return $name
+ } else {
+ return "<a href=\"#$name\">$name</a>"
+ }
+}
+
+foreach name [lsort [array names name_to_idx]] {
+ set i $name_to_idx($name)
+ if {[info exists done($i)]} continue
+ set done($i) 1
+ foreach {namelist prototype desc} [lindex $apilist $i] break
+ foreach name $namelist {
+ puts "<a name=\"$name\"></a>"
+ }
+ puts "<p><hr></p>"
+ puts "<blockquote><pre>"
+ regsub "^( *\n)+" $prototype {} p2
+ regsub "(\n *)+\$" $p2 {} p3
+ puts $p3
+ puts "</pre></blockquote>"
+ regsub -all {\[} $desc {\[} desc
+ regsub -all {sqlite3_[a-z0-9_]+} $desc "\[resolve_name $name &\]" d2
+ foreach x $specialname {
+ regsub -all $x $d2 "\[resolve_name $name &\]" d2
+ }
+ regsub -all "\n( *\n)+" [subst $d2] "</p>\n\n<p>" d3
+ puts "<p>$d3</p>"
+}
+
+puts "<DIV class=pdf_ignore>"
+footer $rcsid
+puts "</DIV>"